diff options
author | Diego Roversi <diegor@tiscali.it> | 2019-09-08 18:12:27 +0200 |
---|---|---|
committer | Diego Roversi <diegor@tiscali.it> | 2019-09-08 18:12:27 +0200 |
commit | 1d9925c287b318ec21343e2682b51ab6a36ae8db (patch) | |
tree | 17d1c0ac21eea6f291146520afa8381db4586fb4 /bubbob |
initial commit from cvs 1.6.2
Diffstat (limited to 'bubbob')
-rw-r--r-- | bubbob/.cvsignore | 4 | ||||
-rw-r--r-- | bubbob/Makefile | 2 | ||||
-rwxr-xr-x | bubbob/bb.py | 345 | ||||
-rw-r--r-- | bubbob/binboards.py | 122 | ||||
-rw-r--r-- | bubbob/boarddef.py | 69 | ||||
-rw-r--r-- | bubbob/boards.py | 1470 | ||||
-rw-r--r-- | bubbob/bonuses.py | 2504 | ||||
-rw-r--r-- | bubbob/bubbles.py | 1284 | ||||
-rw-r--r-- | bubbob/command.py | 10 | ||||
-rw-r--r-- | bubbob/doc/.cvsignore | 1 | ||||
-rwxr-xr-x | bubbob/doc/bonus-doc.py | 170 | ||||
-rw-r--r-- | bubbob/doc/images/.cvsignore | 1 | ||||
-rw-r--r-- | bubbob/ext1/.cvsignore | 2 | ||||
-rw-r--r-- | bubbob/ext1/__init__.py | 403 | ||||
-rw-r--r-- | bubbob/ext1/brick.wav | bin | 0 -> 29150 bytes | |||
-rw-r--r-- | bubbob/ext1/image1-0.ppm | bin | 0 -> 7732 bytes | |||
-rw-r--r-- | bubbob/ext1/music.wav | bin | 0 -> 748826 bytes | |||
-rw-r--r-- | bubbob/ext1/wall.wav | bin | 0 -> 13274 bytes | |||
-rw-r--r-- | bubbob/ext2/.cvsignore | 1 | ||||
-rw-r--r-- | bubbob/ext2/__init__.py | 578 | ||||
-rw-r--r-- | bubbob/ext2/image1.ppm | bin | 0 -> 24635 bytes | |||
-rw-r--r-- | bubbob/ext2/image2.ppm | bin | 0 -> 3898 bytes | |||
-rw-r--r-- | bubbob/ext2/music.wav | bin | 0 -> 353000 bytes | |||
-rw-r--r-- | bubbob/ext3/.cvsignore | 2 | ||||
-rw-r--r-- | bubbob/ext3/__init__.py | 367 | ||||
-rw-r--r-- | bubbob/ext3/image1-0.ppm | bin | 0 -> 3124 bytes | |||
-rw-r--r-- | bubbob/ext3/music.wav | bin | 0 -> 284134 bytes | |||
-rw-r--r-- | bubbob/ext3/shoot.wav | bin | 0 -> 13274 bytes | |||
-rw-r--r-- | bubbob/ext4/.cvsignore | 2 | ||||
-rw-r--r-- | bubbob/ext4/__init__.py | 440 | ||||
-rw-r--r-- | bubbob/ext4/image1-0.ppm | bin | 0 -> 1588 bytes | |||
-rw-r--r-- | bubbob/ext4/music.wav | bin | 0 -> 566532 bytes | |||
-rw-r--r-- | bubbob/ext5/.cvsignore | 1 | ||||
-rw-r--r-- | bubbob/ext5/__init__.py | 289 | ||||
-rw-r--r-- | bubbob/ext5/image1.ppm | 5 | ||||
-rw-r--r-- | bubbob/ext5/image2.ppm | 5 | ||||
-rw-r--r-- | bubbob/ext5/image3.ppm | 5 | ||||
-rw-r--r-- | bubbob/ext5/image4.ppm | bin | 0 -> 9274 bytes | |||
-rw-r--r-- | bubbob/ext5/music.wav | bin | 0 -> 346458 bytes | |||
-rw-r--r-- | bubbob/ext5/ouch.wav | bin | 0 -> 2631 bytes | |||
-rw-r--r-- | bubbob/ext6/.cvsignore | 2 | ||||
-rw-r--r-- | bubbob/ext6/__init__.py | 268 | ||||
-rw-r--r-- | bubbob/ext6/crash.wav | bin | 0 -> 13123 bytes | |||
-rw-r--r-- | bubbob/ext6/image1-0.ppm | bin | 0 -> 1971 bytes | |||
-rw-r--r-- | bubbob/ext6/music.wav | bin | 0 -> 431045 bytes | |||
-rw-r--r-- | bubbob/ext7/.cvsignore | 2 | ||||
-rw-r--r-- | bubbob/ext7/__init__.py | 399 | ||||
-rw-r--r-- | bubbob/ext7/fire.wav | bin | 0 -> 3352 bytes | |||
-rw-r--r-- | bubbob/ext7/hit.wav | bin | 0 -> 3352 bytes | |||
-rw-r--r-- | bubbob/ext7/image1-0.ppm | bin | 0 -> 93365 bytes | |||
-rw-r--r-- | bubbob/ext7/music.wav | bin | 0 -> 379900 bytes | |||
-rw-r--r-- | bubbob/images.py | 542 | ||||
-rw-r--r-- | bubbob/images/.cvsignore | 7 | ||||
-rw-r--r-- | bubbob/images/10000_0.ppm | bin | 0 -> 10852 bytes | |||
-rw-r--r-- | bubbob/images/20000_0.ppm | bin | 0 -> 13282 bytes | |||
-rw-r--r-- | bubbob/images/30000_0.ppm | bin | 0 -> 13282 bytes | |||
-rw-r--r-- | bubbob/images/40000_0.ppm | bin | 0 -> 13282 bytes | |||
-rw-r--r-- | bubbob/images/50000_0.ppm | bin | 0 -> 12877 bytes | |||
-rw-r--r-- | bubbob/images/60000_0.ppm | bin | 0 -> 13012 bytes | |||
-rw-r--r-- | bubbob/images/70000_0.ppm | bin | 0 -> 13012 bytes | |||
-rw-r--r-- | bubbob/images/big_bubble.ppm | 4 | ||||
-rw-r--r-- | bubbob/images/big_bubble_2.ppm | 4 | ||||
-rw-r--r-- | bubbob/images/black.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/blitzy.ppm | bin | 0 -> 33845 bytes | |||
-rw-r--r-- | bubbob/images/blitzy_angry.ppm | bin | 0 -> 12341 bytes | |||
-rw-r--r-- | bubbob/images/blitzy_shot.ppm | bin | 0 -> 1588 bytes | |||
-rw-r--r-- | bubbob/images/bonus_0.ppm | bin | 0 -> 17339 bytes | |||
-rw-r--r-- | bubbob/images/bonus_1.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_10.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_11.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_12.ppm | bin | 0 -> 12341 bytes | |||
-rw-r--r-- | bubbob/images/bonus_2.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_3.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_4.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_5.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_6.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_7.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_8.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bonus_9.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/bubble.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/buildcolors.py | 324 | ||||
-rw-r--r-- | bubbob/images/butterfly.ppm | bin | 0 -> 27701 bytes | |||
-rw-r--r-- | bubbob/images/cream_pie_big.ppm | bin | 0 -> 24352 bytes | |||
-rw-r--r-- | bubbob/images/diamond_big_blue.ppm | bin | 0 -> 24352 bytes | |||
-rw-r--r-- | bubbob/images/diamond_big_purple.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/diamond_big_red.ppm | bin | 0 -> 24352 bytes | |||
-rw-r--r-- | bubbob/images/diamond_big_yellow.ppm | bin | 0 -> 24352 bytes | |||
-rw-r--r-- | bubbob/images/digits_0.ppm | bin | 0 -> 7193 bytes | |||
-rw-r--r-- | bubbob/images/door.ppm | bin | 0 -> 3130 bytes | |||
-rw-r--r-- | bubbob/images/dragon_0.ppm | bin | 0 -> 61493 bytes | |||
-rw-r--r-- | bubbob/images/dragon_bubble_0.ppm | bin | 0 -> 49205 bytes | |||
-rw-r--r-- | bubbob/images/extend.ppm | bin | 0 -> 55355 bytes | |||
-rw-r--r-- | bubbob/images/extra1.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/extra2.ppm | bin | 0 -> 6203 bytes | |||
-rw-r--r-- | bubbob/images/extra3.ppm | bin | 0 -> 21518 bytes | |||
-rw-r--r-- | bubbob/images/extra4.ppm | bin | 0 -> 9229 bytes | |||
-rw-r--r-- | bubbob/images/extra5.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/extra6.ppm | bin | 0 -> 11374 bytes | |||
-rw-r--r-- | bubbob/images/extra7.ppm | 13 | ||||
-rw-r--r-- | bubbob/images/extra8.ppm | bin | 0 -> 34259 bytes | |||
-rw-r--r-- | bubbob/images/fire_drop.ppm | bin | 0 -> 444 bytes | |||
-rw-r--r-- | bubbob/images/fire_surface.ppm | bin | 0 -> 3085 bytes | |||
-rw-r--r-- | bubbob/images/fish_0.ppm | bin | 0 -> 21557 bytes | |||
-rw-r--r-- | bubbob/images/flappy.ppm | bin | 0 -> 46133 bytes | |||
-rw-r--r-- | bubbob/images/flapy_angry.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/game_over_0.ppm | bin | 0 -> 6196 bytes | |||
-rw-r--r-- | bubbob/images/ghost.ppm | 6 | ||||
-rw-r--r-- | bubbob/images/ghosty.ppm | bin | 0 -> 46133 bytes | |||
-rw-r--r-- | bubbob/images/ghosty_angry.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/glue.ppm | 4 | ||||
-rw-r--r-- | bubbob/images/gramy.ppm | bin | 0 -> 58421 bytes | |||
-rw-r--r-- | bubbob/images/gramy_angry.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/hat1.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/hat2.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/hat5.ppm | 5 | ||||
-rw-r--r-- | bubbob/images/ice_cyan_big.ppm | bin | 0 -> 24358 bytes | |||
-rw-r--r-- | bubbob/images/ice_violet_big.ppm | bin | 0 -> 24358 bytes | |||
-rw-r--r-- | bubbob/images/keys.ppm | bin | 0 -> 49167 bytes | |||
-rw-r--r-- | bubbob/images/level_digits.ppm | bin | 0 -> 25253 bytes | |||
-rw-r--r-- | bubbob/images/lightning_large.ppm | bin | 0 -> 17833 bytes | |||
-rw-r--r-- | bubbob/images/lightning_small.ppm | bin | 0 -> 1741 bytes | |||
-rw-r--r-- | bubbob/images/monky.ppm | bin | 0 -> 61493 bytes | |||
-rw-r--r-- | bubbob/images/monky_angry.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/nasty.ppm | bin | 0 -> 46133 bytes | |||
-rw-r--r-- | bubbob/images/nasty_angry.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/orcy.ppm | bin | 0 -> 70709 bytes | |||
-rw-r--r-- | bubbob/images/orcy_angry.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/palettes.dat | bin | 0 -> 28800 bytes | |||
-rw-r--r-- | bubbob/images/pastec_big.ppm | 44 | ||||
-rw-r--r-- | bubbob/images/peach_big.ppm | bin | 0 -> 24352 bytes | |||
-rw-r--r-- | bubbob/images/point_0.ppm | bin | 0 -> 86453 bytes | |||
-rw-r--r-- | bubbob/images/red_Hurry_up.ppm | bin | 0 -> 9851 bytes | |||
-rw-r--r-- | bubbob/images/sheep.ppm | bin | 0 -> 27707 bytes | |||
-rw-r--r-- | bubbob/images/shot.ppm | bin | 0 -> 24629 bytes | |||
-rw-r--r-- | bubbob/images/spinning_drop.ppm | bin | 0 -> 4660 bytes | |||
-rw-r--r-- | bubbob/images/springy.ppm | bin | 0 -> 58421 bytes | |||
-rw-r--r-- | bubbob/images/springy_angry.ppm | bin | 0 -> 36917 bytes | |||
-rw-r--r-- | bubbob/images/star_large.ppm | bin | 0 -> 36923 bytes | |||
-rw-r--r-- | bubbob/images/sugar_pie_big.ppm | bin | 0 -> 24358 bytes | |||
-rw-r--r-- | bubbob/images/water_flow.ppm | 4 | ||||
-rw-r--r-- | bubbob/images/water_still.ppm | 4 | ||||
-rw-r--r-- | bubbob/images/water_surface.ppm | 4 | ||||
-rw-r--r-- | bubbob/images/yellow_Hurry_up.ppm | bin | 0 -> 9851 bytes | |||
-rw-r--r-- | bubbob/levels/Arena.bin | bin | 0 -> 164736 bytes | |||
-rw-r--r-- | bubbob/levels/CompactLevels.py | 1902 | ||||
-rw-r--r-- | bubbob/levels/HouseOfFun.bin | bin | 0 -> 196096 bytes | |||
-rw-r--r-- | bubbob/levels/Levels.bin | bin | 0 -> 178432 bytes | |||
-rw-r--r-- | bubbob/levels/LostLevels.bin | bin | 0 -> 178432 bytes | |||
-rw-r--r-- | bubbob/levels/README.txt | 23 | ||||
-rw-r--r-- | bubbob/levels/RandomLevels.py | 362 | ||||
-rw-r--r-- | bubbob/levels/rnglevel | 1276 | ||||
-rw-r--r-- | bubbob/levels/scratch.py | 2301 | ||||
-rw-r--r-- | bubbob/macbinary.py | 260 | ||||
-rw-r--r-- | bubbob/mnstrmap.py | 397 | ||||
-rw-r--r-- | bubbob/monsters.py | 937 | ||||
-rw-r--r-- | bubbob/music/Snd1-8.wav | bin | 0 -> 532268 bytes | |||
-rw-r--r-- | bubbob/music/Snd2-8.wav | bin | 0 -> 10778156 bytes | |||
-rw-r--r-- | bubbob/music/Snd3-8.wav | bin | 0 -> 1890476 bytes | |||
-rw-r--r-- | bubbob/music/Snd4-8.wav | bin | 0 -> 339884 bytes | |||
-rw-r--r-- | bubbob/music/Snd5-8.wav | bin | 0 -> 503468 bytes | |||
-rw-r--r-- | bubbob/music/Snd6-8.wav | bin | 0 -> 9895724 bytes | |||
-rw-r--r-- | bubbob/patmap.py | 177 | ||||
-rw-r--r-- | bubbob/player.py | 1213 | ||||
-rw-r--r-- | bubbob/ranking.py | 391 | ||||
-rw-r--r-- | bubbob/save_rnglevel.py | 125 | ||||
-rwxr-xr-x | bubbob/setup.py | 22 | ||||
-rw-r--r-- | bubbob/sounds/die.wav | bin | 0 -> 29758 bytes | |||
-rw-r--r-- | bubbob/sounds/extra.wav | bin | 0 -> 3084 bytes | |||
-rw-r--r-- | bubbob/sounds/extralife.wav | bin | 0 -> 20624 bytes | |||
-rw-r--r-- | bubbob/sounds/fruit.wav | bin | 0 -> 3300 bytes | |||
-rw-r--r-- | bubbob/sounds/hell.wav | bin | 0 -> 41036 bytes | |||
-rw-r--r-- | bubbob/sounds/hurry.wav | bin | 0 -> 19536 bytes | |||
-rw-r--r-- | bubbob/sounds/jump.wav | bin | 0 -> 5516 bytes | |||
-rw-r--r-- | bubbob/sounds/letsgo.wav | bin | 0 -> 19007 bytes | |||
-rw-r--r-- | bubbob/sounds/pop.wav | bin | 0 -> 2837 bytes | |||
-rw-r--r-- | bubbob/sounds/shh.wav | bin | 0 -> 18272 bytes | |||
-rw-r--r-- | bubbob/sounds/yippee.wav | bin | 0 -> 6305 bytes | |||
-rw-r--r-- | bubbob/sprmap.py | 533 | ||||
-rw-r--r-- | bubbob/statesaver.c | 610 | ||||
-rw-r--r-- | bubbob/statesaver.py | 135 | ||||
-rwxr-xr-x | bubbob/statesaver.so | bin | 0 -> 62672 bytes | |||
-rw-r--r-- | bubbob/test_rnglevel.py | 64 | ||||
-rw-r--r-- | bubbob/test_statesaver.py | 127 | ||||
-rw-r--r-- | bubbob/tmp/pat00.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat01.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat02.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat03.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat04.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat05.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat06.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat07.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat08.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat09.ppm | bin | 0 -> 17294 bytes | |||
-rw-r--r-- | bubbob/tmp/pat10.ppm | bin | 0 -> 6925 bytes | |||
-rw-r--r-- | bubbob/tmp/pat11.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat12.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat13.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat14.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat15.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat16.ppm | 4 | ||||
-rw-r--r-- | bubbob/tmp/pat17.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat18.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat19.ppm | bin | 0 -> 30734 bytes | |||
-rw-r--r-- | bubbob/tmp/pat20.ppm | bin | 0 -> 7693 bytes |
204 files changed, 20607 insertions, 0 deletions
diff --git a/bubbob/.cvsignore b/bubbob/.cvsignore new file mode 100644 index 0000000..687980a --- /dev/null +++ b/bubbob/.cvsignore @@ -0,0 +1,4 @@ +*.py[co] +build +*.so +*.pyd diff --git a/bubbob/Makefile b/bubbob/Makefile new file mode 100644 index 0000000..ab56103 --- /dev/null +++ b/bubbob/Makefile @@ -0,0 +1,2 @@ +statesaver.so: statesaver.c + python setup.py build_ext -i diff --git a/bubbob/bb.py b/bubbob/bb.py new file mode 100755 index 0000000..c0ab351 --- /dev/null +++ b/bubbob/bb.py @@ -0,0 +1,345 @@ +#! /usr/bin/env python + +from __future__ import generators + +# __________ +import os, sys +if __name__ == '__main__': + LOCALDIR = sys.argv[0] +else: + LOCALDIR = __file__ +try: + LOCALDIR = os.readlink(LOCALDIR) +except: + pass +LOCALDIR = os.path.dirname(os.path.abspath(LOCALDIR)) +# ---------- + +import random, time + +sys.path.insert(0, os.path.join(os.path.dirname(LOCALDIR), 'common')) +sys.path.insert(0, LOCALDIR) +import gamesrv + +PROFILE = 0 + + +class BubBobGame(gamesrv.Game): + + FnDesc = "Bub & Bob" + FnBasePath = "bubbob" + Quiet = 0 + End = 0 + + def __init__(self, levelfile, + beginboard = 1, + stepboard = 1, + finalboard = 100, + limitlives = None, + extralife = 50000, + lifegainlimit = None, + autoreset = 0, + metaserver = 0, + monsters = 0): + gamesrv.Game.__init__(self) + self.game_reset_gen = None + self.levelfile = levelfile + self.beginboard = beginboard + self.finalboard = finalboard + self.stepboard = stepboard + self.limitlives = limitlives + self.lifegainlimit = lifegainlimit + self.extralife = extralife + self.autoreset = autoreset + self.metaserver = metaserver + self.f_monsters = monsters + self.updatemetaserver() + levelsname, ext = os.path.splitext(os.path.basename(levelfile)) + self.FnDesc = BubBobGame.FnDesc + ' ' + levelsname + self.reset() + self.openserver() + + def openboard(self, num=None): + self.End = 0 + if num is None: + num = self.beginboard-1 + import boards + boards.loadmodules(force=1) + import boards # possibly re-import + self.width = boards.bwidth + 9*boards.CELL + self.height = boards.bheight + boards.curboard = None + boards.BoardGen = [boards.next_board(num)] + self.updatemetaserver() + + def reset(self): + import player + self.openboard() + for p in player.BubPlayer.PlayerList: + p.reset() + + def FnPlayers(self): + from player import BubPlayer + result = {} + for p in BubPlayer.PlayerList: + result[p.pn] = p + return result + + def FnFrame(self): + if self.metaregister: + self.do_updatemetaserver() + frametime = 0.0 + for i in xrange(500): + import boards + for gen in boards.BoardGen[:]: + try: + frametime += gen.next() + except StopIteration: + try: + boards.BoardGen.remove(gen) + except ValueError: + pass + if frametime >= 1.1: + break + else: + # should normally never occur + boards.BoardGen[:] = [boards.next_board()] + frametime = 1.0 + if self.game_reset_gen is None: + if self.End and self.autoreset: + self.game_reset_gen = boards.game_reset() + else: + try: + self.game_reset_gen.next() + except StopIteration: + self.game_reset_gen = None + return frametime * boards.FRAME_TIME + + def FnServerInfo(self, msg): + try: + from images import writestr + writestr(50, 50, msg) + self.sendudpdata() + except: + pass + + def FnExcHandler(self, kbd): + if kbd: + self.FnServerInfo("Server was Ctrl-C'ed!") + else: + self.FnServerInfo('Ooops -- server crash!') + from player import BubPlayer + if kbd and not [p for p in BubPlayer.PlayerList if p.isplaying()]: + return 0 + import traceback + print "-"*60 + traceback.print_exc() + print "-"*60 + if not kbd: + try: + if self.metaserver: + try: + import metaclient + except ImportError: + pass + else: + if metaclient.metaclisrv: + metaclient.metaclisrv.send_traceback() + except Exception, e: + print '! %s: %s' % (e.__class__.__name__, e) + import boards + num = getattr(boards.curboard, 'num', None) + if self.Quiet: + print "Crash recovery! Automatically restarting board %s" % num + import time; time.sleep(2) + else: + print "Correct the problem and leave pdb to restart board %s..."%num + import pdb; pdb.post_mortem(sys.exc_info()[2]) + self.openboard(num) + return 1 + + def FnListBoards(): + import boards + result = [] + for fn in os.listdir('levels'): + base, ext = os.path.splitext(fn) + if ext in ('.py', '.bin'): + result.append((base, os.path.join('levels', fn))) + return result + FnListBoards = staticmethod(FnListBoards) + + def FnExtraDesc(self): + import boards + s = gamesrv.Game.FnExtraDesc(self) + if boards.curboard and self.End != 'gameover': + s = '%s, board %d' % (s, boards.curboard.num+1) + return s + + def do_updatemetaserver(self): + self.metaregister -= 1 + if self.metaregister > 0: + return + if self.metaserver and (self.autoreset or self.End != 'gameover'): + setuppath('metaserver') + import metaclient + metaclient.meta_register(self) + print '.' + else: + try: + import metaclient + except ImportError: + pass + else: + metaclient.meta_unregister(self) + + def updatemetaserver(self): + self.metaregister = 2 + + updateboard = updateplayers = updatemetaserver + + +def setuppath(dirname): + dir = os.path.abspath(os.path.join(LOCALDIR, os.pardir, dirname)) + if not os.path.isdir(dir): + print >> sys.stderr, ( + '../%s: directory not found ("cvs update -d" ?)' % dirname) + sys.exit(1) + if dir not in sys.path: + sys.path.insert(0, dir) + +def parse_cmdline(argv): + # parse command-line + def usage(): + print >> sys.stderr, 'usage:' + print >> sys.stderr, ' python bb.py' +## print >> sys.stderr, ' python bb.py [-w/--webbrowser=no]' +## print >> sys.stderr, 'where:' +## print >> sys.stderr, ' -w --webbrowser=no don''t automatically start web browser' + print >> sys.stderr, 'or:' + print >> sys.stderr, ' python bb.py [level-file.bin] [-m] [-b#] [-s#] [-l#] [-M#]' + print >> sys.stderr, 'with options:' + print >> sys.stderr, ' -m --metaserver register the server on the Metaserver so anyone can join' + print >> sys.stderr, ' -b# --begin # start at board number # (default 1)' + print >> sys.stderr, ' --start # synonym for --begin' + print >> sys.stderr, ' --final # end at board number # (default 100)' + print >> sys.stderr, ' -s# --step # advance board number by steps of # (default 1)' + print >> sys.stderr, ' -l# --lives # limit the number of lives to #' + print >> sys.stderr, ' --extralife # gain extra life every # points' + print >> sys.stderr, ' --limitlives # max # of lives player can gain in one board' + print >> sys.stderr, ' -M# --monsters # multiply the number of monsters by #' + print >> sys.stderr, ' (default between 1.0 and 2.0 depending on # of players)' + print >> sys.stderr, ' -i --infinite restart the server at the end of the game' + print >> sys.stderr, ' --port LISTEN=# set fixed tcp port for game server' + print >> sys.stderr, ' --port HTTP=# set fixed tcp port for http server' + print >> sys.stderr, ' -h --help display this text' + #print >> sys.stderr, ' -rxxx record the game in file xxx' + sys.exit(1) + + try: + from getopt import gnu_getopt as getopt + except ImportError: + from getopt import getopt + from getopt import error + try: + opts, args = getopt(argv, 'mb:s:l:M:ih', + ['metaserver', 'start=', 'step=', + 'lives=', 'monsters=', 'infinite', 'help', + 'extralife=', 'limitlives=', 'final=', + 'saveurlto=', 'quiet', 'port=', 'makeimages']) + except error, e: + print >> sys.stderr, 'bb.py: %s' % str(e) + print >> sys.stderr + usage() + + options = {} + #webbrowser = 1 + save_url_to = None + quiet = 0 + for key, value in opts: + if key in ('-m', '--metaserver'): + options['metaserver'] = 1 + elif key in ('-b', '--start', '--begin'): + options['beginboard'] = int(value) + elif key in ('-s', '--step'): + options['stepboard'] = int(value) + elif key in ('-l', '--lives'): + options['limitlives'] = int(value) + elif key in ('--limitlives'): + options['lifegainlimit'] = int(value) + elif key in ('--final'): + options['finalboard'] = int(value) + if options['finalboard'] < options['beginboard']: + print >> sys.stderr, 'bb.py: final board value must be larger than begin board.' + sys.exit(1) + elif key in ('--extralife'): + options['extralife'] = int(value) + elif key in ('-M', '--monsters'): + options['monsters'] = float(value) + elif key in ('-i', '--infinite'): + options['autoreset'] = 1 + elif key in ('-h', '--help'): + usage() + elif key == '--saveurlto': + save_url_to = value + elif key == '--quiet': + quiet = 1 + elif key == '--port': + portname, portvalue = value.split('=') + portvalue = int(portvalue) + import msgstruct + msgstruct.PORTS[portname] = portvalue + elif key == '--makeimages': + import images + sys.exit(0) + #elif key in ('-w', '--webbrowser'): + # webbrowser = value.startswith('y') + if args: + if len(args) > 1: + print >> sys.stderr, 'bb.py: multiple level files specified' + sys.exit(1) + levelfile = os.path.abspath(args[0]) + os.chdir(LOCALDIR) + BubBobGame(levelfile, **options) + else: + if options: + print >> sys.stderr, 'bb.py: command-line options ignored' + start_metaserver(save_url_to, quiet) + +def start_metaserver(save_url_to, quiet): + os.chdir(LOCALDIR) + setuppath('http2') + import httppages + httppages.main(BubBobGame, save_url_to, quiet) + + +def setup(): + keybmp = gamesrv.getbitmap(os.path.join('images', 'keys.ppm')) + def keybmplist(x): + return [keybmp.geticon(x, y, 32, 32) for y in range(0, 128, 32)] + BubBobGame.FnKeys = [ + ("right", keybmplist(0), "kRight"), + ("left", keybmplist(32), "kLeft"), + ("jump", keybmplist(64), "kJump"), + ("fire", keybmplist(96), "kFire"), + ("-right", [], "kmRight"), + ("-left", [], "kmLeft"), + ("-jump", [], "kmJump"), + ("-fire", [], "kmFire"), + ] + +setup() + +def main(): + parse_cmdline(sys.argv[1:]) + if not PROFILE: + gamesrv.mainloop() + else: + import profile + prof = profile.Profile() + try: + prof = prof.run('gamesrv.mainloop()') + finally: + prof.dump_stats('profbb') + +if __name__ == '__main__': + main() diff --git a/bubbob/binboards.py b/bubbob/binboards.py new file mode 100644 index 0000000..af878ff --- /dev/null +++ b/bubbob/binboards.py @@ -0,0 +1,122 @@ +import macbinary +import boards +import gamesrv +from mnstrmap import Nasty, Monky, Ghosty, Flappy +from mnstrmap import Springy, Orcy, Gramy, Blitzy +from images import KEYCOL + +keycol = (KEYCOL & 0xFF, + (KEYCOL>>8) & 0xFF, + (KEYCOL>>16) & 0xFF) + + +def meancolor(img): + r1 = g1 = b1 = 0 + count = float(len(img) * len(img[0])) + for line in img: + for r, g, b in line: + r1 += r + g1 += g + b1 += b + return r1/count, g1/count, b1/count + +def addshadow(img, (r1, g1, b1), depth=8): + w = len(img[0]) + h = len(img) + pad = depth * [keycol] + result = [line + pad for line in img] + [ + (w+depth) * [keycol] for d in range(depth)] + for d in range(depth): + f = 1.0 - float(d)/depth + color = (r1 * f, g1 * f, b1 * f) + for i in range(w): + result[h+d][1+d+i] = color + for i in range(h): + result[1+d+i][w+d] = color + return result + +def addrshadow(img, (r1, g1, b1), depth=8): + w = len(img[0]) + h = len(img) + pad = depth * [keycol] + result = [line + pad for line in img] + for d in range(depth): + f = 1.0 - float(d)/depth + color = (r1 * f, g1 * f, b1 * f) + for i in range(h): + result[i][w+d] = color + return result + + +def load(filename): + print "Loading %s..." % filename + Bin = macbinary.MacBinary(filename) + levels = {} + mnstrlist = [Nasty, Monky, Ghosty, Flappy, + Springy, Orcy, Gramy, Blitzy] + + for key, lvl in Bin['LEVL'].items(): + d = lvl.getlevel(mnstrlist) + class BinBoard(boards.Board): + pass + for key1, value1 in d.items(): + setattr(BinBoard, key1, value1) + levels[key] = BinBoard + + def loader(code, rsrc=Bin['ppat'], cache={}): + try: + return cache[code] + except KeyError: + pass + keycol1 = None + bid = code[0] + result = None + if code[1] == 'l': + # left border wall + img = rsrc[bid + 228].getimage() + color = meancolor(img) + img = [line[:32] for line in img] + result = addrshadow(img, color) + elif code[1] == 'r': + # right border wall + img = rsrc[bid + 228].getimage() + w = len(img[0]) + assert w in (32, 64), bid + if w == 64: + color = meancolor(img) + img = [line[32:64] for line in img] + result = addrshadow(img, color) + else: + # normal wall + dx, dy = code[1:] + img = rsrc[bid + 128].getimage() + w = len(img[0]) + h = len(img) + assert w & 15 == h & 15 == 0, bid + dx *= 16 + dy *= 16 + if dx < w and dy < h: + color = meancolor(img) + img = [line[dx:dx+16] for line in img[dy:dy+16]] + result = addshadow(img, color) + keycol1 = KEYCOL + if result is not None: + w, h, data = macbinary.image2rgb(result) + ppmdata = "P6\n%d %d\n255\n%s" % (w, h, data) + result = gamesrv.newbitmap(ppmdata, keycol1), (0, 0, w, h) + cache[code] = result + return result + + def bin_haspat(code, loader=loader): + try: + return loader(code) is not None + except KeyError: + return 0 + def bin_loadpattern(code, keycol=None, loader=loader): + result = loader(code) + assert result is not None, code + return result + + boards.haspat = bin_haspat + boards.loadpattern = bin_loadpattern + return levels diff --git a/bubbob/boarddef.py b/bubbob/boarddef.py new file mode 100644 index 0000000..168a3e1 --- /dev/null +++ b/bubbob/boarddef.py @@ -0,0 +1,69 @@ +import boards, mnstrmap + +class left: + dir = 1 + def __init__(self, cls): + self.cls = cls + def build(self, x, y): + return self.cls(x=x, y=y-1, dir=self.dir) + +class right(left): + dir = 0 + +LNasty = left(mnstrmap.Nasty) +RNasty = right(mnstrmap.Nasty) + +LMonky = left(mnstrmap.Monky) +RMonky = right(mnstrmap.Monky) + +LGhosty = left(mnstrmap.Ghosty) +RGhosty = right(mnstrmap.Ghosty) + +LFlappy = left(mnstrmap.Flappy) +RFlappy = right(mnstrmap.Flappy) + +LSpringy = left(mnstrmap.Springy) +RSpringy = right(mnstrmap.Springy) + +LOrcy = left(mnstrmap.Orcy) +ROrcy = right(mnstrmap.Orcy) + +LGramy = left(mnstrmap.Gramy) +RGramy = right(mnstrmap.Gramy) + +LBlitzy = left(mnstrmap.Blitzy) +RBlitzy = right(mnstrmap.Blitzy) + + +# Sugar around the Board class +class Level(boards.Board): + + WIND_DELTA = boards.CELL + winds = None + monsters = [] + + def __init__(self, num): + walls = [line for line in self.walls.split('\n') if line] + self.monsters = list(self.monsters) + for y in range(len(walls)): + line = walls[y] + for x in range(len(line)): + c = line[x] + if c != ' ' and c != '#': + deflist = getattr(self, c) + if isinstance(deflist, left): + deflist = (deflist,) + for builder in deflist: + self.monsters.append(builder.build(x,y)) + self.walls = self.walls.replace(c, ' ') + if self.winds is None: + width = len(walls[0]) + height = len(walls) + spaces = " " * (width-6) + lbar = '>'*(width/2-2) + rbar = '<'*(width/2-2) + winds = ['>> ' + spaces + ' <<', + lbar + 'x'*(width-len(lbar)-len(rbar)) + rbar] + winds += ['>>^' + spaces + '^<<'] * (height-2) + self.winds = '\n'.join(winds) + boards.Board.__init__(self, num) diff --git a/bubbob/boards.py b/bubbob/boards.py new file mode 100644 index 0000000..dced6f5 --- /dev/null +++ b/bubbob/boards.py @@ -0,0 +1,1470 @@ +from __future__ import generators +import random, os, sys, math +import gamesrv +import images + +CELL = 16 # this constant is inlined at some places, don't change +HALFCELL = CELL//2 +FRAME_TIME = 0.025 +#DEFAULT_LEVEL_FILE = 'levels/scratch.py' + +BOARD_BKGND = 1 # 0 = black, 1 = darker larger wall tiles + + +class Copyable: + pass # see bonuses.py, class Clock + + +class Board(Copyable): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + WIND_DELTA = HALFCELL + + def __init__(self, num): + # the subclasses should define 'walls', 'winds', 'monsters' + self.walls = walls = [line for line in self.walls.split('\n') if line] + self.winds = winds = [line for line in self.winds.split('\n') if line] + self.num = num + self.width = len(walls[0]) + self.height = len(walls) + for line in walls: + assert len(line) == self.width, "some wall lines are longer than others" + for line in winds: + assert len(line) == self.width, "some wind lines are longer than others" + #assert walls[0] == walls[-1], "first and last lines must be identical" + assert len(winds) == self.height, "wall and wind heights differ" + self.walls_by_pos = {} + self.sprites = {} + if self.top: + testline = self.walls[0] + else: + testline = self.walls[-1] + self.holes = testline.find(' ') >= 0 + self.playingboard = 0 + self.bonuslevel = not self.monsters or (gamesrv.game.finalboard is not None and self.num >= gamesrv.game.finalboard) + self.cleaning_gen_state = 0 + + def set_musics(self, prefix=[]): + if (self.num+1) % 20 < 10: + gamesrv.set_musics(prefix + [images.music_intro], [images.music_game], + reset=0) + else: + gamesrv.set_musics(prefix + [], [images.music_game2], reset=0) + + def writesprites(self, name, xyicolist): + sprlist = self.sprites.setdefault(name, []) + xyicolist = xyicolist[:] + xyicolist.reverse() + for s in sprlist[:]: + if xyicolist: + s.move(*xyicolist.pop()) + else: + s.kill() + sprlist.remove(s) + while xyicolist: + x, y, ico = xyicolist.pop() + sprlist.append(gamesrv.Sprite(ico, x, y)) + + def enter(self, complete=1, inplace=0, fastreenter=False): + global curboard + if inplace: + print "Re -", + print "Entering board", self.num+1 + self.set_musics() + # add board walls + l = self.sprites.setdefault('walls', []) + bl = self.sprites.setdefault('borderwalls', []) + if inplace: + deltay = 0 + else: + deltay = bheight + wnx = wny = 1 + while haspat((self.num, wnx, 0)): + wnx += 1 + while haspat((self.num, 0, wny)): + wny += 1 + self.wnx = wnx + self.wny = wny + + if haspat((self.num, 'l')): + lefticon = patget((self.num, 'l')) + if haspat((self.num, 'r')): + righticon = patget((self.num, 'r')) + else: + righticon = lefticon + xrange = range(2, self.width-2) + else: + xrange = range(self.width) + lefticon = righticon = None + + if BOARD_BKGND == 1: + gl = self.sprites.setdefault('background', []) + xmax = (self.width-2)*CELL + ymax = self.height*CELL + y = -HALFCELL + ystep = 0 + firstextra = 1 + while y < ymax: + x = 2*CELL+HALFCELL + xstep = 0 + while x < xmax: + bitmap, rect = loadpattern((self.num, xstep, ystep), + images.KEYCOL) + bitmap, rect = images.makebkgndpattern(bitmap, rect) + if firstextra: + # special position where a bit of black might show up + x -= rect[2] + xstep = (xstep-1) % wnx + firstextra = 0 + continue + bkgndicon = bitmap.geticon(*rect) + w = gamesrv.Sprite(bkgndicon, x, y + deltay) + gl.append(w) + x += rect[2] + xstep = (xstep+1) % wnx + y += rect[3] + ystep = (ystep+1) % wny + else: + gl = [] + + if lefticon is not None: + for y in range(0, self.height, lefticon.h // CELL): + bl.append(gamesrv.Sprite(lefticon, 0, y*CELL + deltay)) + + for y in range(self.height): + for x in xrange: + c = self.walls[y][x] + if c == '#': + wallicon = patget((self.num, x%wnx, y%wny), images.KEYCOL) + w = gamesrv.Sprite(wallicon, x*CELL, y*CELL + deltay) + l.append(w) + self.walls_by_pos[y,x] = w + + if not l: + # self.sprites['walls'] must not be empty, for putwall + wallicon = patget((self.num, 0, 0), images.KEYCOL) + w = gamesrv.Sprite(wallicon, 0, -wallicon.h) + l.append(w) + + if righticon is not None: + for y in range(0, self.height, lefticon.h // CELL): + bl.append(gamesrv.Sprite(righticon, (self.width-2)*CELL, y*CELL + deltay)) + + while deltay: + dy = -min(deltay, 8) + for w in gl: + w.step(0, dy) + for w in l: + w.step(0, dy) + for w in bl: + w.step(0, dy) + deltay += dy + yield 1 + + if inplace: + for w in images.ActiveSprites: + w.to_front() + + curboard = self + if gamesrv.game: + gamesrv.game.updateboard() + if not complete: + return + # add players + from player import BubPlayer, scoreboard + if not inplace: + random.shuffle(BubPlayer.PlayerList) + scoreboard(1, inplace=inplace) + if not fastreenter: + random.shuffle(BubPlayer.PlayerList) + playing = [] + for p in BubPlayer.PlayerList: + if p.isplaying(): + p.enterboard(playing) + p.zarkon() + playing.append(p) + for d in BubPlayer.DragonList: + d.enter_new_board() + else: + # kill stuff left over from leave(inplace=1) (Big Clock bonus only) + import bonuses + keepme = bonuses.Points + dragons = {} + playing = [] + for p in BubPlayer.PlayerList: + if p.isplaying(): + for d in p.dragons: + if hasattr(d, 'dcap'): + d.dcap['shield'] = 90 + dragons[d] = True + playing.append(p) + for s in images.ActiveSprites[:]: + if isinstance(s, keepme) or s in dragons: + pass + else: + s.kill() + # add monsters + if not self.bonuslevel: + import monsters + f_monsters = gamesrv.game.f_monsters + if f_monsters < 0.1: + f_monsters = max(1.0, min(2.0, (len(playing)-2)/2.2+1.0)) + for mdef in self.monsters: + if not fastreenter: + yield 2 + cls = getattr(monsters, mdef.__class__.__name__) + dir = mdef.dir + i = random.random() + while i < f_monsters: + cls(mdef, dir=dir) + dir = -dir + i += 1.0 + self.playingboard = 1 + + def putwall(self, x, y, w=None): + wallicon = patget((self.num, x%self.wnx, y%self.wny), images.KEYCOL) + if w is None: + w = gamesrv.Sprite(wallicon, 0, bheight) + l = self.sprites['walls'] + w.to_back(l[-1]) + l.append(w) + self.walls_by_pos[y,x] = w + if y >= 0: + line = self.walls[y] + self.walls[y] = line[:x] + '#' + line[x+1:] + + def killwall(self, x, y, kill=1): + w = self.walls_by_pos[y,x] + if kill: + l = self.sprites['walls'] + if len(l) > 1: + l.remove(w) + w.kill() + else: + # self.sprites['walls'] must never be empty + # or putwall will crash! + w.move(0, -bheight) + del self.walls_by_pos[y,x] + line = self.walls[y] + self.walls[y] = line[:x] + ' ' + line[x+1:] + return w + + def reorder_walls(self): + walls_by_pos = self.walls_by_pos + items = [(yx, w1.ico) for yx, w1 in walls_by_pos.items()] + if not items: + return # otherwise self.sprites['walls'] would be emptied + items.sort() + l = self.sprites['walls'] + while len(l) > len(items): + l.pop().kill() + assert len(items) == len(l) + for ((y,x), ico), w2 in zip(items, l): + w2.move(x*CELL, y*CELL, ico) + walls_by_pos[y,x] = w2 + + def leave(self, inplace=0): + global curboard + if not gamesrv.has_loop_music(): + gamesrv.fadeout(1.5) + from player import BubPlayer + for p in BubPlayer.PlayerList: + if p.isplaying(): + p.savecaps() + if BubPlayer.LeaveBonus: + for t in BubPlayer.LeaveBonus: + yield t + BubPlayer.LeaveBonus = None + curboard = None + if inplace: + i = -1 + else: + while images.ActiveSprites: + s = random.choice(images.ActiveSprites) + s.kill() + yield 0.9 + i = 0 + sprites = [] + for l in self.sprites.values(): + sprites += l + self.sprites.clear() + self.walls_by_pos.clear() + random.shuffle(sprites) + for s in sprites: + s.kill() + if i: + i -= 1 + else: + yield 0.32 + i = 3 + if not inplace: + for p in BubPlayer.PlayerList: + if p.isplaying(): + p.zarkoff() + yield 4 + + def clean_gen_state(self): + self.cleaning_gen_state = 1 + while len(BoardGen) > 1: + #yield force_singlegen() + #if 'flood' in self.sprites: + # for s in self.sprites['flood']: + # s.kill() + # del self.sprites['flood'] + yield normal_frame() + self.cleaning_gen_state = 0 + +def bget(x, y): + if 0 <= x < curboard.width: + if y < 0 or y >= curboard.height: + y = 0 + return curboard.walls[y][x] + else: + return '#' + +def wget(x, y): + delta = curboard.WIND_DELTA + x = (x + delta) // 16 + y = (y + delta) // 16 + if 0 <= x < curboard.width: + if y < 0: + y = 0 + elif y >= curboard.height: + y = -1 + return curboard.winds[y][x] + elif x < 0: + return '>' + else: + return '<' + +def onground(x, y): + if y & 15: + return 0 + x0 = (x+5) // 16 + x1 = (x+16) // 16 + x2 = (x+27) // 16 + y0 = y // 16 + 2 + + if x0 < 0 or x2 >= curboard.width: + return 0 + y1 = y0 - 1 + if not (0 < y0 < curboard.height): + if y0 != curboard.height: + y1 = 0 + y0 = 0 + y0 = curboard.walls[y0] + y1 = curboard.walls[y1] + return (' ' == y1[x0] == y1[x1] == y1[x2] and + not (' ' == y0[x0] == y0[x1] == y0[x2])) + #return (' ' == bget(x0,y0-1) == bget(x1,y0-1) == bget(x2,y0-1) and + # not (' ' == bget(x0,y0) == bget(x1,y0) == bget(x2,y0))) + #return (bget(x1,y0-1)==' ' and + # ((bget(x1,y0)=='#') or + # (bget(x0,y0)=='#' and bget(x0,y0-1)==' ') or + # (bget(x2,y0)=='#' and bget(x2,y0-1)==' '))) + +def onground_nobottom(x, y): + return onground(x, y) and y+32 < bheight + +def underground(x, y): + if y % CELL: + return 0 + x0 = (x+5) // CELL + x1 = (x+CELL) // CELL + x2 = (x+2*CELL-5) // CELL + y0 = y // CELL + + if x0 < 0 or x2 >= curboard.width: + return 0 + y1 = y0 - 1 + if not (0 < y0 < curboard.height): + if y0 != curboard.height: + y1 = 0 + y0 = 0 + y0 = curboard.walls[y0] + y1 = curboard.walls[y1] + return (' ' == y0[x0] == y0[x1] == y0[x2] and + not (' ' == y1[x0] == y1[x1] == y1[x2])) + +def x2bounds(x): + if x < 32: + return 32 + elif x > bwidth - 64: + return bwidth - 64 + else: + return x + +def vertical_warp(nx, ny): + if ny >= bheight: + ny -= bheightmod + elif ny < -32: + ny += bheightmod + return nx, ny + +def vertical_warp_sprite(spr): + if spr.y >= bheight: + spr.step(0, -bheightmod) + elif spr.y < -32: + spr.step(0, bheightmod) + +##def vertical_warp(nx, ny): +## if ny >= bheight: +## ny -= bheightmod +## elif ny < -32: +## ny += bheightmod +## else: +## return (nx, ny), 0 +## from player import BubPlayer +## if BubPlayer.Moebius: +## nx = bwidth - 2*CELL - nx +## return (nx, ny), 1 +## else: +## return (nx, ny), 0 + + +MODULES = ['boards', 'bonuses', 'bubbles', 'images', + 'mnstrmap', 'monsters', 'player', 'ranking', + 'binboards', 'macbinary', 'boarddef', + 'ext1', 'ext2', 'ext3', 'ext4', 'ext5', 'ext6', 'ext7'] + +def loadmodules(force=0): + levelfilename = gamesrv.game.levelfile + modulefiles = {None: levelfilename} + for m in MODULES: + if os.path.isfile(m+'.py'): + modulefiles[m] = m+'.py' + elif os.path.isfile(os.path.join(m, '__init__.py')): + modulefiles[m] = os.path.join(m, '__init__.py') + mtimes = {} + for m, mfile in modulefiles.items(): + mtimes[m] = os.stat(mfile).st_mtime + reload = force or (mtimes != getattr(sys, 'ST_MTIMES', None)) + import player + playerlist = player.BubPlayer.PlayerList + if reload: + delete = hasattr(sys, 'ST_MTIMES') + sys.ST_MTIMES = mtimes + if delete: + print "Reloading modules." + for m, mfile in modulefiles.items(): + if m is not None and m in sys.modules: + del sys.modules[m] + + # Clear + clearallsprites() + + import player + for p in playerlist: + player.upgrade(p) + for n in range(len(playerlist), images.MAX): + playerlist.append(player.BubPlayer(n)) + player.BubPlayer.PlayerList = playerlist + if reload: + import boards + from images import haspat, loadpattern + boards.haspat = haspat + boards.loadpattern = loadpattern + del boards.BoardList[:] + if levelfilename.lower().endswith('.py'): + levels = {} + print 'Source level file:', levelfilename + execfile(levelfilename, levels) + if 'GenerateLevels' in levels: + levels = levels['GenerateLevels']() + if isinstance(levels, list): + levels = dict(zip(range(len(levels)), levels)) + else: + import binboards + levels = binboards.load(levelfilename) + boards.register(levels) + return reload + +def clearallsprites(): + gamesrv.clearsprites() + import images + del images.ActiveSprites[:] + images.SpritesByLoc.clear() + +def wait_for_one_player(): + from player import BubPlayer + clearallsprites() + nimages = None + while not [p for p in BubPlayer.PlayerList if p.isplaying()]: + yield 3 + if not nimages: + desc = getattr(gamesrv.game, 'FnDesc', '?') + host, port = getattr(gamesrv.game, 'address', ('?', '?')) + images.writestrlines([ + "Welcome to", + desc.upper(), + "at %s:%s" % (host.lower(), port), + None, + "Click on your Favorite Color's Dragon", + "Choose four keys: Right, Left, Jump, Shoot", + "and Let's Go!", + None, + "Click again for more than one player", + "on the same machine.", + ]) + + from mnstrmap import PlayerBubbles + nimages = [PlayerBubbles.bubble[0], + PlayerBubbles.bubble[1], + PlayerBubbles.bubble[1], + PlayerBubbles.bubble[0], + PlayerBubbles.bubble[2], + PlayerBubbles.bubble[2]] + screenwidth = bwidth + 9*CELL + screenheight = bheight + + def welcomebubbling(self): + fx = self.x + dx = (random.random() - 0.5) * 1.9 + for y in range(self.y-3, -self.ico.h, -3): + fx += dx + self.move(int(fx), y) + yield None + if y == self.ypop: + from mnstrmap import PlayerBubbles + self.setimages(None) + self.gen.append(self.die(PlayerBubbles.explosion)) + self.kill() + + yield 10 + gamesrv.set_musics([], [images.music_game2], reset=0) + + if ((not images.ActiveSprites or random.random() < 0.05678) and + gamesrv.clients): + # make sure the extension's images are loaded too + # NB. this is also needed for import auto-detection + import ext1; import ext2; import ext3 + import ext4; import ext5; import ext6 + import ext7 + + ico = images.sprget(nimages[0]) + s = images.ActiveSprite(ico, + random.randrange(0, screenwidth-ico.w), + screenheight) + s.ypop = random.randrange(-ico.h, screenheight) + s.gen = [welcomebubbling(s)] + s.setimages(s.cyclic(nimages, speed=1)) + if random.random() > 0.4321: + try: + key, (filename, (x, y, w, h)) = random.choice( + images.sprmap.items()) + except: + w = h = 0 + if w == h == 32: + s2 = images.ActiveSprite(images.sprget(key), -32, 0) + s2.gen = [s2.following(s, (s.ico.w-32)//2, (s.ico.h-32)//2)] + s.ypop = None + images.action(images.ActiveSprites[:]) + +def patget(n, keycol=None): + bitmap, rect = loadpattern(n, keycol) + return bitmap.geticon(*rect) + +def get_lives(): + return gamesrv.game.limitlives + +def do_nothing(): + while True: + yield 5 + +BoardList = [] +curboard = None +BoardGen = [do_nothing()] + +def next_board(num=0, complete=1, fastreenter=False): + yield force_singlegen() + set_frametime(1.0) + brd = curboard + inplace = 0 + if brd: + inplace = brd.bonuslevel or fastreenter + num = brd.num + if not inplace: + num += gamesrv.game.stepboard + if num >= len(BoardList): + num = len(BoardList)-1 + if (gamesrv.game.finalboard is not None and num > gamesrv.game.finalboard): + num = gamesrv.game.finalboard + for t in brd.leave(inplace=inplace): + yield t + + # reset global board state + from player import BubPlayer, reset_global_board_state + reset_global_board_state() + if not inplace: + del BubPlayer.MonsterList[:] + # wait for at least one player + for t in wait_for_one_player(): + yield t + # reload modules if changed + if loadmodules(): + import boards + boards.BoardGen = [boards.next_board(num)] + return + + if num < 0: + num = 0 + elif num >= len(BoardList): + num = len(BoardList)-1 + brd = BoardList[num](num) + for t in brd.enter(complete, inplace=inplace, fastreenter=fastreenter): + yield t + + if brd.bonuslevel: + gen = bonus_play + else: + gen = normal_play + BoardGen[0] = gen() + +def set_frametime(ft, privtime=100): + from player import BubPlayer + BubPlayer.BaseFrametime = ft + BubPlayer.PlayersPrivateTime = privtime + images.loadsounds(1.0 / ft) + +def extra_boardgen(gen, at_end=0): + if curboard.playingboard: + if at_end or not BoardGen: + BoardGen.append(gen) + else: + BoardGen.insert(1, gen) + +def replace_boardgen(gen, force=0): + if curboard.playingboard or force: + curboard.playingboard = 0 + BoardGen[0] = gen + +def force_singlegen(): + del BoardGen[1:] + return 0 + +def has_singlegen(): + return len(BoardGen) <= 1 + +def display_hat(p, d): + if p.team == -1 or getattr(d,'isdying',0) or hasattr(d,'no_hat'): + return + try: + bottom_up = d.bottom_up() + except AttributeError: + bottom_up = 0 + try: + image = ('hat', p.team, d.dir, d.hatangle) + except AttributeError: + image = ('hat', p.team) + if bottom_up: + image = 'vflip', image + y = d.y + else: + y = d.y - 16 + ico = images.sprget(image) + if (getattr(d,'hatsprite',None) is None or + not d.hatsprite.alive): + d.hatsprite = images.ActiveSprite(ico, d.x, y) + else: + d.hatsprite.to_front() + d.hatsprite.move(d.x, y, ico) + d.hatsprite.gen = [d.hatsprite.die([None])] + +def normal_frame(): + from player import BubPlayer + BubPlayer.FrameCounter += 1 + + # main generator dispatch loop + images.action(images.ActiveSprites[:]) + + frametime = 10 + for p in BubPlayer.PlayerList: + if p.isplaying(): + frametime = BubPlayer.BaseFrametime + p.zarkon() + for d in p.dragons: + d.to_front() + display_hat(p, d) + d.prefix(p.pn) + if not (BubPlayer.FrameCounter & 31): + gamesrv.compactsprites() + reset = getattr(BubPlayer, 'MultiplyerReset', 0) + if reset and BubPlayer.FrameCounter >= reset: + BubPlayer.MultiplyerReset = 0 + set_frametime(1.0) + return frametime + +def normal_play(): + from player import BubPlayer + import bonuses + framecounter = 0 + bonus_callback = bonuses.start_normal_play() + while BubPlayer.MonsterList: + bonus_callback() + yield normal_frame() + if not BubPlayer.DragonList: + continue + framecounter += 1 + BASE = 500 + if not (framecounter % BASE): + if framecounter == 4*BASE: + from monsters import Monster + from mnstrmap import BigImages + ico = images.sprget(BigImages.hurryup[1]) + s = images.ActiveSprite(ico, (bwidth-ico.w)//2, (bheight-ico.h)//2) + s.setimages(s.die(BigImages.hurryup * 12, 2)) + images.Snd.Hurry.play() + mlist = [s for s in images.ActiveSprites + if (isinstance(s, Monster) and s.regular() and + not s.angry)] + if mlist: + s = random.choice(mlist) + s.angry = [s.genangry()] + s.resetimages() + if framecounter >= 6*BASE: + mlist = [s for s in images.ActiveSprites + if isinstance(s, Monster) and s.regular() and s.angry] + if mlist: + images.Snd.Hell.play() + gamesrv.set_musics([], []) + s = random.choice(mlist) + s.become_ghost() + framecounter = -200 + else: + framecounter = 2*BASE + if framecounter == 0: + curboard.set_musics() + replace_boardgen(last_monster_killed(), 1) + +##def normal_play(): +## # TESTING!! +## from player import BubPlayer +## for p in BubPlayer.PlayerList: +## if not p.icons: +## p.loadicons(p.icons, images.sprget) +## results = {BubPlayer.PlayerList[0]: 100, +## BubPlayer.PlayerList[1]: 200, +## BubPlayer.PlayerList[2]: 300, +## BubPlayer.PlayerList[3]: 400, +## BubPlayer.PlayerList[4]: 100, +## BubPlayer.PlayerList[5]: 200, +## BubPlayer.PlayerList[6]: 300, +## BubPlayer.PlayerList[7]: 400, +## BubPlayer.PlayerList[8]:1000, +## BubPlayer.PlayerList[9]:1000, +## } +## maximum = None +## for t in result_ranking(results, maximum): +## yield t + +def last_monster_killed(end_delay=390, music=None): + from player import BubPlayer + for t in exit_board(music=music): + yield t + if curboard.bonuslevel: + curboard.playingboard = 1 + for t in bonus_play(): + yield t + end_delay -= 1 + if end_delay <= 0: + replace_boardgen(next_board(), 1) + break + else: + for i in range(end_delay): + yield normal_frame() + replace_boardgen(next_board(), 1) + +##def bonus_play(): +## from player import BubPlayer +## import bubbles +## while BubPlayer.LimitScoreColor is None: +## yield normal_frame() +## players = [(p.points, p.pn) for p in BubPlayer.PlayerList +## if p.isplaying()] +## if players: +## players.sort() +## points, BubPlayer.LimitScoreColor = players[-1] +## BubPlayer.LimitScore = ((points + limit) // 100000) * 100000 +## for p in BubPlayer.PlayerList: +## if p.isplaying(): +## p.givepoints(0) # check LimitScore and update scoreboard() +## while not (BubPlayer.BubblesBecome or BubPlayer.MegaBonus): +## if random.random() < 0.06: +## bubbles.newbonusbubble() +## yield normal_frame() +## # special board end +## import monsters +## monsters.argh_em_all() +## replace_boardgen(last_monster_killed(), 1) + +class TimeCounter(Copyable): + def __init__(self, limittime, blink=0): + from player import BubPlayer + self.saved_time = BubPlayer.LimitTime + self.time = limittime / FRAME_TIME + self.prev = None + self.blink = blink + def update(self, t): + from player import BubPlayer, scoreboard + self.time -= t + if self.time < 0.0: + self.time = 0.0 + BubPlayer.LimitTime = self.time * FRAME_TIME + next = int(BubPlayer.LimitTime) + if self.blink and BubPlayer.LimitTime - next >= 0.5: + BubPlayer.LimitTime = next = None + if self.prev != next: + scoreboard(compresslimittime=1) + self.prev = next + def restore(self): + from player import BubPlayer + BubPlayer.LimitTime = self.saved_time + +def bonus_play(): + from player import BubPlayer + import bubbles + BubPlayer.MegaBonus = None + BubPlayer.BubblesBecome = None + Time0 = 5.0 / FRAME_TIME # when to slow down time + tc = TimeCounter(BubPlayer.LimitTime or 180.9) # 3:00 + prev = None + while not (BubPlayer.BubblesBecome or BubPlayer.MegaBonus): + if random.random() < 0.099: + bubbles.newbonusbubble() + t = normal_frame() + tc.update(t) + if tc.time < Time0: + if tc.time <= 0.5: + tc.time = 0.5 + BubPlayer.LimitTime = 0.0 + t *= math.sqrt(Time0 / tc.time) + yield t + if tc.time == 0.5: + gamesrv.game.End = 'gameover' + gamesrv.game.updateboard() + replace_boardgen(game_over(), 1) + return + # special board end + import monsters + monsters.argh_em_all() + replace_boardgen(last_monster_killed(), 1) + +def game_over(): + yield force_singlegen() + from player import scoreboard + import ranking + images.Snd.Extralife.play() + gamesrv.set_musics([], [images.music_potion]) + scoreboard() + for t in ranking.game_over(): + yield t + +def game_reset(): + import time + from player import BubPlayer + t1 = time.time() + while 1: + yield 0 + if BubPlayer.LimitTime and BubPlayer.LimitTime >= 1.0: + # someone else ticking the clock, try again later + return + if abs(time.time() - t1) > 2.0: + break + # anyone playing ? + if not gamesrv.game.End: + return # yes -> cancel game_reset() + # let's tick the clock ! + tc = TimeCounter(60.9, blink=1) # 1:00 + t1 = time.time() + while tc.time: + yield 0 + # anyone playing now ? + if not gamesrv.game.End: + tc.restore() + return # yes -> cancel game_reset() + t = time.time() # use real time + deltat = (t-t1)/FRAME_TIME + if deltat < 1.0: + deltat = 1.0 + elif deltat > 100.0: + deltat = 100.0 + tc.update(deltat) + t1 = t + gamesrv.game.reset() + +##def wasting_play(): +## from player import BubPlayer, scoreboard +## import bubbles +## curboard.wastingplay = {} +## for p in BubPlayer.PlayerList: +## if p.isplaying(): +## p.letters = {} +## p.bonbons = p.points // 50000 +## scoreboard() + +## while len(BubPlayer.DragonList) > 1: +## if random.random() < 0.03: +## bubbles.newbubble(1) +## yield normal_frame() +## for d in BubPlayer.DragonList: +## curboard.wastingplay[d.bubber] = len(curboard.wastingplay) +## for i in range(50): +## yield normal_frame() + +## total = len(curboard.wastingplay) +## results = [(total-n, p) for p, n in curboard.wastingplay.items()] +## results.sort() +## results = [(p, str(n)) for n, p in results] +## for t in display_ranking(results): +## yield t +## # never ending + +def skiplevels(blink, skip): + # (not used any more) + saved = BoardGen[:] + while skip: + skip -= 1 + BoardGen[:] = saved + for i in range(10): # frozen pause + yield 3 + if blink: + blink.step(-bwidth, 0) + yield 3.33 + blink.step(bwidth, 0) + blink = None + for t in next_board(complete=(skip==0)): + yield t + +def exit_board(delay=8, music=None, repeatmusic=[]): + from bubbles import Bubble + from bonuses import RandomBonus, end_normal_play + from player import BubPlayer + from monsters import Monster + end_normal_play() + curboard.playingboard = 0 + actives = images.ActiveSprites[:] + for s in actives: + if ((isinstance(s, Monster) and s.still_playing()) + or isinstance(s, RandomBonus)): + s.kill() + music = music or [] + if BubPlayer.MegaBonus: + music[:1] = [images.music_modern] + if music or repeatmusic: + gamesrv.set_musics(music, repeatmusic) + for i in range(delay): + yield normal_frame() + bubble_outcome = BubPlayer.BubblesBecome or Bubble.pop + for s in actives: + if isinstance(s, Bubble): + bubble_outcome(s) + yield normal_frame() + if BubPlayer.MegaBonus: + BubPlayer.MegaBonus() + +def potion_fill(blist, big=0): + from player import BubPlayer + from bonuses import Bonus + #timeleft = 1680.0 + for t in exit_board(0, music=[images.music_potion]): + #timeleft -= t + yield t + notes = all_notes = [] + y = 1 + while y < 11 or (y < height-2 and (len(all_notes) < 10 or big)): + for x in range(2, width-3, 2): + if ' ' == bget(x,y) == bget(x+1,y) == bget(x,y+1) == bget(x+1,y+1): + b = Bonus(x*CELL, y*CELL, falling=0, *blist[((x+y)//2)%len(blist)]) + b.timeout = (444,666)[big] + all_notes.append(b) + for i in range(2): + t = normal_frame() + #timeleft -= t + yield t + y += 2 + while notes: #and timeleft > 0.0: + notes = [b for b in notes if b.alive] + t = normal_frame() + #timeleft -= t + yield t + for i in range(10): + t = normal_frame() + #timeleft -= t + yield t + results = {} + for b in all_notes: + for d in b.taken_by: + bubber = d.bubber + results[bubber] = results.get(bubber, 0) + 1 + for t in result_ranking(results, len(all_notes)): + yield t + #fadeouttime = 3.33 + #fullsoundframes = bonusframes - 10 - int(fadeouttime / FRAME_TIME) + #for i in range(fullsoundframes): + # yield normal_frame() + #gamesrv.fadeout(fadeouttime) + #for i in range(fullsoundframes, 490): + # yield normal_frame() + +def result_ranking(results, maximum=None, timeleft=200): + import ranking + results = ranking.ranking_picture(results, maximum, timeleft is not None) + if curboard.bonuslevel and timeleft is not None: + play_again = bonus_play() + else: + play_again = None + for t in ranking.display(results, timeleft, play_again): + yield t + if gamesrv.game.End != 'gameover': + gamesrv.set_musics([], []) + replace_boardgen(next_board(), 1) + +def extra_water_flood(): + from mnstrmap import Flood + from monsters import Monster + waves_icons = [images.sprget(n) for n in Flood.waves] + fill_icon = images.sprget(Flood.fill) + bspr = [] + if 'flood' in curboard.sprites: + return # only one flooding at a time + curboard.sprites['flood'] = bspr + waves_sprites = [gamesrv.Sprite(waves_icons[0], x, bheight-CELL) + for x in range(0, bwidth, CELL)] + bspr += waves_sprites + fill_by_line = [] + poplist = [None] + while waves_sprites[0].y > 0: + yield 0 + waves_icons.insert(0, waves_icons.pop()) + for s in waves_sprites: + s.seticon(waves_icons[0]) + yield 0 + sprites = [gamesrv.Sprite(fill_icon, s.x, s.y) for s in waves_sprites] + bspr += sprites + fill_by_line.append(sprites) + for s in waves_sprites: + s.step(0, -16) + for s in images.touching(0, waves_sprites[0].y, bwidth, bheight): + if isinstance(s, Monster): + s.argh(poplist) + while 1: + for i in range(2): + yield 0 + waves_icons.insert(0, waves_icons.pop()) + for s in waves_sprites: + s.seticon(waves_icons[0]) + if not fill_by_line: + break + for s in fill_by_line.pop(): + s.kill() + for s in waves_sprites: + s.step(0, 16) + for s in waves_sprites: + s.kill() + del curboard.sprites['flood'] + +def extra_aquarium(): + from mnstrmap import Flood + from player import BubPlayer + for i in range(200): + if 'flood' not in curboard.sprites: # only one flooding at a time + break + yield 0 + if curboard.cleaning_gen_state: + return + else: + return + curboard.sprites['flood'] = [] + gl = curboard.sprites.setdefault('background', []) + curboard.holes = True # so that random PlainBubbles show up anyway + walls = curboard.sprites['walls'] + seen = {} + + def newsprite(ico, x, y): + s = gamesrv.Sprite(ico, x, y) + s.to_back(walls[0]) + gl.append(s) + return s + + def fishplayers(ymin): + for d in BubPlayer.DragonList: + if d not in seen and d.y >= ymin: + seen[d] = True + d.become_fish() + d.bubber.emotic(d, 4) + + waves_icons = [images.sprget(n) for n in Flood.waves] + fill_icon = images.sprget(Flood.fill) + waves_sprites = [newsprite(waves_icons[0], x, bheight-CELL) + for x in range(2*CELL + HALFCELL, bwidth - 2*CELL, CELL)] + while waves_sprites[0].y > -fill_icon.h: + fishplayers(waves_sprites[0].y) + yield 0 + waves_icons.append(waves_icons.pop(0)) + for s in waves_sprites: + s.seticon(waves_icons[0]) + fishplayers(waves_sprites[0].y) + yield 0 + for s in waves_sprites: + newsprite(fill_icon, s.x, s.y) + for s in waves_sprites: + s.step(0, -16) + for s in waves_sprites: + s.kill() + BubPlayer.SuperFish = True + fishplayers(-sys.maxint) + +def extra_walls_falling(): + walls_by_pos = curboard.walls_by_pos + moves = 1 + while moves and not curboard.cleaning_gen_state: + moves = 0 + for y in range(height-3, -1, -1): + for x in range(2, width-2): + if ((y,x) in walls_by_pos and + (y+1,x) not in walls_by_pos and + (y+2,x) not in walls_by_pos): + y0 = y + while (y0-1,x) in walls_by_pos: + y0 -= 1 + w = curboard.killwall(x, y0, 0) + curboard.putwall(x, y+1, w) + moves = 1 + curboard.reorder_walls() + for y in range(6): + yield 0 + +def single_blocks_falling(xylist): + walls_by_pos = curboard.walls_by_pos + while xylist: + newlist = [] + for x, y in xylist: + if ((y,x) in walls_by_pos and (y+1,x) not in walls_by_pos and + y < curboard.height-1): + newlist.append((x, y+1)) + for x, y in newlist: + w = curboard.killwall(x, y-1, 0) + curboard.putwall(x, y, w) + xylist = newlist + curboard.reorder_walls() + for i in range(7): + yield 0 + +def extra_display_repulse(cx, cy, dlimit=5000, dfactor=1000): + offsets = {} + for s in gamesrv.sprites_by_n.values(): + x, y = s.getdisplaypos() + if x is not None: + dx = x - cx + dy = y - cy + d = dx*dx + dy*dy + 100 + if d <= dlimit: + dx = (dx*dfactor)//d + dy = (dy*dfactor)//d + offsets[s] = dx, dy + s.setdisplaypos(int(x+dx), int(y+dy)) + yield 0 + yield 0 + while offsets: + prevoffsets = offsets + offsets = {} + for s, (dx, dy) in prevoffsets.items(): + if s.alive: + if dx < 0: + dx += max(1, (-dx)//5) + elif dx: + dx -= max(1, dx//5) + if dy < 0: + dy += max(1, (-dy)//5) + elif dy: + dy -= max(1, dy//5) + if dx or dy: + offsets[s] = dx, dy + s.setdisplaypos(int(s.x+dx), int(s.y+dy)) + yield 0 + +def extra_bkgnd_black(cx, cy): + gl = curboard.sprites.get('background') + dist = 0 + while gl: + dist += 17 + dist2 = dist * dist + gl2 = [] + for s in gl: + if (s.x-cx)*(s.x-cx) + (s.y-cy)*(s.y-cy) < dist2: + s.kill() + else: + gl2.append(s) + gl[:] = gl2 + yield 0 + +def extra_light_off(timeout, icocache={}): + for i in range(timeout): + if curboard.cleaning_gen_state: + break + dragons = {} + import player + playerlist = player.BubPlayer.PlayerList + for bubber in playerlist: + for dragon in bubber.dragons: + dragons[dragon] = True + for s in gamesrv.sprites_by_n.values(): + try: + ico = icocache[s.ico, s in dragons] + except KeyError: + ico = images.make_darker(s.ico, s in dragons) + icocache[s.ico, s in dragons] = ico + s.setdisplayicon(ico) + yield 0 + for s in gamesrv.sprites_by_n.values(): + s.setdisplayicon(s.ico) + +def extra_swap_up_down(N=27): + # unregister all walls + walls = curboard.walls_by_pos.items() + walls.sort() + if not walls: + return + curboard.walls_by_pos.clear() + emptyline = '##' + ' '*(width-4) + '##' + curboard.walls = [emptyline] * height + l = curboard.sprites['walls'] + wallicon = l[0].ico + wallpool = l[:] + l[:] = [gamesrv.Sprite(wallicon, 0, -wallicon.h)] + + # force the top half of the walls on front + #for (y,x), w in walls: + # if y*2 < height: + + # show the walls swapping up/down + ycenter = ((height-1)*CELL) // 2 + for i in range(N): + alpha = math.cos((math.pi*(i+1))/N) + ymap = {} + for y in range(height): + ymap[y] = int(alpha*(y*CELL-ycenter)) + ycenter + for (y,x), w in walls: + if y in ymap: + w.move(x*CELL, ymap[y]) + yield 0 + if i == (N+1)//2: + # reorder the wall sprites in the middle of the swap + walls = [((-y,x), w) for (y,x), w in walls] + walls.sort() + for i in range(len(walls)): + (y,x), w = walls[i] + walls[i] = (y,x), wallpool[i] + walls = [((-y,x), w) for (y,x), w in walls] + walls.sort() + # reverse all dragons! + from player import BubPlayer + for dragon in BubPlayer.DragonList: + dragon.dcap['gravity'] *= -1.0 + + # freeze the walls in their new position + i = 0 + for (y,x), w in walls: + y = height-1 - y + if 0 <= y < height and (y,x) not in curboard.walls_by_pos: + w = wallpool[i] + i += 1 + curboard.putwall(x, y, w) + l[:0] = wallpool[:i] + for w in wallpool[i:]: + w.kill() + curboard.reorder_walls() + +def extra_catch_all_monsters(dragons=[], everything=False): + from monsters import Monster + from bubbles import BigBubbleCatcher + from bonuses import Bonus, BonusMaker + if not dragons: + from player import BubPlayer + dragons = BubPlayer.DragonList + i = 0 + give_up = 33 + for s in images.ActiveSprites[:]: + if curboard.cleaning_gen_state: + break + while not dragons: + give_up -= 1 + if give_up == 0: + return + yield 0 + if not s.alive or not s.touchable: + continue + if isinstance(s, Bonus): + ok = s.bubblable + elif isinstance(s, BonusMaker): + ok = everything + else: + ok = isinstance(s, Monster) + if ok: + dragon = dragons[i%len(dragons)] + BigBubbleCatcher(dragon, s, 542 + 17*i) + i += 1 + yield 0 + yield 0 + +def extra_make_random_level(cx=None, cy=None, repeat_delay=200): + from bonuses import DustStar + # generate any random level + localdir = os.path.dirname(__file__) + filename = os.path.join(localdir, 'levels', 'RandomLevels.py') + d = {} + execfile(filename, d) + Level = d['GenerateSingleLevel'](curboard.width, curboard.height) + lvl = Level(curboard.num) + walllist = [] + if cx is None: cx = bwidth // 2 + if cy is None: cy = bheight // 2 + for y in range(curboard.height): + dy = cy - (y*16+8) + dy2 = dy*dy*0.75 + for x in range(2, curboard.width-2): + dx = cx - (x*16+8) + d2 = dx*dx + dy2 + walllist.append((d2, x, y)) + walllist.sort() + + # dynamically replace the current level's walls with the new level's + dist = 0 + speedf = 15.0 + added = 0 + for d2, x, y in walllist: + while d2 > dist*dist: + if added: + curboard.reorder_walls() + added = 0 + yield 0 + dist += 4.1 + speedf *= 0.99 + if curboard.walls[y][x] == ' ': + if lvl.walls[y][x] == ' ': + continue + else: + curboard.putwall(x, y) + added = 1 + big = 1 + else: + if lvl.walls[y][x] == ' ': + curboard.killwall(x, y) + big = 1 + else: + big = 0 + sx = x*16 + sy = y*16 + DustStar(sx - 8*big, + sy - 8*big, + speedf * (sx-cx) / dist, + speedf * (sy-cy) / dist, + big=big) + # patch the winds too + curboard.winds = lvl.winds + curboard.reorder_walls() + yield 0 + # wait a bit and restart + if repeat_delay < 1000: + for i in range(repeat_delay): + yield 0 + if curboard.cleaning_gen_state: + return + extra_boardgen(extra_make_random_level( + repeat_delay = repeat_delay * 3 // 2)) + +def extra_bubbles(timeout): + from bubbles import newforcedbubble + falloff = 0.25 + L = math.log(0.965) # same speed falloff rate as in throwing_bubble() + cx = (bwidth - CELL) // 2 + cy = (bheight - CELL) // 2 + for i in range(timeout): + if curboard.cleaning_gen_state: + return + if random.random() < falloff: + bubble = newforcedbubble() + if bubble: + tx = random.randrange(CELL, bwidth - 2*CELL) - cx + ty = random.randrange(CELL, bheight - 2*CELL) - cy + if ty == 0: + ty = 1 + dist = math.sqrt(tx * tx + ty * ty) + acos = tx / dist + asin = ty / dist + hspeed = 4 - dist * L + bubble.thrown_bubble(cx, cy, hspeed, (acos, asin)) + falloff *= 0.998 + yield 0 + + +def initsubgame(music, displaypoints): + from player import BubPlayer, scoreboard + for t in exit_board(0, repeatmusic=[music]): + yield t + BubPlayer.DisplayPoints = displaypoints + scoreboard() + for t in curboard.clean_gen_state(): + yield t + +def register(dict): + global width, height, bwidth, bheight, bheightmod + items = dict.items() + items.sort() + for name, board in items: + try: + if not issubclass(board, Board) or board is Board: + continue + except TypeError: + continue + BoardList.append(board) + # check sizes + assert BoardList, "board file does not define any board" + B = BoardList[0] + try: + test = B(-1) + width = test.width + height = test.height + for B in BoardList[1:]: + test = B(-1) + assert test.width == width, "some boards have a different width" + assert test.height == height, "some boards have a different height" + except Exception, e: + print 'Caught "%s" in level "%s":' % (e, B.__name__) + raise e + bwidth = width*CELL + bheight = height*CELL + bheightmod = (height+2)*CELL + +##def define_boards(filename): +## global curboard, boards, width, height, bwidth, bheight, bheightmod +## curboard = None +## boards = [] +## def board((wallfile, wallrect), shape): +## lines = shape.strip().split('\n') +## bmp = gamesrv.getbitmap(wallfile) +## wallicon = bmp.geticon(*wallrect) +## boards.append(Board(lines, wallicon)) +## d = {'board': board} +## execfile(filename, d) +## assert boards, "board file does not define any board" +## width = boards[0].width +## height = boards[0].height +## for b in boards[1:]: +## assert b.width == width, "some boards have a different width" +## assert b.height == height, "some boards have a different height" +## bwidth = width*CELL +## bheight = height*CELL +## bheightmod = len(boards[0].lines)*CELL + + +#try: +# import psyco +#except ImportError: +# pass +#else: +# psyco.bind(normal_frame) diff --git a/bubbob/bonuses.py b/bubbob/bonuses.py new file mode 100644 index 0000000..6873188 --- /dev/null +++ b/bubbob/bonuses.py @@ -0,0 +1,2504 @@ +from __future__ import generators +import random, os, math +import random as random_module +import gamesrv +import images +import boards +from boards import * +from images import ActiveSprite +from mnstrmap import GreenAndBlue, Bonuses, Diamonds, Stars, BigImages +from mnstrmap import PotionBonuses, Fire +from player import BubPlayer + + +questionmarklist = ['questionmark3', + 'questionmark4', + 'questionmark5', + 'questionmark4', + 'questionmark3', + 'questionmark2', + 'questionmark1', + 'questionmark2'] + +class Bonus(ActiveSprite): + bubblable = 1 + touchable = 1 + points = 750 + timeout = 250 + sound = 'Fruit' + endaction = None + multiply = 1 + killgens = 1 + + def __init__(self, x, y, nimage=None, points=None, falling=1): + if nimage is not None: + self.nimage = nimage + if points is not None: + self.points = points + ActiveSprite.__init__(self, images.sprget(self.nimage), x, y) + self.taken_by = [] + self.gen.append(self.timeouter()) + if falling: + self.gen.append(self.faller()) + + def buildoutcome(self): + return (self.__class__,) + + def faller(self): + while self.y < boards.bheight: + if onground_nobottom(self.x, self.y): + yield None + yield None + else: + self.move(self.x, (self.y+4) & ~3) + yield None + self.kill() + + def timeouter(self): + for i in range(self.timeout): + yield None + if self.timeout: + self.kill() + + def touched(self, dragon): + dx, dy, dw, dh = dragon.x, dragon.y, dragon.ico.w, dragon.ico.h + if (dx + dw > self.x + 10 and + dy + dh > self.y + 8 and + self.x + self.ico.w > dx + 10 and + self.y + self.ico.h > dy + 10): + self.reallytouched(dragon) + + def reallytouched(self, dragon): + if not self.taken_by: + if self.killgens: + self.gen = [] + self.gen.append(self.taking()) + sound = self.sound + if sound: + if isinstance(sound, str): + sound = getattr(images.Snd, sound) + self.play(sound) + if dragon not in self.taken_by: + self.taken_by.append(dragon) + if isinstance(self, (RandomBonus, MonsterBonus)): + s_bonus = dragon.bubber.stats.setdefault('bonus', {}) + s_bonus[self.nimage] = s_bonus.get(self.nimage, 0) + 1 + + def taking(self, follow_dragons=0, delay=1): + from player import Dragon + for t in range(delay): + yield None # time to be taken by several dragons + if self.points: + for p in self.taken_by: + if follow_dragons and p.alive: + s = p + else: + s = self + points(s.x + s.ico.w//2, s.y + s.ico.h//2 - CELL, p, self.points) + dragons = [d for d in self.taken_by if isinstance(d, Dragon)] + if self.taken1(dragons) != -1: + self.kill() + + def taken1(self, dragons): + for d in dragons * self.multiply: + if d.alive: + self.taken(d) + + def taken(self, dragon): + pass + + def in_bubble(self, bubble): + self.untouchable() + bubble.move(self.x, self.y) + bubble.to_front() + self.to_front() + self.gen = [self.bubbling(bubble, self.ico)] + self.move(bubble.x+8, bubble.y+8, images.sprget('questionmark3')) + self.setimages(self.cyclic(questionmarklist, 2)) + + def bubbling(self, bubble, ico): + while not hasattr(bubble, 'poplist'): + self.move(bubble.x+8, bubble.y+8) + yield None + if bubble.poplist is not None: + dragon = bubble.poplist[0] + if dragon is not None: + self.play(images.Snd.Yippee) + if dragon not in self.taken_by: + self.taken_by.append(dragon) + if self.points > 10: + dragon.bubber.givepoints(self.points - 10) + pn = dragon.bubber.pn + if self.points in GreenAndBlue.points[pn]: + Points(bubble.x + bubble.ico.w//2, bubble.y, pn, self.points) + self.taken1(BubPlayer.DragonList) + p = Parabolic(ico, bubble.x, bubble.y) + p.gen.append(p.moving(-1.0)) + self.kill() + + def is_on_ground(self): + return onground(self.x, self.y) + + +def points(x, y, dragon, points): + dragon.bubber.givepoints(abs(points)) + pn = dragon.bubber.pn + if points in GreenAndBlue.points[pn]: + Points(x, y, pn, points) + +class Points(ActiveSprite): + + def __init__(self, x, y, pn, points): + ico = images.sprget(GreenAndBlue.points[pn][points]) + ActiveSprite.__init__(self, ico, x - ico.w//2, max(8, y)) + self.nooverlap = 1 + self.gen.append(self.raiser()) + + def raiser(self): + wait = 0 + for s in images.ActiveSprites: + if s is self: + break + if (isinstance(s, Points) and s.nooverlap and + abs(self.x-s.x)<self.ico.w*2//3 and + abs(self.y-s.y)<self.ico.h): + wait += 5 + for t in range(wait): + yield None + for i in range(25): + if i == 7: + self.nooverlap = 0 + self.step(0, -2) + yield None + if self.y <= 0: + break + for i in range(20): + yield None + self.kill() + + +class Parabolic(ActiveSprite): + fallstraight = 0 + fallspeed = 4 + + def moving(self, y_amplitude = -8.0): + bottom_up = self.fallspeed < 0 + dxy = [(random.random()-0.5) * 15.0, + (random.random()+0.5) * y_amplitude * (1,-1)[bottom_up]] + if bottom_up: + kw = {'gravity': -0.3} + else: + kw = {} + for n in self.parabolic(dxy, self.fallstraight, **kw): + progress = self.parabole_progress = dxy[1] * (1,-1)[bottom_up] + yield n + if progress >= 4.0 and self.fallstraight: + del self.parabole_progress + self.gen.append(self.falling()) + return + self.kill() + + def falling(self): + nx, ny = vertical_warp(self.x, self.y & ~3) + if self.fallspeed < 0: + groundtest = underground + else: + groundtest = onground + while not groundtest(nx, ny): + ny += self.fallspeed + nx, ny1 = vertical_warp(nx, ny) + if ny1 != ny: + ny = ny1 + self.wrapped_around() + self.move(nx, ny) + yield None + self.move(nx, ny) + self.build() + self.kill() + + def killmonsters(self, poplist): + from monsters import Monster + while 1: + for s in self.touching(0): + if isinstance(s, Monster): + s.argh(poplist) + yield None + + def build(self): + pass + + def wrapped_around(self): + pass + + +class Parabolic2(Parabolic): + points = 0 + + def __init__(self, x, y, imglist, imgspeed=3, onplace=0, y_amplitude=-8.0): + Parabolic.__init__(self, images.sprget(imglist[0]), x, y) + if onplace: + self.gen.append(self.falling()) + else: + self.gen.append(self.moving(y_amplitude)) + if len(imglist) > 1: + self.setimages(self.cyclic(imglist, imgspeed)) + + def touched(self, dragon, rect=None): + if self.points: + points(self.x + self.ico.w/2, self.y + self.ico.h/2 - CELL, + dragon, self.points) + self.kill() + + +class BonusMaker(Parabolic2): + fallstraight = 1 + touchable = 1 + + def __init__(self, x, y, imglist, imgspeed=3, onplace=0, outcome=None): + assert outcome + self.outcome = outcome + if outcome == (Flower2,): + self.fallspeed = -self.fallspeed + Parabolic2.__init__(self, x, y, imglist, imgspeed, onplace) + + def falling(self): + cls = self.outcome[0] + if issubclass(cls, Megabonus): + self.build() + return self.die([]) + else: + return Parabolic2.falling(self) + + def wrapped_around(self): + cls = self.outcome[0] + if issubclass(cls, RandomBonus) and not boards.curboard.playingboard: + self.kill() + + def build(self): + cls = self.outcome[0] + args = self.outcome[1:] + if issubclass(cls, RandomBonus) and not boards.curboard.playingboard: + return None + else: + return cls(self.x, self.y, *args) + + def touched(self, dragon, rect=None): + pass + + def in_bubble(self, bubble): + bonus = self.build() + self.kill() + if bonus: + bonus.in_bubble(bubble) + return bonus + +class BonusMakerExtraStar(ActiveSprite): + + def __init__(self, x, y, sx, sy, colorname): + self.sx = sx + self.sy = sy + imglist = [('smstar', colorname, k) for k in range(2)] + ActiveSprite.__init__(self, images.sprget(imglist[-1]), + x + HALFCELL, y + HALFCELL) + self.setimages(self.cyclic(imglist, speed=2)) + + def follow_bonusmaker(self, bm): + for t in range(4): + yield None + if hasattr(bm, 'parabole_progress'): + break + else: + self.kill() + return + start = bm.parabole_progress + if start < 3.9: + while bm.alive and hasattr(bm, 'parabole_progress'): + f = (bm.parabole_progress-start) / (4.0-start) + self.move(bm.x + HALFCELL + int(f*self.sx), + bm.y + HALFCELL + int(f*self.sy)) + yield None + self.kill() + + +class MonsterBonus(Bonus): + + def __init__(self, x, y, multiple, forceimg=0): + self.level = multiple + if multiple >= len(Bonuses.monster_bonuses): + multiple = len(Bonuses.monster_bonuses) - 1 + img, pts = Bonuses.monster_bonuses[multiple] + Bonus.__init__(self, x, y, forceimg or img, pts) + + def buildoutcome(self): + return (self.__class__, self.level) + + def taken(self, dragon): + dragon.carrybonus(self, 543) + +class IceMonsterBonus(MonsterBonus): + + def __init__(self, x, y, multiple): + self.level = multiple + if multiple >= 1: + img, pts = Bonuses.violet_ice, 750 + else: + img, pts = Bonuses.cyan_ice, 700 + Bonus.__init__(self, x, y, img, pts) + + + +class DustStar(ActiveSprite): + localrandom = random.Random() + + def __init__(self, x, y, basedx, basedy, big=1, clock=0): + self.colorname = self.localrandom.choice(Stars.COLORS) + self.imgspeed = self.localrandom.randrange(3, 6) + self.rotation_reversed = self.localrandom.random() < 0.5 + ico, imggen = self.select_ico(getattr(Stars, self.colorname)) + ActiveSprite.__init__(self, ico, x, y) + self.setimages(imggen) + self.gen.append(self.fly(basedx, basedy, big)) + if not big: + self.make_small() + elif clock: + self.setimages(None) + self.seticon(images.sprget(Bonuses.clock)) + + def select_ico(self, imglist): + if self.rotation_reversed: + imglist = list(imglist) + imglist.reverse() + return (images.sprget(imglist[-1]), + self.cyclic(imglist, self.imgspeed)) + + def make_small(self): + images = [('smstar', self.colorname, k) for k in range(2)] + ico, imggen = self.select_ico(images) + self.seticon(ico) + self.setimages(imggen) + + def fly(self, dx, dy, big): + random = self.localrandom + dx += (random.random() - 0.5) * 2.8 + dy += (random.random() - 0.5) * 2.8 + fx = self.x + fy = self.y + if big: + j = 0 + else: + j = 2 + while j < 3: + ttl = random.expovariate(1.0 / 12) + if ttl > 35: + ttl = 35 + for i in range(int(ttl)+4): + fx += dx + fy += dy + self.move(int(fx), int(fy)) + yield None + if j == 0: + self.make_small() + fx += 8 + fy += 8 + j += 1 + self.kill() + + +class RandomBonus(Bonus): + timeout = 500 + +class TemporaryBonus(RandomBonus): + captime = 0 + bonusleveldivider = 2 + def taken(self, dragon): + dragon.dcap[self.capname] += 1 + self.carried(dragon) + def carried(self, dragon): + captime = self.captime + if boards.curboard.bonuslevel: + captime = (captime or 999) // self.bonusleveldivider + if captime: + dragon.carrybonus(self, captime) + else: + dragon.carrybonus(self) + self.endaction = None + def endaction(self, dragon): + if dragon.dcap[self.capname] >= 1: + dragon.dcap[self.capname] -= 1 + + +class ShoeSpeed(RandomBonus): + "Fast Runner. Cumulative increase of horizontal speed." + nimage = Bonuses.shoe + bigbonus = {'multiply': 3} + bigdoc = "Run Really Fast." + def taken(self, dragon): + dragon.dcap['hspeed'] += 1 + dragon.carrybonus(self) + +class CoffeeSpeed(RandomBonus): + "Caffeine. Cumulative increase of the horizontal speed and fire rate." + nimage = Bonuses.coffee + big = 0 + bigbonus = {'big': 1, 'multiply': 3} + bigdoc = "Super-Excited! Break through walls!" + def taken(self, dragon): + dragon.dcap['hspeed'] += 0.5 + dragon.dcap['firerate'] += 1 + if self.big: + dragon.dcap['breakwalls'] = 1 + dragon.carrybonus(self) + +class Butterfly(TemporaryBonus): + "Lunar Gravity. Allows you to jump twice as high as before." + nimage = Bonuses.butterfly + big = 0 + bigbonus = {'big': 1} + bigdoc = "Butterflies all around." + def taken1(self, dragons): + if self.big: + import mnstrmap, monsters + for i in range(17): + monsters.Butterfly(mnstrmap.Butterfly, + self.x + random.randrange(-40, 41), + self.y + random.randrange(-30, 31), + random.choice([-1, 1])) + else: + TemporaryBonus.taken1(self, dragons) + def taken(self, dragon): + dragon.dcap['gravity'] *= 0.5 + self.carried(dragon) + def endaction(self, dragon): + dragon.dcap['gravity'] *= 2.0 + +class Cocktail(TemporaryBonus): + "Short Lived Bubbles. Makes your bubbles explode more quickly." + nimage = Bonuses.cocktail + points = 2000 + capname = 'bubbledelay' + bigbonus = {'multiply': 3} + bigdoc = "Makes your bubbles explode at once. Dangerous!" + +class Extend(RandomBonus): + "E X T E N D. Gives you your missing letters and clear the level. " + nimage = Bonuses.extend + points = 0 + big = 0 + bigbonus = {'big': 1} + bigdoc = "A lot of letter bubbles! Run! Run!" + + def taken1(self, dragons): + if self.big: + self.letterexplosion() + else: + RandomBonus.taken1(self, dragons) + + def taken(self, dragon): + from bubbles import extend_name + names = [extend_name(l) for l in range(6)] + missing = [name for name in names if name not in dragon.bubber.letters] + x = dragon.x + dragon.ico.w//2 + y = dragon.y + points(x, y, dragon, 10000*len(missing)) + for l in range(6): + if extend_name(l) in missing: + dragon.bubber.giveletter(l, promize=0) + + def letterexplosion(self): + from bubbles import LetterBubble + playercount = len([p for p in BubPlayer.PlayerList if p.isplaying()]) + N = 3 + (playercount > 3) + angles = [i*(2.0*math.pi/N) for i in range(N)] + for l, dx, dy in [(0, 5, 9), (1, 16, 10), (2, 26, 8), + (3, 7, 23), (4, 15, 24), (5, 25, 24)]: + delta = 2.0*math.pi * random.random() + angles = [angle-delta for angle in angles] + x = self.x + self.ico.w//2 + 3*(dx-16) + y = self.y + self.ico.h//2 + 3*(dy-16) + for angle in angles: + bubble = LetterBubble(None, l) + bubble.thrown_bubble(x, y, 7.0 + 4.0 * random.random(), + (math.cos(angle), math.sin(angle))) + +class HeartPoison(RandomBonus): + "Heart Poison. Freeze all free monsters." + nimage = Bonuses.heart_poison + big = 0 + bigbonus = {'big': 1} + bigdoc = "Freeze all other players too!" + def taken1(self, dragons): + import monsters + monsters.freeze_em_all() + if self.big: + def heart_pause(dragon, gen): + for i in range(222): + yield None + dragon.gen = gen + for d in BubPlayer.DragonList: + if d not in dragons: + d.gen = [heart_pause(d, d.gen)] + +class VioletNecklace(RandomBonus): + "Monster Duplicator. Double the number of free monsters." + points = 650 + nimage = Bonuses.violet_necklace + big = 0 + bigbonus = {'big': 1} + bigdoc = "This level's boring, let's bring even more monsters..." + def taken1(self, dragons): + if self.big: + import monsters, mnstrmap + mlist = 2*['Nasty', 'Monky', 'Springy', 'Orcy', 'Gramy', 'Blitzy'] + wrange = (boards.bwidth - 8*CELL) // 2 + for dir in [1, -1]: + for i in range(len(mlist)): + name = mlist[i] + mdef = getattr(mnstrmap, name) + cls = getattr(monsters, name) + x = wrange * i // len(mlist) + if dir == 1: + x = 2*CELL + HALFCELL + x + else: + x = boards.bwidth - 4*CELL - HALFCELL - x + y = -2*CELL - i * 2*CELL + cls(mdef, x, y, dir) + else: + for s in BubPlayer.MonsterList[:]: + if s.regular(): + for i in range(self.multiply): + s.__class__(s.mdef, s.x, s.y, -s.dir * (-1)**i) + +class WandBonus(RandomBonus): + "Wand/Chest. Turn the bubble into bonuses at the end of the level." + nimages = [Bonuses.brown_wand, Bonuses.yellow_wand, Bonuses.green_wand, + Bonuses.violet_wand, Bonuses.blue_wand, Bonuses.red_wand, + Bonuses.violet_chest, Bonuses.blue_chest, Bonuses.red_chest, + Bonuses.yellow_chest, + ] + Modes = [ + (Bonuses.brown_wand, 750, Bonuses.cyan_ice, 700, BigImages.cyan_ice, 20000), + (Bonuses.yellow_wand, 750, Bonuses.violet_ice, 750, BigImages.violet_ice, 20000), + (Bonuses.green_wand, 750, Bonuses.peach2, 800, BigImages.peach2, 30000), + (Bonuses.violet_wand, 750, Bonuses.pastec2, 850, BigImages.pastec2, 30000), + (Bonuses.blue_wand, 750, Bonuses.cream_pie, 900, BigImages.cream_pie, 40000), + (Bonuses.red_wand, 750, Bonuses.sugar_pie, 950, BigImages.sugar_pie, 40000), + (Bonuses.violet_chest, 2000, Diamonds.violet, 6000, BigImages.violet, 60000), + (Bonuses.blue_chest, 2000, Diamonds.blue, 7000, BigImages.blue, 60000), + (Bonuses.red_chest, 2000, Diamonds.red, 8000, BigImages.red, 70000), + (Bonuses.yellow_chest, 2000, Diamonds.yellow, 9000, BigImages.yellow, 70000), + ] + def __init__(self, x, y): + self.mode = random.choice(WandBonus.Modes) + RandomBonus.__init__(self, x, y, *self.mode[:2]) + def taken1(self, dragons): + BubPlayer.BubblesBecome = self.bubble_outcome + BubPlayer.MegaBonus = self.mega_bonus + def bubble_outcome(self, bubble): + if bubble.pop(): + x = bubble.x + if x < 2*CELL: + x = 2*CELL + elif x > boards.bwidth - 4*CELL: + x = boards.bwidth - 4*CELL + Bonus(x, bubble.y, *self.mode[2:4]) + def mega_bonus(self): + nico, npoints = self.mode[4:6] + ico = images.sprget(nico) + x = random.randrange(0, boards.bwidth-ico.w) + mb = Megabonus(x, -ico.h, nico, npoints) + mb.outcome = (Bonus,) + self.mode[2:4] + mb.outcome_image = self.mode[2] +WandBonus1 = WandBonus # increase probability + +class Megabonus(Bonus): + touchable = 0 + vspeed = 6 + sound = 'Extra' + coverwithbonus = 99 + fallerdelay = 71 + + def faller(self): + self.fullpoints = self.points + self.bubbles = {} + for t in range(self.fallerdelay): + yield None + self.ready_to_go() + self.bubbles_pos = list(self.bubbles_position()) + self.gen.append(self.animate_bubbles()) + y0 = self.y - HALFCELL + ymax = boards.bheight - CELL - self.ico.h + self.touchable = 1 + ny = self.y + while self.y >= y0: + if self.vspeed: + ny += self.vspeed + if ny > ymax: + ny = ymax + self.vspeed = 0 + self.move(self.x, int(ny)) + yield None + self.kill() + + def ready_to_go(self): + pass + + def is_on_ground(self): + return self.y == boards.bheight - CELL - self.ico.h + + def kill(self): + for bubble in self.bubbles.values(): + bubble.pop() + Bonus.kill(self) + + def taken(self, dragon): + poplist = [dragon] + for bubble in self.bubbles.values(): + bubble.pop(poplist) + + def bubbles_position(self): + import time; start=time.time() + cx = self.ico.w//2 - CELL + cy = self.ico.h//2 - CELL + positions = [] + pi2 = math.pi * 2 + dist = 10.0 + for i in range(31): + while 1: + angle = random.random() * pi2 + nx = cx + int(dist*math.sin(angle)) + ny = cy + int(dist*math.cos(angle)) + for ox, oy in positions: + if (nx-ox)*(nx-ox) + (ny-oy)*(ny-oy) < 220: + dist += 0.3 + break + else: + break + positions.append((nx, ny)) + #print time.time()-start + return positions +## nx = 5 +## ny = 6 +## xmargin = 2 +## ymargin = 7 +## xstep = (self.ico.w+2*xmargin-2*CELL) / float(nx-1) +## ystep = (self.ico.h+2*ymargin-2*CELL) / float(ny-1) +## for dx in range(nx): +## corner = dx in [0, nx-1] +## for dy in range(corner, ny-corner): +## dx1 = int(dx*xstep)-xmargin +## dy1 = int(dy*ystep)-ymargin +## yield (dx1 + random.randrange(-2,3), +## dy1 + random.randrange(-2,3)) + + def nearest_free_point(self, x0, y0): + distlst = [((x0-x)*(x0-x)+(y0-y)*(y0-y)+random.random(), x, y) + for x, y in self.bubbles_pos if (x, y) not in self.bubbles] + if distlst: + ignored, dx, dy = min(distlst) + return dx, dy + else: + return None, None + + def in_bubble(self, bubble): + if not self.touchable: + return # bubbling a BonusMaker about to make a big bonus + dx, dy = self.nearest_free_point(bubble.x-self.x, bubble.y-self.y) + if dx is not None: + self.cover_bubble(dx, dy, bubble.d.bubber) + self.gen.append(self.cover_bubbles(bubble.d.bubber)) + bubble.kill() + + def cover_bubbles(self, bubber): + while 1: + for t in range(2): + yield None + bubbles = [dxy for dxy, b in self.bubbles.items() + if b.bubber is bubber] + if not bubbles: + break + dx, dy = self.nearest_free_point(*random.choice(bubbles)) + if dx is None: + break + self.cover_bubble(dx, dy, bubber) + self.untouchable() + + def cover_bubble(self, dx, dy, bubber): + if (dx, dy) in self.bubbles: + return + from bubbles import Bubble + if len(self.bubbles) & 1: + MegabonusBubble = Bubble + elif self.coverwithbonus: + self.coverwithbonus -= 1 + outcome = self.outcome + outcome_image = self.outcome_image + class MegabonusBubble(Bubble): + def popped(self, dragon): + BonusMaker(self.x, self.y, [outcome_image], outcome=outcome) + return 10 + else: + MegabonusBubble = Bubble + + nimages = GreenAndBlue.normal_bubbles[bubber.pn] + b = MegabonusBubble(images.sprget(nimages[1]), self.x+dx, self.y+dy) + b.dx = dx + b.dy = dy + b.bubber = bubber + b.nimages = nimages + self.bubbles[dx, dy] = b + self.timeout = 0 + f = float(len(self.bubbles)) / len(self.bubbles_pos) + self.vspeed = -0.73*f + self.vspeed*(1.0-f) + self.points = int(self.fullpoints*(1.0-f) / 10000.0 + 0.9999) * 10000 + + def animate_bubbles(self): + if 0: # disabled clipping + d = {} + for dx, dy in self.bubbles_pos: + d[dx] = d[dy] = None + north = d.copy() + south = d.copy() + west = d.copy() + east = d.copy() + del d + for dx, dy in self.bubbles_pos: + lst = [y for x, y in self.bubbles_pos if x==dx and y<dy] + if lst: north[dy] = max(lst) + lst = [y for x, y in self.bubbles_pos if x==dx and y>dy] + if lst: south[dy] = min(lst) + lst = [x for x, y in self.bubbles_pos if x<dx and y==dy] + if lst: west[dx] = max(lst) + lst = [x for x, y in self.bubbles_pos if x>dx and y==dy] + if lst: east[dx] = min(lst) + W = 2*CELL + H = 2*CELL + bubbles = self.bubbles + while 1: + for cycle in [1]*8 + [2]*10 + [1]*8 + [0]*10: + yield None + for (dx, dy), bubble in bubbles.items(): + if not hasattr(bubble, 'poplist'): + if 0: # disabled clipping + if (dx, north[dy]) in bubbles: + margin_n = (north[dy]+H-dy)//2 + else: + margin_n = 0 + if (dx, south[dy]) in bubbles: + margin_s = (dy+H-south[dy])//2 + else: + margin_s = 0 + if (west[dx], dy) in bubbles: + margin_w = (west[dx]+W-dx)//2 + else: + margin_w = 0 + if (east[dx], dy) in bubbles: + margin_e = (dx+W-east[dx])//2 + else: + margin_e = 0 + r = (margin_w, + margin_n, + W-margin_w-margin_e, + H-margin_n-margin_s) + bubble.move(self.x + bubble.dx + margin_w, + self.y + bubble.dy + margin_n, + images.sprget_subrect( + bubble.nimages[cycle], r)) + else: + bubble.move(self.x + bubble.dx, + self.y + bubble.dy, + images.sprget(bubble.nimages[cycle])) + elif len(bubbles) == len(self.bubbles_pos): + self.pop_bubbles(bubble.poplist) + return + + def pop_bubbles(self, poplist): + def bubble_timeout(bubble, vspeed): + ny = bubble.y + for t in range(random.randrange(15,25)): + if hasattr(bubble, 'poplist'): + return + ny += vspeed + bubble.move(bubble.x, int(ny)) + yield None + bubble.pop(poplist) + + for bubble in self.bubbles.values(): + bubble.gen.append(bubble_timeout(bubble, self.vspeed)) + self.bubbles.clear() + self.kill() + +class Cactus(RandomBonus): + "Cactus. Drop a big version of a random bonus." + points = 600 + nimage = 'cactus' + extra_cheat_arg = None + bigbonus = {'multiply': 3} + bigdoc = "Let's get more big bonuses!" + + def taken1(self, dragons): + count = 0 + while count < self.multiply: + args = () + if self.extra_cheat_arg: + cls = globals()[self.extra_cheat_arg] + self.extra_cheat_arg = None + elif bigclockticker and bigclockticker.state == 'pre': + cls = Clock + args = (1,) + else: + cls = random.choice(Classes) + if makecactusbonus(cls, *args): + count += 1 + cactusbonussound() + +#Cactus1 = Cactus # increase probability + +OFFSCREEN = -3*CELL +def makecactusbonus(cls, *args): + bonus = cls(OFFSCREEN, 0, *args) + if not bonus.alive or getattr(bonus, 'bigbonus', None) is None: + if bonus.alive: + bonus.kill() + return None + bonus.__dict__.update(bonus.bigbonus) + bonus.untouchable() + bonus.gen = [] + megacls = bonus.bigbonus.get('megacls', Cactusbonus) + mb = megacls(0, -3*CELL, 'cactus', 10000) # temp image + mb.outcome = (cls,) + (args or bonus.bigbonus.get('outcome_args', ())) + mb.outcome_image = bonus.nimage + mb.bonus = bonus + mb.gen.append(mb.prepare_image()) + mb.gen.append(mb.remove_if_no_bonus()) + return mb + +def cactusbonussound(): + gamesrv.set_musics([], []) + boards.curboard.set_musics(prefix=[images.music_modern]) + boards.curboard.set_musics() + +class Cactusbonus(Megabonus): + coverwithbonus = 5 + + def prepare_image(self): + while images.computebiggericon(self.bonus.ico) is None: + yield None + + def remove_if_no_bonus(self): + while self.bonus.alive: + yield None + self.kill() + + def ready_to_go(self): + ico = images.biggericon(self.bonus.ico) + x = random.randrange(0, boards.bwidth-ico.w) + self.move(x, -ico.h, ico) + + def taken1(self, dragons): + d1 = list(dragons) + Megabonus.taken1(self, dragons) + if self.bonus.alive: + x = self.x + self.ico.w//2 - CELL + y = self.y + self.ico.h//2 - CELL + self.bonus.move(x, y) + res = self.bonus.taken1(d1) + self.untouchable() + if res == -1: + self.taken_by = [] + self.gen.append(self.touchdelay(10)) + self.bonus.move(OFFSCREEN, 0) + else: + self.bonus.kill() + return res + + def kill(self): + Megabonus.kill(self) + if self.bonus.alive: + self.bonus.kill() + +class LongDurationCactusbonus(Cactusbonus): + timeout = 500 + killgens = 0 + +def starexplosion(x, y, multiplyer, killmonsters=0, outcomes=[]): + outcomes = list(outcomes) + poplist = [None] + for i in range(multiplyer): + colors = list(Stars.COLORS) + random.shuffle(colors) + for colorname in colors: + images = getattr(Stars, colorname) + if outcomes: + outcome = outcomes.pop() + extra_stars = [] + if hasattr(outcome[0], 'extra_stars_location'): + for sx, sy in outcome[0].extra_stars_location: + extra_stars.append(BonusMakerExtraStar(x, y, sx, sy, + colorname)) + bm = BonusMaker(x, y, images, outcome=outcome) + for star in extra_stars: + star.gen.append(star.follow_bonusmaker(bm)) + else: + b = Parabolic2(x, y, images) + if killmonsters: + b.gen.append(b.killmonsters(poplist)) + +class HomingStar(ActiveSprite): + def __init__(self, x, y, colorname, poplist): + imglist = getattr(Stars, colorname) + ActiveSprite.__init__(self, images.sprget(imglist[0]), x, y) + self.colorname = colorname + self.setimages(self.cyclic(imglist, 2)) + self.gen.append(self.homing(poplist)) + + def homing(self, poplist): + from monsters import Monster + target = None + vx = (random.random() - 0.5) * 6.6 + vy = (random.random() - 0.5) * 4.4 + nx = self.x + ny = self.y + counter = 10 + while 1: + if random.random() < 0.02: + target = None + if target is None or not target.alive: + bestdist = 1E10 + for s in BubPlayer.MonsterList: + if isinstance(s, Monster): + dx = s.x - nx + dy = s.y - ny + dist = dx*dx + dy*dy + (random.random() * 25432.1) + if dist < bestdist: + bestdist = dist + target = s + if target is None: + break + dx = target.x - nx + dy = target.y - ny + dist = dx*dx + dy*dy + if dist <= 3*CELL*CELL: + target.argh(poplist) + break + yield None + vx = (vx + dx * 0.005) * 0.96 + vy = (vy + dy * 0.005) * 0.96 + nx += vx + ny += vy + self.move(int(nx), int(ny)) + if counter: + counter -= 1 + else: + img = ('smstar', self.colorname, random.randrange(2)) + s = ActiveSprite(images.sprget(img), self.x + 8, self.y + 8) + s.gen.append(s.die([None], speed=10)) + counter = 3 + self.kill() + +class Book(RandomBonus): + "Magic Bomb. Makes a magical explosion killing touched monsters." + points = 2000 + nimage = Bonuses.book + big = 0 + bigbonus = {'big': 1} + bigdoc = "Homing Magical Stars." + def taken1(self, dragons): + if self.big: + poplist = [None] + x = self.x + (self.ico.w - 2*CELL) // 2 + y = self.y + (self.ico.h - 2*CELL) // 2 + colors = list(Stars.COLORS) + random.shuffle(colors) + for colorname in colors + colors[len(colors)//2:]: + HomingStar(x, y, colorname, poplist) + else: + starexplosion(self.x, self.y, self.multiply, killmonsters=1) + +class Potion(RandomBonus): + "Potions. Clear the level and fill its top with bonuses." + nimages = [Bonuses.red_potion, Bonuses.green_potion, Bonuses.yellow_potion, + 'potion4'] + Potions = [(Bonuses.red_potion, 150, [(PotionBonuses.coin, 350), + (PotionBonuses.rainbow, 600)]), + (Bonuses.green_potion, 350, [(PotionBonuses.flower, 1000), + (PotionBonuses.trefle, 2000)]), + (Bonuses.yellow_potion, 550, [(PotionBonuses.green_note, 2000), + (PotionBonuses.blue_note, 3000)]), + ('potion4', 750, None), + ] + LocalDir = os.path.dirname(__file__) or os.curdir + Extensions = [s for s in os.listdir(LocalDir) + if s.startswith('ext') and + os.path.isdir(os.path.join(LocalDir, s))] + random.shuffle(Extensions) + extra_cheat_arg = None + big = 0 + bigdoc = "Fill the whole level with bonuses." + + def __init__(self, x, y): + p_normal = 3 + if boards.curboard.bonuslevel: + p_extension = 2 # make extensions rare in the bonus level + else: + p_extension = 5 + if self.extra_cheat_arg: + Potion.Extensions.append(self.extra_cheat_arg) + p_normal = 0 + if not Potion.Extensions: + p_extension = 0 + choices = [] + for mode in Potion.Potions: + if mode[2] is None: + p = p_extension + else: + p = p_normal + choices += [mode] * p + self.mode = random.choice(choices) + if self.mode[2] is not None: + self.bigbonus = {'big': 1} + RandomBonus.__init__(self, x, y, *self.mode[:2]) + def taken1(self, dragons): + blist = self.mode[2] + if blist is not None: + if random.random() < 0.6: + blist = [random.choice(blist)] + boards.replace_boardgen(boards.potion_fill(blist, self.big)) + else: + n_players = len([p for p in BubPlayer.PlayerList if p.isplaying()]) + while Potion.Extensions: + ext = Potion.Extensions.pop() + #print "Trying potion:", ext + ext = __import__(ext, globals(),locals(), ['run','min_players']) + if n_players >= ext.min_players: + ext.run() + boards.BoardGen.append(boards.extra_bkgnd_black(self.x, self.y)) + #print "Accepted because:", n_players, ">=", ext.min_players + break + else: + #print "Rejected because:", n_players, "<", ext.min_players + pass + +class FireBubble(RandomBonus): + "Fire Bubbles. Makes you fire napalm bubbles." + nimage = Bonuses.hamburger + bubkind = 'FireBubble' + bubcount = 10 + bigbonus = {'bubkind': 'BigFireBubble'} + bigdoc = "Makes you shoot fire - you're a dragon after all." + def taken(self, dragon): + dragon.dcap['shootbubbles'] = [self.bubkind] * self.bubcount + dragon.carrybonus(self) + +class WaterBubble(FireBubble): + "Water Bubbles. Your bubbles will now be filled with water." + nimage = Bonuses.beer + bubkind = 'WaterBubble' + bigbonus = {'bubkind': 'SnookerBubble'} + bigdoc = "Snooker balls." + +class LightningBubble(FireBubble): + "Lightning Bubbles." + nimage = Bonuses.french_fries + bubkind = 'LightningBubble' + bigbonus = {'bubkind': 'BigLightBubble'} + bigdoc = "Even-more-lightning Bubbles." + +class Megadiamond(Megabonus): + nimage = BigImages.red + points = 20000 + fallerdelay = 0 + outcome = (MonsterBonus, -1) + outcome_image = Bonuses.monster_bonuses[-1][0] + extra_stars_location = [ (-24,-28),(0,-28),(24,-28), + (-40,-11), (40,-11), + (-32, 8), (32, 8), + (-16,23), (16,23), + (0,38), ] + def __init__(self, x, y): + ico = images.sprget(self.nimage) + x -= (ico.w - 2*CELL) // 2 + y -= (ico.h - 2*CELL) // 2 + Megabonus.__init__(self, x, y) + +class Door(RandomBonus): + "Magic Door. Let bonuses come in!" + points = 1000 + nimage = Bonuses.door + diamond_outcome = (MonsterBonus, -1) + bigbonus = {'diamond_outcome': (Megadiamond,)} + bigdoc = "Let bigger bonuses come in!" + def taken1(self, dragons): + starexplosion(self.x, self.y, 2, + outcomes = [self.diamond_outcome] * 10) + +class LongFire(RandomBonus): + "Long Fire. Increase the range of your bubble throw out." + nimage = Bonuses.softice1 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Throw bubbles that split into more bubbles." + def taken(self, dragon): + if self.big: + dragon.dcap['shootbubbles'] = ['MoreBubblesBubble'] * 10 + else: + dragon.dcap['shootthrust'] *= 1.5 + dragon.carrybonus(self) + +class Glue(RandomBonus): + "Glue. Triple fire." + nimage = 'glue' + points = 850 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Heptuple fire. (That's 7.)" + def taken(self, dragon): + if self.big: + dragon.dcap['flower'] = -16 # heptuple fire + elif dragon.dcap['flower'] >= 0: + dragon.dcap['flower'] = -1 # triple fire + else: + dragon.dcap['flower'] -= 1 # cumulative effect + dragon.carrybonus(self) + +class ShortFire(RandomBonus): + "Short Fire. Shorten the range of your bubble throw out." + nimage = Bonuses.softice2 + points = 300 + factor = 1 / 1.5 + bigbonus = {'factor': 0} + bigdoc = "What occurs if you throw bubbles at range zero?" + def taken(self, dragon): + dragon.dcap['shootthrust'] *= self.factor + dragon.carrybonus(self) + +class HighSpeedFire(RandomBonus): + "High Speed Fire. Increase your fire rate." + nimage = Bonuses.custard_pie + points = 700 + bigbonus = {'multiply': 4} + bigdoc = "Machine-gun speed!" + def taken(self, dragon): + dragon.dcap['firerate'] += 1.5 + dragon.carrybonus(self) + +class Mushroom(TemporaryBonus): + "Bouncy Bouncy. Makes you jump continuously." + nimage = Bonuses.mushroom + points = 900 + capname = 'pinball' + captime = 625 + bigbonus = {'captime': captime*2, 'multiply': 2} + bigdoc = "The same, but even more annoying." + +class AutoFire(TemporaryBonus): + "Auto Fire. Makes you fire continuously." + nimage = Bonuses.rape + points = 800 + capname = 'autofire' + captime = 675 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Adds many bubbles to the level." + def taken1(self, dragons): + if self.big: + boards.extra_boardgen(boards.extra_bubbles(900)) + else: + TemporaryBonus.taken1(self, dragons) + +class Insect(RandomBonus): + "Crush World." + nimage = Bonuses.insect + big = 0 + bigbonus = {'big': 1} + bigdoc = "What if the level looked like that instead... Or like that... Or..." + def taken1(self, dragons): + if self.big: + if dragons: + d = random.choice(dragons) + cx, cy = d.x, d.y + else: + cx, cy = None, None + boards.extra_boardgen(boards.extra_make_random_level(cx, cy)) + else: + boards.extra_boardgen(boards.extra_walls_falling()) + +class Ring(TemporaryBonus): + "The One Ring." + nimage = Bonuses.ring + points = 4000 + capname = 'ring' + captime = 700 + bonusleveldivider = 5 + bigbonus = {'multiply': 3} + bigdoc = "Where am I?" + +class GreenPepper(TemporaryBonus): + "Hot Pepper. Run! Run! That burns." + nimage = Bonuses.green_pepper + capname = 'hotstuff' + captime = 100 + bigbonus = {'captime': 250, 'multiply': 2} + bigdoc = "That burns a lot!" + +class Lollipop(TemporaryBonus): + "Yo Man! Makes you walk backward." + nimage = Bonuses.lollipop + big = 0 + bigbonus = {'big': 1} + bigdoc = "Just swapping 'left' and 'right' is not confusing enough." + def taken(self, dragon): + dragon.dcap['left2right'] = -dragon.dcap['left2right'] + if self.big: + perm = range(4) + while perm[0] == 0 or perm[1] == 1 or perm[2] == 2 or perm[3] == 3: + random.shuffle(perm) + names = ('key_left', 'key_right', 'key_jump', 'key_fire') + dragon.dcap['key_right'] = names[perm[0]] + dragon.dcap['key_left'] = names[perm[1]] + dragon.dcap['key_jump'] = names[perm[2]] + dragon.dcap['key_fire'] = names[perm[3]] + self.carried(dragon) + def endaction(self, dragon): + dragon.dcap['left2right'] = -dragon.dcap['left2right'] + for name in ('key_left', 'key_right', 'key_jump', 'key_fire'): + dragon.dcap[name] = name + +class Chickpea(TemporaryBonus): + "Basilik. Allows you to touch the monsters." + nimage = Bonuses.chickpea + points = 800 + capname = 'overlayglasses' + captime = 400 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Turn off the light." + + def taken1(self, dragons): + if self.big: + boards.extra_boardgen(boards.extra_light_off(597), 1) + else: + TemporaryBonus.taken1(self, dragons) + + def taken(self, dragon): + TemporaryBonus.taken(self, dragon) + dragon.dcap['shield'] += 420 + +class IceCream(RandomBonus): + "Icecream. An icecream which is so good you'll always want more." + nimages = [Bonuses.icecream6, Bonuses.icecream5, + Bonuses.icecream4, Bonuses.icecream3] + IceCreams = [(Bonuses.icecream6, 250), + (Bonuses.icecream5, 500), + (Bonuses.icecream4, 1000), + (Bonuses.icecream3, 2000)] + big = 0 + bigbonus = {'big': 1} + bigdoc = "BIG ice creams!" + def __init__(self, x, y, generation=0): + self.generation = generation + RandomBonus.__init__(self, x, y, *self.IceCreams[generation]) + def taken1(self, dragons): + nextgen = self.generation + 1 + if nextgen < len(self.IceCreams): + for i in range(2): + if self.big: + makecactusbonus(IceCream, nextgen) + else: + x, y = chooseground(200) + if x is None: + return + IceCream(x, y, nextgen) + if self.big: + cactusbonussound() + +class Grenade(RandomBonus): + "Barbecue." + nimage = Bonuses.grenade + points = 550 + big = 0 + bigbonus = {'big': 1} + bigdoc = "360-degree flames." + def taken1(self, dragons): + from bubbles import FireFlame + poplist = [None] + for y in range(1, boards.height-1): + for x in range(2, boards.width-2): + if bget(x,y) != ' ': + continue + if bget(x,y+1) == '#': + FireFlame(x, y, poplist) + elif self.big: + if bget(x,y-1) == '#': + FireFlame(x, y, poplist, flip='vflip') + elif bget(x-1,y) == '#': + FireFlame(x, y, poplist, flip='cw') + elif bget(x+1,y) == '#': + FireFlame(x, y, poplist, flip='ccw') + +class Conch(RandomBonus): + "Sea Shell. Let's bring the sea here!" + nimage = Bonuses.conch + points = 650 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Aquarium." + def taken1(self, dragons): + if self.big: + gen = boards.extra_aquarium + else: + gen = boards.extra_water_flood + boards.extra_boardgen(gen()) + +def fire_rain(x, poplist): + from bubbles import FireDrop + FireDrop(x, -CELL, poplist) + +def water_rain(x, poplist): + from bubbles import watercell + watercell(x, 0, poplist) + +def ball_rain(x, poplist): + from bubbles import SpinningBall + SpinningBall(x, -CELL, poplist) + +class Umbrella(RandomBonus): + "Umbrellas. Beware of what's going to fall on everyone's head!" + nimages = [Bonuses.brown_umbrella, Bonuses.grey_umbrella, + Bonuses.violet_umbrella] + Umbrellas = [(Bonuses.brown_umbrella, 900, fire_rain, 10, 60), + (Bonuses.grey_umbrella, 950, water_rain, 5, 60), + (Bonuses.violet_umbrella, 1000, ball_rain, 9, 120)] + bigbonus = {'multiply': 3.1416} + bigdoc = "It's raining hard." + def __init__(self, x, y): + self.mode = random.choice(Umbrella.Umbrellas) + RandomBonus.__init__(self, x, y, *self.mode[:2]) + def taken1(self, dragons): + for i in range(self.multiply): + boards.extra_boardgen(self.raining()) + def raining(self): + builder, drops, timemax = self.mode[2:] + timemax = int(timemax * math.sqrt(self.multiply)) + drops = int(drops * self.multiply) + times = [random.randrange(0, timemax) for i in range(drops)] + poplist = [None] + for t in range(timemax): + for i in range(times.count(t)): + x = random.randrange(2*CELL, bwidth-3*CELL+1) + builder(x, poplist) + yield 0 + +class Fruits(RandomBonus): + "Fruits. A small little bonus. But the size doesn't matter, does it? If you're lucky enough you might get a great shower!" + nimages = [Bonuses.kirsh, Bonuses.erdbeer, Bonuses.tomato, + Bonuses.apple, Bonuses.corn, Bonuses.radish] + bubblable = 0 + sound = 'Extra' + Fruits = [(Bonuses.kirsh, 100), + #(Bonuses.icecream1, 150), + (Bonuses.erdbeer, 150), + #(Bonuses.fish1, 250), + (Bonuses.tomato, 200), + #(Bonuses.donut, 250), + (Bonuses.apple, 250), + (Bonuses.corn, 300), + #(Bonuses.icecream2, 600), + (Bonuses.radish, 350), + ] + def __init__(self, x, y): # x and y ignored ! + fine = 0 + for i in range(20): + x0 = random.randint(3, boards.width-5) + y0 = random.randint(1, boards.height-3) + for xt in range(x0-1, x0+3): + if xt == x0-1 or xt == x0+2: + yplus = 1 + else: + yplus = 0 + for yt in range(y0+yplus, y0+4-yplus): + if bget(xt,yt) != ' ': + break + else: + continue + break + else: + x, y = x0*CELL, y0*CELL + fine = 1 + break + mode = random.choice(Fruits.Fruits) + RandomBonus.__init__(self, x, y, falling=0, *mode) + self.repeatcount = 0 + if not fine: + self.kill() + elif random.random() < 0.04: + self.superfruit = mode + self.sound = 'Shh' + self.points = 0 + self.repeatcount = random.randrange(50,100) + def taken1(self, dragons): + if self.repeatcount: + image, points = self.superfruit + f = Parabolic2(self.x, self.y, [image], y_amplitude = -1.5) + f.points = points + f.touchable = 1 + self.repeatcount -= 1 + self.gen.append(self.taking(1, 2)) + return -1 +Fruits1 = Fruits # increase probability +Fruits2 = Fruits +Fruits3 = Fruits +Fruits4 = Fruits +Fruits5 = Fruits +Fruits6 = Fruits + +class BlueNecklace(RandomBonus): + "Self Duplicator. Mirror yourself." + points = 1000 + nimage = Bonuses.blue_necklace + copies = 1 + bigbonus = {'copies': 3} + bigdoc = "Mirrors vertically too." + def taken(self, dragon): + dragons = [dragon] + modes = [(-1, 1), (1, -1), (-1, -1)][:self.copies] + modes.reverse() + dcap = dragon.dcap.copy() + for sign, gravity in modes: + if len(dragon.bubber.dragons) >= 7: + break # avoid burning the server with two much dragons + d1 = self.makecopy(dragon, sign, gravity, dcap) + dragons.append(d1) + d1 = random.choice(dragons) + d1.carrybonus(self, 250) + + def makecopy(self, dragon, sign=-1, gravity=1, dcap=None): + from player import Dragon + dcap = dcap or dragon.dcap + d = Dragon(dragon.bubber, dragon.x, dragon.y, -dragon.dir, dcap) + d.dcap['left2right'] = sign * dcap['left2right'] + d.dcap['gravity'] = gravity * dcap['gravity'] + d.up = dragon.up + s = (dcap['shield'] + 12) & ~3 + dragon.dcap['shield'] = s+2 + if sign*gravity > 0: + s += 2 + d.dcap['shield'] = s + dragon.bubber.dragons.append(d) + return d + +class Monsterer(RandomBonus): + "Monsterificator. Let's play on the other side!" + nimages = [Bonuses.red_crux, Bonuses.blue_crux] + Sizes = [(Bonuses.red_crux, 800), (Bonuses.blue_crux, 850)] + mlist = [['Nasty', 'Monky', 'Springy', 'Orcy'], + ['Ghosty', 'Flappy', 'Gramy', 'Blitzy'] + ] + big = 0 + bigbonus = {'big': 1} + bigdoc = "Ta, ta ta, ta, taaaaaa..." + def __init__(self, x, y): + self.mode = random.choice([0,1]) + RandomBonus.__init__(self, x, y, *self.Sizes[self.mode]) + def taken(self, dragon): + mcls = random.choice(self.mlist[self.mode]) + dragon.become_monster(mcls, self.big) + +Monsterer1 = Monsterer # increase probability + +class Bubblizer(RandomBonus): + "Bubblizer." + points = 750 + nimage = Bonuses.gold_crux + big = 0 + bigbonus = {'big': 1} + bigdoc = "Special powers for your bubble." + def taken(self, dragon): + args = (dragon.bubber.pn,) + if self.big: + from bubbles import SnookerBubble, BigLightBubble + bcls = random.choice([SnookerBubble, BigLightBubble]) + if bcls is SnookerBubble: + args = (dragon, dragon.x, dragon.y, 1000000) + else: + from bubbles import FireBubble, WaterBubble, LightningBubble + bcls = random.choice([FireBubble, WaterBubble, LightningBubble]) + b = bcls(*args) + b.move(dragon.x, dragon.y) + if not dragon.become_bubblingeyes(b): + b.kill() + +class Carrot(RandomBonus): + "Angry Monster. Turns all free monsters angry." + nimage = Bonuses.carrot + points = 950 + ghost = 0 + bigbonus = {'ghost': 1} + bigdoc = "What do angry monsters turn into if you don't hurry up?" + def taken1(self, dragons): + from monsters import Monster + lst = [s for s in images.ActiveSprites + if isinstance(s, Monster) and s.regular()] + if lst: + if self.ghost: + images.Snd.Hell.play() + for s in lst: + s.become_ghost() + else: + for s in lst: + s.angry = [s.genangry()] + s.resetimages() + +class Egg(RandomBonus): + "Teleporter. Exchange yourself with somebody else." + nimage = Bonuses.egg + big = 0 + bigbonus = {'big': 1} + bigdoc = "Exchange colors too." + def taken1(self, dragons): + if self.big: + self.exchange_bubbers() + else: + self.exchange_dragons(dragons) + + def exchange_dragons(self, dragons): + dragons = [d for d in dragons if d in d.bubber.dragons] + alldragons = [d for d in BubPlayer.DragonList if d in d.bubber.dragons] + others = [d for d in alldragons if d not in dragons] + xchg = {} + random.shuffle(dragons) + random.shuffle(others) + while dragons and others: + d1 = dragons.pop() + d2 = others.pop() + xchg[d1] = d2.bubber + xchg[d2] = d1.bubber + if len(dragons) > 1: + copy = dragons[:] + for i in range(10): + random.shuffle(copy) + for j in range(len(dragons)): + if dragons[j] == copy[j]: + break + else: + break + for d1, d2 in zip(dragons, copy): + xchg[d1] = d2.bubber + elif len(dragons) == 1: + x, y = chooseground(200) + if x is not None: + d1 = dragons[0] + d1.move(x, y) + d1.dcap['shield'] = 50 + for d1, bubber2 in xchg.items(): + d1.bubber.dragons.remove(d1) + d1.bubber = bubber2 + bubber2.dragons.append(d1) + d1.dcap['shield'] = 50 + + def exchange_bubbers(self): + self.exchange_dragons(list(BubPlayer.DragonList)) + players = [p for p in BubPlayer.PlayerList + if p.isplaying()] + if len(players) > 1: + while 1: + copy = players[:] + random.shuffle(copy) + for j in range(len(players)): + if players[j] is copy[j]: + break + else: + break + for b1, b2 in zip(players, copy): + for d in b1.dragons: + d.dcap['bubbericons'] = b2 + +class Bomb(RandomBonus): + "Baaoouuuummmm! Explode that wall!" + nimage = Bonuses.bomb + bigbonus = {'multiply': 3.8} + bigdoc = "Makes a BIG hole." + def taken1(self, dragons): + bomb_explosion(self.x, self.y, self.multiply) + +def bomb_explosion(x0, y0, multiply=1, starmul=2): + RADIUS = 3.9 * CELL * math.sqrt(multiply) + Radius2 = RADIUS * RADIUS + brd = boards.curboard + cx = x0 + HALFCELL + cy = y0 + HALFCELL - RADIUS/2 + for y in range(0, brd.height): + dy1 = abs(y*CELL - cy) + dy2 = abs((y-(brd.height-1))*CELL - cy) + dy3 = abs((y+(brd.height-1))*CELL - cy) + dy = min(dy1, dy2, dy3) + for x in range(2, brd.width-2): + dx = x*CELL - cx + if dx*dx + dy*dy < Radius2: + try: + brd.killwall(x,y) + except KeyError: + pass + brd.reorder_walls() + starexplosion(x0, y0, starmul) + gen = boards.extra_display_repulse(x0+CELL, y0+CELL, + 15000 * multiply, + 1000 * multiply) + boards.extra_boardgen(gen) + +class Ham(RandomBonus): + "Protein. Let's build something!" + nimage = Bonuses.ham + bigbonus = {'multiply': 3.4} + bigdoc = "Builds something BIG." + def taken1(self, dragons): + RADIUS = 3.9 * CELL * math.sqrt(self.multiply) + Radius2 = RADIUS * RADIUS + brd = boards.curboard + cx = self.x + HALFCELL + cy = self.y + HALFCELL - RADIUS/2 + xylist = [] + for y in range(0, brd.height): + dy1 = abs(y*CELL - cy) + dy2 = abs((y-(brd.height-1))*CELL - cy) + dy3 = abs((y+(brd.height-1))*CELL - cy) + dy = min(dy1, dy2, dy3) + for x in range(2, brd.width-2): + dx = x*CELL - cx + if dx*dx + dy*dy < Radius2: + if (y,x) not in brd.walls_by_pos and random.random() < 0.5: + brd.putwall(x,y) + xylist.append((x, y)) + brd.reorder_walls() + boards.extra_boardgen(boards.single_blocks_falling(xylist)) + gen = boards.extra_display_repulse(self.x+CELL, self.y+CELL, + 5000 * self.multiply, + 1000 * self.multiply) + boards.extra_boardgen(gen) + +class Chestnut(RandomBonus): + "Relativity. Speed up or slow down the game." + nimage = Bonuses.chestnut + sound = None + big = 0 + bigbonus = {'big': 1} + bigdoc = "Relative relativity - not the same one for players and monsters." + def taken1(self, dragons): + timeout = 500 + if not self.big: + ft = random.choice([0.5, 2.0]) + boards.set_frametime(ft) + if ft == 2.0: + timeout = 430 + else: + if random.randrange(0, 2) == 1: + # super-fast game + boards.set_frametime(0.25) + timeout = 1800 + else: + # board unchanged, players slower + boards.set_frametime(1.0, privtime=250) + timeout = 800 + BubPlayer.MultiplyerReset = BubPlayer.FrameCounter + timeout + self.play(images.Snd.Fruit) + + +try: + import statesaver +except ImportError: + print "'statesaver' module not compiled, no clock bonus" + Clock = None +else: + import new + try: + from statesaver import standard_build # PyPy + except ImportError: + def standard_build(self): + return new.instance(self.__class__) + boards.Copyable.inst_build = standard_build + gamesrv.Sprite.inst_build = standard_build + + def copygamestate(): + # makes a copy of the game state. + ps = [] + for p1 in BubPlayer.PlayerList: + #if p1.isplaying(): + d = p1.__dict__.copy() + for key in BubPlayer.TRANSIENT_DATA: + if key in d: + del d[key] + ps.append(d) + #else: + # ps.append(None) + topstate = ( + [g for g in boards.BoardGen if not g.gi_running], + boards.curboard, + images.ActiveSprites, + images.SpritesByLoc, + BubPlayer.__dict__.items(), + gamesrv.sprites, + gamesrv.sprites_by_n, + ps, + images.Snd.__dict__.items(), + ) + #import pdb; pdb.set_trace() + return statesaver.copy(topstate) + + def restoregamestate(savedstate): + (boards.BoardGen, + boards.curboard, + images.ActiveSprites, + images.SpritesByLoc, + BubPlayerdictitems, + gamesrv.sprites, + gamesrv.sprites_by_n, + ps, + imagesSnddictitems, + ) = savedstate + for key, value in BubPlayerdictitems: + try: + setattr(BubPlayer, key, value) + except (AttributeError, TypeError): + pass + for key, value in imagesSnddictitems: + try: + setattr(images.Snd, key, value) + except (AttributeError, TypeError): + pass + + for p, d in zip(BubPlayer.PlayerList, ps): + #if d is None: + # p.reset() + #else: + p.__dict__.update(d) + if not p.isplaying(): + p.zarkoff() + + class Clock(RandomBonus): + "Time Machine. Let's do it again!" + touchable = 0 + points = 0 + nimage = Bonuses.clock + ticker = None + bigdoc = "Let's do the whole level again - with the help of ghosts from your own past." + + def __init__(self, x, y, big=0): + RandomBonus.__init__(self, -boards.bwidth, 0) + #print "starting clock" + self.savedstate = None + self.savedscreens = [] + if bigclockticker: + if not big: + self.kill() # confusion between the two levels of saving + return + if x == OFFSCREEN: + if bigclockticker.state == 'pre': + self.bigbonus = {'ticker': bigclockticker} + bigclockticker.state = 'seen' + else: + # when taken, this has the same effect as the big clock + self.ticker = bigclockticker + self.move(x, y) + self.touchable = 1 + return + self.gen = [self.delayed_show()] + + def delayed_show(self): + boards.extra_boardgen(self.state_saver()) + for i in range(10): + yield None + if self.savedstate is not None: + for i in range(55): + yield None + x, y = chooseground(200) + if x is not None: + self.move(x, y) + self.touchable = 1 + self.gen.append(self.timeouter()) + self.gen.append(self.faller()) + return + self.kill() + + def taken1(self, dragons): + if self.ticker: + return self.ticker.taken(dragons) + savedstate = self.savedstate + self.savedstate = None + if savedstate is not None: + boards.replace_boardgen(self.state_restorer(savedstate, + self.savedscreens, + self)) + self.untouchable() + return -1 + + def state_saver(self): + # called from BoardGen + self.savedstate = copygamestate() + while self.alive: + gamesrv.sprites[0] = '' + data = ''.join(gamesrv.sprites) + self.savedscreens.append(data) + yield 0 + yield 0 + self.savedscreens.append(data) + yield 0 + yield 0 + self.savedscreens = [] + def state_restorer(self, savedstate, savedscreens, blinkme): + # called from BoardGen + from player import scoreboard + status = 0 + for t in range(10): + if not (t & 1): + gamesrv.sprites[0] = '' + savedscreens.append(''.join(gamesrv.sprites)) + time = boards.normal_frame() + for i in range(t): + status += 1 + if status % 3 == 0 and blinkme.alive: + if status % 6 == 0: + blinkme.step(boards.bwidth, 0) + else: + blinkme.step(-boards.bwidth, 0) + yield time + yield boards.force_singlegen() + yield 15.0 + for p1 in BubPlayer.PlayerList: + del p1.dragons[:] + delay = 8.5 + gamesrv.clearsprites() + while savedscreens: + gamesrv.sprites[:] = ['', savedscreens.pop()] + if delay > 0.6: + delay *= 0.9 + yield delay + yield 15.0 + restoregamestate(savedstate) + scoreboard() + yield 2.5 + + class DragonGhost(ActiveSprite): + def __init__(self, entry): + ActiveSprite.__init__(self, entry.ico, entry.x, entry.y) + + def setentry(self, entry): + #ico = images.make_darker(entry.ico, True) + self.lastx = self.x + self.lasty = self.y + self.move(entry.x, entry.y, entry.ico) + self.entry = entry + self.bubber = entry.d.bubber + self.dir = entry.dir + self.poplist = [self] + + def integrate(self): + self.play(images.Snd.Shh) + for j in range(15): + DustStar(self.x, self.y, 0, -3, clock=j==14) + + def disintegrate(self): + self.play(images.Snd.Shh) + dx = self.x - self.lastx + dy = self.y - self.lasty + if dx < -4: dx = -4 + if dy < -4: dy = -4 + if dx > 4: dx = 4 + if dy > 4: dy = 4 + for j in range(15): + DustStar(self.x, self.y, dx, dy, clock=j==14) + self.kill() + + def bottom_up(self): + return self.entry.dcap['gravity'] < 0.0 + + class SavedDragonEntry(object): + __slots__ = ['d', 'x', 'y', 'ico', 'flag', 'dir', 'dcap'] + def __init__(self, d, flag, dir, dcap): + self.d = d + self.x = d.x + self.y = d.y + self.ico = d.ico + self.flag = flag + self.dir = dir + self.dcap = dcap + + class SavedFrameEntry(object): + __slots__ = ['saved_next', 'tick', 'dragonlist', 'shoots1'] + def __init__(self, tick, dragonlist): + self.saved_next = None + self.tick = tick + self.dragonlist = dragonlist + self.shoots1 = [] + + class BigClockTicker: + dragonlist = None + tick = 1000 + + def __init__(self): + global random + random = random_module.Random() + localrandom = DustStar.localrandom + self.state = 'pre' + self.randombase1 = hash(localrandom.random()) * 914971L + self.randombase2 = hash(localrandom.random()) * 914971L + self.saved_next = None + self.saved_last = self + random.seed(self.randombase1) + random_module.seed(self.randombase2) + self.latest_entries = {} + + def common_tick(self, entry): + self.dragonlist = entry.dragonlist + random.seed(self.randombase1 - entry.tick) + random_module.seed(self.randombase2 - entry.tick) + bonus_frame_tick() + random.seed(self.randombase1 + entry.tick) + random_module.seed(self.randombase2 + entry.tick) + + def save_frame_tick(self): + entry = self.save_frame() + self.common_tick(entry) + + def save_frame(self): + from player import Dragon + from bubbles import DragonBubble + tick = self.saved_last.tick + 1 + dragonlist = [] + new_entries = {} + for bubber in BubPlayer.PlayerList: + if bubber.isplaying(): + for d in bubber.dragons: + try: + dcap = self.latest_entries[d].dcap + except KeyError: + dcap = None + dir = getattr(d, 'dir', 1) + cur_dcap = getattr(d, 'dcap', Dragon.DCAP) + if dcap != cur_dcap: + dcap = cur_dcap.copy() + if isinstance(d, Dragon): + if d.monstervisible(): + flag = 'visible' + else: + flag = 'hidden' + else: + flag = 'other' + entry = SavedDragonEntry(d, flag, dir, dcap) + new_entries[d] = entry + dragonlist.append(entry) + self.latest_entries = new_entries + entry = SavedFrameEntry(tick, dragonlist) + self.saved_last.saved_next = entry + self.saved_last = entry + return entry + + def taken(self, dragons): + boards.replace_boardgen(self.jump_to_past()) + + def jump_to_past(self): + self.state = 'restoring' + boards.replace_boardgen(boards.next_board(fastreenter=True), 1) + + def restore(self): + self.ghosts = {} + random.seed(self.randombase1) + random_module.seed(self.randombase2) + + def show_ghosts(self, dragonlist, interact): + new_ghosts = {} + for entry in dragonlist: + try: + ghost = self.ghosts[entry.d] + except KeyError: + ghost = DragonGhost(entry) + ghost.setentry(entry) + new_ghosts[entry.d] = ghost + if (interact and entry.flag != 'other' and + not entry.dcap.get('infinite_shield')): + touching = images.touching(entry.x+1, entry.y+1, 30, 30) + touching.reverse() + for s in touching: + if isinstance(s, interact): + s.touched(ghost) + for d, ghost in self.ghosts.items(): + if d not in new_ghosts: + ghost.kill() + self.ghosts = new_ghosts + + def restore_frame_tick(self): + from bubbles import Bubble, DragonBubble + interact = (Bonus, Parabolic2, Bubble) + self.save_frame() + entry = self.saved_next + self.saved_next = entry.saved_next + self.common_tick(entry) + self.show_ghosts(entry.dragonlist, interact) + for args in entry.shoots1: + DragonBubble(*args) + if self.state == 'restoring' and self.ghosts: + self.state = 'post' + for ghost in self.ghosts.values(): + ghost.integrate() + + def flush_ghosts(self): + if self.latest_entries: + for ghost in self.ghosts.values(): + ghost.disintegrate() + self.latest_entries.clear() + self.dragonlist = None + +bigclockticker = None + +class MultiStones(RandomBonus): + "Gems. Very demanded stones. It will take time to pick it up." + nimages = [Bonuses.emerald, Bonuses.sapphire, Bonuses.ruby] + Stones = [(Bonuses.emerald, 1000), + (Bonuses.sapphire, 2000), + (Bonuses.ruby, 3000), + ] + killgens = 0 + big = 0 + bigdoc = "Stones so big you will jump of joy picking them up." + def __init__(self, x, y, mode=None): + mode = mode or random.choice(MultiStones.Stones) + RandomBonus.__init__(self, x, y, *mode) + self.bigbonus = {'big': 1, 'outcome_args': (mode,), + 'megacls': LongDurationCactusbonus} + self.multi = 10 + def taken1(self, dragons): + if self.big: + self.repulse(dragons) + self.multi -= (len(dragons) or 1) + if self.multi > 0: + self.taken_by = [] + self.untouchable() + self.gen.append(self.touchdelay(5)) + return -1 # don't go away + def repulse(self, dragons): + for d in dragons: + repulse_dragon(d) + +def repulse_dragon(d): + if hasattr(d, 'become_bubblingeyes'): + from bubbles import Bubble + ico = images.sprget(GreenAndBlue.normal_bubbles[d.bubber.pn][0]) + b = Bubble(ico, d.x, d.y) + d.become_bubblingeyes(b) + b.pop() + +class Slippy(TemporaryBonus): + "Greased Feet. Do you want some ice skating?" + nimage = Bonuses.orange_thing + points = 900 + capname = 'slippy' + captime = 606 + bigbonus = {'multiply': 3} + bigdoc = "Zip zip zip bouncing off walls!" + +class Aubergine(TemporaryBonus): + "Mirror. The left hand is the one with the thumb on the right, right?" + nimage = Bonuses.aubergine + big = 0 + bigbonus = {'big': 1, 'multiply': 2} + bigdoc = "Super Bonus-catching teleport ability." + def taken(self, dragon): + if self.big: + dragon.dcap['teleport'] = dragon.bubber.pcap['teleport'] = 1 + else: + dragon.dcap['lookforward'] = -dragon.dcap['lookforward'] + self.carried(dragon) + def endaction(self, dragon): + if self.big: + pass + else: + dragon.dcap['lookforward'] = -dragon.dcap['lookforward'] + +class WhiteCarrot(TemporaryBonus): + "Fly. Become a great flying dragon!" + nimage = Bonuses.white_carrot + points = 650 + capname = 'fly' + captime = 650 + bigbonus = {'capname': 'jumpdown', + 'captime': 999999} + bigdoc = "Jump down off the ground!" + def taken(self, dragon): + TemporaryBonus.taken(self, dragon) + dragon.bubber.pcap['jumpdown'] = dragon.dcap['jumpdown'] + +class AmphetamineSpeed(TemporaryBonus): + "Amphetamine Dose. Increase of your general speed!" + nimage = Bonuses.tin + points = 700 + bigbonus = {'multiply': 3} + bigdoc = "Let's move!" + def taken(self, dragon): + dragon.angry = dragon.angry + [dragon.genangry()] + dragon.carrybonus(self, 633) + def endaction(self, dragon): + dragon.angry = dragon.angry[1:] + +class Sugar1(Bonus): + nimage = Bonuses.yellow_sugar + timeout = 2600 + points = 250 + def taken(self, dragon): + #if boards.curboard.wastingplay is None: + dragon.carrybonus(self, 99999) + #else: + # from player import scoreboard + # dragon.bubber.bonbons += 1 + # scoreboard() + +class Sugar2(Sugar1): + timeout = 2500 + points = 500 + nimage = Bonuses.blue_sugar + +class Pear(RandomBonus): + "Pear. Will explode into sugars for your pockets but watch out or you'll lose them!" + points = 1000 + nimage = Bonuses.green_thing + bigbonus = {'multiply': 4} + bigdoc = "The more the better." + def taken1(self, dragons): + starexplosion(self.x, self.y, 3 * self.multiply, + outcomes = [random.choice([(Sugar1,), (Sugar2,)]) + for i in range(18 * self.multiply)]) + +class Megalightning(ActiveSprite): + def __init__(self, dragon): + ActiveSprite.__init__(self, images.sprget(BigImages.blitz), + gamesrv.game.width, gamesrv.game.height) + self.gen.append(self.killing(dragon)) + + def killing(self, dragon): + from monsters import Monster + from bubbles import Bubble + poplist = [dragon] + while 1: + for s in self.touching(10): + if isinstance(s, Monster): + s.argh(poplist) + elif isinstance(s, Bubble): + s.pop(poplist) + yield None + yield None + + def moving_to(self, x1, y1): + x0 = self.x + y0 = self.y + x1 += CELL - self.ico.w//2 + y1 += CELL - self.ico.h//2 + deltax = x1 - x0 + if deltax > -100: + deltax = -100 + deltay = y1 - y0 + a = - deltay / float(deltax*deltax) + b = 2 * deltay / float(deltax) + for x in range(self.x, -self.ico.w, -13): + x1 = x - x0 + self.move(x, y0 + int((a*x1+b)*x1)) + yield None + self.kill() + +class Fish2(RandomBonus): + "Rotten Fish. Will blast monsters up to here, so move it around!" + points = 3000 + nimage = Bonuses.fish2 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Gives seven blasts." + def taken1(self, dragons): + dragon = random.choice(dragons or [None]) + if not self.big: + m = Megalightning(dragon) + m.gen.append(m.moving_to(self.x, self.y)) + else: + N = 7 + base = random.random() * 2*math.pi + angles = [base + (math.pi*2 * n)/N for n in range(N)] + random.shuffle(angles) + for angle in angles: + m = Megalightning(dragon) + dx = 13 * math.cos(angle) + dy = 12 * math.sin(angle) + maxlive = max((gamesrv.game.width + m.ico.w) // 13, + (gamesrv.game.height + m.ico.h) // 12) + m.move(self.x + (self.ico.w - m.ico.w) // 2 - int(dx*maxlive), + self.y + (self.ico.h - m.ico.h) // 2 - int(dy*maxlive)) + m.gen.append(m.straightline(dx, dy)) + m.gen.append(m.die([None], maxlive*2)) + + +class Sheep(RandomBonus): + "Sheep. What a stupid beast!" + nimage = 'sheep-sm' + points = 800 + big = 0 + bigbonus = {'big': 1} + bigdoc = "You're a sheep. Let's bounce around." + def __init__(self, x, y): + RandomBonus.__init__(self, x, y) + if boards.curboard.bonuslevel: + self.kill() + def taken1(self, dragons): + if not self.big: + self.points0 = {} + for p in BubPlayer.PlayerList: + self.points0[p] = p.points + BubPlayer.LeaveBonus = self.boardleave() + else: + from player import Dragon + BubPlayer.SuperSheep = True + for p in BubPlayer.PlayerList: + for d in p.dragons: + if isinstance(d, Dragon): + d.become_monster('Sheep') + + def boardleave(self): + from player import BubPlayer + BubPlayer.OverridePlayerIcon = images.sprget(self.nimage) + gamesrv.set_musics([], []) + images.Snd.Yippee.play() + slist = [] + ico = images.sprget('sheep-big') + for p in BubPlayer.PlayerList: + if p.isplaying() and p.dragons: + d = random.choice(p.dragons) + dx = (d.ico.w - ico.w) // 2 + dy = (d.ico.h - ico.h) // 2 + s = ActiveSprite(ico, d.x + dx, d.y + dy) + dir = getattr(d, 'dir', None) + if dir not in [-1, 1]: + dir = random.choice([-1, 1]) + s.gen.append(s.parabolic([dir, -2.0])) + slist.append(s) + for d in p.dragons[:]: + d.kill() + delta = {} + for p in BubPlayer.PlayerList: + if p.points or p.isplaying(): + delta[p] = 2 * (self.points0[p] - p.points) + vy = 0 + while delta or slist: + ndelta = {} + for p, dp in delta.items(): + if dp: + d1 = max(-250, min(250, dp)) + p.givepoints(d1) + if p.points > 0: + ndelta[p] = dp - d1 + delta = ndelta + images.action(slist) + slist = [s for s in slist if s.y < boards.bheight] + yield 1 + +class Flower(RandomBonus): + "Flower. Fire in all directions." + nimage = 'flower' + points = 800 + big = 0 + bigbonus = {'big': 1, 'multiply': 5} + bigdoc = "Rotational Bubble Thrower (tm)." + def taken(self, dragon): + if self.big: + dragon.dcap['bigflower'] = -99 + dragon.dcap['autofire'] = 22 + else: + dragon.dcap['flower'] += 12 + dragon.carrybonus(self) + +class Flower2(TemporaryBonus): + "Bottom-up Flower. Turn you upside-down." + nimage = 'flower2' + points = 1000 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Turn the level upside-down." + def __init__(self, *args): + RandomBonus.__init__(self, *args) + if self.x != OFFSCREEN: + while not underground(self.x, self.y): + self.step(0, -CELL) + if self.y < 0: + self.kill() + return + def taken1(self, dragons): + if self.big: + boards.extra_boardgen(boards.extra_swap_up_down()) + else: + RandomBonus.taken1(self, dragons) + def faller(self): + while self.y >= 0: + if underground(self.x, self.y): + yield None + yield None + else: + self.move(self.x, (self.y-1) & ~3) + yield None + self.kill() + def taken(self, dragon): + dragon.dcap['gravity'] *= -1.0 + self.carried(dragon) + def endaction(self, dragon): + dragon.dcap['gravity'] *= -1.0 + def is_on_ground(self): + return underground(self.x, self.y) + +##class Moebius(RandomBonus): +## "Moebius Band. Bottom left is top right and bottom right is top left... or vice-versa." +## nimage = 'moebius' +## points = 900 +## def taken1(self, dragons): +## BubPlayer.Moebius = not BubPlayer.Moebius + +class StarBubble(FireBubble): + "Star Bubbles. Makes you fire bonus bubbles." + nimage = 'moebius' + bubkind = 'StarBubble' + bubcount = 3 + bigbonus = {'bubcount': 10} + bigdoc = "More bonus bubbles => more confusion." + +class Donut(RandomBonus): + "Donut. Catch every free monster in a bubble." + nimage = Bonuses.donut + points = 950 + big = 0 + bigbonus = {'big': 1} + bigdoc = "Catch dragons too." + + def taken1(self, dragons): + extra_boardgen(boards.extra_catch_all_monsters(dragons, self.big)) + if self.big: + # catch all dragons as well + from bubbles import NormalBubble + for dragon in BubPlayer.DragonList[:]: + b = NormalBubble(dragon, dragon.x, dragon.y, 542) + if not dragon.become_bubblingeyes(b): + b.kill() + + +Classes = [c for c in globals().values() + if type(c)==type(RandomBonus) and issubclass(c, RandomBonus)] +Classes.remove(RandomBonus) +Classes.remove(TemporaryBonus) +Cheat = [] +#Classes = [Cactus, Insect] # CHEAT + +AllOutcomes = ([(c,) for c in Classes if c is not Fruits] + + 2 * [(MonsterBonus, lvl) + for lvl in range(len(Bonuses.monster_bonuses))]) + +for c in Classes: + assert (getattr(c, 'points', 0) or 100) in GreenAndBlue.points[0], c + +def getdragonlist(): + if bigclockticker and bigclockticker.dragonlist is not None: + return [entry for entry in bigclockticker.dragonlist + if entry.flag != 'other'] + else: + return BubPlayer.DragonList + +def getvisibledragonlist(): + if bigclockticker and bigclockticker.dragonlist is not None: + return [entry for entry in bigclockticker.dragonlist + if entry.flag == 'visible'] + else: + return [d for d in BubPlayer.DragonList if d.monstervisible()] + +def record_shot(args): + if bigclockticker: + entry = bigclockticker.saved_last + if hasattr(entry, 'shoots1'): + entry.shoots1.append(args) + + +def chooseground(tries=15): + avoidlist = getdragonlist() + for i in range(tries): + x0 = random.randint(2, boards.width-4) + y0 = random.randint(1, boards.height-3) + if (' ' == bget(x0,y0+1) == bget(x0+1,y0+1) and + '#' == bget(x0,y0+2) == bget(x0+1,y0+2)): + x0 *= CELL + y0 *= CELL + for dragon in avoidlist: + if abs(dragon.x-x0) < 3*CELL and abs(dragon.y-y0) < 3*CELL: + break + else: + return x0, y0 + else: + return None, None + +def newbonus(): + others = [s for s in images.ActiveSprites if isinstance(s, RandomBonus)] + if others: + return + if BubPlayer.SuperSheep: + return + x, y = chooseground() + if x is None: + return + cls = random.choice(Classes) + cls(x, y) + +##def newbonus(): +## others = [s for s in images.ActiveSprites if isinstance(s, RandomBonus)] +## if others: +## return +## for cls in Classes: +## x, y = chooseground(200) +## if x is not None: +## cls(x, y) + +def cheatnew(): + if Cheat: + x, y = chooseground() + if x is None: + return + cls = random.choice(Cheat) + if not isinstance(cls, tuple): + cls = cls, + else: + Cheat.remove(cls) + if len(cls) > 1: + class C(cls[0]): + extra_cheat_arg = cls[1] + cls = (C,) + cls[0](x, y) + +def bonus_frame_tick(): + if random.random() < 0.04: + cheatnew() + if random.random() < 0.15: + newbonus() + else: + import bubbles + bubbles.newbubble() + +def start_normal_play(): + global bigclockticker + if bigclockticker and bigclockticker.state == 'restoring': + bigclockticker.restore() + return bigclockticker.restore_frame_tick + if (Clock and not boards.curboard.bonuslevel and + random.choice(Classes) is Clock): + bigclockticker = BigClockTicker() + return bigclockticker.save_frame_tick + else: + bigclockticker = None + return bonus_frame_tick + +def end_normal_play(): + if bigclockticker and bigclockticker.state == 'post': + bigclockticker.flush_ghosts() + +# hack hack hack! +def __cheat(c): + c = c.split(',') + c[0] = globals()[c[0]] + assert issubclass(c[0], Bonus) + Cheat.append(tuple(c)) +import __builtin__ +__builtin__.__cheat = __cheat diff --git a/bubbob/bubbles.py b/bubbob/bubbles.py new file mode 100644 index 0000000..9734826 --- /dev/null +++ b/bubbob/bubbles.py @@ -0,0 +1,1284 @@ +from __future__ import generators +import random, math +import gamesrv +import images +import boards +from boards import * +from images import ActiveSprite +from mnstrmap import GreenAndBlue, LetterBubbles, Stars +from mnstrmap import Lightning, Water, Fire, SpinningBalls, PlayerBubbles + + +bubble_wind = { + '<': (-1, 0), + '>': (+1, 0), + '^': ( 0,-1), + 'v': ( 0,+1), + 'x': ( 0, 0), + } + + +class Bubble(ActiveSprite): + exploding_bubbles = range(131,136) + red_bubbles = [156, 157, 156, 155] + white_bubbles = [164, 165, 164, 163] + pink_bubbles = [172, 173, 172, 171] + check_onbubble = ([(0,-1)], [(0,1)]) + + touchable = 1 + warp = 0 + default_windless = None + catch_dragons = None + nimages = GreenAndBlue.normal_bubbles + + def touched(self, dragon): + dx, dy = dragon.x, dragon.y + o = [] + if abs(self.x - dx) >= 25: + if self.x < dx: + o.append((1,0)) + else: + o.append((-1,0)) + if abs(self.y - dy) >= 25: + if self.y < dy: + o.append((0,1)) + else: + o.append((0,-1)) + if o: + self.obstacle = o + elif (self.catch_dragons and + abs(self.x - dx) < 15 and abs(self.y - dy) < 15): + if dragon not in self.catch_dragons: + self.catch_dragons.append(dragon) +## elif not self.pop(getattr(dragon, 'poplist', None)): +## if self.x < dx: +## o.append((1,0)) +## else: +## o.append((-1,0)) +## if self.y < dy: +## o.append((0,1)) +## else: +## o.append((0,-1)) +## self.obstacle = o + else: + self.pop(getattr(dragon, 'poplist', None)) + return o == self.check_onbubble[dragon.bottom_up()] + + def can_catch_dragons(self, author, catch_myself=0): + self.catch_dragons = [author] + if catch_myself: + self.catch_dragons.append(author) + self.move(author.x, author.y) + self.gen.append(self.catching_dragons()) + + def catching_dragons(self): + from player import Dragon + yield None # time to catch several dragons + dragons = [d for d in self.catch_dragons if isinstance(d, Dragon)] + self.catch_dragons = None + if len(dragons) >= 2: + import bonuses + author = dragons.pop(0) + imglist = [self.nimages[d.dcap.get('bubbericons', + d.bubber).pn][i] + for d in dragons for i in [1,2,1,0]] + self.setimages(self.cyclic(imglist)) + self.warp = 1 + caught = [(bonus.points, bonus) for d in dragons + if d.bubber is not author.bubber + for bonus in d.listcarrybonuses() + if isinstance(bonus, CatchNote)] + caught.sort() + # count caught dragons, excluding team mates, but including self + count = 0 + for d in dragons: + if (d.bubber is author.bubber or + not d.bubber.sameteam(author.bubber)): + count += 1 + if count: + if count == 1: + points = 250 + else: + self.play(images.Snd.Extra) + if count == 2: + points = 10000 + elif count == 3: + points = 30000 + else: + points = 70000 + caught.append((points, CatchNote(points))) + caught = caught[-3:] + for points, bonus in caught: + author.carrybonus(bonus, 111) + bonuses.points(self.x, self.y-HALFCELL, author, points) + for d in dragons: + d.become_bubblingeyes(self) + s_catch = author.bubber.stats.setdefault('catch', {}) + s_catch[d.bubber] = s_catch.get(d.bubber, 0) + 1 + + def pop(self, poplist=None): + if self.touchable: + self.play(images.Snd.Pop) + self.poplist = poplist + self.untouchable() + self.gen = [self.die(Bubble.exploding_bubbles)] + if poplist: + dragon = poplist[0] + points = self.popped(dragon) + if dragon: + dragon.bubber.givepoints(points) + dragon.bubber.stats['bubble'] += 1 + self.gen.append(self.poprec()) + return 1 + else: + return 0 + + def popped(self, dragon): + return 10 + + def poprec(self): + yield None + for s in self.touching(0): + if isinstance(s, Bubble): + s.pop(self.poplist) + + def normal_movements(self, dx=0, dy=-1, timeout=800): + self.obstacle = [] + time = 0 + touchbubble = timeout = (timeout or 0) * 2 + while 1: + del self.obstacle[:] + yield None + timeout -= 2 + if not timeout: + self.setimages(self.bubble_red()) + if timeout > touchbubble: + continue + if timeout&2: + for s in self.touching(13+(timeout&6)): + if isinstance(s, Bubble) and s is not self: + if (s.x-self.x)*dx > 0 or (s.y-self.y)*dy > 0: + touchbubble = timeout - (timeout&12) - 3 + break + if timeout > touchbubble: + continue + if (dx,dy) not in self.obstacle: + if dx==dy==0: + if len(self.obstacle)==1: + dx1, dy1 = self.obstacle[0] + self.step(-dx1, -dy1) + else: + self.step(dx, dy) + if self.y < -32 or self.y >= boards.bheight: + if not self.warp: + self.poplist = None + self.kill() + return + self.vertical_warp() +## dx = -dx + w = wget(self.x, self.y) + if w != ' ': + dx, dy = bubble_wind[w] + elif self.default_windless: + dx, dy = self.default_windless + + if dx == dy == 0: + # this is the same as the whole loop but runs faster + while len(self.obstacle) != 1: + del self.obstacle[:] + yield None + timeout -= 2 + if not timeout: + self.setimages(self.bubble_red()) + + def bubble_red(self, speed=5): + for n in self.imgseq(Bubble.white_bubbles, repeat=3): + yield n + for n in self.imgseq(Bubble.pink_bubbles, repeat=4): + yield n + for n in self.imgseq(Bubble.red_bubbles, repeat=4): + yield n + for n in self.imgseq([Bubble.pink_bubbles[0], Bubble.red_bubbles[0]], + speed=2, repeat=10): + yield n + self.pop() + + def startnormalbubble(self, dx=0, dy=-1, timeout=800): + self.touchable = 1 + self.gen.append(self.normal_movements(dx=dx, dy=dy, timeout=timeout)) + imglist = GreenAndBlue.normal_bubbles[self.d.bubber.pn] + self.setimages(self.cyclic([imglist[1], + imglist[2], + imglist[1], + imglist[0]])) + + def startsnookerbubble(self, timeout, hspeed, monsterpoplist=None): + self.gen.append(self.snooker_movements(dir=hspeed, timeout=timeout)) + self.gen.append(self.kill_touching_monsters(monsterpoplist)) + colorname = random.choice(Stars.COLORS) + imglist = [('smstar', colorname, k) for k in range(2)] + self.to_front() + s = images.ActiveSprite(images.sprget(imglist[-1]), self.x, self.y) + s.setimages(s.cyclic(imglist, speed=2)) + s.gen.append(s.following(self)) + def to_front(): + Bubble.to_front(self) + s.to_front() + self.to_front = to_front + + def snooker_movements(self, dir, dy=0.3, timeout=500): + icons = [images.sprget(n) + for n in GreenAndBlue.normal_bubbles[self.d.bubber.pn]] + icotimeout = 0 + ico = icons[1] + yfrac = 0.0 + self.dragon_jumped = False + for i in xrange(timeout): + hspeed = random.randrange(2, 4) + if ico is not icons[1]: + icotimeout += 1 + if icotimeout >= 16: + ico = icons[1] + icotimeout = 0 + elif abs(dy) > 16: + if dy > 0: + dy = 16.0 + else: + dy = -16.0 + self.touchable = 1 + if self.dragon_jumped: + self.untouchable() + if self.dragon_jumped[1]: # bottom-up dragon + dy = -abs(dy) + else: + dy = abs(dy) + self.dragon_jumped = False + stepx = 0 + y1 = (self.y + 27) // CELL + if dir < 0: + x1 = (self.x + 5) // CELL + if bget(x1, y1) == ' ' == bget(x1, y1-1): + stepx = -hspeed + else: + ico = icons[0] + dir = 1 + else: + x1 = (self.x + 26) // CELL + if bget(x1, y1) == ' ' == bget(x1, y1-1): + stepx = hspeed + else: + ico = icons[0] + dir = -1 + x = self.x + deltay = yfrac + if x < 32: + x += hspeed + dir = 1 + elif x > boards.bwidth - 64: + x -= hspeed + dir = -1 + else: + x += stepx + dy += 0.21 + deltay = yfrac + dy + y = self.y + while deltay >= 1.0: + deltay -= 1.0 + if onground(x, y-4): + ico = icons[2] + deltay = -deltay + dy = -abs(dy) + else: + y += 1 + while deltay < 0.0: + deltay += 1.0 + if underground(x, y+4): + ico = icons[2] + dy = abs(dy) * 0.95 + break + y -= 1 + self.move(x, y, ico) + self.vertical_warp() + yfrac = deltay + yield None + self.pop() + + def kill_touching_monsters(self, poplist=None): + from monsters import Monster + poplist = poplist or [self.d] + while 1: + yield None + for s in self.touching(10): + if isinstance(s, Monster): + s.argh(poplist) + yield None + + +class NormalBubble(Bubble): + warp = 1 + + def __init__(self, dragon, x, y, timeout=800): + imglist1 = GreenAndBlue.new_bubbles[dragon.bubber.pn] + Bubble.__init__(self, images.sprget(imglist1[0]), x, y) + self.d = dragon + self.startnormalbubble(timeout=timeout) + +class SnookerBubble(Bubble): + warp = 1 + touchable = 0 + + def __init__(self, dragon, x, y, timeout=500): + imglist1 = GreenAndBlue.new_bubbles[dragon.bubber.pn] + Bubble.__init__(self, images.sprget(imglist1[0]), x, y) + self.d = dragon + self.startsnookerbubble(timeout=timeout, hspeed=dragon.dir) + + +class BigBubbleCatcher(ActiveSprite): + numlist = [(PlayerBubbles.explosion[2], 1), + (PlayerBubbles.explosion[1], 2), + (PlayerBubbles.explosion[0], 2), + (PlayerBubbles.bubble[1], 5), + (PlayerBubbles.appearing[4], 3), + (PlayerBubbles.appearing[3], 3), + (PlayerBubbles.appearing[2], 2), + (PlayerBubbles.appearing[1], 2), + (PlayerBubbles.appearing[0], 2)] + + def __init__(self, dragon, target, timeout): + img = images.sprget(PlayerBubbles.explosion[2]) + ActiveSprite.__init__(self, img, -img.w, 0) + self.dragon = dragon + self.target = target + self.timeout = timeout + self.gen.append(self.follow()) + self.recenter(PlayerBubbles.explosion[2]) + for imgnum, delay in self.numlist: + images.sprget(imgnum) # preload + + def recenter(self, imgnum): + s = self.target + if not s.alive or not s.touchable: + self.kill() + else: + img = images.sprget(imgnum) + self.move(s.x + (s.ico.w - img.w) // 2, + s.y + (s.ico.h - img.h) // 2, + img) + + def follow(self): + for imgnum, delay in self.numlist: + for t in range(delay): + self.recenter(imgnum) + yield None + self.recenter(imgnum) + if self.alive: + s = self.target + s.in_bubble(NormalBubble(self.dragon, s.x, s.y, self.timeout)) + self.kill() + + +class CatchNote: + def __init__(self, points): + self.points = points + def endaction(self, dragon): + pass + + +class DragonBubble(Bubble): + touchable = 0 + + def __init__(self, d, x, y, dir, special_bubble=None, angle=0, + thrustfactor=None, shootthrust=None): + self.d = d + pn = d.bubber.pn + imglist1 = GreenAndBlue.new_bubbles[pn] + imglist2 = GreenAndBlue.normal_bubbles[pn] + if angle: + asin, acos = math.sin(angle), math.cos(angle) + else: + asin, acos = 0, 1 + Bubble.__init__(self, images.sprget(imglist1[0]), x + 12*dir, y) + self.setimages(self.imgseq(imglist1[1:] + imglist2[2:3], 4)) + if shootthrust is None: + shootthrust = d.dcap['shootthrust'] + hspeed = dir * shootthrust + if thrustfactor is not None: + negative = hspeed < 0 + hspeed = (abs(hspeed) - 4.0) * thrustfactor + 4.0 + if negative: + hspeed = -hspeed + self.gen.append(self.throw_bubble(hspeed, special_bubble, (acos,asin))) + + def throw_bubble(self, hspeed, special_bubble=None, (acos,asin)=(1,0)): + from monsters import Monster + nx = self.x + ny = self.y + stop = 0 + withmonster = 0 + specialangle = (acos,asin) != (1,0) + if special_bubble == 'BigFireBubble': + if not specialangle: + BigFireBubble(self.x, self.y, hspeed, self.d) + self.kill() + return + + self.warp = 0 + monsterpoplist = [self.d] + while abs(hspeed) >= 4.0: + touched_monsters = [s for s in self.touching(9) + if isinstance(s, Monster)] + if touched_monsters: + if special_bubble == 'SnookerBubble': + for monster in touched_monsters: + monster.argh(monsterpoplist) + else: + monster = random.choice(touched_monsters) + in_bubble = monster.in_bubble(self) + withmonster = self.withmonster = 1 + if in_bubble is None: + self.warp = 1 + try: + key = monster.mdef.jailed[0] + except AttributeError: + pass + else: + s_monster = self.d.bubber.stats.setdefault( + 'monster', {}) + s_monster[key] = s_monster.get(key, 0) + 1 + break + if specialangle: + nx, ny = vertical_warp(nx + hspeed*acos, ny + hspeed*asin) +## if moebius: +## acos = -acos + else: + nx += hspeed + hspeed *= 0.965 + xc = int(nx-3.8)//CELL+1 + yc = (self.y+HALFCELL)//CELL + if bget(xc,yc) == '#' == bget(xc, yc+1): + stop += 1 + if stop <= 1: + self.move(int(nx+0.5), int(ny+0.5)) + yield None + + if special_bubble == 'SnookerBubble': + if stop > 1: + hspeed = -hspeed + self.startsnookerbubble(self.d.dcap['bubbledelay'] or 800, + hspeed, monsterpoplist) + return + if not withmonster: + from bonuses import Bonus, BonusMaker + touched_bonuses = [s for s in self.touching(15) + if isinstance(s, Bonus) and s.bubblable] + if touched_bonuses: + random.choice(touched_bonuses).in_bubble(self) + withmonster = 1 + else: + touched_bonuses = [s for s in self.touching(7) + if isinstance(s, BonusMaker)] + if touched_bonuses: + bonusmaker = random.choice(touched_bonuses) + if bonusmaker.in_bubble(self): + withmonster = 1 + if not self.alive: + return + if special_bubble: + cls = globals()[special_bubble] + if not withmonster: + b = cls(self.d.bubber.pn) + b.move(self.x, self.y) + b.can_catch_dragons(self.d, hspeed == 0) + self.kill() + return + bubbledelay = self.d.dcap['bubbledelay'] + if bubbledelay: + timeout = 1 + if bubbledelay > 1: + self.gen.append(self.delayed_pop(7)) + else: + timeout = 800 + self.startnormalbubble(timeout=timeout) + if not withmonster: + self.can_catch_dragons(self.d, hspeed == 0) + + def delayed_pop(self, delay): + for i in range(delay): + yield None + self.pop() + + +class FishBubble(Bubble): + touchable = 0 + + def __init__(self, dragon): + ico = images.sprget(GreenAndBlue.new_bubbles[dragon.bubber.pn][0]) + Bubble.__init__(self, ico, dragon.x + dragon.dir*12, dragon.y) + timeout = random.randrange(50, 150) + self.gen.append(self.normal_movements(timeout=timeout)) + self.gen.append(self.fuzz()) + + def fuzz(self): + while 1: + prevx = self.x + yield None + yield None + yield None + if prevx == self.x: + self.step(random.choice([-1, 1]), 0) + + def bubble_red(self, *args, **kwds): + self.gen.append(self.die([])) + + +class BubblingEyes(ActiveSprite): + + def __init__(self, bubber, saved_caps, bubble): + ico = images.sprget(('eyes', 0, 0)) + ActiveSprite.__init__(self, ico, bubble.x, bubble.y) + self.bubber = bubber + self.dcap = saved_caps + self.gen = [self.playing_bubble(bubble)] + + def bottom_up(self): + return self.dcap['gravity'] < 0.0 + + def playing_bubble(self, bubble): + from player import Dragon + bottom_up = self.bottom_up() + flip = 'vflip'*bottom_up + timer = 0 + red = 0 + normalbub = bubble.imgsetter + redblinker = bubble.cyclic([Bubble.pink_bubbles[0], Bubble.red_bubbles[0]], 2) + bubber = self.bubber + ndir = random.choice([-1, 1]) + prev_dx_dy = None + while not hasattr(bubble, 'poplist'): + dx = bubber.wannago(self.dcap) + if dx: + ndir = dx + if bubber.key_jump: + dy = -1 + else: + dy = 0 + if bubber.key_fire: + red += 1 + if red > 20: + d = Dragon(bubber, self.x, self.y, ndir, self.dcap) + Bubble.pop(bubble, [d]) # hack to pop SolidBubbles too + d.kill() + break + if bubble.imgsetter is not redblinker: + normalbub = bubble.imgsetter + bubble.setimages(redblinker) + else: + #red = 0 + if bubble.imgsetter is redblinker: + bubble.setimages(normalbub) + key = ('eyes', dx, dy) + if timer < 50: + if (timer % 9) < 3: + key = 'eyes-blink' + elif random.random() < 0.1: + key = 'eyes-blink' + timer += 1 + if bubble.x <= 3*HALFCELL and dx < 0: + dx = 0 + if bubble.x >= boards.bwidth - 7*HALFCELL and dx > 0: + dx = 0 + if bottom_up: + dy = -dy + nx = bubble.x + dx + ny = bubble.y + dy + if timer&1: + nx += dx + else: + ny += dy + nx, ny = vertical_warp(nx, ny) + bubble.move(nx, ny) + self.move(nx+dx, ny+dy, images.sprget((flip, key))) +## if moebius: +## self.dcap['left2right'] *= -1 + if dx == dy == 0: + bubble.default_windless = prev_dx_dy + else: + prev_dx_dy = dx, dy + bubble.default_windless = 0, 0 + yield None + # jumping out of the bubble + if bottom_up: + kw = {'gravity': -0.3} + else: + kw = {} + from player import BubPlayer + bi = self.dcap.get('bubbericons', bubber) + if BubPlayer.SuperFish and 'fish' in bi.transformedicons: + self.setimages(None) + self.seticon(bi.transformedicons['fish'][0, +1]) + else: + self.setimages(self.cyclic( + [(flip, n) for n in GreenAndBlue.comming[bi.pn]], 2)) + dxy = [(random.random()-0.5) * 9.0, + (random.random()+0.5) * (-5.0,5.0)[bottom_up]] + for n in self.parabolic(dxy, 1, **kw): + yield n + if dxy[1] * (1,-1)[bottom_up] >= 4.0: + break + if dxy[0] < 0: + ndir = -1 + else: + ndir = 1 + d = Dragon(bubber, self.x, self.y, ndir, self.dcap) + d.dcap['shield'] = 50 + bubber.dragons.append(d) + self.kill() + + def kill(self): + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + +class BonusBubble(Bubble): + max = None + timeout = None + flip = '' + + def __init__(self, pn, nimages=None, top=None): + if nimages is None: + nimages = self.nimages[pn] + b = boards.curboard + if top is None: + top = b.top + if top == 0: + testline = b.walls[-1] + x, y = self.findhole(testline), boards.bheight + dx, dy = 0, -1 + elif top == 1: + testline = b.walls[0] + x, y = self.findhole(testline), -2*CELL + dx, dy = 0, 1 + elif top == 2: + x, y = -2*CELL, random.randint(2*CELL, boards.bheight-4*CELL) + dx, dy = 1, 0 + else: # top == 3: + x, y = (boards.bwidth - CELL, + random.randint(2*CELL, boards.bheight-4*CELL)) + dx, dy = -1, 0 + Bubble.__init__(self, images.sprget((self.flip, nimages[0])), x, y) + self.gen.append(self.normal_movements(dx=dx, dy=dy, + timeout=self.timeout)) + if len(nimages) == 3: + nimages = [nimages[1], nimages[2], nimages[1], nimages[0]] + if len(nimages) > 1: + self.setimages(self.cyclic([(self.flip, n) for n in nimages])) + + def findhole(self, testline): + holes = [x for x in range(len(testline)-1) if testline[x:x+2]==' '] + if not holes: + holes = range(2, len(testline)-3) + return random.choice(holes) * CELL + + def thrown_bubble(self, x, y, hspeed, acossin): + self.untouchable() + self.move(x, y) + self.gen = [self.throwing_bubble(hspeed, acossin, self.imgsetter)] + + def throwing_bubble(self, hspeed, (acos,asin), restore_img): + nx = self.x + ny = self.y + while abs(hspeed) >= 4.0: + nx, ny = vertical_warp(nx + hspeed*acos, ny + hspeed*asin) +## if moebius: +## acos = -acos + if nx <= CELL: + acos = abs(acos) + if nx >= boards.bwidth-3*CELL: + acos = -abs(acos) + hspeed *= 0.965 + self.move(int(nx+0.5), int(ny+0.5)) + yield None + self.touchable = 1 + self.gen.append(self.normal_movements(timeout=self.timeout)) + self.setimages(restore_img) + + +class PlainBubble(BonusBubble): + timeout = 500 + def condition(): + return boards.curboard.holes + +def extend_name(l): + text = 'extend' + return text[:l] + text[l].upper() + text[l+1:] + +class LetterBubble(BonusBubble): + max = 2 + def condition(): + return boards.curboard.letter + def __init__(self, pn, l=None): + if l is None: + l = random.randint(0,5) + self.l = l + lettername = extend_name(self.l) + BonusBubble.__init__(self, pn, nimages=getattr(LetterBubbles, lettername)) + def popped(self, dragon): + if dragon: + dragon.bubber.giveletter(self.l) + return 50 + +class FireFlame(ActiveSprite): + timeout = 17 + def __init__(self, x0, y0, poplist, dirs=None, countdown=0, flip=''): + ico = images.sprget((flip, Fire.ground[0])) + ActiveSprite.__init__(self, ico, x0*CELL, y0*CELL) + if not countdown: + dirs = [] + self.poplist = poplist + self.gen.append(self.burning(dirs, countdown)) + self.setimages(self.cyclic([(flip, n) for n in Fire.ground], 1)) + def burning(self, dirs, countdown): + from monsters import Monster + x0 = self.x//CELL + y0 = self.y//CELL + for dir in dirs: + if bget(x0+dir, y0+1) == '#' and bget(x0+dir, y0) == ' ': + FireFlame(x0+dir, y0, self.poplist, [dir], countdown-1) + for i in range(self.timeout): + yield None + if self.poplist: + for s in self.touching(0): + if isinstance(s, Monster): + s.argh(self.poplist) + yield None + self.kill() + +class FireDrop(ActiveSprite): + def __init__(self, x, y, poplist=None): + ActiveSprite.__init__(self, images.sprget(Fire.drop), x, y) + self.poplist = poplist or [None] + self.gen.append(self.dropping()) + def dropping(self): + x0 = self.x//CELL + while bget(x0, self.y//CELL) == '#' or bget(x0, self.y//CELL+1) != '#': + if self.y >= boards.bheight: + self.kill() + return + self.move(self.x, (self.y + 8) & ~7) + yield None + y0 = self.y//CELL + #if bget(x0-1, y0) == ' ': + FireFlame(x0, y0, self.poplist, [-1, 1], 5) + self.kill() + +class FireBubble(BonusBubble): + max = 4 + nimages = GreenAndBlue.fire_bubbles + def condition(): + return boards.curboard.fire + def popped(self, dragon): + if dragon: + x0 = self.x // CELL + 1 + y0 = self.y // CELL + 1 + if bget(x0, y0) == '#': + x1 = (self.x + HALFCELL) // CELL + if x1 == x0: + tries = [x1+1, x1-1] + else: + tries = [x1, x1+2] + for x1 in tries: + if bget(x1, y0) == ' ': + x0 = x1 + break + FireDrop(x0*CELL, self.y) + return 10 + +##class BombBubble(FireBubble): +## flip = 'vflip' +## def popped(self, dragon): +## if dragon: +## import bonuses +## bonuses.bomb_explosion(self.x, self.y + CELL, starmul=1) +## return 100 + +##class WaterCell(ActiveSprite): +## ICONS = { +## ( 0,1, None) : Water.bottom, +## ( 1,0, None) : Water.start_left, +## (-1,0, None) : Water.start_right, +## ( 0,0, None) : Water.bottom, + +## (0,1, 0,1) : Water.v_flow, +## (0,1, 1,0) : Water.bl_corner, +## (0,1, -1,0) : Water.br_corner, + +## (-1,0, 0,1) : Water.tl_corner, +## (-1,0, 1,0) : Water.start_right, +## #(-1,0, -1,0) : Water.h_flow, + +## (1,0, 0,1) : Water.tr_corner, +## #(1,0, 1,0) : Water.h_flow, +## (1,0, -1,0) : Water.start_left, + +## (0,0, 0,1) : Water.top, +## (0,0, 1,0) : Water.top, +## (0,0, -1,0) : Water.top, + +## (None, 0,1) : Water.top, +## (None, -1,0) : Water.start_left, +## (None, 1,0) : Water.start_right, +## (None, 0,0) : Water.top, +## } + +## def __init__(self, x, y): +## ActiveSprite.__init__(self, images.sprget(Water.top), x, y) +## self.touchable = 1 + +## def ready(self, celllist): +## self.gen.append(self.flooding(celllist)) + +## def flooding(self, celllist): +## from monsters import Monster +## x0 = self.x // 16 +## y0 = self.y // 16 +## ping = 0 +## dir = random.choice([-1, 1]) +## take_with_us = [[] for cell in celllist] +## poplist = [None] +## icons = {} +## for key, value in self.ICONS.items(): +## icons[key] = images.sprget(value) +## icodef = images.sprget(Water.h_flow) +## stop = 0 +## while not stop: +## dx = dy = 0 +## if bget(x0, y0+1) == ' ': +## dy = y0*16 < boards.bheight +## ping = 0 +## elif bget(x0+dir, y0) == ' ': +## dx = dir +## elif bget(x0-dir, y0) == ' ': +## ping += 1 +## if ping < 3: +## dir = -dir +## dx = dir +## # change the head icon +## head = celllist[0] +## second = celllist[1] +## head.seticon(icons.get((x0-second.x//16, y0-second.y//16, +## dx, dy), icodef)) +## # move the tail to the new head position +## x0 += dx +## y0 += dy +## newhead = celllist.pop() +## celllist.insert(0, newhead) +## newhead.move(x0*16, y0*16, icons.get((dx,dy, None), icodef)) +## # change the new tail icon +## tail = celllist[-1] +## second = celllist[-2] +## tail.seticon(icons.get((None, (second.x-tail.x)//16, +## (second.y-tail.y)//16), icodef)) +## # take monsters with us +## for i in range(0, len(celllist), 3): +## for s in celllist[i].touching(0): +## if isinstance(s, Monster): +## s.untouchable() +## s.gen = [] +## take_with_us[i].append(s) +## elif isinstance(s, Bubble): +## s.pop(poplist) +## yield 0 +## stop = dx == dy == 0 +## for cell, takelist in zip(celllist, take_with_us): +## stop &= cell.x == newhead.x and cell.y == newhead.y +## for s in takelist: +## if s.alive: +## s.move(x2bounds(cell.x-8), cell.y-16) +## if stop: +## s.argh(poplist, onplace=1) +## for c in celllist: +## c.kill() +## def touched(self, dragon): +## dragon.watermove(x2bounds(self.x-HALFCELL), self.y-CELL+1) +## return 1 + +class WaterCell(ActiveSprite): + TESTLIST = [(-CELL,0), (CELL,0), (0,CELL), (0,-CELL)] + ICONS = [Water.v_flow, + Water.start_left, + Water.start_right, + Water.h_flow, + Water.top, + Water.tr_corner, + Water.tl_corner, + Water.h_flow, + + Water.bottom, + Water.br_corner, + Water.bl_corner, + Water.h_flow, + Water.v_flow, + Water.v_flow, + Water.v_flow, + Water.v_flow] + + def __init__(self, x, y, dir, watercells, poplist, repeat): + ActiveSprite.__init__(self, images.sprget(Water.top), x, y) + self.poplist = poplist + self.take_with_me = [] + self.ping = 0 + self.repeat = repeat + self.watercells = watercells + self.touchable = repeat % 3 == 1 + if (x, y, dir) not in watercells: + watercells[x,y,dir] = self + if None not in watercells or not watercells[None].alive: + self.in_charge() + else: + watercells[x,y,dir].join(self) + + def join(self, other): + self.take_with_me += other.take_with_me + self.ping = min(self.ping, other.ping) + self.repeat += other.repeat + self.touchable = self.touchable or other.touchable + del other.take_with_me[:] + other.kill() + + def in_charge(self): + self.gen = [self.flooding()] + self.watercells[None] = self + + def kill(self): + from monsters import Monster + for s in self.take_with_me[:]: + if isinstance(s, Monster) and s.alive: + s.argh(self.poplist, onplace=1) + del self.take_with_me[:] + ActiveSprite.kill(self) + if not self.watercells[None].alive: + del self.watercells[None] + for s in self.watercells.values(): + if s.alive: + s.in_charge() + break + + def flooding(self): + from monsters import Monster + watercells = self.watercells + while watercells[None] is self: + + new = [] + nwatercells = {None: self} + for key, s in watercells.items(): + if key: + x, y, dir = key + if s.repeat: + new.append((x, y, dir, watercells, + s.poplist, s.repeat-1)) + s.repeat = 0 + x0 = x // CELL + y0 = y // CELL + if bget(x0, y0+1) == ' ': + if y >= boards.bheight: + s.kill() + continue + s.ping = 0 + y += CELL + elif bget(x0+dir, y0) == ' ': + x += dir*CELL + elif bget(x0-dir, y0) == ' ': + s.ping += 1 + if s.ping == 3: + s.kill() + continue + dir = -dir + x += dir*CELL + else: + s.kill() + continue + key = x, y, dir + if key in nwatercells: + nwatercells[key].join(s) + else: + nwatercells[key] = s + + watercells.clear() + watercells.update(nwatercells) + for args in new: + WaterCell(*args) + + for key, s in watercells.items(): + if key: + x, y, dir = key + flag = 0 + for k in range(4): + dx, dy = s.TESTLIST[k] + if ((x+dx, y+dy, -1) in watercells or + (x+dx, y+dy, 1) in watercells): + flag += 1<<k + ico = images.sprget(s.ICONS[flag]) + s.move(x, y, ico) + if s.touchable: + for s1 in s.touching(0): + if isinstance(s1, Monster): + s1.untouchable() + s1.gen = [] + s.take_with_me.append(s1) + elif isinstance(s1, Bubble): + s1.pop(s.poplist) + for s1 in s.take_with_me: + if s1.alive: + s1.move(x2bounds(x-HALFCELL), y-CELL) + yield None + if not watercells[None].alive: + self.in_charge() + + def touched(self, dragon): + dragon.watermove(x2bounds(self.x-HALFCELL), self.y-CELL+1) + return 1 + +def watercell(x, y, poplist, dir=None, repeat=4): + b = boards.curboard + if not hasattr(b, 'watercells'): + b.watercells = {} + dir = dir or random.choice([-1, 1]) + WaterCell(x, y, dir, b.watercells, poplist, repeat) + +class WaterBubble(BonusBubble): + max = 4 + nimages = GreenAndBlue.water_bubbles + def condition(): + return boards.curboard.water + def popped(self, dragon): + if dragon: + x0 = self.x // CELL + 1 + y0 = self.y // CELL + 1 + for x1 in [x0, x0+1, x0-1]: + if bget(x1,y0) == ' ' or bget(x1,y0+1) == ' ': + x0 = x1 + break + watercell(x0*CELL, y0*CELL, [None], repeat=19) + return 10 + +##class SolidBubble(WaterBubble): +## timeout = 450 +## solidbubble = 1 +## flip = 'vflip' + +## def bubble_red(self, *args): +## self.solidbubble = 0 +## return WaterBubble.bubble_red(self, *args) + +## def pop(self, poplist=None): +## return (not (self.solidbubble and poplist is not None) +## and WaterBubble.pop(self, poplist)) + +## def popped(self, dragon): +## return 100 + +class FiredLightning(ActiveSprite): + def __init__(self, x, y, dir, poplist, diry=0): + ActiveSprite.__init__(self, images.sprget(Lightning.fired), x, y) + self.dir = int(13*dir) + self.diry = int(13*diry) + self.gen.append(self.moving(poplist)) + def moving(self, poplist): + from monsters import Monster + while (-2*CELL < self.x < boards.bwidth and + -2*CELL < self.y < boards.bheight): + for s in self.touching(2): + if isinstance(s, Monster): + s.argh(poplist) + elif isinstance(s, Bubble): + s.pop(poplist) + self.step(self.dir, self.diry) + yield None + self.kill() + +class LightningBubble(BonusBubble): + max = 4 + nimages = GreenAndBlue.light_bubbles + def condition(): + return boards.curboard.lightning + def popped(self, dragon): + if dragon: + FiredLightning(self.x, self.y, -dragon.dir, self.poplist) + return 10 + +class BigLightBubble(LightningBubble): + flip = 'vflip' + def popped(self, dragon): + base = random.random() * 2.0*math.pi + for a in range(7): + FiredLightning(self.x, self.y, math.cos(base+a), self.poplist, + diry = -math.sin(base+a)) + return 100 + + +class BigFireBubble(ActiveSprite): + touchable = 1 + + def __init__(self, x, y, hspeed, author): + if hspeed > 0: + imgs = PlayerBubbles.right_weapon + else: + imgs = PlayerBubbles.left_weapon + ActiveSprite.__init__(self, images.sprget(imgs[-1]), x, y) + self.setimages(self.cyclic(imgs, 2)) + self.author = author + self.gen.append(self.moving(hspeed)) + self.play(images.Snd.Shh) + + def moving(self, hspeed): + from monsters import Monster + if abs(hspeed) < 3: + if hspeed > 0: + hspeed = 3 + else: + hspeed = -3 + fx = self.x + poplist = [self.author] + while 1: + fx += hspeed + self.move(int(fx), self.y) + xc = int(fx)//CELL + 1 + yc = (self.y+HALFCELL)//CELL + if bget(xc,yc) == '#' == bget(xc, yc+1): + break + yield None + for s in self.touching(4): + if isinstance(s, Monster): + s.argh(poplist) + self.kill() + + def touched(self, dragon): + if dragon is not self.author: + import bonuses + bonuses.repulse_dragon(dragon) + + +class SpinningBall(ActiveSprite): + def __init__(self, x, y, poplist): + ActiveSprite.__init__(self, images.sprget(SpinningBalls.free[-1]), x,y) + self.poplist = poplist + self.gen.append(self.dropping()) + imgs = SpinningBalls.free + if random.random() < 0.5: + imgs = list(imgs) + imgs.reverse() + self.setimages(self.cyclic(imgs, random.randrange(2,5))) + self.touchable = 1 + def dropping(self): + from monsters import Monster + for ny in range(self.y, boards.bheight, 5): + self.move(self.x, ny) + yield None + for s in self.touching(0): + if isinstance(s, Monster): + s.argh(self.poplist) + elif isinstance(s, Bubble): + s.pop(self.poplist) + self.kill() + def touched(self, dragon): + dragon.die() + +class StarBubble(BonusBubble): + timeout = 250 + def __init__(self, pn): + self.colorname = random.choice(Stars.COLORS) + BonusBubble.__init__(self, pn, [('starbub', self.colorname, i) + for i in range(3)]) +## def __init__(self, pn): +## BonusBubble.__init__(self, pn) +## self.colorname = random.choice(Stars.COLORS) +## starimg = [('smstar', self.colorname, 0), +## ('smstar', self.colorname, 1)] +## smallstar = ActiveSprite(images.sprget(starimg[-1]), +## self.x+8, self.y+8) +## smallstar.setimages(smallstar.cyclic(starimg)) +## smallstar.gen.append(smallstar.following(self, 8, 8)) + def popped(self, dragon): + if dragon: + from bonuses import BonusMaker, AllOutcomes, Parabolic2 + BonusMaker(self.x, self.y, getattr(Stars, self.colorname), + outcome=random.choice(AllOutcomes)) + for i in range(2): + Parabolic2(self.x, self.y, [('smstar', self.colorname, i) + for i in range(2)]) + return 100 + +class MonsterBubble(BonusBubble): + timeout = 100 + def __init__(self, pn, mcls): + import monsters, mnstrmap + BonusBubble.__init__(self, pn) + mdef = getattr(mnstrmap, mcls.__name__) + m = mcls(mdef, self.x, self.y, 1) + m.in_bubble(self) + +class MoreBubblesBubble(BonusBubble): + + def can_catch_dragons(self, author, catch_myself=0): + # at this point, explode the bubble into more bubbles + d = author + for angle in [math.pi, math.pi/2, -math.pi/2, 0]: + for factor in [0.2, 0.55, 0.9, 1.25, 1.6]: + DragonBubble(d, self.x, self.y, d.dir, + angle = angle, thrustfactor = factor) + self.pop() + + +Classes = ([PlainBubble] * 7 + + [FireBubble, WaterBubble, LightningBubble] * 4 + + [LetterBubble]) + +def newbubble(): #force=0): + #if force: + #cls = PlainBubble + #else: + cls = random.choice(Classes) + if not cls.__dict__['condition'](): + return + if cls.max is not None: + others = [s for s in images.ActiveSprites if isinstance(s, cls)] + if len(others) >= cls.max: + return + sendbubble(cls) + +def newforcedbubble(): + choices = [PlainBubble] * 5 + for cls in [FireBubble, WaterBubble, LightningBubble]: + if cls.__dict__['condition'](): + n = 4 + else: + n = 1 + choices.extend([cls] * n) + cls = random.choice(choices) + return sendbubble(cls) + +def sendbubble(cls, *args, **kw): + from player import BubPlayer + players = [p for p in BubPlayer.PlayerList if p.isplaying()] + if not players: + return None + pn = random.choice(players).pn + return cls(pn, *args, **kw) + +def newbonusbubble(): + boards.curboard.top = random.choice([0,0,0, 1,1,1, 2,2, 3,3]) + r = random.random() + if r < 0.14: + sendbubble(random.choice(Classes)) + elif r < 0.16: + from player import BubPlayer + import monsters + mcls = random.choice(monsters.MonsterClasses) + for d in BubPlayer.DragonList: + sendbubble(MonsterBubble, mcls) + else: + sendbubble(StarBubble) diff --git a/bubbob/command.py b/bubbob/command.py new file mode 100644 index 0000000..71239ed --- /dev/null +++ b/bubbob/command.py @@ -0,0 +1,10 @@ +import os, sys + +levels, ext = os.path.splitext(os.path.basename(sys.argv[1])) +for ext in ['.py', '.bin']: + levelfile = 'levels/%s%s' % (levels, ext) + if os.path.exists(levelfile): + break +sys.argv[1] = levelfile + +execfile('bb.py') diff --git a/bubbob/doc/.cvsignore b/bubbob/doc/.cvsignore new file mode 100644 index 0000000..2d19fc7 --- /dev/null +++ b/bubbob/doc/.cvsignore @@ -0,0 +1 @@ +*.html diff --git a/bubbob/doc/bonus-doc.py b/bubbob/doc/bonus-doc.py new file mode 100755 index 0000000..f03b013 --- /dev/null +++ b/bubbob/doc/bonus-doc.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python + +import os, sys, string, struct +os.chdir(os.pardir) +sys.path.insert(0, os.getcwd()) +sys.path.insert(1, os.path.abspath(os.path.join(os.pardir, 'common'))) + +from images import sprmap +import bonuses, images + +try: + import psyco; psyco.full() +except ImportError: + pass + +def create_image(name,source,extralines=0,alt=''): + if len(sys.argv) == 2 and sys.argv[1] == '-i': + return + print name, source + src = open(source[0],'r') + assert src.readline().strip() == 'P6' + line = src.readline() + while line[0] == '#': + line = src.readline() + size = string.split(line) + w = string.atoi(size[0]) + h = string.atoi(size[1]) + c = src.readline().strip() + data = src.read() + src.close() + img = os.popen("convert PPM:- doc/images/"+name+'.png','w') + print >> img, 'P6' + print >> img, source[1][2], source[1][3]+extralines + print >> img, c + cx = source[1][0]+source[1][2]//2 + cy = source[1][1]+source[1][3]*6//7 + for y in range(source[1][1],source[1][1]+source[1][3]): + for x in range(source[1][0],source[1][0]+source[1][2]): + rgb = data[y*3*w+3*x:y*3*w+3*x+3] + if rgb == '\x01\x01\x01': + d = (x-cx)*(x-cx)+(y-cy)*(y-cy)*6 + if d > 255: d = 255 + rgb = chr(d)*3 + img.write(rgb) + for y in range(y+1, y+1+extralines): + for x in range(source[1][0],source[1][0]+source[1][2]): + d = (x-cx)*(x-cx)+(y-cy)*(y-cy)*6 + if d > 255: d = 255 + rgb = chr(d)*3 + img.write(rgb) + img.close() + return html_tag(name, alt) + +def html_tag(name, alt): + url = 'images/%s.png' % (name,) + f = open('doc/'+url, 'rb') + alldata = f.read() + f.close() + url = 'data:image/png;base64,' + alldata.encode('base64').replace('\n','') + return '<IMG SRC="%s" ALT="%s">' % (url, alt) + + +def split_name(name): + "Split a name into its words based on capitalisation." + words = [] + word = '' + for c in name: + if c.isupper() and word != '': + words.append(word) + word = c + else: + word += c + words.append(word) + return words + +dfile = open('doc/bonuses.html','w') +print >> dfile, """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<HTML> + <HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <META NAME="Author" CONTENT="The Bub's Brothers"> + <TITLE>The Bub's Brothers Bonuses</TITLE> + </HEAD> + <BODY bgcolor=white text=black> + <TABLE cellspacing=0 border=0> + <TR> + <TD width=132 align=right> + </TD> + <TD width=20> + </TD> + <TD bgcolor="#80FF80" align=center> + <I>regular bonus</I> + </TD> + <TD bgcolor="#80FF80" width=20> + </TD> + <TD bgcolor="#80FF80" align=center> + <I>big bonus</I> + </TD> + </TR> +""" +#" A stupid comment to stop emacs from mis-fontifying. + +class Potion4: + "Subgame! For a while, let's play one of the seven mini-games." + nimage = 'potion4' + +# Some classes exists in more than one example just to increase their +# probability. Removes the duplicate with the help of this dict. +processed = {} + +for bonus in bonuses.Classes: + if processed.has_key(bonus): + continue + name = split_name(bonus.__name__) + name.reverse() + processed[bonus] = string.join(name) + if bonus is bonuses.Potion: + processed[Potion4] = string.join(name) + '.' + +def sorter(a,b): + if a[1] == b[1]: + return 0 + elif a[1] < b[1]: + return -1 + else: + return 1 + +sorted_classes = processed.items() +sorted_classes.sort(sorter) + +for clasindex, clas in enumerate(sorted_classes): + bonus = clas[0] + images = '' + name = bonus.__name__ + if bonus.__dict__.has_key('nimages'): + # A multi image entry. + i = 0 + l = len(bonus.nimages) + for image in bonus.nimages: + if image == 'potion4': + continue + images += create_image(name+`i`, sprmap[image], alt=name) + i += 1 + if (l-3*(i/3) >= 3) and (i % 3) == 0: + images += '<br>' + elif bonus.__dict__.has_key('nimage'): + images = create_image(name, sprmap[bonus.nimage], alt=name) + doc = bonus.__doc__ + if doc == None: + doc = '' + bigdoc = getattr(bonus, 'bigdoc', None) or '' + if hasattr(bonus, 'bigbonus'): + assert bigdoc, "missing 'bigdoc' on %r" % (bonus,) + if clasindex % 2 == 1: + bgcolor = '"#E0FFE0"' + else: + bgcolor = 'white' + print >> dfile, '<TR><TD width=132 align=right>', + print >> dfile, images, + print >> dfile, '</TD><TD width=20></TD>', + print >> dfile, '<TD bgcolor=%s>%s</TD>' % (bgcolor, doc), + print >> dfile, '</TD><TD width=20 bgcolor=%s></TD>' % bgcolor, + print >> dfile, '<TD bgcolor=%s>%s</TD></TR>' % (bgcolor, bigdoc) + +print >> dfile, """ </TABLE> + </BODY> +</HTML> +""" + +dfile.close() diff --git a/bubbob/doc/images/.cvsignore b/bubbob/doc/images/.cvsignore new file mode 100644 index 0000000..e33609d --- /dev/null +++ b/bubbob/doc/images/.cvsignore @@ -0,0 +1 @@ +*.png diff --git a/bubbob/ext1/.cvsignore b/bubbob/ext1/.cvsignore new file mode 100644 index 0000000..553d0ec --- /dev/null +++ b/bubbob/ext1/.cvsignore @@ -0,0 +1,2 @@ +*.py[co] +image1-[2-9].ppm diff --git a/bubbob/ext1/__init__.py b/bubbob/ext1/__init__.py new file mode 100644 index 0000000..9f90b0c --- /dev/null +++ b/bubbob/ext1/__init__.py @@ -0,0 +1,403 @@ +from __future__ import generators +import os, math, random +import images, gamesrv +from images import ActiveSprite +from boards import CELL, HALFCELL +from mnstrmap import GreenAndBlue +from bubbles import BubblingEyes, Bubble +from bonuses import Bonus + +LocalDir = os.path.basename(os.path.dirname(__file__)) + + +localmap = { + 'ark-paddle': ('image1-%d.ppm', (0, 0, 80, 32)), + } + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) +snd_wall = gamesrv.getsample(os.path.join(LocalDir, 'wall.wav')) +snd_brick = gamesrv.getsample(os.path.join(LocalDir, 'brick.wav')) + + +def aget(x, y): + if 0 <= x < curboard.width and y >= 0: + if y >= curboard.height: + return ' ' + return curboard.walls[y][x] + else: + return '#' + +def sign(x): + if x >= 0.0: + return 1 + else: + return -1 + + +class PaddleEyes(BubblingEyes): + + def __init__(self, bubber, saved_caps, paddle): + BubblingEyes.__init__(self, bubber, saved_caps, paddle) + self.deltax = (paddle.ico.w - self.ico.w) // 2 + self.deltay = (paddle.ico.h - self.ico.h) // 2 + self.step(self.deltax, self.deltay) + + def playing_bubble(self, paddle, accel=0.75, vmax=4.5): + import boards + dx = self.deltax + dy = self.deltay + bubber = paddle.bubber + vx = 0.0 + fx = paddle.x + while paddle.alive: + wannago = bubber.wannago(self.dcap) + if paddle.timeleft is None: + keydy = 0 + else: + keydy = -1 + key = ('eyes', wannago, keydy) + if fx < 2*CELL: + if vx < 0.0: + vx = -vx * 0.45 + wannago = 1 + elif fx + paddle.ico.w > boards.bwidth - 2*CELL: + if vx > 0.0: + vx = -vx * 0.45 + wannago = -1 + if not wannago: + if -accel <= vx <= accel: + vx = 0 + elif vx < 0.0: + wannago = 0.7 + else: + wannago = -0.7 + vx += accel * wannago + if vx < -vmax: + vx = -vmax + elif vx > vmax: + vx = vmax + fx += vx + paddle.move(int(fx), paddle.y) + self.move(paddle.x+dx, paddle.y+dy, images.sprget(key)) + yield None + self.kill() + + def bottom_up(self): + return 0 + + +class Paddle(ActiveSprite): + + def __init__(self, arkanoid, bubber, px, py): + ico = images.sprget(('ark-paddle', bubber.pn)) + ActiveSprite.__init__(self, ico, px - (ico.w-2*CELL)//2, + py - (ico.h-2*CELL)//2) + self.arkanoid = arkanoid + self.bubber = bubber + self.timeleft = None + self.gen.append(self.bounce_down()) + self.gen.append(self.bkgndstuff()) + self.arkanoid.paddles.append(self) + + def bounce_down(self): + import boards + target_y = boards.bheight - self.ico.h + fy = self.y + vy = 0.0 + while fy < target_y or abs(vy) > 0.3: + if fy < target_y: + vy += 0.3 + elif vy > 0.0: + vy = -vy / 3.0 + fy += vy + self.move(self.x, int(fy)) + yield None + while self.y > target_y: + self.step(0, -2) + yield None + self.move(self.x, target_y) + self.gen.append(self.wait_and_shoot()) + + def wait_and_shoot(self): + timeout = 30 + while timeout > 0: + timeout -= self.arkanoid.ready + yield None + self.gen.append(self.catch(Ball(self))) + + def catch(self, ball): + import boards + while ball.alive: + if ball.y > boards.bheight//2+1 and ball.vy > 0.0: + deltay = self.y - Ball.Y_MARGIN - ball.y + self.timeleft = deltay / ball.vy + #if -1.25 <= self.timeleft <= 0.5: + if -12 <= deltay <= 1: + ball.bouncepad(self.arkanoid.paddles) + yield None + self.timeleft = None + if ball.missed: + self.kill() + + def kill(self): + images.Snd.Pop.play(1.0, pad=0.0) + images.Snd.Pop.play(1.0, pad=1.0) + ico = images.sprget(Bubble.exploding_bubbles[0]) + for i in range(11): + s = ActiveSprite(ico, + self.x + random.randrange(self.ico.w) - CELL, + self.y + random.randrange(self.ico.h) - CELL) + s.gen.append(s.die(Bubble.exploding_bubbles)) + try: + self.arkanoid.paddles.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + def bkgndstuff(self): + while 1: + if self.timeleft is not None: + self.arkanoid.order.append((self.timeleft, self)) + yield None + touching = images.touching(self.x+1, self.y+1, + self.ico.w-2, self.ico.h-2) + touching.reverse() + for s in touching: + if isinstance(s, Bonus): + s.touched(self) + + def score(self, hits): + bricks = self.arkanoid.bricks + bricks[self.bubber] = bricks.get(self.bubber, 0) + hits + self.bubber.givepoints(125*(2**hits)) + + +class Ball(ActiveSprite): + + Y_MARGIN = 20 + SPEED = 5.8 + + def __init__(self, paddle): + self.paddle = paddle + imglist1 = GreenAndBlue.new_bubbles[paddle.bubber.pn] + ActiveSprite.__init__(self, images.sprget(imglist1[0]), + paddle.x + paddle.ico.w//2, + paddle.y - Ball.Y_MARGIN) + self.missed = 0 + self.setimages(self.imgseq(imglist1[1:], 6)) + self.bounceangle(0.2) + self.gen.append(self.flying()) + + def bouncepad(self, paddles): + for paddle in paddles: + dx = (self.x + self.ico.w//2) - (paddle.x + paddle.ico.w//2) + dxmax = paddle.ico.w//2 + angle = float(dx) / dxmax + if 0.0 <= angle <= 1.0: + angle = angle * 1.111 + 0.07 + elif -1.0 <= angle <= 0.0: + angle = angle * 1.111 - 0.07 + else: + continue + self.bounceangle(angle) + self.play(snd_wall) + break + + def bounceangle(self, angle): + self.vx = math.sin(angle) * self.SPEED + self.vy = - math.cos(angle) * self.SPEED + + def flying(self): + import boards + fx = self.x + fy = self.y + while self.y < boards.bheight: + fx += self.vx + fy += self.vy + self.move(int(fx), int(fy)) + yield None + cx = self.x // CELL + 1 + cy = self.y // CELL + 1 + dx = sign(self.vx) + dy = sign(self.vy) + hits = 0.0 + if aget(cx, cy) == '#': + hits += self.ahit(cx, cy, 0, 0) + if aget(cx+dx, cy) == '#': + hits += self.ahit(cx+dx, cy, 0, dy) + self.vx = -self.vx + if aget(cx, cy+dy) == '#': + hits += self.ahit(cx, cy+dy, dx, 0) + self.vy = -self.vy + if hits: + hits = int(hits) + if hits: + self.paddle.score(hits) + self.play(snd_brick) + else: + self.play(snd_wall) + self.missed = 1 + self.kill() + + def ahit(self, cx, cy, dx, dy): + total = 0.01 + for i in (-1, 0, 1): + x = cx + i*dx + y = cy + i*dy + if (2 <= x < curboard.width - 2 and 0 <= y < curboard.height and + aget(x, y) == '#'): + curboard.killwall(x, y) + self.paddle.arkanoid.killedbricks += 1 + total += 1.0 + return total + + def pop(self): + self.play(images.Snd.Pop) + self.gen = [self.die(Bubble.exploding_bubbles)] + + +class Arkanoid: + + def bgen(self, limittime = 60.1): # 0:60 + import boards + from player import BubPlayer + + self.bricks = {} + for t in boards.initsubgame(music, self.displaypoints): + yield t + + tc = boards.TimeCounter(limittime) + self.ready = 0 + self.builddelay = {} + self.nbbricks = 0 + self.order = [] + self.paddles = [] + #finish = 0 + for t in self.frame(): + self.order_paddles() + t = boards.normal_frame() + self.build_paddles() + yield t + #if len(self.paddles) == 0: + # finish += 1 + # if finish == 20: + # break + #else: + # finish = 0 + tc.update(t) + if tc.time == 0.0: + break + if (BubPlayer.FrameCounter & 15) == 7: + for s in images.ActiveSprites: + if isinstance(s, Bonus): + s.timeout = 0 # bonuses stay + elif isinstance(s, Bubble): + s.pop() + + tc.restore() + self.ready = 0 + for s in images.ActiveSprites[:]: + if isinstance(s, Ball): + s.pop() + for t in boards.result_ranking(self.bricks, self.nbbricks): + self.build_paddles() + yield t + self.remove_paddles() + self.unframe() + + def displaypoints(self, bubber): + return self.bricks.get(bubber, 0) + + def frame(self): + for y in range(curboard.height-1, curboard.height//2, -1): + yield None + yield None + for x in range(2, curboard.width-2): + if aget(x, y) == '#': + curboard.killwall(x, y) + brickline = curboard.width-4 + expected = (brickline * curboard.height) // 5 + y = curboard.height//2 + nbbricks = 0 + while y>=0 and nbbricks + (y+1)*brickline >= expected: + yield None + for x in range(2, curboard.width-2): + if aget(x, y) == '#': + nbbricks += 1 + y -= 1 + while y >= -1: + yield None + yield None + for x in range(2, curboard.width-2): + if y < 0 or aget(x, y) == ' ': + curboard.putwall(x, y) + nbbricks += brickline + curboard.reorder_walls() + y -= 1 + + nbbricks -= brickline + self.ready = 1 + self.nbbricks = nbbricks + self.killedbricks = 0 + while self.killedbricks < self.nbbricks: + yield None + + def unframe(self): + for x in range(2, curboard.width-2): + curboard.killwall(x, -1) + + def build_paddles(self): + from player import BubPlayer + for p in BubPlayer.PlayerList: + dragons = [d for d in p.dragons if not isinstance(d, PaddleEyes)] + if dragons and len(p.dragons) == len(dragons): + if self.builddelay.get(p): + self.builddelay[p] -= 1 + else: + self.builddelay[p] = 53 + dragon = random.choice(dragons) + paddle = Paddle(self, p, dragon.x, dragon.y) + eyes = PaddleEyes(p, dragon.dcap, paddle) + p.dragons.append(eyes) + p.emotic(dragon, 4) + for d in dragons: + d.kill() + + def order_paddles(self): + self.order.sort() + self.order.reverse() + for timeleft, paddle in self.order: + try: + self.paddles.remove(paddle) + except ValueError: + pass + else: + self.paddles.insert(0, paddle) + paddle.to_front() + del self.order[:] + + def remove_paddles(self): + killclasses = (Paddle, PaddleEyes, Ball, Bonus) + for s in images.ActiveSprites[:]: + if isinstance(s, killclasses): + s.kill() + +# This game is suitable for at least min_players players +min_players = 1 + +def run(): + global curboard + import boards + from boards import curboard + boards.replace_boardgen(Arkanoid().bgen()) + +def setup(): + from player import BubPlayer + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + if filename.find('%d') >= 0: + for p in BubPlayer.PlayerList: + images.sprmap[key, p.pn] = (filename % p.pn, rect) + else: + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext1/brick.wav b/bubbob/ext1/brick.wav Binary files differnew file mode 100644 index 0000000..5f62625 --- /dev/null +++ b/bubbob/ext1/brick.wav diff --git a/bubbob/ext1/image1-0.ppm b/bubbob/ext1/image1-0.ppm Binary files differnew file mode 100644 index 0000000..03d6b3a --- /dev/null +++ b/bubbob/ext1/image1-0.ppm diff --git a/bubbob/ext1/music.wav b/bubbob/ext1/music.wav Binary files differnew file mode 100644 index 0000000..261b9cf --- /dev/null +++ b/bubbob/ext1/music.wav diff --git a/bubbob/ext1/wall.wav b/bubbob/ext1/wall.wav Binary files differnew file mode 100644 index 0000000..f93df15 --- /dev/null +++ b/bubbob/ext1/wall.wav diff --git a/bubbob/ext2/.cvsignore b/bubbob/ext2/.cvsignore new file mode 100644 index 0000000..539da74 --- /dev/null +++ b/bubbob/ext2/.cvsignore @@ -0,0 +1 @@ +*.py[co] diff --git a/bubbob/ext2/__init__.py b/bubbob/ext2/__init__.py new file mode 100644 index 0000000..81cd833 --- /dev/null +++ b/bubbob/ext2/__init__.py @@ -0,0 +1,578 @@ +from __future__ import generators +import os, math, random +import images, gamesrv +from images import ActiveSprite +from boards import CELL, HALFCELL, bget +from mnstrmap import GreenAndBlue, Ghost +from bonuses import Bonus +from bubbles import Bubble + +LocalDir = os.path.basename(os.path.dirname(__file__)) + + +localmap = { + ('pac-lg', -1,0) : ('image1.ppm', ( 0, 0, 32, 32)), + ('pac-sm', -1,0) : ('image1.ppm', (32, 0, 32, 32)), + ('pac-lg', 0,-1) : ('image1.ppm', ( 0, 32, 32, 32)), + ('pac-sm', 0,-1) : ('image1.ppm', (32, 32, 32, 32)), + ('pac-lg', 1,0) : ('image1.ppm', ( 0, 64, 32, 32)), + ('pac-sm', 1,0) : ('image1.ppm', (32, 64, 32, 32)), + ('pac-lg', 0,1) : ('image1.ppm', ( 0, 96, 32, 32)), + ('pac-sm', 0,1) : ('image1.ppm', (32, 96, 32, 32)), + 'pac-black' : ('image2.ppm', ( 0, 0, 32, 32)), + 'pac-dot' : ('image2.ppm', ( 0, 32, 8, 8)), + } + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) + + +class PacSprite(ActiveSprite): + + def __init__(self, ico, x, y): + import boards + if y < -2*CELL: + y = -2*CELL + elif y > boards.bheight: + y = boards.bheight + x = (x+HALFCELL) & -CELL + y = (y+HALFCELL) & -CELL + ActiveSprite.__init__(self, ico, x, y) + self.wannadx = self.wannady = 0 + + def moving(self): + import boards + dx = dy = 0 + turned = None + was_clear = 0 + while 1: + if dx or dy: + frontx = self.x+CELL+dx*(CELL+1) + fronty = self.y+CELL+dy*(CELL+1) + clear = (bget((frontx+dy)//CELL, (fronty-dx)//CELL) == ' ' and + bget((frontx-dy)//CELL, (fronty+dx)//CELL) == ' ') + if clear: + blocked = 0 + else: + blocked = (was_clear or (self.x<=2*CELL and dx<0) or + (self.x>=boards.bwidth-4*CELL and dx>0)) + else: + blocked = 1 + if blocked: + if turned: + dx, dy = turned + turned = None + continue + self.lastmove = None + else: + if turned: + self.resetimages(dx, dy) + turned = None + self.lastmove = dx, dy + self.step(2*dx, 2*dy) + self.vertical_warp() + was_clear = clear + yield None + if self.wannadx != dx or self.wannady != dy: + if ((self.wannadx and not (self.y % CELL)) or + (self.wannady and not (self.x % CELL))): + turned = dx, dy + dx = self.wannadx + dy = self.wannady + + +class Pac(PacSprite): + no_hat = 1 + + def __init__(self, pacman, bubber, x, y, dcap): + ico = GreenAndBlue.normal_bubbles[bubber.pn][1] + PacSprite.__init__(self, images.sprget(('eyes', 0, 0)), x, y) + self.bubble = ActiveSprite(images.sprget(ico), x, y) + self.bubber = bubber + self.pacman = pacman + self.ready = 0 + self.gen.append(self.playing()) + self.pacman.pacs.append(self) + self.dcap = dcap + + def resetimages(self, dx, dy): + self.ready = 1 + self.setimages(self.cyclic([('pac-lg', dx, dy), + 'pac-black', + ('pac-sm', dx, dy)], 5)) + + def to_front(self): + self.bubble.to_front() + ActiveSprite.to_front(self) + + def kill(self): + self.play(images.Snd.Pop) + self.bubble.gen = [self.bubble.die(Bubble.exploding_bubbles)] + self.pacman.latestposition[self.bubber] = self.x, self.y + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + try: + self.pacman.pacs.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + def playing(self): + bubber = self.bubber + for t in self.moving(): + if self.pacman.ready: + d = [(bubber.key_left, -1, 0), + (bubber.key_right, 1, 0), + (bubber.key_jump, 0,-1), + (bubber.key_fire, 0, 1)] + d.sort() + if d[-1][0] > d[-2][0]: + self.wannadx, self.wannady = d[-1][1:] + + self.bubble.move(self.x, self.y) + yield None + + if self.ready: + touching = images.touching(self.x+CELL-3, self.y+CELL-3, 6, 6) + touching.reverse() + for s in touching: + if isinstance(s, Bonus): + s.touched(self) + elif isinstance(s, PacGhost): + self.kill() + return + + +class PacGhost(PacSprite): + + def __init__(self, pacman, x, y): + left = random.random() < 0.5 + if left: + ico = Ghost.left[0] + else: + ico = Ghost.right[0] + PacSprite.__init__(self, images.sprget(ico), x, y) + self.pacman = pacman + self.gen.append(self.waiting()) + if left: + self.wannadx = -1 + else: + self.wannadx = 1 + self.resetimages(self.wannadx, self.wannady) + + def resetimages(self, dx, dy): + if dx > 0: + self.setimages(self.cyclic(Ghost.right, 3)) + elif dx < 0: + self.setimages(self.cyclic(Ghost.left, 3)) + #else: don't change image + + def waiting(self, delay=45): + while not self.pacman.ready: + yield None + for i in range(delay): + yield None + self.gen.append(self.walking()) + + def walking(self): + round = 0 + self.touchable = 1 + lastmove_x = 0 + for t in self.moving(): + if random.random() < 0.1: + lastmove_x = self.wannadx + if self.lastmove is None and random.random() < 0.75: + round = 0 # try to move again immediately + if self.lastmove is None or random.random() < 0.01: + dragons = self.pacman.pacs or images.ActiveSprites + distances = [(abs(dragon.x-self.x)+abs(dragon.y-self.y), + dragon) for dragon in dragons] + distance, dragon = min(distances) + if lastmove_x: + self.wannadx = 0 + if (dragon.y < self.y) ^ (random.random() < 0.3): + self.wannady = -1 + else: + self.wannady = 1 + else: + self.wannady = 0 + if (dragon.x < self.x) ^ (random.random() < 0.3): + self.wannadx = -1 + else: + self.wannadx = 1 + else: + lastmove_x = self.lastmove[0] +## for i in range(10): +## dragon = random.choice(dragons) +## dx = dragon.x - self.x +## dy = dragon.y - self.y +## if dx or dy: +## dist = math.sqrt(dx*dx+dy*dy) +## dx /= dist +## dy /= dist +## wx, wy = random.choice([(-1,0), (1,0), (0,-1), (0,1)]) +## ex = wx-dx +## ey = wy-dy +## if ex*ex + ey*ey < random.random()*3.14: +## break +## self.wannadx = wx +## self.wannady = wy + if round == 0: # go just a bit faster than the players + round = 6 + else: + round -= 1 + yield None + + +class FruitBonus(Bonus): + pass + + +class Pacman: + + def bgen(self, limittime = 45.1): # 0:45 + import boards + from player import BubPlayer + + self.ready = 0 + self.dots = [] + monsters = BubPlayer.MonsterList[:] + random.shuffle(monsters) + keep = len([p for p in BubPlayer.PlayerList if p.isplaying()]) + monsters = monsters[:2 + keep//2] + for d in monsters: + PacGhost(self, d.x, d.y) + + for t in boards.initsubgame(music, self.displaypoints): + yield t + + tc = boards.TimeCounter(limittime) + self.builddelay = {} + self.latestposition = {} + self.pacs = [] + #finish = 0 + for t in self.frame(): + t = boards.normal_frame() + self.build_pacs() + yield t + #if len(self.pacs) == 0: + # finish += 1 + # if finish == 20: + # break + #else: + # finish = 0 + tc.update(t) + if tc.time == 0.0: + break + if (BubPlayer.FrameCounter & 15) == 7: + for s in images.ActiveSprites: + if isinstance(s, Bubble): + s.pop() + + tc.restore() + self.ready = 0 + results = {} + for b in self.dots: + for d in b.taken_by: + bubber = d.bubber + results[bubber] = results.get(bubber, 0) + 1 + for t in boards.result_ranking(results, len(self.dots)): + self.remove_pacs() + yield t + for s in images.ActiveSprites[:]: + if isinstance(s, Bonus): + s.kill() + + def displaypoints(self, bubber): + result = 0 + for b in self.dots: + for d in b.taken_by: + if d.bubber is bubber: + result += 1 + return result + + def frame(self): + import boards + from bonuses import Fruits + for t in self.digwalls(): + yield t + + def anywall(x1,y1,x2,y2): + for tx in range(x1, x2): + for ty in range(y1, y2): + if bget(tx, ty) == '#': + return 1 + return 0 + + def give_one_point(dragon): + dragon.bubber.displaypoints += 1 + scoreboard() + + dots = self.dots + ico = images.sprget('pac-dot') + for x in range(boards.width): + for y in range(boards.height): + if not anywall(x, y, x+2, y+2): + if anywall(x-1, y-1, x+3, y+3): + b = Bonus((x+1)*CELL - ico.w//2, + (y+1)*CELL - ico.h//2, + 'pac-dot', points=-100, falling=0) + b.sound = 'Extra' + b.timeout = 0 + b.taken = give_one_point + dots.append(b) + + for s in images.ActiveSprites: + if isinstance(s, PacGhost): + s.to_front() + yield None + + self.ready = 1 + + for i in range(len([s for s in images.ActiveSprites + if isinstance(s, PacGhost)])): + for j in range(32): + yield None + for j in range(100): + x = random.randrange(4, boards.width-6) + y = random.randrange(3, boards.height-5) + if not anywall(x-2, y-2, x+4, y+4): + nimage, points = random.choice(Fruits.Fruits) + points += 650 # boost to the range 750-1000 + b = FruitBonus(x*CELL, y*CELL, + nimage, points, falling=0) + b.timeout = 0 + break + + while dots: + dots = [b for b in dots if b.alive] + yield None + + def build_pacs(self): + from player import BubPlayer + for p in BubPlayer.PlayerList: + dragons = [d for d in p.dragons if not isinstance(d, Pac)] + if dragons and len(p.dragons) == len(dragons): + if self.builddelay.get(p): + self.builddelay[p] -= 1 + else: + self.builddelay[p] = 109 + dragon = random.choice(dragons) + if p in self.latestposition: + dragon.move(*self.latestposition[p]) + pac = Pac(self, p, dragon.x, dragon.y, dragon.dcap) + p.dragons.append(pac) + p.emotic(dragon, 4) + for d in dragons: + d.kill() + + def remove_pacs(self): + from player import Dragon + killclasses = (PacSprite, FruitBonus, Dragon) + for s in images.ActiveSprites[:]: + if isinstance(s, killclasses): + s.kill() + + def digwalls(self): + import boards + holes = {} + for x in range(2, boards.width-2): + y = boards.height-1 + if bget(x, 0) == '#' or bget(x, y) == '#': + if bget(x, 0) == ' ': curboard.putwall(x, 0) + if bget(x, y) == ' ': curboard.putwall(x, y) + curboard.reorder_walls() + for y in range(1, boards.height-1): + if bget(x, y) == ' ': + holes[x, y] = 0 + if x % 7 == 0: + yield None + + # 'holes' maps coordinates (x,y) to 0 (not processed) + # or 1 (processed). + # All processed holes are pacman-connected. + + def rdig(x1,y1,x2,y2, reversed, holes=holes): + # digs the rectangle (x1,y1,x2,y2) and marks it as + # processed. Also recursively mark as processed all existing + # holes that are pacman-connected to the rectangle. + xrange = range(x1,x2) + yrange = range(y1,y2) + if not reversed: + xrange.reverse() + yrange.reverse() + if len(xrange) > len(yrange): + xylist = [(x, y) for x in xrange for y in yrange] + else: + xylist = [(x, y) for y in yrange for x in xrange] + t = 0 + for x, y in xylist: + if bget(x, y) == '#': + curboard.killwall(x, y) + if t == 0: + yield None + t = 2 + else: + t -= 1 + holes.setdefault((x, y), 0) + fill = [] + for x in range(x1,x2-1): + for y in range(y1,y2-1): + fill.append((x, y)) + for x, y in fill: + if ((x, y) in holes and + (x, y+1) in holes and + (x+1, y) in holes and + (x+1, y+1) in holes): + if (holes[x, y] == 0 or + holes[x, y+1] == 0 or + holes[x+1, y] == 0 or + holes[x+1, y+1] == 0): + + holes[x, y] = 1 + holes[x, y+1] = 1 + holes[x+1, y] = 1 + holes[x+1, y+1] = 1 + fill.append((x+1,y)) + fill.append((x-1,y)) + fill.append((x,y+1)) + fill.append((x,y-1)) + + def joined(x1,y1,x2,y2, holes=holes, boards=boards): + # returns + # 1 if the rectangle (x1,y1,x2,y2) is pac-connected to + # some already-processed holes + # 0 if it is not + # -1 if (x1,y1,x2,y2) is out of the screen + if x1<2 or y1<1 or x2>boards.width-2 or y2>boards.height-1: + return -1 + accum1 = accum2 = 0 + for x in range(x1,x2): + if holes.get((x,y1-1)): + accum1 += 1 + if accum1 == 2: + return 1 + else: + accum1 = 0 + if holes.get((x,y2)): + accum2 += 1 + if accum2 == 2: + return 1 + else: + accum2 = 0 + accum1 = accum2 = 0 + for y in range(y1,y2): + if holes.get((x1-1,y)): + accum1 += 1 + if accum1 == 2: + return 1 + else: + accum1 = 0 + if holes.get((x2,y)): + accum2 += 1 + if accum2 == 2: + return 1 + else: + accum2 = 0 + return 0 + + if not holes: + holes[boards.width//2, boards.height//2] = 0 + holeslist = holes.keys() + random.shuffle(holeslist) + startx, starty = holeslist.pop() + # make the hole larger (2x2) towards the center of the board + if startx > boards.width//2: + startx -= 1 + if starty > boards.height//2: + starty -= 1 + # initial 2x2 hole + for t in rdig(startx, starty, startx+2, starty+2, 0): + yield t + + dlist = [ + (0,0,1,0, 0,-1), (0,0,1,0, 0,0), # right + (0,0,0,1, -1,0), (0,0,0,1, 0,0), # bottom + (-1,0,0,0, -1,-1), (-1,0,0,0, -1,0), # left + (0,-1,0,0, -1,-1), (0,-1,0,0, 0,-1), # top + ] + while holeslist: + random.shuffle(dlist) + pending = holeslist + holeslist = [] + progress = 0 + for x, y in pending: + if holes[x, y] != 0: + continue + for dx1, dy1, dx2, dy2, dx, dy in dlist: + x1 = x + dx + y1 = y + dy + x2 = x1 + 2 + y2 = y1 + 2 + result = 0 + while result == 0: + result = joined(x1,y1,x2,y2) + if result == 1: + # rectangle (x1,y1,x2,y2) is good + for t in rdig(x1, y1, x2, y2, + dx1<0 or dy1<0 or dx2<0 or dy2<0): + yield t + progress = 1 + break + x1 += dx1 + y1 += dy1 + x2 += dx2 + y2 += dy2 + else: + # rectangle (x1,y1,x2,y2) is too large for the screen + # failure + continue + break + else: + # no successful direction found from this point + holeslist.append((x, y)) # try again later + if not progress: + # deadlocked situation, add a new random hole + x = random.randrange(2, boards.width-2) + y = random.randrange(1, boards.height-1) + holeslist.insert(0, (x, y)) + holes.setdefault((x, y), 0) + yield None + + # pattern transformation: + # X. .. + # ... --> ... + # .X .X + progress = 1 + while progress: + progress = 0 + for y in range(1, boards.height-1): + for x in range(3, boards.width-3): + if (' ' == bget(x, y) + == bget(x+1, y) + == bget(x-1, y) + == bget(x, y+1) + == bget(x, y-1)): + if '#' == bget(x-1, y-1) == bget(x+1, y+1): + curboard.killwall(x-1, y-1) + progress = 1 + elif '#' == bget(x+1, y-1) == bget(x-1, y+1): + curboard.killwall(x+1, y-1) + progress = 1 + yield None + +# This game is suitable for at least min_players players +min_players = 1 + +def run(): + global curboard + import boards + from boards import curboard + boards.replace_boardgen(Pacman().bgen()) + +def setup(): + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext2/image1.ppm b/bubbob/ext2/image1.ppm Binary files differnew file mode 100644 index 0000000..1daa864 --- /dev/null +++ b/bubbob/ext2/image1.ppm diff --git a/bubbob/ext2/image2.ppm b/bubbob/ext2/image2.ppm Binary files differnew file mode 100644 index 0000000..4ed7060 --- /dev/null +++ b/bubbob/ext2/image2.ppm diff --git a/bubbob/ext2/music.wav b/bubbob/ext2/music.wav Binary files differnew file mode 100644 index 0000000..921322e --- /dev/null +++ b/bubbob/ext2/music.wav diff --git a/bubbob/ext3/.cvsignore b/bubbob/ext3/.cvsignore new file mode 100644 index 0000000..553d0ec --- /dev/null +++ b/bubbob/ext3/.cvsignore @@ -0,0 +1,2 @@ +*.py[co] +image1-[2-9].ppm diff --git a/bubbob/ext3/__init__.py b/bubbob/ext3/__init__.py new file mode 100644 index 0000000..f5408c1 --- /dev/null +++ b/bubbob/ext3/__init__.py @@ -0,0 +1,367 @@ +from __future__ import generators +import os, math, random +import images, gamesrv +from images import ActiveSprite +import boards +from boards import CELL, HALFCELL, bget +from mnstrmap import GreenAndBlue, Fire +from bonuses import Bonus +from player import Dragon, BubPlayer +import monsters +from bubbles import Bubble + +LocalDir = os.path.basename(os.path.dirname(__file__)) + +localmap = { + 'gala': ('image1-%d.ppm', (0, 0, 32, 32)), + } + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) +snd_shoot = gamesrv.getsample(os.path.join(LocalDir, 'shoot.wav')) + + +class Ship(ActiveSprite): + + def __init__(self, galaga, bubber, x, y): + ico = images.sprget(('gala', bubber.pn)) + ActiveSprite.__init__(self, ico, x, y) + self.galaga = galaga + self.bubber = bubber + self.gen.append(self.movedown()) + self.gen.append(self.playing_ship()) + self.gen.append(self.doomed()) + self.galaga.ships.append(self) + + def movedown(self): + import boards + target_y = boards.bheight - self.ico.h + while self.y < target_y: + yield None + self.move(self.x, self.y + 3) + self.move(self.x, target_y) + + def playing_ship(self): + import boards + bubber = self.bubber + xmin = HALFCELL + xmax = boards.bwidth - HALFCELL - self.ico.w + fire = 0 + while 1: + wannago = bubber.wannago(self.dcap) + nx = self.x + 2*wannago + if nx < xmin: + nx = xmin + elif nx > xmax: + nx = xmax + self.move(nx, self.y) + if fire: + fire -= 1 + elif bubber.key_fire: + self.firenow() + fire = 28 + yield None + + def firenow(self): + ico = images.sprget(GreenAndBlue.new_bubbles[self.bubber.pn][0]) + s = Shot(ico, self.x, self.y) + s.d = self + s.gen = [s.straightup(self)] + self.play(snd_shoot) + + def doomed(self): + dangerous = Alien, monsters.MonsterShot + while 1: + touching = images.touching(self.x+3, self.y+3, 26, 26) + for s in touching: + if isinstance(s, dangerous): + self.kill() + return + yield None + yield None + + def kill(self): + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + images.Snd.Pop.play(1.0, pad=0.0) + images.Snd.Pop.play(1.0, pad=1.0) + ico = images.sprget(Bubble.exploding_bubbles[0]) + for i in range(11): + s = ActiveSprite(ico, + self.x + random.randrange(self.ico.w) - CELL, + self.y + random.randrange(self.ico.h) - CELL) + s.gen.append(s.die(Bubble.exploding_bubbles)) + try: + self.galaga.ships.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + +class Shot(Bubble): + touchable = 0 + + def straightup(self, ship): + ymin = -self.ico.h + while self.y > ymin: + self.step(0, -10) + touching = images.touching(self.x+CELL-1, self.y+CELL-1, 2, 2) + touching = [s for s in touching if isinstance(s, Alien)] + if touching: + alien = random.choice(touching) + self.gen = [] + self.touchable = 1 + self.move(alien.x, alien.y) + self.pop([ship]) + alien.kill() + scores = ship.galaga.scores + scores[ship.bubber] = scores.get(ship.bubber, 0) + 1 + ship.bubber.givepoints(100) + return + yield None + + def popped(self, dragon): + return 200 + + +class Alien(monsters.Monster): + ANGLES = 32 + SPEED = 5 + ANGLE_TABLE = [(SPEED * math.cos(a*2.0*math.pi/ANGLES), + -SPEED * math.sin(a*2.0*math.pi/ANGLES)) + for a in range(ANGLES)] + touchable = 0 + + def __init__(self, galaga, squadron, rank, relativey): + centerx = boards.bwidth // 2 + go_left = squadron % 2 + dx = (1,-1)[go_left] + halfspan = centerx*7//12 + relativex = - halfspan + 4*CELL*rank + if relativex > halfspan: + raise StopIteration + + if squadron % 3 == 2: + from mnstrmap import Ghosty as mcls + else: + from mnstrmap import Flappy as mcls + mdef = mcls(centerx // CELL - 1, -7, go_left) + mdef.left_weapon = mdef.right_weapon = [Fire.drop] + monsters.Monster.__init__(self, mdef) + + self.path = [(None, centerx + (dx*centerx)*2//3, boards.bheight//3), + (None, centerx - (dx*centerx)*4//5, boards.bheight//6), + (galaga, -dx*relativex, -relativey)] + self.gen = [self.waiting(rank * 20)] + self.in_place = 0 + galaga.nbmonsters += 1 + + def default_mode(self, angle=ANGLES//4): + self.touchable = 1 + speed = self.SPEED + relative, tx, ty = self.path[0] + fx = self.x + fy = self.y + ymax = boards.bheight - self.ico.h + cont = 1 + if relative: + shoot_prob = 0.0085 + else: + shoot_prob = 0.021 + while cont: + if self.angry: + self.kill() # never getting out of a bubble + return + if relative: + dx = relative.globalx + tx + dy = relative.globaly + ty + else: + dx = tx + dy = ty + dx -= self.x + dy -= self.y + + tests = [] + for a1 in (-1, 0, 1): + a1 = (angle+a1) % self.ANGLES + testx, testy = self.ANGLE_TABLE[a1] + testx -= dx + testy -= dy + tests.append((testx*testx+testy*testy, a1)) + ignored, angle = min(tests) + if dx*dx+dy*dy > speed*speed: + dx, dy = self.ANGLE_TABLE[angle] + elif relative: + self.in_place = 1 + if self.y > ymax and relative.ships: + for ship in relative.ships[:]: + ship.kill() + relative.builddelay[ship.bubber] = 9999 + relative.gameover = 1 + #x0 = self.x//CELL + 1 + #if x0 < 2: x0 = 0 + #if x0 >= boards.width-2: x0 = boards.width-3 + #bubbles.FireFlame(x0, boards.height-2, None, [-1, 1], + # boards.width) + else: + self.path.pop(0) + self.gen.append(self.default_mode(angle)) + cont = 0 + fx += dx + fy += dy + self.move(int(fx), int(fy)) + if dx and (self.dir > 0) != (dx > 0): + self.dir = -self.dir + self.resetimages() + if random.random() < shoot_prob and self.y >= 0: + monsters.DownShot(self) + yield None + + +class Galaga: + gameover = 0 + + def bgen(self): + self.scores = {} + for t in boards.initsubgame(music, self.displaypoints): + yield t + + self.ships = [] + self.builddelay = {} + self.nbmonsters = 0 + #finish = 0 + for t in self.frame(): + t = boards.normal_frame() + self.build_ships() + yield t + #if len(self.ships) == 0: + # finish += 1 + # if finish == 50: + # break + #else: + # finish = 0 + if (BubPlayer.FrameCounter & 15) == 7: + for s in images.ActiveSprites: + if isinstance(s, Bubble) and not isinstance(s, Shot): + s.pop() + + for t in boards.result_ranking(self.scores, self.nbmonsters): + self.build_ships() + yield t + for s in images.ActiveSprites[:]: + if isinstance(s, (Alien, Ship)): + s.kill() + + def displaypoints(self, bubber): + return self.scores.get(bubber, 0) + + def frame(self): + curboard.walls_by_pos.clear() + curboard.winds = ['v' * curboard.width] * curboard.height + for y in range(len(curboard.walls)): + curboard.walls[y] = ' ' * len(curboard.walls[y]) + l1 = curboard.sprites['walls'] + l2 = curboard.sprites['borderwalls'] + while l1 or l2: + for l in [l1, l2]: + for w in l[:]: + w.step(0, 5) + if w.y >= boards.bheight: + l.remove(w) + w.kill() + yield None + + self.globalx = boards.bwidth // 2 + self.globaly = 0 + shifter = self.shifter() + squadrons = len([p for p in BubPlayer.PlayerList if p.isplaying()]) + squadrons = 3 + (squadrons+1)//3 + nextsquad = 0 + relativey = 0 + squadtime = 0 + while not self.gameover: + yield None + #if random.random() < 0.015: + # bubbles.sendbubble(bubbles.PlainBubble, top=0) + in_place = {0: [], 1: [], 2: []} + for s in BubPlayer.MonsterList: + if isinstance(s, Alien): + in_place[s.in_place].append(s) + toohigh = self.globaly - relativey < -3*CELL + if in_place[1]: + xbounds = [s.x for s in in_place[1]] + self.alien_bounds = min(xbounds), max(xbounds) + shifter.next() + elif toohigh: + self.globaly += 1 + squadtime -= 1 + if nextsquad >= squadrons: + if not (in_place[0] or in_place[1]): + break + elif squadtime < 0 and not toohigh: + squadtime = 200 + try: + rank = 0 + while 1: + Alien(self, nextsquad, rank, relativey) + rank += 1 + except StopIteration: + pass + nextsquad += 1 + relativey += 4*CELL + for t in range(20): + yield None + + def shifter(self): + while 1: + # go right + while self.alien_bounds[1] < boards.bwidth-5*CELL: + self.globalx += 2 + yield None + # go down + for i in range(3*CELL): + self.globaly += 1 + yield None + # go left + while self.alien_bounds[0] > 3*CELL: + self.globalx -= 2 + yield None + # go down + for i in range(3*CELL): + self.globaly += 1 + yield None + + def build_ships(self): + for p in BubPlayer.PlayerList: + dragons = [d for d in p.dragons if not isinstance(d, Ship)] + if dragons and len(p.dragons) == len(dragons): + if self.builddelay.get(p): + self.builddelay[p] -= 1 + else: + self.builddelay[p] = 75 + dragon = random.choice(dragons) + ship = Ship(self, p, dragon.x, dragon.y) + ship.dcap = dragon.dcap + p.dragons.append(ship) + p.emotic(dragon, 4) + for d in dragons: + d.kill() + +# This game is suitable for at least min_players players +min_players = 1 + +def run(): + global curboard + from boards import curboard + boards.replace_boardgen(Galaga().bgen()) + +def setup(): + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + if filename.find('%d') >= 0: + for p in BubPlayer.PlayerList: + images.sprmap[key, p.pn] = (filename % p.pn, rect) + else: + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext3/image1-0.ppm b/bubbob/ext3/image1-0.ppm Binary files differnew file mode 100644 index 0000000..89e9178 --- /dev/null +++ b/bubbob/ext3/image1-0.ppm diff --git a/bubbob/ext3/music.wav b/bubbob/ext3/music.wav Binary files differnew file mode 100644 index 0000000..4684177 --- /dev/null +++ b/bubbob/ext3/music.wav diff --git a/bubbob/ext3/shoot.wav b/bubbob/ext3/shoot.wav Binary files differnew file mode 100644 index 0000000..fb2b099 --- /dev/null +++ b/bubbob/ext3/shoot.wav diff --git a/bubbob/ext4/.cvsignore b/bubbob/ext4/.cvsignore new file mode 100644 index 0000000..553d0ec --- /dev/null +++ b/bubbob/ext4/.cvsignore @@ -0,0 +1,2 @@ +*.py[co] +image1-[2-9].ppm diff --git a/bubbob/ext4/__init__.py b/bubbob/ext4/__init__.py new file mode 100644 index 0000000..1efa86e --- /dev/null +++ b/bubbob/ext4/__init__.py @@ -0,0 +1,440 @@ +from __future__ import generators +import os, math, random +import images, gamesrv +from images import ActiveSprite +from boards import CELL, HALFCELL, bget +from mnstrmap import GreenAndBlue +from bubbles import BubblingEyes, Bubble +from bonuses import Bonus, points + +LocalDir = os.path.basename(os.path.dirname(__file__)) + + +localmap = { + 't-brick1': ('image1-%d.ppm', (0, 0, 16, 16)), + 't-brick2': ('image1-%d.ppm', (16, 0, 16, 16)), + } + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) + + +class BrickEyes(BubblingEyes): + + Patterns = [[(-2,0), (-1,0), (0,0), (1,0)], + [(-2,0), (-1,0), (0,0), (0,-1)], + [(-1,-1), (-1,0), (0,0), (1,0)], + [(-1,0), (0,0), (0,-1), (1,0)], + [(-1,-1), (0,-1), (0,0), (1,0)], + [(-2,0), (-1,0), (-1,-1), (0,-1)], + [(-1,-1), (-1,0), (0,-1), (0,0)]] + + def __init__(self, tetris, bubber, saved_caps, olddragon): + BubblingEyes.__init__(self, bubber, saved_caps, olddragon) + self.tetris = tetris + self.bricks = [] + + def playing_bubble(self, oldsprite): + import boards + self.pat = random.choice(self.Patterns) + self.orientation = 1,0 + xmin = 2 - min([x for x,y in self.pat]) + xmax = boards.width-3 - max([x for x,y in self.pat]) + x = int(random.normalvariate(oldsprite.x, boards.bwidth/4)) + x = (x+HALFCELL) // CELL + if x<xmin: x=xmin + if x>xmax: x=xmax + y = -1 + self.tx = x + self.ty = y + self.move((x-1)*CELL, (y-1)*CELL) + for i in range(5): + yield None + self.bricks = [Brick(self.bubber, px, py) + for px, py in self.brick_positions()] + self.gen.append(self.step_control()) + self.gen.append(self.rotate_control()) + self.gen.append(self.fall_control()) + self.gen.append(self.move_eyes()) + + def bottom_up(self): + return 0 + + def kill(self): + for b in self.bricks: + b.stop(self.tetris) + b.remove() + self.bricks = [] + BubblingEyes.kill(self) + + def brick_positions(self): + ox, oy = self.orientation + result = [] + cx = self.tx*CELL - HALFCELL + cy = self.ty*CELL - HALFCELL + for px, py in self.pat: + px = px*CELL + HALFCELL + py = py*CELL + HALFCELL + result.append((cx+px*ox-py*oy, cy+px*oy+py*ox)) + return result + + def save_position(self): + return self.tx, self.ty, self.orientation + + def restore_position(self, p): + self.tx, self.ty, self.orientation = p + + def moved(self, old_position): + for b in self.bricks: + b.set(' ') + try: + for px, py in self.brick_positions(): + if bget(px//CELL, py//CELL) != ' ': + self.restore_position(old_position) + return 0 + for b, (px, py) in zip(self.bricks, self.brick_positions()): + b.follow(px, py) + finally: + for b in self.bricks: + b.set('!') # note: we need '!' < '#' + return 1 + + def onground(self): + for b in self.bricks: + if b.gen: # brick still moving + return 0 + for px, py in self.brick_positions(): + if bget(px//CELL, py//CELL+1) >= '#': + return 1 + return 0 + + def step_control(self): + while 1: + while not self.bubber.wannago(self.dcap): + yield None + pos = self.save_position() + self.tx += self.bubber.wannago(self.dcap) + if self.moved(pos): + for i in range(4): + yield None + yield None + + def fall_control(self): + delay = 1 + while 1: + for i in range(delay and 14): + if self.bubber.key_fire: + break + yield None + pos = self.save_position() + self.ty += 1 + delay = self.moved(pos) + if delay: + for i in range(3): + yield None + elif self.onground() and self.tetris.ready: + self.gen = [self.stopping()] + yield None + + def rotate_control(self): + while 1: + while not self.bubber.key_jump: + yield None + pos = self.save_position() + ox, oy = self.orientation + self.orientation = oy, -ox + if self.moved(pos): + for i in range(7): + yield None + yield None + + def stopping(self): + self.move(self.x, -self.ico.h) + positions = [(py//CELL, px//CELL) for px, py in self.brick_positions() + if py >= 0] + positions.sort() + positions = [(px, py) for py, px in positions] + for b in self.bricks: + b.stop(self.tetris) + if b.ty < 0: + b.remove() + self.bricks = [] + staticbricks = self.tetris.staticbricks + pts = 500 + while 1: + for px, py in positions: + y = py + x1 = px + while (x1-1, y) in staticbricks: + x1 -= 1 + if bget(x1-1, y) != '#': + continue + x2 = px + while (x2, y) in staticbricks: + x2 += 1 + if bget(x2, y) != '#': + continue + if x2-x1 < 2: + continue + # full line + ico = images.sprget(Bubble.exploding_bubbles[0]) + self.tetris.score[self.bubber] = self.tetris.score.get( + self.bubber, 0) + 1 + xlist = range(x1, x2) + for x in xlist: + s = ActiveSprite(ico, + x*CELL + random.randrange(CELL) - CELL, + y*CELL + random.randrange(CELL) - CELL) + s.gen.append(s.die(Bubble.exploding_bubbles)) + s = staticbricks[x, y] + points(x*CELL + HALFCELL, y*CELL + HALFCELL, s, pts) + s.remove() + if pts == 500: + self.play(images.Snd.Fruit) + elif pts == 4000: + self.play(images.Snd.Extralife) + else: + self.play(images.Snd.Extra) + pts *= 2 + for y in range(py-1, -1, -1): + if not [x for x in xlist if (x, y) in staticbricks]: + break + for t in range(4): + yield None + if [x for x in xlist if (x, y+1) in staticbricks]: + break + for x in xlist: + if (x, y) in staticbricks: + staticbricks[x, y].shiftdown() + yield None + break + else: + break + if self.tetris.ready < 2: + self.gen.append(self.playing_bubble(self)) + + def move_eyes(self): + while 1: + tx = (self.tx-1) * CELL + ty = (self.ty-1) * CELL + for i in range(3): + if tx < self.x: + dx = -1 + elif tx > self.x: + dx = +1 + else: + dx = 0 + if ty > self.y: + dy = +1 + else: + dy = 0 + self.step(2*dx, 2*dy) + key = ('eyes', dx, 0) + self.seticon(images.sprget(key)) + yield None + + +class Brick(ActiveSprite): + + def __init__(self, bubber, x, y): + ico = images.sprget(('t-brick1', bubber.pn)) + ActiveSprite.__init__(self, ico, x, y) + self.tx = x//CELL + self.ty = y//CELL + self.bubber = bubber + + def follow(self, x, y): + self.tx = x//CELL + self.ty = y//CELL + self.gen = [self.following(x, y)] + + def following(self, nx, ny): + dx = (nx - self.x) / 7.0 + dy = (ny - self.y) / 7.0 + for i in range(6, 0, -1): + self.move(nx - int(i*dx), ny - int(i*dy)) + yield None + self.move(nx, ny) + + def set(self, c): + x, y = self.tx, self.ty + if 0 <= x < curboard.width and 0 <= y < curboard.height: + line = curboard.walls[y] + curboard.walls[y] = line[:x] + c + line[x+1:] + + def stop(self, tetris): + self.set('X') + self.seticon(images.sprget(('t-brick2', self.bubber.pn))) + images.ActiveSprites.remove(self) + tetris.staticbricks[self.tx, self.ty] = self + self.staticbricks = tetris.staticbricks + + def remove(self): + del self.staticbricks[self.tx, self.ty] + self.set(' ') + gamesrv.Sprite.kill(self) + + def shiftdown(self): + del self.staticbricks[self.tx, self.ty] + self.set(' ') + self.ty += 1 + self.set('X') + self.staticbricks[self.tx, self.ty] = self + self.step(0, CELL) + + +class Tetris: + + def bgen(self, limittime = 90.1): # 1:30 + import boards + from player import BubPlayer + + self.score = {} + for t in boards.initsubgame(music, self.displaypoints): + yield t + + tc = boards.TimeCounter(limittime) + self.ready = 0 + self.staticbricks = {} + finished = 0 + for t in self.frame(): + t = boards.normal_frame() + self.build_eyes() + yield t + tc.update(t) + if tc.time == 0.0: + self.ready = 2 + finished += not self.still_playing() + if finished > 16: + break + if (BubPlayer.FrameCounter & 15) == 7: + for s in images.ActiveSprites: + if isinstance(s, Bubble): + s.pop() + elif isinstance(s, Bonus): + s.kill() + + tc.restore() + for t in boards.result_ranking(self.score): + self.remove_eyes() + yield t + for s in self.staticbricks.values(): + s.remove() + + def displaypoints(self, bubber): + return self.score.get(bubber, 0) + + def frame(self): + heights = {1: curboard.height, + curboard.width-2: curboard.height} + ymax = curboard.height-1 + maxheight = curboard.height*3//4 + for x in range(2, curboard.width-2): + if bget(x, ymax) == ' ': + curboard.putwall(x, ymax) + height = 1 + for y in range(ymax-1, -1, -1): + if bget(x, y) == '#': + if height == maxheight: + curboard.killwall(x, y) + else: + height += 1 + heights[x] = height + xlist = range(2, curboard.width-2) + random.shuffle(xlist) + for x in xlist: + h = heights[x] + x1 = x2 = x + while heights[x1-1] == h: + x1 -= 1 + while heights[x2] == h: + x2 += 1 + parts = (x2-x1) // 8 + if not parts: + continue + left = 0 + if heights[x1-1] > h: + x1 -= 1 + left += 1 + right = parts+1 + if heights[x2] > h: + x2 += 1 + right -= 1 + for p in range(left, right): + x = x1 + ((x2-x1-1)*p+parts//2)//parts + y = ymax + for i in range(2): + while bget(x, y) == '#': + y -= 1 + if y >= 3: + curboard.putwall(x, y) + heights[x] += 1 + curboard.reorder_walls() + + walls_by_pos = curboard.walls_by_pos + moves = 1 + s = 8.0 + while moves: + moves = 0 + for y in range(curboard.height-3, -1, -1): + for x in range(2, curboard.width-2): + if ((y,x) in walls_by_pos and + (y+1,x) not in walls_by_pos): + y0 = y + while (y0-1,x) in walls_by_pos: + y0 -= 1 + w = curboard.killwall(x, y0, 0) + curboard.putwall(x, y+1, w) + moves = 1 + curboard.reorder_walls() + for i in range(int(s)+2): + yield None + s *= 0.95 + self.ready = 1 + while 1: + yield None + + def build_eyes(self): + from player import BubPlayer + for p in BubPlayer.PlayerList: + dragons = [d for d in p.dragons if not isinstance(d, BrickEyes)] + if dragons and len(p.dragons) == len(dragons): + dragon = random.choice(dragons) + eyes = BrickEyes(self, p, dragon.dcap, dragon) + p.dragons.append(eyes) + #p.emotic(dragon, 4) + for d in dragons: + d.kill() + + def still_playing(self): + from player import BubPlayer + for p in BubPlayer.PlayerList: + for d in p.dragons: + if d.gen: + return 1 + return 0 + + def remove_eyes(self): + from player import BubPlayer + for p in BubPlayer.PlayerList: + for d in p.dragons: + d.kill() + +# This game is suitable for at least min_players players +min_players = 1 + +def run(): + global curboard + import boards + from boards import curboard + boards.replace_boardgen(Tetris().bgen()) + +def setup(): + from player import BubPlayer + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + if filename.find('%d') >= 0: + for p in BubPlayer.PlayerList: + images.sprmap[key, p.pn] = (filename % p.pn, rect) + else: + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext4/image1-0.ppm b/bubbob/ext4/image1-0.ppm Binary files differnew file mode 100644 index 0000000..60a86ac --- /dev/null +++ b/bubbob/ext4/image1-0.ppm diff --git a/bubbob/ext4/music.wav b/bubbob/ext4/music.wav Binary files differnew file mode 100644 index 0000000..b4b01a4 --- /dev/null +++ b/bubbob/ext4/music.wav diff --git a/bubbob/ext5/.cvsignore b/bubbob/ext5/.cvsignore new file mode 100644 index 0000000..539da74 --- /dev/null +++ b/bubbob/ext5/.cvsignore @@ -0,0 +1 @@ +*.py[co] diff --git a/bubbob/ext5/__init__.py b/bubbob/ext5/__init__.py new file mode 100644 index 0000000..ef73f14 --- /dev/null +++ b/bubbob/ext5/__init__.py @@ -0,0 +1,289 @@ +from __future__ import generators +import os, random +import images, gamesrv +from images import ActiveSprite +import boards +from boards import CELL, HALFCELL, bget +from monsters import Monster +from player import Dragon, BubPlayer +from bubbles import Bubble +import bonuses + +LocalDir = os.path.basename(os.path.dirname(__file__)) + +localmap = { + ('lem-walk', 1,0) : ('image1.ppm', ( 0, 0, 32, 32)), + ('lem-walk', 1,1) : ('image1.ppm', ( 32, 0, 32, 32)), + ('lem-walk', 1,2) : ('image1.ppm', ( 64, 0, 32, 32)), + ('lem-walk', 1,3) : ('image1.ppm', ( 96, 0, 32, 32)), + ('lem-walk', 1,4) : ('image1.ppm', (128, 0, 32, 32)), + ('lem-walk', 1,5) : ('image1.ppm', (160, 0, 32, 32)), + ('lem-walk', 1,6) : ('image1.ppm', (192, 0, 32, 32)), + ('lem-walk', 1,7) : ('image1.ppm', (224, 0, 32, 32)), + ('lem-fall', 1,0) : ('image1.ppm', (256, 0, 32, 32)), + ('lem-fall', 1,1) : ('image1.ppm', (288, 0, 32, 32)), + ('lem-fall', 1,2) : ('image1.ppm', (320, 0, 32, 32)), + ('lem-fall', 1,3) : ('image1.ppm', (352, 0, 32, 32)), + + ('lem-fall',-1,3) : ('image2.ppm', ( 0, 0, 32, 32)), + ('lem-fall',-1,2) : ('image2.ppm', ( 32, 0, 32, 32)), + ('lem-fall',-1,1) : ('image2.ppm', ( 64, 0, 32, 32)), + ('lem-fall',-1,0) : ('image2.ppm', ( 96, 0, 32, 32)), + ('lem-walk',-1,7) : ('image2.ppm', (128, 0, 32, 32)), + ('lem-walk',-1,6) : ('image2.ppm', (160, 0, 32, 32)), + ('lem-walk',-1,5) : ('image2.ppm', (192, 0, 32, 32)), + ('lem-walk',-1,4) : ('image2.ppm', (224, 0, 32, 32)), + ('lem-walk',-1,3) : ('image2.ppm', (256, 0, 32, 32)), + ('lem-walk',-1,2) : ('image2.ppm', (288, 0, 32, 32)), + ('lem-walk',-1,1) : ('image2.ppm', (320, 0, 32, 32)), + ('lem-walk',-1,0) : ('image2.ppm', (352, 0, 32, 32)), + + ('lem-jail', 0) : ('image4.ppm', ( 0, 0, 32, 32)), + ('lem-jail', 1) : ('image4.ppm', ( 0, 32, 32, 32)), + ('lem-jail', 2) : ('image4.ppm', ( 0, 64, 32, 32)), + } +for n in range(16): + localmap[('lem-crash', n)] = ('image3.ppm', (32*n, 0, 32, 32)) + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) +snd_ouch = gamesrv.getsample(os.path.join(LocalDir, 'ouch.wav')) + + +class Lemmy: + right = [('lem-walk', 1,n) for n in range(8)] + left = [('lem-walk',-1,n) for n in range(8)] + jailed = [('lem-jail', n) for n in range(3)] + + +class Lemming(Monster): + + def __init__(self, lemmings, x, y, dir): + Monster.__init__(self, Lemmy, x, y, dir, in_list=lemmings.lemlist) + self.lemmings = lemmings + + def argh(self, *args, **kwds): + self.untouchable() + self.gen = [self.jumpout()] + + def resetimages(self): + pass + + def touched(self, dragon): + if 20 >= abs(self.x - dragon.x) >= 14: + if self.x < dragon.x: + self.dir = -1 + else: + self.dir = 1 + + def in_bubble(self, bubble): + self.move(bubble.x, bubble.y) + Monster.in_bubble(self, bubble) + return -1 + + def bubbling(self, bubble): + dx = random.randrange(-3, 4) + dy = random.randrange(-4, 2) + counter = 0 + while not hasattr(bubble, 'poplist'): + if self.y < -CELL and bubble.y > CELL: # bubble wrapped + self.leaveboard(bubble) + return + self.move(bubble.x+dx, bubble.y+dy) + yield None + if bubble.poplist is None and bubble.y <= -2*CELL+1: + self.leaveboard(bubble) + return + self.setimages(None) + self.gen = [self.jumpout()] + + def jumpout(self): + # jumping out of the bubble + self.seticon(images.sprget(self.mdef.jailed[1])) + dxy = [(random.random()-0.5) * 9.0, + (random.random()+0.5) * (-5.0)] + for n in self.parabolic(dxy): + yield n + if dxy[1] >= 2.0: + break + if dxy[0] < 0: + self.dir = -1 + else: + self.dir = 1 + self.touchable = 1 + self.gen.append(self.falling()) + + def falling(self): + self.setimages(None) + n = 0 + lemmap = self.lemmings.lemmap + while not self.onground(): + yield None + self.move(self.x, (self.y + 4) & ~3, + lemmap['lem-fall', self.dir, n&3]) + n += 1 + if self.y >= boards.bheight: + self.kill() + return + yield None + if n <= 33: + self.gen.append(self.walking()) + else: + self.play(snd_ouch) + self.untouchable() + self.to_front() + self.gen = [self.die([('lem-crash', n) for n in range(16)], 2)] + + def walking(self): + self.setimages(None) + n = 0 + lemmap = self.lemmings.lemmap + y0 = self.y // 16 + while self.y == y0*16: + yield None + nx = self.x + self.dir*2 + x0 = (nx+15) // 16 + if bget(x0, y0+1) == ' ': + if bget(x0, y0+2) == ' ': + y0 += 1 # fall + elif bget(x0, y0) != ' ': + self.dir = -self.dir + self.resetimages() + continue + else: # climb + y0 -= 1 + n2 = 0 + while self.y > y0*16: + self.step(0, -2) + if n2: + n2 -= 1 + else: + self.seticon(lemmap['lem-walk', self.dir, n&7]) + n += 1 + n2 = 2 + yield None + self.move(nx, self.y, lemmap['lem-walk', self.dir, n&7]) + n += 1 + yield None + yield None + self.gen.append(self.falling()) + + def onground(self): + if self.y & 15: + return 0 + x0 = (self.x+15) // 16 + y0 = self.y // 16 + 2 + return bget(x0, y0) != ' ' == bget(x0, y0-1) + + def leaveboard(self, bubble): + if hasattr(bubble, 'd'): + bubble.play(images.Snd.Extra) + score = self.lemmings.score + bubber = bubble.d.bubber + score[bubber] = score.get(bubber, 0) + 1 + bonuses.points(bubble.x, bubble.y, bubble.d, 500) + self.kill() + + default_mode = walking + + +class Lemmings: + + def bgen(self, limittime = 60.1): # 0:60 + self.score = {} + for t in boards.initsubgame(music, self.displaypoints): + yield t + self.lemmap = {} + for key in localmap: + self.lemmap[key] = images.sprget(key) + + tc = boards.TimeCounter(limittime) + self.lemlist = [] + self.lemtotal = 0 + for t in self.frame(): + t = boards.normal_frame() + yield t + tc.update(t) + if tc.time == 0.0: + break + + tc.restore() + for s in self.lemlist[:]: + if s.alive: + s.kill() + for s in images.ActiveSprites[:]: + if isinstance(s, Bubble): + s.pop() + for t in boards.result_ranking(self.score.copy(), self.lemtotal): + yield t + + def displaypoints(self, bubber): + return self.score.get(bubber, 0) + + def frame(self): + windline = '>>' + '^'*(curboard.width-4) + '<<' + curboard.winds = [windline] * curboard.height + + countholes = 0 + ymax = curboard.height-1 + for x in range(2, curboard.width-2): + if bget(x, ymax) == ' ': + countholes += 1 + + xrange = [] + try: + for delta in range(2, curboard.width): + for x in [delta, curboard.width-delta-1]: + if x in xrange: raise StopIteration + xrange.append(x) + except StopIteration: + pass + + for x in xrange: + if countholes > curboard.width//6 and bget(x, ymax) == ' ': + curboard.putwall(x, ymax) + curboard.reorder_walls() + countholes -= 1 + for y in range(0, ymax): + if bget(x, y) == ' ': + break + curboard.killwall(x, y) + yield None + + testing = {} + def addlemming(): + for x, y in testing.items(): + if bget(x, y) != ' ' == bget(x, y-1): + if x <= curboard.width//2: + dir = 1 + else: + dir = -1 + s = Lemming(self, x*CELL-HALFCELL, (y-2)*CELL, dir) + self.lemtotal += 1 + if y < ymax: + testing[x] = y+1 + else: + del testing[x] + for x in xrange: + testing[x] = 1 + addlemming() + yield None + while testing: + addlemming() + yield None + + while self.lemlist: + yield None + +# This game is suitable for at least min_players players +min_players = 1 + +def run(): + global curboard + from boards import curboard + boards.replace_boardgen(Lemmings().bgen()) + +def setup(): + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext5/image1.ppm b/bubbob/ext5/image1.ppm new file mode 100644 index 0000000..bb36c9c --- /dev/null +++ b/bubbob/ext5/image1.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +384 32 +255 +@ÿ diff --git a/bubbob/ext5/image2.ppm b/bubbob/ext5/image2.ppm new file mode 100644 index 0000000..6eef883 --- /dev/null +++ b/bubbob/ext5/image2.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +384 32 +255 +@ÿ diff --git a/bubbob/ext5/image3.ppm b/bubbob/ext5/image3.ppm new file mode 100644 index 0000000..5d0eb00 --- /dev/null +++ b/bubbob/ext5/image3.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +512 32 +255 + diff --git a/bubbob/ext5/image4.ppm b/bubbob/ext5/image4.ppm Binary files differnew file mode 100644 index 0000000..8f22db8 --- /dev/null +++ b/bubbob/ext5/image4.ppm diff --git a/bubbob/ext5/music.wav b/bubbob/ext5/music.wav Binary files differnew file mode 100644 index 0000000..de1efb4 --- /dev/null +++ b/bubbob/ext5/music.wav diff --git a/bubbob/ext5/ouch.wav b/bubbob/ext5/ouch.wav Binary files differnew file mode 100644 index 0000000..5d3dd8b --- /dev/null +++ b/bubbob/ext5/ouch.wav diff --git a/bubbob/ext6/.cvsignore b/bubbob/ext6/.cvsignore new file mode 100644 index 0000000..553d0ec --- /dev/null +++ b/bubbob/ext6/.cvsignore @@ -0,0 +1,2 @@ +*.py[co] +image1-[2-9].ppm diff --git a/bubbob/ext6/__init__.py b/bubbob/ext6/__init__.py new file mode 100644 index 0000000..bd4c4d4 --- /dev/null +++ b/bubbob/ext6/__init__.py @@ -0,0 +1,268 @@ +from __future__ import generators +import os, random +import images, gamesrv +from images import ActiveSprite +import boards +from boards import CELL, HALFCELL, bget +from player import Dragon, BubPlayer +from mnstrmap import Monky +from bubbles import Bubble +from bonuses import Bonus + +LocalDir = os.path.basename(os.path.dirname(__file__)) + +localmap = { + ('trn-head', 0,-1): ('image1-%d.ppm', (0, 0, 8, 8)), + ('trn-head',-1, 0): ('image1-%d.ppm', (0, 8, 8, 8)), + ('trn-head', 0, 1): ('image1-%d.ppm', (0,16, 8, 8)), + ('trn-head', 1, 0): ('image1-%d.ppm', (0,24, 8, 8)), + ('trn', 0,-1, 1, 0): ('image1-%d.ppm', (0,32, 8, 8)), + ('trn', 0, 1, 1, 0): ('image1-%d.ppm', (0,40, 8, 8)), + ('trn', 1, 0, 0,-1): ('image1-%d.ppm', (0,48, 8, 8)), + ('trn', 1, 0, 0, 1): ('image1-%d.ppm', (0,56, 8, 8)), + ('trn', 1, 0, 1, 0): ('image1-%d.ppm', (0,64, 8, 8)), + ('trn', 0, 1, 0, 1): ('image1-%d.ppm', (0,72, 8, 8)), + } + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) +snd_crash = gamesrv.getsample(os.path.join(LocalDir, 'crash.wav')) + + +class TronHead(ActiveSprite): + + def __init__(self, tron, bubber, dcap, cx, cy, dir): + self.tron = tron + self.bubber = bubber + self.dcap = dcap + self.cx = cx + self.cy = cy + self.dir = dir + self.icons = {} + for key in localmap: + ico = images.sprget((key, bubber.pn)) + key = key[1:] + self.icons[key] = ico + if len(key) == 4: + dx1, dy1, dx2, dy2 = key + key = -dx2, -dy2, -dx1, -dy1 + self.icons[key] = ico + ActiveSprite.__init__(self, self.icons[self.dir], + self.cx*8-2, self.cy*8-2) + self.gen.append(self.trailing()) + + def forward_step(self, dir): + s = gamesrv.Sprite(self.icons[self.dir + dir], self.x, self.y) + self.tron.trailsprites.append(s) + self.dir = dir + self.cx += dir[0] + self.cy += dir[1] + self.move(self.cx*8-2, self.cy*8-2, self.icons[dir]) + + def trailing(self): + unoccupied = self.tron.unoccupied + bubber = self.bubber + # first go straight forward until we enter the playing board itself + while (self.cx, self.cy) not in unoccupied: + self.forward_step(self.dir) + yield None + yield None + # playing! + unoccupied[self.cx, self.cy] = False + while True: + # turn + d = [(bubber.key_left, -1, 0), + (bubber.key_right, 1, 0), + (bubber.key_jump, 0,-1), + (bubber.key_fire, 0, 1)] + d.sort() + newdir = self.dir + if d[-1][0] > d[-2][0]: + newdir = d[-1][1:] + if (self.dir + newdir) not in self.icons: + newdir = self.dir # forbidden 180-degree turn + # move one step forward + self.forward_step(newdir) + # crash? + if not unoccupied.get((self.cx, self.cy)): + self.crash() + return + unoccupied[self.cx, self.cy] = False + yield None + yield None + + def to_front(self): + if self.gen: + ActiveSprite.to_front(self) + + def crash(self): + self.move(self.x - self.dir[0], self.y - self.dir[1], + self.icons[self.dir+self.dir]) + self.to_back() + self.play(snd_crash) + ico = images.sprget(Monky.decay_weapon[1]) + s = ActiveSprite(ico, self.x + self.ico.w//2 - CELL, + self.y + self.ico.h//2 - CELL) + s.gen.append(s.die(Monky.decay_weapon[1:], 4)) + self.stop() + + def stop(self): + del self.gen[:] + try: + self.tron.trons.remove(self) + except ValueError: + pass + + def kill(self): + self.stop() + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + +class Tron: + + def bgen(self, limittime = 60.1): # 1:00 + self.score = {} + for t in boards.initsubgame(music, self.displaypoints): + yield t + + self.ready = 0 + self.trons = [] + self.trailsprites = [] + self.playerlist = BubPlayer.PlayerList[:] + tc = boards.TimeCounter(limittime) + for t in self.frame(tc): + t = boards.normal_frame() + self.build_trons() + yield t + tc.update(t) + if (BubPlayer.FrameCounter & 15) == 7: + for s in images.ActiveSprites: + if isinstance(s, Bubble): + s.pop() + elif isinstance(s, Bonus): + s.kill() + + self.ready = 0 + tc.restore() + for t in boards.result_ranking(self.score): + for p in BubPlayer.PlayerList: + for d in p.dragons[:]: + d.kill() + yield t + self.remove_trons() + + def displaypoints(self, bubber): + return self.score.get(bubber, 0) + + def build_trons(self): + if self.ready == 0: + self.remove_trons() + return + for p in self.playerlist: + dragons = [d for d in p.dragons if not isinstance(d, TronHead)] + if self.ready < 10 and dragons and len(p.dragons) == len(dragons): + self.score.setdefault(p, 0) + dragon = random.choice(dragons) + x, y, dir = self.select_start_point() + head = TronHead(self, p, dragon.dcap, x, y, dir) + self.trons.append(head) + p.dragons.append(head) + #p.emotic(head, 4) + for d in dragons: + d.kill() + + def remove_trons(self): + for p in BubPlayer.PlayerList: + for d in p.dragons[:]: + d.kill() + for s in self.trailsprites: + s.kill() + del self.trailsprites[:] + + def select_start_point(self): + distmin = 12 + while True: + x, y, dir = random.choice(self.start_points) + for head in self.trons: + if abs(x-head.cx//2) + abs(y-head.cy//2) < distmin: + break + else: + break + distmin *= 0.95 + if (y, x) in curboard.walls_by_pos: + curboard.killwall(x, y) + x = 2*x+1 + y = 2*y - dir[1] + if dir[1] < 0: + y += 2 + return x, y, dir + + def frame(self, tc): + y1 = 1 + y2 = curboard.height-2 + while y1 <= y2: + for y in [y1, y2]: + for x in range(2, curboard.width-2): + if (y, x) in curboard.walls_by_pos: + curboard.killwall(x, y) + yield None + y1 += 1 + y2 -= 1 + + self.start_points = [] + for x in range(4, curboard.width-3): + self.start_points.append((x, 0, (0, 1))) + self.start_points.append((x, curboard.height-1, (0, -1))) + + while tc.time != 0.0: + for y in [0, curboard.height-1]: + for x in range(2, curboard.width-2): + if (y, x) not in curboard.walls_by_pos: + curboard.putwall(x, y) + curboard.reorder_walls() + self.unoccupied = {} + for x in range(5, 2*curboard.width-4): + for y in range(3, 2*curboard.height-2): + self.unoccupied[x, y] = True + random.shuffle(self.playerlist) + for i in range(5): + yield None + + min_players = 1 + while self.ready < 20 or len(self.trons) >= min_players: + if len(self.trons) >= 2: + min_players = 2 + self.ready += 1 + yield None + + if len(self.trons) == 1: + bubber = self.trons[0].bubber + self.score[bubber] += 1 + bubber.givepoints(100) + self.trons[0].stop() + self.ready = 99 + + for i in range(28): + yield None + self.ready = 0 + +# This game is suitable for at least min_players players +min_players = 2 + +def run(): + global curboard + from boards import curboard + boards.replace_boardgen(Tron().bgen()) + +def setup(): + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + if filename.find('%d') >= 0: + for p in BubPlayer.PlayerList: + images.sprmap[key, p.pn] = (filename % p.pn, rect) + else: + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext6/crash.wav b/bubbob/ext6/crash.wav Binary files differnew file mode 100644 index 0000000..65499db --- /dev/null +++ b/bubbob/ext6/crash.wav diff --git a/bubbob/ext6/image1-0.ppm b/bubbob/ext6/image1-0.ppm Binary files differnew file mode 100644 index 0000000..bb54ce2 --- /dev/null +++ b/bubbob/ext6/image1-0.ppm diff --git a/bubbob/ext6/music.wav b/bubbob/ext6/music.wav Binary files differnew file mode 100644 index 0000000..8c55b34 --- /dev/null +++ b/bubbob/ext6/music.wav diff --git a/bubbob/ext7/.cvsignore b/bubbob/ext7/.cvsignore new file mode 100644 index 0000000..553d0ec --- /dev/null +++ b/bubbob/ext7/.cvsignore @@ -0,0 +1,2 @@ +*.py[co] +image1-[2-9].ppm diff --git a/bubbob/ext7/__init__.py b/bubbob/ext7/__init__.py new file mode 100644 index 0000000..2d157ab --- /dev/null +++ b/bubbob/ext7/__init__.py @@ -0,0 +1,399 @@ +from __future__ import generators +import os, random, math +import images, gamesrv +from images import ActiveSprite +import boards +from boards import CELL +from player import Dragon, BubPlayer, scoreboard +from bubbles import Bubble +from bonuses import Bonus +from mnstrmap import PlayerBubbles +from mnstrmap import Monky +import bonuses +from ext6 import snd_crash + +LocalDir = os.path.basename(os.path.dirname(__file__)) + +ANGLE_COUNT = 24 +ANGLE_STEP = 360 / ANGLE_COUNT +ANGLE_TABLE = {} +for i in range(ANGLE_COUNT): + a = i*ANGLE_STEP*math.pi/180.0 + ANGLE_TABLE[i*ANGLE_STEP] = (math.cos(a), math.sin(a)) + + +localmap = {} +for i in range(ANGLE_COUNT): + localmap['camel', i] = ('image1-%d.ppm', (0, i*36, 36, 36)) + +music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) +snd_fire = gamesrv.getsample(os.path.join(LocalDir, 'fire.wav')) +snd_hit = gamesrv.getsample(os.path.join(LocalDir, 'hit.wav')) + + +class Plane(ActiveSprite): + lock = None + + def __init__(self, camel, bubber, dcap, x, y, dirhint=None): + self.bubber = bubber + self.dcap = dcap + self.camel = camel + self.shotlist = camel.score.setdefault(bubber, {}) + + if x < x_min: + x = x_min + elif x > x_max: + x = x_max + + if y < 4*CELL: + y = 4*CELL + elif y > (curboard.height-4)*CELL - 36: + y = (curboard.height-4)*CELL - 36 + + if dirhint not in (1, -1): + controldelay = 5 + if x < boards.bwidth//2: + dir = 1 + else: + dir = -1 + else: + controldelay = 20 + if x < boards.bwidth//3: + dir = 1 + elif x >= boards.bwidth*2//3: + dir = -1 + else: + dir = dirhint + if dir > 0: + self.angle = 0 + self.flipped = False + else: + self.angle = 180 + self.flipped = True + + ActiveSprite.__init__(self, self.getico(), x, y) + self.fx = self.x + self.fy = self.y + self.controlgen = self.control(delay=controldelay) + self.gen.append(self.fly()) + self.gen.append(self.controlgen) + self.gen.append(self.blink()) + self.gen.append(self.bob()) + + def controlled(self): + return self.controlgen in self.gen + + def getico(self): + a = self.angle // ANGLE_STEP + if self.flipped: + if a: + a = ANGLE_COUNT-a + key = 'vflip', ('camel', a, self.bubber.pn) + else: + key = 'camel', a, self.bubber.pn + return images.sprget(key) + + def blink(self): + for i in range(10): + yield None + yield None + self.setdisplaypos(-256, -256) + yield None + self.setdisplaypos(-256, -256) + yield None + self.touchable = 1 + + def bob(self): + f = 3.0 + for i in range(0, 1080, ANGLE_STEP): + self.fy += f * ANGLE_TABLE[i % 360][1] + f *= 0.98 + yield None + +## def loosealtitude(self, y0, angle0): +## if 90 <= angle0 < 270 or (angle0 == 270 and not self.flipped): +## angledir = -1 +## else: +## angledir = 1 +## for i in range(0, 180, ANGLE_STEP): +## if i % (4*ANGLE_STEP) == 0 and not (45 <= angle0 <= 135): +## angle0 += ANGLE_STEP * angledir +## angle0 = (angle0 + 360) % 360 +## y0 += 4.0 * ANGLE_TABLE[i][1] +## if y0 > self.fy: +## self.fy = y0 +## self.angle = angle0 +## yield None + + def turn(self, dir): + self.angle += ANGLE_STEP * dir + self.angle = (self.angle + 360) % 360 + + def control(self, delay=0): + bubber = self.bubber + prev_key_jump = 0 + for i in range(delay): + yield None + shootdelay = 0 + while True: + wannago = bubber.wannago(self.dcap) + self.turn(wannago) + if shootdelay: + shootdelay -= 1 + elif bubber.key_fire: + x = self.x + self.ico.w//2 + y = self.y + self.ico.h//2 + acos, asin = ANGLE_TABLE[self.angle] + x += acos * 20 + y += asin * 20 + if self.flipped: + acos = -acos + asin = -asin + x -= asin * 5 + y += acos * 5 + self.play(snd_fire) + Shot(self, int(x), int(y), self.angle, 2) + Shot(self, int(x), int(y), self.angle) + shootdelay = 7 + for i in range(2): + if bubber.key_jump > prev_key_jump: + self.flipped = not self.flipped + prev_key_jump = bubber.key_jump + yield None + for s in self.touching(12): + if isinstance(s, Plane) and s is not self: + ico = images.sprget(Monky.decay_weapon[1]) + s1 = ActiveSprite(ico, + (self.x+s.x)//2 + self.ico.w//2 - CELL, + (self.y+s.y)//2 + self.ico.h//2 - CELL) + s1.gen.append(s1.die(Monky.decay_weapon[1:], 4)) + s1.play(snd_crash) + self.gen = [self.godowninflames(s)] + s.gen = [s.godowninflames(self)] + + def fly(self, speed=3.3): + while True: + if (self.y < 0 and not (0 < self.angle < 180) and + ((abs(270 - self.angle) < -4*self.y) or random.random() < 0.2)): + if (90 <= self.angle < 270 or + (self.angle == 270 and not self.flipped)): + self.turn(-1) + else: + self.turn(1) + ico = self.getico() + acos, asin = ANGLE_TABLE[self.angle] + self.fx += acos * speed + self.fy += asin * speed + self.move(int(self.fx), int(self.fy), ico) + if self.x < x_min: + self.angle = 2 * ANGLE_STEP + self.flipped = not self.flipped + self.gen = [self.godowninflames()] + self.play(images.Snd.Pop) + elif self.x > x_max: + self.angle = 180 - 2 * ANGLE_STEP + self.flipped = not self.flipped + self.gen = [self.godowninflames()] + self.play(images.Snd.Pop) + elif self.y > y_max: + self.gen = [self.crashed()] + yield None + + def godowninflames(self, hit_by_plane=None): + if hit_by_plane and hit_by_plane in self.shotlist: + hittime = self.shotlist[hit_by_plane] + if BubPlayer.FrameCounter < hittime + 60: + del self.shotlist[hit_by_plane] + scoreboard() + self.seticon(self.getico()) + self.gen.append(self.fly()) + trail = [(self.x, self.y)] * 7 + ico = images.sprget(PlayerBubbles.explosion[0]) + s = ActiveSprite(ico, self.x + self.ico.w//2 - CELL, + self.y + self.ico.h//2 - CELL) + s.gen.append(s.die(PlayerBubbles.explosion)) + self.bubber.emotic(self, 4) + while True: + yield None + if random.random() < 0.37: + ico = images.sprget(Bubble.exploding_bubbles[0]) + x, y = random.choice(trail) + x += random.randint(-10, 10) + y += random.randint(-10, 10) + s = ActiveSprite(ico, x+2, y+2) + s.gen.append(s.die(Bubble.exploding_bubbles)) + if random.random() < 0.5: + yield None + if 90 <= self.angle < 270: + lst = [0, 0, 0, 0, -1, -1, -1, 1, 1] + else: + lst = [0, 0, 0, 0, -1, -1, 1, 1, 1] + self.turn(random.choice(lst)) + trail.pop(0) + trail.append((self.x, self.y)) + + def crashed(self): + self.untouchable() + self.play(snd_crash) + ico = images.sprget(Monky.decay_weapon[1]) + self.seticon(ico) + self.step(self.ico.w//2 - CELL, + self.ico.h//2 - CELL) + self.gen.append(self.die(Monky.decay_weapon[1:], 4)) + yield None + + def kill(self): + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + +class Shot(ActiveSprite): + + def __init__(self, plane, x, y, angle, steps=0): + ico = images.sprcharacterget('.') + ActiveSprite.__init__(self, ico, x-4, y-12) + self.plane = plane + self.angle = angle + self.gen.append(self.moving(steps)) + + def moving(self, steps=0): + minx = 2*CELL - 4 + maxx = (curboard.width-2)*CELL - 4 + maxy = (curboard.height-1)*CELL - 12 + fx = self.x + fy = self.y + dx, dy = ANGLE_TABLE[self.angle] + dx *= 7.6 + dy *= 7.6 + fx += dx * steps + fy += dy * steps + for i in range(22-steps): + for s in images.touching(self.x+3, self.y+11, 2, 2): + if isinstance(s, Plane) and s is not self.plane: + self.play(snd_hit) + self.kill() + if s.controlled(): + s.gen = [s.godowninflames(self.plane)] + self.plane.shotlist[s] = BubPlayer.FrameCounter + bonuses.points(self.x + 4 - CELL, self.y + 12 - CELL, + self.plane, 100) + return + fx += dx + fy += dy + self.move(int(fx), int(fy)) + if self.x < minx or self.x > maxx or self.y > maxy: + break + yield None + self.kill() + + +class Camel: + + def bgen(self, limittime = 90.1): # 1:30 + self.score = {} + for t in boards.initsubgame(music, self.displaypoints): + yield t + + tc = boards.TimeCounter(limittime) + for t in self.frame(tc): + t = boards.normal_frame() + self.build_planes() + yield t + tc.update(t) + if (BubPlayer.FrameCounter & 15) == 7: + for s in images.ActiveSprites: + if isinstance(s, Bubble): + s.pop() + elif isinstance(s, Bonus): + s.kill() + + tc.restore() + score = {} + for player, shotlist in self.score.items(): + score[player] = len(shotlist) + for t in boards.result_ranking(score): + for p in BubPlayer.PlayerList: + for d in p.dragons[:]: + d.kill() + yield t + self.remove_planes() + + def displaypoints(self, bubber): + return len(self.score.get(bubber, ())) + + def build_planes(self): + for p in BubPlayer.PlayerList: + dragons = [d for d in p.dragons if not isinstance(d, Plane)] + if dragons and len(p.dragons) == len(dragons): + dragon = random.choice(dragons) + if dragon.dcap['infinite_shield']: + start_position = self.select_start_position() + dirhint = None + else: + start_position = dragon.x-2, dragon.y-2 + dirhint = getattr(dragon, 'dir', None) + plane = Plane(self, p, dragon.dcap, + start_position[0], start_position[1], dirhint) + p.dragons.append(plane) + p.emotic(plane, 4) + for d in dragons: + d.kill() + + def remove_planes(self): + for p in BubPlayer.PlayerList: + for d in p.dragons[:]: + d.kill() + + def select_start_position(self): + planes = [d for p in BubPlayer.PlayerList + for d in p.dragons + if isinstance(d, Plane)] + distmin = 180 + while True: + x = random.choice([x_min, x_max]) + y = random.randint(2*CELL, (curboard.height-4)*CELL - 36) + for d in planes: + dist = (x-d.x)*(x-d.x) + (y-d.y)*(y-d.y) + if dist < distmin*distmin: + break + else: + return x, y + distmin = int(distmin * 0.94) + + def frame(self, tc): + y = curboard.height-1 + for x in range(2, curboard.width-2): + if (y, x) not in curboard.walls_by_pos: + curboard.putwall(x, y) + curboard.reorder_walls() + for y in range(0, curboard.height-1): + yield None + for x in range(2, curboard.width-2): + if (y, x) in curboard.walls_by_pos: + curboard.killwall(x, y) + while tc.time != 0.0: + yield None + +# This game is suitable for at least min_players players +min_players = 2 + +def run(): + global curboard, x_min, x_max, y_max + from boards import curboard + x_min = 2*CELL - 3 + x_max = (curboard.width-2)*CELL - 36 + 3 + y_max = (curboard.height-1)*CELL - 36 + 7 + boards.replace_boardgen(Camel().bgen()) + +def setup(): + for key, (filename, rect) in localmap.items(): + filename = os.path.join(LocalDir, filename) + if filename.find('%d') >= 0: + for p in BubPlayer.PlayerList: + images.sprmap[key + (p.pn,)] = (filename % p.pn, rect) + else: + images.sprmap[key] = (filename, rect) +setup() diff --git a/bubbob/ext7/fire.wav b/bubbob/ext7/fire.wav Binary files differnew file mode 100644 index 0000000..c8531a3 --- /dev/null +++ b/bubbob/ext7/fire.wav diff --git a/bubbob/ext7/hit.wav b/bubbob/ext7/hit.wav Binary files differnew file mode 100644 index 0000000..36d497a --- /dev/null +++ b/bubbob/ext7/hit.wav diff --git a/bubbob/ext7/image1-0.ppm b/bubbob/ext7/image1-0.ppm Binary files differnew file mode 100644 index 0000000..2df7b9b --- /dev/null +++ b/bubbob/ext7/image1-0.ppm diff --git a/bubbob/ext7/music.wav b/bubbob/ext7/music.wav Binary files differnew file mode 100644 index 0000000..b45f018 --- /dev/null +++ b/bubbob/ext7/music.wav diff --git a/bubbob/images.py b/bubbob/images.py new file mode 100644 index 0000000..4de0b56 --- /dev/null +++ b/bubbob/images.py @@ -0,0 +1,542 @@ +from __future__ import generators +import gamesrv, os +from sprmap import sprmap as original_sprmap +from patmap import patmap +import mnstrmap +import pixmap + +KEYCOL = 0x010101 +MAX = 10 + +ActiveSprites = [] +SpritesByLoc = {} + + +class ActiveSprite(gamesrv.Sprite): + touchable = 0 + imgsetter = None + angry = [] + priority = 0 + + def __init__(self, *args): + gamesrv.Sprite.__init__(self, *args) + if self.priority: + ActiveSprites.insert(0, self) + else: + ActiveSprites.append(self) + self.ranges = [] + self.gen = [] + + def kill(self): + self.untouchable() + del self.gen[:] + ActiveSprites.remove(self) + gamesrv.Sprite.kill(self) + + def untouchable(self): + self.touchable = 0 + for key in self.ranges: + del key[self] + del self.ranges[:] + + def play(self, snd, volume=0.8): + import boards + xmin = 2*boards.CELL + xmax = boards.bwidth-4*boards.CELL + snd.play(volume, pad=float(self.x-xmin)/(xmax-xmin)) + + def setimages(self, gen): + if self.imgsetter is not None: + try: + self.gen.remove(self.imgsetter) + except ValueError: + pass + self.imgsetter = gen + if gen is not None: + self.gen.append(gen) + + def vertical_warp(self): + # short-cut this method to boards.py + import boards + ActiveSprite.vertical_warp = boards.vertical_warp_sprite + self.vertical_warp() +## if moebius: +## self.moebius() +## return moebius +## def moebius(self): +## pass + + # common generators + def cyclic(self, nimages, speed=5): + images = [sprget(n) for n in nimages] + speed = range(speed) + while 1: + for img in images: + self.seticon(img) + for i in speed: + yield None + + def imgseq(self, nimages, speed=5, repeat=1): + images = [sprget(n) for n in nimages] + for r in range(repeat): + for img in images: + self.seticon(img) + for i in range(speed): + yield None + + def die(self, nimages, speed=1): + for n in nimages: + if n is not None: + self.seticon(sprget(n)) + for i in range(speed): + yield None + self.kill() + + def straightline(self, dx, dy): + fx = self.x + 0.5 + fy = self.y + 0.5 + while 1: + fx += dx + fy += dy + self.move(int(fx), int(fy)) + yield None + + def parabolic(self, dxy, warp=0, gravity=0.3): + import boards + from boards import CELL + nx = self.x + ny = self.y + dx, dy = dxy + xmax = boards.bwidth - 2*CELL - self.ico.w + while ny < boards.bheight: + nx += dx + ny += dy + dy += gravity + if nx < 2*CELL: + nx = 2*CELL + dx = abs(dx) + elif nx >= xmax: + nx = xmax + dx = -abs(dx) + if warp and (ny < -2*CELL or ny >= boards.bheight): + nx, ny = boards.vertical_warp(nx, ny) +## if moebius: +## self.moebius() +## dx = -dx + self.move(int(nx), int(ny)) + dxy[:] = [dx, dy] + yield None + + def following(self, other, dx=0, dy=0): + while other.alive: + self.move(other.x + dx, other.y + dy) + yield None + self.kill() + + def touchdelay(self, delay): + for i in range(delay): + yield None + self.touchable = 1 + + def touching(self, margin=0): + return touching(self.x, self.y, self.ico.w, self.ico.h, margin) + + def genangry(self): + # do one more step throught all generators of self.gen + while 1: + glist = self.gen[:] + try: + for g in glist: + if self.alive: + g.next() + except StopIteration: + try: + self.gen.remove(g) + except ValueError: + pass + for g in glist[glist.index(g)+1:]: + if self.alive: + try: + g.next() + except StopIteration: + pass + yield None + +def touching(x1, y1, w1, h1, margin=0): + touch = {} + x1 = int(x1) + y1 = int(y1) + xrange = range(x1>>5, (x1+w1+31)>>5) + for y in range(y1>>4, (y1+h1+15)>>4): + for x in xrange: + touch.update(SpritesByLoc.get((x,y), {})) + return [s for s in touch + if x1+margin < s.x+s.ico.w and y1+margin < s.y+s.ico.h and + s.x+margin < x1+w1 and s.y+margin < y1+h1] + +def action(sprlist, len=len): + # Main generator dispatch loop + for self in sprlist: + glist = self.gen + self.angry + try: + for g in glist: + if self.alive: + g.next() + except StopIteration: + try: + self.gen.remove(g) + except ValueError: + pass + for g in glist[glist.index(g)+1:]: + if self.alive: + try: + g.next() + except StopIteration: + pass + if self.touchable and self.alive: + # record position + x = self.x & -8 + y = self.y & -8 + if self.touchable != (x, y): + self.touchable = x, y + for key in self.ranges: + del key[self] + del self.ranges[:] + xrange = range(x>>5, (x+self.ico.w+38)>>5) + for y in range(y>>4, (y+self.ico.h+22)>>4): + for x in xrange: + key = SpritesByLoc.setdefault((x,y), {}) + key[self] = 1 + self.ranges.append(key) + +def sprget(n, spriconcache={}): + try: + return spriconcache[n] + except KeyError: + key = n + if isinstance(key, tuple) and key[0] in Transformations: + t, n = key + transform = Transformations[t] + else: + transform = transform_noflip + filename, rect = sprmap[n] + bitmap, rect = transform(filename, rect) + if isinstance(n, tuple): + n1 = n[0] + else: + n1 = n + if isinstance(n1, int): + n1 = n1 % 1000 + alpha = transparency.get(n1, 255) + ico = bitmap.geticon(alpha=alpha, *rect) + spriconcache[key] = ico + return ico + +def transform_noflip(filename, rect): + bitmap = gamesrv.getbitmap(filename, KEYCOL) + return bitmap, rect + +def make_transform(datamap, ptmap): + def transform(filename, rect, datamap=datamap, ptmap=ptmap, cache={}): + try: + bitmap, width, height = cache[filename] + except KeyError: + f = open(filename, "rb") + data = f.read() + f.close() + width, height, data = pixmap.decodepixmap(data) + data = datamap(width, height, data) + dummy, dummy, nwidth, nheight = ptmap(0, 0, width, height) + data = pixmap.encodepixmap(nwidth, nheight, data) + bitmap = gamesrv.newbitmap(data, KEYCOL) + cache[filename] = bitmap, width, height + #print 'transformed', filename, 'to', nwidth, nheight + x, y, w, h = rect + x1, y1, dummy, dummy = ptmap(x, y, width, height) + x2, y2, dummy, dummy = ptmap(x+w, y+h, width, height) + rect = min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1) + #print filename, ':', (x,y,w,h), '->', rect + return bitmap, rect + return transform + +Transformations = { + '': transform_noflip, + 'vflip': make_transform(pixmap.vflip, lambda x,y,w,h: (x,h-y,w,h)), + 'hflip': make_transform(pixmap.hflip, lambda x,y,w,h: (w-x,y,w,h)), + 'cw': make_transform(pixmap.rotate_cw, lambda x,y,w,h: (h-y,x,h,w)), + 'ccw': make_transform(pixmap.rotate_ccw,lambda x,y,w,h: (y,w-x,h,w)), + 'rot180': make_transform(pixmap.rotate_180,lambda x,y,w,h: (w-x,h-y,w,h)), + } + +if 0: # disabled clipping + def sprget_subrect(n, subrect): + x, y, w, h = subrect + filename, (x0, y0, w0, h0) = sprmap[n] + key = (n, 'subrect', subrect) + sprmap[key] = filename, (x0+x, y0+y, w, h) + return sprget(key) + +def make_darker(ico, is_dragon, bmpcache={}): + bmp, rect = ico.getorigin() + try: + darkbmp = bmpcache[bmp, is_dragon] + except KeyError: + image = pixmap.decodepixmap(bmp.read()) + if is_dragon: + translation = pixmap.translation_dragon + else: + translation = pixmap.translation_darker + darkimage = pixmap.make_dark(image, translation) + data = pixmap.encodepixmap(*darkimage) + darkbmp = gamesrv.newbitmap(data, bmp.colorkey) + bmpcache[bmp, is_dragon] = darkbmp + return darkbmp.geticon(*rect) + +def haspat(n): + return n in patmap + +def loadpattern(n, keycol=None): + if not haspat(n): + n = (n[0] % 100,) + n[1:] + filename, rect = patmap[n] + filename = os.path.join('tmp', filename) + bitmap = gamesrv.getbitmap(filename, keycol) + return bitmap, rect + +def makebkgndpattern(bitmap, (x,y,w,h), darker={}): + from boards import CELL + try: + nbitmap, hscale, vscale = darker[bitmap] + except KeyError: + data = bitmap.read() + width, height, data = pixmap.decodepixmap(data) + nwidth, nheight, data = pixmap.makebkgnd(width, height, data) + hscale = float(nwidth) / width + vscale = float(nheight) / height + data = pixmap.encodepixmap(nwidth, nheight, data) + nbitmap = gamesrv.newbitmap(data, None) + darker[bitmap] = nbitmap, hscale, vscale + x = int(x*hscale) + y = int(y*vscale) + w = int(CELL*hscale) + h = int(CELL*vscale) + return nbitmap, (x,y,w,h) + +def computebiggericon(ico, bigger={}): + try: + result, computing = bigger[ico] + except KeyError: + bigger[ico] = None, pixmap.imagezoomer(*ico.getimage()) + return None + if computing is not None: + result = computing.next() or computing.next() or computing.next() + if not result: + return None # still computing + w, h, data = result + data = pixmap.encodepixmap(w, h, data) + result = gamesrv.newbitmap(data, KEYCOL).geticon(0, 0, w, h) + bigger[ico] = result, None + return result + +def biggericon(ico): + result = None + while result is None: + result = computebiggericon(ico) + return result + +extramap = { + 'shield-left': ('extra1.ppm', (0, 0, 32, 32)), + 'shield-right': ('extra1.ppm', (0, 32, 32, 32)), + 'moebius': ('extra1.ppm', (0, 64, 32, 32)), + 'flower': ('extra1.ppm', (0, 96, 32, 32)), + 'flower2': ('extra1.ppm', (0, 128, 32, 32)), + 'potion4': ('extra1.ppm', (0, 160, 32, 32)), + ('glasses', -1):('extra1.ppm', (0, 192, 32, 16)), + ('glasses', +1):('extra1.ppm', (0, 208, 32, 16)), + 'cactus': ('extra1.ppm', (0, 224, 32, 32)), + 'questionmark3':('extra2.ppm', (0, 0, 16, 16)), + 'questionmark1':('extra2.ppm', (0, 16, 16, 16)), + 'questionmark5':('extra2.ppm', (0, 32, 16, 16)), + 'questionmark2':('extra2.ppm', (0, 48, 16, 16)), + 'questionmark4':('extra2.ppm', (0, 64, 16, 16)), + 'percent': ('extra2.ppm', (0, 80, 16, 16)), + 'colon': ('extra2.ppm', (0, 96, 16, 16)), + 'gameoverbkgnd':('extra2.ppm', (0, 112, 16, 16)), + ('eyes', 0,0): ('extra3.ppm', (0, 0, 32, 32)), + ('eyes', 0,-1): ('extra3.ppm', (0, 32, 32, 32)), + ('eyes', -1,0): ('extra3.ppm', (0, 64, 32, 32)), + ('eyes', -1,-1):('extra3.ppm', (0, 96, 32, 32)), + ('eyes', 1,0): ('extra3.ppm', (0, 128, 32, 32)), + ('eyes', 1,-1): ('extra3.ppm', (0, 160, 32, 32)), + 'eyes-blink': ('extra3.ppm', (0, 192, 32, 32)), + ('smstar','blue' ,0): ('extra4.ppm', ( 0, 0, 16, 16)), + ('smstar','blue' ,1): ('extra4.ppm', ( 0, 16, 16, 16)), + ('smstar','yellow' ,0): ('extra4.ppm', ( 0, 32, 16, 16)), + ('smstar','yellow' ,1): ('extra4.ppm', ( 0, 48, 16, 16)), + ('smstar','red' ,0): ('extra4.ppm', (16, 0, 16, 16)), + ('smstar','red' ,1): ('extra4.ppm', (16, 16, 16, 16)), + ('smstar','green' ,0): ('extra4.ppm', (16, 32, 16, 16)), + ('smstar','green' ,1): ('extra4.ppm', (16, 48, 16, 16)), + ('smstar','magenta',0): ('extra4.ppm', (32, 0, 16, 16)), + ('smstar','magenta',1): ('extra4.ppm', (32, 16, 16, 16)), + ('smstar','cyan' ,0): ('extra4.ppm', (32, 32, 16, 16)), + ('smstar','cyan' ,1): ('extra4.ppm', (32, 48, 16, 16)), + ('starbub','blue' ,0): ('extra5.ppm', (0, 0, 32, 32)), + ('starbub','blue' ,1): ('extra5.ppm', (0, 32, 32, 32)), + ('starbub','blue' ,2): ('extra5.ppm', (0, 64, 32, 32)), + ('starbub','yellow' ,0): ('extra5.ppm', (0, 96, 32, 32)), + ('starbub','yellow' ,1): ('extra5.ppm', (0,128, 32, 32)), + ('starbub','yellow' ,2): ('extra5.ppm', (0,160, 32, 32)), + ('starbub','red' ,0): ('extra5.ppm', (0,192, 32, 32)), + ('starbub','red' ,1): ('extra5.ppm', (0,224, 32, 32)), + ('starbub','red' ,2): ('extra5.ppm', (0,256, 32, 32)), + ('starbub','green' ,0): ('extra5.ppm', (0,288, 32, 32)), + ('starbub','green' ,1): ('extra5.ppm', (0,320, 32, 32)), + ('starbub','green' ,2): ('extra5.ppm', (0,352, 32, 32)), + ('starbub','magenta',0): ('extra5.ppm', (0,384, 32, 32)), + ('starbub','magenta',1): ('extra5.ppm', (0,416, 32, 32)), + ('starbub','magenta',2): ('extra5.ppm', (0,448, 32, 32)), + ('starbub','cyan' ,0): ('extra5.ppm', (0,480, 32, 32)), + ('starbub','cyan' ,1): ('extra5.ppm', (0,512, 32, 32)), + ('starbub','cyan' ,2): ('extra5.ppm', (0,544, 32, 32)), + 'sheep-sm': ('extra6.ppm', (0, 0, 32, 32)), + 'sheep-big': ('extra6.ppm', (0, 32, 46, 50)), + ('emotic', 0): ('extra7.ppm', (0, 0, 8, 8)), + ('emotic', 1): ('extra7.ppm', (0, 8, 8, 8)), + ('emotic', 2): ('extra7.ppm', (0, 16, 8, 8)), + ('emotic', 3): ('extra7.ppm', (0, 24, 8, 8)), + ('emotic', 4): ('extra7.ppm', (0, 32, 8, 8)), + ('emotic', 5): ('extra7.ppm', (0, 40, 8, 8)), + ('emotic', 6): ('extra7.ppm', (0, 48, 8, 8)), + ('butterfly', 'jailed', 0): ('butterfly.ppm', (0, 0, 32, 32)), + ('butterfly', 'jailed', 1): ('butterfly.ppm', (0, 32, 32, 32)), + ('butterfly', 'jailed', 2): ('butterfly.ppm', (0, 64, 32, 32)), + ('butterfly', 'dead', 0): ('butterfly.ppm', (0, 96, 32, 32)), + ('butterfly', 'dead', 1): ('butterfly.ppm', (0, 128, 32, 32)), + ('butterfly', 'dead', 2): ('butterfly.ppm', (0, 160, 32, 32)), + ('butterfly', 'dead', 3): ('butterfly.ppm', (0, 192, 32, 32)), + ('butterfly', 'fly', 0): ('butterfly.ppm', (0, 224, 32, 32)), + ('butterfly', 'fly', 1): ('butterfly.ppm', (0, 256, 32, 32)), + 'glue': ('glue.ppm', (0, 0, 32, 32)), + 'black': ('black.ppm', (0, 0, 32, 32)), + ('sheep',-1, 0): ('sheep.ppm', (0, 0, 32, 32)), + ('sheep',-1, 1): ('sheep.ppm', (0, 32, 32, 32)), + ('sheep',-1, 2): ('sheep.ppm', (0, 64, 32, 32)), + ('sheep',-1, 3): ('sheep.ppm', (0, 96, 32, 32)), + ('sheep', 1, 0): ('sheep.ppm', (0, 128, 32, 32)), + ('sheep', 1, 1): ('sheep.ppm', (0, 160, 32, 32)), + ('sheep', 1, 2): ('sheep.ppm', (0, 192, 32, 32)), + ('sheep', 1, 3): ('sheep.ppm', (0, 224, 32, 32)), + ('sheep', 'a'): ('sheep.ppm', (2, 263, 7, 8)), + ('sheep', 'b'): ('sheep.ppm', (11, 262, 6, 10)), + ('sheep', 'c'): ('sheep.ppm', (17, 264, 11, 8)), + ('sheep', 'd'): ('sheep.ppm', (18, 272, 11, 7)), + ('sheep', 'e'): ('sheep.ppm', (18, 279, 11, 8)), + ('sheep', 'f'): ('sheep.ppm', (4, 273, 10, 12)), + ('sheep', 'g'): ('sheep.ppm', (19, 257, 11, 8)), + } +hatmap = { + ('hat', 0, -1,1):('hat2.ppm',( 0, 0, 32, 48)), + ('hat', 0, -1,2):('hat2.ppm',( 32, 0, 32, 48)), + ('hat', 0, -1,3):('hat2.ppm',( 64, 0, 32, 48)), + ('hat', 0, 1,3):('hat2.ppm',( 96, 0, 32, 48)), + ('hat', 0, 1,2):('hat2.ppm',(128, 0, 32, 48)), + ('hat', 0, 1,1):('hat2.ppm',(160, 0, 32, 48)), + ('hat', 1, -1,1):('hat1.ppm',( 0, 0, 32, 48)), + ('hat', 1, -1,2):('hat1.ppm',( 32, 0, 32, 48)), + ('hat', 1, -1,3):('hat1.ppm',( 64, 0, 32, 48)), + ('hat', 1, 1,3):('hat1.ppm',( 96, 0, 32, 48)), + ('hat', 1, 1,2):('hat1.ppm',(128, 0, 32, 48)), + ('hat', 1, 1,1):('hat1.ppm',(160, 0, 32, 48)), + ('hat', 0) :('hat5.ppm',( 32, 0, 32, 48)), + ('hat', 1) :('hat5.ppm',( 0, 0, 32, 48)), + } + +def generate_sprmap(): + # check and maybe regenerate the colored image files + file = os.path.join('images', 'buildcolors.py') + g = {'__name__': '__auto__', '__file__': file} + execfile(file, g) + # replace the entries 'filename_%d.ppm' by a family of entries, + # one for each color + sprmap = {} + for n, (filename, rect) in (original_sprmap.items() + + extramap.items() + hatmap.items()): + if filename.find('%d') >= 0: + for i in range(MAX): + sprmap[n+1000*i] = (os.path.join('images',filename % i), rect) + else: + sprmap[n] = (os.path.join('images', filename), rect) + return sprmap +sprmap = generate_sprmap() + +transparency = { + mnstrmap.GreenAndBlue.new_bubbles[0][0]: 0xA0, + mnstrmap.GreenAndBlue.new_bubbles[0][1]: 0xB0, + mnstrmap.GreenAndBlue.new_bubbles[0][2]: 0xC0, + mnstrmap.GreenAndBlue.new_bubbles[0][3]: 0xD0, + mnstrmap.GreenAndBlue.normal_bubbles[0][0]: 0xE0, + mnstrmap.GreenAndBlue.normal_bubbles[0][1]: 0xE0, + mnstrmap.GreenAndBlue.normal_bubbles[0][2]: 0xE0, + mnstrmap.DyingBubble.first[0]: 0xD0, + mnstrmap.DyingBubble.first[1]: 0xD0, + mnstrmap.DyingBubble.first[2]: 0xD0, + mnstrmap.DyingBubble.medium[0]: 0xC0, + mnstrmap.DyingBubble.medium[1]: 0xC0, + mnstrmap.DyingBubble.medium[2]: 0xC0, + mnstrmap.DyingBubble.last[0]: 0xB0, + mnstrmap.DyingBubble.last[1]: 0xB0, + mnstrmap.DyingBubble.last[2]: 0xB0, + 'starbub': 0xE0, + } + +def sprcharacterget(c, filename=os.path.join('images', 'extra8.ppm')): + n = ord(c) - 32 + if 0 <= n < 95: + return gamesrv.getbitmap(filename, KEYCOL).geticon(n*8, 0, 8, 15) + else: + return None + +def writestr(x, y, text): + result = [] + for c in text: + ico = sprcharacterget(c) + if ico is not None: + result.append(gamesrv.Sprite(ico, x, y)) + x += 7 + return result + +def writestrlines(lines): + import boards + width = boards.bwidth + 9*boards.CELL + y = 50 + for text in lines: + if text: + writestr((width - 7*len(text)) // 2, y, text) + y += 28 + else: + y += 14 + + + +def getsample(fn, freq): + return gamesrv.getsample(os.path.join('sounds', fn), freq) + +SoundList = ['Pop', 'Jump', 'Die', 'LetsGo', 'Extralife', + 'Fruit', 'Extra', 'Yippee', 'Hurry', 'Hell', 'Shh'] + +class Snd: + pass + +def loadsounds(freqfactor=1): + for key in SoundList: + setattr(Snd, key, getsample(key.lower()+'.wav', freqfactor)) + +loadsounds() +music_intro = gamesrv.getmusic('music/Snd1-8.wav') +music_game = gamesrv.getmusic('music/Snd2-8.wav') +music_potion = gamesrv.getmusic('music/Snd3-8.wav') +music_modern = gamesrv.getmusic('music/Snd4-8.wav') +music_old = gamesrv.getmusic('music/Snd5-8.wav') +music_game2 = gamesrv.getmusic('music/Snd6-8.wav') +#gamesrv.set_musics([music_intro, music_game], 1) diff --git a/bubbob/images/.cvsignore b/bubbob/images/.cvsignore new file mode 100644 index 0000000..7a1612c --- /dev/null +++ b/bubbob/images/.cvsignore @@ -0,0 +1,7 @@ +[1-7]0000_[2-9].ppm +digits_[2-9].ppm +dragon_[2-9].ppm +dragon_bubble_[2-9].ppm +game_over_[2-9].ppm +point_[2-9].ppm +fish_[2-9].ppm diff --git a/bubbob/images/10000_0.ppm b/bubbob/images/10000_0.ppm Binary files differnew file mode 100644 index 0000000..2915dc9 --- /dev/null +++ b/bubbob/images/10000_0.ppm diff --git a/bubbob/images/20000_0.ppm b/bubbob/images/20000_0.ppm Binary files differnew file mode 100644 index 0000000..d1abe4b --- /dev/null +++ b/bubbob/images/20000_0.ppm diff --git a/bubbob/images/30000_0.ppm b/bubbob/images/30000_0.ppm Binary files differnew file mode 100644 index 0000000..9869ceb --- /dev/null +++ b/bubbob/images/30000_0.ppm diff --git a/bubbob/images/40000_0.ppm b/bubbob/images/40000_0.ppm Binary files differnew file mode 100644 index 0000000..56b3fca --- /dev/null +++ b/bubbob/images/40000_0.ppm diff --git a/bubbob/images/50000_0.ppm b/bubbob/images/50000_0.ppm Binary files differnew file mode 100644 index 0000000..765ab0f --- /dev/null +++ b/bubbob/images/50000_0.ppm diff --git a/bubbob/images/60000_0.ppm b/bubbob/images/60000_0.ppm Binary files differnew file mode 100644 index 0000000..ed220b8 --- /dev/null +++ b/bubbob/images/60000_0.ppm diff --git a/bubbob/images/70000_0.ppm b/bubbob/images/70000_0.ppm Binary files differnew file mode 100644 index 0000000..575cd03 --- /dev/null +++ b/bubbob/images/70000_0.ppm diff --git a/bubbob/images/big_bubble.ppm b/bubbob/images/big_bubble.ppm new file mode 100644 index 0000000..f0e7b7b --- /dev/null +++ b/bubbob/images/big_bubble.ppm @@ -0,0 +1,4 @@ +P6 +64 384 +255 +ÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿÿÿÿÿÿÿÌÿÿÿÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÿÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÌÿÿÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
\ No newline at end of file diff --git a/bubbob/images/big_bubble_2.ppm b/bubbob/images/big_bubble_2.ppm new file mode 100644 index 0000000..8436f6e --- /dev/null +++ b/bubbob/images/big_bubble_2.ppm @@ -0,0 +1,4 @@ +P6 +64 320 +255 +ŒŒŒÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«««ÿÿÿÿÿÿÿÿÿ˜˜˜üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{{{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ===ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ===ÿÿÿÿÿÿ===ÿÿÿÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿOOOÿÿÿÄÄÄÿÿÿÿÿÿÿÿÿÄÄÄ===ÿÿÿÿÿÿÄÄÄ===ÿÿÿÿÿÿÿÿÿ===ÿÿÿÿÿÿÿÿÿÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{{{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒŒŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¥¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKKKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄÄÄÄÄÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄÄÄÄÄ{{{{{{{{{{{{iiiÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
\ No newline at end of file diff --git a/bubbob/images/black.ppm b/bubbob/images/black.ppm new file mode 100644 index 0000000..c5adbe8 --- /dev/null +++ b/bubbob/images/black.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +32 32 +255 +
\ No newline at end of file diff --git a/bubbob/images/blitzy.ppm b/bubbob/images/blitzy.ppm Binary files differnew file mode 100644 index 0000000..b2e0963 --- /dev/null +++ b/bubbob/images/blitzy.ppm diff --git a/bubbob/images/blitzy_angry.ppm b/bubbob/images/blitzy_angry.ppm Binary files differnew file mode 100644 index 0000000..464ce2b --- /dev/null +++ b/bubbob/images/blitzy_angry.ppm diff --git a/bubbob/images/blitzy_shot.ppm b/bubbob/images/blitzy_shot.ppm Binary files differnew file mode 100644 index 0000000..378c956 --- /dev/null +++ b/bubbob/images/blitzy_shot.ppm diff --git a/bubbob/images/bonus_0.ppm b/bubbob/images/bonus_0.ppm Binary files differnew file mode 100644 index 0000000..4fa6e4c --- /dev/null +++ b/bubbob/images/bonus_0.ppm diff --git a/bubbob/images/bonus_1.ppm b/bubbob/images/bonus_1.ppm Binary files differnew file mode 100644 index 0000000..82ddd25 --- /dev/null +++ b/bubbob/images/bonus_1.ppm diff --git a/bubbob/images/bonus_10.ppm b/bubbob/images/bonus_10.ppm Binary files differnew file mode 100644 index 0000000..9ffd660 --- /dev/null +++ b/bubbob/images/bonus_10.ppm diff --git a/bubbob/images/bonus_11.ppm b/bubbob/images/bonus_11.ppm Binary files differnew file mode 100644 index 0000000..1a5c509 --- /dev/null +++ b/bubbob/images/bonus_11.ppm diff --git a/bubbob/images/bonus_12.ppm b/bubbob/images/bonus_12.ppm Binary files differnew file mode 100644 index 0000000..354a1fe --- /dev/null +++ b/bubbob/images/bonus_12.ppm diff --git a/bubbob/images/bonus_2.ppm b/bubbob/images/bonus_2.ppm Binary files differnew file mode 100644 index 0000000..61588b0 --- /dev/null +++ b/bubbob/images/bonus_2.ppm diff --git a/bubbob/images/bonus_3.ppm b/bubbob/images/bonus_3.ppm Binary files differnew file mode 100644 index 0000000..dd1ff6d --- /dev/null +++ b/bubbob/images/bonus_3.ppm diff --git a/bubbob/images/bonus_4.ppm b/bubbob/images/bonus_4.ppm Binary files differnew file mode 100644 index 0000000..c85e926 --- /dev/null +++ b/bubbob/images/bonus_4.ppm diff --git a/bubbob/images/bonus_5.ppm b/bubbob/images/bonus_5.ppm Binary files differnew file mode 100644 index 0000000..57bd455 --- /dev/null +++ b/bubbob/images/bonus_5.ppm diff --git a/bubbob/images/bonus_6.ppm b/bubbob/images/bonus_6.ppm Binary files differnew file mode 100644 index 0000000..2ac4a64 --- /dev/null +++ b/bubbob/images/bonus_6.ppm diff --git a/bubbob/images/bonus_7.ppm b/bubbob/images/bonus_7.ppm Binary files differnew file mode 100644 index 0000000..5de3eaa --- /dev/null +++ b/bubbob/images/bonus_7.ppm diff --git a/bubbob/images/bonus_8.ppm b/bubbob/images/bonus_8.ppm Binary files differnew file mode 100644 index 0000000..b1c6c2a --- /dev/null +++ b/bubbob/images/bonus_8.ppm diff --git a/bubbob/images/bonus_9.ppm b/bubbob/images/bonus_9.ppm Binary files differnew file mode 100644 index 0000000..8d51608 --- /dev/null +++ b/bubbob/images/bonus_9.ppm diff --git a/bubbob/images/bubble.ppm b/bubbob/images/bubble.ppm new file mode 100644 index 0000000..7c6af5e --- /dev/null +++ b/bubbob/images/bubble.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.1 +32 448 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ff¤lh jgœgf]V‡VI‡VI‡VIqió›xù°ˆý¾“þ×þ–ü½’ø®‡õ¡}ás¯rj[Sásü»ÿؤÿãªÿãªÿߨÿؤÿÌœÿÆ™ü½’ø®‡î–vÃ~n‘_ZÐ…pÿÕ¢ÿ÷¶ÿü¹ÿü¹ÿ÷¶ÿð²ÿà¨ÿÓ ÿÌœÿÇ™ýÁ•ù²Šî–vºxl‘_ZÀ|mÿá©ÿÿãÿÿÿÿÿÿÿÿÖÿû¸ÿí°ÿݧÿÕ¢ÿПÿËœþ×ü»ø®‡ë”u½zl‘_Z jgÿПÿÿñÿÿÿÿÿÿÿÿÿÿÿÈÿ÷¶ÿí°ÿãªÿÙ¤ÿÔ¡ÿÏžÿÇ™ýÁ•û¹÷¨‚ãt¸wk]V[S÷ªƒÿû¸ÿÿÿÿÿÿÿÿÿÿÿÿÿýºÿ÷¶ÿí°ÿå«ÿݧÿÕ¢ÿПÿËœþ×ü½’ù°ˆóœyÞs¯rj‹ZOãtÿð²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿü¹ÿõµÿí°ÿç¬ÿߨÿÕ¢ÿПÿËœÿÆ™ý¾“ú³‹ö¥€ë”uË‚o–db؉qÿá©ÿÿñÿÿÿÿÿÿÿÿÿÿÿãÿÿ»ÿù·ÿô´ÿí°ÿå«ÿߨÿؤÿПÿËœÿÆ™ý¾“ú¶ö¦î–v؉q¯rj‹ZOù°ˆÿýºÿÿÿÿÿÿÿÿÿÿÿñÿÿÈÿü¹ÿõµÿð²ÿé®ÿä«ÿݧÿÕ¢ÿПÿËœÿÆ™ý¾“ú¶÷¨‚ó™wÞsÀ|m’`[Ë‚oÿПÿÿÈÿÿÿÿÿÿÿÿÿÿÿÈÿýºÿù·ÿó³ÿí°ÿç¬ÿá©ÿÛ¥ÿÔ¡ÿÏžÿÉ›þ×ü½’ú³‹÷¨‚ó™wás΄p¤lh‰XLÅnÿÙ¤ÿÿÈÿÿÿÿÿÿÿÿãÿýºÿù·ÿõµÿð²ÿë¯ÿä«ÿߨÿÙ¤ÿÓ ÿÌœÿÇ™þ–û¹ù²Š÷¨‚ó›xãtÐ…p¸wk‰XLÀ|mÿߨÿÿ»ÿÿÿÿÿÿÿÿ»ÿû¸ÿ÷¶ÿñ³ÿí°ÿç¬ÿà¨ÿÛ¥ÿÕ¢ÿÏžÿÉ›þ×ý¾“û·ù°ˆ÷¨‚óœyæ‘tÓ‡qÀ|m‹ZO¸wkÿݧÿü¹ÿÿãÿýºÿû¸ÿ÷¶ÿñ³ÿí°ÿèÿãªÿݧÿؤÿÓ ÿÌœÿÇ™þ–ü½’ú¶ø®‡ö¦óœyæ‘tÖˆqÈ€n[S²tjÿÓ ÿ÷¶ÿýºÿø·ÿõµÿñ³ÿí°ÿèÿä«ÿà¨ÿÙ¤ÿÔ¡ÿÏžÿËœÿÆ™ýÁ•ü»ú³‹ø¬…ö¥€ó›xæ‘t؉q΄p]V¯rjÿÉ›ÿñ³ÿ÷¶ÿó³ÿð²ÿì¯ÿç¬ÿãªÿà¨ÿÛ¥ÿÕ¢ÿПÿÌœÿÇ™þ×ý¾“û¹ù°ˆ÷¨‚õ¡}ó™wæ‘tãtÖˆq–dbªoiü½’ÿç¬ÿï±ÿèÿç¬ÿãªÿà¨ÿݧÿÙ¤ÿÕ¢ÿПÿËœÿÇ™þ×ýÁ•ü»ú³‹÷ªƒõ¢~ó›xë”uãtë”u؉q™ff¤lhø®‡ÿÓ ÿݧÿÛ¥ÿݧÿÛ¥ÿؤÿÔ¡ÿÓ ÿÏžÿÉ›ÿÆ™þ×ýÁ•ü»ú¶ø¬…ö¥€óœyî–vÞsé“uó™wÐ…p]Vœgfõ¡}ý¾“ÿÉ›ÿÏžÿÓ ÿÔ¡ÿÓ ÿÏžÿËœÿÇ™þ×ýÁ•ý¾“ü»û·ù°ˆ÷¨‚ôŸ{ï—vãtãtõ¡}ôŸ{Ã~n‰XL–dbæ‘tú³‹ü½’þ–ÿÇ™ÿËœÿÌœÿÉ›ÿÆ™þ–ü½’ü»û¹û·ù²Š÷ªƒõ¢~ó™wæ‘té“uö¥€ù²Šóœyµvk‰XLÅnö¦ú³‹û¹ý¾“þ–þ×ýÁ•ü½’û¹ú¶ú³‹ù²Šù°ˆ÷ªƒö¥€óœyë”uë”uö¥€ü»ú¶ás–dbqië”u÷¨‚ø®‡ú³‹û·û¹û·ú¶ù²Šø®‡ø¬…÷ªƒ÷¨‚õ¢~ó›xë”uë”uõ¢~û¹ÿÆ™ø®‡Ã~n‹ZOÀ|mï—vö¥€÷ªƒø¬…ø®‡ø®‡ø®‡ø¬…÷¨‚ö¥€õ¢~ôŸ{ó™wé“uæ‘tôŸ{û·þ×ü»ë”uŸig’`[È€nï—vôŸ{õ¡}õ¢~õ¢~ö¥€ö¥€õ¡}ôŸ{ó™wï—vé“ué“uóœyù²ŠýÁ•û¹óœyºxl‹ZO–dbË‚oé“uë”uî–vï—vó™wó™wï—vî–vé“uæ‘të”uóœyø¬…û¹û·ôŸ{Ån‹ZOœgfÅnÖˆqÛ‹rásé“ué“uæ‘tæ‘té“uë”uóœy÷¨‚ù²Šø®‡ó›xË‚o’`[™ffºxlË‚o؉qæ‘tæ‘tæ‘té“uï—vó›xõ¡}ö¥€õ¢~ãtºxl’`[”b_¯rjÀ|mÐ…pÞsãtæ‘të”ué“uæ‘tÞsË‚oªoi‰XL‰XL”b_µvkË‚oÓ‡qÓ‡qÓ‡qË‚o½zl jg‘_Z‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VIvF,‡VI‡VI‡VI„SC‡VI‡VI‡VI‡VIœgfÐ…p÷ªƒü½’ÿÆ™ÿÇ™ÿÆ™ü»÷ªƒó›xÀ|m]Vœgf؉qü½’ÿߨÿÛ¥ÿݧÿÕ¢ÿÔ¡ÿПÿËœÿÇ™ýÁ•ù°ˆë”uŸig‡VI‡VIø®‡ÿù·ÿÿ»ÿõµÿó³ÿï±ÿãªÿÛ¥ÿÕ¢ÿПÿÌœÿÇ™ýÁ•ú³‹æ‘t½zl‡VI‡VIü½’ÿÿÖÿÿÿÿÿÿÿÿãÿü¹ÿô´ÿë¯ÿá©ÿÙ¤ÿÔ¡ÿПÿËœþ–ü»ú³‹ë”uÅn‡VI”b_÷¨‚ÿÿñÿÿÿÿÿÿÿÿÿÿÿÿÿýºÿø·ÿï±ÿå«ÿߨÿؤÿÓ ÿÏžÿÇ™ýÁ•ü½’÷¨‚é“uÓ‡q‡VI…TEï—vÿù·ÿÿãÿÿÿÿÿÿÿÿÿÿÿãÿü¹ÿ÷¶ÿð²ÿé®ÿà¨ÿÙ¤ÿÓ ÿÏžÿÉ›þ×ý¾“ø®‡ôŸ{é“uÀ|m‡VIásÿé®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿÿ»ÿû¸ÿõµÿï±ÿèÿá©ÿÙ¤ÿÔ¡ÿÏžÿÉ›þ×ý¾“ú³‹ö¥€ë”u؉q¤lh¤lhÿð²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿýºÿõµÿó³ÿí°ÿç¬ÿá©ÿÙ¤ÿÔ¡ÿÏžÿÉ›þ×ü½’ú³‹ö¦ï—vásÀ|m‡VIù°ˆÿø·ÿÿÿÿÿÿÿÿÿÿÿãÿÿÈÿýºÿù·ÿô´ÿï±ÿé®ÿä«ÿߨÿؤÿÓ ÿÌœÿÉ›þ×ü½’ú³‹ö¦ï—vãtÓ‡q¤lh‡VIÿÓ ÿø·ÿÿÿÿÿÿÿÿÿÿÿÈÿýºÿû¸ÿõµÿñ³ÿì¯ÿç¬ÿá©ÿݧÿÕ¢ÿПÿËœÿÇ™þ–û¹ù²Šö¦ó›xãtÓ‡q½zl‡VI‡VIÿߨÿ÷¶ÿÿÿÿÿÿÿÿñÿýºÿû¸ÿõµÿó³ÿí°ÿèÿãªÿߨÿÙ¤ÿÔ¡ÿÏžÿÉ›þ×ý¾“û·ù°ˆ÷¨‚ó›xæ‘tÓ‡q΄p‡VI‡VIÿߨÿô´ÿÿãÿÿ»ÿü¹ÿû¸ÿ÷¶ÿñ³ÿí°ÿé®ÿå«ÿà¨ÿÛ¥ÿÕ¢ÿПÿËœÿÆ™þ–ü½’ú¶ø®‡÷¨‚ó›xé“uÓ‡q؉q‡VI‡VIÿÕ¢ÿí°ÿ÷¶ÿù·ÿ÷¶ÿõµÿð²ÿí°ÿé®ÿå«ÿá©ÿݧÿؤÿÓ ÿÏžÿÉ›þ×ýÁ•ü»ú³‹ø¬…ö¦ó›xë”uÓ‡qÞs‡VI‡VIÿÌœÿå«ÿð²ÿô´ÿó³ÿï±ÿë¯ÿç¬ÿãªÿà¨ÿݧÿؤÿÔ¡ÿПÿËœÿÆ™þ–ü½’û·ø®‡÷¨‚õ¢~ï—vé“uæ‘tæ‘t™ff‡VIü»ÿÙ¤ÿä«ÿèÿé®ÿç¬ÿãªÿà¨ÿݧÿÙ¤ÿÕ¢ÿÓ ÿÏžÿËœÿÇ™þ×ý¾“û¹ù°ˆ÷¨‚õ¢~óœyë”uÞsó›xé“u–db‡VI÷¨‚ÿÌœÿÕ¢ÿÙ¤ÿݧÿݧÿÛ¥ÿؤÿÕ¢ÿÓ ÿÏžÿËœÿÉ›ÿÆ™þ–ý¾“û¹ù²Š÷ªƒõ¢~óœyî–vÓ‡qó™wõ¡}Û‹r‡VI‡VIó›xû¹þ×ÿËœÿÏžÿÓ ÿÕ¢ÿÓ ÿÏžÿËœÿÇ™þ×þ–ýÁ•ü½’ü»ú³‹ø¬…ö¥€ó›xï—vÓ‡qî–vù°ˆ÷¨‚Ån‡VIÖˆqù²Šü»ý¾“þ×ÿÇ™ÿÌœÿÏžÿÇ™þ×ýÁ•ü½’ü»ü»û¹ú³‹ø®‡ö¦ôŸ{ï—v؉qôŸ{û·ý¾“ôŸ{¯rj‘_Zõ¢~ù°ˆú³‹û¹ü½’ýÁ•þ–ý¾“ü½’û·ú¶ú³‹ù²Šù°ˆø¬…ö¦õ¢~ï—væ‘tóœyû¹ÿÌœû·Ó‡q‡VIÓ‡qõ¢~÷ªƒø®‡ù²Šú³‹ú¶ú¶ú³‹ù°ˆø®‡÷ªƒ÷¨‚ö¦õ¢~óœyæ‘tÞsõ¡}ü»ÿÌœÿÌœö¥€¯rj‡VIÖˆqôŸ{ö¥€÷¨‚÷¨‚÷¨‚÷ªƒ÷ªƒ÷ªƒ÷¨‚ö¥€õ¡}ôŸ{ó™wæ‘tæ‘tóœyû¹ÿÌœÿÇ™÷¨‚Ë‚o‡VI‡VI؉qó™wó™wó›xó›xõ¡}õ¡}õ¡}óœyóœyï—vé“uæ‘të”uõ¡}û·ÿÆ™ýÁ•ôŸ{Û‹r‡VI‡VIÓ‡qÞsÞsãtãtë”uë”ué“ué“uæ‘tæ‘tó›xõ¢~ú³‹ü½’ú¶õ¢~Þs‡VI‡VIºxlÓ‡qÓ‡qæ‘tó›xé“uæ‘té“uó™wóœy÷¨‚ø¬…ù²Š÷¨‚ë”u½zl‡VI‡VIªoiºxlÓ‡qÛ‹rë”uë”uï—vó›xó™wó™wó›xæ‘tÓ‡q‡VI‡VI‡VI™ff²tjË‚oÛ‹rÓ‡qÓ‡qÓ‡qºxlªoi‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VI‡VIºxlÖˆqõ¢~û¹ýÁ•ÿÇ™ÿÆ™þ×û¹ø¬…õ¡}Þsµvk’`[À|mó™wÿÇ™ÿð²ÿõµÿ÷¶ÿó³ÿë¯ÿãªÿݧÿؤÿÏžÿÇ™ü»ôŸ{΄pŸig[Sªoiû·ÿø·ÿÿÿÿÿÿÿÿÿÿÿãÿû¸ÿó³ÿé®ÿãªÿߨÿؤÿÓ ÿËœü»ö¥€Þs²tj‘_Z²tjÿËœÿÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿø·ÿí°ÿå«ÿߨÿÕ¢ÿÏžÿÇ™þ×ý¾“û·ö¦ãt½zl–dbµvkýÁ•ÿü¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýºÿõµÿí°ÿèÿà¨ÿؤÿПÿÌœÿÇ™þ–ü½’ù²Šõ¡}é“uÅn–db²tjû·ÿü¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿù·ÿô´ÿí°ÿç¬ÿà¨ÿÙ¤ÿÓ ÿÏžÿÉ›þ×ý¾“ú¶÷¨‚ó™wÞsºxl‘_Z²tjÿÉ›ÿü¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿÿÈÿü¹ÿõµÿñ³ÿì¯ÿå«ÿà¨ÿؤÿÓ ÿÏžÿÉ›þ×ý¾“û·ø¬…óœyæ‘tÐ…pqi‰XLé“uÿí°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿÿÈÿü¹ÿø·ÿó³ÿí°ÿèÿãªÿߨÿÕ¢ÿПÿÌœÿÇ™þ×ü½’ú¶ø¬…ôŸ{ë”uÛ‹rÀ|m–db–dbü»ÿõµÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÿü¹ÿù·ÿô´ÿð²ÿë¯ÿä«ÿà¨ÿÛ¥ÿÔ¡ÿÏžÿËœÿÆ™þ–ü»ú³‹÷ªƒõ¡}ë”uÞsË‚o¯rj[S–dbþ×ÿù·ÿÿÿÿÿÿÿÿÿÿÿÈÿü¹ÿø·ÿô´ÿð²ÿì¯ÿç¬ÿá©ÿݧÿؤÿÓ ÿÌœÿÉ›þ×ý¾“û¹ù²Š÷ªƒõ¡}î–vÞsÐ…pºxl[S–dbþ×ÿÿ»ÿÿÿÿÿÿÿýºÿû¸ÿø·ÿó³ÿï±ÿë¯ÿç¬ÿãªÿݧÿؤÿÔ¡ÿÏžÿÉ›þ×ýÁ•ü½’ú¶ù°ˆ÷¨‚õ¡}ï—vásÓ‡qÃ~nœgf”b_ü»ÿü¹ÿÿÿÿÿ»ÿ÷¶ÿô´ÿñ³ÿí°ÿé®ÿå«ÿá©ÿߨÿÙ¤ÿÔ¡ÿПÿËœÿÇ™þ–ý¾“û¹ú³‹ø¬…ö¦ôŸ{î–vás؉qÈ€nœgf”b_ù²Šÿ÷¶ÿÿ»ÿø·ÿð²ÿí°ÿé®ÿç¬ÿãªÿà¨ÿݧÿÙ¤ÿÔ¡ÿПÿÌœÿÇ™þ×ýÁ•ü»ú¶ø®‡÷¨‚õ¢~ó›xé“uãtæ‘tÓ‡qœgf‘_Zõ¡}ÿç¬ÿù·ÿë¯ÿä«ÿãªÿá©ÿߨÿÛ¥ÿؤÿÔ¡ÿПÿÌœÿËœÿÇ™þ×ýÁ•ü½’ú¶ù°ˆ÷¨‚õ¡}ó›xë”uásé“uî–vÐ…pœgf[Së”uÿÏžÿèÿПÿÔ¡ÿؤÿؤÿÕ¢ÿÓ ÿПÿÌœÿÉ›ÿÆ™þ×þ–ý¾“ü»ú¶ù°ˆ÷¨‚õ¡}ó›xë”uÞsæ‘tôŸ{ó™wÅn]V–dbÛ‹rû·ÿÇ™þ–ÿÆ™ÿËœÿÌœÿÏžÿÌœÿÉ›ÿÆ™þ–ý¾“ü½’ü½’û¹ú¶ù°ˆ÷ªƒõ¢~ó›xë”uãtî–vö¦ø®‡ï—vºxl]VqiôŸ{ú³‹ú¶û¹ý¾“þ–þ×þ×ýÁ•ü½’û¹ú¶ú¶ú³‹ù²Šø®‡÷¨‚õ¢~ó›xë”ué“uó›xù²Šü½’ø¬…Öˆqœgf]V؉qóœy÷ªƒø®‡ú³‹ú¶û·û·û·ú³‹ù°ˆø®‡ø¬…÷ªƒ÷¨‚ö¥€ôŸ{ó™wé“uë”uõ¡}ú¶þ×ý¾“ó›x½zl‰XL™ff΄pó™wõ¢~÷¨‚÷ªƒ÷ªƒ÷ªƒø¬…÷ªƒ÷¨‚ö¦õ¢~õ¡}ôŸ{ó™wë”uæ‘të”uõ¢~û·þ×ü½’ö¥€È€n’`[”b_Ã~né“uó™wó›xó›xóœyõ¡}õ¡}ôŸ{óœyó›xï—vë”ué“ué“uï—vö¥€û·ý¾“ú¶ôŸ{È€n”b_‘_ZºxlÖˆqÞsásãté“uî–vë”ué“ué“uæ‘tæ‘tî–vóœyö¦ù²Šú¶ø¬…ï—vÀ|m’`[[SqiÀ|m΄pÖˆqãtë”uæ‘tæ‘té“uë”uï—vôŸ{ö¥€÷¨‚÷¨‚ó™wÓ‡qqi]V‰XL”b_ªoiºxlË‚o؉qásæ‘té“uë”uë”uë”ué“uÞsÐ…pªoi[S‡VI‰XL]V jg½zlÐ…pÖˆqÖˆqÖˆqÐ…pÃ~nµvk’`[‹ZO‡VI‡VI‡VI–dbŸig–db]V‡VI‡VIY33_66^55[44R..L++L++L++d99ŒPPŸXX±^^¸``µ__¯]]XX‘RR€IIe99Q..€II¬\\ÕffõffõffçffÕffÃccº``¯]]XXˆMMp??S//wDDÐffÿ~~ÿÿŽŽÿ~~ÿqqìffÉeeÃcc¼aa³^^¡YYˆMMk==S//n??ñffÿÿÆÆÿ½½ÿ©©ÿ‰‰ÿmmãffÐffÇddÀbb¸``¬\\XX†LLm>>S//^55Çddÿ²²ÿææÿëëÿÏÏÿ££ÿ||ÿmmõffÙffÌffÅdd¼aa³^^ª\\—VV‚JJj<<R..Q..™WWÿ‡‡ÿÍÍÿððÿééÿÀÀÿ––ÿ~~ÿnnÿffãffÐffÇddÀbb¸``¯]]ŸXXŽQQHHe99O--‚JJÿqqÿ¶¶ÿÍÍÿ××ÿÌÌÿªªÿŽŽÿ{{ÿnnÿggçffÐffÇddÀbbº``±^^¤ZZ”TT†LLtBBX22|FFñffÿ±±ÿååÿÖÖÿ¿¿ÿ®®ÿ™™ÿ„„ÿwwÿmmÿffçffÕffÇddÀbbº``±^^¦ZZ•UUˆMM|FFe99O--ŸXXÿ‘‘ÿßßÿööÿÔÔÿ²²ÿ ÿÿ{{ÿqqÿiiúffãffÐffÇddÀbbº``±^^¦ZZ—VV‹OOHHn??U00tBBÇddÿ¡¡ÿêêÿêêÿÁÁÿ¡¡ÿ’’ÿƒƒÿvvÿmmÿggñffÞffÌffÅdd¾bb¸``¯]]¤ZZ—VV‹OO€IIvCC_66N,,q@@Ùffÿ¡¡ÿääÿØØÿ®®ÿ••ÿ……ÿ{{ÿqqÿkkúffçffÙffÉeeÃcc¼aaµ__ª\\¡YY—VVŒPP‚JJwDDj<<N,,n??çffÿ™™ÿÌÌÿ½½ÿœœÿˆˆÿ||ÿttÿmmÿggìffÞffÐffÅdd¾bb¸``±^^¨[[ŸXX—VVŽQQƒKKyEEn??O--j<<ãffÿŒŒÿ¬¬ÿ““ÿ‡‡ÿ~~ÿttÿmmÿhhõffãffÕffÉeeÃcc¼aaµ__¯]]¦ZZXX•UUŽQQƒKKzEEsAAQ..g::Éeeÿ||ÿ’’ÿÿzzÿttÿmmÿhhúffìffÙffÌffÅddÀbbº``³^^¬\\¤ZZ›WW”TTŒPPƒKK|FFvCCR..e99¾bbÿttÿ||ÿuuÿqqÿllÿggõffìffÞffÐffÇddÃcc¼aa¸``±^^ª\\ŸXX—VV‘RR‹OOƒKK‚JJzEEX22b88¯]]ÿggÿppÿhhÿggõffìffãffÙffÐffÇddÀbb¼aa¸``³^^¬\\¤ZZ™WW’SSŒPP†LL‚JJ†LL|FFY33_66XXÉeeãffÞffãffÞffÕffÌffÉeeÅdd¾bbº``¸``³^^¬\\¦ZZ›WW”TTŽQQˆMMHH…KK‹OOwDDR..[44‘RR±^^¾bbÅddÉeeÌffÉeeÅddÀbb¼aa¸``³^^±^^¬\\¨[[ŸXX—VVQQ‰NN‚JJ‚JJ‘RRQQp??N,,X22ƒKK¤ZZ¯]]µ__¼aaÀbbÃcc¾bbº``µ__¯]]¬\\ª\\¨[[¡YY™WW’SS‹OOƒKK…KK”TT¡YYŽQQh;;N,,q@@•UU¤ZZª\\±^^µ__¸``³^^¯]]ª\\¦ZZ¤ZZ¡YYŸXX™WW”TTŽQQ†LL†LL”TT¬\\¦ZZ€IIX22d99†LL—VVXX¤ZZ¨[[ª\\¨[[¦ZZ¡YYXX›WW™WW—VV’SSŒPP†LL†LL’SSª\\º``XXp??O--n??‰NN”TT™WW›WWXXXXXX›WW—VV”TT’SSQQ‹OO…KKƒKKQQ¨[[¸``¬\\†LL\44U00sAA‰NNQQ‘RR’SS’SS”TT”TT‘RRQQ‹OO‰NN…KK…KKŽQQ¡YY³^^ª\\ŽQQk==O--X22tBB…KK†LLˆMM‰NN‹OO‹OO‰NNˆMM…KKƒKK†LLŽQQ›WWª\\¨[[QQq@@O--[44q@@zEE}GG€II…KK…KKƒKKƒKK…KK†LLŽQQ—VV¡YYXXŒPPtBBU00Y33k==tBB|FFƒKKƒKKƒKK…KK‰NNŒPP‘RR”TT’SS‚JJk==U00V11e99n??wDDHH‚JJƒKK†LL…KKƒKKHHtBBb88N,,N,,V11h;;tBByEEyEEyEEtBBm>>^55S//L++L++L++L++L++L++L++L++?##L++L++L++I))L++L++L++L++[44wDD™WW¯]]º``¼aaº``¬\\™WWŒPPn??R..[44|FF¯]]çffÞffãffÐffÌffÇddÀbb¼aa³^^ŸXX†LL\44L++L++XXÿ„„ÿ——ÿzzÿuuÿppõffÞffÐffÇddÃcc¼aa³^^¤ZZƒKKm>>L++L++¯]]ÿ¥¥ÿääÿääÿªªÿŒŒÿyyÿkkñffÙffÌffÇddÀbbµ__¬\\¤ZZ†LLq@@L++V11—VVÿ³³ÿòòÿÿÿÿôôÿÅÅÿ‘‘ÿÿppÿffçffÕffÉeeÅdd¼aa³^^¯]]—VV…KKyEEL++K**‰NNÿ„„ÿªªÿääÿùùÿÝÝÿ®®ÿÿ~~ÿqqÿiiìffÙffÉeeÅdd¾bb¸``±^^XXQQ…KKn??L++€IIÿiiÿÉÉÿääÿÃÃÿÈÈÿ±±ÿ››ÿ‡‡ÿzzÿppÿhhñffÙffÌffÅdd¾bb¸``±^^¤ZZ”TT†LL|FF_66_66ÿrrÿÁÁÿÿÿÿÿÿÿÃÃÿ··ÿŸŸÿ‘‘ÿ{{ÿuuÿmmÿggñffÙffÌffÅdd¾bb¸``¯]]¤ZZ•UU‰NN€IIn??L++ŸXXÿÿææÿýýÿèèÿ¯¯ÿ¤¤ÿ““ÿƒƒÿwwÿppÿiiúffçffÕffÉeeÃcc¾bb¸``¯]]¤ZZ•UU‰NN‚JJyEE_66L++ÉeeÿÿííÿôôÿÈÈÿ¤¤ÿ““ÿˆˆÿ{{ÿttÿllÿggñffãffÐffÇddÀbb¼aaµ__ª\\¡YY•UUŒPP‚JJyEEm>>L++L++çffÿ~~ÿ××ÿââÿµµÿ““ÿˆˆÿzzÿuuÿnnÿhhõffçffÙffÌffÅdd¾bb¸``±^^¨[[ŸXX—VVŒPPƒKKyEEvCCL++L++çffÿyyÿ®®ÿ››ÿŽŽÿ‡‡ÿ||ÿttÿnnÿiiÿffìffÞffÐffÇddÀbbº``µ__¯]]¦ZZXX—VVŒPP…KKyEE|FFL++L++Ðffÿmmÿ||ÿƒƒÿ~~ÿzzÿrrÿmmÿiiÿffñffãffÕffÉeeÅdd¾bb¸``³^^¬\\¤ZZ›WW•UUŒPP†LLyEEHHL++L++ÃccÿffÿqqÿyyÿuuÿppÿkkÿggõffìffãffÕffÌffÇddÀbbº``µ__¯]]¨[[XX—VV’SS‰NN…KKƒKKƒKKY33L++¬\\ÙffúffÿhhÿiiÿggõffìffãffÙffÐffÉeeÅddÀbb¼aa¸``±^^ª\\ŸXX—VV’SSŽQQ†LLHHŒPP…KKX22L++—VVÃccÐffÙffãffãffÞffÕffÐffÉeeÅddÀbb¾bbº``µ__±^^ª\\¡YY™WW’SSŽQQˆMMyEE‹OO‘RR}GGL++L++ŒPPª\\¸``ÀbbÅddÉeeÐffÉeeÅddÀbb¼aa¸``µ__³^^¯]]¬\\¤ZZ›WW”TTŒPP‰NNyEEˆMMŸXX—VVq@@L++zEE¡YY¬\\±^^¸``¼aaÃccÅdd¼aa¸``³^^¯]]¬\\¬\\ª\\¤ZZXX•UUQQ‰NN|FFQQ¨[[±^^QQe99S//’SSŸXX¤ZZª\\¯]]³^^µ__±^^¯]]¨[[¦ZZ¤ZZ¡YYŸXX›WW•UU’SS‰NNƒKKŽQQª\\Ãcc¨[[yEEL++yEE’SS™WWXX¡YY¤ZZ¦ZZ¦ZZ¤ZZŸXXXX™WW—VV•UU’SSŽQQƒKKHH‘RR¬\\ÃccÃcc”TTe99L++zEEQQ”TT—VV—VV—VV™WW™WW™WW—VV”TT‘RRQQ‹OOƒKKƒKKŽQQª\\Ãcc¼aa—VVtBBL++L++|FF‹OO‹OOŒPPŒPP‘RR‘RR‘RRŽQQŽQQ‰NN…KKƒKK†LL‘RR¨[[º``³^^QQ}GGL++L++yEEHHHH‚JJ‚JJ†LL†LL…KK…KKƒKKƒKKŒPP’SS¤ZZ¯]]¦ZZ’SSHHL++L++k==yEEyEEƒKKŒPP…KKƒKK…KK‹OOŽQQ—VV›WW¡YY—VV†LLm>>L++L++b88k==yEE}GG†LL†LL‰NNŒPP‹OO‹OOŒPPƒKKyEEL++L++L++Y33g::tBB}GGyEEyEEyEEk==b88L++L++L++L++L++L++L++L++L++L++L++L++L++L++L++L++L++L++k==zEE’SSª\\³^^¼aaº``¸``ª\\›WW‘RRHHh;;U00n??‹OO¼aaÿrrÿzzÿ||ÿvvÿkkõffãffÕffÅdd¼aa¬\\QQvCC\44Q..b88¨[[ÿ‚‚ÿ¸¸ÿÁÁÿ¼¼ÿªªÿŠŠÿuuÿiiõffçffÕffÉeeÀbb¬\\”TTHHg::S//g::Àbbÿ¡¡ÿ××ÿêêÿÝÝÿÊÊÿ¡¡ÿ€€ÿnnÿffçffÐffÅdd¼aa¸``±^^¨[[•UU‚JJm>>X22h;;³^^ÿÿÎÎÿððÿððÿÔÔÿ··ÿ••ÿzzÿnnÿhhìffÕffÇddÃcc¼aaµ__¯]]¡YY‘RR…KKq@@X22g::¨[[ÿÿ½½ÿÍÍÿÔÔÿÏÏÿ¶¶ÿššÿ……ÿyyÿnnÿggìffÙffÉeeÅdd¾bb¸``±^^¦ZZ—VV‹OOHHk==S//g::¾bbÿŽŽÿÑÑÿííÿÖÖÿ½½ÿ±±ÿžžÿŒŒÿ{{ÿttÿllÿffìffÕffÉeeÅdd¾bb¸``±^^¨[[›WWŽQQƒKKwDDd99N,,…KKÿnnÿ¿¿ÿïïÿððÿÌÌÿÿŸŸÿÿ€€ÿuuÿnnÿhhõffçffÐffÇddÃcc¼aa¸``¯]]¦ZZ›WWQQ†LL}GGn??X22X22¬\\ÿ{{ÿááÿââÿááÿ¶¶ÿœœÿÿƒƒÿyyÿqqÿkkúffìffÞffÌffÅddÀbbº``µ__¬\\¤ZZ™WW‘RR†LLHHtBBe99Q..X22¸``ÿƒƒÿÛÛÿÓÓÿÈÈÿ ÿÿ‚‚ÿwwÿrrÿllÿggñffãffÕffÉeeÃcc¾bb¸``±^^ª\\¡YY™WW‘RRˆMMHHwDDk==Q..X22¸``ÿ——ÿÎÎÿÈÈÿ••ÿ‰‰ÿÿvvÿppÿkkÿggõffãffÕffÌffÅdd¾bb¸``³^^¯]]¦ZZŸXX—VV‘RR‰NN€IIyEEp??[44V11¬\\ÿŽŽÿ¼¼ÿœœÿ~~ÿyyÿttÿnnÿiiÿffñffçffÙffÌffÇddÀbb¼aaµ__±^^ª\\¤ZZ›WW•UUQQˆMM€II|FFsAA[44V11¡YYÿ||ÿššÿ€€ÿrrÿnnÿiiÿggõffìffãffÙffÌffÇddÃcc¼aa¸``³^^¬\\¦ZZXX—VV’SSŒPP…KK‚JJƒKKyEE[44S//‘RRÿggÿƒƒÿkkúffõffñffçffÞffÕffÌffÇddÃccÀbb¼aa¸``³^^¯]]¦ZZŸXX—VV‘RRŒPP†LL€II…KKˆMMwDD[44Q..†LLÅddÿhhÇddÌffÕffÕffÐffÉeeÇddÃcc¾bbº``¸``µ__±^^¬\\¦ZZŸXX—VV‘RRŒPP†LLHHƒKKQQ‹OOq@@R..X22}GG¨[[¼aaµ__º``ÀbbÃccÅddÃcc¾bbº``µ__±^^¯]]¯]]ª\\¦ZZŸXX™WW’SSŒPP†LL‚JJˆMM•UUXX‰NNk==R..d99QQ¤ZZ¦ZZª\\±^^µ__¸``¸``³^^¯]]ª\\¦ZZ¦ZZ¤ZZ¡YYXX—VV’SSŒPP†LL…KKŒPP¡YY¯]]›WWzEE[44R..|FFŽQQ™WWXX¤ZZ¦ZZ¨[[¨[[¨[[¤ZZŸXXXX›WW™WW—VV”TTQQ‹OO…KK†LL‘RR¦ZZ¸``±^^ŒPPm>>N,,Y33vCC‹OO’SS—VV™WW™WW™WW›WW™WW—VV•UU’SS‘RRQQ‹OO†LLƒKK†LL’SS¨[[¸``¯]]”TTsAAU00V11p??…KK‹OOŒPPŒPPŽQQ‘RR‘RRQQŽQQŒPP‰NN†LL…KK…KK‰NN”TT¨[[±^^¦ZZQQsAAV11S//k==zEEHH€II‚JJ…KKˆMM†LL…KK…KKƒKKƒKKˆMMŽQQ•UU¡YY¦ZZ›WW‰NNn??U00Q..d99n??vCCzEE‚JJ†LLƒKKƒKK…KK†LL‰NNQQ”TT—VV—VV‹OOyEEd99R..N,,V11b88k==tBB|FF€IIƒKK…KK†LL†LL†LL…KKHHwDDb88Q..L++N,,R..^55m>>wDDzEEzEEzEEwDDp??h;;U00O--L++L++L++X22\44X22R..L++L++?D""B!!@ 8333G##}33°33Ö33ê33å33Ñ33«33‹33a00H$$7a00Ì33ÿ;;ÿFFÿFFÿBBÿ;;ÿ44ï33Ñ33«33o33R)):Y,,ÿ::ÿddÿxxÿwwÿddÿUUÿCCÿ88ÿ44ô33Û33´33o33M&&:P((ÿDDÿššÿººÿ¯¯ÿ——ÿqqÿQQÿ@@ÿ::ÿ66ÿ33ê33Ì33«33j33O'':B!!ÿ66ÿ¡¡ÿààÿææÿÄÄÿÿccÿQQÿFFÿ==ÿ99ÿ55ô33Û33Ç3333b11L&&87¢33ÿnnÿÂÂÿííÿääÿ±±ÿ€€ÿddÿRRÿHHÿ@@ÿ::ÿ66ÿ33ê33Ñ33°3333a00H$$7b11ÿUUÿ¦¦ÿÂÂÿÎÎÿ¿¿ÿ˜˜ÿwwÿ``ÿRRÿIIÿBBÿ::ÿ66ÿ33ï33Ö33¹33”33j33U**=]..ÿDDÿŸŸÿßßÿÌÌÿ°°ÿœœÿ‚‚ÿjjÿ\\ÿQQÿHHÿBBÿ;;ÿ66ÿ33ï33Ö33¾33™33o33]..H$$7°33ÿyyÿ××ÿóóÿËËÿ¡¡ÿ‹‹ÿvvÿ``ÿUUÿMMÿGGÿ@@ÿ::ÿ66ÿ33ï33Ö33¾3333x33a00P((;U**ÿ66ÿŒŒÿååÿååÿ´´ÿŒŒÿ{{ÿiiÿ[[ÿQQÿIIÿDDÿ??ÿ99ÿ55ù33ê33Ñ33¹3333x33a00W++D""5T**ÿ==ÿŒŒÿÞÞÿÐÐÿœœÿÿmmÿ``ÿUUÿNNÿGGÿBBÿ==ÿ88ÿ44ô33å33Ç33´3333}33b11Y,,L&&5P((ÿBBÿ‚‚ÿ¿¿ÿ¯¯ÿ‡‡ÿooÿccÿYYÿQQÿIIÿCCÿ??ÿ::ÿ55ù33ê33Ö33Â33°333333d22Z--P((7L&&ÿ@@ÿssÿ™™ÿ||ÿnnÿddÿYYÿQQÿKKÿFFÿ@@ÿ;;ÿ88ÿ44ô33å33Ñ33¾33«33™3333d22\..T**7J%%ÿ88ÿccÿ{{ÿeeÿ__ÿYYÿQQÿKKÿGGÿCCÿ==ÿ99ÿ55ÿ33ï33Û33Ì33¹33¦33”33}33d22]..W++8H$$ù33ÿYYÿccÿZZÿUUÿPPÿIIÿFFÿCCÿ??ÿ::ÿ66ÿ44ô33ê33Ö33Ç33°3333‹33x33d22b11\..=E""Ñ33ÿIIÿSSÿKKÿIIÿFFÿCCÿ@@ÿ==ÿ::ÿ66ÿ33ô33ê33Û33Ì33¹33¢3333}33j33b11j33]..?D""«33ÿ88ÿ@@ÿ??ÿ@@ÿ??ÿ;;ÿ99ÿ88ÿ55ù33ï33ê33Û33Ì33¾33¦33”3333o33a00f33x33Y,,8@ ‹33Ö33ù33ÿ55ÿ88ÿ99ÿ88ÿ55ÿ33ô33ê33Û33Ö33Ì33Â33°3333†33s33b11b11‹33†33R))5=d22¹33Ñ33å33ô33ÿ33ÿ44ù33ï33å33Ñ33Ì33Ç33Â33´33¢3333x33d22f33”33´3333L&&5T**™33¹33Ç33Ö33å33ê33Û33Ñ33Ç33¾33¹33´33°33¢33”3333j33j33”33Ì33¾33a00=G##j3333«33¹33Â33Ç33Â33¾33´33«33¦33¢333333}33j33j3333Ç33ï33«33R))7P((s33”33¢33¦33«33«33«33¦3333”3333†33x33f33d22†33Â33ê33Ì33j33@ ;T**s33†33‹333333”33”33‹33†33x33s33f33f3333´33Û33Ç3333M&&7=U**f33j33o33s33x33x33s33o33f33d22j3333¦33Ç33Â33†33T**7@ T**\.._//a00f33f33d22d22f33j333333´33«33}33U**;?M&&U**]..d22d22d22f33s33}33‹33”3333b11M&&;;H$$P((Y,,a00b11d22j33f33d22a00U**E""55;L&&U**Z--Z--Z--U**O''B!!:33333333*33323333@ Y,,¢33Ñ33ï33ô33ï33Ì33¢33}33P((8@ ]..Ñ33ÿBBÿ??ÿ@@ÿ::ÿ99ÿ66ÿ33ô33Û33°33j33@ 33«33ÿjjÿÿ__ÿZZÿSSÿFFÿ??ÿ::ÿ66ÿ44ô33Û33¹33d22O''33Ñ33ÿ’’ÿÞÞÿÞÞÿ˜˜ÿssÿ^^ÿNNÿDDÿ==ÿ99ÿ66ÿ33å33Ì33¹33j33T**3;33ÿ¢¢ÿïïÿÿÿÿòòÿ¸¸ÿyyÿeeÿSSÿHHÿBBÿ;;ÿ88ÿ55ô33Û33Ñ3333f33Z--33s33ÿjjÿ˜˜ÿÞÞÿøøÿÖÖÿœœÿxxÿddÿUUÿMMÿCCÿ==ÿ88ÿ55ù33ê33Ö33«33†33f33P((3a00ÿMMÿ½½ÿÞÞÿµµÿ»»ÿŸŸÿ……ÿnnÿ__ÿSSÿKKÿDDÿ==ÿ99ÿ55ù33ê33Ö33¹33”33j33]..D""D""ÿVVÿ´´ÿÿÿÿÿÿÿµµÿ§§ÿŠŠÿyyÿ``ÿZZÿQQÿIIÿDDÿ==ÿ99ÿ55ù33ê33Ñ33¹33™33s33a00P((3°33ÿeeÿààÿýýÿããÿžžÿÿ||ÿiiÿ\\ÿSSÿMMÿGGÿBBÿ;;ÿ88ÿ44ù33ê33Ñ33¹33™33s33b11Z--D""3ÿ88ÿeeÿééÿòòÿ»»ÿÿ||ÿooÿ``ÿYYÿPPÿIIÿDDÿ@@ÿ::ÿ66ÿ33ô33å33Ç33´33™33}33b11Z--O''33ÿBBÿddÿÎÎÿÜÜÿ££ÿ||ÿooÿ__ÿZZÿRRÿKKÿFFÿBBÿ==ÿ99ÿ55ù33ê33Ö33Â33°3333}33d22Z--W++33ÿBBÿ^^ÿœœÿ……ÿwwÿnnÿccÿYYÿRRÿMMÿHHÿCCÿ??ÿ::ÿ66ÿ33ï33å33Ñ33¾33«3333}33f33Z--]..33ÿ::ÿQQÿccÿiiÿddÿ__ÿVVÿQQÿMMÿHHÿDDÿ@@ÿ;;ÿ88ÿ55ù33ê33Û33Ì33¹33¦33™33}33j33Z--a0033ÿ44ÿHHÿUUÿ^^ÿZZÿSSÿNNÿIIÿFFÿCCÿ@@ÿ;;ÿ99ÿ66ÿ33ï33å33Ñ33Â33«333333s33f33d22d22?3Ì33ÿ==ÿGGÿKKÿMMÿIIÿFFÿCCÿ@@ÿ==ÿ::ÿ88ÿ55ÿ33ô33ê33Ö33Ç33°33333333j33a00}33f33=333ÿ44ÿ::ÿ==ÿ@@ÿ@@ÿ??ÿ;;ÿ::ÿ88ÿ55ÿ33ù33ï33å33Ö33Ç33´33¢333333o33Z--x33‹33_//33}33Ç33ê33ÿ33ÿ55ÿ88ÿ::ÿ88ÿ55ÿ33ô33ê33å33Û33Ñ33Ì33¹33¦33”33}33s33Z--o33°3333T**3\..´33Ì33Ö33ê33ô33ÿ44ÿ55ô33ê33Û33Ñ33Ì33Ì33Ç33¹33«33™33†33s33]..†33Â33Ö33†33H$$:33°33¹33Ç33Ñ33Û33å33Ö33Ñ33Â33¾33¹33´33°33¦33™3333s33d2233Ç33ÿ44Â33Z--3Z--33¢33«33´33¹33¾33¾33¹33°33«33¢3333™333333d22a00‹33Ì33ÿ44ÿ44”33H$$3\..†33”33333333¢33¢33¢3333”33‹33†33x33d22d2233Ç33ÿ44ô3333U**33]..x33x33}33}33‹33‹33‹333333s33f33d22j33‹33Â33ï33Û33†33_//33Z--a00a00b11b11j33j33f33f33d22d22}3333¹33Ñ33¾3333a0033M&&Z--Z--d22}33f33d22f33x333333¦33´3333j33O''33E""M&&Z--_//j33j33s33}33x33x33}33d22Z--333?J%%U**_//Z--Z--Z--M&&E""333333333333333333M&&\..33Ç33Û33ô33ï33ê33Ç33¦33‹33a00L&&;P((x33ô33ÿVVÿ__ÿccÿ[[ÿNNÿFFÿ@@ÿ;;ÿ55ô33Ì33†33W++@ 7E""Â33ÿhhÿ¨¨ÿ´´ÿÿ˜˜ÿrrÿZZÿMMÿFFÿBBÿ;;ÿ88ÿ33Ì33”33a00J%%:J%%ÿ33ÿŒŒÿÎÎÿååÿÖÖÿ¾¾ÿŒŒÿffÿRRÿHHÿBBÿ::ÿ55ô33ê33Ö33Â33™33b11O''=L&&Û33ÿxxÿÃÃÿííÿííÿËËÿ§§ÿÿ__ÿRRÿKKÿCCÿ;;ÿ66ÿ44ô33å33Ñ33´33‹33f33T**=J%%Â33ÿvvÿ¯¯ÿÂÂÿËËÿÄÄÿ¦¦ÿ„„ÿmmÿ^^ÿRRÿIIÿCCÿ==ÿ88ÿ55ù33ê33Ö33¾3333x33a00M&&:J%%ù33ÿwwÿÅÅÿééÿÌÌÿ¯¯ÿŸŸÿ‰‰ÿssÿ``ÿYYÿPPÿHHÿCCÿ;;ÿ88ÿ55ù33ê33Ö33Â33¦3333d22Y,,G##5f33ÿRRÿ°°ÿëëÿííÿ¿¿ÿššÿŠŠÿxxÿffÿZZÿRRÿKKÿFFÿBBÿ::ÿ66ÿ44ô33ê33Ñ33¾33¦33†33j33_//P((==Ì33ÿ``ÿÚÚÿÜÜÿÚÚÿ¦¦ÿ‡‡ÿxxÿiiÿ^^ÿUUÿNNÿGGÿCCÿ??ÿ99ÿ55ÿ33ï33å33Ì33¹33¢33‹33j33a00U**H$$7=ê33ÿiiÿÒÒÿÉÉÿ»»ÿ‹‹ÿvvÿhhÿ\\ÿVVÿPPÿIIÿDDÿ@@ÿ;;ÿ88ÿ44ù33ê33Ö33Ç33´33¢33‹33o33a00Y,,M&&7=ê33ÿÿÃÃÿ»»ÿÿqqÿeeÿ[[ÿSSÿNNÿIIÿFFÿ@@ÿ;;ÿ99ÿ55ù33ê33Û33Ñ33¾33°3333‹33s33a00Z--R))@ ;Ì33ÿwwÿÿ‡‡ÿddÿ^^ÿYYÿRRÿMMÿHHÿDDÿBBÿ==ÿ99ÿ66ÿ33ô33å33Ö33Ç33¹33¦33™33†33o33a00]..T**@ ;´33ÿccÿ„„ÿffÿVVÿRRÿMMÿIIÿFFÿCCÿ@@ÿ==ÿ99ÿ66ÿ44ô33ê33Û33Ì33¾33«333333}33f33b11d22Z--@ :‹33ÿIIÿiiÿNNÿGGÿFFÿDDÿBBÿ??ÿ;;ÿ99ÿ66ÿ44ÿ33ô33ê33Û33Ñ33¾33°3333‹33}33j33a00f33o33Y,,@ 7j33ÿ55ÿKKÿ66ÿ99ÿ;;ÿ;;ÿ::ÿ88ÿ66ÿ44ù33ï33ê33å33Ö33Ì33¾33°3333‹33}33j33a00d22†33x33T**8=_//Â33ô33å33ï33ÿ33ÿ44ÿ55ÿ44ù33ï33å33Ö33Ñ33Ñ33Ç33¾33°33¢3333}33j33b11o33™33«33s33M&&8G##†33¹33¾33Ç33Ö33å33ê33ê33Û33Ñ33Ç33¾33¾33¹33´33«333333}33j33f33}33´33Ñ33¦33\..@ 8]..33¢33«33¹33¾33Â33Â33Â33¹33°33«33¦33¢3333”33†33x33f33j33‹33¾33ê33Ö33}33O''5?W++x333333¢33¢33¢33¦33¢3333™3333‹33†33x33j33d22j3333Â33ê33Ñ33”33T**;;R))f33x33}33}3333‹33‹33†3333}33s33j33f33f33s33”33Â33Ö33¾33†33T**;:M&&\..a00a00b11f33o33j33f33f33d22d22o3333™33´33¾33¦33s33P((;7G##P((W++\..b11j33d22d22f33j33s33†33”333333x33Z--G##85;E""M&&U**]..a00d22f33j33j33j33f33a00Y,,E""7358B!!O''Y,,\..\..\..Y,,R))L&&;7333=@ =833
\ No newline at end of file diff --git a/bubbob/images/buildcolors.py b/bubbob/images/buildcolors.py new file mode 100644 index 0000000..0f7ea01 --- /dev/null +++ b/bubbob/images/buildcolors.py @@ -0,0 +1,324 @@ +#! /usr/bin/env python +import sys, os + +if __name__ == '__main__': + ThisDir = sys.argv[0] +else: + ThisDir = __file__ +ThisDir = os.path.dirname(os.path.abspath(ThisDir)) + +### rotate colors +import colorsys +COLORS = [#(0, 0.0, 1.0, 1, 1), # vert + #(1, 0.0, 1.0, 1, 1), # bleu + (1, -0.7, 1.0, 1, 1), # rose + (0, -0.2, 1.0, 1, 1), # brun + (1, 0.72,1.0,-1, 1), # jaune + (0, -0.35,0.85,1, 1), # rouge + (0, 0, 0.0, 1, 1), # gris + (0, -0.85, 0.9, 1, 1), # cyan (was mauve) + #(0, 0.2, 1.0, 1, 1), # turquoise + (0, 0.925, 0.95,-1, 1), # bleu fonce + #(0, 0.45, 0.5, -0.5, 0.75), # hum + (1, 'specialpixelmap'), # vert fonce + ] +MAX = 2 + len (COLORS) + +## By ION: +# +# Here's the new palette-based method. +# +# It's an array [N][320] of 24bit unsigned integers +# (where N is the total number of color sets including the original one.) +# +# That is, you access it like +# +# Palettes[(PALETTESIZE * palettenumber)+paletteindex] +# +# Activate it by passing a palette file as a cmdline argument. +# +# The color mapping could be further sped up +# by making Palettes an array of bytes rather than ints, +# at the cost of increased complexity (Palettes [ (PALETTESIZE * 3 * palettenumber) + paletteindex + component]) +# + +Palettes = None # currently there is no 'internal' palette since this is experimental. + +PALETTESIZE = 960 + +PaletteIndex = None +# generate the string:paletteindex lookup table +def initpalettelut (): + global PaletteIndex + global COLORS, COLORMAPS + # palette 0 is the base palette (green dragon, blue tiger) + # + # Palette 0 must contain NO duplicate colors. + PaletteIndex = {} + for i in range (PALETTESIZE): + v = Palettes[i] + #if v & 0xff == 0 and (v >> 8) & 0xff == 0x87 and (v >> 16) & 0xff == 0: + # print 'FOUND' + s = "".join ([chr ((v >> shift) & 0xff) for shift in (0,8,16)]) + PaletteIndex[s] = i + # invalidate COLORS, but match the length to the number of alt palettes. + COLORS = range ((len (Palettes) / PALETTESIZE) - 1) + #print 'COLORS',COLORS + COLORMAPS = [{} for n in COLORS] + #print 'COLORMAPS',COLORMAPS + +def loadpalettesets (filename): + global Palettes + #import array + #Palettes = array.array ('I') + Palettes = [] + assert ((os.path.getsize (filename) % (PALETTESIZE * 3)) == 0) + #print os.path.getsize (filename) + f = open (filename, 'rb') + for i in range (os.path.getsize(filename) / (PALETTESIZE * 3)): + for j in range (PALETTESIZE): + tmp = f.read (3) + val = ord (tmp[0]) | (ord (tmp[1]) << 8) | (ord (tmp[2]) << 16) + Palettes.append (val) + #debuggest + #print len(Palettes) + #print len(Palettes) % PALETTESIZE + assert (len (Palettes) % PALETTESIZE) == 0 + #print "Palettes len:",len (Palettes) + +def inputfiles (): + InputFiles = { + os.path.join (ThisDir, os.pardir, 'ext1', 'image1-%d.ppm'): 1, + os.path.join (ThisDir, os.pardir, 'ext3', 'image1-%d.ppm'): 1, + os.path.join (ThisDir, os.pardir, 'ext4', 'image1-%d.ppm'): 1, + os.path.join (ThisDir, os.pardir, 'ext6', 'image1-%d.ppm'): 1, + os.path.join (ThisDir, os.pardir, 'ext7', 'image1-%d.ppm'): 1, + } + d = {} + execfile (os.path.join(ThisDir, os.pardir, 'sprmap.py'), d) + sprmap = d['sprmap'] + for key, (filename, rect) in sprmap.items (): + if filename.find('%d') >= 0: + InputFiles[os.path.join (ThisDir, filename)] = 1 + return InputFiles.keys () + +# ____________________________________________________________ + +def pixelmap (r, g, b): + r /= 255.0 + g /= 255.0 + b /= 255.0 + h, s, v = colorsys.rgb_to_hsv(r, g, b) + h = (h*sign + delta) % 1.0 + s *= sat + v *= lumen + r, g, b = colorsys.hsv_to_rgb(h, s, v) + return r*255.1, g*255.1, b*255.1 + +def specialpixelmap (r, g, b): + return r * 0.1, g * 0.7, r * 0.5 + +usingpalette = 0 + +def palettepixelmap (r, g, b): +# print max(r,g,b) + packed = chr(r) + chr(g) + chr(b) + try: + index = PaletteIndex[packed] + #print 'index %r' % index +# print 'USING', usingpalette + v = thispalette[index] #Palettes[(PALETTESIZE * (usingpalette + 1)) + index] +# print 'hit! %r' % packed +# print '-> %r' % (chr(v & 0xff) + chr ((v >> 8) & 0xff) + chr((v >> 16) & 0xff)) +# print '%r : %r' % (Palettes[index], Palettes[PALETTESIZE + index]) + return v & 0xff, (v >> 8) & 0xff, (v >> 16) & 0xff + except KeyError: + return r,g,b + +def ppmbreak (f): + sig = f.readline ().strip () + assert sig == "P6" + while 1: + line = f.readline ().strip () + if not line.startswith('#'): + break + wh = line.split () + w, h = map (int, wh) + sig = f.readline ().strip() + assert sig == "255" + data = f.read () + return w, h, data + +COLORMAPS = [{} for n in COLORS] +del n + +def paletterotate (imglist, chr=chr, int=int, ord=ord): + global thispalette + gw, gh, green = imglist[0] +# assert bw == gw and bh == gh + n = 0 + (_, _, fromimage) = imglist[0] + for reserved in COLORS: + # is not being entered, the fool. +# lut = {} +# for + thispalette = Palettes[(PALETTESIZE * (reserved + 1)):(PALETTESIZE * (reserved + 2))] + # wot is this? _ means unused? +# (_, _, otherimage) = imglist[1-n] + image = [] + colormap = COLORMAPS[reserved] + append = image.append + + for i in range (0, len(fromimage), 3): + rgb1 = fromimage[i:i+3] +# rgb2 = otherimage[i:i+3] +# if rgb1 == rgb2: +# append (rgb1) + if rgb1 in colormap: + append (colormap[rgb1]) + else: +# print 'HI!' + r, g, b = ord(rgb1[0]), ord(rgb1[1]), ord(rgb1[2]) +# print '%d,%d,%d ->' % (r,g,b) + r, g, b = palettepixelmap (r, g, b) +# print '%d,%d,%d.' % (r,g,b) + newrgb = chr (int (r))+chr (int (g))+chr (int (b)) + append (newrgb) + colormap[rgb1] = newrgb + imglist.append((gw, gh, ''.join (image))) + + +def rotate (imglist, chr=chr, int=int, ord=ord): + global delta, sat, sign, lumen + (bw, bh, blue), (gw, gh, green) = imglist + assert bw == gw and bh == gh + for reserved in range (len (COLORS)): + if len (COLORS[reserved]) == 2: + n, fn = COLORS[reserved] + fn = globals ()[fn] + else: + n, delta, sat, sign, lumen = COLORS[reserved] + fn = pixelmap + (_, _, fromimage) = imglist[n] + (_, _, otherimage) = imglist[1-n] + image = [] + colormap = COLORMAPS[reserved] + append = image.append + for i in range (0, len(fromimage), 3): + rgb1 = fromimage[i:i+3] + rgb2 = otherimage[i:i+3] + if rgb1 == rgb2: + append (rgb1) + elif rgb1 in colormap: + append (colormap[rgb1]) + else: + r, g, b = fn(ord(rgb1[0]), ord(rgb1[1]), ord(rgb1[2])) + newrgb = chr(int(r))+chr(int(g))+chr(int(b)) + append(newrgb) + colormap[rgb1] = newrgb + imglist.append((bw, bh, ''.join(image))) + +def writeout (imglist, namepattern, paletted = False): + start = 2 + if paletted: + start = 1 + for i in range (start, len (imglist)): + w, h, data = imglist[i] + fn = namepattern % i + f = open (fn, 'wb') + print >> f, 'P6' + print >> f, w, h + print >> f, 255 + f.write (data) + f.close () + + +def convert (name): + print >> sys.stderr, 'generating colors for %s...' % name + imglist = [ppmbreak (open (name % 0, 'rb'))] + paletted = False + if Palettes: + paletterotate (imglist) + paletted = True + else: + imglist.append(ppmbreak (open (name % 1, 'rb'))) + rotate (imglist) + writeout (imglist, name, paletted) + +def updatecheck (): + myself = os.path.join (ThisDir, 'buildcolors.py') + + def older (list1, list2): + def mtime (name): + try: + st = os.stat (name) + except OSError: + return None + else: + return st.st_mtime + list2 = [mtime (name) for name in list2] + if None in list2: + return 0 + else: + list1 = [mtime(name) for name in list1] + list1 = [t for t in list1 if t is not None] + return list1 and list2 and max (list1) < min (list2) + + rebuild = {} + for filename in inputfiles (): + distfiles = [myself, filename % 0] + genfiles = [filename % n for n in range (1, MAX)] + rebuild[filename] = not older (distfiles, genfiles) + return rebuild + + +#try to load palettes first +tmp = os.path.join (ThisDir, os.pardir, 'images', 'palettes.dat') +if os.path.exists (tmp): + #print 'loading palettes' + loadpalettesets (tmp) + initpalettelut () +else: + # from now on we should always use the palette approach; + # comment out the following line to restore the old color-rotation code. + raise IOError("cannot find the palette file %r" % (tmp,)) + + +if __name__ == '__auto__': # when execfile'd from images.py + rebuild = updatecheck ().items () + rebuild.sort () + for fn, r in rebuild: + if r: + convert(fn) + +#try: +# import psyco +# psyco.bind(rotate) +#except: +# pass + +if __name__ == '__main__': + if sys.argv[1:2] == ['-f']: + files = inputfiles () + elif sys.argv[1:2] == ['-c']: + for filename in inputfiles (): + for n in range (1, MAX): + try: + os.unlink (filename % n) + except OSError: + pass + else: + print 'rm', filename % n + sys.exit() + else: + rebuild = updatecheck () + if 0 in rebuild.values (): + print >> sys.stderr, ('%d images up-to-date. ' + 'Use -f to force a rebuild or -c to clean.' % + rebuild.values ().count(0)) + files = [fn for fn, r in rebuild.items () if r] + + files.sort () + for filename in files: + convert (filename) + diff --git a/bubbob/images/butterfly.ppm b/bubbob/images/butterfly.ppm Binary files differnew file mode 100644 index 0000000..a7af2c5 --- /dev/null +++ b/bubbob/images/butterfly.ppm diff --git a/bubbob/images/cream_pie_big.ppm b/bubbob/images/cream_pie_big.ppm Binary files differnew file mode 100644 index 0000000..827a2e0 --- /dev/null +++ b/bubbob/images/cream_pie_big.ppm diff --git a/bubbob/images/diamond_big_blue.ppm b/bubbob/images/diamond_big_blue.ppm Binary files differnew file mode 100644 index 0000000..78f7a8c --- /dev/null +++ b/bubbob/images/diamond_big_blue.ppm diff --git a/bubbob/images/diamond_big_purple.ppm b/bubbob/images/diamond_big_purple.ppm new file mode 100644 index 0000000..2b0938e --- /dev/null +++ b/bubbob/images/diamond_big_purple.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +90 90 +255 +ˆˆˆˆ˜!˜ $ $ §&§§&§¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯§&§§&§ $ $ ˜!˜ˆˆˆˆÀ/ÀÜ3Ìÿ9Ìÿ9ÌÿGÌÿGÌÿQÌÿQÌÿ[Ìÿ[Ìÿ[Ìÿ[ÌÿfÌÿ_Ìÿ_Ìÿ[ÌÿQÌÿGÌÿ9Ìÿ3Ìõ3Ìõ3Ìñ3Ìñ3Ìê3Ìç3Ìà3ÌÜ3ÌÕ3ÌÐ3ÌÎ3ÌÉ2ÉÄ0ÄÀ/À»-»¸,¸¶+¶´+´²*²¯)¯¯)¯¯)¯¯)¯¯)¯• •• •• •À/ÀÜ3Ìÿ9ÌÿXÌÿgÌÿmÏÿoÐÿoÐÿoÐÿpÑÿpÑÿrÒÿtÓÿtÓÿtÓÿtÓÿtÓÿrÒÿpÑÿkÎÿbÌÿQÌÿGÌÿ9Ìÿ6Ìÿ3Ìü3Ìú3Ìõ3Ìñ3Ìñ3Ìç3Ìà3ÌÜ3ÌÐ3ÌÉ2ÉÄ0ÄÀ/À»-»¶+¶´+´²*²¯)¯¯)¯¯)¯¯)¯¡$¡¡$¡• •• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿoÐÿoÐÿpÑÿpÑÿrÒÿrÒÿrÒÿrÒÿrÒÿrÒÿrÒÿpÑÿoÐÿkÎÿfÌÿXÌÿNÌÿ@Ìú3Ìú3Ìú3Ìõ3Ìó3Ìì3Ìç3Ìà3ÌÞ3ÌÜ3ÌÕ3ÌÎ3ÌÉ2ÉÃ0ø,¸¶+¶´+´²*²¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¡$¡¡$¡• •ŠŠ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿtÓÿzÖÿzÖÿzÖÿzÖÿzÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿNÌü3Ìñ3Ìñ3Ìñ3Ìê3ÌÜ3ÌÎ3ÌÎ3ÌÉ2ÉÄ0ÄÀ/ÀÀ/À¿.¿»-»»-»¶+¶¶+¶¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯§&§¡$¡• •ŠŠ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿØÿØÿØÿØÿØÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿJÌü3ÌÜ3ÌÜ3ÌÜ3ÌÎ3ÌÉ2ÉÄ0ÄÄ0ÄÀ/À¿.¿¿.¿»-»»-»¶+¶¶+¶¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯¯)¯ª'ª¡$¡• •ŠŠ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„ÛÿŠÞÿŒßÿ‘áÿ‘áÿŠÞÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿJÌü3Ìç3ÌÞ3ÌÞ3ÌÞ3ÌÜ3Ì×3ÌÕ3ÌÐ3ÌÎ3ÌÉ2ÉÄ0ÄÄ0ÄÄ0ÄÃ0ÃÀ/ÀÀ/À¿.¿¼-¼»-»¸,¸¶+¶¶+¶¶+¶´+´²*²¯)¯¯)¯¯)¯ª'ª¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ¥ëÿ²òÿ¥ëÿ˜åÿ‘áÿŠÞÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿ@Ìø3Ìñ3Ìñ3Ìñ3Ìñ3Ìì3Ìê3Ìç3Ìå3Ìà3ÌÞ3ÌÜ3ÌÜ3Ì×3ÌÐ3ÌÎ3ÌÎ3ÌÉ2ÉÇ1ÇÃ0ÿ.¿¼-¼¼-¼¼-¼¸,¸¶+¶´+´¯)¯ª'ª¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿ¿øÿÄûÿ¿øÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿJÌÿ3Ìø3Ìõ3Ìõ3Ìñ3Ìì3Ìê3Ìç3Ìå3Ìà3ÌÞ3ÌÜ3ÌÜ3Ì×3ÌÐ3ÌÎ3ÌÎ3ÌÌ3ÌÉ2ÉÇ1ÇÃ0ÿ.¿¼-¼¼-¼¸,¸¶+¶´+´¯)¯ª'ª¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿÌÿÿÕÿÿÕÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿJÌÿ@Ìÿ3Ìü3Ìø3Ìõ3Ìõ3Ìó3Ìñ3Ìê3Ìê3Ìç3Ìç3Ìç3Ìå3Ìà3ÌÞ3ÌÞ3Ì×3ÌÐ3ÌÇ1ÇÃ0ÿ.¿¼-¼¼-¼¸,¸¶+¶´+´¯)¯ª'ª¤%¤• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿàÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿGÌÿ9Ìÿ6Ìÿ3Ìÿ3Ìÿ3Ìü3Ìú3Ìõ3Ìõ3Ìõ3Ìõ3Ìõ3Ìó3Ìó3Ìó3Ìñ3Ìê3ÌÞ3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¤%¤• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿçÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿGÌÿ=Ìÿ9Ìÿ6Ìÿ3Ìÿ3Ìÿ3Ìü3Ìú3Ìõ3Ìõ3Ìõ3Ìõ3Ìó3Ìó3Ìñ3Ìñ3Ìñ3ÌÞ3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¤%¤• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿGÌÿ=Ìÿ=Ìÿ9Ìÿ9Ìÿ6Ìÿ3Ìü3Ìü3Ìú3Ìø3Ìø3Ìø3Ìõ3Ìó3Ìó3Ìñ3Ìñ3Ìå3Ì×3ÌÌ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¤%¤• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿGÌÿ@Ìÿ@Ìÿ=Ìÿ9Ìÿ9Ìÿ3Ìÿ3Ìü3Ìü3Ìú3Ìø3Ìø3Ìõ3Ìó3Ìó3Ìñ3Ìñ3Ìå3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿGÌÿ@Ìÿ=Ìÿ=Ìÿ9Ìÿ9Ìÿ3Ìÿ3Ìü3Ìü3Ìú3Ìø3Ìõ3Ìó3Ìó3Ìñ3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯«(«¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿGÌÿ@Ìÿ@Ìÿ=Ìÿ9Ìÿ9Ìÿ6Ìÿ3Ìÿ3Ìü3Ìú3Ìú3Ìõ3Ìõ3Ìó3Ìñ3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯«(«¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿGÌÿ@Ìÿ@Ìÿ=Ìÿ=Ìÿ9Ìÿ9Ìÿ6Ìÿ3Ìÿ3Ìü3Ìú3Ìø3Ìõ3Ìó3Ìñ3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯«(«¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿGÌÿ@Ìÿ@Ìÿ=Ìÿ=Ìÿ9Ìÿ9Ìÿ9Ìÿ6Ìÿ3Ìü3Ìú3Ìø3Ìõ3Ìó3Ìñ3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯«(«¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿNÌÿGÌÿ@Ìÿ@Ìÿ=Ìÿ=Ìÿ9Ìÿ9Ìÿ9Ìÿ3Ìÿ3Ìú3Ìø3Ìõ3Ìó3Ìó3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯«(«¡$¡• •ŽŽ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿNÌÿGÌÿDÌÿ@Ìÿ=Ìÿ=Ìÿ=Ìÿ=Ìÿ9Ìÿ6Ìÿ3Ìü3Ìú3Ìõ3Ìõ3Ìó3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¡$¡• •ŠŠ• •À/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿNÌÿGÌÿDÌÿ@Ìÿ@Ìÿ=Ìÿ9Ìÿ9Ìÿ9Ìÿ6Ìÿ3Ìÿ3Ìú3Ìõ3Ìõ3Ìó3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¡$¡• •ŠŠÀ/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿpÑÿmÏÿiÍÿfÌÿ_ÌÿXÌÿQÌÿNÌÿNÌÿJÌÿGÌÿGÌÿDÌÿDÌÿ@Ìÿ=Ìÿ9Ìÿ9Ìÿ6Ìÿ3Ìü3Ìú3Ìõ3Ìó3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯ª'ª¡$¡• •ŠŠÀ/ÀÜ3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿrÒÿoÐÿmÏÿiÍÿgÌÿfÌÿbÌÿ_Ìÿ[ÌÿXÌÿXÌÿTÌÿQÌÿJÌÿDÌÿ@Ìÿ=Ìÿ9Ìÿ9Ìÿ3Ìü3Ìú3Ìú3Ìó3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯§&§¡$¡• •Ü3Ìÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿrÒÿoÐÿmÏÿiÍÿgÌÿfÌÿbÌÿ_Ìÿ_Ìÿ[ÌÿXÌÿXÌÿTÌÿQÌÿNÌÿJÌÿDÌÿ=Ìÿ9Ìÿ3Ìü3Ìü3Ìú3Ìó3Ìñ3Ìç3Ì×3ÌÎ3ÌÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯¤%¤œ#œŒŒÿ9ÌÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿòÿÿúÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿòÿÿçÿÿàÿÿØÿÿÌÿÿ·ôÿ¥ëÿ˜åÿŒßÿ„ÛÿØÿ{ÖÿxÕÿtÓÿrÒÿoÐÿmÏÿkÎÿiÍÿgÌÿbÌÿbÌÿ_Ìÿ_Ìÿ[ÌÿXÌÿXÌÿTÌÿQÌÿQÌÿJÌÿ@Ìÿ9Ìÿ3Ìü3Ìü3Ìú3Ìõ3Ìñ3Ìç3Ì×3ÌÉ2ÉÇ1ÇÃ0ÿ.¿¼-¼¸,¸¶+¶´+´¯)¯¡$¡• •ÿXÌÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿçÿÿçÿÿçÿÿòÿÿòÿÿòÿÿòÿÿòÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿòÿÿòÿÿòÿÿòÿÿòÿÿòÿÿçÿÿçÿÿçÿÿçÿÿàÿÿÒÿÿÊþÿ·ôÿ°ñÿïÿ£êÿžèÿ˜åÿ“âÿŽàÿŠÞÿ†ÜÿƒÚÿØÿ}×ÿ{ÖÿzÖÿxÕÿxÕÿvÔÿvÔÿtÓÿrÒÿpÑÿpÑÿoÐÿkÎÿkÎÿgÌÿfÌÿ[ÌÿXÌÿQÌÿNÌÿJÌÿDÌÿ9Ìÿ6Ìø3Ìó3Ìñ3Ìç3Ìç3ÌÞ3ÌÐ3ÌÉ2ÉÀ/Àª'ªÿmÏÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿçÿÿçÿÿçÿÿòÿÿòÿÿòÿÿòÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿúÿÿòÿÿòÿÿçÿÿçÿÿàÿÿØÿÿØÿÿÒÿÿÐÿÿÍÿÿÄûÿ¿øÿ¿øÿ·ôÿ¶ôÿ²òÿ¥ëÿ¢êÿšæÿ‘áÿàÿŠÞÿ†Üÿ†Üÿ„ÛÿÙÿÙÿØÿ{Öÿ{ÖÿzÖÿvÔÿtÓÿtÓÿpÑÿoÐÿkÎÿgÌÿgÌÿfÌÿ_Ìÿ_Ìÿ_ÌÿTÌÿNÌÿNÌÿ@Ìÿ9Ìÿ9Ìø3Ìñ3ÌÉ2ÉÿtÓÿzÖÿ„Ûÿ˜åÿ²òÿÌÿÿØÿÿàÿÿàÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿçÿÿàÿÿØÿÿØÿÿÒÿÿÒÿÿÍÿÿÌÿÿÄûÿ¿øÿ½÷ÿ·ôÿ²òÿ°ñÿ¥ëÿšæÿ˜åÿ‘áÿŠÞÿŠÞÿ†Üÿ„ÛÿƒÚÿÙÿØÿØÿ{ÖÿzÖÿvÔÿvÔÿtÓÿtÓÿpÑÿkÎÿkÎÿgÌÿfÌÿfÌÿ_Ìÿ_Ìÿ[ÌÿTÌÿNÌÿJÌÿ@Ìÿ9Ìÿ3Ìñ3ÌÜ3ÌÉ2ÉÃ0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿ}×ÿ„ÛÿŠÞÿŽàÿàÿ•ãÿ˜åÿ¥ëÿ¥ëÿ²òÿ´óÿ½÷ÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÁùÿÁùÿ¹õÿ²òÿ«îÿ¥ëÿ¢êÿ‘áÿŒßÿƒÚÿÙÿ{ÖÿzÖÿxÕÿvÔÿvÔÿtÓÿtÓÿrÒÿrÒÿrÒÿpÑÿoÐÿoÐÿkÎÿiÍÿgÌÿ_Ìÿ_ÌÿXÌÿTÌÿQÌÿNÌÿQÌÿ_ÌÿbÌÿfÌÿfÌÿgÌÿiÍÿkÎÿiÍÿgÌÿgÌÿbÌÿXÌÿGÌÿ9ÌÃ0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿ}×ÿ„ÛÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿ«îÿ²òÿ¶ôÿÂúÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿ½÷ÿ·ôÿïÿ¢êÿ—äÿ‘áÿˆÝÿƒÚÿ}×ÿxÕÿrÒÿoÐÿkÎÿiÍÿgÌÿfÌÿXÌÿXÌÿTÌÿQÌÿNÌÿNÌÿJÌÿGÌÿ=Ìÿ6Ìü3Ìñ3Ìó3Ìü3Ìÿ3Ìÿ6Ìÿ=ÌÿQÌÿTÌÿ[ÌÿfÌÿiÍÿkÎÿoÐÿrÒÿvÔÿvÔÿvÔÿvÔÿpÑÿpÑÿiÍÃ0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿ}×ÿ„ÛÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿ«îÿ²òÿ·ôÿÂúÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿ½÷ÿïÿ¢êÿ•ãÿŽàÿ„ÛÿØÿzÖÿvÔÿoÐÿiÍÿ_ÌÿXÌÿQÌÿJÌÿ@Ìÿ9Ìü3Ìø3Ìõ3Ìñ3Ìñ3Ìì3Ìê3Ìç3Ìç3Ìå3Ìà3Ìå3Ìì3Ìó3Ìø3Ìÿ3ÌÿJÌÿTÌÿ[ÌÿiÍÿkÎÿpÑÿvÔÿvÔÿxÕÿxÕÿxÕÿxÕÿpÑÿtÓÿiÍÃ0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿØÿ„ÛÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿ«îÿ²òÿ·ôÿÂúÿÌÿÿÌÿÿÌÿÿÌÿÿ¹õÿ¢êÿ•ãÿŒßÿ„ÛÿØÿzÖÿtÓÿoÐÿgÌÿXÌÿJÌÿ@Ìÿ6Ìÿ3Ìø3Ìó3Ìê3Ìå3Ìà3ÌÞ3ÌÜ3Ì×3ÌÕ3ÌÕ3ÌÐ3ÌÐ3ÌÐ3ÌÜ3ÌÞ3Ìê3Ìì3Ìú3ÌÿGÌÿXÌÿgÌÿkÎÿpÑÿvÔÿxÕÿ{Öÿ}×ÿ}×ÿ{ÖÿxÕÿtÓÿpÑÿbÌÃ0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿ²òÿ·ôÿÂúÿÌÿÿÌÿÿÌÿÿ¶ôÿ˜åÿŽàÿ„ÛÿØÿzÖÿtÓÿmÏÿgÌÿTÌÿDÌÿ3Ìü3Ìú3Ìõ3Ìñ3Ìê3Ìå3ÌÞ3ÌÜ3Ì×3ÌÕ3ÌÐ3ÌÐ3ÌÎ3ÌÌ3ÌÌ3ÌÌ3ÌÎ3ÌÜ3ÌÞ3Ìê3Ìõ3ÌÿJÌÿfÌÿkÎÿpÑÿvÔÿ{Öÿ}×ÿØÿØÿ}×ÿ{ÖÿtÓÿpÑÿpÑÿ9ÌÃ0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿ²òÿ·ôÿÂúÿÌÿÿÌÿÿ²òÿ“âÿ„ÛÿØÿzÖÿtÓÿmÏÿgÌÿQÌÿ@Ìú3Ìõ3Ìñ3Ìì3Ìç3Ìà3ÌÞ3ÌÜ3ÌÕ3ÌÐ3ÌÐ3ÌÎ3ÌÌ3ÌÌ3ÌÉ2ÉÉ2ÉÉ2ÉÉ2ÉÌ3ÌÕ3ÌÞ3Ìê3Ìú3ÌÿQÌÿiÍÿpÑÿvÔÿ{Öÿ}×ÿØÿ„ÛÿØÿ}×ÿtÓÿpÑÿpÑÿ9Ì»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿ6Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿ²òÿ·ôÿÂúÿÌÿÿïÿŒßÿØÿzÖÿtÓÿmÏÿgÌÿQÌÿ=Ìõ3Ìê3Ìà3ÌÜ3Ì×3ÌÐ3ÌÌ3ÌÉ2ÉÉ2ÉÇ1ÇÇ1ÇÄ0ÄÄ0ÄÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÇ1ÇÎ3ÌÞ3Ìó3Ìÿ9Ìÿ_ÌÿkÎÿtÓÿ{Öÿ}×ÿŠÞÿŠÞÿ„ÛÿØÿzÖÿpÑÿpÑÿ9Ì»-»Ã0ÃÃ0ÃÿNÌÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿ²òÿ·ôÿÂúÿ§ìÿ„ÛÿzÖÿtÓÿmÏÿgÌÿQÌÿ=Ìó3Ìç3Ì×3ÌÕ3ÌÌ3ÌÇ1ÇÄ0ÄÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÄ0ÄÎ3Ìå3Ìü3ÌÿJÌÿgÌÿpÑÿvÔÿ}×ÿŠÞÿŠÞÿŠÞÿØÿzÖÿtÓÿpÑÿ9Ì»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿmÏÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿ²òÿ·ôÿšæÿØÿvÔÿoÐÿgÌÿQÌÿ=Ìó3Ìå3ÌÕ3ÌÉ2ÉÇ1ÇÃ0ÃÀ/ÀÀ/ÀÀ/ÀÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÉ2ÉÜ3Ìõ3Ìÿ@Ìÿ_ÌÿmÏÿvÔÿ{ÖÿŠÞÿŠÞÿŠÞÿ„Ûÿ{ÖÿtÓÿpÑÿXÌ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿgÌÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿ²òÿ‘áÿ{ÖÿrÒÿgÌÿQÌÿ=Ìó3Ìå3ÌÐ3ÌÃ0ü-¼¸,¸¸,¸¸,¸¸,¸»-»¿.¿Ã0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÄ0ÄÐ3Ìì3Ìÿ6Ìÿ[ÌÿkÎÿrÒÿzÖÿ„ÛÿŠÞÿŠÞÿ„Ûÿ{ÖÿtÓÿpÑÿXÌ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿNÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿïÿŽàÿxÕÿmÏÿXÌÿ=Ìó3Ìç3ÌÕ3ÌÃ0ø,¸´+´²*²¯)¯¯)¯²*²´+´¸,¸¿.¿Ã0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÄ0ÄÌ3Ìç3Ìú3ÌÿQÌÿiÍÿpÑÿxÕÿØÿ„Ûÿ„Ûÿ„Ûÿ{ÖÿtÓÿpÑÿXÌ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿ¥ëÿ„ÛÿtÓÿgÌÿJÌø3Ìç3ÌÕ3ÌÄ0Ä»-»´+´®)®«(«§&§§&§ª'ª«(«¯)¯¸,¸À/ÀÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÄ0ÄÉ2Éå3Ìõ3ÌÿGÌÿfÌÿoÐÿvÔÿØÿØÿ„Ûÿ„Ûÿ{ÖÿtÓÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿœçÿƒÚÿpÑÿ[Ìÿ3Ìç3Ì×3ÌÄ0ÄÀ/À¶+¶®)®ª'ª¦&¦¡$¡¡$¡¡$¡¦&¦«(«¸,¸À/ÀÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÉ2Éà3Ìñ3ÌÿDÌÿbÌÿoÐÿrÒÿ{ÖÿØÿØÿØÿ{ÖÿtÓÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿ•ãÿØÿmÏÿNÌì3Ì×3ÌÇ1ÇÀ/À¸,¸¯)¯§&§¤%¤ $ ## $ ¤%¤ª'ª¶+¶¿.¿Ã0ÃÃ0ÃÃ0ÃÃ0ÃÃ0ÃÉ2Éà3Ìñ3Ìÿ6Ìÿ_ÌÿoÐÿrÒÿ{Öÿ{ÖÿØÿØÿ{ÖÿtÓÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿàÿØÿiÍÿ=Ìå3ÌÉ2ÉÀ/À¸,¸¯)¯¦&¦¡$¡™"™˜!˜“ “• •#¡$¡ª'ª¶+¶¿.¿Ã0ÃÃ0ÃÃ0ÃÃ0ÃÉ2Éà3Ìñ3Ìÿ3Ìÿ_ÌÿmÏÿpÑÿzÖÿ{Öÿ{ÖÿØÿzÖÿtÓÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿŽàÿzÖÿ[Ìú3ÌÕ3ÌÀ/À¸,¸¯)¯¦&¦ $ ™"™• •’’ŽŽ˜!˜ $ §&§¶+¶¿.¿Ã0ÃÃ0ÃÃ0ÃÉ2ÉÜ3Ìó3Ìÿ6ÌÿXÌÿiÍÿpÑÿvÔÿzÖÿzÖÿzÖÿvÔÿtÓÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿŠÞÿzÖÿTÌì3ÌÉ2ɸ,¸¯)¯¦&¦ $ ™"™“ “ŒŒŒŒŽŽ“ “#¦&¦´+´¿.¿Ã0ÃÃ0ÃÃ0ÃÕ3Ìñ3Ìÿ6ÌÿXÌÿgÌÿmÏÿvÔÿvÔÿvÔÿvÔÿvÔÿtÓÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿ_ÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿ†ÜÿxÕÿGÌÞ3Ì¿.¿²*²¦&¦ $ ™"™’’ŽŽŠŠ‡‡‡‡ŽŽ“ “œ#œ¦&¦´+´¿.¿Ã0ÃÃ0ÃÉ2ÉÞ3Ìú3ÌÿQÌÿfÌÿmÏÿpÑÿvÔÿvÔÿvÔÿvÔÿrÒÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿbÌÿiÍÿoÐÿtÓÿxÕÿ}×ÿØÿvÔÿ=ÌÎ3̶+¶§&§ $ ™"™ŒŒ‡‡‡‡ŽŽ“ “™"™¦&¦²*²¿.¿Ã0ÃÃ0ÃÕ3Ìñ3Ìÿ9Ìÿ[ÌÿkÎÿpÑÿtÓÿvÔÿvÔÿvÔÿrÒÿpÑÿiÍ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿDÌÿQÌÿbÌÿiÍÿpÑÿtÓÿxÕÿ}×ÿpÑñ3ÌÃ0î)® $ ™"™ŒŒ‡‡‡‡ŒŒ“ “™"™¦&¦¯)¯¿.¿Ã0ÃÃ0Ãà3Ìõ3Ìÿ@ÌÿbÌÿoÐÿpÑÿtÓÿvÔÿtÓÿpÑÿiÍÿXÌ»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ9ÌÿGÌÿQÌÿbÌÿiÍÿpÑÿtÓÿxÕÿoÐê3̼-¼¦&¦™"™ŒŒ‡‡‡‡ŒŒ“ “™"™¦&¦¯)¯¿.¿Ã0ÃÉ2Éà3Ìú3ÌÿDÌÿfÌÿoÐÿrÒÿtÓÿrÒÿmÏÿbÌÿ9Ì»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=ÌÿGÌÿQÌÿbÌÿiÍÿpÑÿtÓÿkÎå3Ì´+´#’’ŒŒ‡‡‡‡ŒŒ“ “™"™¦&¦¯)¯¿.¿Ã0ÃÉ2Éê3Ìÿ3ÌÿDÌÿbÌÿoÐÿpÑÿoÐÿmÏÿbÌÿ9Ì»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=ÌÿGÌÿQÌÿbÌÿiÍÿpÑÿbÌ×3Ì®)®˜!˜ŽŽŒŒ‡‡ŒŒ“ “™"™¦&¦¯)¯¿.¿Ã0ÃÐ3Ìê3Ìÿ9ÌÿGÌÿbÌÿmÏÿmÏÿiÍÿbÌÿ9Ì»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=ÌÿGÌÿTÌÿbÌÿiÍÿQÌÕ3̪'ª“ “ŒŒŠŠ‡‡ŒŒ“ “™"™¦&¦²*²¿.¿É2É×3Ìõ3Ìÿ9ÌÿJÌÿ_ÌÿiÍÿ_ÌÿQÌÿ9Ì»-»Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=ÌÿGÌÿTÌÿbÌÿDÌÐ3̦&¦’’ŒŒˆˆ‡‡ŒŒ“ “™"™¦&¦²*²¿.¿Î3ÌÞ3Ìõ3Ìÿ=ÌÿTÌÿ[ÌÿXÌÿ9Ìì3̧&§Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=ÌÿGÌÿTÌÿ6ÌÌ3̤%¤’’ŒŒ‡‡‡‡ŒŒ“ “™"™¦&¦²*²Ã0ÃÐ3Ìç3Ìø3Ìÿ=ÌÿQÌÿQÌÿ3ÌÕ3̧&§Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=ÌÿGÌø3ÌÉ2ɤ%¤ŒŒŒŒ‡‡‡‡ŒŒ“ “™"™ª'ª¶+¶É2É×3Ìç3Ìø3Ìÿ9Ìÿ=Ìü3ÌÀ/À§&§Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìÿ=Ìñ3ÌÇ1Çœ#œŒŒŒŒ‡‡‡‡ŽŽ“ “™"™ª'ª¶+¶É2ÉÞ3Ìì3Ìú3Ìÿ6Ìø3ÌÀ/À§&§Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìÿ3Ìê3ÌÃ0Ù"™ŒŒŒŒ‡‡‡‡ŽŽ“ “™"™«(«»-»Ð3Ìç3Ìì3Ìó3Ìì3ÌÀ/À§&§Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìú3Ìå3ÌÀ/À™"™ŒŒŒŒ‡‡‡‡ŒŒ“ “™"™«(«»-»Þ3Ìì3Ìì3ÌÞ3̯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3Ìõ3Ìà3Ì¿.¿™"™ŒŒŒŒ‡‡‡‡ŒŒ“ “ $ ¯)¯¿.¿ì3Ìì3Ìì3̯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìó3ÌÞ3̼-¼˜!˜ŒŒŒŒˆˆ‡‡ˆˆŒŒ™"™¦&¦¯)¯Ä0Äì3Ìì3̯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3ÌÜ3Ì»-»˜!˜ŒŒŒŒŠŠˆˆ‡‡‡‡ŠŠ“ “ $ ¯)¯¸,¸Î3Ìì3̯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3Ìñ3Ì×3̸,¸˜!˜ŒŒŒŒŠŠŠŠˆˆ‡‡‡‡‡‡ŠŠŒŒ™"™¤%¤¸,¸Î3ÌÞ3̯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3Ìñ3ÌÕ3̶+¶• •ŒŒŒŒŠŠŠŠŠŠˆˆ‡‡ŠŠŒŒ“ “¤%¤«(«¸,¸Î3̯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3Ìñ3ÌÕ3Ì´+´• •ŒŒŒŒŒŒŠŠŠŠŠŠŒŒŒŒ“ “¤%¤«(«¯)¯Ä0į)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3Ìñ3ÌÐ3̲*²• •ŒŒŒŒŒŒŒŒŒŒŒŒŒŒ#¯)¯¯)¯¸,¸¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3Ìñ3ÌÐ3̲*²• •ŒŒŒŒŒŒŒŒŒŒŒŒ#¯)¯¯)¯¸,¸¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌñ3ÌÐ3̲*²• •ŒŒŒŒŒŒŒŒŒŒ#¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍÿNÌÐ3̯)¯• •ŒŒŒŒŒŒŒŒ• •¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿiÍê3̯)¯• •ŒŒŒŒŒŒ• •¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿiÍÿ6̯)¯• •ŒŒŒŒ• •¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÿX̯)¯• •¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿiÍÕ3Ì• •¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿiÍÿ6̯)¯¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÿX̯)¯¯)¯¯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿiÍÿiÍÕ3̯)¯¯)¯¯)¯˜!˜Ã0ÃÃ0Ãì3ÌÿNÌÿiͯ)¯¯)¯˜!˜Ã0ÃÃ0ÃÃ0ÃÃ0ï)¯˜!˜Ã0ÃÃ0ÃÃ0Ø!˜Ã0Ã
\ No newline at end of file diff --git a/bubbob/images/diamond_big_red.ppm b/bubbob/images/diamond_big_red.ppm Binary files differnew file mode 100644 index 0000000..f612f53 --- /dev/null +++ b/bubbob/images/diamond_big_red.ppm diff --git a/bubbob/images/diamond_big_yellow.ppm b/bubbob/images/diamond_big_yellow.ppm Binary files differnew file mode 100644 index 0000000..7ace2ba --- /dev/null +++ b/bubbob/images/diamond_big_yellow.ppm diff --git a/bubbob/images/digits_0.ppm b/bubbob/images/digits_0.ppm Binary files differnew file mode 100644 index 0000000..54cc4e0 --- /dev/null +++ b/bubbob/images/digits_0.ppm diff --git a/bubbob/images/door.ppm b/bubbob/images/door.ppm Binary files differnew file mode 100644 index 0000000..dbc8b69 --- /dev/null +++ b/bubbob/images/door.ppm diff --git a/bubbob/images/dragon_0.ppm b/bubbob/images/dragon_0.ppm Binary files differnew file mode 100644 index 0000000..14272ec --- /dev/null +++ b/bubbob/images/dragon_0.ppm diff --git a/bubbob/images/dragon_bubble_0.ppm b/bubbob/images/dragon_bubble_0.ppm Binary files differnew file mode 100644 index 0000000..7457ffe --- /dev/null +++ b/bubbob/images/dragon_bubble_0.ppm diff --git a/bubbob/images/extend.ppm b/bubbob/images/extend.ppm Binary files differnew file mode 100644 index 0000000..bed1ae8 --- /dev/null +++ b/bubbob/images/extend.ppm diff --git a/bubbob/images/extra1.ppm b/bubbob/images/extra1.ppm Binary files differnew file mode 100644 index 0000000..bf57813 --- /dev/null +++ b/bubbob/images/extra1.ppm diff --git a/bubbob/images/extra2.ppm b/bubbob/images/extra2.ppm Binary files differnew file mode 100644 index 0000000..0aa664d --- /dev/null +++ b/bubbob/images/extra2.ppm diff --git a/bubbob/images/extra3.ppm b/bubbob/images/extra3.ppm Binary files differnew file mode 100644 index 0000000..42d3030 --- /dev/null +++ b/bubbob/images/extra3.ppm diff --git a/bubbob/images/extra4.ppm b/bubbob/images/extra4.ppm Binary files differnew file mode 100644 index 0000000..c2b1066 --- /dev/null +++ b/bubbob/images/extra4.ppm diff --git a/bubbob/images/extra5.ppm b/bubbob/images/extra5.ppm new file mode 100644 index 0000000..d162d9e --- /dev/null +++ b/bubbob/images/extra5.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.1 +32 576 +255 +```fffeeeEXo?QhRRRRRRRRRkkk‘‘‘ŸŸŸ¨¨¨¤¨¬Yˆ¸V…µ–šž•••†††lllWWW†††¦¦¦¸¸¸¿¿¿¿¿¿§ºa‘ÀZ‹½™¯§§§žžžwwwZZZ~~~···ÐÐÐÚÚÚÙÙÙÎÏÏw Åe”Â^Ž¾cŽº¬ªªª rrrZZZuuu¾¾¾çççðððíííååå¿ÉÏm›Æd“Á`À]¾˜¤¯¬¬¬¦¦¦žžžŒŒŒtttZZZeee³³³éééùùùúúúòòòãããš´Çm›Æg–Ãa‘À_¿|šµ®®®ªªª¥¥¥šššˆˆˆqqqYYYWWW›››ÕÕÕñññûûûùùùíííÛÜÜ~§ÊnœÆi—Äd“Á`Àc¼®¯¯¬¬¬§§§ŸŸŸ’’’………lllVVVˆˆˆÈÈÈêêêñññôôôñññæææÃÌÑv¢ÊnœÆi—Äe”Â`À]¾›¦°¨¨¨¡¡¡———ŒŒŒ{{{___‚‚‚¾¾¾èèèøøøôôôíííçççÞÞÞ¢¹Ês Ém›Æi—Äe”Âa‘À]¾€š³¨¨¨¢¢¢™™™‚‚‚lllVVVŸŸŸÛÛÛ÷÷÷ýýýôôôéééâââÙÙÙ§ÈpÇk™Åg–Ãd“Á`À]¾eº¨¨¨¢¢¢ššš………uuu\\\{{{³³³âââúúúúúúîîîâââÛÛÛÁÉÍs Ém›Æi—Äf•Âc’Á_¿\Œ½YŠ¼›¤«§§§¡¡¡ššš†††|||fffTTT7^a‘À†°ÒšÁÜ—¾Ú‹´Ô¬Ðz¦Ìv¢ÊpÇlšÅg–Ãe”Âa‘À^Ž¾Z‹½X‰¼V‡¹R‚´O}°Ly«Fr¤@kœ;c•3YŠ!Br;_Še”ƒГ»Ø¸×…¯Ñ{§Íw£ËqžÈm›Æi—Äe”Âc’Á`À\Œ½YŠ¼WˆºT„¶Q³O}°Ly«Gs¥Al;d–5\)Gq`kxf”Á~©ÎŠ³Ô¬Ðz¦Ìw£ËqžÈm›Æj˜Äg–Ãd“Áa‘À^Ž¾Z‹½X‰¼V‡¹T„¶Q€²N|¯KxªGs¥Al<e—:a’IS_nnn©³{¥Ê€«Ïw£Ët¡ÉqžÈm›Æj˜Äg–Ãe”Âa‘À_¿\Œ½Z‹½WˆºV†¸SƒµP±Mz¬Jw©Fr¤Al@i˜gt‚YYYlll¯¯¯½ÂƆªÈrŸÈpÇlšÅi—Äg–Ãe”Âc’Á`À]¾Z‹½X‰¼WˆºT„¶R‚´O}°Ly«Iu§Eq£Kpšzƒ‹___iii§§§ÁÁÁÅÆÆ’ª¾i—Ãg–Ãe”Âd“Áa‘À`À]¾Z‹½X‰¼WˆºV†¸SƒµP±Mz¬Iv¨Fr£b|˜…‡ˆŒŒŒ‚‚‚```fffžžž´´´»»»ººº§±¹o—¾a‘À_¿^Ž¾\Œ½YŠ¼WˆºWˆºV†¸SƒµQ€²Mz¬Jw©Px£y…‘………ŠŠŠ~~~YYYbbb•••¨¨¨¯¯¯²²²´´´›©´^Ž¾\Œ½Z‹½X‰¼WˆºV†¸T„¶SƒµQ³O}°Ly«Ht¦t„”ˆˆˆˆˆˆ•••”””wwwTTT___‰‰‰¡¡¡§§§«««®®®t•¶Z‹½YŠ¼WˆºV‡¹T„¶SƒµR‚´Q³O}°Mz¬Iv¨Eq£St˜ŠŠŠ——— ’’’oooTTTxxx™™™¡¡¡¥¥¥œ¢¨W‡¹WˆºV†¸T„¶R‚´Q€²P±O}°O}°Mz¬Jw©Gs¥Cn CnŸ‹‘˜¦¦¦¢¢¢†††___kkkŒŒŒšššžžžsŽ©Q³R‚´Q³Q€²O}°N|¯N{N{Ly«Iv¨Fr¤Cn Cn Iv¨w‘«žžžwwwVVVuuuŽŽŽ•––S}«Mz¬N|¯N|¯N|¯Mz¬Q|«‚Žš™Lv¥Eq£BmžAlHt¦Q³_‹¸¥¦¦ŒŒŒccc\\\zzzy†”Ht¦Iu§Iv¨Iv¨Jw©T|§ˆ–”””‚‰‘LqœBmžGs¥O}°V†¸R‚´|‰–rrrVVV___Zo‡BmžCn Do¡Dp¢XzžŠŽŽŽŠŠŠ‰‰‰‡ŠZ| Mz¬R‚´Q³Ht¦VkƒVVV@Vr7^<e—?i™bx‘‰ŠŠŠŠŠ‰‰‰‰‰‰ŠŠŠŒŒŒ’’’™ššw¦P}¯Fr¤9a“<Qm2PvJd‚qw~‚‚‚‰‰‰‰‰‰‰‰‰ŠŠŠŽŽŽ‘‘‘•••———–––}ƒŠJd‚0Ms]]]llluuu~~~………ˆˆˆ‰‰‰ŒŒŒŠŠŠ‰‰‰………{{{iiiTTTTTT]]]ooo{{{{{{ttteeeZZZRRRRRRRRRRRRRRRRRRRRRRRRDDDRRRRRRRRR8G\?L]RRRRRRRRRbbb~~~›››§§§Š®X‰¼Xˆº›§›››‘‘‘uuuYYYbbb‚‚‚§§§¼¼¼ººº·¹ºg“¾_¿]¾k’·®®®ªªªŸŸŸŒŒŒcccRRRRRRžžžÔÔÔÞÞÞÎÎÎËËË«¹Ãg–Ãc’Á`À]¾›¦°®®®ªªª¡¡¡‰‰‰tttRRRRRR§§§äääøøøøøøæææØØØŽ¬ÅlšÅf•Âa‘À_¿€›µ°°°«««¦¦¦¡¡¡ŒŒŒxxxRRR]]]šššéééüüüÿÿÿüüüïïïÖØØy¤ÊpÇi—Äe”Âa‘Àd‘½°±±®®®ªªª§§§šššŠŠŠRRRQQQŽŽŽÔÔÔæææøøøþþþöööçççÀÊÑw£ËpÇk™Åe”Âa‘À^Ž¾œ§±¯¯¯¬¬¬¨¨¨žžž”””ŠŠŠuuuRRR†††ÃÃÃðððøøøîîîðððèèèßßߢ¹Êt¡ÉpÇj˜Äf•Âa‘À_¿‚œ´¯¯¯¬¬¬¨¨¨¡¡¡———ŒŒŒ‚‚‚ffffffÉÉÉîîîÿÿÿÿÿÿîîîêêêáááÛÛÛ„©ÈrŸÈm›Æi—Äf•Âa‘À_¿fº¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽ†††uuuRRRŸŸŸÑÑÑùùùÿÿÿùùùèèèãããÜÜÜËÎÐs ÈpÇk™Åg–Ãe”Âa‘À^Ž¾Z‹½Ÿ¨¯¬¬¬§§§¡¡¡™™™ŽŽŽˆˆˆfff:J`v˜¸ƒ©É¡ÄÝ ÄÞ“»Ø‡±Ó¬Ð{§Ív¢ÊqžÈlšÅi—Äf•Âd“Á`À]¾Z‹½X‰¼V‡¹R‚´O}°KxªFr£CmœCh“Db‡:Ja5Ide”Âw£Ë—¾ÚšÁÜŒµÕ¬Ð{§Ít¡ÉrŸÈnœÆj˜Äg–Ãe”Âa‘À_¿\Œ½YŠ¼WˆºT„¶Q³O}°Ly«Fr¤Al;d–9a“4IeRRR¥ºs É‹´Ô„®Ñ~©Îz¦Ìw£ËqžÈnœÆk™Åi—Äe”Âc’Á`À]¾Z‹½WˆºV‡¹T„¶Q€²N|¯Ly«Fr¤Bmž;d–YsQRRRRR···¥µÁz¥Êy¥Ìw£Ët¡ÉpÇm›Æk™Åi—Äf•Âd“Áa‘À^Ž¾\Œ½YŠ¼WˆºV†¸SƒµP±Mz¬KxªFr¤EoŸcuˆ………RRRRRR±±±ÀÀÀºÀÅ„¨ÆrŸÈpÇlšÅi—Äg–Ãe”Âd“Áa‘À_¿]¾Z‹½WˆºV‡¹T„¶Q³N|¯Ly«Iv¨PuŸ|…‰‰‰‰‰‰```RRR¦¦¦¹¹¹¿¿¿ÀÁÁ•¬¿i—Ãg–Ãe”Âd“Áa‘À`À^Ž¾\Œ½Z‹½X‰¼WˆºT„¶R‚´O}°Ly«Iv¨h‚Š‹Œ………‘‘‘ŠŠŠ___RRRššš±±±···¹¹¹»»»§±¸o—¾a‘À`À^Ž¾\Œ½Z‹½YŠ¼WˆºV‡¹T„¶R‚´O}°Mz¬R{§~Š–•••ƒƒƒRRRRRR‘‘‘¥¥¥¬¬¬°°°²²²´´´œªµ^Ž¾\Œ½Z‹½X‰¼WˆºV‡¹V†¸T„¶SƒµP±Mz¬Jw©v‡˜ŽŽŽŸŸŸšššxxxRRR ¦¦¦¨¨¨¬¬¬®®®t•¶\Œ½X‰¼WˆºV†¸T„¶SƒµSƒµR‚´P±N|¯KxªHt¦Vxœ‚‚‚”””¤¤¤¨¨¨”””lllZZZ–––ŸŸŸ¡¡¡¥¥¥›¡§V†¸V‡¹T„¶T„¶Q³Q€²P±O}°O}°Mz¬KxªIv¨Dp¢Al†”¥¥¥±±±¤¤¤RRR–––›››žžžr¨P±Q€²Q€²P±O}°N|¯N{Mz«KxªIv¨Gs¥Al?išIu§x’«±±±±±±———lllRRR”””•––R|ªLy«Ly«Mz¬Mz¬Mz¬Q|«‚Žš€Œ˜Lv¥Eq£AlAlGs¥R‚´cŽº¬ššš{{{RRRRRR‚‚‚z‡”Eq£Fr¤Fr¤Iu§Iu§Rz¤…Œ“’’’ŽŽŽ~…Kp›Cn Iu§Q³WˆºV†¸~‹˜ƒƒƒRRRRRR]rŠ?iš?iš@kœ@kœVw›†‰ŒŠŠŠŠŠŠ‰‰‰‰‰‰‹Ž‘]£P±T„¶Q€²Iv¨awRRR5Id3Z‹;d–<d•e|”‘‘ŠŠŠ‰‰‰ŠŠŠ’’’ššš›œœw¦Mz«Cn 4[Œ3Hd)DkC[zhnuƒƒƒŒŒŒŒŒŒŽŽŽ‘‘‘‘‘‘‰‰‰u{‚2Hf)DkRRR```nnn{{{ƒƒƒrrriiiRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR%Cm Aq Aq%CmRRRRRRrrr–––¥¥¥¤©X‰¼WˆºWˆºR‚´—•••………ooo\\\uuu®®®ÉÉÉÎÎÎÐÐЧ¸ÅlšÅg–Ãd“Áa‘ÀŽ¡²®®®¦¦¦”””|||cccWWWiii¤¤¤ÓÓÓëëëîîîìììææ昵ÌrŸÈk™Åg–Ãe”Â{œº´´´°°°¦¦¦———………nnnZZZnnn°°°âââôôôúúúöööñññßàà{¦ËnœÆi—Äe”Â`À^¼ª¬¬¬¬¨¨¨¤¤¤™™™ˆˆˆttt___oooªªªÚÚÚòòòûûûûûûôôôêêêÅÎÓt¡ÉnœÆj˜Äe”Âa‘À]¾š¦±®®®«««§§§ •••ŠŠŠxxx___nnn¤¤¤ÙÙÙíííñññôôôòòòêêêßßߤºÊs ÉnœÆi—Äe”Âa‘À^Ž¾ƒœ³¯¯¯¬¬¬¨¨¨¢¢¢ššš………rrrZZZnnn¯¯¯ÙÙÙóóóúúúôôôíííèèèàààØØØ„©ÈqžÈlšÅi—Äe”Âa‘À^Ž¾j’¹¯¯¯¬¬¬¨¨¨¤¤¤œœœ’’’‰‰‰~~~kkkTTTŠŠŠÇÇÇíííûûûûûûñññçççáááÚÚÚÈÌÏrŸÈnœÆj˜Äg–Ãe”Â`À]¾Z‹½¤©¬¬¬§§§¢¢¢œœœ”””ŒŒŒƒƒƒuuu______‚–ªŽÇ§ÇÜŸÃÜšÁܶՅ¯ÑªÏy¥Ìs ÉpÇlšÅg–Ãe”Âc’Á_¿\Œ½Z‹½WˆºV‡¹SƒµP±Mz¬Iu§FpŸFl—IiObxWWW___}—°y¥Ì˜¿Û•½Ú“»Ø†°Ò~©Îy¥Ìs ÉpÇlšÅi—Äf•Âd“Áa‘À^Ž¾Z‹½YŠ¼WˆºT„¶R‚´O}°Mz¬Iu§Do¡?iš;c•MdWWW___¬¬¬ÂÑ”¼Ù“»Ø¬Ð}¨Íw£Ës ÉpÇlšÅi—Äg–Ãd“Áa‘À_¿\Œ½YŠ¼WˆºV†¸T„¶Q€²O}°Ly«Iu§Dp¢@j›WpŒvwwbbb]]]¦¦¦ÙÙÙË×݈±Òw£Ës ÉqžÈnœÆk™Åi—Äf•Âe”Âa‘À_¿]¾Z‹½X‰¼V‡¹T„¶R‚´P±Mz¬KxªHt¦Ep h{‚‚‚zzzbbb]]] ÐÐÐßßßÄÉÍ¥ÅnœÆk™Åi—Äg–Ãe”Âd“Áa‘À_¿]¾Z‹½X‰¼WˆºV†¸SƒµQ€²N|¯Ly«Iv¨Rw¡|…ˆˆˆ‰‰‰bbbZZZ•••ÁÁÁÓÓÓÄÄĽ¾¾’©½f•Âe”Âc’Áa‘À_¿]¾Z‹½Z‹½X‰¼WˆºV†¸T„¶Q€²O}°Ly«Iu§gœŠ‹Œ†††ŠŠŠ~~~bbbWWWŒŒŒ²²²Â³³³¶¶¶¸¸¸¤®¶l•½^Ž¾]¾Z‹½YŠ¼WˆºWˆºV‡¹T„¶SƒµQ€²O}°Ly«Rz¥}‰•ŒŒŒ………‰‰‰”””xxxYYY___ƒƒƒ¤¤¤®®®«««°°°±±±–¥±Z‹½YŠ¼WˆºV‡¹T„¶T„¶T„¶R‚´Q€²O}°Mz¬Iv¨v‡˜ŒŒŒˆˆˆ™™™žžžŽŽŽrrrYYYkkk”””¡¡¡¢¢¢¥¥¥¨¨¨«««p’³WˆºV†¸T„¶R‚´Q€²Q€²P±O}°N|¯Ly«Iv¨Fr¤UvšŠŠŠ‘‘‘ §§§œœœbbbYYY‚‚‚’’’›››žžž¡¡¡–œ¢Q²Q³Q³P±O}°N|¯Mz¬Mz¬Ly«Jw©Ht¦Eq£BmžCnŸˆ–¢¢¢¬¬¬¨¨¨‘‘‘tttTTT```|||–––šššn‰¥Mz¬Mz¬Mz¬Mz¬Ly«KxªKw¨Jv§Ht¦Eq£Cn AlCn Iv¨uª¬¬¬§§§———zzz\\\]]]wwwŠŠŠ‘Jt¢Fr¤Gs¥Iu§Iu§Ht¦Ku¤}‰–z†“GpžBmžBmžDp¢Jw©Q³Y‡¶ž ¡”””zzz]]]ZZZrrrewŠ?iš@j›@kœBmžDo¡Lr~…ŠŠŠ‰‰‰}„ŒMtžGs¥KxªO}°Q€²Mz¬qƒ–uuu\\\WWW>[~5\9a“<e—@kœVwšƒ†‰‰‰‰ŠŠŠŒŒŒŽŽŽŽ‘”^€¤Ly«Ly«Eq£;d–>[~YYY,Fk&Iz.S„9]‰_q…‚‚‚†††‰‰‰ŠŠŠŒŒŒŒŒŒŒŒŒŠŠŠhzŽ@f”.S„#Ev+EjDO\VXYeeettt~~~~~~wwwooo\\\UVVBMZRRRRRR___ccc___YYYRRRRRR```fffeeebbbYYYRRRRRRRRRkkk‘‘‘ŸŸŸ¨¨¨¬¬¬“r«qžžž•••†††lllWWW†††¦¦¦¸¸¸¿¿¿¿¿¿º¶®½„H¸|E¨ §§§žžžwwwZZZ~~~···ÐÐÐÚÚÚÙÙÙÐÐЫ„¿…G»D²–q®®®ªªª rrrZZZuuu¾¾¾çççðððíííåååÖÖÔÄ’S¿„F¼E¹ƒK¯®¬¬¬¦¦¦žžžŒŒŒtttZZZeee³³³éééùùùúúúòòòãããÊÁ«ÅJÁˆH½‚E¼€D²£®®®ªªª¥¥¥šššˆˆˆqqqYYYWWW›››ÕÕÕñññûûûùùùíííÝÝÝÈ}ÆŽK‰H¿„F¼Eµ’g°°°¬¬¬§§§ŸŸŸ’’’………lllVVVˆˆˆÈÈÈêêêñññôôôñññæææØØÖÊšUÆŽK‰H¿…G¼E¹I¯®¨¨¨¡¡¡———ŒŒŒ{{{___‚‚‚¾¾¾èèèøøøôôôíííçççÞÞÞÎƲɓMÅJ‰H¿…G½‚Eº~C°£¨¨¨¢¢¢™™™‚‚‚lllVVVŸŸŸÛÛÛ÷÷÷ýýýôôôéééâââÙÙÙȯƒÆKËIÁˆH¿„F¼Eº~C²’k¨¨¨¢¢¢ššš………uuu\\\{{{³³³âââúúúúúúîîîâââÛÛÛÓÓÓÈšYÅJ‰HÀ†G¾ƒF¼€D¹|C¶L¬¬¬§§§¡¡¡ššš†††|||fffTTTxxx¹¹¹âââøøøõõõçççÜÜÜÕÕÕÊĵÆKÄŒJÁˆH¿…G½‚E»D¹{B·yA« ‘¥¥¥ ššš‘‘‘ˆˆˆ~~~qqqTTT}`I¿ŠOÒ¥]Ù¸tØ·uÒ¬jÍ¢^ÊšUÇ’MÅJ‰H¿…G¾ƒF¼E¹|C¸zB¶xA³s?±p=m<¨i:Ÿd7•^3‰X0|Q,Z< yV9¿„FÐQÕ¨\Ò TÍšPË–NÇ‘LÅJÊIÁˆH¿„F½‚E»D¹{B·yAµw@³s?°o=¬l;¦i9Ÿd7•^3‹Y0‚T.\@&rf\»GË–NÑŸSË–NÊ”MÇ‘LÅJÊIÁˆH¿…G½‚E¼€D¹|C¹{B¶xAµv@²r>¯n<©j:¥h9žc6•^3Y1…V/[QHlll¯¡‹È”QË–NÈ’LÆKÄŒJ‰HÁˆH¿…G¾ƒF¼Eº~C¹{B·yA¶xA³s?²q>m<¨i:¢f8b6•^3•_6‡ug___iii§§§¾¸¬Å™\ÊI‰HÁˆH¿…G¿„F½‚E¼Eº~C¹{B·yA¶xAµv@²r>¯n<©j:¤g8žc6˜_4”c>Ž„z‚‚‚```fffžžž´´´»¹·¹œs¾„G¾ƒF½‚E¼€D»D¹|C¸zB¶xA¶xAµv@²r>°o=©j:¥h9Ÿd7™`5mRŠˆ†~~~YYYbbb•••¨¨¨¯¯¯²²²³§•º‡P»D¹|C¹{B·yA¶xAµv@³s?²r>±p=m<¨i:¡e7œg?}pˆˆˆ•••”””wwwTTT___‰‰‰¡¡¡§§§«««®®®° ‡¹{B¸zB¶xAµw@³s?²r>²q>±p=m<©j:¤g8b6ziŠŠŠ——— ’’’oooTTTxxx™™™¡¡¡¥¥¥¨¨¨±†Z¶xAµv@³s?²q>°o=¯n<m<m<©j:¥h9Ÿd7˜_4—jH———¦¦¦¢¢¢†††___kkkŒŒŒšššžžž¢™°p>²q>±p=°o=m<¬l;©j:©j:¨i:¤g8žc6˜_4˜_4£g9¥ž“žžžwwwVVVuuuŽŽŽ———¢€`©j:¬l;¬l;¬l;©j:¨i:¥i:¤h:¡e7b6—_4•^3¡e7±p=¯j¦¦¦ŒŒŒccc\\\zzz‹‰¡g<¢f8¤g8¤g8¥h9¥h9¢h<—‡x”„u›d:—_4—_4Ÿd7m<µv@°tB‘rrrVVV___ƒkX—_4˜_4š`5›a5b6h@‡~ŠŠŠ‹‚z˜e>Ÿd7©j:²q>±p=¡e7hVVVVhJ0S-‹Y0ŽZ1’\2—iFŠ†‚‰‰‰‰‰‰ŠŠŠŒŒŒ’Ž‰¦vOm<¬l;žc6„U.aF,eE(yO+„U.ŒgL‰‡†‰‰‰‰‰‰ŠŠŠŽŽŽ‘‘‘•••–•”ŸxX”]3yO+`B'bF-r\Juuu~~~………ˆˆˆ‰‰‰ŒŒŒŠŠŠ‰‰‰………{{{nYHW?(TTT]]]ooo{{{{{{ttteeeZZZRRRRRRRRRRRRRRRRRRRRRRRRDDDRRRRRRRRRRB4VF7RRRRRRRRRbbb~~~›››§§§¨ ·{DµyC¦¡™›››‘‘‘uuuYYYbbb‚‚‚§§§¼¼¼ººº»»»·œv¼€Dº~C±•p®®®ªªªŸŸŸŒŒŒcccRRRRRRžžžÔÔÔÞÞÞÎÎÎËËËÇÆÅÀP¾ƒF¼E¹ƒK°¯®®®®ªªª¡¡¡‰‰‰tttRRRRRR§§§äääøøøøøøæææØØØȾ¨ÄŒJÀ†G½‚E¼€D²¤Ž°°°«««¦¦¦¡¡¡ŒŒŒxxxRRR]]]šššéééüüüÿÿÿüüüïïïÛÛÛÈ}ÆK‰H¿…G½‚E¶“g²²²®®®ªªª§§§šššŠŠŠRRRQQQŽŽŽÔÔÔæææøøøþþþöööçççÙÙ×Ë›VÆKËI¿…G½‚EºƒJ±°¯¯¯¯¬¬¬¨¨¨žžž”””ŠŠŠuuuRRR†††ÃÃÃðððøøøîîîðððèèèßßßÎƲʔMÆKÊIÀ†G½‚E¼€D±¤¯¯¯¬¬¬¨¨¨¡¡¡———ŒŒŒ‚‚‚ffffffÉÉÉîîîÿÿÿÿÿÿîîîêêêáááÛÛÛȯƒÈ’LÅJ‰HÀ†G½‚E¼€D³“k¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽ†††uuuRRRŸŸŸÑÑÑùùùÿÿÿùùùèèèãããÜÜÜÓÓÓÈšYÆKËIÁˆH¿…G½‚E»D·‚L¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽˆˆˆfffRRR´´´ÑÑÑúúúüüüðððãããÜÜÜÖÖÖÊĵǑLÄŒJ‰HÀ†G¿„F¼Eº~C¹{B®£”«««¥¥¥ ™™™‘‘‘ˆˆˆtttRRRVA/ÀˆJËšTÛ¼wÝÁÖ´rЩfΡ\Ê—SÈ“MÆŽKÊIÁˆH¿…G½‚E¼€D¹|C¸zB¶xA³s?±p=m<¨i:c7–_7‹\8†^?VF8VF8¿…GÉ“MÖ©]Ó£VÐQÍšPË–NÇ‘LÆŽKËI‰H¿…G¾ƒF¼Eº~C¹{B¶xAµw@³s?°o=¬l;¨i:žc6—_4‰X0Y1WD2RRR·šsÅJË–N͘OË–NÊ”MÆKÅJËI‰HÀ†G¿„F½‚E»D¹|C¸zB¶xAµv@²r>¯n<©j:¦i9žc6˜_4‰X0lQRQQRRR±±±½¬ŽÆ’OÉ“MÈ’LÆKÄŒJ‰HÁˆH¿…G¿„F½‚E¼€Dº~C¹{B¶xAµw@³s?±p=¬l;¨i:¤g8›a5—_6‘wc‰‰‰```RRR¦¦¦¹¹¹¼¶©Á”ZËI‰HÁˆH¿…G¿„F½‚E¼E»D¹|C¹{B·yA¶xA³s?²q>m<¨i:¤g8Ÿd7˜fAˆ}t‘‘‘ŠŠŠ___RRRššš±±±···¸·¶ºžv¾„G¾ƒF½‚E¼E»D¹|C¹{B¸zB¶xAµw@³s?²q>m<©j:¤g8Ÿd7—tX}|•••ƒƒƒRRRRRR‘‘‘¥¥¥¬¬¬°°°²²²³§•»‰Q»D¹|C¹{B·yA¶xAµw@µv@³s?²r>¯n<©j:¥h9ži@‘‚tŸŸŸšššxxxRRR ¦¦¦¨¨¨¬¬¬®®®° ‡¹|C·yA¶xAµv@³s?²r>²r>²q>¯n<¬l;¦i9¡e7“~l‚‚‚”””¤¤¤¨¨¨”””lllZZZ–––ŸŸŸ¡¡¡¥¥¥§§§°…Yµw@³s?³s?±p=°o=¯n<m<m<©j:¦i9¤g8›a5•hG’’’¥¥¥±±±¤¤¤RRR–––›››žžž¡˜Ž®n=°o=°o=¯n<m<¬l;©j:¨i:¦i9¤g8Ÿd7•^3[2¢f8¦Ÿ”±±±±±±———lllRRR”””———¡`¨i:¨i:©j:©j:©j:¨i:¥j=¢h<¡e7b6•^3•^3Ÿd7²q>³•o®®®ššš{{{RRRRRR‚‚‚œg>žc6žc6¢f8¢f8¢f8Ÿg=”‰}‘†{—b:•^3˜_4¢f8±p=¶xA²}L”””ƒƒƒRRRRRRzu[2[2”]3”]3˜_4˜d=Œƒ{ŠŠŠ‰‰‰‹‚zžh?¤g8¯n<³s?°o=¤g8‡yRRRSNIyO+‰X0‰X0•^3nI‹†‰‰‰ŠŠŠ’’’›–§uMm<¨i:˜_4{P+SNIW?)oI'zR0‡lX‚€ŒŒŒŒŒŒŽŽŽ‘‘‘Ž‘u_‹[5U:W?)RQQ```nnn{{{ƒƒƒrrriiiRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRUF9UI>RRRRRRRRRrrr–––¥¥¥ªªª¯™{¶xA¶xA¦–‚œœœ•••………ooo\\\uuu®®®ÉÉÉÎÎÎÐÐÐÊÉÇÄ‘QÁˆH¿„FºŒW²²²®®®¦¦¦”””|||cccWWWiii¤¤¤ÓÓÓëëëîîîìììæææÎèȒLËIÁˆH¿…G¶ª—´´´°°°¦¦¦———………nnnZZZnnn°°°âââôôôúúúöööñññâââɨqÆŽK‰H¿…G¼E´’h®®®¬¬¬¨¨¨¤¤¤™™™ˆˆˆttt___oooªªªÚÚÚòòòûûûûûûôôôêêêÙÙÔÉ•PÆŽKÊI¿…G½‚E¹I°¯®®®®«««§§§ •••ŠŠŠxxx___nnn¤¤¤ÙÙÙíííñññôôôòòòêêêßßßÍÄÉ“MÆŽK‰H¿…G½‚E»D±¤¯¯¯¬¬¬¨¨¨¢¢¢ššš………rrrZZZnnn¯¯¯ÙÙÙóóóúúúôôôíííèèèàààØØØÇ®‚Ç‘LÄŒJ‰H¿…G½‚E»D´”m¯¯¯¬¬¬¨¨¨¤¤¤œœœ’’’‰‰‰~~~kkkTTTŠŠŠÇÇÇíííûûûûûûñññçççáááÚÚÚÒÒÒÇ›]ÆŽKÊIÁˆH¿…G¼Eº~C·‚L®®®¬¬¬§§§¢¢¢œœœ”””ŒŒŒƒƒƒuuu______¦¦¦ÏÏÏ÷÷÷øøø÷÷÷êêêàààÚÚÚÓÓÓÊÇÁÆLÄŒJÁˆH¿…G¾ƒF¼€D¹|C¹{B Ž«©¨¦¦¦¡¡¡›››•••ŒŒŒ………{{{lllWWWeM7¶xAÍœUÛ½xÚ½zÙºxÒkϤ`ÌXÈ•QÇ‘LÄŒJ‰HÀ†G¿„F½‚E»D¹{B¸zB¶xA³s?±r@qB©pE¢nF™jF‘fG‰cGz\C\I7`\Y²‚RÒ¡UÚ²fÙ±eÒ TÏœQË–NÉ“MÆKÄŒJ‰HÁˆH¿„F½‚E¼€D¹|C¸zB¶xAµv@³s?°o=m<¨i:¢f8›a5’\2‰X0W4eZQ]]]¦¤¢Íµ„Ø®aÓ¤WË–NÉ“MÇ‘LÆŽKËI‰HÀ†G¿…G½‚E¼€Dº~C¹{B·yAµw@³s?²q>¯n<©j:¦i9¡e7š`5’\2fI{wtbbb]]] ÐÐÐÓÉÌšSÆKÆŽKËI‰HÁˆH¿…G¿„F½‚E¼€Dº~C¹{B·yA¶xAµv@²r>°o=¬l;¨i:¤g8žc6–_6wc‰‰‰bbbZZZ•••ÁÁÁÓÓÓÁ¼±¿“[ÁˆHÀ†G¿…G¾ƒF½‚E¼€Dº~C¹{B¹{B·yA¶xAµv@³s?°o=m<¨i:¢f8žc6˜hCˆwŠŠŠ~~~bbbWWWŒŒŒ²²²Â³³³µ´³¸›s½‚F¼E»Dº~C¹{B¸zB¶xA¶xAµw@³s?²r>°o=m<¨i:¢f8c7–sX…ƒ‚‰‰‰”””xxxYYY___ƒƒƒ¤¤¤®®®«««°°°±£¸K¹{B¸zB¶xAµw@³s?³s?³s?²q>°o=m<©j:¤g8Ÿg>‘€qˆˆˆ™™™žžžŽŽŽrrrYYYkkk”””¡¡¡¢¢¢¥¥¥¨¨¨«««®›‚¶xAµv@³s?²q>°o=°o=¯n<m<¬l;¨i:¤g8žc6’|iŠŠŠ‘‘‘ §§§œœœbbbYYY‚‚‚’’’›››žžž¡¡¡¢¢¢«U±p=±p=¯n<m<¬l;©j:©j:¨i:¥h9¡e7b6—_4—jH•••¢¢¢¬¬¬¨¨¨‘‘‘tttTTT```|||–––šššœ“‰©j;©j:©j:©j:¨i:¦i9¤g8¢f8¡e7b6˜_4•^3˜_4£g9¤’¬¬¬§§§———zzz\\\]]]wwwŠŠŠ›wZžc6Ÿd7¢f8¢f8¡e7Ÿd7že;›d:˜_4—_4—_4›a5¥h9±p=¬‹g¢¢¢”””zzz]]]ZZZrrr‚}‘]6’\2”]3—_4š`5˜_4—b:‚wŒw–a9š`5Ÿd7¦i9m<°o=©m?‹‰uuu\\\WWWr]M|Q,„U.‹Y0”]3˜_4–b;‹‚zŠŠŠŒŒŒ‡~¡j@¥h9¨i:¨i:b6‰X0r]MYYYW?(aA#oI'yO+„U.Ž_;ˆ€z‰‰‰ŠŠŠŒŒŒŒŒŒ‡€–d>[2‡W/oI'[=!V>'XB.]?#lL/upl~~~~~~wwwqlhaD+Z="WA.RRRRRR___ccc___YYYRRRRRR```fffeeebbbnAMRRRRRRRRRkkk‘‘‘ŸŸŸ¨¨¨¬¬¬¬¾ll¡‘‘•••†††lllWWW†††¦¦¦¸¸¸¿¿¿¿¿¿¼¼¼º““Átt´‰‰§§§žžžwwwZZZ~~~···ÐÐÐÚÚÚÙÙÙÐÐÐÇÆÆÄ‚‚ÃwwÀxx¬¬ªªª rrrZZZuuu¾¾¾çççðððíííååå×××Á´´Å}}ÄyyÂvv±ŸŸ¬¬¬¦¦¦žžžŒŒŒtttZZZeee³³³éééùùùúúúòòòãããÐÐПŸÇ€€Ä{{Ãxx·ŒŒ®®®ªªª¥¥¥šššˆˆˆqqqYYYWWW›››ÕÕÕñññûûûùùùíííÝÝÝÏÎÎÈ‹‹ÇÅ}}ÄyyÀzz¯®®¬¬¬§§§ŸŸŸ’’’………lllVVVˆˆˆÈÈÈêêêñññôôôñññæææÙÙÙÉ¿¿Ê‡‡ÇÆ~~ÄyyÂvv°¡¡¨¨¨¡¡¡———ŒŒŒ{{{___‚‚‚¾¾¾èèèøøøôôôíííçççÞÞÞÔÔÔÆ©©É††ÇÆ~~Ä{{ÂvvµŽŽ¨¨¨¢¢¢™™™‚‚‚lllVVVŸŸŸÛÛÛ÷÷÷ýýýôôôéééâââÙÙÙÏÏÏÈ‘‘È„„Ç€€Å}}ÄyyÂvv½{{¨¨¨¢¢¢ššš………uuu\\\{{{³³³âââúúúúúúîîîâââÛÛÛÓÓÓÈÀÀɆ†ÇÆÅ||ÃxxÂuuÁss¬ §§§¡¡¡ššš†††|||fffTTT‹``½ŒŒÒ§§ÜµµÛ¯¯Ö¢¢Ò™™Ï’’ÍŽŽÊˆˆÉ……Ç€€Æ~~Ä{{ÃwwÁttÀrr¿oo½jj»ee¹aaµYY²QQ®IIC[y-;YYÆ~~ÒššÙªªØ§§ÓÏ””Íˉ‰É††ÇÆ~~Å||ÄyyÂuuÁssÀpp¾ll¼ii»ee¹aa¶ZZ²RR¯JJ•EZw4Cqqq»™™Ð––Õ¡¡Ò™™Ï’’Íˉ‰É††ÈƒƒÇ€€Å}}Ä{{ÃwwÁttÀrr¿oo¾ll¼ggºdd¸``¶ZZ²RR¯KK¨HKaMSnnn´´´È¸¸ÑššÍÌˉ‰É††ÈƒƒÇ€€Æ~~Ä{{ÃxxÂuuÁttÀpp¿nn½kk»ff¹bb¸^^µYY²RRPP†mmYYYlll¯¯¯ÊÊÊËÅÅÈ——ʈˆÉ……ÇÇ€€Æ~~Å||ÄyyÂvvÁttÀrrÀpp¾ll½jj»ee¹aa·\\µXX©[[Œ~~___iii§§§ÁÁÁÈÈÈÁÀÀ¾ Æ€€Æ~~Å}}Ä{{ÄyyÂvvÁttÀrrÀpp¿nn½kk»ff¹bb·]]µYYžnnˆ††ŒŒŒ‚‚‚```fffžžž´´´»»»ººº»»»¸¬¬ÀƒƒÃxxÃwwÂuuÁssÀppÀpp¿nn½kk¼gg¹bb¸^^¯bb’………ŠŠŠ~~~YYYbbb•••¨¨¨¯¯¯²²²´´´¶¶¶´ŸŸÂuuÁttÀrrÀpp¿nn¾ll½kk¼ii»ee¹aa¶[[–||ˆˆˆˆˆˆ•••”””wwwTTT___‰‰‰¡¡¡§§§«««®®®°°°¹„„ÁssÀpp¿oo¾ll½kk½jj¼ii»ee¹bb·]]µXX¤aaŠŠŠ——— ’’’oooTTTxxx™™™¡¡¡¥¥¥¨¨¨«££¿pp¿nn¾ll½jj¼gg»ff»ee»ee¹bb¸^^¶ZZ³TT³TT™ŽŽ¦¦¦¢¢¢†††___kkkŒŒŒšššžžž¡¡¡¬……½jj¼ii¼gg»eeºdd¹bb²jj¸bb·]]µYY³TT³TT·]]„„žžžwwwVVVuuuŽŽŽ———›ššµhhºddºddºdd¹bb¹aa¦vv–””™……±[[³SS²RR¶[[¼ii½tt¥££ŒŒŒccc\\\zzzŽŽŽš……·\\·]]·]]¸^^´cc›ƒƒ”””ŽŽŽŽ‚‚«[[¶ZZ»ee¿nn½jj›||rrrVVV___{{{žjj³TT´UU´WW§hh’ŠŠŽŽŽŠŠŠ‰‰‰ŒŒŒ’®qq½jj¼ii¶[[ MMVVVbbb NN¯KK¬QQ“qqŠŠŠŠŠŠ‰‰‰‰‰‰ŠŠŠŒŒŒ’’’šššŸžž«{{µYY©HKy:IpLXˆ[[}ww‚‚‚‰‰‰‰‰‰‰‰‰ŠŠŠŽŽŽ‘‘‘•••———–––ˆˆˆ‡[[x:I]]]llluuu~~~………ˆˆˆ‰‰‰ŒŒŒŠŠŠ‰‰‰………{{{iiiTTTTTT]]]ooo{{{{{{ttteeeZZZRRRRRRRRRRRRRRRRRRRRRRRRDDDRRRRRRRRRONOc?HTPQRRRRRRbbb~~~›››§§§±””Àpp††›››‘‘‘uuuYYYbbb‚‚‚§§§¼¼¼ººº»»»¶´´À~~Âvv¿ww««ªªªŸŸŸŒŒŒcccRRRRRRžžžÔÔÔÞÞÞÎÎÎËËËÈÈȼ©©Å||ÄyyÂvv±œœ®®®ªªª¡¡¡‰‰‰tttRRRRRR§§§äääøøøøøøæææØØØÍÍ͘˜ÆÄ{{Ãxx¸‰‰°°°«««¦¦¦¡¡¡ŒŒŒxxxRRR]]]šššéééüüüÿÿÿüüüïïïÛÛÛÐÎÎʉ‰ÇÆ~~Ä{{Âzz²°°®®®ªªª§§§šššŠŠŠRRRQQQŽŽŽÔÔÔæææøøøþþþöööçççÚÚÚɽ½ÊˆˆÈ„„Æ~~Ä{{Ãww²¢¢¯¯¯¬¬¬¨¨¨žžž”””ŠŠŠuuuRRR†††ÃÃÃðððøøøîîîðððèèèßßßÕÕÕÆ©©ÊˆˆÈƒƒÆÄ{{Ãxxµ¯¯¯¬¬¬¨¨¨¡¡¡———ŒŒŒ‚‚‚ffffffÉÉÉîîîÿÿÿÿÿÿîîîêêêáááÛÛÛÏÏÏÈ––Ɇ†ÇÆÄ{{Ãxx¼¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽ†††uuuRRRŸŸŸÑÑÑùùùÿÿÿùùùèèèãããÜÜÜÒÑÑÉÃÃʈˆÈ„„Ç€€Æ~~Ä{{ÃwwÁtt¯¨¨¬¬¬§§§¡¡¡™™™ŽŽŽˆˆˆffff=G·É§§ßÉÉàÇÇÙ¸¸Ó¨¨ÑžžÏ••ÌŽŽË‰‰É……ÇÆÅ}}ÄyyÂvvÁttÀrr¿oo½jj»ee¸``µYY¯TT§QQ™OOc@IZHM††ÍÛ®®Ü±±Ö¤¤Ò™™Ï””ÌËŠŠÊ‡‡ÈƒƒÇ€€Æ~~Ä{{ÃxxÂuuÁssÀpp¾ll¼ii»ee¹aaµYY²RR¯JJ©HKh<GRRRº¸¸Æ££Ö¢¢ÓœœÐ––Ï’’Íˉ‰Ê‡‡È„„ÇÆ~~Å||ÄyyÂvvÁttÀpp¿oo¾ll¼ggºdd¹aaµYY³SS¯JJ˜ddRQRRRR···ÆÆÆɹ¹Í””ÍÌʈˆÉ††È„„ÇÆÅ}}Ä{{ÃwwÂuuÁssÀpp¿nn½kk»ff¹bb¸``µYY±VVŽkk………RRRRRR±±±ÀÀÀÈÈÈÊÄÄǘ˜ÊˆˆÉ……ÇÇ€€Æ~~Å}}Ä{{ÃxxÂvvÁttÀpp¿oo¾ll¼iiºdd¹aa·]]«``Ž‰‰‰‰‰‰```RRR¦¦¦¹¹¹¿¿¿ÂÂÂÂÁÁ¾ Æ€€Æ~~Å}}Ä{{ÄyyÃwwÂuuÁttÀrrÀpp¾ll½jj»ee¹aa·]]¡uuŒŠŠ………‘‘‘ŠŠŠ___RRRššš±±±···¹¹¹»»»»»»¸««ÁÄyyÃwwÂuuÁttÁssÀpp¿oo¾ll½jj»ee¹bb±ee—„„•••ƒƒƒRRRRRR‘‘‘¥¥¥¬¬¬°°°²²²´´´···´žžÂuuÁttÀrrÀpp¿oo¿nn¾ll½kk»ff¹bb¸^^™ŽŽŽŸŸŸšššxxxRRR ¦¦¦¨¨¨¬¬¬®®®±±±¹……ÀrrÀpp¿nn¾ll½kk½kk½jj»ffºdd¸``¶[[¦ff‚‚‚”””¤¤¤¨¨¨”””lllZZZ–––ŸŸŸ¡¡¡¥¥¥§§§ª¢¢¿oo¾ll¾ll¼ii¼gg»ff»ee»ee¹bb¸``·]]´WW²RR•ŠŠ¥¥¥±±±¤¤¤RRR–––›››žžž ªƒƒ¼gg¼gg»ff»eeºdd¹bb²ii¸aa·]]¶ZZ²RR±OO·\\®††±±±±±±———lllRRR”””———š™™´gg¹aa¹bb¹bb¹bb¹aa¦vv•““™……±[[²RR²RR¶ZZ½jjÀxx««ššš{{{RRRRRR‚‚‚–µYYµYY·\\·\\³aa™€€’’’ŽŽŽŠŠŠ«\\·\\¼iiÀpp¿nn~~ƒƒƒRRRRRR›ee±OO²QQ²QQ¥ccŽ††ŠŠŠŠŠŠ‰‰‰‰‰‰‘‘‘–‘‘±vv¾ll¼gg·]]¦YYRRRRRR”JS¯JJªNN•tt‘‘‘ŠŠŠ‰‰‰ŠŠŠ’’’šššœœœŸžž¨xx³TT‘E^r3@d@I€STtnnƒƒƒŒŒŒŒŒŒŽŽŽ‘‘‘‘‘‘‰‰‰c@Ir3@RRR```nnn{{{ƒƒƒrrriiiRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRYJNr2>YJNRRRRRRrrr–––¥¥¥ªªª®®®¹||Àpp¶vvœœœ•••………ooo\\\uuu®®®ÉÉÉÎÎÎÐÐÐÌÌÌÀ²²Ç€€Å}}Ä{{²¡¡®®®¦¦¦”””|||cccWWWiii¤¤¤ÓÓÓëëëîîîìììæææ×××Æ¢¢È„„Ç€€Æ~~»´´´°°°¦¦¦———………nnnZZZnnn°°°âââôôôúúúöööñññâââÐÏÏÉŠŠÇÆ~~ÄyyÀxx««¬¬¬¨¨¨¤¤¤™™™ˆˆˆttt___oooªªªÚÚÚòòòûûûûûûôôôêêêÜÜÜȽ½Ê‡‡ÈƒƒÆ~~Ä{{Âvv±¡¡®®®«««§§§ •••ŠŠŠxxx___nnn¤¤¤ÙÙÙíííñññôôôòòòêêêßßßÕÕÕƪªÊ‡‡ÇÆ~~Ä{{Ãwwµ¯¯¯¬¬¬¨¨¨¢¢¢ššš………rrrZZZnnn¯¯¯ÙÙÙóóóúúúôôôíííèèèàààØØØÏÏÏÇ””É……ÇÆ~~Ä{{Ãww¼¯¯¯¬¬¬¨¨¨¤¤¤œœœ’’’‰‰‰~~~kkkTTTŠŠŠÇÇÇíííûûûûûûñññçççáááÚÚÚÑÐÐÇÀÀɇ‡ÈƒƒÇ€€Æ~~ÄyyÂvvÁtt®§§¬¬¬§§§¢¢¢œœœ”””ŒŒŒƒƒƒuuu___d[[‡‡Ç©©ÞÊÊÞÈÈÝÃÃÖ³³Ò¦¦ÏœœÎ’’ËŒŒÊˆˆÉ……Ç€€Æ~~Å||ÃxxÂuuÁttÀpp¿oo½kk»ff¹bb¶\\±XX©VVUU„VVWWW___¬ŸŸÌššÛ¯¯ÚÙªªÔžžÐ––Α‘ÌŒŒÊˆˆÉ……ÇÆÅ}}Ä{{ÃwwÁttÁssÀpp¾ll½jj»ee¹bb·\\´UU±OO®IIŽUUWWW___¬¬¬ÛÚÚÛÅÅÙªªÒ™™Ð••ÍÌŒŒÊˆˆÉ……ÇÇ€€Å}}Ä{{ÃxxÂuuÁssÀpp¿nn¾ll¼gg»ee¹aa·\\´WW±PP–aawvvbbb]]]¦¦¦ÙÙÙìììÔÈÈÌ’’ÌŒŒË‰‰Ê‡‡È„„ÇÆÆ~~Ä{{ÃxxÂvvÁttÀrr¿oo¾ll½jj»ff¹bb¸``¶[[²WW“qq‚‚‚zzzbbb]]] ÐÐÐßßßÒÒÒÆÀÀÆ••È„„ÇÇ€€Æ~~Å}}Ä{{ÃxxÂvvÁttÀrrÀpp¿nn½kk¼ggºdd¹aa·]]¬ccŽˆˆˆ‰‰‰bbbZZZ•••ÁÁÁÓÓÓÄÄÄ¿¿¿¾½½½žžÅ~~Å||Ä{{ÃxxÂvvÁttÁttÀrrÀpp¿nn¾ll¼gg»ee¹aa¶\\ ttŒŠŠ†††ŠŠŠ~~~bbbWWWŒŒŒ²²²Â³³³¶¶¶¸¸¸¸¸¸µ§§¿}}ÂvvÁttÁssÀppÀpp¿oo¾ll½kk¼gg»ee¹aa±dd–ƒƒŒŒŒ………‰‰‰”””xxxYYY___ƒƒƒ¤¤¤®®®«««°°°±±±²²²²ÁssÀpp¿oo¾ll¾ll¾ll½jj¼gg»ee¹bb·]]™ŒŒŒˆˆˆ™™™žžžŽŽŽrrrYYYkkk”””¡¡¡¢¢¢¥¥¥¨¨¨«««¬¬¬¶‚‚¿nn¾ll½jj¼gg¼gg»ff»eeºdd¹aa·]]µYY¥ccŠŠŠ‘‘‘ §§§œœœbbbYYY‚‚‚’’’›››žžž¡¡¡¢¢¢¥››¼ii¼ii»ff»eeºdd¹bb¹bb¹aa¸^^¶[[µXX³SS³TT—ŒŒ¢¢¢¬¬¬¨¨¨‘‘‘tttTTT```|||–––ššš›››§||¹bb¹bb¹bb¹aa¸``·]]·\\¶[[µXX³TT²RR³TT·]]ƒƒ¬¬¬§§§———zzz\\\]]]wwwŠŠŠ‘ŽŽ²\\¶ZZ·\\·\\¶[[µ\\ tt’‚‚˜uu¯VV³SS´WW¸^^¼ii»pp¢ ”””zzz]]]ZZZrrr“pp±PP²QQ³SS´UU¬\\ŠŠŠ‰‰‰‰‰‰……®bb¸``»ee¼gg¹bb—zzuuu\\\WWWkkk›KN©HK¯KK²QQ¦ccŠ„„‰‰‰ŠŠŠŒŒŒŽŽŽ”””—””§zz¸bbµXX¯JJ„JZYYYTRSz;K‰=RD\–\\‚€€†††‰‰‰ŠŠŠŒŒŒŒŒŒŒŒŒŠŠŠ………‚yyRUs9Go5As4AiFPeeettt~~~~~~wwwooo\\\VVVRRRRRRRRR___ccc___YYYRRRRRR```fffeeebbbYYYRRRRRRRRRkkk‘‘‘ŸŸŸ¨¨¨¬¬¬u¤ur ržžž•••†††lllWWW†††¦¦¦¸¸¸¿¿¿¿¿¿°¹°I§IEŸE¡«¡§§§žžžwwwZZZ~~~···ÐÐÐÚÚÚÙÙÙÐÐЋ»‹H©HD¢Dt¨t®®®ªªª rrrZZZuuu¾¾¾çççðððíííåååÕÖÕX²XG¨GE¥EK¤K¯¬¬¬¦¦¦žžžŒŒŒtttZZZeee³³³éééùùùúúúòòòããã³Ç³N°NJ«JE¦ED£D®®®ªªª¥¥¥šššˆˆˆqqqYYYWWW›››ÕÕÕñññûûûùùùíííÝÝ݇À‡O±OK¬KG¨GE¥Ej¨j°°°¬¬¬§§§ŸŸŸ’’’………lllVVVˆˆˆÈÈÈêêêñññôôôñññæææ×Ø×]¹]O±OK¬KH©HE¥EI£I¯¨¨¨¡¡¡———ŒŒŒ{{{___‚‚‚¾¾¾èèèøøøôôôíííçççÞÞ޻˻SµSN°NK¬KH©HE¦EC¡C’¬’¨¨¨¢¢¢™™™‚‚‚lllVVVŸŸŸÛÛÛ÷÷÷ýýýôôôéééâââÙÙÙÀP²PM®MJ«JG¨GE¥EC¡Cn¦n¨¨¨¢¢¢ššš………uuu\\\{{{³³³âââúúúúúúîîîâââÛÛÛÓÓÓ`¸`N°NK¬KIªIF§FD£DC CL L¬¬¬§§§¡¡¡ššš†††|||fffTTTxxx¹¹¹âââøøøõõõçççÜÜÜÕÕջȻP²PM¯MJ«JH©HE¦ED¢DBžBAœA“§“¥¥¥ ššš‘‘‘ˆˆˆ~~~qqqTTT,i,H©H_Ã_xÌxsÊsbÄbY¼YV¸VQ³QN°NK¬KH©HF§FE¥EC CBBA›A?–?=’=<<:Š:7ƒ73{30r0,i, M 1g1G¨G[¾[iÇi^Á^X»XV¸VQ³QN°NLLJ«JG¨GE¦ED¢DBžBAœA@š@?–?=‘=;Ž;9‰97ƒ73{30s0.n.&P&YlYF£FV¸V]À]V¸VT¶TQ³QN°NLLJ«JH©HE¦ED£DC CBžBA›A@˜@>•><<:‹:9ˆ96‚63{31u1/p/HWHlllŽªŽV´VV¸VR´RP²PM¯MK¬KJ«JH©HF§FE¥EC¡CBžBAœAA›A?–?>”><<:Š:8…8663{36{6g~g___iii§§§¯¼¯c¶cLLK¬KJ«JH©HG¨GE¦EE¥EC¡CBžBAœAA›A@˜@>•><<:‹:8†86‚64}4>|>zŠz‚‚‚```fffžžž´´´¸º¸x¯xH¨HF§FE¦ED£DD¢DC CBBA›AA›A@˜@>•>=‘=:‹:9ˆ97ƒ75~5RR†‰†~~~YYYbbb•••¨¨¨¯¯¯²²²—¯—R¦RD¢DC CBžBAœAA›A@˜@?–?>•>=’=<<:Š:7„7?ƒ?p†pˆˆˆ•••”””wwwTTT___‰‰‰¡¡¡§§§«««®®®Š«ŠBžBBBA›A@š@?–?>•>>”>=’=<<:‹:8†866i†iŠŠŠ——— ’’’oooTTTxxx™™™¡¡¡¥¥¥¨¨¨Z ZA›A@˜@?–?>”>=‘=<<<<<<:‹:9ˆ97ƒ74}4H‚H———¦¦¦¢¢¢†††___kkkŒŒŒšššžžžŸ>’>>”>=’==‘=<<;Ž;:‹::‹::Š:8†86‚64}44}49†9”£”žžžwwwVVVuuuŽŽŽ———`”`:‹:;Ž;;Ž;;Ž;:‹::Š::‰::ˆ:7„7664|43{37„7=’=l£l¦¦¦ŒŒŒccc\\\zzz‰‰<…<8…88†88†89ˆ99ˆ9<‡<x‘xuu::4|44|47ƒ7<<@˜@B•B‘rrrVVV___XwX4|44}45~55566@ƒ@~~ŠŠŠz‡z>€>7ƒ7:‹:>”>=’=7„7VtVVVV0[0-k-0s01v12x2FF‚ˆ‚‰‰‰‰‰‰ŠŠŠŒŒŒŠŠOO<<;Ž;6‚6.o.,U,(V(+f+.o.L{L†ˆ†‰‰‰‰‰‰ŠŠŠŽŽŽ‘‘‘•••”–”XŽX3y3+f+'S'-V-JhJuuu~~~………ˆˆˆ‰‰‰ŒŒŒŠŠŠ‰‰‰………{{{HeH(M(TTT]]]ooo{{{{{{ttteeeZZZRRRRRRRRRRRRRRRRRRRRRRRRDDDRRRRRRRRR4K47O7RRRRRRRRRbbb~~~›››§§§¡«¡DžDCœCš¤š›››‘‘‘uuuYYYbbb‚‚‚§§§¼¼¼ººº»»»z®zD£DC¡Cs§s®®®ªªªŸŸŸŒŒŒcccRRRRRRžžžÔÔÔÞÞÞÎÎÎËËËÆÇÆSSF§FE¥EK¤K®°®®®®ªªª¡¡¡‰‰‰tttRRRRRR§§§äääøøøøøøæææØØØ°Ä°M¯MIªIE¦ED£D‘®‘°°°«««¦¦¦¡¡¡ŒŒŒxxxRRR]]]šššéééüüüÿÿÿüüüïïïÛÛÛ‡À‡P²PK¬KH©HE¦Ek©k²²²®®®ªªª§§§šššŠŠŠRRRQQQŽŽŽÔÔÔæææøøøþþþöööçççØÙØ^º^P²PM®MH©HE¦EJ¤J¯±¯¯¯¯¬¬¬¨¨¨žžž”””ŠŠŠuuuRRR†††ÃÃÃðððøøøîîîðððèèèßß߻˻T¶TP²PLLIªIE¦ED£D““¯¯¯¬¬¬¨¨¨¡¡¡———ŒŒŒ‚‚‚ffffffÉÉÉîîîÿÿÿÿÿÿîîîêêêáááÛÛÛÀR´RN°NK¬KIªIE¦ED£Do§o¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽ†††uuuRRRŸŸŸÑÑÑùùùÿÿÿùùùèèèãããÜÜÜÓÓÓ`¸`P²PM®MJ«JH©HE¦ED¢DL¢L¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽˆˆˆfffRRR´´´ÑÑÑúúúüüüðððãããÜÜÜÖÖֻȻQ³QM¯MK¬KIªIG¨GE¥EC¡CBžB–ª–«««¥¥¥ ™™™‘‘‘ˆˆˆtttRRR8O8e®ed»d†Ï†…Ñ…mÈm^Á^Y¼YT¶TR´RO±OLLJ«JH©HE¦ED£DC CBBA›A?–?=’=<<:Š:7‚77|78u8?s?8O82N2H©HSµSkÇk`Ã`[¾[X»XV¸VQ³QO±OM®MK¬KH©HF§FE¥EC¡CBžBA›A@š@?–?=‘=;Ž;:Š:6‚64|40r01u12O2RRRuuN°NV¸VWºWV¸VT¶TP²PN°NM®MK¬KIªIG¨GE¦ED¢DC CBBA›A@˜@>•><<:‹:9‰96‚64}40r0Q~QQRQRRR±±±”·”T´TSµSR´RP²PM¯MK¬KJ«JH©HG¨GE¦ED£DC¡CBžBA›A@š@?–?=’=;Ž;:Š:8†8556|6c…c‰‰‰```RRR¦¦¦¹¹¹º_±_M®MK¬KJ«JH©HG¨GE¦EE¥ED¢DC CBžBAœAA›A?–?>”><<:Š:8†87ƒ7AAtƒt‘‘‘ŠŠŠ___RRRššš±±±···¶¸¶{±{H¨HF§FE¦EE¥ED¢DC CBžBBBA›A@š@?–?>”><<:‹:8†87ƒ7X†X|~|•••ƒƒƒRRRRRR‘‘‘¥¥¥¬¬¬°°°²²²˜¯˜S§SD¢DC CBžBAœAA›A@š@@˜@?–?>•><<:‹:9ˆ9@…@t‹tŸŸŸšššxxxRRR ¦¦¦¨¨¨¬¬¬®®®Š«ŠC CAœAA›A@˜@?–?>•>>•>>”><<;Ž;9‰97„7lŠl‚‚‚”””¤¤¤¨¨¨”””lllZZZ–––ŸŸŸ¡¡¡¥¥¥§§§YŸY@š@?–??–?=’==‘=<<<<<<:‹:9‰98†855G€G’’’¥¥¥±±±¤¤¤RRR–––›››žžžž===‘==‘=<<<<;Ž;:‹::Š:9‰98†87ƒ73{32w28…8•¤•±±±±±±———lllRRR”””———`“`:Š::Š::‹::‹::‹::Š:=‰=<‡<7„7663{33{37ƒ7>”>r¨r®®®ššš{{{RRRRRR‚‚‚>ƒ>6‚66‚68…88…88…8=…=}}{Œ{:~:3{34}48…8=’=A›ALœL”””ƒƒƒRRRRRRu~u2w22w23y33y34}4=={ˆ{ŠŠŠ‰‰‰z‡z?„?8†8<<?–?=‘=8†8y„yRRRIQI+f+0r00r03{3I‡I‰‰‰‰ŠŠŠ’’’™M‘M<<:Š:4}4+h+IQI)M)'^'0h0XzX€‚€ŒŒŒŒŒŒŽŽŽ‘‘‘ŽŽ_„_5t5I)M)QRQ```nnn{{{ƒƒƒrrriiiRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQRQ9O9>P>RRRRRRRRRrrr–––¥¥¥ªªª~§~A›AA›A„¡„œœœ•••………ooo\\\uuu®®®ÉÉÉÎÎÎÐÐÐÈÊÈU°UJ«JG¨GY©Y²²²®®®¦¦¦”””|||cccWWWiii¤¤¤ÓÓÓëëëîîîìììæææ³Ê³R´RM®MJ«JH©Hš²š´´´°°°¦¦¦———………nnnZZZnnn°°°âââôôôúúúöööñññâââz¿zO±OK¬KH©HE¥Ek¨k®®®¬¬¬¨¨¨¤¤¤™™™ˆˆˆttt___oooªªªÚÚÚòòòûûûûûûôôôêêê×Ù×V·VO±OLLH©HE¦EI£I®°®®®®«««§§§ •••ŠŠŠxxx___nnn¤¤¤ÙÙÙíííñññôôôòòòêêêßß߶ʶSµSO±OK¬KH©HE¦ED¢D’’¯¯¯¬¬¬¨¨¨¢¢¢ššš………rrrZZZnnn¯¯¯ÙÙÙóóóúúúôôôíííèèèàààØØØ‹¿‹Q³QM¯MK¬KH©HE¦ED¢Dp©p¯¯¯¬¬¬¨¨¨¤¤¤œœœ’’’‰‰‰~~~kkkTTTŠŠŠÇÇÇíííûûûûûûñññçççáááÚÚÚÒÒÒd¸dO±OLLJ«JH©HE¥EC¡CL¢L®®®¬¬¬§§§¢¢¢œœœ”””ŒŒŒƒƒƒuuu______¦¦¦ÏÏÏ÷÷÷øøø÷÷÷êêêàààÚÚÚÑÒѽǽQ²QM¯MJ«JH©HF§FD£DC CBžB¨¨ª¨¦¦¦¡¡¡›››•••ŒŒŒ………{{{lllWWW<[<h£h‡Á‡Ö Ó –ЖyÆydÁdZ»ZSµSP²PM¯MK¬KIªIG¨GE¦ED¢DBžBBBA›A?–?@”@B‘BEŽEFŠFFƒFG}GGwGClC7T7O^OKžK_Ã_zÍzxÌx^Á^Z½ZV¸VSµSP²PM¯MK¬KJ«JG¨GE¦ED£DC CBBA›A@˜@?–?=‘=<<:Š:8…8552x20r04n4Q`Q]]] ¥ ÄsÊsbÄbV¸VSµSQ³QO±OM®MK¬KIªIH©HE¦ED£DC¡CBžBAœA@š@?–?>”><<:‹:9‰97„75~52x2I{Itytbbb]]] ÐÐйй[º[P²PO±OM®MK¬KJ«JH©HG¨GE¦ED£DC¡CBžBAœAA›A@˜@>•>=‘=;Ž;:Š:8†86‚66|6c„c‰‰‰bbbZZZ•••ÁÁÁÓÓÓµ¿µ`¯`J«JIªIH©HF§FE¦ED£DC¡CBžBBžBAœAA›A@˜@?–?=‘=<<:Š:8…86‚6CCw„wŠŠŠ~~~bbbWWWŒŒŒ²²²Â³³³´µ´y¯yF¦FE¥ED¢DC¡CBžBBBA›AA›A@š@?–?>•>=‘=<<:Š:8…87‚7X…X‚„‚‰‰‰”””xxxYYY___ƒƒƒ¤¤¤®®®«««°°°’¬’K¢KBžBBBA›A@š@?–??–??–?>”>=‘=<<:‹:8†8>„>q‰qˆˆˆ™™™žžžŽŽŽrrrYYYkkk”””¡¡¡¢¢¢¥¥¥¨¨¨«««…§…A›A@˜@?–?>”>=‘==‘=<<<<;Ž;:Š:8†86‚6iˆiŠŠŠ‘‘‘ §§§œœœbbbYYY‚‚‚’’’›››žžž¡¡¡¢¢¢U™U=’==’=<<<<;Ž;:‹::‹::Š:9ˆ97„7664|4H‚H•••¢¢¢¬¬¬¨¨¨‘‘‘tttTTT```|||–––šššŠ™Š;‹;:‹::‹::‹::Š:9‰98†88…87„7664}43{34}49†9“¢“¬¬¬§§§———zzz\\\]]]wwwŠŠŠZŠZ6‚67ƒ78…88…87„77ƒ7;ƒ;::4}44|44|4559ˆ9=’=hŸh¢¢¢”””zzz]]]ZZZrrr}€}6x62x23y34|45~54}4:~:wˆww‡w9}95~57ƒ79‰9<<=‘=??‰‰uuu\\\WWWMhM,i,.o.0s03y34}4;};z‡zŠŠŠŒŒŒ~~@†@9ˆ9:Š::Š:660r0MhMYYY(M(#S#'^'+f+.o.;x;z…z‰‰‰ŠŠŠŒŒŒŒŒŒ€Š€>>2w2/q/'^'!N!'L'.O.#P#/^/lrl~~~~~~wwwhnh+T+"N".M.RRRRRR___ccc___YYYRRRRRR```fffeeemLmeEeRRRRRRRRRkkk‘‘‘ŸŸŸ¨¨¨¬¦ª¾l¥¼h¤Ÿ˜œ•••†††lllWWW†††¦¦¦¸¸¸¿¿¿¿¿¿»›±Äv«Áo¨±Œ¥§§§žžžwwwZZZ~~~···ÐÐÐÚÚÚÙÙÙÏÎÏÈŠ´Æz¬Ãr©¾u§¬ªªª rrrZZZuuu¾¾¾çççðððíííåååÏÄÍÉ‚²Åx¬ÄuªÂq©°žª¬¬¬¦¦¦žžžŒŒŒtttZZZeee³³³éééùùùúúúòòòãããȦ¿É‚²Ç|®Äv«Ãtª·‰¨®®®ªªª¥¥¥šššˆˆˆqqqYYYWWW›››ÕÕÕñññûûûùùùíííÜÛÜÌ‘ºÊƒ³Ç}®Åx¬ÄuªÀv¨¯®¯¬¬¬§§§ŸŸŸ’’’………lllVVVˆˆˆÈÈÈêêêñññôôôñññæææÑÈÐÍŠ¸Êƒ³Ç}®Æz¬ÄuªÂq©° «¨¨¨¡¡¡———ŒŒŒ{{{___‚‚‚¾¾¾èèèøøøôôôíííçççÞÞÞÊÃ̇¶É‚²Ç}®Æz¬Äv«Âq©µ‹§¨¨¨¢¢¢™™™‚‚‚lllVVVŸŸŸÛÛÛ÷÷÷ýýýôôôéééâââÙÙÙË’ºÊ„³È°Ç|®Åx¬ÄuªÂq©½w§¨¨¨¢¢¢ššš………uuu\\\{{{³³³âââúúúúúúîîîâââÛÛÛÍÅÌ̇¶É‚²Ç}®Æ{Åw«ÃtªÂp¨Án§¬Ÿ¨§§§¡¡¡ššš†††|||fffTTTDÄv«ÔšÃÜ®ÑÛªÏÖŸÇÒ•ÀÏŽ»ÍŠ¸Ê„³É€±Ç|®Æz¬Äv«Ãr©Áo¨Àl§¿j¦½d£»_¡¹[ŸµRœªL˜•I•†@†f.f‰H‰Æz¬Ò—ÁÙ§ÌØ£ÊÓ™Âϼ͋¸Ë…´É‚²Ç}®Æz¬Åw«ÄuªÂp¨Án§Àk¦¾f¤¼c£»_¡¹[Ÿ¶SœM™˜I•ŠCŠh4h}cvÄz¬Ð’½ÕžÆÒ•ÀÏŽ»Í‹¸Ë…´É‚²È~¯Ç|®Åx¬Äv«Ãr©Áo¨Àl§¿j¦¾f¤¼b¢º^¡¸YŸ¶SœM™›J–‘G‘^L^nnn´£¯ÌŽ¹Ñ”¿Í‹¸Ì‰·Ë…´É‚²È~¯Ç|®Æz¬Äv«ÃtªÂp¨Áo¨Àk¦¿i¥½e¤»a¢¹\ ¸XžµRœM™¡L•†kYYYlll¯¯¯ÆÀÅÊ—»Ë†µÊ„³É€±Ç}®Ç|®Æz¬Åw«ÄuªÂq©Áo¨Àl§Àk¦¾f¤½d£»_¡¹[Ÿ·VµQ›©U”Œ~ˆ___iii§§§ÁÁÁÆÅÆ¿žµÇ}®Ç|®Æz¬Åx¬Äv«ÄuªÂq©Áo¨Àl§Àk¦¿i¥½e¤»a¢¹\ ·WžµR›žkˆ†ˆŒŒŒ‚‚‚```fffžžž´´´»»»ººº¹¬¶Á¬Äv«ÃtªÃr©Âp¨Án§Àk¦Àk¦¿i¥½e¤¼b¢¹\ ¸Xž¯\™’~Œ………ŠŠŠ~~~YYYbbb•••¨¨¨¯¯¯²²²´´´µ¡¯Ãr©Âp¨Áo¨Àl§Àk¦¿i¥¾f¤½e¤¼c£»_¡¹[Ÿ¶U–zŽˆˆˆˆˆˆ•••”””wwwTTT___‰‰‰¡¡¡§§§«««®®®¸‚¦Áo¨Án§Àk¦¿j¦¾f¤½e¤½d£¼c£»_¡¹\ ·WžµQ›¤]‘ŠŠŠ——— ’’’oooTTTxxx™™™¡¡¡¥¥¥¨Ÿ¦¿j¦Àk¦¿i¥¾f¤½d£¼b¢»a¢»_¡»_¡¹\ ¸Xž¶Sœ³Nš³N™™Ž•¦¦¦¢¢¢†††___kkkŒŒŒšššžžž«¼c£½d£¼c£¼b¢»_¡º^¡¹] ¹] ¹[Ÿ·WžµRœ³Nš³Nš·Wž‚ŸžžžwwwVVVuuuŽŽŽ–•–µaž¹\ º^¡º^¡º^¡¹\ ¶`ž›‡•š†”³Y›µQ›°M™M™¶U¼c£½q¥¦¥¦ŒŒŒccc\\\zzz•Ž¶U·V·Wž·Wž¸Xž±b›—Œ“”””‘…«V•°M™¶Sœ»_¡¿i¥½d£—‚‘rrrVVV___`ƒ°M™³Nš´Oš´P›§c•‘‹ŽŽŽŠŠŠ‰‰‰ŽˆŒ¨f–¹\ ½d£¼c£¶U[VVVoHoD›J–£K––jŠŠ‰ŠŠŠŠ‰‰‰‰‰‰ŠŠŠŒŒŒ’’’š™š¨›º_¡µRœ’G’iCiq=q„R€s}‚‚‚‰‰‰‰‰‰‰‰‰ŠŠŠŽŽŽ‘‘‘•••———–––‹€‡„Rm;m]]]llluuu~~~………ˆˆˆ‰‰‰ŒŒŒŠŠŠ‰‰‰………{{{iiiTTTTTT]]]ooo{{{{{{ttteeeZZZRRRRRRRRRRRRRRRRRRRRRRRRDDDRRRRRRRRRX>XZDZRRRRRRRRRbbb~~~›››§§§¯“¦Àl§¿k¦¨”¢›››‘‘‘uuuYYYbbb‚‚‚§§§¼¼¼ºººº¸¹ÁzªÃtªÂq©»|¦®®®ªªªŸŸŸŒŒŒcccRRRRRRžžžÔÔÔÞÞÞÎÎÎËËËò¿Ç|®Åw«ÄuªÂq©° «®®®ªªª¡¡¡‰‰‰tttRRRRRR§§§äääøøøøøøæææØØØÇœ»É€±Æ{Äv«Ãtª·Œ©°°°«««¦¦¦¡¡¡ŒŒŒxxxRRR]]]šššéééüüüÿÿÿüüüïïïØ×ØÍŒ¹Ê„³Ç}®Æz¬Äv«Áw©±°±®®®ªªª§§§šššŠŠŠRRRQQQŽŽŽÔÔÔæææøøøþþþöööçççÑÅÏÍ‹¸Ê„³È°Æz¬Äv«Ãr©±¡¯¯¯¬¬¬¨¨¨žžž”””ŠŠŠuuuRRR†††ÃÃÃðððøøøîîîðððèèèßßßËÃ̉·Ê„³È~¯Æ{Äv«Ãtª¶Ž©¯¯¯¬¬¬¨¨¨¡¡¡———ŒŒŒ‚‚‚ffffffÉÉÉîîîÿÿÿÿÿÿîîîêêêáááÛÛÛÊ–ºË†µÉ‚²Ç}®Æ{Äv«Ãtª¾x§¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽ†††uuuRRRŸŸŸÑÑÑùùùÿÿÿùùùèèèãããÜÜÜÐÍÏˇ¶Ê„³È°Ç|®Æz¬Äv«Ãr©Áo¨¯£¬¬¬¬§§§¡¡¡™™™ŽŽŽˆˆˆfff\@\»…©Ë•»Ý³ÔÞ²ÕÙ§ÌÔ›ÄÒ•Àϼ͊¸Ë…´É€±Ç}®Æ{Åx¬ÄuªÂq©Áo¨Àl§¿j¦½d£»_¡¸YŸµR›¬N—œN‘‡N‡]@]_<_Æz¬Í‹¸ÛªÏÜ®ÑÖ ÇÒ•Àϼ̉·Ë†µÊƒ³È~¯Ç|®Æz¬Äv«ÃtªÂp¨Án§Àk¦¾f¤¼c£»_¡¹[ŸµRœM™˜I•’G’_<_RRR¼˜±Ì‡¶ÖŸÇÓ˜ÂÐ’½ÏŽ»Í‹¸Ë…´Êƒ³È°Ç}®Æz¬Åw«ÄuªÂq©Áo¨Àk¦¿j¦¾f¤¼b¢º^¡¹[ŸµRœ°M™˜I•˜a‰RQRRRR···Á¼ÍŽºÎºÍ‹¸Ì‰·Ê„³É‚²È°Ç}®Æ{Åx¬Äv«Ãr©Âp¨Án§Àk¦¿i¥½e¤»a¢¹\ ¸YŸµRœ±O™Ži„………RRRRRR±±±ÀÀÀžÃÈ•¹Ë†µÊ„³É€±Ç}®Ç|®Æz¬Åx¬Äv«ÃtªÂq©Áo¨Àk¦¿j¦¾f¤¼c£º^¡¹[Ÿ·Wž«\–Ž€Š‰‰‰‰‰‰```RRR¦¦¦¹¹¹¿¿¿ÁÀÁÀ ·Ç}®Ç|®Æz¬Åx¬Äv«ÄuªÃr©Âp¨Áo¨Àl§Àk¦¾f¤½d£»_¡¹[Ÿ·W¡r“ŒŠ‹………‘‘‘ŠŠŠ___RRRššš±±±···¹¹¹»»»¹¬µÁ¬Äv«ÄuªÃr©Âp¨Áo¨Án§Àk¦¿j¦¾f¤½d£»_¡¹\ ±`›—ƒ‘•••ƒƒƒRRRRRR‘‘‘¥¥¥¬¬¬°°°²²²´´´¶¢°Ãr©Âp¨Áo¨Àl§Àk¦¿j¦¿i¥¾f¤½e¤»a¢¹\ ¸Xž™}ŽŽŽŸŸŸšššxxxRRR ¦¦¦¨¨¨¬¬¬®®®¸‚¦Âp¨Àl§Àk¦¿i¥¾f¤½e¤½e¤½d£»a¢º^¡¸YŸ¶U¦a”‚‚‚”””¤¤¤¨¨¨”””lllZZZ–––ŸŸŸ¡¡¡¥¥¥¨ž¥¾i¥¿j¦¾f¤¾f¤¼c£¼b¢»a¢»_¡»_¡¹\ ¸YŸ·Wž´P›¬M˜•‰‘¥¥¥±±±¤¤¤RRR–––›››žžžª~œ»a¢¼b¢¼b¢»a¢»_¡º^¡¹] ¸\Ÿ¸YŸ·Wž¶SœM™¤K—·V®ƒŸ±±±±±±———lllRRR”””–•–´`¹[Ÿ¹[Ÿ¹\ ¹\ ¹\ ¶`ž›‡•š…“³Y›µQ›M™M™¶Sœ½d£¾u§¬ššš{{{RRRRRR‚‚‚–€µQ›µRœµRœ·V·V¯_š”ˆ’’’ŽŽŽŽŠªU•³Nš·V¼c£Àk¦¿i¥™„’ƒƒƒRRRRRR’c†¤K—¤K—ªL˜ªL˜¥`“‡‹ŠŠŠŠŠŠ‰‰‰‰‰‰‘Œªi˜»a¢¾f¤¼b¢·Wž–hŠRRR_<_‡A‡˜I•—I”™mŒ‘‘ŠŠŠ‰‰‰ŠŠŠ’’’šššœ›œ¨›¸\Ÿ³Nš‰B‰^;^b4bxLxxjtƒƒƒŒŒŒŒŒŒŽŽŽ‘‘‘‘‘‘‰‰‰ƒw€`;`b4bRRR```nnn{{{ƒƒƒrrriiiRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRc1cd-dd-dc1cRRRRRRrrr–––¥¥¥ª §Àl§Àk¦Àk¦½d£ž“š•••………ooo\\\uuu®®®ÉÉÉÎÎÎÐÐÐƯÀÉ€±Ç|®Åx¬Äv«´—«®®®¦¦¦”””|||cccWWWiii¤¤¤ÓÓÓëëëîîîìììæææ̦ˆµÈ°Ç|®Æz¬½Š¬´´´°°°¦¦¦———………nnnZZZnnn°°°âââôôôúúúöööñññàßàͺʃ³Ç}®Æz¬ÄuªÀr¨«¬¬¬¬¨¨¨¤¤¤™™™ˆˆˆttt___oooªªªÚÚÚòòòûûûûûûôôôêêêÓÊÒ̉·Êƒ³È~¯Æz¬Äv«Âq©± ¬®®®«««§§§ •••ŠŠŠxxx___nnn¤¤¤ÙÙÙíííñññôôôòòòêêêßßß˯Ä̇¶Êƒ³Ç}®Æz¬Äv«Ãr©µŽ©¯¯¯¬¬¬¨¨¨¢¢¢ššš………rrrZZZnnn¯¯¯ÙÙÙóóóúúúôôôíííèèèàààØØØÊ–ºË…´É€±Ç}®Æz¬Äv«Ãr©¼{§¯¯¯¬¬¬¨¨¨¤¤¤œœœ’’’‰‰‰~~~kkkTTTŠŠŠÇÇÇíííûûûûûûñññçççáááÚÚÚÏÊΈµÊƒ³È~¯Ç|®Æz¬ÄuªÂq©Áo¨®§¬¬¬¬§§§¢¢¢œœœ”””ŒŒŒƒƒƒuuu______«‹¡È¼Ü·ÕܲÓÜ®ÑסÈÓ™ÂÑ“¾ÎºÌ‡¶Ê„³É€±Ç|®Æz¬Åw«ÃtªÂp¨Áo¨Àk¦¿j¦½e¤»a¢¹\ ¶V±Q™§P“–RŠyUxWWW___²ˆ¤ÎºÛ«ÐÚ©ÎÙ§ÌÔšÃÐ’½ÎºÌ‡¶Ê„³É€±Ç}®Æ{Åx¬Äv«Ãr©Áo¨Án§Àk¦¾f¤½d£»_¡¹\ ·V´Oš¤K—•I•„T€WWW___¬¬¬Ñ·ËÚ¨ÍÙ§ÌÒ•ÀБ½Í‹¸Ì‡¶Ê„³É€±Ç}®Ç|®Åx¬Äv«ÃtªÂp¨Án§Àk¦¿i¥¾f¤¼b¢»_¡¹[Ÿ·V´P›§L˜–^‡wvwbbb]]]¦¦¦ÙÙÙÝÑÜÓœÄÍ‹¸Ì‡¶Ë…´Êƒ³È°Ç}®Æ{Æz¬Äv«ÃtªÂq©Áo¨Àl§¿j¦¾f¤½d£»a¢¹\ ¸YŸ¶U²P™“o‰‚‚‚zzzbbb]]] ÐÐÐßßßÍÇÌÇ‘¶Êƒ³È°Ç}®Ç|®Æz¬Åx¬Äv«ÃtªÂq©Áo¨Àl§Àk¦¿i¥½e¤¼b¢º^¡¹[Ÿ·Wž¬^—Ž€Šˆˆˆ‰‰‰bbbZZZ•••ÁÁÁÓÓÓÄÄľ½¾¾´Æ{Æz¬Åw«Äv«ÃtªÂq©Áo¨Áo¨Àl§Àk¦¿i¥¾f¤¼b¢»_¡¹[Ÿ¶V q“ŒŠ‹†††ŠŠŠ~~~bbbWWWŒŒŒ²²²Â³³³¶¶¶¸¸¸¶©³À~ªÃr©Âq©Áo¨Án§Àk¦Àk¦¿j¦¾f¤½e¤¼b¢»_¡¹[Ÿ±_š–‚ŒŒŒ………‰‰‰”””xxxYYY___ƒƒƒ¤¤¤®®®«««°°°±±±²«Áo¨Án§Àk¦¿j¦¾f¤¾f¤¾f¤½d£¼b¢»_¡¹\ ·Wž™}ŒŒŒˆˆˆ™™™žžžŽŽŽrrrYYYkkk”””¡¡¡¢¢¢¥¥¥¨¨¨«««¶¤Àk¦¿i¥¾f¤½d£¼b¢¼b¢»a¢»_¡º^¡¹[Ÿ·WžµRœ¥_’ŠŠŠ‘‘‘ §§§œœœbbbYYY‚‚‚’’’›››žžž¡¡¡£™ ¼c£¼c£¼c£»a¢»_¡º^¡¹\ ¹\ ¹[Ÿ¸Xž¶UµQ›°M™³N™—Œ“¢¢¢¬¬¬¨¨¨‘‘‘tttTTT```|||–––ššš§y™¹\ ¹\ ¹\ ¹\ ¹[Ÿ¸YŸ·Yž¶X¶UµQ›³NšM™³Nš·Wžž¬¬¬§§§———zzz\\\]]]wwwŠŠŠ‘Ž²VšµRœ¶Sœ·V·V¶U²Wš—‚”Ž°Q˜°M™°M™´P›¸Xž¼c£»l¤¡Ÿ¡”””zzz]]]ZZZrrrŽk…¤K—§L˜ªL˜°M™´Oš«W–ŽŠŠŠŠ‰‰‰€‰¬X–¶Sœ¸YŸ»_¡¼b¢¹\ ˜xuuu\\\WWW|H|ŠCŠ’G’›J–ªL˜¤a’Š„ˆ‰‰‰ŠŠŠŒŒŒŽŽŽ”“«k˜¹[Ÿ¹[ŸµQ›˜I•|H|YYYc6cp4p};}‡F‡‹d‚‚‚†††‰‰‰ŠŠŠŒŒŒŒŒŒŒŒŒŠŠŠ’nˆ™L’};}j1ja6aZHZYWYeeettt~~~~~~wwwooo\\\VUVXFXRRRRRR___ccc___YYYRRRRRR```fffeeebbbYYYRRRRRRRRRkkk‘‘‘ŸŸŸ¨¨¨¬¬¬€›}š˜žžž•••†††lllWWW†††¦¦¦¸¸¸¿¿¿¿¿¿²¸¸^™•V“¤ªª§§§žžžwwwZZZ~~~···ÐÐÐÚÚÚÙÙÙÐÐЖ´³]š–W• ž®®®ªªª rrrZZZuuu¾¾¾çççðððíííåååÕÖÖm¤¡\™•Y—‘^—’®¯¯¬¬¬¦¦¦žžžŒŒŒtttZZZeee³³³éééùùùúúúòòòããã¶ÅÅe¡_œ˜Z˜“X–—¨¨®®®ªªª¥¥¥šššˆˆˆqqqYYYWWW›››ÕÕÕñññûûûùùùíííÝÝÝ“·¶f¢ža™\™•Y—‘xŸœ°°°¬¬¬§§§ŸŸŸ’’’………lllVVVˆˆˆÈÈÈêêêñññôôôñññæææ×ØØt«©f¢ža™]š–Y—‘\–‘®¯¯¨¨¨¡¡¡———ŒŒŒ{{{___‚‚‚¾¾¾èèèøøøôôôíííçççÞÞÞ½ÉÉj¦£e¡a™]š–Z˜“V”Ž˜¨§¨¨¨¢¢¢™™™‚‚‚lllVVVŸŸŸÛÛÛ÷÷÷ýýýôôôéééâââÙÙÙ—¸¸g£ŸcŸ›_œ˜\™•Y—‘V”Ž{žœ¨¨¨¢¢¢ššš………uuu\\\{{{³³³âââúúúúúúîîîâââÛÛÛÓÓÓv«¨e¡a™^›—[˜”X–U“]”¬¬¬§§§¡¡¡ššš†††|||fffTTTxxx¹¹¹âââøøøõõõçççÜÜÜÕÕÕ¼ÇÇg£Ÿd œ_œ˜]š–Z˜“W•T’ŒRŠ˜¤¤¥¥¥ ššš‘‘‘ˆˆˆ~~~qqqTTT3a^]š–x³±ˆÀÀ„½½{µ³rªn©¦h¤¡e¡a™]š–[˜”Y—‘U“S‘‹Q‰NŠ„M‡‚KƒH{Cyu>qn8jg3a^&GE7`^\™•t¯¬¹·w²°q¬©n©¦h¤¡e¡bžš_œ˜\™•Z˜“W•T’ŒRŠPŽ‡NŠ„L†K‚~H~zCyu>qn9kh6fc,KI\ihY–n©¦v±¯n©¦k§¤h¤¡e¡bžš_œ˜]š–Z˜“X–U“T’ŒQ‰O†N‰ƒL…€I€|G}yCxt>qn:li7heJUTlll•¦¥l¦£n©¦i¥¢g£Ÿd œa™_œ˜]š–[˜”Y—‘V”ŽT’ŒRŠQ‰NŠ„MˆƒKƒH{E{wBws>qnArok{z___iii§§§²ººw©§bžša™_œ˜]š–\™•Z˜“Y—‘V”ŽT’ŒRŠQ‰O†N‰ƒL…€I€|F|xCxt?spHtq}ˆ‡‚‚‚```fffžžž´´´¸ºº…¦¥]™•[˜”Z˜“X–W•U“S‘‹Q‰Q‰O†N‰ƒL†I€|G}yCyu@tq[yw‡‰‰~~~YYYbbb•••¨¨¨¯¯¯²²²¬«dš–W•U“T’ŒRŠQ‰O†NŠ„N‰ƒM‡‚KƒH{DzvKzvtƒˆˆˆ•••”””wwwTTT___‰‰‰¡¡¡§§§«««®®®’¦¥T’ŒS‘‹Q‰PŽ‡NŠ„N‰ƒMˆƒM‡‚KƒI€|F|xBwsn‚€ŠŠŠ——— ’’’oooTTTxxx™™™¡¡¡¥¥¥¨¨¨j–“Q‰O†NŠ„MˆƒL†L…€KƒKƒI€|G}yCyu?spRzw———¦¦¦¢¢¢†††___kkkŒŒŒšššžžž“œœM‡‚MˆƒM‡‚L†KƒK‚~I€|I€|H{F|xCxt?sp?spF|x— žžžwwwVVVuuuŽŽŽ———jŠI€|K‚~K‚~K‚~I€|H{H~zG}yDzvBws?ro>qnDzvM‡‚x›™¦¦¦ŒŒŒccc\\\zzzŠŒŒH{xE{wF|xF|xG}yG}yI|y}ŒzŠ‰Ewt?ro?roCyuKƒO†QŠ„ŽrrrVVV___^sr?ro?sp@tqAvrBwsLzw‹ŠŠŠŠ|†…IwtCyuI€|MˆƒM‡‚Dzv[poVVV5VT4c`9kh:mj<olPyvƒ‡‡‰‰‰‰‰‰ŠŠŠŒŒŒ‹Z‡ƒKƒK‚~Cxt6gd2PN.QO2_\6gdTur†ˆˆ‰‰‰‰‰‰ŠŠŠŽŽŽ‘‘‘••••––a†ƒ=pm2_\,NK2QONdcuuu~~~………ˆˆˆ‰‰‰ŒŒŒŠŠŠ‰‰‰………{{{Ka`-HGTTT]]]ooo{{{{{{ttteeeZZZRRRRRRRRRRRRRRRRRRRRRRRRDDDRRRRRRRRR7HG:LKRRRRRRRRRbbb~~~›››§§§¤ªªU’ŒSŠ££›››‘‘‘uuuYYYbbb‚‚‚§§§¼¼¼ººº»»»‡¦¥X–V”Ž€Ÿ®®®ªªªŸŸŸŒŒŒcccRRRRRRžžžÔÔÔÞÞÞÎÎÎËËËÆÇÇhŸœ[˜”Y—‘^—’¯°°®®®ªªª¡¡¡‰‰‰tttRRRRRR§§§äääøøøøøøæææØØزÂÂd œ^›—Z˜“X–˜©©°°°«««¦¦¦¡¡¡ŒŒŒxxxRRR]]]šššéééüüüÿÿÿüüüïïïÛÛÛ“·¶g£Ÿa™]š–Z˜“y ž²²²®®®ªªª§§§šššŠŠŠRRRQQQŽŽŽÔÔÔæææøøøþþþöööçççØÙÙu¬ªg£ŸcŸ›]š–Z˜“]—’°±±¯¯¯¬¬¬¨¨¨žžž”””ŠŠŠuuuRRR†††ÃÃÃðððøøøîîîðððèèèßßß½ÉÉk§¤g£Ÿbžš^›—Z˜“X–™©¨¯¯¯¬¬¬¨¨¨¡¡¡———ŒŒŒ‚‚‚ffffffÉÉÉîîîÿÿÿÿÿÿîîîêêêáááÛÛÛ—¸¸i¥¢e¡a™^›—Z˜“X–|Ÿ¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽ†††uuuRRRŸŸŸÑÑÑùùùÿÿÿùùùèèèãããÜÜÜÓÓÓv«¨g£ŸcŸ›_œ˜]š–Z˜“W•^•‘¯¯¯¬¬¬§§§¡¡¡™™™ŽŽŽˆˆˆfffRRR´´´ÑÑÑúúúüüüðððãããÜÜÜÖÖÖ¼ÇÇh¤¡d œa™^›—\™•Y—‘V”ŽT’Œ›§§«««¥¥¥ ™™™‘‘‘ˆˆˆtttRRR3IHaœ˜s«©–ÈÉŸÌÍÁÀ„·¶|±¯q©§k¦£f¢žbžš_œ˜]š–Z˜“X–U“S‘‹Q‰NŠ„M‡‚KƒH{CxtAsp@mjGmj;LK;LK]š–j¦£€¹¸z´²t¯¬q¬©n©¦h¤¡f¢žcŸ›a™]š–[˜”Y—‘V”ŽT’ŒQ‰PŽ‡NŠ„L†K‚~H{Cxt?ro8jg:li6KJRRR„¤£e¡n©¦p«¨n©¦k§¤g£Ÿe¡cŸ›a™^›—\™•Z˜“W•U“S‘‹Q‰O†N‰ƒL…€I€|H~zCxt?sp8jgYxvQRRRRR±±±œ²²k¥¢j¦£i¥¢g£Ÿd œa™_œ˜]š–\™•Z˜“X–V”ŽT’ŒQ‰PŽ‡NŠ„M‡‚K‚~H{F|xAvr@spi€~‰‰‰```RRR¦¦¦¹¹¹¯¸¸r¤¡cŸ›a™_œ˜]š–\™•Z˜“Y—‘W•U“T’ŒRŠQ‰NŠ„MˆƒKƒH{F|xCyuLxuv€‘‘‘ŠŠŠ___RRRššš±±±···¶¸¸ˆ¨§]™•[˜”Z˜“Y—‘W•U“T’ŒS‘‹Q‰PŽ‡NŠ„MˆƒKƒI€|F|xDyua€~|~~•••ƒƒƒRRRRRR‘‘‘¥¥¥¬¬¬°°°²²²ž««e›—W•U“T’ŒRŠQ‰PŽ‡O†NŠ„N‰ƒL…€I€|G}yL|xyˆ†ŸŸŸšššxxxRRR ¦¦¦¨¨¨¬¬¬®®®’¦¥U“RŠQ‰O†NŠ„N‰ƒN‰ƒMˆƒL…€K‚~H~zDzvq†„‚‚‚”””¤¤¤¨¨¨”””lllZZZ–––ŸŸŸ¡¡¡¥¥¥§§§i•’PŽ‡NŠ„NŠ„M‡‚L†L…€KƒKƒI€|H~zF|xAvrQxu’’’¥¥¥±±±¤¤¤RRR–––›››žžž’››L…€L†L†L…€KƒK‚~I€|H{H~zF|xCyu>qn;nkF{w˜¡¡±±±±±±———lllRRR”””———jŒ‰H{H{I€|I€|I€|H{K~{I|yDzvBws>qn>qnCyuMˆƒ~ ž®®®ššš{{{RRRRRR‚‚‚JzvCxtCxtE{wE{wE{wI{wŒŠ‰Euq>qn?spE{wM‡‚Q‰[‘Œ”””ƒƒƒRRRRRRv}|;nk;nk=pm=pm?spHws}‡†ŠŠŠ‰‰‰|†…K{wF|xL…€NŠ„L†F|x{‚RRRJPO2_\8jg8jg>qnU{‚ˆ‡‰‰‰ŠŠŠ’’’’˜—Y‡ƒKƒH{?sp3`]JPO-HG.WU7b_^vt‚‚ŒŒŒŒŒŒŽŽŽ‘‘‘f}>mi$DB-HGQRR```nnn{{{ƒƒƒrrriiiRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR<LK@MMRRRRRRRRRrrr–––¥¥¥ªªªˆ¡ŸQ‰Q‰‹œ›œœœ•••………ooo\\\uuu®®®ÉÉÉÎÎÎÐÐÐÈÊÊl¤ _œ˜\™•kš²²²®®®¦¦¦”””|||cccWWWiii¤¤¤ÓÓÓëëëîîîìììæææµÇÇi¥¢cŸ›_œ˜]š– ¯®´´´°°°¦¦¦———………nnnZZZnnn°°°âââôôôúúúöööñññââ⊵³f¢ža™]š–Y—‘yžœ®®®¬¬¬¨¨¨¤¤¤™™™ˆˆˆttt___oooªªªÚÚÚòòòûûûûûûôôôêêê×ÙÙm¨¥f¢žbžš]š–Z˜“\–‘¯°°®®®«««§§§ •••ŠŠŠxxx___nnn¤¤¤ÙÙÙíííñññôôôòòòêêêßß߸ÈÈj¦£f¢ža™]š–Z˜“W•™©¨¯¯¯¬¬¬¨¨¨¢¢¢ššš………rrrZZZnnn¯¯¯ÙÙÙóóóúúúôôôíííèèèàààØØØ–··h¤¡d œa™]š–Z˜“W•} ž¯¯¯¬¬¬¨¨¨¤¤¤œœœ’’’‰‰‰~~~kkkTTTŠŠŠÇÇÇíííûûûûûûñññçççáááÚÚÚÒÒÒy«©f¢žbžš_œ˜]š–Y—‘V”Ž^•‘®®®¬¬¬§§§¢¢¢œœœ”””ŒŒŒƒƒƒuuu______¦¦¦ÏÏÏ÷÷÷øøø÷÷÷êêêàààÚÚÚÓÓÓÄÈÈh£Ÿd œ_œ˜]š–[˜”X–U“T’Œ–¥¤¨ªª¦¦¦¡¡¡›››•••ŒŒŒ………{{{lllWWW<VUQ‰u«˜ÉÉ™ÉÉ–Çlj»º€´²x®¬n¨¥i¤¡d œa™^›—\™•Z˜“W•T’ŒS‘‹Q‰NŠ„O‰ƒP†R„€S€}QzwPvsNqnHge;POY]]b”x³±‰ÁÁˆÀÀw²°s®«n©¦j¦£g£Ÿd œa™_œ˜\™•Z˜“X–U“S‘‹Q‰O†NŠ„L†KƒH{E{wAvr<ol8jg;gdS^]]]]£¥¥š¾¾„½½{µ³n©¦j¦£h¤¡f¢žcŸ›a™^›—]š–Z˜“X–V”ŽT’ŒRŠPŽ‡NŠ„MˆƒL…€I€|H~zDzv@tq<olQtquxxbbb]]] ÐÐмÍÍs«©g£Ÿf¢žcŸ›a™_œ˜]š–\™•Z˜“X–V”ŽT’ŒRŠQ‰O†N‰ƒL†K‚~H{F|xCxtAspi}‰‰‰bbbZZZ•••ÁÁÁÓÓÓ¶¾¾s£ _œ˜^›—]š–[˜”Z˜“X–V”ŽT’ŒT’ŒRŠQ‰O†NŠ„L†KƒH{E{wCxtNyvyƒ‚ŠŠŠ~~~bbbWWWŒŒŒ²²²Â³³³³µµ…¥¤[˜“Y—‘W•V”ŽT’ŒS‘‹Q‰Q‰PŽ‡NŠ„N‰ƒL†KƒH{E{wCxt`}‚„„‰‰‰”””xxxYYY___ƒƒƒ¤¤¤®®®«««°°°˜¨§]–‘T’ŒS‘‹Q‰PŽ‡NŠ„NŠ„NŠ„MˆƒL†KƒI€|F|xJ{wu†…ˆˆˆ™™™žžžŽŽŽrrrYYYkkk”””¡¡¡¢¢¢¥¥¥¨¨¨«««¢¡Q‰O†NŠ„MˆƒL†L†L…€KƒK‚~H{F|xCxto„‚ŠŠŠ‘‘‘ §§§œœœbbbYYY‚‚‚’’’›››žžž¡¡¡¢¢¢b‹M‡‚M‡‚L…€KƒK‚~I€|I€|H{G}yDzvBws?roRzw•••¢¢¢¬¬¬¨¨¨‘‘‘tttTTT```|||–––ššš––J€|I€|I€|I€|H{H~zF|xE{wDzvBws?sp>qn?spF|x–ŸŸ¬¬¬§§§———zzz\\\]]]wwwŠŠŠb„€CxtCyuE{wE{wDzvCyuGyvEwt?sp?ro?roAvrG}yM‡‚t—”¢¢¢”””zzz]]]ZZZrrr~€€?pm<ol=pm?ro@tq?spEuqz†…z…„Dtp@tqCyuH~zKƒL†M‚~ŠŒŒuuu\\\WWWQed3a^6gd9kh=pm?spFuq|†…ŠŠŠŒŒŒ‹ŠL}yG}yH{H{Bws8jgQedYYY-HG)MK.WU2_\6gdDpm|ƒ‚‰‰‰ŠŠŠŒŒŒŒŒŒ‚‰ˆIvs;nk7if.WU&IF,GE2JI)KI5XVmqq~~~~~~wwwimm0OM'IF2IHRRRRRR___ccc___YYYRRRRRR
\ No newline at end of file diff --git a/bubbob/images/extra6.ppm b/bubbob/images/extra6.ppm Binary files differnew file mode 100644 index 0000000..26dbee1 --- /dev/null +++ b/bubbob/images/extra6.ppm diff --git a/bubbob/images/extra7.ppm b/bubbob/images/extra7.ppm new file mode 100644 index 0000000..9482b77 --- /dev/null +++ b/bubbob/images/extra7.ppm @@ -0,0 +1,13 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +8 56 +255 +989USU.,-=;=×××äää»»»€312XXXÍÍÍ×××µµµˆˆˆYYY*)*535¤¤¤ªªª•••rqr978
/-/EDE.,.0.0dcdÖÖÖÖÕÖRPR«««ØØØÜÜܳ³³878 + +±±±³³³šššqppjhiƒƒƒsssUUU*)* + +utuigi¤£¤ÖÖÖäääZXZ-+,£££ÌÌÌ××ו”•|z{¤¤¤ªªª”“”onnzzzlll
?>????
.-.A@AGEFÓÓÓÅÅÅ›š›qopéééÓÓÓ¤¤¤423KIK···”””"!"‘‘‘ŒŒŒqpqHFG]]]535/// + +MKLcacA?AàààÖÖÖ¤££…„…ÚÚÚÒÒÒ¨¨¨767Ž®®®ªªª‰ˆ‰pppyyy#"# + +CCCJII%#$ECDÐÎÏÔÔÔ_]^/./²±²ÞÞÞÚÚÚ¬¬¬
poo›››µµµ´´´•”•
*()WWWuuu‡†‡trt&%&=;<mkl\[\645‚¶¶¶ãããÞÞÞ_]_%#$QQQ€€€ÏÏÏÌÌÌnnn,*,hgh£££ 312323
\ No newline at end of file diff --git a/bubbob/images/extra8.ppm b/bubbob/images/extra8.ppm Binary files differnew file mode 100644 index 0000000..af6288d --- /dev/null +++ b/bubbob/images/extra8.ppm diff --git a/bubbob/images/fire_drop.ppm b/bubbob/images/fire_drop.ppm Binary files differnew file mode 100644 index 0000000..dbb706b --- /dev/null +++ b/bubbob/images/fire_drop.ppm diff --git a/bubbob/images/fire_surface.ppm b/bubbob/images/fire_surface.ppm Binary files differnew file mode 100644 index 0000000..8ef46ee --- /dev/null +++ b/bubbob/images/fire_surface.ppm diff --git a/bubbob/images/fish_0.ppm b/bubbob/images/fish_0.ppm Binary files differnew file mode 100644 index 0000000..4f2040f --- /dev/null +++ b/bubbob/images/fish_0.ppm diff --git a/bubbob/images/flappy.ppm b/bubbob/images/flappy.ppm Binary files differnew file mode 100644 index 0000000..0724f84 --- /dev/null +++ b/bubbob/images/flappy.ppm diff --git a/bubbob/images/flapy_angry.ppm b/bubbob/images/flapy_angry.ppm Binary files differnew file mode 100644 index 0000000..4e71e8b --- /dev/null +++ b/bubbob/images/flapy_angry.ppm diff --git a/bubbob/images/game_over_0.ppm b/bubbob/images/game_over_0.ppm Binary files differnew file mode 100644 index 0000000..d76ecae --- /dev/null +++ b/bubbob/images/game_over_0.ppm diff --git a/bubbob/images/ghost.ppm b/bubbob/images/ghost.ppm new file mode 100644 index 0000000..424668e --- /dev/null +++ b/bubbob/images/ghost.ppm @@ -0,0 +1,6 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +32 256 +255 +ßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞßáÞÕ×ÔÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÕ×ÔöøõßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞßáÞÕ×ÔÈÊÇÈÊÇÕ×ÔöøõöøõëîêëîêëîêöøõöøõöøõöøõöøõëîêíÖØìt‰ír‡özŽ÷~ðtŠíiŒÚ¾ÅÕ×Ôöøõöøõëîêëîêöøõöøõöøõöøõöøõöøõëãâõr‘íjír‡özŽ÷~ðtŠïd‰ñ`ÙÅÉßáÞöøõëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêø£·õe‚ïs‰ë‡•ð’œõ˜ó‘îc‰í?|îQØÆÉÕÔÒöøõëîêëîêëîêöøõöøõöøõöøõëîêëîêëîêõzðfîë› è¬î¡§ï{“ên‚çIrê<oíW…Ô¡¯öøõëîêëîêëîêëîêëîêëîêëîêëîêßáÞßáÞõníl쇒êÇÁëÔÍìm|ëbvèMmæ9jé0oí-r§©¦ëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×ÔðuŠëf|ó£©ïØÒîéãð\qðVvëDlå.gæ fâ f§©¦ßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇ爚ëYsïi|ó£©ðåØîîçíGjëGkè8hå%då_áZ§©¦²´°ßáÞßáÞÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»Ò°³ò\tí_{ï^vïìÞó£©æ8fæ8få/bå WáIà<²´°²´°Õ×ÔÕ×ÔÈÊDz´°²´°¼¾»ÈÊÇÕ×ÔÈÊDz´°²´°¼¾»â~ìXuëQlïHgë9eå0då,cä'[áEÚ(Ö¼¾»¼¾»ÈÊÇÈÊǼ¾»²´°²´°²´°¼¾»ÈÊǼ¾»¼¾»¼¾»¼¾»¿·¶á\}ìIní<hè/bå&_ä ZâMÝ:ØÓÈÊǼ¾»¼¾»²´°¼¾»¼¾»²´°¼¾»ÈÊÇÈÊÇÈÊǼ¾»¿µµßj‰ì1fç#]åXáLÛ0ÛÝ,‘Õ³±ÈÊÇÈÊǧ©¦²´°²´°ÈÊÇÈÊǼ¾»¼¾»²´°²´°Á’™“6¾JÏGÖ/JÙfgÞÒÐÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°²´°ßáÞßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÈÊǧ©¦§©¦ßáÞßáÞßáÞßáÞßáÞÕ×ÔÈÊÇÈÊÇßáÞßáÞßáÞÕ×ÔÈÊÇÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞöøõöøõöøõÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔöøõëîêëîêßáÞëîêëîêöøõëîê¼¾»¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÈÊǼ¾»ÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°ëîêÕ×Ô¼¾»²´°²´°ÈÊÇÈÊÇÕ×ÔÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÈÊǼ¾»²´°Õ×ÔÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»Õ×ÔÕ×ÔÈÊǼ¾»¼¾»ÈÊÇÈÊÇÈÊDz´°²´°²´°¼¾»¼¾»¼¾»ÈÊÇÕ×ÔÈÊÇÈÊÇÈÊÇÈÊǼ¾»¼¾»²´°²´°ßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»²´°²´°§©¦§©¦²´°²´°²´°²´°¼¾»²´°²´°²´°§©¦ßáÞÈÊǼ¾»¼¾»¼¾»²´°§©¦§©¦²´°²´°²´°Õ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÕ×ÔöøõßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞßáÞÕ×ÔÈÊÇÈÊÇÕ×Ôöøõöøõëîêëîêëîêöøõöøõöøõöøõöøõëîêëëè襨ê|…ñn|ôwŒç Ѭ²Õ×ÔÕ×Ôöøõöøõëîêëîêöøõöøõöøõöøõöøõöøõëëèô‡›îj}íq…õy÷}Žðt‰ìd†æ}œÖÍÎßáÞöøõëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêø¸Æõeïs‰ë†•ð’œõ˜ó‘îc‰ì?|íP€ÙÀÅÕ×Ôöøõëîêëîêëîêöøõöøõöøõöøõëîêëîêëîêõ”ðfîë› è¬î¡§ï{“ên‚çIrê<oîO€Ï±¸öøõëîêëîêëîêëîêëîêëîêëîêëîêßáÞßáÞölíl쇒êÇÁëÔÍìm|ëbvèMmæ9jé0oê6w§©¦ëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×Ôòj‚ëf|ó£©ïØÒîéãð\qðVvëDlå.gæ fâ f§©¦ßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇëyëYsïi|ó£©ðåØîîçíGjëGkè8hå%då_áZ§©¦²´°ßáÞßáÞÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»Ö©ò\tí_{ï^vïìÞó£©æ8fæ8få/bå WáIà<²´°²´°Õ×ÔÕ×ÔÈÊDz´°²´°¼¾»ÈÊÇÕ×ÔÈÊDz´°²´°¼¾»äzŽìXuëQlïHgë9eå0då,cä'[áEÚ(Ö¼¾»¼¾»ÈÊÇÈÊǼ¾»¼¾»²´°¼¾»¼¾»ÈÊǼ¾»¼¾»¼¾»¼¾»¾¹¸Þd‚ìIní<hè/bå&_ä ZâMÝ:ØÓÈÊǼ¾»¼¾»¼¾»¼¾»ÈÊÇÈÊǼ¾»¼¾»²´°¼¾»ÈÊÇÈÊÇÈÊǼ¾»½¹¸Ùw‘ì1fç#]åXáLÛ0ÛÝ,‘Õ³±ÈÊÇÈÊDz´°¼¾»ÈÊÇÕ×Ô¼¾»ÈÊÇÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°½›ŸÓZyÝ0eÞOÕ!?Ø]^ÞÐÎÕ×ÔÕ×ÔÈÊÇÕ×ÔßáÞßáÞëîêßáÞÈÊDz´°²´°²´°²´°²´°¼¾»Õ×ÔßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÈÊÇÈÊǼ¾»²´°²´°Õ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÈÊÇÈÊÇÕ×Ô¼¾»ÈÊÇÈÊÇÕ×ÔßáÞßáÞÕ×ÔÈÊÇÕ×ÔßáÞöøõöøõöøõÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞöøõöøõöøõöøõöøõöøõëîêëîê²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔöøõëîêëîêßáÞëîêëîêßáÞöøõëîêëîêëîê¼¾»²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÈÊǼ¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔëîêëîêßáÞÕ×ÔÕ×ÔßáÞÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°Õ×Ô¼¾»²´°²´°ÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÈÊǼ¾»²´°ÈÊÇÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔÈÊǼ¾»¼¾»ÈÊÇÈÊÇÈÊDz´°²´°²´°¼¾»¼¾»¼¾»ÈÊÇÕ×ÔÈÊÇÈÊÇÈÊÇÈÊǼ¾»¼¾»²´°²´°ßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»²´°²´°§©¦§©¦²´°²´°²´°²´°¼¾»²´°²´°²´°§©¦ßáÞÈÊǼ¾»¼¾»¼¾»²´°§©¦§©¦²´°²´°²´°Õ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÕ×ÔöøõßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞßáÞÕ×ÔÈÊÇÈÊÇÕ×Ôöøõöøõëîêëîêëîêöøõöøõöøõöøõöøõëîêëëè襨ê|…ñn|ôwŒç Ѭ²Õ×ÔÕ×Ôöøõöøõëîêëîêöøõöøõöøõöøõöøõöøõëëèô‡›îj}íq…õy÷}Žðt‰ìd†æ}œÖÍÎßáÞöøõëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêø¸Æõeïs‰ë†•ð’œõ˜ó‘îc‰ì?|íP€ÙÀÅÕ×Ôöøõëîêëîêëîêöøõöøõöøõöøõëîêëîêëîêõ”ðfîë› è¬î¡§ï{“ên‚çIrê<oîO€Ï±¸öøõëîêëîêëîêëîêëîêëîêëîêëîêßáÞßáÞölíl쇒êÇÁëÔÍìm|ëbvèMmæ9jé0oê6w§©¦ëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×Ôòj‚ëf|ó£©ïØÒîéãð\qðVvëDlå.gæ fâ f§©¦ßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇëyëYsïi|ó£©ðåØîîçíGjëGkè8hå%då_áZ§©¦²´°ßáÞßáÞÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»Ö©ò\tí_{ï^vïìÞó£©æ8fæ8få/bå WáIà<²´°²´°Õ×ÔÕ×ÔÈÊDz´°²´°¼¾»ÈÊÇÕ×ÔÈÊDz´°²´°¼¾»äzŽìXuëQlïHgë9eå0då,cä'[áEÚ(Ö¼¾»¼¾»ÈÊÇÈÊǼ¾»²´°²´°²´°¼¾»ÈÊǼ¾»¼¾»¼¾»¼¾»¾¹¸Þd‚ìIní<hè/bå&_ä ZâMÝ:ØÓÈÊǼ¾»¼¾»²´°¼¾»¼¾»²´°¼¾»ÈÊÇÈÊÇÈÊǼ¾»½¹¸Ùw‘ì1fç#]åXáLÛ0ÛÝ,‘Õ³±ÈÊÇÈÊǧ©¦²´°²´°ÈÊÇÈÊǼ¾»¼¾»²´°²´°½›ŸŒ3ÃKÙKÖ#@Ø]^ÞÐÎÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°²´°ßáÞßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÈÊǧ©¦§©¦ßáÞßáÞßáÞßáÞßáÞÕ×ÔÈÊÇÈÊÇßáÞßáÞßáÞÕ×ÔÈÊÇÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞöøõöøõöøõÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔöøõëîêëîêßáÞëîêëîêöøõëîê¼¾»¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÈÊǼ¾»ÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°ëîêÕ×Ô¼¾»²´°²´°ÈÊÇÈÊÇÕ×ÔÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÈÊǼ¾»²´°Õ×ÔÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»Õ×ÔÕ×ÔÈÊǼ¾»¼¾»ÈÊÇÈÊÇÈÊDz´°²´°²´°¼¾»¼¾»¼¾»ÈÊÇÕ×ÔÈÊÇÈÊÇÈÊÇÈÊǼ¾»¼¾»²´°²´°ßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»²´°²´°§©¦§©¦²´°²´°²´°²´°¼¾»²´°²´°²´°§©¦ßáÞÈÊǼ¾»¼¾»¼¾»²´°§©¦§©¦²´°²´°²´°Õ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÕ×ÔöøõßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞßáÞÕ×ÔÈÊÇÈÊÇÕ×Ôöøõöøõëîêëîêëîêöøõöøõöøõöøõöøõëîêëäá雟ê}†ðt‚òƒ–啥ε¸Õ×ÔÕ×ÔöøõöøõëîêëîêöøõöøõöøõöøõöøõöøõëäâõsŒîj}íq…õy÷}Žðt‰ìd†æ}œÖÍÎßáÞöøõëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêø§ºõeïs‰ë†•ð’œõ˜ó‘îc‰ì?|íP€ÙÀÅÕ×Ôöøõëîêëîêëîêöøõöøõöøõöøõëîêëîêëîêõ}‘ðfîë› è¬î¡§ï{“ên‚çIrê<oîO€Ï±¸öøõëîêëîêëîêëîêëîêëîêëîêëîêßáÞßáÞöm€íl쇒êÇÁëÔÍìm|ëbvèMmæ9jé0oê6w§©¦ëîêßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞÕ×Ôño†ëf|ó£©ïØÒîéãð\qðVvëDlå.gæ fâ f§©¦ßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊÇëzëYsïi|ó£©ðåØîîçíGjëGkè8hå%då_áZ§©¦²´°ßáÞßáÞÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»ÚŸ¦ò\tí_{ï^vïìÞó£©æ8fæ8få/bå WáIà<²´°²´°Õ×ÔÕ×ÔÈÊDz´°²´°¼¾»ÈÊÇÕ×ÔÈÊDz´°²´°¼¼¹çtŠìXuëQlïHgë9eå0då,cä'[áEÚ(Ö¼¾»¼¾»ÈÊÇÈÊǼ¾»§©¦ÈÊÇÈÊǼ¾»¼¾»¼¾»ÁµµÜj†ìJoí=jè0cå&_ä ZâMÝ:ØÓ
ÈÊǼ¾»²´°²´°ÈÊÇÈÊÇÈÊÇÈÊǼ¾»¼¾»Ì—¦ÞQ€æ%däYáMÛ0ÚÝ/’Õ¬ÈÊÇÈÊǼ¾»¼¾»²´°²´°!U%q(nÚÞØÖÕ×ÔÕ×ÔÈÊǧ©¦§©¦ßáÞßáÞßáÞßáÞÕ×ÔÕ×ÔÈÊÇÕ×ÔßáÞßáÞÕ×ÔÈÊÇÈÊÇßáÞßáÞßáÞÕ×ÔÈÊÇÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔëîêëîêßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇëîêßáÞÈÊǼ¾»ÈÊÇÕ×ÔÕ×ÔÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊǼ¾»²´°²´°ëîêÕ×Ô¼¾»²´°²´°ÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»Õ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊǼ¾»²´°ëîêÕ×ÔÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊDz´°¼¾»¼¾»¼¾»¼¾»Õ×ÔÕ×ÔÕ×ÔÈÊÇÈÊǼ¾»¼¾»¼¾»¼¾»Õ×ÔÕ×ÔÈÊǼ¾»¼¾»ÈÊÇÈÊǼ¾»²´°²´°²´°¼¾»¼¾»¼¾»ÈÊÇÕ×ÔÈÊÇÈÊÇÈÊÇÈÊǼ¾»¼¾»²´°²´°ßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»²´°²´°§©¦§©¦²´°²´°²´°²´°¼¾»²´°²´°²´°§©¦ßáÞÈÊǼ¾»¼¾»¼¾»²´°§©¦§©¦²´°²´°²´°ÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞëîêÕ×ÔÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîêÕ×ÔÕ×ÔÈÊÇÕ×ÔßáÞßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞöøõßáÞÕ×ÔØ´¸âŒ›òsŽöl„ê{†å¥¨ßßÜëîêöøõöøõöøõöøõöøõëîêëîêëîêöøõöøõßáÞß××èžëd…ït‰÷}öyŽíq†íj}ð†šëìéëîêöøõöøõöøõöøõöøõëîêëîêöøõöøõÕÖÓÙÀÅîR‚ì?{îc‰ó‘õ˜ð’œë†•ïs‰õeñ¶ÃëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêöøõÏ°¸îPê<oçIrên‚ï{“è¬ë› îðfô‚•ßáÞëîêëîêöøõöøõöøõöøõëîêëîêëîêöøõ§©¦ê6wé0oæ9jèMmëbvìm|ëÔÍêÇÁ쇒ílõl€ßáÞßáÞëîêëîêëîêëîêëîêëîêëîêëîêöøõ§©¦â fæ få.gëDlðVvð\qîéãïØÒó£©î€‹ëf|óg€Õ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîê²´°§©¦áZå_å%dè8hëGkíGjîîçðåØó£©ïi|ëYsìvŒÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞ²´°²´°à<áIå Wå/bæ8fæ8fó£©ïìÞï^uí_zò\sؤ©¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔßáÞßáÞ¼¾»¼¾»ÖÚ(áEä'[å,cå0dë9eïHgìRkíYræw‡¼½º²´°²´°ÈÊÇÕ×ÔÈÊǼ¾»²´°²´°ÈÊÇÕ×ÔÕ×Ô¼¾»ÈÊÇÓØÞ<âMä Zå&_è/bí;gîJlâg}¾¸¶¼¾»¼¾»¼¾»¼¾»ÈÊǼ¾»¼¾»²´°²´°¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÔ²®à6”ÜÛ.áJäWæ!^è+eØsŽ¾¸·¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»ÈÊǼ¾»²´°²´°ÈÊÇÕ×ÔÕÖÓßÒ×Úe[Ô$3ÙCà7k„5»™ ²´°²´°¼¾»¼¾»ÈÊÇÈÊǼ¾»¼¾»²´°ÈÊÇÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞ²´°²´°§©¦²´°ÈÊÇÈÊÇÕ×ÔßáÞßáÞÕ×ÔßáÞßáÞ§©¦§©¦ßáÞÕ×ÔÈÊÇÕ×ÔÕ×ÔßáÞÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇöøõöøõöøõÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»²´°ëîêöøõëîêëîêßáÞëîêëîêöøõ²´°²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊǼ¾»ÈÊÇßáÞëîꧩ¦²´°¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊÇÕ×ÔÈÊÇÈÊDz´°²´°¼¾»Õ×ÔßáÞ²´°¼¾»¼¾»¼¾»ÈÊÇÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊDz´°²´°¼¾»¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÈÊǼ¾»²´°²´°²´°§©¦²´°ÈÊÇÈÊÇÈÊǼ¾»¼¾»ÈÊÇÕ×ÔÕ×Ô§©¦§©¦²´°²´°¼¾»²´°²´°²´°²´°§©¦§©¦§©¦²´°¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞ§©¦²´°²´°§©¦§©¦²´°¼¾»¼¾»²´°¼¾»Õ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞëîêÕ×ÔÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîêÕ×ÔÕ×ÔÈÊÇÕ×ÔßáÞßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞöøõßáÞÕ×ÔØ´¸âŒ›òsŽöl„ê{†å¥¨ßßÜëîêöøõöøõöøõöøõöøõëîêëîêëîêöøõöøõßáÞß××èžëd…ït‰÷}öyŽíq†íj}ð†šëìéëîêöøõöøõöøõöøõöøõëîêëîêöøõöøõÕÖÓÙÀÅîR‚ì?{îc‰ó‘õ˜ð’œë†•ïs‰õeñ¶ÃëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêöøõÏ°¸îPê<oçIrên‚ï{“è¬ë› îðfô‚•ßáÞëîêëîêöøõöøõöøõöøõëîêëîêëîêöøõ§©¦ê6wé0oæ9jèMmëbvìm|ëÔÍêÇÁ쇒ílõl€ßáÞßáÞëîêëîêëîêëîêëîêëîêëîêëîêöøõ§©¦â fæ få.gëDlðVvð\qîéãïØÒó£©î€‹ëf|óg€Õ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîê²´°§©¦áZå_å%dè8hëGkíGjîîçðåØó£©ïi|ëYsìvŒÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞ²´°²´°à<áIå Wå/bæ8fæ8fó£©ïìÞï^uí_zò\sؤ©¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔßáÞßáÞ¼¾»¼¾»ÖÚ(áEä'[å,cå0dë9eïHgìRkíYræw‡¼½º²´°²´°ÈÊÇÕ×ÔÈÊǼ¾»²´°²´°ÈÊÇÕ×ÔÕ×Ô¼¾»ÈÊÇÓØÞ<âMä Zå&_è/bí;gîJlâg}¾¸¶¼¾»¼¾»¼¾»¼¾»ÈÊǼ¾»¼¾»²´°¼¾»¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÔ²®à6”ÜÛ.áJäWæ!^è+eØsŽ¾¸·¼¾»ÈÊÇÈÊÇÈÊǼ¾»²´°²´°¼¾»¼¾»ÈÊǼ¾»¼¾»¼¾»ÈÊÇÕ×ÔÕÖÓßÒ×Úe[Ô$3ÙCà7kÞjŽ»™ ²´°²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÈÊDz´°Õ×ÔÈÊǼ¾»²´°ÈÊÇÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞÕ×Ô¼¾»²´°²´°²´°²´°¼¾»Õ×ÔëîêßáÞßáÞÕ×ÔÈÊÇÈÊÇÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔßáÞ§©¦²´°²´°¼¾»ßáÞÕ×ÔÈÊÇÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊDz´°¼¾»ßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔöøõöøõöøõÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»²´°Õ×ÔßáÞöøõöøõöøõöøõöøõöøõÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»²´°¼¾»ëîêëîêëîêëîêßáÞëîêëîêßáÞëîêëîêöøõ²´°²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇßáÞÕ×ÔÕ×ÔßáÞëîêëîêÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»ÈÊÇÕ×ÔßáÞ§©¦²´°¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔßáÞßáÞÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊDz´°²´°¼¾»ÈÊDz´°¼¾»¼¾»¼¾»ÈÊÇÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊDz´°²´°¼¾»¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÈÊǼ¾»²´°²´°²´°§©¦²´°ÈÊÇÈÊÇÈÊǼ¾»¼¾»ÈÊÇÕ×ÔÕ×Ô§©¦§©¦²´°²´°¼¾»²´°²´°²´°²´°§©¦§©¦§©¦²´°¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞ§©¦²´°²´°§©¦§©¦²´°¼¾»¼¾»²´°¼¾»Õ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞëîêÕ×ÔÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîêÕ×ÔÕ×ÔÈÊÇÕ×ÔßáÞßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞöøõßáÞÕ×ÔØ´¸âŒ›òsŽöl„ê{†å¥¨ßßÜëîêöøõöøõöøõöøõöøõëîêëîêëîêöøõöøõßáÞß××èžëd…ït‰÷}öyŽíq†íj}ð†šëìéëîêöøõöøõöøõöøõöøõëîêëîêöøõöøõÕÖÓÙÀÅîR‚ì?{îc‰ó‘õ˜ð’œë†•ïs‰õeñ¶ÃëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêöøõÏ°¸îPê<oçIrên‚ï{“è¬ë› îðfô‚•ßáÞëîêëîêöøõöøõöøõöøõëîêëîêëîêöøõ§©¦ê6wé0oæ9jèMmëbvìm|ëÔÍêÇÁ쇒ílõl€ßáÞßáÞëîêëîêëîêëîêëîêëîêëîêëîêöøõ§©¦â fæ få.gëDlðVvð\qîéãïØÒó£©î€‹ëf|óg€Õ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîê²´°§©¦áZå_å%dè8hëGkíGjîîçðåØó£©ïi|ëYsìvŒÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞ²´°²´°à<áIå Wå/bæ8fæ8fó£©ïìÞï^uí_zò\sؤ©¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔßáÞßáÞ¼¾»¼¾»ÖÚ(áEä'[å,cå0dë9eïHgìRkíYræw‡¼½º²´°²´°ÈÊÇÕ×ÔÈÊǼ¾»²´°²´°ÈÊÇÕ×ÔÕ×Ô¼¾»ÈÊÇÓØÞ<âMä Zå&_è/bí;gîJlâg}¾¸¶¼¾»¼¾»¼¾»¼¾»ÈÊǼ¾»¼¾»²´°²´°¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÔ²®à6”ÜÛ.áJäWæ!^è+eØsŽ¾¸·¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»ÈÊǼ¾»²´°²´°ÈÊÇÕ×ÔÕÖÓßÒ×Úe[Ô$3ÙCà7k„5»™ ²´°²´°¼¾»¼¾»ÈÊÇÈÊǼ¾»¼¾»²´°ÈÊÇÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞ²´°²´°§©¦²´°ÈÊÇÈÊÇÕ×ÔßáÞßáÞÕ×ÔßáÞßáÞ§©¦§©¦ßáÞÕ×ÔÈÊÇÕ×ÔÕ×ÔßáÞÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇöøõöøõöøõÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊǼ¾»²´°ëîêöøõëîêëîêßáÞëîêëîêöøõ²´°²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÈÊǼ¾»ÈÊÇßáÞëîꧩ¦²´°¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊÇÕ×ÔÈÊÇÈÊDz´°²´°¼¾»Õ×ÔßáÞ²´°¼¾»¼¾»¼¾»ÈÊÇÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÈÊǼ¾»¼¾»¼¾»¼¾»¼¾»ÈÊÇÈÊÇÈÊǼ¾»¼¾»¼¾»ÈÊÇÈÊDz´°²´°¼¾»¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÈÊǼ¾»²´°²´°²´°§©¦²´°ÈÊÇÈÊÇÈÊǼ¾»¼¾»ÈÊÇÕ×ÔÕ×Ô§©¦§©¦²´°²´°¼¾»²´°²´°²´°²´°§©¦§©¦§©¦²´°¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞ§©¦²´°²´°§©¦§©¦²´°¼¾»¼¾»²´°¼¾»Õ×ÔÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞëîêÕ×ÔÕ×ÔÕ×ÔßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîêÕ×ÔÕ×ÔÈÊÇÕ×ÔßáÞßáÞßáÞßáÞëîêëîêëîêëîêëîêëîêßáÞßáÞßáÞöøõßáÞÕ×Ô×½¿âžðwôqˆê€‹å©«ßÝÚëîêöøõöøõöøõöøõöøõëîêëîêëîêöøõöøõßáÞß××è‚Ÿêd…ït‰÷}öyŽíq†íj}ò”ëéæëîêöøõöøõöøõöøõöøõëîêëîêöøõöøõÕÖÓÙÀÅîR‚ì?{îc‰ó‘õ˜ð’œë†•ïs‰õeó£µëîêëîêöøõöøõöøõöøõöøõöøõëîêëîêöøõÏ°¸îPê<oçIrên‚ï{“è¬ë› îðföp‡ßáÞëîêëîêöøõöøõöøõöøõëîêëîêëîêöøõ§©¦ê6wé0oæ9jèMmëbvìm|ëÔÍêÇÁ쇒íl÷cxßàÝßáÞëîêëîêëîêëîêëîêëîêëîêëîêöøõ§©¦â fæ få.gëDlðVvð\qîéãïØÒó£©î€‹ëf|ôa{ÕÕÒßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞßáÞëîê²´°§©¦áZå_å%dè8hëGkíGjîîçðåØó£©ïi|ëYsìuŒÈÊÇÕ×ÔÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞßáÞ²´°²´°à<áIå Wå/bæ8fæ8fó£©ïìÞï^uí_zò\sᬲ¼¾»ÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔßáÞßáÞ¼¾»¼¾»ÖÚ(áEä'[å,cå0dë9eïHgìRkíYr¼½º²´°²´°ÈÊÇÕ×ÔÈÊÇÈÊDz´°²´°ÈÊÇÕ×ÔÕ×Ô¼¾»ÈÊÇÓØÞ<âMä Zå&_è/bí;hîKlèt‰¾¹·¼¾»²´°¼¾»ÈÊÇÕ×Ô§©¦¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÔ©¥àC˜ÝÜ0áKåUæ"[å7nЉœ¼»¹¼¾»¼¾»ÈÊÇÈÊÇÈÊǧ©¦²´°ÈÊÇÕ×ÔÙ ¾à†¶Ü Õ ÝFáK§>A +¼š¢º¤§¼¾»¼¾»ÈÊÇÕ×ÔÕ×ÔßáÞÕ×ÔßáÞßáÞ²´°²´°ÈÊÇÈÊÇÕ×ÔßáÞÕ×ÔÕ×ÔßáÞÕ×ÔÈÊÇÕ×ÔßáÞßáÞÕ×ÔßáÞßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×ÔÕ×ÔÕ×ÔßáÞëîêëîê²´°²´°¼¾»ÈÊÇÕ×ÔÕ×ÔÈÊÇÈÊÇÕ×ÔÕ×Ô¼¾»Õ×ÔßáÞÈÊǼ¾»ÈÊÇßáÞëîꧩ¦²´°¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×Ô¼¾»ÈÊÇÈÊÇÕ×ÔÕ×ÔÈÊDz´°²´°¼¾»Õ×Ôëîê²´°¼¾»¼¾»¼¾»ÈÊÇÈÊÇÕ×ÔÕ×ÔÕ×Ô¼¾»¼¾»¼¾»¼¾»²´°ÈÊÇÈÊǼ¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞ²´°²´°¼¾»¼¾»ÈÊÇÈÊÇÈÊÇÈÊÇÕ×ÔÈÊǼ¾»²´°¼¾»²´°²´°²´°¼¾»ÈÊÇÈÊǼ¾»¼¾»ÈÊÇÕ×ÔÕ×Ô§©¦§©¦²´°²´°¼¾»²´°²´°²´°²´°§©¦§©¦§©¦²´°¼¾»¼¾»¼¾»¼¾»ÈÊÇÕ×ÔßáÞ§©¦²´°²´°§©¦§©¦²´°¼¾»²´°²´°ÈÊÇÕ×Ô
\ No newline at end of file diff --git a/bubbob/images/ghosty.ppm b/bubbob/images/ghosty.ppm Binary files differnew file mode 100644 index 0000000..52ff8d3 --- /dev/null +++ b/bubbob/images/ghosty.ppm diff --git a/bubbob/images/ghosty_angry.ppm b/bubbob/images/ghosty_angry.ppm Binary files differnew file mode 100644 index 0000000..95f533e --- /dev/null +++ b/bubbob/images/ghosty_angry.ppm diff --git a/bubbob/images/glue.ppm b/bubbob/images/glue.ppm new file mode 100644 index 0000000..0723e34 --- /dev/null +++ b/bubbob/images/glue.ppm @@ -0,0 +1,4 @@ +P6 +32 32 +255 +áÝÝáÝÝÊ#áÝÝáÝÝÔ#Ô#áÝÝáÝÝÔ#Ô#áÝÝáÝÝè"Ü#Ü#áÝÝáÝÝÿ86ÿ86ÿ86è&Ü#Ü#è"áÝÝáÝÝÿ86ÿ86ÿCCÿCCÿCCí#è&Ü#è"ÿ86áÝÝáÝÝÿ86ÿ86ÿCCÿCCÿ86ÿ86í#è&è"Ü#è"ÿ86ÿCCáááÝÙÙÿ86ÿ86ÿCCÿ86ÿ86è&è&è&è"è"è"è"ÿ86ÿCCÿ86ÝÙÙÿ86ÿ86ÿCCÿ86è"è&è&Ü#Ü#Ü#è"è"è"è"ÿ86ÿ86ÿ86ÿ86ÿCCÿ86è"í#Ü#Ü#Ü#Ô#Ô#Ô#Ô#è"ÿ86ÿ86ÿ86ÿ86ÿCCÿ86è"è&Ü#Ü#Ü#Ô#Ô#Ê#Ê#Ä#¼#¼#ÿ86ÿ86ÿCCÿ86è"è&Ü#Ü#Ô#Ô#Ô#Ê#Ä#¼#¼#ÿ86ÿ86ÿCCÿ86è"è&Ü#Ü#Ô#Ô#Ê#Ä#¼#¼#ÿ86ÿ86ÿCCÿ86è"è&Ü#Ü#Ô#Ô#Ê#Ä#¼#ÿ86ÿ86ÿCCÿ86è"è&Ü#Ü#Ô#Ô#Ê#Ä#¼#æææÿ86ÿCCÿ86è"è&Ü#Ü#Ô#Ô#Ê#Ä#¼#æææÿÿÿÿ96è"è&Ü#Ü#Ô#Ô#Ê#Ä#¼#æææÿÿÿæææè&Ü#Ü#Ô#Ô#Ê#Ä#¼#ÿÿÿÿÿÿæéêáááÜ#Ô#Ô#Ê#Ä#¼#ÿÿÿæéêáááááá×××Ô#Ê#Ä#¼#¾ÀÂæéêááá×××××××××Ä#¼#¾À³¶·©«¬××××××ÍÍÍÍÍÍÍÍ;À³¶·©«¬¾À³¶·©«¬¾À³¶·©«¬ßß«©«¬ßß«ßØŸßß«ßØŸßØŸßØŸßØŸßØŸßØŸßß«ßß«ßß«ßß«ßß«ßß«ßß«ßß«¼±ußØžÿ÷µÿ÷µÿ÷µÿ÷µÿ÷µÿ÷µÿ÷µßØžßß«ßß«ßß«ßß«ßß«ßß«ßß«ßß«ÕÕ£ÕÕ£ÕÕ£ÕÕ£ÕÕ£´ªqçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚçÚ´ªq´ªq´ªq
\ No newline at end of file diff --git a/bubbob/images/gramy.ppm b/bubbob/images/gramy.ppm Binary files differnew file mode 100644 index 0000000..ad2c757 --- /dev/null +++ b/bubbob/images/gramy.ppm diff --git a/bubbob/images/gramy_angry.ppm b/bubbob/images/gramy_angry.ppm Binary files differnew file mode 100644 index 0000000..c1672ee --- /dev/null +++ b/bubbob/images/gramy_angry.ppm diff --git a/bubbob/images/hat1.ppm b/bubbob/images/hat1.ppm new file mode 100644 index 0000000..7e15239 --- /dev/null +++ b/bubbob/images/hat1.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +192 48 +255 +###111<<<)))222222111&&&&&&111222222)))<<<111###+++666<<<777111)))333333888===BBBBBB===......$$$.........333333888888===::::::===888888333333.........$$$......===BBBBBB===888333333)))111777<<<666+++:::BBB@@@======888888333333)))...333333666888===BBBBBB===888888............))).........333333888888===BBBBBB======BBBBBB===888888333333.........)))............888888===BBBBBB===888666333333...)))333333888888======@@@BBB:::888BBBBBB@@@======888888333333............333333888===@@@BBBBBB===888888333333.........)))))).........333333888888===BBBBBB====== ======BBBBBB===888888333333.........)))))).........333333888888===BBBBBB@@@===888333333............333333888888======@@@BBBBBB888333888BBBBBB@@@======888888333333...............333333666888===BBBBBB===;;;888888333333333..................333333888888===BBBBBB=========888888=========BBBBBB===888888333333..................333333333888888;;;===BBBBBB===888666333333...............333333888888======@@@BBBBBB888333333888BBBBBBBBB@@@======888888333333.........))).........$$$......333333888===@@@BBBBBB===888888666333333333)))...............333333888888===BBB=========888333------333888=========BBB===888888333333...............)))333333333666888888===BBBBBB@@@===888333333......$$$.........))).........333333888888======@@@BBBBBBBBB888333)))333888BBBBBB@@@======888888888333333.........)))$$$............)))......333333888===BBBBBB===;;;888888333333333..................888888===BBBBBB======888888333000000333888888======BBBBBB===888888..................333333333888888;;;===BBBBBB===888333333......)))............$$$))).........333333888888888======@@@BBBBBB888333)))...333888BBBBBB@@@======888888333333............))))))..................333333888===BBBBBB===888888666333333333...............888===BBB======888888333000...!!!!!!...000333888888======BBB===888...............333333333666888888===BBBBBB===888333333..................))))))............333333888888======@@@BBBBBB888333.....................333888BBBBBB@@@======888888333333.........))))))))))))...............888===BBBBBB===888888333333333.........))).........BBBBBB======888333000...000))))))000...000333888======BBBBBB.........))).........333333333888888===BBBBBB===888...............)))))))))))).........333333888888======@@@BBBBBB888333................................................@@@======888888333333.........))))))))) ...............BBBBBB===888888333333333.........---.........======888888333000000000******000000000333888888======.........---.........333333333888888===BBBBBB............... ))))))))).........333333888888======@@@...............................................................333333.........)))))))))###..................888888333333333.........(((......888888333000000000...******...000000000333888888......(((.........333333333888888..................###))))))))).........333333..................................................................)))))))))###...............333333333.........$$$......888333000000000+++$$$$$$+++000000000333888......$$$.........333333333...............###)))))))))............................................................)))....................................000000+++))))))+++000000....................................)))....................................................................................++++++))))))++++++.............................................................................................)))$$$$$$)))..........................................................................................................................................................................................
\ No newline at end of file diff --git a/bubbob/images/hat2.ppm b/bubbob/images/hat2.ppm new file mode 100644 index 0000000..bce2b98 --- /dev/null +++ b/bubbob/images/hat2.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +192 48 +255 +{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{€€€{{{{{{{{{€€€€€€€€€€€€{{{{{{{{{€€€{{{{{{{{{{{{{{{{{{€€€†††ŒŒŒ––––––{{{{{{€€€€€€††††††{{{{{{€€€€€€€€€††††††††††††€€€€€€€€€{{{{{{††††††€€€€€€{{{{{{––––––ŒŒŒ†††€€€{{{{{{{{{{{{€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ{{{{{{€€€€€€††††††ŒŒŒ‘‘‘{{{{{{{{{{{{€€€€€€€€€††††††††††††€€€€€€€€€{{{{{{{{{{{{‘‘‘ŒŒŒ††††††€€€€€€{{{{{{ŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€{{{{{{{{{{{{€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{€€€€€€€€€††††††ŒŒŒ‘‘‘––––––‘‘‘{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{‘‘‘––––––‘‘‘ŒŒŒ††††††€€€€€€€€€{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€{{{{{{{{{{{{€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{uuuuuu{{{{{{€€€€€€††††††‰‰‰ŒŒŒ‘‘‘––––––‘‘‘ŒŒŒŒŒŒ€€€€€€{{{{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘––––––––––––‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{{{{€€€€€€ŒŒŒŒŒŒ‘‘‘––––––‘‘‘ŒŒŒ‰‰‰††††††€€€€€€{{{{{{uuuuuu{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€{{{{{{{{{{{{{{{€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{uuuuuu{{{{{{€€€€€€€€€††††††ŒŒŒ‘‘‘”””––––––‘‘‘ŒŒŒŒŒŒ†††††††††€€€€€€€€€€€€{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘––––––‘‘‘‘‘‘––––––‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{€€€€€€€€€€€€†††††††††ŒŒŒŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒ††††††€€€€€€€€€{{{{{{uuuuuu{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€{{{{{{{{{{{{{{{€€€†††ŒŒŒ–––––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{uuuuuu{{{{{{€€€€€€††††††‰‰‰ŒŒŒ‘‘‘––––––‘‘‘ŒŒŒŒŒŒ†††††††††€€€€€€€€€€€€€€€{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘––––––‘‘‘‘‘‘ŒŒŒŒŒŒ‘‘‘‘‘‘––––––‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{€€€€€€€€€€€€€€€†††††††††ŒŒŒŒŒŒ‘‘‘––––––‘‘‘ŒŒŒ‰‰‰††††††€€€€€€{{{{{{uuuuuu{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””–––––––––ŒŒŒ†††€€€{{{{{{{{{{{{€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{uuuuuu€€€€€€€€€{{{{{{€€€€€€††††††ŒŒŒ‘‘‘”””––––––‘‘‘ŒŒŒŒŒŒ‰‰‰†††††††††€€€€€€€€€{{{{{{€€€€€€€€€€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘––––––‘‘‘‘‘‘‘‘‘ŒŒŒ†††ƒƒƒƒƒƒ†††ŒŒŒ‘‘‘‘‘‘‘‘‘––––––‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€€€€€€€€€€{{{{{{€€€€€€€€€†††††††††‰‰‰ŒŒŒŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒ††††††€€€€€€{{{{{{€€€€€€€€€uuuuuu{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€{{{{{{{{{{{{€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€€€€{{{{{{{{{uuuuuu€€€€€€€€€€€€{{{{{{€€€€€€††††††ŒŒŒ‘‘‘––––––‘‘‘ŒŒŒŒŒŒ†††††††††€€€€€€€€€€€€{{{{{{€€€€€€€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘–––‘‘‘‘‘‘‘‘‘ŒŒŒ††††††ƒƒƒƒƒƒ††††††ŒŒŒ‘‘‘‘‘‘‘‘‘–––‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€€€€€€€{{{{{{€€€€€€€€€€€€†††††††††ŒŒŒŒŒŒ‘‘‘––––––‘‘‘ŒŒŒ††††††€€€€€€{{{{{{€€€€€€€€€€€€uuuuuu{{{{{{{{{€€€€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€{{{{{{€€€€€€€€€€€€€€€€€€€€€†††ŒŒŒ––––––”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{uuuuuu€€€€€€€€€€€€€€€€€€††††††ŒŒŒ‘‘‘––––––‘‘‘ŒŒŒŒŒŒ‰‰‰†††††††††€€€€€€€€€€€€{{{{{{€€€€€€€€€€€€ŒŒŒŒŒŒ‘‘‘––––––‘‘‘‘‘‘ŒŒŒŒŒŒ†††ƒƒƒƒƒƒ}}}€€€€€€}}}ƒƒƒƒƒƒ†††ŒŒŒŒŒŒ‘‘‘‘‘‘––––––‘‘‘ŒŒŒŒŒŒ€€€€€€€€€€€€{{{{{{€€€€€€€€€€€€†††††††††‰‰‰ŒŒŒŒŒŒ‘‘‘––––––‘‘‘ŒŒŒ††††††€€€€€€€€€€€€€€€€€€uuuuuu{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””––––––ŒŒŒ†††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€”””‘‘‘‘‘‘ŒŒŒŒŒŒ††††††€€€€€€€€€{{{{{{{{{uuuuuu€€€€€€€€€€€€€€€€€€ŒŒŒ‘‘‘––––––‘‘‘ŒŒŒŒŒŒ†††††††††€€€€€€€€€€€€{{{{{{{{{€€€€€€€€€ŒŒŒ‘‘‘–––‘‘‘‘‘‘ŒŒŒŒŒŒ†††ƒƒƒ€€€ƒƒƒ€€€}}}{{{{{{}}}€€€ƒƒƒ€€€ƒƒƒ†††ŒŒŒŒŒŒ‘‘‘‘‘‘–––‘‘‘ŒŒŒ€€€€€€€€€{{{{{{{{{€€€€€€€€€€€€†††††††††ŒŒŒŒŒŒ‘‘‘––––––‘‘‘ŒŒŒ€€€€€€€€€€€€€€€€€€uuuuuu{{{{{{{{{€€€€€€€€€††††††ŒŒŒŒŒŒ‘‘‘‘‘‘”””€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††€€€€€€€€€{{{{{{{{{uuuuuu€€€€€€€€€€€€€€€––––––‘‘‘ŒŒŒŒŒŒ†††††††††€€€€€€€€€€€€{{{{{{{{{€€€€€€€€€––––––‘‘‘‘‘‘ŒŒŒ†††ƒƒƒ€€€ƒƒƒ€€€}}}{{{{{{{{{{{{}}}€€€ƒƒƒ€€€ƒƒƒ†††ŒŒŒ‘‘‘‘‘‘––––––€€€€€€€€€{{{{{{{{{€€€€€€€€€€€€†††††††††ŒŒŒŒŒŒ‘‘‘––––––€€€€€€€€€€€€€€€uuuuuu{{{{{{{{{€€€€€€€€€††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{{{{{{{{{uuuuuu€€€€€€€€€€€€€€€€€€ŒŒŒŒŒŒ†††††††††€€€€€€€€€{{{{{{{{{€€€€€€€€€‘‘‘‘‘‘ŒŒŒŒŒŒ†††ƒƒƒƒƒƒƒƒƒ}}}{{{{{{{{{{{{{{{{{{}}}ƒƒƒƒƒƒƒƒƒ†††ŒŒŒŒŒŒ‘‘‘‘‘‘€€€€€€€€€{{{{{{{{{€€€€€€€€€†††††††††ŒŒŒŒŒŒ€€€€€€€€€€€€€€€€€€uuuuuu{{{{{{{{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††€€€€€€€€€{{{{{{{{{€€€€€€ŒŒŒŒŒŒ†††ƒƒƒƒƒƒƒƒƒ€€€}}}{{{{{{{{{{{{{{{{{{}}}€€€ƒƒƒƒƒƒƒƒƒ†††ŒŒŒŒŒŒ€€€€€€{{{{{{{{{€€€€€€€€€†††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{{{{{{€€€€€€ŒŒŒ†††ƒƒƒƒƒƒƒƒƒ}}}{{{{{{{{{{{{{{{{{{}}}ƒƒƒƒƒƒƒƒƒ†††ŒŒŒ€€€€€€{{{{{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{{{{{{€€€€€€€€€ƒƒƒƒƒƒ}}}{{{{{{{{{{{{{{{{{{}}}ƒƒƒƒƒƒ€€€€€€€€€{{{{{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€}}}}}}{{{{{{{{{{{{{{{{{{}}}}}}€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{{{{{{{{{{{{{{{{{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€{{{{{{€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€
\ No newline at end of file diff --git a/bubbob/images/hat5.ppm b/bubbob/images/hat5.ppm new file mode 100644 index 0000000..78300f8 --- /dev/null +++ b/bubbob/images/hat5.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +64 48 +255 +{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{...BBBBBB@@@===;;;;;;888888333'''{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{888===BBBBBB@@@===;;;;;;888888333333...{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{333888===BBBBBB@@@===;;;;;;888888333333......{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{...333888===BBBBBB@@@===;;;;;;888888333333......+++{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{&&&...333888===BBBBBB@@@===;;;;;;888888333333......+++$$${{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{......333888===BBBBBB@@@===;;;;;;888888333333......++++++{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{......333888===BBBBBB@@@===;;;;;;888888333333......++++++{{{{{{€€€€€€†††ŒŒŒ‘‘‘––––––”””‘‘‘ŒŒŒŒŒŒ††††††€€€€€€}}}}}}{{{{{{$$$......333888===BBBBBB@@@===;;;;;;888888333333......++++++$$$€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€(((......333888===BBBBBB@@@===;;;;;;888888333333......++++++(((€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€..............................................................................................................................................................................
\ No newline at end of file diff --git a/bubbob/images/ice_cyan_big.ppm b/bubbob/images/ice_cyan_big.ppm Binary files differnew file mode 100644 index 0000000..d22c371 --- /dev/null +++ b/bubbob/images/ice_cyan_big.ppm diff --git a/bubbob/images/ice_violet_big.ppm b/bubbob/images/ice_violet_big.ppm Binary files differnew file mode 100644 index 0000000..d1d967e --- /dev/null +++ b/bubbob/images/ice_violet_big.ppm diff --git a/bubbob/images/keys.ppm b/bubbob/images/keys.ppm Binary files differnew file mode 100644 index 0000000..4d20147 --- /dev/null +++ b/bubbob/images/keys.ppm diff --git a/bubbob/images/level_digits.ppm b/bubbob/images/level_digits.ppm Binary files differnew file mode 100644 index 0000000..1110f8b --- /dev/null +++ b/bubbob/images/level_digits.ppm diff --git a/bubbob/images/lightning_large.ppm b/bubbob/images/lightning_large.ppm Binary files differnew file mode 100644 index 0000000..67ac930 --- /dev/null +++ b/bubbob/images/lightning_large.ppm diff --git a/bubbob/images/lightning_small.ppm b/bubbob/images/lightning_small.ppm Binary files differnew file mode 100644 index 0000000..cd3cf22 --- /dev/null +++ b/bubbob/images/lightning_small.ppm diff --git a/bubbob/images/monky.ppm b/bubbob/images/monky.ppm Binary files differnew file mode 100644 index 0000000..01afbae --- /dev/null +++ b/bubbob/images/monky.ppm diff --git a/bubbob/images/monky_angry.ppm b/bubbob/images/monky_angry.ppm Binary files differnew file mode 100644 index 0000000..786d39a --- /dev/null +++ b/bubbob/images/monky_angry.ppm diff --git a/bubbob/images/nasty.ppm b/bubbob/images/nasty.ppm Binary files differnew file mode 100644 index 0000000..a006bcd --- /dev/null +++ b/bubbob/images/nasty.ppm diff --git a/bubbob/images/nasty_angry.ppm b/bubbob/images/nasty_angry.ppm Binary files differnew file mode 100644 index 0000000..58375e1 --- /dev/null +++ b/bubbob/images/nasty_angry.ppm diff --git a/bubbob/images/orcy.ppm b/bubbob/images/orcy.ppm Binary files differnew file mode 100644 index 0000000..1ddb609 --- /dev/null +++ b/bubbob/images/orcy.ppm diff --git a/bubbob/images/orcy_angry.ppm b/bubbob/images/orcy_angry.ppm Binary files differnew file mode 100644 index 0000000..59b51c3 --- /dev/null +++ b/bubbob/images/orcy_angry.ppm diff --git a/bubbob/images/palettes.dat b/bubbob/images/palettes.dat Binary files differnew file mode 100644 index 0000000..5070cbb --- /dev/null +++ b/bubbob/images/palettes.dat diff --git a/bubbob/images/pastec_big.ppm b/bubbob/images/pastec_big.ppm new file mode 100644 index 0000000..6805f73 --- /dev/null +++ b/bubbob/images/pastec_big.ppm @@ -0,0 +1,44 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +90 90 +255 +ÿÿÎÿÿÎÿÿÎÿÿÎÿÿÎÿûÌÿ³ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¥¥ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ ÿ¦¦ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ¨¨ÿ³ÿûÌÿÿÎÿÿÎÿÿÎÿÿÎÿÿÐÿÿÌÿÿÌÿÿÌÿÿÌÿùÉÿà½ÿ»«ÿ¢Ÿÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ˜˜ÿ’’ÿ••ÿ››ÿ››ÿ››ÿ››ÿ””ÿ””ÿ’’ÿ˜˜ÿ››ÿ››ÿ››ÿ››ÿ››ÿ››ÿ¢Ÿÿ»«ÿà½ÿùÉÿÿÌÿÿÌÿÿÍÿÿÎÿÿÌÿÿÌÿÿÌÿÿÌÿ÷ÈÿÞ»ÿºªÿ¡ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ——ÿ——ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ——ÿ‹‹ÿÿŠŠÿ””ÿ™™ÿ™™ÿ––ÿŒŒÿ‰‰ÿ‰‰ÿ’’ÿ––ÿ——ÿ™™ÿ™™ÿ™™ÿ™™ÿ¡ÿºªÿÞ»ÿ÷ÈÿÿÌÿÿÌÿÿÌÿÿÎûÿÌûÿÌûÿÌûÿÌûöÇüÛ¹þµ¤ÿ™•ÿ‘‘ÿ’’ÿ““ÿ””ÿ••ÿ••ÿ••ÿ••ÿ••ÿ––ÿ˜˜ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ˜˜ÿ––ÿ••ÿ““ÿ‘‘ÿ‘‘ÿ““ÿ••ÿ••ÿ••ÿÿ’’ÿÿ~~ÿ‹‹ÿÿŽŽÿ‘‘ÿ††ÿ‹‹ÿ~~ÿ„„ÿˆˆÿ‡‡ÿ‹‹ÿ‹‹ÿŒŒÿŒŒÿŒŒÿ•‘þ²¡üÚ·ûöÇûÿÌûÿÌüÿÌÿÿÎîÿÌîÿÌîÿÌîÿÌðôÅôÓ°ù¤“þƒÿyyÿ}}ÿƒƒÿ‡‡ÿˆˆÿˆˆÿˆˆÿˆˆÿŠŠÿŽŽÿ““ÿ˜˜ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ˜˜ÿ““ÿŽŽÿŠŠÿ††ÿ„„ÿˆˆÿŠŠÿˆˆÿˆˆÿˆˆÿ††ÿ„„ÿ~~ÿ„„ÿƒƒÿ€€ÿƒƒÿÿ€€ÿuuÿooÿjjÿeeÿppÿrrÿggþMMÿggÿggþsoù™ˆôÍ«ðóÄîÿÌîÿÌïÿÌýÿÎÝÿÌÝÿÌÝÿÌÛÿËßñÂèÇ¥ôŒ{üb^ÿWWÿ^^ÿiiÿppÿxxÿxxÿyyÿwwÿyyÿ€€ÿ‹‹ÿ’’ÿ––ÿ˜˜ÿ™™ÿ™™ÿ˜˜ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ˜˜ÿ––ÿ••ÿ——ÿ——ÿ’’ÿ‹‹ÿ€€ÿ{{ÿwwÿ}}ÿƒƒÿƒƒÿ}}ÿwwÿvvÿuuþxxþzzþ||ÿÿvvÿkkÿttÿwwÿkkÿTTÿWWÿWWÿMMÿMMÿ))ý ý..ÿ22üC?óud缚ßî¿ÝÿÌÝÿÌÜÿÌÐÿÎÐÿÌÐÿÌÏÿÌÐÿÌÒðÀÞÁžð{kûKGÿ>>ÿ^^ÿWWÿccÿssÿwwÿiiÿiiÿmmÿƒƒÿÿ‡‡ÿ‘‘ÿ““ÿ––ÿ––ÿ——ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ™™ÿ˜˜ÿ––ÿ••ÿ””ÿ‘‘ÿŒŒÿŠŠÿŒŒÿ‹‹ÿˆˆÿ~~ÿ^^ÿhhÿhhÿ}}ÿxxþ‚‚ÿmmÿmmÿmmÿooÿ~~ýqqývvÿllÿwwÿPPÿYYþOOÿRRÿMMÿEEÿUUÿWWÿ55þ þ''ûÿ
ú!î\KÞ°Ôë¼ÐÿÌÐÿÌÐÿÌÎÿÎÌÿÌÌÿÌÌÿÌÊÿÊÏî¿Ý¼šîveûD@ÿTTþWWýhhÿ{{ÿnnÿnnÿ\\ÿaaÿxxÿrrþ€€ÿ‚‚ÿ~~ÿŒŒÿŠŠÿŒŒÿ””ÿ––ÿ––ÿ••ÿ––ÿ˜˜ÿ™™ÿ™™ÿ™™ÿ™™ÿ˜˜ÿ––ÿ••ÿ••ÿ••ÿ––ÿ––ÿ••ÿ‘‘ÿŒŒÿ‰‰ÿ‡‡ÿƒƒÿ||ÿ~~ÿˆˆÿÿuuÿccÿIIÿKKÿ]]ÿiiÿxxÿccþrrÿiiÿqqÿuuýmmþxxÿppÿnnÿPPÿAAþþ;;û%%ûÿBBý]]ý__ÿTTÿGGûÿ + +ÿÿýýEE÷ñí + +é +Ý Ê ÈC“ +ßÙ + +úýõûú,,úüü + +ÿÿÿúï + +ü77ÿ55þ88ÿý--óæîòùû--ùü==õ ÷úù + +û ÷ +æåæÙ +àé
ãàä +æ
ßã + +øEEêè + +Ð + +ô11êèêéçá +Ñ +Ð + +Ð + +ßâ +Ð +Ï +Ð +Ð +Ð +Ï +Ð +Ð +Ð +Ð diff --git a/bubbob/images/peach_big.ppm b/bubbob/images/peach_big.ppm Binary files differnew file mode 100644 index 0000000..3f2b6ca --- /dev/null +++ b/bubbob/images/peach_big.ppm diff --git a/bubbob/images/point_0.ppm b/bubbob/images/point_0.ppm Binary files differnew file mode 100644 index 0000000..c6ef319 --- /dev/null +++ b/bubbob/images/point_0.ppm diff --git a/bubbob/images/red_Hurry_up.ppm b/bubbob/images/red_Hurry_up.ppm Binary files differnew file mode 100644 index 0000000..6f957ac --- /dev/null +++ b/bubbob/images/red_Hurry_up.ppm diff --git a/bubbob/images/sheep.ppm b/bubbob/images/sheep.ppm Binary files differnew file mode 100644 index 0000000..66fe04a --- /dev/null +++ b/bubbob/images/sheep.ppm diff --git a/bubbob/images/shot.ppm b/bubbob/images/shot.ppm Binary files differnew file mode 100644 index 0000000..74606ff --- /dev/null +++ b/bubbob/images/shot.ppm diff --git a/bubbob/images/spinning_drop.ppm b/bubbob/images/spinning_drop.ppm Binary files differnew file mode 100644 index 0000000..6bb22f9 --- /dev/null +++ b/bubbob/images/spinning_drop.ppm diff --git a/bubbob/images/springy.ppm b/bubbob/images/springy.ppm Binary files differnew file mode 100644 index 0000000..16277ba --- /dev/null +++ b/bubbob/images/springy.ppm diff --git a/bubbob/images/springy_angry.ppm b/bubbob/images/springy_angry.ppm Binary files differnew file mode 100644 index 0000000..07bb7f1 --- /dev/null +++ b/bubbob/images/springy_angry.ppm diff --git a/bubbob/images/star_large.ppm b/bubbob/images/star_large.ppm Binary files differnew file mode 100644 index 0000000..513c538 --- /dev/null +++ b/bubbob/images/star_large.ppm diff --git a/bubbob/images/sugar_pie_big.ppm b/bubbob/images/sugar_pie_big.ppm Binary files differnew file mode 100644 index 0000000..e63512b --- /dev/null +++ b/bubbob/images/sugar_pie_big.ppm diff --git a/bubbob/images/water_flow.ppm b/bubbob/images/water_flow.ppm new file mode 100644 index 0000000..0950083 --- /dev/null +++ b/bubbob/images/water_flow.ppm @@ -0,0 +1,4 @@ +P6 +16 160 +255 +fÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÌÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿÌÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿÿÿÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÌÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿÿÿÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÌÌÿfÌÿÿÿÿÌÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿÌÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿÌÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿÌÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿ™ÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿÌÌÿfÌÿÿÿÿÌÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿÿÿÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÌÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿ™ÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿ™ÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿ™ÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿÿÿÿfÌÿfÌÿfÌÿÿÿÿfÌÿ™ÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÿÿÿfÌÿfÌÿÿÿÿfÌÿfÌÿÌÌÿfÌÿÿÿÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿÌÌÿfÌÿfÌÿfÌÿfÌÿfÌÿfÌÿ
\ No newline at end of file diff --git a/bubbob/images/water_still.ppm b/bubbob/images/water_still.ppm new file mode 100644 index 0000000..ec040f0 --- /dev/null +++ b/bubbob/images/water_still.ppm @@ -0,0 +1,4 @@ +P6 +16 16 +255 +3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÌÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿÌÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÌÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÌÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿÌÌÿ3™ÿ™Ìÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÌÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÌÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿÌÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ
\ No newline at end of file diff --git a/bubbob/images/water_surface.ppm b/bubbob/images/water_surface.ppm new file mode 100644 index 0000000..fcbbc1a --- /dev/null +++ b/bubbob/images/water_surface.ppm @@ -0,0 +1,4 @@ +P6 +16 64 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿÿÿÿÿÿÿÿÿÿÿÿÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ™Ìÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ™Ìÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿfÌÿ3™ÿ3™ÿ3™ÿ™Ìÿ3™ÿ™Ìÿ3™ÿ
\ No newline at end of file diff --git a/bubbob/images/yellow_Hurry_up.ppm b/bubbob/images/yellow_Hurry_up.ppm Binary files differnew file mode 100644 index 0000000..52682af --- /dev/null +++ b/bubbob/images/yellow_Hurry_up.ppm diff --git a/bubbob/levels/Arena.bin b/bubbob/levels/Arena.bin Binary files differnew file mode 100644 index 0000000..73db756 --- /dev/null +++ b/bubbob/levels/Arena.bin diff --git a/bubbob/levels/CompactLevels.py b/bubbob/levels/CompactLevels.py new file mode 100644 index 0000000..e48908a --- /dev/null +++ b/bubbob/levels/CompactLevels.py @@ -0,0 +1,1902 @@ +# +# A series of compact levels. +# + +import boarddef, mnstrmap, random +from boarddef import LNasty, LMonky, LGhosty, LFlappy +from boarddef import LSpringy, LOrcy, LGramy, LBlitzy +from boarddef import RNasty, RMonky, RGhosty, RFlappy +from boarddef import RSpringy, ROrcy, RGramy, RBlitzy + + +class level01(boarddef.Level): + a = LNasty + b = RNasty + + walls = """ +############################# +## ## +## ## +## ## +## ## +## ## +## ## +## a b ## +#### ############### #### +## ## +## ## +## ## +## a b ## +#### ############### #### +## ## +## ## +## ## +## a b ## +#### ############### #### +## ## +## ## +## ## +## ## +############################# +""" + + +class level02(boarddef.Level): + a = LNasty + b = RNasty + g = RGhosty + + walls = """ +############################# +## # ## +## # ## +## # ## +## # g ## +## g # ## +## # ## +## # ## +#### ############### #### +## # # ## +## # # ## +## # # ## +## # a b # ## +#### ############### #### +## ## +## ## +## ## +## ## +#### #### +## ## +## ## +## ## +## ## +############################# +""" + + +class level03(boarddef.Level): + a = LNasty + b = RNasty + + letter = 1 + + walls = """ +######### ### ######### +## # # ## +## # # ## +## # # ## +## # # ## +##b # # # # a ## +###### ##### ##### ###### +## ## +## ## +## ## +## #b # # # # a # ## +## ##### ##### ##### ## +## # # ## +## # # ## +## # # ## +##b # # a ## +####### # b # ####### +## ####### ## +## # ## +## # ## +## # ## +## # ## +## # ## +######### ### ######### +""" #|# #|# #|# """ + + +class level04(boarddef.Level): + a = LNasty + b = RNasty + g = LOrcy + + fire = 1 + + walls = """ +######### ### ######### +## # # ## +## # # ## +## # # ## +## # # ## +##b # # # # a ## +###### ##### ##### ###### +## ## +## ## +## ## +## #b # #gg # # a # ## +## ##### ##### ##### ## +## # # ## +## # # ## +## # # ## +##b # # a ## +####### # b # ####### +## ####### ## +## # ## +## # ## +## # ## +## # ## +## # ## +######### ### ######### +""" #|# #|# #|# """ + +class level05(boarddef.Level): + a = LNasty + b = RNasty + g = LOrcy + f = RFlappy + + water = top = letter = 1 + + walls = """ +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +##b # # # # a ## +#### # ##### ##### # #### +## ## +## ## +## ## +## #b # #gg # # a # ## +## # ### ## ## ### # ## +## # # ## +## # # ## +## # f # ## +##b # # a ## +####### # b # ####### +## # ### # ## +## ## +## ## +## ## +## ## +## ## +######### ######### +""" #|# #|# #|# """ + + winds = """ +>> << +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +""" + +class level06(boarddef.Level): + a = LNasty, RNasty, LMonky + + letter = fire = 1 + + walls = """ +############################# +## ## +## ## +## ## +## ## +## ## +## ## +## ### ## +## # ## ## +## ## # ## # ## +## # # ## # # ## +## ## # # # # # ## +## # #### # # #a ## +## a #### ############# +## #### ## +#### ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level07(boarddef.Level): + g = LGhosty, LGhosty, LGhosty, LGhosty + h = RGhosty, RGhosty, RGhosty, RGhosty + + letter = lightning = 1 + + walls = """ +## ## +## ## +## # # ## +## # # ## +## # # ## +## ### ### ## +##h # # # # #g ## +## # # # # # # # ## +##### #### # #### ##### +## # # # ### # # # ## +## # # # # # # ## +## ### # # # ### ## +## # #### #### # ## +## # # # # # ## +## # # # # ## +## ### ## +## # ## +## # # # ## +## # # # ## +## # # ## +## ### ### ## +## # # # # ## +## # # # # # # ## +## #### #### #### #### ## +""" #|# #|# #|# """ + +class level08(boarddef.Level): + s = LSpringy + r = RSpringy + f = LFlappy + g = RFlappy + + walls = """ +############################# +## ## +## g f ## +## ## +## ## +## ## +## ## +## ## +## # ## +## ## +## ### ## +## ## +## ##### ## +## ## +## ####### ## +## ## +## ######### ## +## sssrrr ## +## ########### ## +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level09(boarddef.Level): + m = (LMonky, RMonky,)*5 + g = LGhosty + + walls = """ +####### #### #### ####### +####### #### #### ####### +## ## ## ## +## ## ## ## +## g ## ## g ## +## ## ## ## +## ## ## ## +#### ##### ##### #### +#### ##### ##### #### +## ## +## ## #### #### ## ## +## ## #### #### ## ## +## ## ## ## +#### ## ## #### +#### ## m ## #### +## ## ### ## ## +## ## ## ### ## ## ## +## ## ## ### ## ## ## +## ## ### ## ## +#### ## ## #### +#### ## ## #### +## ## ## ## +## ## #### #### ## ## +####### #### #### ####### +""" #|# #|# #|# """ + +class level10(boarddef.Level): + n = LNasty + + fire = top = 1 + + walls = """ +## ##### ## ## +## ##nn ## ## +## ####### ## +## ## +## ## +## ## ##### ## +## ##nn ## ## +## ####### ##### ## ## +## ##nn ## ## +## ####### ## +## ## +## ## +## ## ##### ## +## ##nn ## ## +## ####### ## +## ## +## ##### ## ## +## ##nn ## ## +## ####### ## +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + + winds = """ +>> << +>>> >>v xxx <<< +>>^ ^<< +>>^ v<<<^<< +>>^ ^<< +>>^ ^<< +>>^ xxx v<< ^<< +>>^ ^<< +>>^>>>v >>v xxx ^<< +>>^ ^<< +>>^ >v ^<< +>>^ ^<< +>>^ ^<< +>>^ xxx v<< ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ xxx ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +""" + +class level11(boarddef.Level): + o = LOrcy + p = ROrcy + + letter = fire = water = lightning = top = 1 + + walls = """ +## ## +## ## +### ### +## # # ## +## # # ## +## # # ## +## # # # # ## +## # ## +## o p o # # #p o p ## +####### #### # #### ####### +## # ## +## # ## +## # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## # # # # # # ## +## # # ## +###### ###### ###### ###### +""" #|# #|# #|# """ + +class level12(boarddef.Level): + o = LGramy + + walls = """ +## #### ## +## # # ## +## #### ## +## # # ## +## # o ## +## ################# +## # # ## +## #### ## +## o # # # ## +## ######### #### ## +## # # # # ## +## #### # o ## +## # # ############ +## #### # # ## +## # # #### ## +## o # # # ## +############ #### ## +## # # # # ## +## #### o # ## +## # # ######## ## +## #### ## +## # # ## +## ## +############################# +""" #|# #|# #|# """ + +class level13(boarddef.Level): + n = LNasty + m = LMonky + g = LGhosty + f = LFlappy + s = LSpringy + o = LOrcy + r = LGramy + b = LBlitzy + + walls = """ +## # #### ## +## f ## ## #### ## +## #### ## ## ## +## #### # ## +## #### f ###### ## +## ## ## f ###### ## +## # #### #### #### ## +## ###### ###### ## +## ###### ###### ## +## #### #### # ## +## ## ## f ## +## #### ## +## f ## +## #### ## +## ###### ## +## #### ###### ## +## ## ## # #### ## +## # #### f ## ## ## +## ###### #### ## +## ###### ## +## #### ## +## ## +## #### ## +###### ###### ##### +""" #|# #|# #|# """ + +class level14(boarddef.Level): + o = LOrcy + r = LGramy + + walls = """ +############################# +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ### ### ## +## ## ## ## +## # # ## +## ### ### ## +## ## ## ## +## # # ## +## ## ## ## +## ## ## ## +## ### ### ## +## ## ## ## +## ## ## ## +## ##or or ## ## +############################# +""" #|# #|# #|# """ + +class level15(boarddef.Level): + s = LSpringy + g = LGhosty + + walls = """ +############################# +## ## +## ## +## ## +## s s ## +## s s ## +## s s ## +## ###s #####s ### ## +## # ## ####### ## # ## +## # ### ### # ## +## ## ### ### ## ## +## # ### g ### # ## +## # ### ### # ## +## # ### ### # ## +## # ## ####### ## # ## +## ### ##### ### ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level16(boarddef.Level): + l = LBlitzy, LGramy + r = RBlitzy, RGramy + + letter = 1 + + walls = """ +############################# +## l r ## +## #### ## ## +## #### ## #### ## ## +## #### ## #### ## ## +## l ## ## #### ## ## +## #### ## ## l ## +## #### ## ## #### ## +## ## ## ## #### ## +## #### ## ## ## +## #### ## ## #### ## ## +## ## #### ## ## +## ## #### ## ## +## ## #### #### l ## ## ## +## ## #### ## ## ## +## ## ## ## ## +## ## ## r ## ## +## #### ## ## ## ## +## #### ## #### ## ## ## +## ## #### ## ## ## +## #### ## ## ## +## #### ## ## +## ## +############################# +""" #|# #|# #|# """ + +class level17(boarddef.Level): + m = LMonky + j = RMonky + g = LGhosty + h = RGhosty + b = LBlitzy + + walls = """ +############ ## +## # ## +## g # ## +## ##### # ########### ## +## # # # # # ## +## # ## # # g # ## +## # #j # # ##### # ## +## # ##### # # # # ## +## # # # ## # ## +## #b # # #j # ## +## ########### # ##### ## +## # ## +## # ## +## ########### # h ## +## # # # ##### ## +## # h # # # # ## +## # ##### # # ## # ## +## # # # # # m # # ## +## # ## # # ##### # ## +## # m # # # # ## +## ##### # # b # ## +## # ########### ## +## b # ## +############ ## +""" #|# #|# #|# """ + +class level18(boarddef.Level): + o = (ROrcy,)*10 + + walls = """ +############# ############# +## ## +## # ## +## ## +## # # ## +## ## +## ## +## # # ## +## ## +## # ####### # ## +## # # ## +## ## ## ## +## # ### ### # ## +## ## ## ## +## # # ## +## # ####### # ## +## # # ## +## ## ## ## +## ###### ###### ## +## ### ### ## +## ## o ## ## +## ## ####### ## ## +## ## ## ## +############# ############# +""" #|# #|# #|# """ + +class level19(boarddef.Level): + n = LNasty + g = RGhosty + f = LFlappy + s = LSpringy + + walls = """ +## ### ### ### ## +## # # # # # # ## +## # #### #### # ## +## #n # #n # #n # ## +## ### ### ### ## +## # # ## +## # # ## +## ### f ### ## +## # # g # # ## +## # # f # # ## +## #n # g #n # ## +## ### ### ## +## # # ## +## # # ## +## ### ### ### ## +## # #s # #s # # ## +## # #### #### # ## +## #n # #n # #n # ## +## ### ### ### ## +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level20(boarddef.Level): + s = LSpringy, RSpringy + + letter = fire = top = 1 + + walls = """ +### ### +### ### +## ## +## ## +##ss ss # # ss ss ## +## # # # # # # ## +## # # ##### # # ## +## # # # # ## +## # # # # ## +## # # ## +## # # ## +## # # ## +## ## +## ## +## ## +## ## +## # # ## +## # # ## +## # # ## +## ## +## ## +## ## +## ## +## ######### ######### ## +""" #|# #|# #|# """ + +class level21(boarddef.Level): + n = (RNasty,)*12 + + letter = 1 + + walls = """ +############################# +## ## +##n ## +######################### ## +## ## +## ## +## ######################### +## ## +## ## +######################### ## +## ## +## ## +## ######################### +## ## +## ## +######################### ## +## ## +## ## +## ######################### +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level22(boarddef.Level): + n = LNasty + m = LMonky + g = LGhosty + f = LFlappy + s = LSpringy + o = LOrcy + r = LGramy + b = LBlitzy + + walls = """ +## ### ### ### ## +## # # # # # # ## +## # #### #### # ## +## #o # #b # #r # ## +## ### ### ### ## +## # # ## +## # # ## +## ### ### ## +## # # # # ## +## # # # # ## +## #g # #f # ## +## ### ### ## +## # # ## +## # # ## +## ### ### ### ## +## # # # # # # ## +## # #### #### # ## +## #m # #s # #n # ## +## ### ### ### ## +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level23(boarddef.Level): + m = LMonky + + water = top = 1 + + walls = """ +###### # # ###### +###### # # ###### +## # m # ## +## ######## ##### ######## ## +## ######## ##### ######## ## +## ######## ##### m ## ## +## ######## ########### ## ## +## m ######## ## ## +## ########### ######## ## ## +## ########### m ## ## +## ################ ###### ## +## ################ ###### ## +## ######## m ## +## ######## ############## ## +## ######## ############## ## +## ########m ########### ## +## m ##### ##### m ## +######## ##### ##### ######## +######## m m ######## +############## ############## +############################# +## ## +## ## +###### ######### ###### +""" #|# #|# #|# """ + +class level24(boarddef.Level): + g = RGhosty + f = RFlappy + s = LSpringy + t = RSpringy + + walls = """ +############################# +## ## +## ## +## s t ## +## s t ## +## s t ## +###### ###### +## ## +## ##### ##### ##### ## +## # # # ## +## #g # #g ## +## # #f # ## +## # # # ## +## ## +## ## +## ##### ##### ##### ## +## # # # ## +## # #g # ## +## #f # #f ## +## # # # ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level25(boarddef.Level): + s = LSpringy + t = RSpringy + + letter = lightning = 1 + + walls = """ +############# ############# +## ## +## # ## +## # ## +## s t ## +## # # # # ## +## # # # # ## +## ## +## # ## +## # ## +## t s ## +## # # # # ## +## # # # # ## +## ## +## # ## +## # ## +## s t ## +## # # # # ## +## # # # # ## +## ## +## # ## +## # ## +## ## +############# ############# +""" #|# #|# #|# """ + +class level26(boarddef.Level): + s = LSpringy + + fire = 1 + + walls = """ +####### ####### +## ## +## ## +##s s ## +##s ####### ####### s ## +##s s ## +#### #### +## ## +## ## ## ## +## ## +## ## ## ## +## ## +## ### ## +## ## +##s s ## +#### ## ## #### +## ## +## ## +## ## +## ## +## s s ## +## ## ## ## +## ## +############################# +""" #|# #|# #|# """ + +class level27(boarddef.Level): + s = LSpringy + + fire = 1 + + walls = """ +## ## +## ## +## # s # ## +##s # # s # # s ## +## # #s # # ## +## # # # # ## +### # ### +## ### ## +### # ### +## # # # # # ## +## #s # # #s # ## +## # # # # ## +## # # ## +## ### ### ## +## # # ## +## # # ## +## s #s # s ## +## # # # # # # ## +## # # # # # ## +## # ### # ## +## ### # ### ## +## # # ## +## ## +############################# +""" #|# #|# #|# """ + +class level28(boarddef.Level): + f = LFlappy + + walls = """ +############################# +######### ########### +## ## f #### ## +##f ## ff #### f ## +##f f ## ### ####f f ## +## f ## ### #### f ## +##### ## ### ###### ## +##### ## ### ###### ## +##### ## #### ###### ## +##### ## #### ###### ## +## #### ## +## ## +## ## +## ## ######## ### ## +## ## ######## ### ## +## ## ######## ### ## +## ## ### ### ## +## ## ### ### ## +## ## ### ### ## +## ##### ### ##### ## +## ##### ### ##### ## +## ### ## +## ### ## +############################# +""" #|# #|# #|# """ + +class level29(boarddef.Level): + f = LFlappy, RFlappy + + top = water = 1 + + walls = """ +## ##################### ## +## ## +## f f ## +## ##################### ## +## ##################### ## +## ## +## f f ## +## ##################### ## +## ##################### ## +## ## +## f f ## +## ##################### ## +## ##################### ## +## ## +## f f ## +## ##################### ## +## ##################### ## +## ## +## f f ## +## ##################### ## +## ##################### ## +## ## +## f f ## +## ##################### ## +""" #|# #|# #|# """ + +class level30(boarddef.Level): + g = RGhosty + h = RGhosty + i = LGhosty + j = LGhosty + + walls = """ +############################# +## ## +## ## +## ## +## ####g # ## +## ###### ## +## j ###### ## +## ####### ## +## ###### ## +## ######h ## +## # #### ## +## i ## +## ## +## ## +## ####g # ####g # ## +## ###### ###### ## +## j ###### j ###### ## +## ####### ####### ## +## ###### ###### ## +## ######h ######h ## +## # #### # #### ## +## i i ## +## ## +############################# +""" #|# #|# #|# """ + +class level31(boarddef.Level): + o = LGramy, RGramy + r = LOrcy + s = ROrcy + + top = letter = lightning = fire = 1 + + walls = """ +## ######## ######## ## +## ## +## ## +## r s ## +## ######## ######## ## +##o o ## +###### ###### +## ## +##o s # #r o ## +############# ############# +## ## +## ## +## ##################### ## +## ## +## ## +## ####### ####### ## +## ## +##o o ## +########## # # ########## +## # # ## +## ## ## ## +## ## +## ## +############# ############# +""" #|# #|# #|# """ + +class level32(boarddef.Level): + n = LNasty, RNasty + f = (LFlappy, RFlappy) * 2 + + walls = """ +############# ############# +## ## ## ## +## f ## ##f ## +## ## ## ## +## ## ## ## +## ## ## ## +## ## ## ## +## ## ## ## +##n ## ## n ## +############################# +############################# +## ## +## f ## +##n n ## +############################# +############################# +## ## ## ## +## ## ## ## +## ## ## ## +## ## ## ## +## ## ## ## +## ## ## ## +## ## ## ## +############# ############# +""" #|# #|# #|# """ + + winds = """ +>> ^ << +>>>>>>>>>>>>> ^ <<<<<<<<<<<<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ xxx ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +""" + +class level33(boarddef.Level): + monsters = [mnstrmap.Gramy(14, -2, 1), + mnstrmap.Gramy(13, -2, 0)] * 10 + top = 1 + + walls = """ +## ## ## ## +## ## ## ## +## ## ## ## +## ####### ####### ## +## # # ## +## # # ## +## # ########### # ## +## # # ## +## # # ## +## ####### ####### ## +## # # ## +## # # ## +## # ########### # ## +## # # ## +## # # ## +## ####### ####### ## +## # # ## +## # # ## +## # ########### # ## +## # # ## +## # # ## +## ####### ####### ## +## ## ## ## +############# ############# +""" #|# #|# #|# """ + + winds = """ +>> << +>>>xxxxxxxxxxxxxxxxxxxxxxx<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +""" + +class level34(boarddef.Level): + n = LNasty + N = RNasty + m = LMonky + M = RMonky + g = LGhosty + G = RGhosty + o = LOrcy + O = ROrcy + r = LGramy + R = RGramy + b = LBlitzy, RBlitzy + + top = water = letter = 1 + + walls = """ +##### # ############# # ##### +## # # # ## +## # # # ## +## # # # ## +## r #R ## +############# # ############# +## # ## +##G # # # g ## +## # # # ## +## # # # ## +## #O b o # ## +##### # ############# # ##### +## # # ## +## # # # ## +## # # # ## +## # # # ## +##M # m ## +############# # ############# +## # ## +## # # # ## +## # # # ## +## # # # ## +## # n #N # ## +##### # ############# # ##### +""" #|# #|# #|# """ + +class level35(boarddef.Level): + g = (RGhosty,)*3 + h = (LGhosty,)*3 + + walls = """ +############# ############# +## h ## +############################# +##g ## +############################# +## h ## +############# ############# +##g ## +############################# +## h ## +############################# +##g ## +############# ############# +## h ## +############################# +##g ## +############################# +## h ## +############# ############# +##g ## +############################# +## ## +## ## +############# ############# +""" #|# #|# #|# """ + +class level36(boarddef.Level): + f = LFlappy + r = RSpringy + + top = fire = letter = 1 + + walls = """ +## # # # # # # ## +## f # # r # # f ## +## # # #f r # # # ## +## # # r # # ## +## f # # # f # # # f ## +## # # # # ## +## f # # # f # # # f ## +## # # f # # ## +## # # # # # # ## +## f # # f # # f ## +## # # # f # # # ## +## # # f # # ## +## # # # # # # ## +## # # f f # # ## +## # # # # # # ## +## # # # # ## +## # # # f # # # ## +## # # # # ## +## # # # f # # # ## +## # # f # # ## +## # # # f # # # ## +## # # # # ## +## # # # f # # # ## +############################# +""" #|# #|# #|# """ + +class level37(boarddef.Level): + n = LNasty + m = RMonky + o = (RFlappy,) * 5 + s = LSpringy + t = RSpringy + + walls = """ +####### ######### ####### +## ## +## ## +## ## +## #s t s t # ## +## ##################### ## +## # # ## +## # # ## +## # o # ## +## # # ## +## # # ## +## # #n m # # ## +## # ####### # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## # # # ## +## ##################### ## +## # # # ## +## # ## +## # ## +## # ## +####### ######### ####### +""" #|# #|# #|# """ + +class level38(boarddef.Level): + f = LFlappy + b = (LBlitzy, RBlitzy) * 2 + + water = 1 + + walls = """ +############# ############# +## ## +## ## +## ## +## b b b ## +## ######### ######### ## +## ######### ######### ## +## ######### ######### ## +## ### ### ## +## ### f f f f ### ## +## ### ### ## +## ### ### ### ### ## +## ### ### ### ### ## +## ### ### ### ### ## +## ### ### ### ### ## +## ### ### ## +## ### f f f f ### ## +## ######### ######### ## +## ######### ######### ## +## ######### ######### ## +## ## +## ## +## ## +############# ############# +""" #|# #|# #|# """ + +class level39(boarddef.Level): + monsters = [] + mnstrclasses = ([mnstrmap.Nasty] * 10 + + [None] * 30 + + [mnstrmap.Monky] * 7 + + [None] * 30 + + [mnstrmap.Orcy] * 5) + for i in range(len(mnstrclasses)): + if mnstrclasses[i]: + left = random.randrange(2) + x = random.choice([7,14,21]) + monsters.append(mnstrclasses[i](x-left, -2*i, left)) + + walls = """ +###### #### #### ###### +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +###### ######### ###### +## ## +## ## +## ## +## ## +############################# +""" #|# #|# #|# """ + +class level40(boarddef.Level): + g = LGhosty + + top = fire = 1 + + walls = """ +## ## +## ## +## # ## ## +## ## ### # ## +## # ## ## +## ## +## ## +## # ## +## # #### ## +## ### ## +## # # g ## +## # ## +## ### # ## +## # # ### ## +## ### g # ## +## # ## +## g ## +## # ## ## +## # # ### ## +## ### # ## +## # g ## +## g # # ## +## ## # ## ## +## # ### ## ## ## +""" #|# #|# #|# """ + + winds = """ +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>>>>>>>>>>>vvxvv<<<<<<<<<<<< +>>>>>>>>>>>>vvxvv<<<<<<<<<<<< +>>>>>>>>>>>>vxxxv<<<<<<<<<<<< +>>>>>>>>>>>>vxxxv<<<<<<<<<<<< +>>>>>>>>>>>>vxxxv<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>>>>>>>>>>>>xxxxx<<<<<<<<<<<< +>> << +""" + +class level41(boarddef.Level): + f = RFlappy + j = LFlappy + b = RBlitzy + d = LBlitzy + + top = 1 + + walls = """ +############# ############# +##b d ## +##### db ##### +## ############# ## +## ## ## ## +### ### jfjfjfjf ### ### +## ## fjfjfjfj ## ## +#### ##### ##### #### +## ## +### ### +#### #### +#b d ## +###### ###### +### ### +####### ####### +##### ##### +##b d ## +########## ########### +##### ##### +#### #### +####### ####### +## ## +## ## +############# ############# +""" #|# #|# #|# """ + + winds = """ +>> << +>>v<<<<<<<<<<<<>>>>>>>>>>>v<< +>>v>>>>>>>>vvvvvvv<<<<<<<<v<< +>>v>>>>>>>>vvvvvvv<<<<<<<<v<< +>>v>>>>>>>>xxxxxxx<<<<<<<<v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>v v<< +>>> vvv <<< +>>> vvv <<< +""" + +class level42(boarddef.Level): + l = LOrcy, LOrcy + r = ROrcy, ROrcy + b = ROrcy, LOrcy, ROrcy, LOrcy + + top = 1 + water = 1 + letter = 1 + + walls = """ +## # ## +## l r ## +## ## ## ## +## ## +## #### ### ## +## # ## +## # # ## +## # # r ## +## # ## ## +## r # # ## +## ## # # ## +## # # ## +## r # # ## +## # # ## # ## +## # # b # ## +## # ## # ## +## # r # ## +## # ## # ## +## # ## +## # # ## +## b ## +## ## # ## +## # ## +## ### ## # ## +""" #|# #|# #|# """ + + winds = """ +>>>vvvvvvvvvvvvvvvvvvvvvvv<<< +>>>vvvvvvvvvvvvvvvvvvvvvvv<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +""" + +class level43(boarddef.Level): + s = LSpringy, RSpringy, LSpringy, RSpringy + f = LFlappy + g = RFlappy + + walls = """ +## # # ## +## #g f # ## +## ## +## # # ## +## # # ## +##s # # # # s ## +## # # # # ## +## # # # ## +## # ## +## # s #s # ## +## # # # # # ## +## # # # # # # # # # ## +## # # # # # # # # # # # ## +## # # # # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # ## +## # # # # # # ## +## # # ## +## # # ## +## # # ## +## # # # # ## +## # # # # ## +## # # # # # # # ## +## # # # # # # # # # # # # ## +""" #|# #|# #|# """ + +class level44(boarddef.Level): + o = LGramy + p = RGramy + g = RFlappy + h = LFlappy + + top = 1 + + walls = """ +## ## +## ## +## ## +######## ## ## +## h ## ## ## +## g ## ## ###### ## +## h ## ## ## ## ## +## g ## ## ## ## ## +## h ## ## ## ## ## +## g ## ## ## ## ## +## ## ## ## ## ## +## ## ## ## ## h ## +## ## ## ## ## g ## +## ## ## ## ## h ## +## ## ## ## ## g ## +## ###### ## ## h ## +## ## ## g ## +## ## ######## +## ## +## ## +## ## +## ## +## p o p o p o ## +############################# +""" #|# #|# #|# """ + + winds = """ +>>>vvvvvvvvvvvvvvvvvvvvvvv<<< +>>>>>>>>vv<<<<<vv<<<>>>vvv<<< +>>^ vv ^<< +>>^ vv ^<< +>>^ vv ^<< +>>^ vv ^<< +>>^ vv >< ^<< +>>^ vv ^^ ^<< +>>^ vv ^^ ^<< +>>^ vv ^^ ^<< +>>^ vv ^^ ^<< +>>^ vv ^^ ^<< +>>^ vv ^^ ^<< +>>^ vv ^^ ^<< +>>^ >< ^^ ^<< +>>^ ^<<<>>>^ ^^ ^<< +>>^ ^^ ^<< +>>^ ^^ ^<< +>>^ >>>>>^^<<<<<^<< +>>^ ^<< +>>^ ^<< +>>^vvvvvvvvvvvvvvvvvvvvvvv^<< +>>^vvvvvvvvvvvvvvvvvvvvvvv^<< +>>^vvvvvvvvvvvvvvvvvvvvvvv^<< +""" + +class level45(boarddef.Level): + b = LBlitzy, RBlitzy + + top = 1 + water = 1 + + walls = """ +## ## +## ## ## ## +## ## ## # # ## ## ## +## # # # # # # ## +## # # #bb # # # ## +## #bb # ## ## #bb # ## +## ## ## ## ## ## +## ## ## ## +## ## ## # # ## ## ## +## # # # # # # ## +## # # # # # # ## +## #bb # # # #bb # ## +## ## ## #bbbb # ## ## ## +## ## ## ## +## ## ## ## ## ## +## # # ## ## # # ## +## # # # # # # ## +## #bb # # # #bb # ## +## ## ## #bb # ## ## ## +## ## ## ## +## ## +## ## +## ## +### ##################### ### +""" #|# #|# #|# """ + + winds = """ +>>>vvvvvvvvvvvvvvvvvvvvvvv<<< +>>>>>>>>>>>>>xxx<<<<<<<<<<<<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +>>xxxxxxxxxxxxxxxxxxxxxxxxx<< +""" + +class level46(boarddef.Level): + m = LMonky, RGramy + n = LGramy, RMonky + g = [RGhosty] * 4 + h = [LGhosty] * 4 + + letter = 1 + fire = 1 + + walls = """ +############# ############# +## ## +## ## +## ## +############# ############# +## ## ## ## +## g ## ## h ## +## ## mnmnm ## ## +## ########### ## +## ## +## ## +## ## +######## ### ######## +## ## # ## ## +## ## # ## ## +##mnm ## # ##mnm ## +######## ##### ######## +## # ## +## # ## +## ####### ## +## ## +## ## +## # # # ## +############ ############ +""" #|# #|# #|# """ + + winds = """ +>>> ^ <<< +>>>>>>>>>>>>>>^<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ x ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +""" + +class level47(boarddef.Level): + l = LOrcy + r = ROrcy + n = LNasty + m = RNasty + + top = 1 + lightning = 1 + + walls = """ +## ################### ## +## ################### ## +## # ## +## # ## +## ## +## ## +## ## +###### #### #### ###### +## # # ## +## # # ## +## # # ## +## ##### ####### ##### ## +## ## +## ## +## ## +##### ###### ###### ##### +## # # ## +## # # ## +##rmr # lr # lnl ## +######### ####### ######### +## # # # ## +## # # # ## +## #rmrmrmr #lnlnlnl # ## +## ################### ## +""" #|# #|# #|# """ + + winds = """ +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvv<< +>>>>>>>vvvvvvvvvvvvvvv<<<<<<< +>>>>>>>>^^^^^^^^^^^^^<<<<<<<< +>>>>>>>>^^^^^^^^^^^^^<<<<<<<< +>>>>>>>>^^^^^^^^^^^^^<<<<<<<< +>>^ ^<< +""" + +class levelFinal(boarddef.Level): + + walls = """ +############# ############# +## ## +## ## +## ## +## ## +## ## +#### ################# #### +## ### ### ## +## ## ## ## +####### ##### ####### +## ## +## ## +## ## +## ## +######### ######### +## ## +## ## +## ## +########## ########## +## ## +## ## +## ## +## ## +############# ############# +""" #|# #|# #|# """ +# nb.: the previous line has no purpose +# other than helping with wall alignment diff --git a/bubbob/levels/HouseOfFun.bin b/bubbob/levels/HouseOfFun.bin Binary files differnew file mode 100644 index 0000000..838344f --- /dev/null +++ b/bubbob/levels/HouseOfFun.bin diff --git a/bubbob/levels/Levels.bin b/bubbob/levels/Levels.bin Binary files differnew file mode 100644 index 0000000..fd176d1 --- /dev/null +++ b/bubbob/levels/Levels.bin diff --git a/bubbob/levels/LostLevels.bin b/bubbob/levels/LostLevels.bin Binary files differnew file mode 100644 index 0000000..a09e365 --- /dev/null +++ b/bubbob/levels/LostLevels.bin diff --git a/bubbob/levels/README.txt b/bubbob/levels/README.txt new file mode 100644 index 0000000..544b571 --- /dev/null +++ b/bubbob/levels/README.txt @@ -0,0 +1,23 @@ +=============================== +How to make your own level sets +=============================== + +The .bin files are MacBinary files from the original MacOS9 game. The +.py files are the levels that we did ourselves. To make new levels, you +need to make a new .py file in the current directory (bubbob/levels/). + +For an example, open CompactLevels.py with any text editor. The +structure should be fairly simple to understand even without knowledge +about Python. (Just don't try to look at RandomLevels.py first: it is a +full Python program that generates the levels completely randomly.) + +To get started, copy CompactLevels.py to a new name, like MyLevels.py, +and start editing it. You can remove or change levels, but the last one +should always be called LevelFinal and have no monster in it. All other +levels should have monsters, otherwise they'll be considered as the +final level. + +Also note that all levels need to have the same size (width and height), +but different level sets can have different sizes. For example, the +levels in CompactLevels.py are a bit smaller than the ones in the .bin +files, and the levels in scratch.py are a bit larger. diff --git a/bubbob/levels/RandomLevels.py b/bubbob/levels/RandomLevels.py new file mode 100644 index 0000000..5c0f77d --- /dev/null +++ b/bubbob/levels/RandomLevels.py @@ -0,0 +1,362 @@ +# +# Second try at automatically generating levels that are +# a bit more related to each other instead of being completely independent. +# + +from __future__ import generators +import sys, random, math +from random import uniform, choice, randrange + + +class Parameter(object): + def __init__(self, name, rng): + self.name = name + self.rng = rng + def __get__(self, instance, cls): + assert self.name not in instance.__dict__ + value = self.rng() + setattr(instance, self.name, value) + return value + +class ChoiceParameter(Parameter): + def __init__(self, name, list): + Parameter.__init__(self, name, lambda : choice(list)) + +class BoolParameter(Parameter): + def __init__(self, name, prob=0.5): + Parameter.__init__(self, name, lambda : random.random() < prob) + +def flat(mean,var): + return randrange(mean-var,mean+var+1) + +def dice(n,sides,orig=1): + result = 0 + for i in range(n): + result += orig+randrange(sides) + return result + +def fork(choice1, prob, choice2): + if random.random() < prob: + return choice1() + else: + return choice2() + +MnstrCategory = { + "Nasty": 0, + "Monky": 0, + "Ghosty": 1, + "Flappy": 1, + "Springy": 2, + "Orcy": 0, + "Gramy": 0, + "Blitzy": 2} +MnstrNames = MnstrCategory.keys() +Bonuses = ['letter', 'fire', 'lightning', 'water', 'top'] + +def mnstrclslist(name): + import boarddef + classname1 = 'L' + name + classname2 = 'R' + name + return [getattr(boarddef, classname1), getattr(boarddef, classname2)] + +class Shape: + basemnstr = ChoiceParameter('basemnstr', MnstrNames) + extramnstr = ChoiceParameter('extramnstr', range(4)) + samemnstr = BoolParameter('samemnstr') + baseshape = ChoiceParameter('baseshape', ' ODBGMPRWZS') + rooms = BoolParameter('rooms') + holes = BoolParameter('holes') + lines = ChoiceParameter('lines', ' -/|') + platforms = BoolParameter('platforms') + platholes = BoolParameter('platholes') + platfull = BoolParameter('platfull') + mess = ChoiceParameter('mess', ' ....!') + closed = BoolParameter('closed', 0.95) + bonuses = ChoiceParameter('bonuses', xrange(3**len(Bonuses))) + smooth = ChoiceParameter('smooth', range(4)) + startplats = BoolParameter('startplats', 0.98) + makespace = BoolParameter('makespace', 0.8) + straightfall = BoolParameter('straightfall', 0.8) + mirrored = BoolParameter('mirrored', 0.4) + enlargeholes = BoolParameter('enlargeholes', 0.9) + + all_parameters = [name for name in locals().keys() + if not name.startswith('_')] + + def __init__(self, shape=None): + if shape: + self.__dict__.update(shape.__dict__) + self.modified = 0 + + def set_gens(self, rooms=0, platforms=0, holes=0, smooth=0, mess=' ', lines=' '): + self.rooms = rooms + self.platforms = platforms + self.holes = holes + self.smooth = smooth + self.mess = mess + self.lines = lines + + def reset(self, attrname=None): + if attrname: + try: + del self.__dict__[attrname] + except KeyError: + pass + else: + self.__dict__.clear() + self.modified = 1 + + def __eq__(self, other): + return (self.__class__ is other.__class__ and + self.__dict__ == other.__dict__) + + def test_similar_parameters(self, prevlist): + similarity = 0 + rprevlist = prevlist[:] + rprevlist.reverse() + for param in Shape.all_parameters: + accum = 0 + for prev in rprevlist: + if getattr(self, param) != getattr(prev, param): + break + else: + accum += 1 + similarity += accum + minimum = min(4*len(prevlist), 7) + if not (minimum <= similarity <= 17): + self.reset() + + def test_not_too_often(self, prevlist): + for param, bad_value, delay in [ + ('mess', '.', 2), + ('mess', '!', 11), + ('holes', 1, 1), + ]: + if getattr(self, param) == bad_value: + for prev in prevlist[-delay:]: + if getattr(prev, param) == bad_value: + self.reset(param) + + def test_mess_hole(self, prevlist): + if self.mess == '!': + self.holes = 1 + + all_tests = [value for (name, value) in locals().items() + if name.startswith('test_')] + + def accept(self, lvl): + f = lambda d=self.difficulty : randrange(3, 4+int(9*d)) + lvl.mlist = [(mnstrclslist(self.basemnstr), f)] + repeat = choice([2,2,3]) - self.extramnstr + if repeat > 1: + lvl.mlist *= repeat + if self.extramnstr: + othermnstr = [name for name in MnstrNames if name!=self.basemnstr] + if self.samemnstr: + othermnstr = [name for name in othermnstr + if MnstrCategory[name]==MnstrCategory[self.basemnstr]] + random.shuffle(othermnstr) + for name in othermnstr[:self.extramnstr]: + lvl.mlist.append((mnstrclslist(name), f)) + + lvl.genwalls = [] + + if self.baseshape == 'G': + lvl.genwalls.append((RandomLevel.grids, + uniform(0.7,0.8), + uniform(0.7,0.8))) + self.set_gens() + if self.baseshape == 'P': + lvl.genwalls.append((RandomLevel.pegs, + uniform(0.1,0.2), + uniform(0.45,0.7), + choice([0,1,1,1]))) + self.set_gens(smooth=3) + self.closed = random.random() < 0.80 + if self.baseshape == 'B': + nr = choice([0,0,1]) + lvl.genwalls.append((RandomLevel.bouncers, + dice(1, 100) + 250 - nr*200, # length + uniform(0.7, 1.7), + nr)) + self.set_gens(smooth=3) + if self.baseshape == 'W': + nr = dice(1, 3) + 2 + lvl.genwalls.append((RandomLevel.walkers, + dice(2, 100) + 100, # length + nr, nr + dice(2, 3), + choice([0,1]))) + self.set_gens() + if self.baseshape == 'R': + lvl.genwalls.append((RandomLevel.rivers, + randrange(3,(lvl.WIDTH-4)/4), # the number of rivers + uniform(0.3, 1.4), # the side stepping threshold + 10)) # the max side stepping size + self.set_gens() + if self.baseshape == 'Z': + lvl.genwalls.append((RandomLevel.zigzag,)) + self.set_gens() + if self.baseshape == 'M': + lvl.genwalls.append((RandomLevel.mondrian,)) + self.set_gens() + if self.baseshape == 'O': + lvl.genwalls.append((RandomLevel.remove_joined_blocks,)) + self.set_gens() + if self.baseshape == 'S': + lvl.genwalls.append((RandomLevel.platforms_reg,)) + self.set_gens() + self.makespace = random.random() < 0.1 + if self.closed: + self.startplats = 0 + if self.baseshape == 'D': + lvl.genwalls.append((RandomLevel.discrete_blocks,)) + self.set_gens() + self.makespace = random.random() < 0.1 + if self.closed: + self.startplats = 0 + + if self.rooms: + nr = dice(2, 6) + lvl.genwalls.append((RandomLevel.rooms, + lambda : flat(9-nr,2), # the half size of the room + lambda : uniform(0.8,1.2), # the excentricity of the room + nr)) # the number of rooms + if self.lines != ' ': + rng_angle = { + '-': lambda : 0, + '/': None, # default + '|': lambda : math.pi/2, + } + lvl.genwalls.append((RandomLevel.lines, + lambda : dice(8,3), # line length + dice(2,4), # number of lines + rng_angle[self.lines])) + if self.platforms: + nplat = dice(2,4,0) + if nplat: space = flat((lvl.HEIGHT-1)/nplat/2,(lvl.HEIGHT-1)/nplat/2-1) + else: space = 1 + if self.platholes: + nholes = lambda : dice(1,3) + else: + nholes = lambda : 0 + wholes = lambda : dice(2,3) + full = self.platfull + lvl.genwalls.append((RandomLevel.platforms, + (nplat,space), # number of platform and spacing + (nholes,wholes), # number of holes and width + full)) # full width platform + if self.mess != ' ': + threshold = { + '.': 0.02 + 0.08*random.random(), # normal + '!': 0.25 + 0.2 *random.random(), # super-filled + } + lvl.genwalls.append((RandomLevel.mess, threshold[self.mess])) + if self.holes: + nh = choice([1,1,2,2,2,3,3,3,4,5]) + lvl.genwalls.append((RandomLevel.holes, + lambda : flat(9-nh,2), # radius of the holes + lambda : uniform(0.9,1.1), # excentricity + nh, # number of holes + lambda : choice([0,0,0,1]))) # circle or rectangle + if self.closed: + lvl.genwalls.append((RandomLevel.close,)) + if self.smooth > 0: + # smooth away all lone empty spaces + lvl.genwalls.append((RandomLevel.smooth, 1.0, 1)) + # possibly smooth away some lone bricks + if self.smooth == 2: + lvl.genwalls.append((RandomLevel.smooth, 0.25, 0)) + elif self.smooth == 3: + lvl.genwalls.append((RandomLevel.smooth, 0.75, 0)) + if self.startplats: + lvl.genwalls.append((RandomLevel.startplatform, )) + lvl.genwalls.append((RandomLevel.openstartway, )) + + if self.makespace: + lvl.genwalls.append((RandomLevel.make_space, )) + + if self.straightfall: + lvl.genwalls.append((RandomLevel.prevent_straight_fall, )) + + if self.mirrored: + lvl.genwalls.append((RandomLevel.mirror, )) + + if self.enlargeholes: + lvl.genwalls.append((RandomLevel.enlarge_tiny_holes, )) + + lvl.genwalls.append((RandomLevel.generate_wind, )) + b = self.bonuses + for name in Bonuses: + setattr(lvl, name, (b % 3) == 1) + b = b // 3 + lvl.autogen_shape = self + + +def generate_shape(prevlist): + tests = Shape.all_tests + s = Shape() + for i in range(100): + s1 = Shape(s) + random.shuffle(tests) + for test in tests: + test(s1, prevlist) + if not s1.modified and s1 == s: + break + s = s1 +# else: +# sys.stdout.write('*') + del s.modified + return s + +def makeshapes(nblevels=25): + shapelist = [] + for i in range(nblevels): + s = generate_shape(shapelist) + s.difficulty = float(i+1)/nblevels + yield s + shapelist.append(s) + if len(shapelist) == 10: + del shapelist[:] + +def GenerateLevels(): +# print 'generating levels', + Levels = [] + for s in makeshapes(): + class level(RandomLevel): + WIDTH = 28 + HEIGHT = 23 + def enter(self, *args, **kw): + result = RandomLevel.enter(self, *args, **kw) + params = self.autogen_shape.__dict__.items() + params.sort() +# for keyvalue in params: +# print '%20s: %s' % keyvalue + return result + s.accept(level) + Levels.append(level) +# sys.stdout.write('.') +# sys.stdout.flush() +# print + class levelfinal(RandomLevel): + WIDTH = level.WIDTH + HEIGHT = level.HEIGHT + genwalls = [(RandomLevel.platforms,(5,3),(lambda:flat(2,1),lambda:flat(6,2)),1), + (RandomLevel.close,)] + Levels.append(levelfinal) + return Levels + +def GenerateSingleLevel(width, height): + [s] = makeshapes(1) + class level(RandomLevel): + WIDTH = width + HEIGHT = height + s.accept(level) + return level + +if __name__ == '__main__': + for s in makeshapes(): + print s.__dict__ +else: + rnglevel = {} + execfile('levels/rnglevel', rnglevel) + RandomLevel = rnglevel['RandomLevel'] diff --git a/bubbob/levels/rnglevel b/bubbob/levels/rnglevel new file mode 100644 index 0000000..cfa6ec0 --- /dev/null +++ b/bubbob/levels/rnglevel @@ -0,0 +1,1276 @@ +from random import * +from math import * + +import boarddef +from boarddef import LNasty, LMonky, LGhosty, LFlappy +from boarddef import LSpringy, LOrcy, LGramy, LBlitzy +from boarddef import RNasty, RMonky, RGhosty, RFlappy +from boarddef import RSpringy, ROrcy, RGramy, RBlitzy + +def flat(mean,var): + return randrange(mean-var,mean+var+1) + +def dice(n,sides,orig=1): + result = 0 + for i in range(n): + result += orig+randrange(sides) + return result + +def fish(mu): + def fact(n): + r = 1. + for i in range(1,n+1): + r *= i + return r + scale = fact(0)/exp(-mu) + dens = [] + while 1: + x = len(dens) + dens.append(int(scale*exp(-mu)*pow(mu,x)/fact(x)+0.5)) + if x > mu and dens[-1] == 0: + break + table = [] + x = 0 + for d in dens: + for i in range(d): + table.append(x) + x += 1 + return choice(table) + + +class RandomLevel(boarddef.Level): + WIDTH = 32 + HEIGHT = 28 + MAXTRY = 1000 + # parameters of the 'mess generator' + # mess_prob : the probability that a cell turn into a wall + + def __init__(self,num): + if hasattr(self.__class__, 'walls'): + #print 'Reusing previously generated level' + #print self.__class__.walls + self.walls = self.__class__.walls + boarddef.Level.__init__(self,num) + return + + #print 'Generating a new level' + self.reset(fill=False) + + self.windmap = [ [' ' for x in range(self.WIDTH)] for y in range(self.HEIGHT) ] + + if hasattr(self, 'auto'): + self.generate() + self.do_bonuses() + + for gw in self.genwalls: + gw[0](self,*gw[1:]) + + if hasattr(self, 'mlist'): + self.do_monsters() + + self.dig_vertical_walls() + self.do_walls() + self.walls = self.__class__.walls + #print self.walls + + self.do_winds() + self.winds = self.__class__.winds + + boarddef.Level.__init__(self,num) + + def reset(self, fill=False): + if fill: + w = '#' + f = 0 + else: + w = ' ' + f = 1 + # map for the walls + self.wmap = [ [w for x in range(self.WIDTH)] for y in range(self.HEIGHT) ] + # map of the free cells + self.fmap = [ [f for x in range(self.WIDTH)] for y in range(self.HEIGHT) ] + + def setw(self,x,y,c='#'): + if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: + return + if self.fmap[y][x]: + self.wmap[y][x] = c + self.fmap[y][x] = 0 + + def getw(self,x,y): + if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: + return '#' + return self.wmap[y][x] + + def clrw(self,x,y): + if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: + return + self.wmap[y][x] = ' ' + self.fmap[y][x] = 1 + + def lockw(self,x,y,c=0): + if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: + return + self.fmap[y][x] = c + + def setwind(self,x,y,c=' '): + if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: + return + self.windmap[y][x] = c + + def getwind(self,x,y): + if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: + return ' ' + return self.windmap[y][x] + + def wind_rect(self,x,y,w,h,ccw=0): + "Set a wind in a rectangle which will move the bubbles cw or ccw" + if w < 1 or h < 1: + return + if ccw == 1: + for dx in range(w): + self.setwind(x+dx+1, y, '<') + self.setwind(x+dx, y+h, '>') + for dy in range(h): + self.setwind(x, y+dy, 'v') + self.setwind(x+w, y+dy+1, '^') + else: + for dx in range(w): + self.setwind(x+dx, y, '>') + self.setwind(x+dx+1, y+h, '<') + for dy in range(h): + self.setwind(x, y+dy+1, '^') + self.setwind(x+w, y+dy, 'v') + + def mirror(self): + "Mirror the level vertically." + for y in range(self.HEIGHT): + for x in range(self.WIDTH/2): + self.wmap[y][x] = self.wmap[y][self.WIDTH-x-1] + + def dig_well_until_space(self, x=1, yadj=1): + "Digs a well either up or down and stops when it encounters first empty wall space." + if yadj == 1: + y = 0 + else: + yadj = -1 + y = self.HEIGHT-1 + while (y < self.HEIGHT) and (y >= 0): + self.clrw(x,y) + self.clrw(x+1,y) + y += yadj + if ((self.getw(x,y) == ' ') and (self.getw(x+1,y) == ' ')): + break + + def enlarge_tiny_holes(self): + "Makes one-block size holes wider." + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + if self.wmap[y][x] == ' ': + single = 0 + for dx in range(x-1,x+2): + for dy in range(y-1,y+2): + if self.getw(dy,dx) == '#': + single = single + 1 + if single == 8: + if x > (self.WIDTH / 2): + self.clrw(x-1,y) + else: + self.clrw(x+1,y) + + def make_space(self, gens=-1): + "Removes walls from a level, to make it more playable." + if gens == -1: + gens = randint(0,62)+1 + if gens & 1: # top + for x in range(self.WIDTH): + self.clrw(x,1) + if random() < 0.5: + self.clrw(x,2) + if gens & 2: # bottom + for x in range(self.WIDTH): + self.clrw(x,self.HEIGHT-1) + if random() < 0.5: + self.clrw(x,self.HEIGHT-2) + if gens & 4: # middle + y = randint(0,self.HEIGHT/10) + (self.HEIGHT/2) + for x in range(self.WIDTH): + self.clrw(x,y) + if random() < 0.5: + self.clrw(x,y-1) + if random() < 0.5: + self.clrw(x,y+1) + if gens & 8: # left + x = randint(0,self.WIDTH/4) + self.dig_well_until_space(x, 1) + self.dig_well_until_space(x, -1) + if gens & 16: # right + x = randint(0,self.WIDTH/4) + self.dig_well_until_space(self.WIDTH-x-2, 1) + self.dig_well_until_space(self.WIDTH-x-2, -1) + if gens & 32: # center + self.dig_well_until_space(self.WIDTH/2, 1) + self.dig_well_until_space(self.WIDTH/2, -1) + + def generate_wind1(self, rndchoice=1, choices=[' ',' ',' ','x','>','<','^','^','v'], xsize=-1,ysize=-1): + """Makes a random wind pattern. Parameters: + 0: if 1=randomly select from choices, else select in order + 1: a list of the choices that are allowed. + 2: horizontal size of wind blocks + 3: vertical size of wind blocks + """ + choicenum = 0 + if xsize == -1: + xsize = randint(1, self.WIDTH) + if ysize == -1: + ysize = randint(1, self.HEIGHT) + if xsize < 1: + xsize = 1 + elif xsize > self.WIDTH: + xsize = self.WIDTH + if ysize < 1: + ysize = 1 + elif ysize > self.HEIGHT: + ysize = self.HEIGHT + for x in range((self.WIDTH/xsize)+1): + for y in range((self.HEIGHT/ysize)+1): + if rndchoice == 1: + wdir = choice(choices) + else: + wdir = choices[choicenum] + choicenum = (choicenum + 1) % len(choices) + for dx in range(xsize+1): + for dy in range(ysize+1): + self.setwind(x*xsize+dx,y*ysize+dy,wdir) + # make sure that the special bubbles can come into screen + for x in range(self.WIDTH): + self.setwind(x, 0, ' ') + self.setwind(x, self.HEIGHT-1, ' ') + + def wind_sidewalls(self): + """Make sure the left and side walls have updraft next to them + """ + for y in range(self.HEIGHT): + self.setwind(0,y,'^') + self.setwind(self.WIDTH-1,y,'^') + + def wind_wallblocking(self, winddirs): + """Sets up wind depending on the number of walls around each place. + winddirs is an array of 16 wind chars. + directions with walls count as: 1=N, 2=E, 4=S, 8=W + 16th place is used if there is wall at the position. + """ + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + walld = 0 + if self.getw(x,y) == '#': + walld = 16 + else: + if self.getw(x,y-1) == '#': + walld = walld + 1 + if self.getw(x+1,y) == '#': + walld = walld + 2 + if self.getw(x,y+1) == '#': + walld = walld + 4 + if self.getw(x-1,y) == '#': + walld = walld + 8 + wnd = winddirs[walld] + self.setwind(x,y,wnd) + + def wind_wallblocking256(self, winddirs): + """Sets up wind depending on the number of walls around each position. + winddirs is an array of 257 wind chars (one of ' x<>^v-'), where '-' means + to use '>' or '<', pointing towards center of level. + directions with walls count as: 1=N, 2=NE, 4=E, 8=SE, 16=S, 32=SW, 64=W, 128=NW + 257th place is use if there is wall at the position. + """ + mdirs = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)] + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + windd = 0 + if self.getw(x,y) == '#': + windd = 256 + else: + for d in range(8): + dx = x + mdirs[d][0] + dy = y + mdirs[d][1] + if self.getw(dx, dy) == '#': + windd = (1 << d) + wd = choice(winddirs[windd]) + if wd == '-': + if x < self.WIDTH / 2: + wd = '>' + else: + wd = '<' + self.setwind(x,y, wd) + + def generate_wind(self, gens = -1): + """Chooses one of the wind pattern generators and uses that to generate the winds. + 0: choose what generator to use. + """ + if gens == -1: + gens = choice([1,1,2,3,4,4,4,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]) + if gens == 1: # totally random pattern + self.generate_wind1() + elif gens == 2: # wind "layers" + self.generate_wind1(0, ['x','^','^','^'],self.WIDTH,1) + self.wind_sidewalls() + elif gens == 3: # "wiggly" winds + self.generate_wind1(1, ['^','<','^','>'],1,1) + self.wind_sidewalls() + elif gens == 4: # "normal" wind pattern + self.wind_sidewalls() + dx = (self.WIDTH/2) + if random() < 0.7: + dy = 1 # usual height where bubbles collect + else: + dy = randint(1, self.HEIGHT-1) + for x in range(dx-3, dx+3): + self.setwind(x,dy,'x') + for x in range(dx-2): + self.setwind(x,dy,'>') + self.setwind(self.WIDTH-x-1,dy,'<') + elif gens == 5: # bubbles are stopped by horizontal walls + self.wind_sidewalls() + for x in range(self.WIDTH): + for y in range(self.HEIGHT-2): + if self.getw(x,y) == '#': + if self.getw(x,y+1) == ' ': + self.setwind(x,y+1,'x') + elif gens == 6: # bubbles move next the walls, rotating cw or ccw + if random() < 0.5: #clockwise + winddirs = [' ','>','v','v','<',' ','<','<','^','>',' ','v','^','>','^','x','x'] + else: + winddirs = [' ','<','^','<','>',' ','^','<','v','v',' ','v','>','>','^','x','x'] + self.wind_wallblocking(winddirs) + elif gens == 7: # bubbles move up in column(s) that zig-zag left and right + wid = choice([self.WIDTH, randint(2, self.WIDTH), randint(2, self.WIDTH)]) + xofs = (self.WIDTH % wid) / 2 + ofs = choice([0,1]) + for dx in range(0, self.WIDTH-wid+1, wid): + for x in range(wid): + for y in range(self.HEIGHT): + if (y+ofs) & 1: + self.setwind(x+dx+xofs,y,'<') + if x == 0: + self.setwind(x+dx+xofs,y,'^') + else: + self.setwind(x+dx+xofs,y,'>') + if x == wid-1: + self.setwind(x+dx+xofs,y,'^') + elif gens == 8: # bubbles move towards the map centerline at random height + for x in range(self.WIDTH): + y = randint(1, self.HEIGHT-1) + if x < (self.WIDTH/2): + self.setwind(x,y,'>') + else: + self.setwind(x,y,'<') + self.setwind(x,0,'v') + self.setwind(x,self.HEIGHT-1,'^') + for y in range(self.HEIGHT): + self.setwind(self.WIDTH/2,y,'x') + elif gens == 9: # bubbles move towards the side walls at random height + for x in range(self.WIDTH): + y = randint(1, self.HEIGHT-1) + if y & 1: + self.setwind(x,y,'>') + else: + self.setwind(x,y,'<') + self.setwind(x,0,'v') + self.setwind(x,self.HEIGHT-1,'^') + for y in range(self.HEIGHT): + self.setwind(0,y,'x') + self.setwind(self.WIDTH-1,y,'x') + elif gens == 10: # bubbles move up-down + ofs = choice([0,1]) + dir_l = choice(['>', '>', '<']) + dir_r = choice(['<', '<', '>']) + for x in range(self.WIDTH): + if x < (self.WIDTH / 2): + self.setwind(x, 0, dir_r) + self.setwind(x,self.HEIGHT-1,dir_l) + else: + self.setwind(x, 0, dir_l) + self.setwind(x,self.HEIGHT-1,dir_r) + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + if (x+ofs) & 1: + self.setwind(x,y+1,'^') + else: + self.setwind(x,y-1,'v') + elif gens == 11: # bubbles rotate + self.wind_sidewalls() + for z in range(20): + wid = randint(2,self.WIDTH/2) + hei = randint(2,self.HEIGHT/2) + y = randint(1, self.HEIGHT - hei - 1) + x = randint(1, self.WIDTH - wid - 1) + ok = 1 + for dx in range(wid): + if self.getwind(x+dx+1, y) != ' ': + ok = 0 + if self.getwind(x+dx, y+hei) != ' ': + ok = 0 + for dy in range(hei): + if self.getwind(x, y+dy) != ' ': + ok = 0 + if self.getwind(x+wid, y+dy+1) != ' ': + ok = 0 + if ok == 1: + self.wind_rect(x,y,wid,hei, random() < 0.5) + elif gens == 12: # bubbles gravitate towards a certain spot + dx = randint(1,self.WIDTH-1) + dy = randint(1,self.HEIGHT-1) + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + ax = abs(dx - x) + ay = abs(dy - y) + sx = cmp(dx - x, 0) + sy = cmp(dy - y, 0) + winds = [' ',' ',' '] + if ax < 2 and ay < 2: + winds = ['x'] + else: + if sx < 0: + winds += ['<'] + elif sx > 0: + winds += ['>'] + else: + if sy > 0: + winds = ['v'] + elif sy < 0: + winds = ['^'] + else: + winds = ['x'] + if sy < 0: + winds += ['^'] + elif sy > 0: + winds += ['v'] + else: + if sx > 0: + winds = ['>'] + elif sx < 0: + winds = ['<'] + else: + winds = ['x'] + self.setwind(x,y,choice(winds)) + elif gens == 13: # bubbles stop at some random positions + self.generate_wind1(1, [' ',' ',' ',' ',' ',' ',' ','x'],1,1) + self.wind_sidewalls() + elif gens == 14: # bubbles move cw and ccw in alternating rectangles + m = max(self.WIDTH / 2, self.HEIGHT / 2) + cwofs = choice([0,1]) + thk = choice([1,1,2,2,3,4,randint(1,m/2)]) + for dx in range(m): + cw = ((dx / thk) + cwofs) % 2 + self.wind_rect(dx,dx, self.WIDTH-(dx*2), self.HEIGHT-(dx*2), cw) + elif gens == 15: # bubbles move cw or ccw in rectangles + m = max(self.WIDTH / 2, self.HEIGHT / 2) + cw = choice([0,1]) + for dx in range(m): + self.wind_rect(dx,dx, self.WIDTH-(dx*2), self.HEIGHT-(dx*2), cw) + elif gens == 16: + xs = randint(2, (self.WIDTH/2)-1) + ys = randint(2, (self.HEIGHT/2)-1) + rx = (self.WIDTH / xs) + 1 + ry = (self.HEIGHT / ys) + 1 + cwchanges = choice([0,0,0,0,0,0,0,0,1,1,1,1,2,2,3]) + if cwchanges == 0: + cw = random() < 0.5 + for x in range(rx): + if cwchanges == 1: + cw = random() < 0.5 + for y in range(ry): + if cwchanges == 2: + cw = random() < 0.5 + maxd = max((xs / 2), (ys / 2)) + for d in range(maxd): + if cwchanges == 3: + cw = random() < 0.5 + self.wind_rect(xs*x+d, ys*y+d, xs-2*d-1, ys-2*d-1, cw) + elif gens == 17: # bubbles bounce between walls + if random() < 0.5: # horizontal + winddirs = [' ',' ','<','<',' ',' ','<','<','>','>',' ',' ','>','>',' ',' ','x'] + else: # vertical + winddirs = [' ','v',' ','v','^',' ','^',' ',' ','v',' ','v','^',' ','^',' ','x'] + self.wind_wallblocking(winddirs) + elif gens == 18: # generate winds based on a random 3x3 matrix ruleset + winddirs = [] + for z in range(257): + winddirs.append(choice([' ',' ',' ','x','^','v','-'])) + winddirs[0] = ' ' + winddirs[256] = choice(['x',' ']) + self.wind_wallblocking256(winddirs) + elif gens == 19: # bubbles will move downwards in a zig-zag pattern + y = 0 + x1 = randint(0, self.WIDTH-1) + while y < self.HEIGHT: + x2 = randint(0, self.WIDTH-1) + if x1 < x2: + self.setwind(x1,y, '>') + else: + self.setwind(x1,y, '<') + dy = choice([1,1,1,2]) + self.setwind(x2,y, 'v') + y += dy + x1 = x2 + + def smooth(self, threshold, rev): + """Remove wall blocks that are surrounded by 4 empty places. + 0: probability which a wall cell is turned into space + 1: smooth away walls or smooth away empty spaces? + """ + # make a copy of self.wmap and adds '#' at the end of line, for + # the overflowing indexing below: [x-1] and [x+1] + tmpwmap = [ line + ['#'] for line in self.wmap ] + if rev == 0: + chr = ' ' + else: + chr = '#' + for x in range(self.WIDTH): + for y in range(1,self.HEIGHT-1): + count = 0 + if tmpwmap[y+1][x] == chr: + count = count + 1 + if tmpwmap[y-1][x] == chr: + count = count + 1 + if tmpwmap[y][x+1] == chr: + count = count + 1 + if tmpwmap[y][x-1] == chr: + count = count + 1 + if (count >= 4) and (random() < threshold): + if rev == 0: + self.clrw(x,y) + else: + self.setw(x,y) + + def mess(self, threshold): + """Random fill of the board with walls. + Only one argument, the probability that + a cell turns out to be a wall. + """ + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + if random() < threshold: + self.setw(x,y) + + def zigzag_lr(self): + """Generate the level with random left-right zig-zags. + """ + first = 1 + self.reset(fill=False) + y = choice([0,0,2,3,3,4]) + while y < (self.HEIGHT-2): + if first == 1: + first = 0 + x1 = x2 = randint(2, self.WIDTH-3) + else: + x2 = randint(2, self.WIDTH-3) + while (x2 > (x1-3)) and (x2 < (x1+3)): + x2 = randint(2, self.WIDTH-3) + for dx in range(min(x1,x2+1), max(x1,x2+1)): + self.setw(dx,y) + dy = choice([2,2,3,3,3,4]) + for dy in range(dy+1): + self.setw(x2,y+dy) + y = y + dy + x1 = x2 + + def zigzag_ud(self): + """Generate the level with random up-down zig-zags. + """ + first = 1 + self.reset(fill=False) + x = -1 + while x < self.WIDTH: + if first == 1: + first = 0 + y1 = y2 = randint(2, self.HEIGHT-1) + else: + y2 = randint(2, self.HEIGHT-1) + while (y2 > (y1-2)) and (y2 < (y1+2)): + y2 = randint(2, self.HEIGHT-1) + for dy in range(min(y1,y2+1), max(y1,y2+1)): + self.setw(x,dy) + dx = choice([3,4,4,4,5,6]) + for dx in range(dx+1): + self.setw(x+dx,y2) + x = x + dx + y1 = y2 + + def zigzag(self): + """Generate a level with a random zig-zag form. + """ + if random() < 0.5: + self.zigzag_lr() + else: + self.zigzag_ud() + + def platforms(self, (nplat, space), (rng_holes, rng_width), full=1): + """Place random platforms. + args is a tuple with the following fields: + 0: a tuple containing the number of platforms and + the minum space between two platforms, + 1: a tuple indicating in order: + - the rng for the number of holes per platform + - the rng for the width of the holes, + 2: a flag indicating whether the platform should cross + the whole level or not. + """ + plat = [] + for i in range(nplat): + ntry = 100 + while ntry: + y = randint(0,self.HEIGHT-1) + found = 0 + for old in plat: + if abs(old-y) <= space: + found = 1 + break + if not found: + plat.append(y) + break + ntry -= 1 + if not ntry: + continue # ignore platform + if full: + x = 0 + w = self.WIDTH + else: + x = randint(0,self.WIDTH-1) + w = randint(0,self.WIDTH-1) + s = choice([-1,1]) + if s == -1: + w = min(w,x) + x -= w + else: + w = min(w,self.WIDTH-x) + for x1 in range(x,x+w): + self.setw(x1,y) + for i in range(rng_holes()): + hx = randint(x,x+w) + hw = rng_width() + for h in range(hx-hw/2,hx+hw/2): + self.clrw(h,y) + + def remove_joined_blocks(self): + """Removes randomly placed, random sized blocks of walls. + The blocks are usually joined, or if not, they're offset so that + it's possible to move from one block to another by jumping. + """ + self.reset(fill=True) + nrooms = randint(1, 4) + while nrooms: + nrooms -= 1; + x = 0 + while x < self.WIDTH: + wid = randint(2,8) + hei = randint(2,6) + y = randint(2, self.HEIGHT - hei - 1) + for dx in range(wid): + for dy in range(hei): + self.clrw(x+dx, y+dy) + x += wid + choice([-2,-2,-1,-1,0]); + + def discrete_blocks(self, blocks = -1): + """Put certain size blocks randomly, but so that they don't touch each other. + """ + self.reset(fill=False) + if blocks == -1: + if random() < 0.75: + blocks = [(4,2),(2,4)] # CompactLevels, level 16 + if random() < 0.30: + blocks.append((2,2)) + if random() < 0.20: + blocks.append((6,2)) + if random() < 0.10: + blocks.append((8,2)) + else: + blocks = [] + while len(blocks) == 0: + for bz in range(10): + if random() < 0.3: + blocks.append((bz+1,1)) + ntry = 300 + while ntry: + ntry -= 1 + doput = 1 + block = choice(blocks) + wid = block[0] + hei = block[1] + x = randint(0,self.WIDTH-wid-2) + y = randint(1,self.HEIGHT-hei-3) + for dx in range(x,x+wid+2): + for dy in range(y,y+hei+2): + if self.getw(dx,dy) == '#': + doput = 0 + if doput: + for dx in range(x+1,x+wid+1): + for dy in range(y+1,y+hei+1): + self.setw(dx,dy) + + def lines(self, rng_len, nlines, rng_angle=None): + """Generate a set of lines in any direction. It takes three + arguments, a rng for the length the lines, the number of lines, + and a rng for the angle. + """ + if rng_angle is None: + rng_angle = lambda : choice([0]+[pi/i for i in range(3,21)]+[-pi/i for i in range(3,21)]) + for i in range(nlines): + len = rng_len() + angle = rng_angle() + ntry = self.MAXTRY + while ntry: + sx = randint(0,self.WIDTH-1) + sy = randint(0,self.HEIGHT-1) + dx = int(sx + len*cos(angle) + 0.5) + dy = int(sy + len*sin(angle) + 0.5) + if dx < self.WIDTH and dy < self.HEIGHT and dx >= 0 and dy >= 0: + break + ntry -= 1 + if ntry == 0: + break + if abs(dx-sx) > abs(dy-sy): + for x in range(dx-sx+1): + y = (2*(dy-sy)*x/(dx-sx)+1)/2 + self.setw(sx+x,sy+y) + else: + for y in range(dy-sy+1): + x = (2*(dx-sx)*y/(dy-sy)+1)/2 + self.setw(sx+x,sy+y) + + def rooms(self, rng_radius, rng_e, n_rooms): + """Generate rooms. It takes the following arguments: + 0: the rng for the radius of the room + 1: the rng for the excentricity of the room + 2: the number of rooms + """ + for i in range(n_rooms): + cx = randint(0,self.WIDTH-1) + cy = randint(0,self.HEIGHT-1) + r = rng_radius() + e = rng_e()*1.0 + left = cx-int(r*e+0.5) + right = cx+int(r*e+0.5) + top = cy-int(r/e+0.5) + bottom = cy+int(r/e+0.5) + for x in range(left,right+1): + self.setw(x,top) + self.setw(x,bottom) + for y in range(top,bottom+1): + self.setw(left,y) + self.setw(right,y) + for x in range(left+1,right): + for y in range(top+1,bottom): + self.lockw(x,y) + + def holes(self, rng_radius, rng_e, n_holes, rng_rect): + """Generate a set of holes in the level. It takes four args: + 0: the rng for the radius of the holes + 1: the rng for the excentricity of the holes + 2: the number of holes + 3: the rng for the shape of the hole 0 for circular, 1 for rectangular + """ + for i in range(n_holes): + cx = randint(0,self.WIDTH-1) + cy = randint(0,self.HEIGHT-1) + r = rng_radius() + e = rng_e()*1.0 + rect = rng_rect() + for x in range(cx-int(r*e+0.5),cx+int(r*e+0.5)+1): + for y in range(cy-int(r/e+0.5),cy+int(r/e+0.5)+1): + if not rect and (((x-cx)/e)**2+((y-cy)*e)**2) > r**2: + continue + self.clrw(x,y) + + def grids(self, horizchance, vertchance): + """Generate a level with a grid of horizontal and vertical lines + 0: gaussian chance of each horizontal line part + 1: gaussian chance of each vertical line part + """ + self.reset(fill=False) + xsize = choice([3,3,3,4,4,4,4,5,6]) + ysize = choice([2,3,3,4,4,4,4,5]) + xofs = choice([-1,0,1]) + yofs = choice([-1,0,1]) + for x in range((self.WIDTH/xsize)+1): + for y in range((self.HEIGHT/ysize)+1): + dx = x*xsize + xofs + dy = y*ysize + yofs + if gauss(0,1) > horizchance: + for i in range(0,xsize+1): + self.setw(dx+i,dy) + if gauss(0,1) > vertchance: + for i in range(0,ysize+1): + self.setw(dx,dy+i) + + def pegs(self, pegchance, posadj, thick): + """Generate a level by putting pegs + 0: gaussian level of a peg appearance + 1: gaussian level of peg position adjustment + """ + self.reset(fill=False) + xdist = choice([3,3,3,4,4,5]) # distance between pegs + ydist = choice([2,3,3,3,4,5]) # distance between pegs + if not thick: + xdist = xdist - randint(0,1) + ydist = ydist - randint(0,1) + xadj = randint(0,4) - 2 + yadj = randint(0,4) - 2 + for x in range(self.WIDTH / xdist): + for y in range(self.HEIGHT / ydist): + if gauss(0,1) > pegchance: + dx = x * xdist + xadj + dy = y * ydist + yadj + if gauss(0,1) > posadj: + dx = dx + randint(0,2) - 1 + dy = dy + randint(0,2) - 1 + self.setw(dx,dy) + if thick: + self.setw(dx+1,dy) + self.setw(dx,dy+1) + self.setw(dx+1,dy+1) + + def mondrian(self, x1=2,y1=2,x2=-1,y2=-1, horiz=-1, mindepth=3): + """Generate a level that looks a bit like a Piet Mondrian painting, or + different sized rectangles stacked on top of each other. + 0-3: the size of the area to be split + 4: whether the first split is horizontal or vertical + 5: minimum number of splits to do + """ + if horiz == -1: + horiz = choice([0,1]) + if x2 == -1: + x2 = self.WIDTH-2 + if y2 == -1: + y2 = self.HEIGHT-2 + if (abs(x2-x1) < 6) or (abs(y2-y1) < 5): + return + mindepth = mindepth - 1 + if horiz == 1: + horiz = 0 + dy = randint(y1+2,y2-2) + for dx in range(min(x1,x2),max(x1,x2)): + self.setw(dx,dy) + if (random() < 0.75) or (mindepth > 0): + self.mondrian(x1,y1,x2,dy, horiz, mindepth) + if (random() < 0.75) or (mindepth > 0): + self.mondrian(x1,dy,x2,y2, horiz, mindepth) + else: + horiz = 1 + dx = randint(x1+3,x2-3) + for dy in range(min(y1,y2),max(y1,y2)): + self.setw(dx,dy) + if (random() < 0.75) or (mindepth > 0): + self.mondrian(x1,y1,dx,y2, horiz, mindepth) + if (random() < 0.75) or (mindepth > 0): + self.mondrian(dx,y1,x2,y2, horiz, mindepth) + + def bouncers(self, length, diradj, rev): + """Generate a level using a down and left or right moving walker + 0: how many steps does the walker take + 1: gaussian level, how often to change moving from left to right + 2: fill empty level with wall or reverse? + """ + if rev == 0: + self.reset(fill=True) + else: + self.reset(fill=False) + x = randint(0,self.WIDTH-2) + y = randint(0,self.HEIGHT-2) + lorr = choice([1, -1]) # move left or right + for i in range(length): + if rev == 0: + self.clrw(x,y) + self.clrw(x+1,y) + self.clrw(x,y+1) + self.clrw(x+1,y+1) + else: + self.setw(x,y) + self.setw(x+1,y) + x = x + lorr + y = y + 1 + if y > self.HEIGHT: + y = 0 + if x > self.WIDTH - 2: + x = self.WIDTH - 2 + lorr = -lorr + elif x < 0: + x = 0 + lorr = -lorr + if gauss(0,1) > diradj: + lorr = -lorr + + + def walkers(self, length, minturn, maxturn, isbig): + """Generate a level with a walker + 0: length of the walker: how many steps it walks + 1: minimum length it walks straight, before turning + 2: maximum length it walks straight, before turning + 3: is the trail is 1 or 2 blocks high + """ + # We start from a full wall + self.reset(fill=True) + x = randint(0,self.WIDTH-2) + y = randint(0,self.HEIGHT-2) + dir = randint(0,4) + dlen = 0 + for i in range(length): + self.clrw(x,y) + self.clrw(x+1,y) + if isbig == 1: + self.clrw(x,y+1) + self.clrw(x+1,y+1) + dlen = dlen + 1 + if dir == 0: + x = x - 2 + if x < 0: + x = self.WIDTH-2 + elif dir == 1: + y = y - 1 + if y < 0: + y = self.HEIGHT + elif dir == 2: + x = x + 2 + if x > (self.WIDTH - 2): + x = 0 + else: + y = y + 1 + if y > self.HEIGHT: + y = 0 + if dlen > randint(minturn, maxturn): + # turn 90 degrees + dir = (dir + choice([1,3])) % 4 + dlen = 0 + + def rivers(self, n_flow, side_threshold, side_shift): + """Generate flow paths by digging a big wall. The arguments are: + 0: the number of parallel flow to dig in the wall + 1: side_threshold is a gausian level for doing a step aside + 2: side_shift is the maximal size of the side step. + """ + # We start from a full wall + self.reset(fill=True) + for x in [0, self.WIDTH-2]+[randint(3,self.WIDTH-5) for f in range(max(0, n_flow-2))]: + for y in range(self.HEIGHT): + self.clrw(x,y) + self.clrw(x+1,y) + g = gauss(0,1) + if abs(g) > side_threshold: + # We want to move aside, let's find which side is the best: + if self.WIDTH/4 < x < 3*self.WIDTH/4: + side = random() > 0.5 + t = random() + if t > x*4/self.WIDTH: + side = 1 + elif t > (self.WIDTH-x)*4/self.WIDTH: + side = -1 + side_step = randint(1,side_shift) + if side > 0: + for i in range(x+2, min(x+2+side_step,self.WIDTH-1)): + self.clrw(i,y) + x = max(0,min(x+side_step, self.WIDTH-2)) + else: + for i in range(max(x-side_step,0),x): + self.clrw(i,y) + x = max(x-side_step, 0) + + def platforms_reg(self): + """Generate random platforms at regular y-intervals. + """ + self.reset(fill=False) + yadjs = [-2,-1,0,0,0,0,0,0,0,0,1,2] + y = randint(2,4) + yinc = randint(2,6) + yincadj = choice(yadjs) + ymax = self.HEIGHT-choice([1,1,1,1,1,2,2,2,3,3,4])-1 + while y < ymax: + holes = randint(choice([0,1,1,1,1]),7) + for x in range(0, self.WIDTH): + self.setw(x,y) + for i in range(holes): + x = randint(0, self.WIDTH-2) + self.clrw(x,y) + self.clrw(x+1,y) + y = y + yinc + yinc = yinc + yincadj + if yinc < 2: + yinc = 2 + yincadj = choice(yadjs) + if yinc > 6: + yinc = 6 + yincadj = choice(yadjs) + + def startplatform(self): + "Make sure there's free space with wall underneath for dragon start positions" + hei = choice([1,1,1,2,2,3]) + lft = choice([0,1]) + wid = choice([3,3,3,4,5]) + for x in range(lft, wid): + self.setw(x,self.HEIGHT-1) + self.setw(self.WIDTH-x-1,self.HEIGHT-1) + for y in range(hei+1): + self.clrw(x,self.HEIGHT-2-y) + self.clrw(self.WIDTH-x-1,self.HEIGHT-2-y) + + def openstartway(self): + "Make sure there is a way from the starting position to the center of the level. Reduces player frustrations." + gen = choice([0,0,0,1]) + if gen == 0: # horizontal open space to middle of level + ypos = choice([1,1,1,1,1,2,2,3,4,randint(1,self.HEIGHT/2)]) + hei = choice([1,1,1,2]) + for x in range(self.WIDTH/2): + for y in range(hei): + self.clrw(x, self.HEIGHT-1-ypos-y) + ypos = choice([1,1,1,1,1,2,2,3,4,randint(1,self.HEIGHT/2)]) + hei = choice([1,1,1,2]) + for x in range(self.WIDTH/2): + for y in range(hei): + self.clrw(self.WIDTH-x-1, self.HEIGHT-1-ypos-y) + elif gen == 1: # open way diagonally to NW or NS, third of a way to the level width + ypos = choice([1,1,1,1,1,2,2,3,4]) + wid = choice([2,2,2,2,2,3,3,4]) + for x in range(self.WIDTH/3): + for z in range(wid): + self.clrw(x+z, self.HEIGHT-1-x-ypos) + ypos = choice([1,1,1,1,1,2,2,3,4]) + wid = choice([2,2,2,2,2,3,3,4]) + for x in range(self.WIDTH/2): + for z in range(wid): + self.clrw(self.WIDTH-x-1-z, self.HEIGHT-1-x-ypos) + + def close(self): + "Just close the level with floor and roof" + for x in range(self.WIDTH): + self.setw(x,0) + self.setw(x,self.HEIGHT) + + def largest_vertical_hole(self, x): + "Returns the (start, stop) of the largest range of holes in column x." + if not (0 <= x < self.WIDTH): + return (0, 0) + ranges = [] + best = 0 + length = 0 + for y in range(self.HEIGHT+1): + if y < self.HEIGHT and self.getw(x,y) == ' ': + length += 1 + elif length > 0: + if length > best: + del ranges[:] + best = length + if length == best: + ranges.append((y-length, y)) + length = 0 + return choice(ranges or [(0, 0)]) + + def dig_vertical_walls(self): + "Check that no vertical wall spans the whole height of the level" + vwall = [] + for x in range(self.WIDTH): + spaces = 0 + for y in range(self.HEIGHT-1): # ignore bottom line spaces + spaces += self.getw(x,y) == ' ' + if spaces == 0 or (random() < 0.4**spaces): + vwall.append(x) + shuffle(vwall) + for x in vwall: + # look for the longest continuous space in each of the two + # adjacent columns, and extend these to the current column + def dig(y1, y2): + for y in range(y1, y2): + self.clrw(x, y) + return y1 < y2 and y1 < self.HEIGHT-1 + progress = False + for col in [x-1, x+1]: + y1, y2 = self.largest_vertical_hole(col) + progress |= dig(y1, y2) + while not progress: + progress |= dig(randint(0, self.HEIGHT-1), + randint(0, self.HEIGHT-1)) + + def prevent_straight_fall(self): + """Make platforms that prevent falling straight from top to bottom, but + still leave space for moving. + """ + falls = [] + for x in range(self.WIDTH): + for y in range(self.HEIGHT): + if self.getw(x,y) == '#': + break + else: + falls = falls + [x] + y = oldy = -10 + for x in falls: + while (y < oldy+2) and (y > oldy-2): + y = randint(2, self.HEIGHT-2) + for dy in range(y-1,y+2): + for dx in range(x-3, x+4): + self.clrw(dx,dy) + self.setw(x-1,y) + self.setw(x+1,y) + self.setw(x,y) + oldy = y + + def do_monsters(self): + """Create monsters based on the requested settings. + mlist is a list of monster setting. Each item is a tuple with: + 0: the list of monster to uses (each item might be a tuple) + 1: the rng for the number of monsters to pick in the list. + """ + current = 'a' + for ms in self.mlist: + n_monsters = ms[1]() + for idx in range(n_monsters): + self.__class__.__dict__[current] = choice(ms[0]) + ntry = self.MAXTRY + while ntry: + x = randint(0,self.WIDTH-2) + y = randint(0,self.HEIGHT-1) + + if self.getw(x,y) == self.getw(x+1,y) == ' ': + self.wmap[y][x] = current + break + ntry -= 1 + current = chr(ord(current)+1) + + def do_walls(self): + "Build the actual walls map for the game." + self.__class__.walls = '' + for y in range(self.HEIGHT-1): + self.__class__.walls += '##' + for x in range(self.WIDTH): + self.__class__.walls += self.wmap[y][x] + self.__class__.walls += '##\n' + self.__class__.walls += '##' + for x in range(self.WIDTH): + if self.getw(x,0) == '#' or self.getw(x,self.HEIGHT-1) == '#': + self.__class__.walls += '#' + else: + self.__class__.walls += ' ' + self.__class__.walls += '##\n' + + def do_winds(self): + "Build the actual wind map for the game." + self.__class__.winds = '' + for y in range(self.HEIGHT): + self.__class__.winds += '>>' + for x in range(self.WIDTH): + self.__class__.winds += self.windmap[y][x] + self.__class__.winds += '<<' + '\n' + + def do_bonuses(self): + self.__class__.letter = choice([0,1]) + self.__class__.fire = choice([0,1]) + self.__class__.lightning = choice([0,1]) + self.__class__.water = choice([0,1]) + self.__class__.top = choice([0,1]) + + def generate(self): + "Generate random level settings." + assert 0, "--- THIS IS NO LONGER REALLY USED ---" + self.mlist = [([ + LNasty, LMonky, LGhosty, LFlappy, LSpringy, LOrcy, LGramy, LBlitzy, + RNasty, RMonky, RGhosty, RFlappy, RSpringy, ROrcy, RGramy, RBlitzy, + ],lambda : flat(12,4))] + gens = choice([512,512,256,256,128,128,64,64,32,32,16,16,16,16,16,16,20,20,8,8,8,8,4,4,4,4,2,2,2,2,1,1,3,5,6,7]) + self.genwalls = [] + if gens & 512: + print 'Using grids generator' + self.genwalls.append((RandomLevel.grids, + uniform(0.0, 0.1), + uniform(0.0, 0.1))) + if gens & 256: + # generate using pegs + print 'Using the pegs generator' + self.genwalls.append((RandomLevel.pegs, + uniform(0.1,0.2), + uniform(0.45,0.7), + choice([0,1,1,1]))) + if gens & 128: + # generate using a bouncer + nr = choice([0,0,1]) + print 'Using the bouncer generator' + self.genwalls.append((RandomLevel.bouncers, + dice(1, 100) + 250 - nr*200, # length + uniform(0.7, 1.7), + nr)) + if gens & 64: + # generate using a walker + print 'Using the walker generator' + nr = dice(1, 3) + 2 + self.genwalls.append((RandomLevel.walkers, + dice(2, 100) + 100, # length + nr, nr + dice(2, 3), # straight walk min, max len + choice([0,1]))) + if gens & 32: + # generate rivers + print 'Using the rivers generator' + self.genwalls.append((RandomLevel.rivers, + randrange(2,(self.WIDTH-4)/5), # the number of rivers + uniform(0.7, 1.7), # the side stepping threshold + 6)) # the max side stepping size + if gens & 16: + # generate rooms + print 'Using the romms generator' + nr = choice([1,2,2,2,3,3,4,5]) + self.genwalls.append((RandomLevel.rooms, + lambda : flat(9-nr,2), # the half size of the room + lambda : uniform(0.8,1.2), # the excentricity of the room + nr)) # the number of rooms + if gens & 8: + # generate a holes generator + # as this is interesting only if the level is filled somehow + print 'Using the holes generator' + self.genwalls.append((RandomLevel.mess,1-uniform(0.2,0.5))) + nh = choice([1,1,2,2,2,3,3,3,4,5]) + self.genwalls.append((RandomLevel.holes, + lambda : flat(9-nh,2), # radius of the holes + lambda : uniform(0.9,1.1), # excentricity + nh, # number of holes + lambda : choice([0,0,0,1]))) # circle or rectangle + if gens & 4: + # generate a lines generator + print 'Using the lines generator' + self.genwalls.append((RandomLevel.lines, + lambda : dice(7,3), # line length + dice(2,3))) # number of lines + if gens & 2: + # generate a platforms generator + print 'Using the platforms generator' + nplat = dice(2,4,0) + if nplat: space = flat((self.HEIGHT-1)/nplat/2,(self.HEIGHT-1)/nplat/2-1) + else: space = 1 + nholes = lambda : dice(1,3,0) + wholes = lambda : dice(2,3) + full = randrange(2) + self.genwalls.append((RandomLevel.platforms, + (nplat,space), # number of platform and spacing + (nholes,wholes), # number of holes and width + full)) # full width platform + if gens & 1: + # generate a mess generator + print 'Using the mess generator' + if gens & ~2: + offset = 0 + scale = 0.05 + else: + offset = 0.05 + scale = 0.10 + self.genwalls.append((RandomLevel.mess,offset+random()*scale)) + if random() < 0.2: + self.genwalls.append((RandomLevel.close,)) + if random() < 0.90: + self.genwalls.append((RandomLevel.startplatform,)) + self.genwalls.append((RandomLevel.generate_wind, )) + + +Levels = [] +for i in range(25): + class level(RandomLevel): + auto = 1 + Levels.append(level) + +class levelfinal(RandomLevel): + genwalls = [(RandomLevel.platforms,(4,3),(lambda:flat(1,1),lambda:flat(4,2)),1)] +Levels.append(levelfinal) diff --git a/bubbob/levels/scratch.py b/bubbob/levels/scratch.py new file mode 100644 index 0000000..a958a28 --- /dev/null +++ b/bubbob/levels/scratch.py @@ -0,0 +1,2301 @@ +# +# An experimental level set. +# + +import boarddef +from boarddef import LNasty, LMonky, LGhosty, LFlappy +from boarddef import LSpringy, LOrcy, LGramy, LBlitzy +from boarddef import RNasty, RMonky, RGhosty, RFlappy +from boarddef import RSpringy, ROrcy, RGramy, RBlitzy + + +class level01(boarddef.Level): + a = LNasty + b = RNasty + + walls = """ +#################################### +## ## +## ## +## ## +## ## +## ## +## a a b b ## +#### ###################### #### +## ## +## ## +## ## +## a a b b ## +#### ###################### #### +## ## +## ## +## ## +## a a b b ## +#### ###################### #### +## ## +## ## +## ## +## a a b b ## +#### ###################### #### +## ## +## ## +## ## +## ## +#################################### +""" + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level02(boarddef.Level): + letter = 1 + lightning = 1 + top = 0 + + a = LNasty + b = RNasty + + walls = """ +## ## +## ## +##b ## +############## ############## +## a ## +############# ############# +##b ## +############ ############ +## a ## +########### ########### +##b ## +########## ########## +## a ## +######### ######### +##b ## +######## ######## +## a ## +####### ####### +##b ## +###### ## ###### +## a ## +##### ### ### ##### +## ## +#### #### #### #### #### +## ## +### ######## ### +## ## +############ ############ +""" # [] [] [] # """ + + winds = """ +>> << +>>x<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>x<< +>> << +>> << +>>>>>>>>>>>>>>^ ^<<<<<<<<<<<<<< +>> << +>>>>>>>>>>>>>^ ^<<<<<<<<<<<<< +>> << +>>>>>>>>>>>>^ ^<<<<<<<<<<<< +>> << +>>>>>>>>>>>^ ^<<<<<<<<<<< +>> << +>>>>>>>>>>^ ^<<<<<<<<<< +>> << +>>>>>>>>>^ ^<<<<<<<<< +>> << +>>>>>>>>^ ^<<<<<<<< +>> << +>>>>>>>^ ^<<<<<<< +>> << +>>>>>>^ ^<<<<<< +>> << +>>>>>^ ^<<<<< +>> << +>>>>^ ^<<<< +>> << +>>>^ ^<<< +>> << +""" + + +class level03(boarddef.Level): + letter = 1 + fire = 0 + lightning = 0 + water = 1 + top = 1 + + a = LNasty, RNasty + b = () + c = LMonky, RMonky + + walls = """ +## ## +## ## +## # # ## +## b ## +## # ## +## a # # ## +## # # ## +## ## +## b a b ## +## # # # ## +## c ## +## # # c ## +## # b # ## +## ## +## # # # ## +## ## +## # # ## +## # a # ## +## # b # ## +## ## +## c # ## +## # # b # ## +## # ## +## # ## +## # ## +## # # ## +## # ## +##### # ##### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level04(level03): + a = LNasty, RNasty + b = LGhosty, RGhosty + c = () + +class level05(level03): + a = LSpringy, RSpringy + b = LSpringy, RSpringy + c = () + + + +class level06(boarddef.Level): + letter = 1 + fire = 0 + lightning = 1 + water = 0 + top = 0 + + a = LSpringy + b = RSpringy + c = LBlitzy + + walls = """ +## ############## ## +## ## +## ## +## b bcbcbcb bb a a acacaca a ## +################ ################ +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## ########### ########### ## +## bababa ## +## ########################## ## +## ## +## ## +## ## +########## ########## +## ## +## ## +## ## +## ## +## ## +###### ############## ###### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>>>>>>>>>>>>>>>^^^^<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<<<<<<<<<<<<<>>>>>>>>>>>>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level07(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 1 + + a = LSpringy + b = RSpringy + + walls = """ +############ ############# #### +### ## +### b ## +## b ## +#### b ## +## # b ## +## #b ## +## # ###### ## +## # b ## +## # b ##### ## +## # b ## +## # b ## +## # b ## +## ### ## +## # ## +## # b ## +## # b ## +## # b ## +## # b ## +## #b ## +## # ## +## # ## +## # ## +## # ## +## # ## +## # ## +## # ## +############ ############# #### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>v v<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvv v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>vvvvv v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>>>>>v v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> v v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> v v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> v v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> xxx v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> v<<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> <<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> <<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> <<<<<<<<<<<<<<<<<<<<<<<<<<< +>>> ^<< +>vv ^<< +>vv ^<< +>vv ^<< +>vv ^<< +>vv ^<< +>vv ^<< +>vv ^<< +>vv ^<< +>vvvvvvvvvvvvvvvv ^<< +>vvvvvvvvvvvvvvvv ^<< +>vvvvvvvvvvvvvvvv ^<< +>vvvvvvvvvvvvvvvv ^<< +""" + + +class level08(boarddef.Level): + letter = 1 + fire = 1 + lightning = 1 + water = 0 + top = 0 + + a = b = c = d = e = LFlappy + + walls = """ +## ## +## ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## a b c d e ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## e d c b a ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## a b c d e ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## e d c b a ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## a b c d e ## +#### ## ## ## ## ## #### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level09(level08): + + a = () + b = RGhosty + c = RBlitzy + d = () + e = LBlitzy + + +class level10(boarddef.Level): + top = 1 + + a = RSpringy + b = LSpringy + c = LSpringy + d = RSpringy + e = LSpringy + + walls = """ +## # # # # # # ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## a b c d e ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## e d c b a ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## a b c d e ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## e d c b a ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## # # # # # # ## +## ## +#### ## ## ## ## ## #### +## ## +## # # # # # # ## +## ## +#### ## ## ## ## ## #### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + + +class level11(boarddef.Level): + letter = 0 + fire = 0 + lightning = 1 + water = 0 + top = 0 + + a = LSpringy + b = RFlappy + c = RSpringy + + walls = """ +#################################### +## ## +## ## +## ## +## ## +## ## +## b b b b b ## +## ## +## b b b b b b ## +## ## +## ## +## ## +## ## +#################################### +## ## +## ## +## ## +## ## +## ## +## a ## +## c ## +## a ## +## c ## +## a ## +## c ## +## a ## +## c ## +#################################### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + + +class level12(level11): + letter = 0 + fire = 1 + lightning = 0 + water = 0 + top = 1 + + a = LGhosty + b = RFlappy + c = RGhosty + + +class level13(boarddef.Level): + letter = 1 + fire = 0 + lightning = 1 + water = 0 + top = 1 + + a = LFlappy + b = RFlappy + + walls = """ +########## ###### ########## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +## a ###### b ## +## ###### ## +## b ###### a ## +## ###### ## +## a ###### b ## +## ###### ## +## b ###### a ## +## ###### ## +## a ###### b ## +## ## +## b ###### a ## +## ###### ## +## a ###### b ## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +## ###### ## +########## ###### ########## +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> xxxx <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>> <<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>> << +""" + + +class level14(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + a = RSpringy + b = () + + walls = """ +#################################### +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +###a # # #a # ## +## # # #a # #a # # # # ## +## # # # # # # # # # ## +## ## ## ## ## ### +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +#################################### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level15(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 1 + top = 0 + + f = LFlappy + b = RBlitzy + a = LOrcy, RMonky + g = RGramy + h = LGhosty + n = LNasty + s = LSpringy + + walls = """ +############# ###### f f ######### +## a #b f f f ## +## # ###### #### ####### +## # a a # # ## +## ####### ## # # g ## +## # # # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## # n n # n # ## +## # #### ### ## +## # # ## +## s # # ## +## s # h # ## +## # # s # h # ## +## # s # # ## +## # #### ## # ## +## # # ##### ### ## ## +## # # ## +## # b # ## +## #### ## +## ## +## ## +## ###### ## +## # # ## +## # # ## +############# ###### f ######### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>>>vvvv<<<<<<<<<<<<<< +>>^ vvvv x<< +>>^ >vvvv< ^<< +>>^ > xxxx < ^<< +>>^ >>>>vvv<<< ^<< +>>^ vv< >>>>>>>>>^<< +>>^ xx ^ ^<< +>>^ ^ ^<< +>>^ ^ ^<< +>>^ >^ ^<< +>>^ ^< >^ ^<< +>>^ ^<<<<<<<>>>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^< ^<< +>>^ ^< xx ^<< +>>^ ^ ^^<<<<<<<<<<<< ^<< +>>^ ^ ^ ^<< +>>^ ^< >^ ^<< +>>^ ^<<>>^ ^<< +>>^ ^<< +>>^ ^^^^^^ ^<< +>>^ ^<>vvvv<>^ ^<< +>>^ ^<>vvvv<>^ ^<< +>> ^<>vvvv<>^ << +""" + + +class level16(boarddef.Level): + letter = 1 + fire = 1 + lightning = 1 + water = 0 + top = 1 + + a = LGhosty, RGhosty + + walls = """ +## # ## +## ## ## ## +## a a ###### ## +## ###### # ## +## ######### ## +## ############ ## +## # # ########## a ## +## a ### ######## ## +## #### ### ####### ## +## ######### ### a ## +## ######### ### # ## +## ############## ## +## ############## ## +## ####### ###### ## +## ###### ###### a ## +## ##### ##### ## +## #### #### a ## +## ##### ### ## +## ########### a ## +## a ### #### ## +## ## ###### ## +## ## ##### ## +## #### ## ## +## ####### ## +## ##### ## +## # #### ## +## ## +#################################### +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>vvvvvvvvvvvvvvvvvvvv<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level17(boarddef.Level): + letter = 1 + fire = 1 + lightning = 0 + water = 1 + top = 0 + + a = LGhosty, RFlappy + + walls = """ +## ##### # ## +## ######## # ## +## ####### a # # ## +## ####### # # ## +## ###### a ## # ## +## ##### ## # ## +## ###### # a ### # #### +## ##### # # #### +## ########### ####### # ##### +## #### ##### # ## #### # ##### +## #### ###### ### ### # # ### +## ## # ## # ## ###### # #### +## ### # ### ## # #### +## ### ## #### # ## #### +## ### a ## ### # # ### +## ### ## ### ## # ### +## ### ## ### a ## # ### +## #### # ## ### # ### +## #### # ### ### # ## +## ### ### #### # ## +## #### a ## # #### ## +## ### ####### ### +### ### ############ ## # ### +### ## ########### #### ## +### ### ### ########## ## +### ### ################ ## +## ## ############# ## +### # ####### ## +""" + + +class level18(boarddef.Level): + letter = 1 + fire = 0 + lightning = 0 + water = 1 + top = 0 + + b = LBlitzy, RBlitzy + a = LOrcy + + walls = """ +################# ################# +## # ## ## # ## +## # ## ## # ## +## # b b ## ## b b # ## +## # ####### ####### # ## +## #b # a #b # ## +## ## #### ## ## +## # a # ## +## # # ## +## a ## +## #### ## +## #a # ## +## # # ## +## a ## +## #### ## +## # a # ## +## # # ## +## a ## +## #### ## +## #a # ## +## # # ## +## a ## +## #### ## +## # a # ## +## ## ## ## +## ## ## ## +## ## ## ## +################# ################# +""" # [] [] [] # """ + + winds = """ +>> << +>>>>>>>>>>>>>>>>>^^<<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ > ^^ < ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<<>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level19(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + a = LNasty + b = LMonky + c = LOrcy + d = LGramy + + walls = """ +#################################### +#################################### +#################################### +#################################### +#################################### +#################################### +#################################### +#################################### +## ## +## # # ## +## # # ## +## # # # # ## +## # # # # ## +## # # # # ## +## # # # # # # ## +## # # # # # # ## +## # # # # # # ## +## # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## # # # # # # # # ## +## #a #b #c #d #c #b #a # ## +#################################### +""" # [] [] [] # """ + + winds = """ +>>xxxxxxxxx>>>>xxxxxx<<<<xxxxxxxxx<< +>>^ >>> ^ ^<< +>>^ ^ <<< ^<< +>>^ ^<< +>>^ >> << ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level20(boarddef.Level): + letter = 1 + fire = 0 + lightning = 0 + water = 0 + top = 1 + + a = RGhosty + b = RNasty + c = LSpringy + d = LFlappy + + walls = """ +## ## # # # ## +## # # ## +## # # ## +## # # ## +## # # ## +## ### # # ## ## +## # # ## +## # # ## +## # # ## +## # # ## +## ## # # # ### ## +## # # ## +## # a d d d # ## +## # a # ## +## # a c # ## +## # c # ## +## # b b b c # ## +## ### # # # ## ## +## # # ## +## # # ## +## # # ## +## # # ## +## ## # # ### ## +## # # ## +## # # ## +## # # ## +## # # ## +################ # # ############### +""" # [] [] [] # """ + + winds = """ +>>v v< v v v>v v<< +>>v v<v>vxvv>v v<< +>>v v<vvvvvv>v v<< +>>v v<vvvvvv>v v<< +>>v v<>v>v<v>v v<< +>>v v<< v x <>>v v<< +>>v v<vvvvvvvv>v v<< +>>v v<vvvvvvvv>v v<< +>>v v<vvvvvvvv>v v<< +>>v v<v<v<v<v<>v v<< +>>v v<<v x x v >>v v<< +>>v v<vvvvvvvvvv>v v<< +>>v v<vvvvvvvvvv>v v<< +>>v v<vvvvvvvvvv>v v<< +>>v v<vvvvvvvvvv>v v<< +>>v v<vvvvvvvvvv>v v<< +>>v v<>>v<v<v<<<>v v<< +>>v v<< v x x v>>v v<< +>>v v<v>>vvvvv>v v<< +>>v v<vvvvvvvv>v v<< +>>v v<vvvvvvvv>v v<< +>>v v<>v>v<v<<>v v<< +>>v v<<v x < >>v v<< +>>v v<vvvvvv>v v<< +>>v v<vvvvvv>v v<< +>>v v<vvvvvv>v v<< +>>v v<>v>v<v>v v<< +>> v< v v v>v << +""" + + +class level21(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + s = LSpringy, LSpringy + f = LFlappy, LFlappy + g = RGhosty, RGhosty + + walls = """ +########### ## ########## +## ## s ## ## +## ##f ## ## ## +## ## ## ## ## +## ## ## s ## ## +## # ## ## +## ## s ## ## +## ## #### ## +## ## ## ## ## +## ## s ## ## ## +## ## ## ## ## +## ## ## ## ## +## ## s ## ## ## +## ## ## ## ## +## ## ## ## ## +## ##s g ## ## +## ## ## ## ## +## ## ## ## ## +## ## ## f ## ## +## # ## ## +## ## g ## ## +## ## #### ## +## ## ## ## ## +## ## f ## ## ## +## ## ## ## ## +## ## ## ## ## +## ## g ## ## ## +########### ## ########## +""" # [] [] [] # """ + + +class level22(level21): + water = 1 + letter = 1 + + s = RBlitzy, RBlitzy + f = LOrcy, LOrcy + g = LGramy, LGramy + + +class level23(boarddef.Level): + letter = 1 + fire = 1 + lightning = 1 + water = 1 + top = 0 + + a = LNasty + b = RMonky + c = LGramy + d = ROrcy + s = LSpringy + + walls = """ +## ## +## ### ####### ############## # ## +## # # a # b # # # ## +## # ### # ### # ##### # ### # ## +## #d # # # # # # c # # # ## +## ### # # # ### # ##### ### # ## +## # # # # s # # d # # ## +## ### # # ### #### ####### ### ## +## #c # # # # a # # ## +## ### # ### # ########## ### # ## +## # # # # # # # b # c # # ## +## # ### # ### # ### ### ### # ## +## # b # #s # # # ## +## # ####### # ######## # ##### ## +## # # a # # # a # d # ## +## ### ##### ### # ##### ### # ## +## # d # # # b # # # ## +## ### # ####### # ##### # ### ## +## # # # cs # # # # ## +## ### ### ######## # ### ### # ## +## # # # b # # #c # # ## +## # ### # ##### ##### # ### # ## +## # d # # # # # # ## +## ### # # # # ########## ### # ## +## # # # # # a # # # ## +## # ####### ################ # ## +## ## +#################################### +""" + + +class level24(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + n = LOrcy + a = RGhosty, LFlappy + + walls = """ +#################################### +## ## ## # # # # # # # ### ## +## ## # # # # # # # ## # # # ## +## ## ### # #a # n # # # ## +## ## # # a #a # ## # ### ## +## ## # a # ### ## +###### # # #a # ######## +###### n #a # # ###### +##### # # # ####### +####### # n ##### +#### # # ####### +##### #a #### +##### # # # ####### +##### # # #### +#### # # ##### +###### # # ##### +#### # # ### #### +####### # ###### +##### # # # # ##### +##### ## # # ###### +####### # # # # # ####### +####### # # # # ###### +######## # # # # # # ######## +######## ## # # # # ######### +########## # # # # # ## ########## +## ###### # # # # # ###### ## +## ## +#################################### +""" + + winds = """ +>> << +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ > xx < ^<< +>>^ > xx < ^<< +>>^ > xxx x < ^<< +>>^ > xxx xxx < ^<< +>>^ > x x< ^<< +>>^ >x ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level25(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + b = RGhosty + a = LGhosty + + walls = """ +#################################### +## ## +## ## +## ####################### ## +## # ## +## a a a a a a # ## +## ################## # ## +## # # # ## +## # a a a a a a #b # ## +## # ########## # ## ## +## # # # # # ## +## #b #a a a #b # # ## +## ## # ##### ## # ## +## # # # # ## +## # # b b b b #b # ## +## # ########### ## ## +## # # # ## +## #b b b b b b # ## +## ###### b ###### ## +## ####### ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +#################################### +""" # [] [] [] # """ + + +class level26(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + n = LNasty + m = RNasty + a = LGramy + g = LGhosty + + walls = """ +## ####### ####### ## +## ### ########## ### ## +## g ## ## g ## +##n m n m n ########## m n m n m ## +############### ############### +## ################ ## +## #### #### ## +## ############ ## +## ###### ## ## ###### ## +## ### ######### ### ## +## ### # ## ### ## +##nmnmnm ### ########## ###mnmnmn ## +###############nmnmn ############### +##### ################ ##### +############# ############# +## ## ## ## ## ## +## ####### ####### ## +## # ################ # ## +## ########### ########### ## +## # # ############ # # ## +## ####### ## ## ####### ## +## # m n # ######## # n m # ## +## ####### # # ####### ## +## ######## ## +## # m # ## +## ######## ## +## ## +#################################### +""" # [] [] [] # """ + + +class level27(boarddef.Level): + letter = 1 + fire = 1 + lightning = 1 + water = 0 + top = 0 + + n = LNasty + m = RMonky + + walls = """ +########### ###### # ## +## # # ## +## ######### # # n ## +## # ########### +## # nnn ## +## ######### ## +## ## +## nnn # ## +## ####### ## # ## +## # # ## +## # # ## +## #nnn mmmm # # m ## +## ##### ###### ########### +## ## +## ## +## ####### ## +## ### # ## +## # # ## +## nnn # # n ## +## ###### ########### +## ## +## #mmmmmm ## +## ######## ## +## ## +## ######## ### # ## +## # # ## +## nnn # # ## +########### ###### ########### +""" # [] [] [] # """ + + +class level28(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 1 + top = 1 + + m = LMonky + g = LGramy + h = LGhosty + f = LFlappy + b = LSpringy + a = RSpringy + + walls = """ +## # # ## +## b # #a ## +## b # #a ## +## ##### ##### ## +## # # ## +## # # ## +## # # ## +## # # ## +## ## +## ## +## ## +## # # ## +###### ##### # # # # # ## +## m m m # # ## +## ######### # # h f h f ## +## g g g # # ## +####### ###### # h f ## +## m m m # # f ## +## ########### # # h h h ## +## g g g # # f f ## +######## ####### # # # # # # # ## +## ## +## ## +## ## +####### ##### ## ##### ####### +## # ## # ## +## # # ## +################# ################# +""" # [] [] [] # """ + + winds = """ +>>v v<< +>>v v<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + +class level29(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 0 + + m = LMonky, RMonky + g = LGramy, RGramy + o = LOrcy, ROrcy + n = LNasty, RNasty + + walls = """ +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +################# ################# +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## # m g o nn o g m # ## +## ######################## ## +## ## +""" # [] [] [] # """ + + winds = """ +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +>> << +>> << +>> << +>> << +>> << +>> << +>> << +>> << +>>xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>x>>>>v>>>>v<>v<<<<v<<<<x<<<<<< +vvvvvvxxxxxxxxxxxxxxxxxxxxxxxxvvvvvv +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +""" + +class level29(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 1 + + a = LNasty + b = RNasty + g = RGhosty + r = RGramy + m = LMonky + + walls = """ +## ############################ +## ## +##### ## +##### ## +##### ## +##### ## +##### # ## +##### a b a ba ab a ba # ## +################################ ## +## ## +## ## +## ## g ## +## ## ## +## ## ## +## ## ## +## ## ## +## ## br a rb a rb a r a ## +## ############################# +## ## ## +## ## ## ## ## +##### ## mm ##mm ## ## +############################## ## +##### ## ## ## ## +##### ## ## ## +##### ## +## ## +## ## +## ################################ +""" # [] [] [] # """ + + winds = """ +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ^<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ^<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ^<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ^<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ^<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ^<< +>>vv>>>>>>>>>>>>>>>>>>>>>>>>>>>> v<< +>>vv << +>>vv << +>>vv vvvvvvvvvvvvvvvvvvvvvv << +>>vv vvvvvvvvvvvvvvvvvvvvvv << +>>vv vvvvvvvvvvvvvvvvvvvvvv << +>>vv vvvvvvvvvvvvvvvvvvvvvv << +>>vv vvvvvvvvvvvvvvvvvvvvvv << +>>vv vvvvvvvvvvvvvvvvvvvvvv << +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>> << +""" + + +class level30(boarddef.Level): + letter = 1 + fire = 1 + lightning = 1 + water = 0 + top = 1 + + a = RNasty + + walls = """ +#################################### +#################################### +## ##### ## ##### ## +## a ##### a ## a ##### a ## +#################################### +#################################### +#################################### +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +###### ###### +## ## +## ## +## ## +## ## +#################################### +""" # [] [] [] # """ + + winds = """ +>> << +>> << +>>>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx<<< +>>>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx<<< +>>>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx<<< +>>>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx<<< +>>> <<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>>vv<<<<<<<<<<<<<<<<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>> << +""" + + +class level31(level30): + fire = 0 + lightning = 0 + + +class level32(boarddef.Level): + n = LNasty + m = LMonky + o = LOrcy + g = LGramy + h = LGhosty + f = LFlappy + s = LSpringy + b = LBlitzy + + walls = """ +#################### ## +## ## +## ## +## ## +## ## +## ## +## ######## +## ## +## ## +## # ## +## ####### ## +## ## +## ## +## # ## +## ####### ## +## ## +## ## +## ## +## # # ## +## # # # ## +## # # # n ## +## # # ###### +## # # # ## +## # # # n n ## +## # ######### +## # ## +## # ## +#################### ## +""" # [] [] [] # """ + + winds = """ +>>>>>>>>>>>>>>>>xxxx<<vvvvvvvvvvvv>v +>>^ ^<<vvvvvvvvvv>v +>>^ ^<<vvvvvvvv>v +>>^ ^<<vvvvvv>v +>>^ ^<<vvvv>v +>>^ ^<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ vvvvvvvvvvvvvv>v +>>^ vvvvvvvvvvvvvv>v +>> vvvvvvvvvvvvvv>v +""" + + +class level33(boarddef.Level): + m = LMonky, RMonky + g = LGramy, RGramy + o = LOrcy, ROrcy + n = LNasty, RNasty + + walls = """ +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +################# ################# +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## +## ## m g o nn o g m ## ## +## ######################## ## +## ## +""" # [] [] [] # """ + + winds = """ +v<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>v +v<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>v +v<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>v +v<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>v +>> << +>> << +>> << +>> << +>> << +>> << +>> << +>> << +>>xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>v>>>>v>>>>v<>v<<<<v<<<<v<<<<<< +>>>>>>x>>>>v>>>>v<>v<<<<v<<<<x<<<<<< +v<vvvvxxxxxxxxxxxxxxxxxxxxxxxxvvvv>v +v<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>v +""" + + +class level34(boarddef.Level): + letter = 0 + fire = 0 + lightning = 0 + water = 0 + top = 1 + + a = RBlitzy + b = LBlitzy + + walls = """ +## # ## # ## # ## # ## +## ## # ba # ## ## +## ### b#### #### ### ## +## # # # # b # # ## +## #### # # #### ## +## #######b # # ####### ## +## # # #b # ## +## # # # # ## +## # # # # ## +## #b # # # ## +## # # #b # ## +## # # ## +## # # # # ## +## #b # # # ## +## # # #b # ## +## # # # # ## +## # # # # ## +## #b # # # ## +## # # #b # ## +## # # # # ## +## # # # # ## +## #b # # # ## +## # # #b # ## +## ####### # # ####### ## +## #### # # #### ## +## #### #### #### #### ## +## #### b #### ## +## ## # # ## ## +""" # [] [] [] # """ + + winds = """ +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>xxxxxvvvvvvvvvvvvvvvvvvvvvvxxxxx<< +""" + + +class level35(boarddef.Level): + s = LNasty + o = LNasty, LNasty, RNasty, RNasty + a = RBlitzy + b = LBlitzy + + walls = """ +#################################### +## ###s ### ## +##s ## b ######## a ##s ## +#################################### +## ## +## ## +## ## +## ## ## +## #### ## +## ######## ## +## ############ ## +## ###### ## +## ########## ## +## ################ ## +## ########## ## +## ############## ## +## ################## ## +## ## ## +## ## ## +## #### ## +## ###### ## +## ## ## ## ## +## ## ## ## ## +## ## +## ## +## ## +## #o o o o o o o o o o o o # ## +#################################### +""" # [] [] [] # """ + + winds = """ +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>> <<<<<<<<<<<<<<< +>>>>>>>>>>>>>> <<<<<<<<<<<<<< +>>>>>>>>>>>>> <<<<<<<<<<<<< +>>>>>>>>>>>> <<<<<<<<<<<< +>>>>>>>>>>> <<<<<<<<<<< +>>>>>>>>>> <<<<<<<<<< +>>>>>>>>> <<<<<<<<< +>>>>>>>> <<<<<<<< +>>>>>>> <<<<<<< +>>>>>> <<<<<< +>>>>> <<<<< +>>>> <<<< +>>> <<< +>>vv<<<<<<<<<<<<<<>>>>>>>>>>>>>>vv<< +>>vv<<<<<<<<<<<<<<>>>>>>>>>>>>>>vv<< +>>vv<<<<<<<<<<<<<<>>>>>>>>>>>>>>vv<< +>>vv<<<<<<<<<<<<<<>>>>>>>>>>>>>>vv<< +>>vv<<<<<<<<<<<<<<>>>>>>>>>>>>>>vv<< +>>vv<<<<<<<<<<<<<<>>>>>>>>>>>>>>vv<< +>>vv<<<<<<^^^^^^^^^^^^^^^^>>>>>>vv<< +>>vv<<<<^^^^^^^^^^^^^^^^^^^^>>>>vv<< +>>xx<<^^^^^^^^^^^^^^^^^^^^^^^^>>xx<< +>>xx<<<<<<<<<<<<<<>>>>>>>>>>>>>>xx<< +""" + + +class level36(boarddef.Level): + s = LSpringy + b = LBlitzy + + walls = """ +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +## b b ## +## s ## +""" # [] [] [] # """ + + winds = """ +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>>>>>>>>>>>>>>>xxxx<<<<<<<<<<<<<<<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>>^ ^<< +>> << +""" + + +class level37(boarddef.Level): + n = LNasty + m = LMonky + + walls = """ +################# ################# +## m m m m m # ## #m m m m m ## +## #n n n n n # #n n n n n # ## +################# ################# +## ####### ####### ## +## ################ ## +## ############################ ## +## # #### #### # ## +## #### ##### ##### #### ## +## #### ################ #### ## +## #### ################ #### ## +## ######### ######### ## +## ### ############## ### ## +## #### # ############ # #### ## +## #### ############ #### ## +############ #### ############ +############ ## ############ +## # ## ## # ## +## ############ ############ ## +## # ##### ##### # ## +## ############################ ## +## ############################ ## +## ######## ######## ## +######## ## ## ######## +######## # #### # ######## +## # #### # ## +## ## ### ### ## ## +###### ################ ###### +""" # [] [] [] # """ + + winds = """ +>>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<< +>>xx<<<<<<<<<<<<<<>>>>>>>>>>>>>>xx<< +>>xx^^^^^^^^^^^^^^^^^^^^^^^^^^^^xx<< +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vv>>>>vvvvvvvvvvvvvvvvvvvv<<<<vv>v +v<vvvv<>vvvvvvvvvvvvvvvvvvvv<>vvvv>v +v<vvvv>>vvvvvvvvvvvvvvvvvvvv<<vvvv>v +v<vv>vvvvvvvvvvvvvvvvvvvvvvvvvv>vv>v +v<vv>vvvvvvvvvvvvvvvvvvvvvvvvvv>vv>v +v>vv<vvvvvvvvvvvvvvvvvvvvvvvvvv>vv<v +v>vv<vvvvvvvvvvvvvvvvvvvvvvvvvv>vv<v +v>vv<vvvvvvvvvvvvvvvvvvvvvvvvvv>vv<v +v>vv<vvvvvvvvvvvvvvvvvvvvvvvvvv>vv<v +v>vv<vvvvvvvvvvvvvvvvvvvvvvvvvv>vv<v +v>vv<vvvvvvvvvvvvvvvvvvvvvvvvvv>vv<v +v>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<v +v>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<v +v>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<v +v>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<v +v>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<v +""" + +class level38(boarddef.Level): + g = RGhosty + + walls = """ +#################################### +## # ## +## g # g ## +## # ## +## # g ## +## # ## +## ### ##### ## ## +## ### ######## ## ## +## #### ### ###### ## +## ### ### ####### ### ## +## ## ########## ## +## # ######## ## +## # g ## +## g ########## ## +## ########### ## +## ########## ## ## +## ############ ## ## +## ### ############ ## ## +## #### ########## ### ## +## ##### ######## ## ## +## #### ###### ## +## ##### ## +## ######## ## +## ## +## ## +## ## +## ## +#################################### +""" + + +class levelFinal(boarddef.Level): + + walls = """ +################ ################ +## ## +## ## +## ### ### ## +## ## +## ### ### ## +## ## +## ### ### ## +## ## +## ### ### ## +## ## +##### ##### +## ## +## ### ### ## +## ## +## ### ### ## +## ## +## ### ### ## +## ## +## ### ### ## +## ## +#################################### +## ## +## ## +## ## +## ## +## ## +################ ################ +""" # [] [] [] # """ +# nb.: the previous line has no purpose +# other than helping with wall alignment + + winds = """ +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>>>>>>>>>>>>>>>>^^^^<<<<<<<<<<<<<<<< +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +>> ^^^^ << +""" diff --git a/bubbob/macbinary.py b/bubbob/macbinary.py new file mode 100644 index 0000000..616bce0 --- /dev/null +++ b/bubbob/macbinary.py @@ -0,0 +1,260 @@ +import struct + + +def padto(n, m): + return (n+m-1) & ~(m-1) + +def resourceclass(rtype): + return globals().get(rtype.strip() + 'Resource', Resource) + + +class TypeList: + + def __init__(self, type, fmap, fdata, namebase, start, count): + self.type = type + self.fmap = fmap + self.fdata = fdata + self.namebase = namebase + self.start = start + self.count = count + self.ids = None + + def resources(self): + if self.ids is None: + ResourceCls = resourceclass(self.type) + d = {} + self.fmap.seek(self.start) + for resid, resname, resattr, resofshi, resofslo in [ + struct.unpack(">HHBBHxxxx", self.fmap.read(12)) + for i in range(self.count)]: + if resname == 0xffff: + name = None + else: + self.fmap.seek(self.namebase + resname) + namelen, = struct.unpack(">B", self.fmap.read(1)) + name = self.fmap.read(namelen) + assert resid not in d + d[resid] = ResourceCls(self.type, resid, name, resattr, + self.fdata, resofslo + (resofshi<<16)) + self.ids = d + return self.ids + + def __getitem__(self, id): + return self.resources()[id] + + def keys(self): + return self.resources().keys() + + def values(self): + return self.resources().values() + + def items(self): + return self.resources().items() + + def namedict(self): + return dict([(r.name, r) for r in self.resources().values() if r.name is not None]) + + +class MacBinary: + + def __init__(self, f): + if type(f) is type(''): + f = open(f, 'rb') + self.f = f + self.f.seek(0x53) + self.dataforksize, self.resforksize = struct.unpack(">ll", self.f.read(8)) + self.loadresources() + + def getdata(self): + self.f.seek(0x80) + return self.f.read(self.dataforksize) + + def loadresources(self): + f = Subfile(self.f, padto(0x80 + self.dataforksize, 0x80), self.resforksize) + ofsdata, ofsmap, lendata, lenmap = struct.unpack(">llll", f.read(16)) + fdata = Subfile(f, ofsdata, lendata) + fmap = Subfile(f, ofsmap, lenmap) + fmap.seek(24) + ofstype, ofsname = struct.unpack(">HH", fmap.read(4)) + self.dtypes = {} + fmap.seek(ofstype) + numtypes, = struct.unpack(">H", fmap.read(2)) + numtypes = numtypes + 1 + for rtype, num, ofsref in [struct.unpack(">4sHH", fmap.read(8)) + for i in range(numtypes)]: + assert rtype not in self.dtypes + self.dtypes[rtype] = TypeList(rtype, fmap, fdata, ofsname, + ofstype + ofsref, num + 1) + + def __getitem__(self, rtype): + return self.dtypes[rtype] + + def types(self): + return self.dtypes + + def keys(self): + return self.dtypes.keys() + + def values(self): + return self.dtypes.values() + + def items(self): + return self.dtypes.items() + + +class Subfile: + def __init__(self, f, start, length): + if start < 0: + raise ValueError, 'negative position' + if isinstance(f, Subfile): + if start + length > f.length: + raise ValueError, 'subfile out of bounds' + f, start = f.f, f.start+start + self.f = f + self.start = start + self.length = length + self.position = 0 + def read(self, size=None): + if size is None or self.position + size > self.length: + size = self.length - self.position + if size <= 0: + return '' + self.f.seek(self.start + self.position) + self.position = self.position + size + return self.f.read(size) + def seek(self, npos): + if npos < 0: + raise ValueError, 'negative position' + self.position = npos + + +class Resource: + + def __init__(self, type, id, name, attr, srcfile, srcofs): + self.type = type + self.id = id + self.name = name + self.attr = attr + self.srcfile = srcfile + self.srcofs = srcofs + + def subfile(self): + self.srcfile.seek(self.srcofs) + length, = struct.unpack(">l", self.srcfile.read(4)) + return Subfile(self.srcfile, self.srcofs + 4, length) + + def load(self): + return self.subfile().read() + + +class RGBImage: + def __init__(self, w, h, data): + assert len(data) == 3*w*h + self.w = w + self.h = h + self.data = data + + +def loadcolormap(f): + size, = struct.unpack(">xxxxxxH", f.read(8)) + size = size + 1 + d = {} + for index, r, g, b in [struct.unpack(">HHHH", f.read(8)) for i in range(size)]: + assert index not in d, 'duplicate color index' + d[index] = r/256.0, g/256.0, b/256.0 + return d + +def image2rgb(image): + # returns (w, h, data) + h = len(image) + result1 = [] + for line in image: + for r, g, b in line: + result1.append(chr(int(r)) + chr(int(g)) + chr(int(b))) + return len(image[0]), len(image), ''.join(result1) + + +class clutResource(Resource): + # a color table + def gettable(self): + return loadcolormap(self.subfile()) + + +class ppatResource(Resource): + # a pattern + def getimage(self): + f = self.subfile() + pattype, patmap, patdata = struct.unpack(">Hll", f.read(10)) + if pattype != 1: + raise ValueError, 'Pattern type not supported' + f.seek(patmap) + (rowBytes, h, w, packType, packSize, + pixelType, pixelSize, cmpCount, cmpSize, pmTable) = ( + struct.unpack(">xxxxHxxxxHHxxHlxxxxxxxxHHHHxxxxlxxxx", f.read(50))) + isBitmap = (rowBytes & 0x8000) != 0 + rowBytes &= 0x3FFF + if packType != 0: + raise ValueError, 'packed image not supported' + if pixelType != 0 or cmpCount != 1: + raise ValueError, 'direct RGB image not supported' + assert cmpSize == pixelSize and pixelSize in [1,2,4,8] + f.seek(pmTable) + colormap = loadcolormap(f) + bits_per_pixel = pixelSize + pixels_per_byte = 8 // bits_per_pixel + image = [] + f.seek(patdata) + for y in range(h): + line = f.read(rowBytes) + imgline = [] + for x in range(w): + n = x//pixels_per_byte + idx = ((ord(line[n]) >> ((pixels_per_byte - 1 - x%pixels_per_byte) * bits_per_pixel)) + & ((1<<bits_per_pixel)-1)) + imgline.append(colormap[idx]) + image.append(imgline) + return image + + +class LEVLResource(Resource): + # bub & bob level + WIDTH = 32 + HEIGHT = 25 + MONSTERS = 30 + WALLS = { 1:'#', 0:' '} + WINDS = { 0:' ', 1:'>', 2:'<', 3:'v', 4:'^', 5:'x', 0x66:' '} + FLAGS = ['flag0', 'letter', 'fire', 'lightning', 'water', 'top', 'flag6', 'flag7'] + + def getlevel(self, mnstrlist): + f = self.subfile() + result = {} + + walls = [] + for y in range(self.HEIGHT): + line = f.read(self.WIDTH//8) + line = [self.WALLS[(ord(line[x//8]) >> (x%8)) & 1] + for x in range(self.WIDTH)] + walls.append(''.join(line)) + result['walls'] = '\n'.join(walls) + + winds = [] + for y in range(self.HEIGHT): + line = f.read(self.WIDTH) + line = [self.WINDS[ord(v)] for v in line] + winds.append(''.join(line)) + result['winds'] = '\n'.join(winds) + + monsters = [] + for i in range(self.MONSTERS): + x,y,monster_type,f1,f2,f3 = struct.unpack(">BBBBBB", f.read(6)) + if monster_type != 0: + assert f1 == 0, f1 + cls = mnstrlist[monster_type-1] + monsters.append(cls(x=x, y=y, dir=f2, player=f3)) + result['monsters'] = monsters + + result['level'], = struct.unpack('>H', f.read(2)) + for i in range(8): + result[self.FLAGS[i]] = ord(f.read(1)) + + return result diff --git a/bubbob/mnstrmap.py b/bubbob/mnstrmap.py new file mode 100644 index 0000000..88e531d --- /dev/null +++ b/bubbob/mnstrmap.py @@ -0,0 +1,397 @@ + +class Monster1: + def __init__(self, x, y, dir, player=0): + self.x = x + self.y = y + self.dir = (1, -1)[dir] + self.player = player + +def nrange(start,n): + return range(start,start+n) + +class Nasty(Monster1): + right = nrange(239,4) + left = nrange(243,4) + jailed = nrange(247,3) + dead = nrange(253,4) + right_angry = nrange(800,4) + left_angry = nrange(804,4) + +class Monky(Monster1): + right = nrange(265,4) + left = nrange(269,4) + jailed = nrange(273,3) + dead = nrange(279,4) + left_weapon = right_weapon = nrange(451,1) # FIXME + decay_weapon = nrange(452,4) # FIXME + right_angry = nrange(808,4) + left_angry = nrange(812,4) + +class Ghosty(Monster1): + right = nrange(291,4) + left = nrange(295,4) + jailed = nrange(299,3) + dead = nrange(305,4) + right_angry = nrange(816,4) + left_angry = nrange(820,4) + +class Flappy(Monster1): + right = nrange(317,4) + left = nrange(321,4) + jailed = nrange(325,3) + dead = nrange(331,4) + right_angry = nrange(824,4) + left_angry = nrange(828,4) + +class Springy(Monster1): + right = nrange(343,4) + left = nrange(347,4) + jailed = nrange(351,3) + dead = nrange(357,4) + right_jump = nrange(369,2) + left_jump = nrange(371,2) + right_angry = nrange(832,4) + left_angry = nrange(836,4) + right_jump_angry = nrange(840,2) + left_jump_angry = nrange(842,2) + +class Orcy(Monster1): + right = nrange(373,4) + left = nrange(377,4) + jailed = nrange(381,3) + dead = nrange(387,4) + left_weapon = nrange(456,4) + right_weapon = nrange(460,4) + decay_weapon = nrange(456,0) # FIXME + right_angry = nrange(844,4) + left_angry = nrange(848,4) + +class Gramy(Monster1): + right = nrange(399,4) + left = nrange(403,4) + jailed = nrange(407,3) + dead = nrange(413,4) + left_weapon = right_weapon = nrange(472,4) # FIXME + right_angry = nrange(852,4) + left_angry = nrange(856,4) + +class Blitzy(Monster1): + right = left = nrange(425,4) + jailed = nrange(429,3) + dead = nrange(435,4) + right_angry = left_angry = nrange(860,4) + left_weapon = right_weapon = nrange(476,1) # FIXME + +class Ghost: + left = nrange(443,4) + right = nrange(447,4) + +class PlayerBubbles: + appearing = nrange(952,5) + bubble = nrange(910,3) + explosion = nrange(913,3) + left_weapon = nrange(464,4) + right_weapon = nrange(468,4) + decay_weapon = [] + +class LetterBubbles: + Extend = nrange(128,3) # FIXME + eXtend = nrange(136,3) + exTend = nrange(144,3) + extEnd = nrange(152,3) + exteNd = nrange(160,3) + extenD = nrange(168,3) + +class DyingBubble: + first = nrange(163,3) + medium = nrange(171,3) + last = nrange(155,3) + +class Fire: + ground = nrange(490,4) + drop = 489 + +class Lightning: + fired = 488 + +class Water: + h_flow = 900 + v_flow = 901 + start_right = 902 + start_left = 904 + bottom = 903 + top = 905 + tl_corner = 906 + bl_corner = 907 + br_corner = 908 + tr_corner = 909 + +class Flood: + waves = nrange(140,4) + fill = 495 + +class MiscPoints: + pink_100 = 496 + +class DigitsMisc: + digits_mask = nrange(519,10) + digits_border = nrange(920,10) + digits_white = nrange(930,10) + +class PotionBonuses: + coin = 477 + flower = 478 + trefle = 479 + rainbow = 480 + green_note = 481 + blue_note = 692 + + +class Bonuses: + monster_bonuses = [ + (593,1000), # banana + (594,2000), # peach + (595,3000), # quince + (596,4000), # pastec + (597,5000), # wine + (598,6000), # ananas + (599,8000) # diamond + ] + + door = 139 # Lots of diamonds + + red_potion = 637 #\ . + green_potion = 638 # > Clean the level and fill the top 5 lines with one of the PotionBonuses. + yellow_potion = 639 #/ + + kirsh = 600 + icecream1 = 601 # NOT_USED + erdbeer = 602 + fish1 = 603 + tomato = 604 + donut = 605 + apple = 606 + corn = 607 + icecream2 = 608 # NOT_USED + radish = 609 + + cyan_ice = 610 #\ . + violet_ice = 611 #| + peach2 = 612 # > Produced from the bubbles after a wand. + pastec2 = 613 #| + cream_pie = 614 #| + sugar_pie = 615 #/ + + brown_wand = 620 #\ . + yellow_wand = 621 #| + green_wand = 622 # > Bubbles turn into bonus of the previous set after + violet_wand = 623 # > the death of the last enemy plus a mega-bonus. + blue_wand = 624 #| + red_wand = 625 #/ + + violet_chest = 626 #\ . + blue_chest = 627 # > Bubbles turn into diamonds plus after the death + red_chest = 628 # > of the last enemy plus a mega-diamond + yellow_chest = 629 #/ + + shoe = 631 # speed player movements + grenade = 632 # put fire everywhere + + brown_umbrella = 633 # fire rain + grey_umbrella = 634 # water rain + violet_umbrella = 635 # spinning balls rain + + clock = 636 # time travel + coffee = 641 # Speed player's movements and fire rate. + book = 642 # Produces stars the middle-top going in any direction which kill the enemy upon contact. + heart_poison = 643 # Froze the enemy and they are now killed on contact. + gold_crux = 644 # become a bubble + red_crux = 645 # become a monster + blue_crux = 646 # become a monster + extend = 647 # Give 100'000 Points to the player and finish the level. + + ring = 640 # lord of the ring + green_pepper = 648 # hot stuff! + orange_thing = 649 # slippy + aubergine = 650 # rear gear + carrot = 651 # angry monsters + rape = 652 # auto-fire + white_carrot = 653 # fly + chickpea = 654 # shield + mushroom = 655 # pinball mode + egg = 656 # players permutation + chestnut = 657 # variation of frames per second + green_thing = 658 # sugar bomb + icecream3 = 659 # \ each icecream becomes two of the + icecream4 = 660 # \ next kind, which scores more points + icecream5 = 661 # / that's a lot of points in total + icecream6 = 662 # / + softice1 = 663 # shoot farther + softice2 = 665 # shoot nearer1 + french_fries = 664 # shoot 10 lightning bubbles + custard_pie = 666 # shoot faster + lollipop = 667 # invert left and right + cocktail = 668 # short-lived bubbles + ham = 669 # wall builder + bomb = 670 # explodes the structure of the level + beer = 671 # shoot 10 water bubbles + emerald = 672 # mega points + fish2 = 673 # mega blitz + sapphire = 681 # mega points + ruby = 682 # mega points + tin = 674 # angry (double-speed) player + hamburger = 675 # shoot 10 fire bubbles + insect = 676 # walls fall down + blue_necklace = 677 # player ubiquity + violet_necklace = 679 # monster ubiquity + butterfly = 678 # lunar gravity + conch = 680 # complete water flood + yellow_sugar = 630 # from a bonbon bomb + blue_sugar = 691 # from a bonbon bomb + +class Diamonds: + # Produced from the bubbles after last enemy is killed and a chest or wand has been caught. + violet = 616 + blue = 617 + red = 618 + yellow = 619 + +class Stars: + # Effect of the book. Kill monsters on contact. + blue = nrange(940,2) + yellow = nrange(942,2) + red = nrange(944,2) + green = nrange(946,2) + magenta = nrange(948,2) + cyan = nrange(950,2) + COLORS = ['blue', 'yellow', 'red', 'green', 'magenta', 'cyan'] + +class SpinningBalls: + free = nrange(482,4) + bubbled = nrange(486,2) # NOT_USED + +class BigImages: + cyan_ice = 10 # Megabonus produced after a wand + violet_ice = 11 + peach2 = 12 + pastec2 = 13 + cream_pie = 14 + sugar_pie = 15 + violet = 16 + blue = 17 + red = 18 + yellow = 19 + blitz = 30 + hurryup = nrange(31,2) + +class birange: + def __init__(self, a,b,n): + self.a = a + self.n = n + def __getitem__(self, pn): + return range(self.a + 1000*pn, self.a + 1000*pn + self.n) + +class bidict: + def __init__(self, a,b): + self.a = a.items() + def __getitem__(self, pn): + pn *= 1000 + d = {} + for key, value in self.a: + d[key] = value + pn + return d + +class GreenAndBlue: + water_bubbles = birange(182,185,3) + fire_bubbles = birange(176,554,3) + light_bubbles = birange(179,557,3) + normal_bubbles = birange(188,195,3) + new_bubbles = birange(191,203,4) + players = birange(210,226,13) + jumping_players = birange(683,687,4) + new_players = birange(693,696,3) + numbers = birange(499,509,10) # FIXME: already seen below + comming = birange(693,696,3) + points = bidict({ + 100: 529, + 150: 530, + 200: 531, + 250: 532, + 300: 533, + 350: 534, + 500: 535, + 550: 536, + 600: 537, + 650: 538, + 700: 539, + 750: 540, + 800: 541, + 850: 542, + 900: 543, + 950: 544, + 1000: 545, + 2000: 546, + 3000: 547, + 4000: 548, + 5000: 549, + 6000: 550, + 7000: 551, + 8000: 552, + 9000: 553, + 10000: 20, + 20000: 21, + 30000: 22, + 40000: 23, + 50000: 24, + 60000: 25, + 70000: 26, + },{ + 100: 561, + 150: 562, + 200: 563, + 250: 564, + 300: 565, + 350: 566, + 500: 567, + 550: 568, + 600: 569, + 650: 570, + 700: 571, + 750: 572, + 800: 573, + 850: 574, + 900: 575, + 950: 576, + 1000: 577, + 2000: 578, + 3000: 579, + 4000: 580, + 5000: 581, + 6000: 582, + 7000: 583, + 8000: 584, + 9000: 585, + 10000: 90, + 20000: 91, + 30000: 92, + 40000: 93, + 50000: 94, + 60000: 95, + 70000: 96, + }) + gameover = birange(497,498,1) + digits = birange(499,509,10) + fish = birange(700,707,7) + +class Butterfly(Monster1): + right = [('butterfly', 'fly', n) for n in range(2)] + left = [Bonuses.insect, Bonuses.butterfly] + jailed = [('butterfly', 'jailed', n) for n in range(3)] + dead = [('butterfly', 'dead', n) for n in range(4)] + +class Sheep(Monster1): + right = [('sheep', 1, n) for n in range(4)] + left = [('sheep',-1, n) for n in range(4)] + right_angry = right + left_angry = left + diff --git a/bubbob/monsters.py b/bubbob/monsters.py new file mode 100644 index 0000000..88983d0 --- /dev/null +++ b/bubbob/monsters.py @@ -0,0 +1,937 @@ +from __future__ import generators +import random +import gamesrv +import images +import boards +from boards import * +from images import ActiveSprite +from mnstrmap import GreenAndBlue, Bonuses, Ghost +from player import BubPlayer +import bonuses + + +class Monster(ActiveSprite): + touchable = 1 + special_prob = 0.2 + shootcls = None + vx = 2 + vy = 0 + vdir = -1 + is_ghost = 0 + MonsterBonus = bonuses.MonsterBonus + + def __init__(self, mnstrdef, x=None, y=None, dir=None, in_list=None): + self.mdef = mnstrdef + self.ptag = None + if dir is None: dir = mnstrdef.dir + if x is None: x = mnstrdef.x*CELL + if y is None: y = mnstrdef.y*CELL + self.dir = dir + ActiveSprite.__init__(self, images.sprget(self.imgrange()[0]), x, y) + self.gen.append(self.waiting()) + if in_list is None: + in_list = BubPlayer.MonsterList + self.in_list = in_list + self.in_list.append(self) + self.no_shoot_before = 0 + #images.ActiveSprites.remove(self) + + def unlist(self): + try: + self.in_list.remove(self) + return 1 + except ValueError: + return 0 + + def kill(self): + self.unlist() + ActiveSprite.kill(self) + + def tagdragon(self): + lst = bonuses.getvisibledragonlist() + if lst: + return random.choice(lst) + else: + return None + + def imgrange(self): + if self.is_ghost: + if self.dir > 0: + return Ghost.right + else: + return Ghost.left + elif self.angry: + if self.dir > 0: + return self.mdef.right_angry + else: + return self.mdef.left_angry + else: + if self.dir > 0: + return self.mdef.right + else: + return self.mdef.left + + def imgrange1(self): + # normally this is self.imgrange()[1] + lst = self.imgrange() + return lst[len(lst) > 1] + + def resetimages(self, is_ghost=0): + self.is_ghost = is_ghost + if self.gen: + self.setimages(self.cyclic(self.imgrange(), 3)) + else: # frozen monster + self.seticon(images.sprget(self.imgrange()[0])) + + def blocked(self): + if self.dir < 0: + x0 = (self.x-1)//16 + else: + x0 = (self.x+33)//16 + y0 = self.y // 16 + 1 + y1 = (self.y + 31) // 16 + return bget(x0,y0) == '#' or bget(x0,y1) == '#' + + def tryhstep(self): + if self.blocked(): + self.dir = -self.dir + self.resetimages() + return 0 + else: + self.step(self.vx*self.dir, 0) + return 1 + + def vblocked(self): + if self.vdir < 0: + y0 = self.y//16 + else: + y0 = (self.y+1)//16 + 2 + x0 = self.x // 16 + x1 = self.x // 16 + 1 + x2 = (self.x+31) // 16 + return bget(x0,y0) == '#' or bget(x1,y0) == '#' or bget(x2,y0) == '#' + + def tryvstep(self): + if self.vblocked(): + self.vdir = -self.vdir + return 0 + else: + self.step(0, self.vy*self.vdir) + self.vertical_warp() + return 1 + + def waiting(self, delay=20): + for i in range(delay): + yield None + self.resetimages() + self.gen.append(self.default_mode()) + + def overlapping(self): + if self.in_list is BubPlayer.MonsterList: + for s in self.in_list: + if (-6 <= s.x-self.x <= 6 and -6 <= s.y-self.y < 6 and + #s.dir == self.dir and s.vdir == self.vdir and + s.vx == self.vx and s.vy == self.vy and + (not s.angry) == (not self.angry)): + return s is not self + return 0 + + def walking(self): + while onground(self.x, self.y): + yield None + if random.random() < 0.2 and self.overlapping(): + yield None + x1 = self.x + if self.dir > 0: + x1 += self.vx + if (x1 & 15) < self.vx and random.random() < self.special_prob: + self.move(x1 & -16, self.y) + if self.special(): + return + self.tryhstep() + if self.seedragon(): + self.gen.append(self.hjumping()) + else: + self.gen.append(self.falling()) + + def seedragon(self, dragon=None): + dragon = dragon or self.tagdragon() + if dragon is None: + return False + return abs(dragon.y - self.y) < 16 and self.dir*(dragon.x-self.x) > 0 + + def special(self): + dragon = self.tagdragon() + if dragon is None: + return 0 + if self.seedragon(dragon) and self.shoot(): + return 1 + if dragon.y < self.y-CELL: + #and abs(dragon.x-self.x) < 2*(self.y-dragon.y): + for testy in range(self.y-2*CELL, self.y-6*CELL, -CELL): + if onground(self.x, testy): + if random.random() < 0.5: + ndir = self.dir + elif dragon.x < self.x: + ndir = -1 + else: + ndir = 1 + self.gen.append(self.vjumping(testy, ndir)) + return 1 + return 0 + + def shooting(self, pause): + for i in range(pause): + yield None + self.shootcls(self) + yield None + self.gen.append(self.default_mode()) + + def shoot(self, pause=10): + if (self.shootcls is None or + self.no_shoot_before > BubPlayer.FrameCounter): + return 0 + else: + self.gen.append(self.shooting(pause)) + self.no_shoot_before = BubPlayer.FrameCounter + 29 + return 1 + + def falling(self): + bubber = getattr(self, 'bubber', None) + while not onground(self.x, self.y): + yield None + ny = self.y + 3 + if (ny & 15) > 14: + ny = (ny//16+1)*16 + elif (ny & 15) < 3: + ny = (ny//16)*16 + nx = self.x + if nx < 32: + nx += 1 + (self.vx-1) * (bubber is not None) + elif nx > boards.bwidth - 64: + nx -= 1 + (self.vx-1) * (bubber is not None) + elif bubber: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + self.setimages(None) + if dx and not self.blocked(): + nx += self.vx*dx + self.seticon(images.sprget(self.imgrange1())) + self.move(nx, ny) + if self.y >= boards.bheight: + self.vertical_warp() + if bubber: + nextgen = self.playing_monster + else: + nextgen = self.walking + self.gen.append(nextgen()) + +## def moebius(self): +## self.dir = -self.dir +## self.resetimages() +## if hasattr(self, 'dcap'): +## self.dcap['left2right'] *= -1 + + def hjumping(self): + y0 = self.y + vspeed = -2.2 + ny = y0-1 + while ny <= y0 and not self.blocked(): + self.move(self.x+2*self.dir, int(ny)) + yield None + vspeed += 0.19 + ny = self.y + vspeed + self.gen.append(self.default_mode()) + + def vjumping(self, limity, ndir): + self.setimages(None) + yield None + self.dir = -self.dir + self.seticon(images.sprget(self.imgrange()[0])) + for i in range(9): + yield None + self.dir = -self.dir + self.seticon(images.sprget(self.imgrange()[0])) + for i in range(4): + yield None + self.dir = ndir + self.seticon(images.sprget(self.imgrange1())) + for ny in range(self.y-4, limity-4, -4): + self.move(self.x, ny) + if ny < -32: + self.vertical_warp() + yield None + self.resetimages() + self.gen.append(self.default_mode()) + + def regular(self): + return self.still_playing() and self.touchable and not self.is_ghost + + def still_playing(self): + return (self.in_list is BubPlayer.MonsterList and + self in self.in_list) + + def touched(self, dragon): + if self.gen: + self.killdragon(dragon) + if self.is_ghost and not hasattr(self, 'bubber'): + self.gen = [self.default_mode()] + self.resetimages() + else: + self.argh(getattr(self, 'poplist', None)) # frozen monster + + def killdragon(self, dragon): + dragon.die() + + def in_bubble(self, bubble): + if not hasattr(self.mdef, 'jailed'): + return + self.untouchable() + self.angry = [] + bubble.move(self.x, self.y) + if not hasattr(bubble, 'withmonster'): + bubble.to_front() + self.to_front() + img = self.mdef.jailed + self.gen = [self.bubbling(bubble)] + self.setimages(self.cyclic([img[1], img[2], img[1], img[0]], 4)) + + def bubbling(self, bubble): + counter = 0 + while not hasattr(bubble, 'poplist'): + self.move(bubble.x, bubble.y) + yield None + counter += 1 + if counter == 50 and hasattr(self, 'bubber'): + bubble.setimages(bubble.bubble_red()) + if bubble.poplist is None: + self.touchable = 1 + self.angry = [self.genangry()] + self.resetimages() + self.gen.append(self.default_mode()) + else: + previous_len = len(BubPlayer.MonsterList) + self.argh(bubble.poplist) + dragon = bubble.poplist[0] + if dragon is not None: + if previous_len and not BubPlayer.MonsterList: + points = 990 + else: + points = 90 + dragon.bubber.givepoints(points) + + def argh(self, poplist=None, onplace=0): + if self not in self.in_list: + return + if not poplist: + poplist = [None] + poplist.append(self) + level = len(poplist) - 2 + bonuses.BonusMaker(self.x, self.y, self.mdef.dead, onplace=onplace, + outcome=(self.MonsterBonus, level)) + self.kill() + + def freeze(self, poplist): + # don't freeze monsters largely out of screen, or they'll never come in + if self.regular() and -self.ico.h < self.y < boards.bheight: + self.gen = [] + self.poplist = poplist + + def flying(self): + blocked = 0 + while 1: + if random.random() < 0.2 and self.overlapping(): + yield None + hstep = self.tryhstep() + vstep = self.tryvstep() + if hstep or vstep: + blocked = 0 + elif blocked: + # blocked! go up or back to the play area + if self.x < 32: + self.step(self.vy, 0) + elif self.x > boards.bwidth - 64: + self.step(-self.vy, 0) + else: + self.step(0, -self.vy) + self.vertical_warp() + else: + blocked = 1 + yield None + + def becoming_monster(self, big=0, immed=0): + if big: + self.is_ghost = 1 + self.seticon(images.sprget(self.imgrange()[0])) + images.Snd.Hell.play() + for i in range(5): + ico = self.ico + self.seticon(self.bubber.icons[11 + immed, self.dir]) + yield None + yield None + self.seticon(ico) + yield None + yield None + self.resetimages(is_ghost=big) + self.gen.append(self.playing_monster()) + + def become_monster(self, bubber, saved_caps, big=0, immed=0): + self.timeoutgen = self.back_to_dragon() + self.default_mode = self.playing_monster + self.bubber = bubber + self.dcap = saved_caps + self.gen = [self.becoming_monster(big, immed)] + + def back_to_dragon(self): + for t in range(259): + yield None + if bonuses.getdragonlist(): + yield None + yield None + yield None + from player import Dragon + d = Dragon(self.bubber, self.x, self.y, self.dir, self.dcap) + d.dcap['shield'] = 50 + self.bubber.dragons.append(d) + self.kill() + + def playing_monster(self): + if self.timeoutgen not in self.gen: + self.gen.append(self.timeoutgen) + bubber = self.bubber + while self.is_ghost: + # ghost + self.angry = [] + key, dx, dy = max([(bubber.key_left, -1, 0), + (bubber.key_right, 1, 0), + (bubber.key_jump, 0, -1), + (bubber.key_fire, 0, 1)]) + if key: + if dx and self.dir != dx: + self.dir = dx + self.resetimages(is_ghost=1) + nx = self.x + 10*dx + ny = self.y + 9*dy + if nx < 0: nx = 0 + if nx > boards.bwidth-2*CELL: nx = boards.bwidth-2*CELL + if ny < -CELL: ny = -CELL + if ny > boards.bheight-CELL: ny = boards.bheight-CELL + self.move(nx, ny) + yield None + if self.vy: + # flying monster + while 1: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + if bubber.key_jump and bubber.key_jump > bubber.key_fire: + dy = self.vdir = -1 + elif bubber.key_fire: + dy = self.vdir = 1 + else: + dy = 0 + hstep = dx and self.tryhstep() + vstep = dy and self.tryvstep() + if dx and dy and not (hstep or vstep): + # blocked? + self.dir = -self.dir + self.vdir = -self.vdir + blocked = self.blocked() and self.vblocked() + self.dir = -self.dir + self.vdir = -self.vdir + if blocked: + # completely blocked! accept move or force back to + # play area + if self.x < 32: + self.step(self.vy, 0) + elif self.x > boards.bwidth - 64: + self.step(-self.vy, 0) + else: + self.step(self.vx*dx, self.vy*dy) + self.vertical_warp() + yield None + elif not isinstance(self, Springy): + # walking monster + jumping_y = 0 + imgsetter = self.imgsetter + while onground(self.x, self.y) or jumping_y: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + imgsetter = self.imgsetter + if dx and not self.blocked(): + self.step(self.vx*dx, 0) + if not jumping_y: + self.setimages(imgsetter) + else: + self.seticon(images.sprget(self.imgrange1())) + self.setimages(None) + else: + self.setimages(None) + dx = 0 + yield None + if not jumping_y: + wannafire = bubber.key_fire + wannajump = bubber.key_jump + if wannafire and self.shoot(1): + return + if wannajump: + jumping_y = CELL + if jumping_y: + self.step(0, -4) + if self.y < -32: + self.vertical_warp() + if onground(self.x, self.y): + jumping_y = 0 + else: + jumping_y -= 1 + self.gen.append(self.falling()) + else: + # springy + if not onground(self.x, self.y): + self.gen.append(self.falling()) + return + prevx = self.x + for t in self.walking(): + dx = bubber.wannago(self.dcap) + if dx: + if dx != self.dir: + self.dir = dx + self.resetimages() + if self.blocked() and (self.x-prevx)*dx <= 0: + dx = 0 + self.move(prevx + self.vx*dx, self.y) + yield None + prevx = self.x + + def become_ghost(self): + self.gen = [self.ghosting()] + self.resetimages(is_ghost=1) + + def ghosting(self): + counter = 0 + while counter < 5: + for i in range(50): + yield None + dragon = self.tagdragon() + if dragon is None: + counter += 1 + else: + counter = 0 + px, py = dragon.x, dragon.y + if abs(px-self.x) < abs(py-self.y): + dx = 0 + if py > self.y: + dy = 1 + else: + dy = -1 + else: + dy = 0 + if px > self.x: + dx = 1 + else: + dx = -1 + self.dir = dx + self.resetimages(is_ghost=1) + dx *= 10 + dy *= 9 + distance = 1E10 + while 1: + self.angry = [] + self.step(dx, dy) + yield None + dist1 = (px-self.x)*(px-self.x)+(py-self.y)*(py-self.y) + if dist1 > distance: + break + distance = dist1 + self.angry = [] + self.gen = [self.default_mode()] + self.resetimages() + + default_mode = falling + + +def argh_em_all(): + poplist = [None] + for s in images.ActiveSprites[:]: + if isinstance(s, Monster): + s.argh(poplist) + +def freeze_em_all(): + poplist = [None] + for s in images.ActiveSprites: + if isinstance(s, Monster): + s.freeze(poplist) + + +class MonsterShot(ActiveSprite): + speed = 6 + touchable = 1 + + def __init__(self, owner, dx=CELL, dy=0): + self.owner = owner + self.speed = owner.dir * self.speed + if owner.dir < 0: + nimages = owner.mdef.left_weapon + else: + nimages = owner.mdef.right_weapon + ActiveSprite.__init__(self, images.sprget(nimages[0]), + owner.x, owner.y + dy) + self.step((owner.ico.w - self.ico.w) // 2, + (owner.ico.h - self.ico.h) // 2) + if not self.blocked(): + self.step(dx*owner.dir, 0) + if len(nimages) > 1: + self.setimages(self.cyclic(nimages, 3)) + self.gen.append(self.moving()) + + def blocked(self): + if self.speed < 0: + x0 = (self.x-self.speed-8)//16 + else: + x0 = (self.x+self.ico.w+self.speed-8)//16 + y0 = (self.y+8) // 16 + 1 + return not (' ' == bget(x0,y0) == bget(x0+1,y0)) + + def moving(self): + while not self.blocked(): + yield None + self.step(self.speed, 0) + self.hitwall() + + def hitwall(self): + self.untouchable() + self.gen.append(self.die(self.owner.mdef.decay_weapon, 2)) + + def touched(self, dragon): + dragon.die() + + +class BoomerangShot(MonsterShot): + speed = 8 + + def hitwall(self): + self.gen.append(self.moveback()) + + def moveback(self): + owner = self.owner + if self.speed > 0: + nimages = owner.mdef.left_weapon + else: + nimages = owner.mdef.right_weapon + self.setimages(self.cyclic(nimages, 3)) + while (owner.x-self.x) * self.speed < 0: + yield None + self.step(-self.speed, 0) + if self.blocked(): + break + self.kill() + +class FastShot(MonsterShot): + speed = 15 + + +class DownShot(MonsterShot): + + def __init__(self, owner): + MonsterShot.__init__(self, owner, 0, CELL) + + def moving(self): + while self.y < boards.bheight: + yield None + self.step(0, 7) + self.kill() + + +##class DragonShot(MonsterShot): +## speed = 8 + +## def __init__(self, owner): +## MonsterShot.__init__(self, owner) +## self.untouchable() +## self.gen.append(self.touchdelay(4)) + +## def touched(self, dragon): +## if dragon is not self.owner: +## if dragon.bubber.bonbons == 0: +## dragon.die() +## else: +## from player import scoreboard +## from bonuses import Sugar1, Sugar2 +## from bonuses import BonusMaker +## if random.random() < 0.2345: +## start = 1 +## else: +## start = 0 +## loose = min(2, dragon.bubber.bonbons) +## for i in range(start, loose): +## cls = random.choice([Sugar1, Sugar2]) +## BonusMaker(self.x, self.y, [cls.nimage], +## outcome=(cls,)) +## dragon.bubber.bonbons -= loose +## scoreboard() +## dragon.dcap['shield'] = 25 +## self.owner.play(images.Snd.Yippee) +## self.kill() + +## def blocked(self): +## return self.x < -self.ico.w or self.x >= gamesrv.game.width +## #return self.x < CELL or self.x >= boards.bwidth - 3*CELL + + +class Nasty(Monster): + pass + +class Monky(Monster): + shootcls = MonsterShot + +class Ghosty(Monster): + default_mode = Monster.flying + vy = 2 + +class Flappy(Monster): + default_mode = Monster.flying + vy = 1 + +class Springy(Monster): + spring_down = 0 + def imgrange(self): + if self.spring_down and not self.is_ghost: + if self.angry: + if self.dir > 0: + r = self.mdef.right_jump_angry + else: + r = self.mdef.left_jump_angry + else: + if self.dir > 0: + r = self.mdef.right_jump + else: + r = self.mdef.left_jump + return [r[self.spring_down-1]] + else: + return Monster.imgrange(self) + def walking(self): + self.spring_down = 1 + self.resetimages() + for t in range(2+self.overlapping()): + yield None + self.spring_down = 2 + self.resetimages() + for t in range(4+2*self.overlapping()): + yield None + self.spring_down = 1 + self.resetimages() + for t in range(2+self.overlapping()): + yield None + self.spring_down = 0 + self.resetimages() + g = 10.0/43 + vy = -20*g + yf = self.y + for t in range(40): + yf += vy + vy += g + if self.blocked(): + self.dir = -self.dir + self.resetimages() + nx = self.x + self.dir*self.vx + if self.y//16 < int(yf)//16: + if onground(self.x, (self.y//16+1)*16): + break + if onground(nx, (self.y//16+1)*16): + self.move(nx, self.y) + break + nx, yf = vertical_warp(nx, yf) + self.move(nx, int(yf)) +## if moebius: +## self.moebius() + yield None + self.gen.append(self.falling()) + +class Orcy(Monster): + shootcls = FastShot + +class Gramy(Monster): + shootcls = BoomerangShot + vx = 3 + +class Blitzy(Monster): + shootcls = DownShot + vx = 3 + + def seedragon(self, dragon=None): + return 0 + + def special(self): + if random.random() < 0.3: + self.shootcls(self) + return 0 + + def shoot(self, pause=0): + # no pause (only used when controlled by the player) + if self.no_shoot_before > BubPlayer.FrameCounter: + pass + else: + self.shootcls(self) + self.no_shoot_before = BubPlayer.FrameCounter + 29 + return 0 + +MonsterClasses = [c for c in globals().values() + if type(c)==type(Monster) and issubclass(c, Monster)] +MonsterClasses.remove(Monster) + + +class Butterfly(Monster): + MonsterBonus = bonuses.IceMonsterBonus + fly_away = False + + def waiting(self, delay=0): + return Monster.waiting(self, delay) + + def imgrange(self): + self.angry = [] + return Monster.imgrange(self) + + def killdragon(self, dragon): + if self.is_ghost: + Monster.killdragon(self, dragon) + else: + self.fly_away = True, dragon.x + + def flying(self): + repeat = 0 + while 1: + r = random.random() + if self.x < 64: + bump = self.dir < 0 + elif self.x > boards.bwidth - 64: + bump = self.dir > 0 + elif self.fly_away: + wannago = self.x - self.fly_away[1] + if self.x < 100: + wannago = 1 + elif self.x > boards.bwidth - 100: + wannago = -1 + bump = self.dir * wannago < 0 + if repeat: + self.fly_away = False + repeat = 0 + else: + repeat = 1 + else: + bump = r < 0.07 + if bump: + self.dir = -self.dir + self.resetimages() + elif r > 0.92: + self.vdir = -self.vdir + self.step(self.dir * (2 + (r < 0.5)), self.vdir * 2) + self.vertical_warp() + if not repeat: + yield None + + default_mode = flying + + +class Sheep(Monster): + + def playing_monster(self): + from bonuses import Bonus + bubber = self.bubber + vy = None + imgsetter = self.imgsetter + poplist = [None] + while 1: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + imgsetter = self.imgsetter + if dx and vy is None: + self.setimages(imgsetter) + else: + self.setimages(None) + if vy is not None: + if vy < 0: + n = 1 + else: + n = 3 + self.seticon(images.sprget(self.imgrange()[n])) + if dx and not self.blocked(): + self.step(self.vx*dx, 0) + yield None + impulse = 0.0 + wannajump = bubber.key_jump + if vy is not None: + vy += 0.33 + if vy > 12.0: + vy = 12.0 + yf = self.y + yfp + vy + yfp = yf - int(yf) + delta = int(yf) - self.y + if delta > 0: + by_y = {} + for s in images.ActiveSprites: + if isinstance(s, Bonus) and s.touchable: + if abs(s.x - self.x) <= 22: + by_y[s.y] = s + for monster in BubPlayer.MonsterList: + if abs(monster.x - self.x) <= 22: + if monster.regular(): + by_y[monster.y] = monster + for ny in range(self.y - 1, self.y + delta + 1): + self.move(self.x, ny) + self.vertical_warp() + if onground(self.x, self.y): + poplist = [None] + impulse = vy + vy = None + break + key = self.y + 29 + if key in by_y: + s = by_y[key] + if isinstance(s, Monster): + self.play(images.Snd.Extra) + s.argh(poplist) + elif isinstance(s, Bonus): + s.reallytouched(self) + yfp = 0.0 + vy = -3.3 + break + else: + self.step(0, delta) + self.vertical_warp() + if vy is None: + if onground(self.x, self.y): + if wannajump: + yfp = 0.0 + vy = - max(1.0, impulse) - 2.02 + impulse = 0.0 + self.play(images.Snd.Jump) + else: + yfp = vy = 0.0 + if impulse > 8.1: + break + self.play(images.Snd.Pop) + for n in range(2): + for letter in 'abcdefg': + ico = images.sprget(('sheep', letter)) + nx = self.x + random.randrange(-1, self.ico.w - ico.w + 2) + ny = self.y + random.randrange(0, self.ico.h - ico.h + 2) + dxy = [random.random() * 5.3 - 2.65, random.random() * 4 - 4.4] + s = images.ActiveSprite(ico, nx, ny) + s.gen.append(s.parabolic(dxy)) + s.gen.append(s.die([None], random.randrange(35, 54))) + self.move(-99, 0) + for t in range(68): + yield None + self.kill() + + default_mode = falling = playing_monster + + def argh(self, *args, **kwds): + pass diff --git a/bubbob/music/Snd1-8.wav b/bubbob/music/Snd1-8.wav Binary files differnew file mode 100644 index 0000000..f4962cf --- /dev/null +++ b/bubbob/music/Snd1-8.wav diff --git a/bubbob/music/Snd2-8.wav b/bubbob/music/Snd2-8.wav Binary files differnew file mode 100644 index 0000000..c59f1e8 --- /dev/null +++ b/bubbob/music/Snd2-8.wav diff --git a/bubbob/music/Snd3-8.wav b/bubbob/music/Snd3-8.wav Binary files differnew file mode 100644 index 0000000..dda70f0 --- /dev/null +++ b/bubbob/music/Snd3-8.wav diff --git a/bubbob/music/Snd4-8.wav b/bubbob/music/Snd4-8.wav Binary files differnew file mode 100644 index 0000000..509afad --- /dev/null +++ b/bubbob/music/Snd4-8.wav diff --git a/bubbob/music/Snd5-8.wav b/bubbob/music/Snd5-8.wav Binary files differnew file mode 100644 index 0000000..b4bfabc --- /dev/null +++ b/bubbob/music/Snd5-8.wav diff --git a/bubbob/music/Snd6-8.wav b/bubbob/music/Snd6-8.wav Binary files differnew file mode 100644 index 0000000..0f2bad5 --- /dev/null +++ b/bubbob/music/Snd6-8.wav diff --git a/bubbob/patmap.py b/bubbob/patmap.py new file mode 100644 index 0000000..3b00252 --- /dev/null +++ b/bubbob/patmap.py @@ -0,0 +1,177 @@ +patmap = {(0, 0, 0): ('pat03.ppm', (0, 120, 24, 24)), + (1, 0, 0): ('pat01.ppm', (0, 24, 24, 24)), + (2, 0, 0): ('pat09.ppm', (0, 0, 24, 24)), + (2, 'l'): ('pat14.ppm', (0, 160, 40, 32)), + (3, 0, 0): ('pat06.ppm', (0, 120, 24, 24)), + (3, 'l'): ('pat17.ppm', (0, 32, 40, 32)), + (4, 0, 0): ('pat04.ppm', (0, 48, 24, 24)), + (4, 'l'): ('pat15.ppm', (0, 192, 40, 32)), + (5, 0, 0): ('pat01.ppm', (0, 216, 24, 24)), + (5, 'l'): ('pat19.ppm', (0, 96, 40, 32)), + (6, 0, 0): ('pat09.ppm', (0, 144, 24, 24)), + (6, 'l'): ('pat18.ppm', (0, 96, 40, 32)), + (7, 0, 0): ('pat07.ppm', (0, 24, 24, 24)), + (8, 0, 0): ('pat02.ppm', (0, 48, 24, 24)), + (9, 0, 0): ('pat09.ppm', (0, 216, 24, 24)), + (9, 'l'): ('pat11.ppm', (0, 64, 40, 32)), + (10, 0, 0): ('pat07.ppm', (0, 96, 24, 24)), + (10, 'l'): ('pat14.ppm', (0, 96, 40, 32)), + (11, 0, 0): ('pat05.ppm', (0, 24, 24, 24)), + (11, 'l'): ('pat13.ppm', (0, 32, 40, 32)), + (12, 0, 0): ('pat02.ppm', (0, 216, 24, 24)), + (12, 'l'): ('pat18.ppm', (0, 0, 40, 32)), + (13, 0, 0): ('pat00.ppm', (0, 72, 24, 24)), + (13, 'l'): ('pat18.ppm', (0, 128, 40, 32)), + (14, 0, 0): ('pat07.ppm', (0, 192, 24, 24)), + (15, 0, 0): ('pat05.ppm', (0, 192, 24, 24)), + (15, 'l'): ('pat18.ppm', (0, 32, 40, 32)), + (16, 0, 0): ('pat06.ppm', (0, 96, 24, 24)), + (16, 'l'): ('pat11.ppm', (0, 32, 40, 32)), + (17, 0, 0): ('pat04.ppm', (0, 0, 24, 24)), + (17, 'l'): ('pat14.ppm', (0, 64, 40, 32)), + (18, 0, 0): ('pat01.ppm', (0, 168, 24, 24)), + (18, 'l'): ('pat13.ppm', (0, 0, 40, 32)), + (19, 0, 0): ('pat09.ppm', (0, 96, 24, 24)), + (19, 'l'): ('pat16.ppm', (0, 192, 40, 32)), + (20, 0, 0): ('pat07.ppm', (0, 0, 24, 24)), + (20, 'l'): ('pat15.ppm', (0, 160, 40, 32)), + (21, 0, 0): ('pat04.ppm', (0, 192, 24, 24)), + (21, 'l'): ('pat19.ppm', (0, 64, 40, 32)), + (22, 0, 0): ('pat02.ppm', (0, 120, 24, 24)), + (22, 'l'): ('pat17.ppm', (0, 224, 40, 32)), + (23, 0, 0): ('pat10.ppm', (0, 24, 24, 24)), + (23, 'l'): ('pat12.ppm', (0, 64, 40, 32)), + (24, 0, 0): ('pat05.ppm', (0, 0, 24, 24)), + (24, 'l'): ('pat17.ppm', (0, 96, 40, 32)), + (25, 0, 0): ('pat02.ppm', (0, 168, 24, 24)), + (25, 'l'): ('pat15.ppm', (0, 64, 40, 32)), + (26, 0, 0): ('pat00.ppm', (0, 24, 24, 24)), + (26, 0, 1): ('pat00.ppm', (0, 0, 24, 24)), + (26, 1, 0): ('pat01.ppm', (0, 120, 24, 24)), + (26, 1, 1): ('pat08.ppm', (0, 144, 24, 24)), + (26, 'l'): ('pat13.ppm', (0, 224, 40, 32)), + (27, 0, 0): ('pat07.ppm', (0, 216, 24, 24)), + (27, 'l'): ('pat17.ppm', (0, 64, 40, 32)), + (28, 0, 0): ('pat05.ppm', (0, 168, 24, 24)), + (28, 'l'): ('pat15.ppm', (0, 224, 40, 32)), + (29, 0, 0): ('pat03.ppm', (0, 72, 24, 24)), + (30, 0, 0): ('pat00.ppm', (0, 216, 24, 24)), + (30, 'l'): ('pat17.ppm', (0, 160, 40, 32)), + (31, 0, 0): ('pat08.ppm', (0, 168, 24, 24)), + (31, 'l'): ('pat12.ppm', (0, 32, 40, 32)), + (32, 0, 0): ('pat08.ppm', (0, 0, 24, 24)), + (33, 0, 0): ('pat05.ppm', (0, 144, 24, 24)), + (33, 'l'): ('pat12.ppm', (0, 192, 40, 32)), + (34, 0, 0): ('pat03.ppm', (0, 48, 24, 24)), + (35, 0, 0): ('pat00.ppm', (0, 168, 24, 24)), + (35, 'l'): ('pat14.ppm', (0, 128, 40, 32)), + (36, 0, 0): ('pat07.ppm', (0, 120, 24, 24)), + (36, 'l'): ('pat16.ppm', (0, 96, 40, 32)), + (37, 0, 0): ('pat06.ppm', (0, 72, 24, 24)), + (38, 0, 0): ('pat03.ppm', (0, 216, 24, 24)), + (38, 'l'): ('pat11.ppm', (0, 128, 40, 32)), + (39, 0, 0): ('pat01.ppm', (0, 96, 24, 24)), + (40, 0, 0): ('pat08.ppm', (0, 48, 24, 24)), + (41, 0, 0): ('pat04.ppm', (0, 24, 24, 24)), + (41, 'l'): ('pat12.ppm', (0, 160, 40, 32)), + (42, 0, 0): ('pat01.ppm', (0, 192, 24, 24)), + (43, 0, 0): ('pat05.ppm', (0, 120, 24, 24)), + (43, 1, 0): ('pat08.ppm', (0, 72, 24, 24)), + (44, 0, 0): ('pat07.ppm', (0, 48, 24, 24)), + (45, 0, 0): ('pat04.ppm', (0, 216, 24, 24)), + (45, 'l'): ('pat16.ppm', (0, 224, 40, 32)), + (46, 0, 0): ('pat02.ppm', (0, 144, 24, 24)), + (46, 'l'): ('pat16.ppm', (0, 64, 40, 32)), + (47, 0, 0): ('pat10.ppm', (0, 72, 24, 24)), + (47, 'l'): ('pat19.ppm', (0, 224, 40, 32)), + (48, 0, 0): ('pat00.ppm', (0, 120, 24, 24)), + (48, 'l'): ('pat12.ppm', (0, 128, 40, 32)), + (49, 0, 0): ('pat08.ppm', (0, 96, 24, 24)), + (49, 'l'): ('pat11.ppm', (0, 192, 40, 32)), + (50, 0, 0): ('pat06.ppm', (0, 24, 24, 24)), + (50, 'l'): ('pat16.ppm', (0, 128, 40, 32)), + (51, 0, 0): ('pat03.ppm', (0, 144, 24, 24)), + (51, 'l'): ('pat14.ppm', (0, 0, 40, 32)), + (52, 0, 0): ('pat01.ppm', (0, 72, 24, 24)), + (52, 'l'): ('pat15.ppm', (0, 0, 40, 32)), + (53, 0, 0): ('pat09.ppm', (0, 48, 24, 24)), + (53, 'l'): ('pat16.ppm', (0, 32, 40, 32)), + (54, 0, 0): ('pat06.ppm', (0, 192, 24, 24)), + (54, 'l'): ('pat19.ppm', (0, 192, 40, 32)), + (55, 0, 0): ('pat04.ppm', (0, 96, 24, 24)), + (55, 'l'): ('pat18.ppm', (0, 160, 40, 32)), + (56, 0, 0): ('pat09.ppm', (0, 120, 24, 24)), + (56, 'l'): ('pat18.ppm', (0, 64, 40, 32)), + (57, 0, 0): ('pat00.ppm', (0, 144, 24, 24)), + (57, 'l'): ('pat12.ppm', (0, 96, 40, 32)), + (58, 0, 0): ('pat04.ppm', (0, 168, 24, 24)), + (59, 0, 0): ('pat02.ppm', (0, 72, 24, 24)), + (59, 'l'): ('pat14.ppm', (0, 192, 40, 32)), + (60, 0, 0): ('pat10.ppm', (0, 48, 24, 24)), + (60, 'l'): ('pat13.ppm', (0, 96, 40, 64)), + (61, 0, 0): ('pat07.ppm', (0, 168, 24, 24)), + (62, 0, 0): ('pat05.ppm', (0, 96, 24, 24)), + (62, 'l'): ('pat19.ppm', (0, 128, 40, 32)), + (63, 0, 0): ('pat03.ppm', (0, 0, 24, 24)), + (63, 'l'): ('pat20.ppm', (0, 0, 40, 32)), + (64, 0, 0): ('pat02.ppm', (0, 96, 24, 24)), + (64, 'l'): ('pat13.ppm', (0, 64, 40, 32)), + (65, 0, 0): ('pat10.ppm', (0, 0, 24, 24)), + (65, 'l'): ('pat19.ppm', (0, 32, 40, 32)), + (66, 0, 0): ('pat07.ppm', (0, 144, 24, 24)), + (66, 'l'): ('pat17.ppm', (0, 192, 40, 32)), + (67, 0, 0): ('pat05.ppm', (0, 72, 24, 24)), + (67, 'l'): ('pat11.ppm', (0, 224, 40, 32)), + (68, 0, 0): ('pat03.ppm', (0, 24, 24, 24)), + (68, 'l'): ('pat15.ppm', (0, 32, 40, 32)), + (69, 0, 0): ('pat00.ppm', (0, 96, 24, 24)), + (69, 'l'): ('pat13.ppm', (0, 192, 40, 32)), + (70, 0, 0): ('pat08.ppm', (0, 192, 24, 24)), + (70, 'l'): ('pat17.ppm', (0, 0, 40, 32)), + (71, 0, 0): ('pat06.ppm', (0, 0, 24, 24)), + (72, 0, 0): ('pat00.ppm', (0, 192, 24, 24)), + (72, 'l'): ('pat15.ppm', (0, 128, 40, 32)), + (73, 0, 0): ('pat08.ppm', (0, 120, 24, 24)), + (74, 0, 0): ('pat06.ppm', (0, 48, 24, 24)), + (75, 0, 0): ('pat03.ppm', (0, 192, 24, 24)), + (76, 0, 0): ('pat01.ppm', (0, 144, 24, 24)), + (76, 'l'): ('pat11.ppm', (0, 0, 40, 32)), + (77, 0, 0): ('pat09.ppm', (0, 72, 24, 24)), + (77, 'l'): ('pat14.ppm', (0, 32, 40, 32)), + (78, 0, 0): ('pat06.ppm', (0, 216, 24, 24)), + (78, 'l'): ('pat13.ppm', (0, 160, 40, 32)), + (79, 0, 0): ('pat04.ppm', (0, 144, 24, 24)), + (79, 'l'): ('pat16.ppm', (0, 160, 40, 32)), + (80, 0, 0): ('pat05.ppm', (0, 48, 24, 24)), + (81, 0, 0): ('pat02.ppm', (0, 192, 24, 24)), + (81, 'l'): ('pat17.ppm', (0, 128, 40, 32)), + (82, 0, 0): ('pat00.ppm', (0, 48, 24, 24)), + (82, 'l'): ('pat12.ppm', (0, 0, 40, 32)), + (83, 0, 0): ('pat08.ppm', (0, 24, 24, 24)), + (83, 'l'): ('pat11.ppm', (0, 96, 40, 32)), + (84, 0, 0): ('pat05.ppm', (0, 216, 24, 24)), + (84, 'l'): ('pat14.ppm', (0, 224, 40, 32)), + (84, 'r'): ('pat18.ppm', (0, 224, 40, 32)), + (85, 0, 0): ('pat03.ppm', (0, 96, 24, 24)), + (86, 0, 0): ('pat01.ppm', (0, 0, 24, 24)), + (87, 0, 0): ('pat08.ppm', (0, 216, 24, 24)), + (88, 0, 0): ('pat03.ppm', (0, 168, 24, 24)), + (88, 'l'): ('pat20.ppm', (0, 32, 40, 32)), + (89, 0, 0): ('pat01.ppm', (0, 48, 24, 24)), + (90, 0, 0): ('pat09.ppm', (0, 24, 24, 24)), + (90, 'l'): ('pat12.ppm', (0, 224, 40, 32)), + (91, 0, 0): ('pat06.ppm', (0, 144, 24, 24)), + (92, 0, 0): ('pat04.ppm', (0, 120, 24, 24)), + (92, 'l'): ('pat18.ppm', (0, 192, 40, 32)), + (93, 0, 0): ('pat02.ppm', (0, 24, 24, 24)), + (93, 'l'): ('pat11.ppm', (0, 160, 40, 32)), + (94, 0, 0): ('pat09.ppm', (0, 192, 24, 24)), + (94, 'l'): ('pat19.ppm', (0, 0, 40, 32)), + (95, 0, 0): ('pat07.ppm', (0, 72, 24, 24)), + (95, 'l'): ('pat15.ppm', (0, 96, 40, 32)), + (96, 0, 0): ('pat06.ppm', (0, 168, 24, 24)), + (97, 0, 0): ('pat04.ppm', (0, 72, 24, 24)), + (97, 'l'): ('pat16.ppm', (0, 0, 40, 32)), + (98, 0, 0): ('pat02.ppm', (0, 0, 24, 24)), + (98, 'l'): ('pat19.ppm', (0, 160, 40, 32)), + (99, 0, 0): ('pat09.ppm', (0, 168, 24, 24))} diff --git a/bubbob/player.py b/bubbob/player.py new file mode 100644 index 0000000..3a336a9 --- /dev/null +++ b/bubbob/player.py @@ -0,0 +1,1213 @@ +from __future__ import generators +import random, math, time +import gamesrv +import images +import boards +import bubbles +from boards import * +from images import ActiveSprite +from mnstrmap import GreenAndBlue, LetterBubbles, PlayerBubbles +from mnstrmap import DigitsMisc + +KEEPALIVE = 5*60 # seconds +CheatDontDie = 0 + + +class Dragon(ActiveSprite): + priority = 1 + mdef = PlayerBubbles + fly_counter = 0 +## glueddown = None + + DCAP = { + 'hspeed': 1, + 'firerate': 2, + 'shootthrust': 8.0, + 'infinite_shield': 1, + 'shield': 50, + 'gravity': 0.21, + 'bubbledelay': 0, + 'shootbubbles': (), + 'pinball': 0, +## 'nojump': 0, + 'autofire': 0, + 'ring': 0, + 'hotstuff': 0, + 'left2right': 1, + 'slippy': 0, + 'vslippy': 0.0, + 'lookforward': 1, + 'fly': 0, + 'jumpdown': 0, + 'flower': 1, + 'bigflower': None, + 'overlayglasses': 0, + 'teleport': 0, + 'breakwalls': 0, + 'carrying': (), + 'key_left': 'key_left', + 'key_right': 'key_right', + 'key_jump': 'key_jump', + 'key_fire': 'key_fire', + } + SAVE_CAP = {'hspeed': 1, + 'firerate': 2, + 'shootthrust': 8.0 / 1.5, + 'flower': -1} + + def __init__(self, bubber, x, y, dir, dcap=DCAP): + self.bubber = bubber + self.dir = dir + icobubber = dcap.get('bubbericons', bubber) + ActiveSprite.__init__(self, icobubber.icons[0, dir], x, y) + self.fire = 0 + self.up = 0.0 + self.watermoveable = 0 + self.dcap = dcap.copy() + self.dcap.update(self.bubber.pcap) + BubPlayer.DragonList.append(self) + self.gen.append(self.normal_movements()) + self.overlaysprite = None + self.overlayyoffset = 4 + self.hatsprite = None + self.hatangle = 1 + self.isdying = 0 + self.lifegained = 0 + self.playing_fish = False + if BubPlayer.SuperSheep: + self.become_monster('Sheep', immed=1) + elif BubPlayer.SuperFish: + self.become_fish() + + def kill(self): + try: + BubPlayer.DragonList.remove(self) + except ValueError: + pass + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + if self.hatsprite is not None: + if self.hatsprite.alive: + self.hatsprite.kill() + self.hatsprite = None + + def die(self): + if (self in BubPlayer.DragonList and not self.dcap['shield'] + and not CheatDontDie): + BubPlayer.DragonList.remove(self) + self.gen = [self.dying()] + self.play(images.Snd.Die) + #wasting = boards.curboard.wastingplay + #if wasting is not None: + # wasting[self.bubber] = len(wasting) + + def dying(self, can_loose_letter=1): + self.isdying = 1 + if self.hatsprite is not None: + if self.hatsprite.alive: + dxy = [3*self.dir, -7] + self.hatsprite.gen = [self.hatsprite.parabolic(dxy)] + self.hatsprite = None + lst = [bonus for timeout, bonus in self.dcap['carrying'] + if hasattr(bonus, 'buildoutcome')] + #if random.random() > 0.2] + if lst: + # loose some bonuses + from bonuses import BonusMaker + for bonus in lst: + self.bubber.givepoints(-bonus.points) + BonusMaker(self.x, self.y, [bonus.nimage], + outcome=bonus.buildoutcome()) + elif self.bubber.letters and random.random() > 0.59 and can_loose_letter: + # loose a letter + lst = range(6) + random.shuffle(lst) + for l in lst: + lettername = bubbles.extend_name(l) + if lettername in self.bubber.letters: + s = self.bubber.letters[lettername] + del self.bubber.letters[lettername] + if isinstance(s, ActiveSprite): + s.kill() + scoreboard() + s = bubbles.LetterBubble(self.bubber.pn, l) + s.move(self.x, self.y) + break + icons = self.getcurrenticons() + for i in range(2, 32): + mode = 5 + ((i>>1) & 3) + self.seticon(icons[mode, self.dir]) + yield None + self.kill() + if not self.bubber.dragons: + self.bubber.bubberdie() + # self.bubber.badpoints = self.bubber.points // 3 + + def killing(self): + self.kill() + if 0: + yield None + + def carrybonus(self, bonus, timeout=500): + timeout += BubPlayer.FrameCounter + lst = list(self.dcap['carrying']) + lst.append((timeout, bonus)) + lst.sort() + self.dcap['carrying'] = lst + + def listcarrybonuses(self): + return [bonus for timeout, bonus in self.dcap['carrying']] + +## def moebius(self): +## self.dir = -self.dir +## self.dcap['left2right'] *= -1 + + def monstervisible(self): + return not self.dcap['ring'] and not self.dcap['shield'] + + def getcurrenticons(self, imgtransform=''): + if self.playing_fish: + imgtransform = 'fish' + icobubber = self.dcap.get('bubbericons', self.bubber) + try: + return icobubber.transformedicons[imgtransform] + except KeyError: + icons = icobubber.transformedicons[imgtransform] = {} + icobubber.loadicons(imgtransform) + return icons + + def normal_movements(self): + yfp = 0.0 + hfp = 0 + angryticks = 0 + mytime = 0 + privatetime = 0 + while 1: + dcap = self.dcap + self.poplist = [self] + carrying = dcap['carrying'] + while carrying and carrying[0][0] < BubPlayer.FrameCounter: + timeout, bonus = carrying.pop(0) + if bonus.endaction: + bonus.endaction(self) + del bonus + + bubber = self.bubber + wannafire = bubber.getkey(dcap, 'key_fire') + wannajump = bubber.getkey(dcap, 'key_jump') + wannago = bubber.wannago(dcap) + bottom_up = self.bottom_up() + onground1 = (onground,underground)[bottom_up] + + if dcap['autofire']: + wannafire = 1 + if dcap['pinball']: + wannajump = 1 + if dcap['pinball'] > 1: + if self.up: + self.up *= 0.982 ** dcap['pinball'] + if dcap['hotstuff']: + if not wannago: + if self.dir * (random.random()-0.07) < 0: + wannago = -1 + else: + wannago = 1 + wannafire = 1 + if self.fire > (11 // dcap['hotstuff']): + self.fire = 0 +## if dcap['hotstuff'] > 1 and random.random() < 0.4: +## from bubbles import FireDrop +## FireDrop(self.x + HALFCELL, self.y + HALFCELL) + if wannago: + self.dir = wannago * dcap['lookforward'] + if self.x & 1: + self.step(self.dir, 0) + if dcap['slippy']: + vx = dcap['vslippy'] + if wannago: + vx += wannago * 0.05 + else: + vx *= 0.95 + if vx < 0.0: + wannago = -1 + else: + wannago = 1 + dcap['vslippy'] = vx + mytime = (mytime+dcap['lookforward']) % 12 + hfp += abs(vx) + else: + hfp += dcap['hspeed'] + +## if self.glueddown: +## if wannajump or not dcap['nojump']: +## del self.glueddown +## else: +## # glued gliding movements +## self.step(self.x & 1, self.y & 1) +## if wannago: +## mytime = (mytime+dcap['lookforward']) % 12 +## else: +## hfp = 0 +## while hfp > 0 and self.glueddown: +## gx, gy = self.glueddown +## dx = wannago*gy +## dy = -wannago*gx +## x0 = (self.x + gx + dx) // CELL + 1 +## y0 = (self.y + gy + dy) // CELL + 1 +## if ' ' == bget(x0+dx, y0+dy) == bget(x0+dx-gx, y0+dy-gy): +## self.step(2*dx, 2*dy) +## # detached from this wall? +## x1 = (self.x + gx + dx) // CELL + 1 +## y1 = (self.y + gy + dy) // CELL + 1 +## if (' ' == bget(x1-dx+gx, y1-dy+gy) +## == bget(x0 +gx, y0 +gy) +## == bget(x0+dx+gx, y0+dy+gy)): +## if bget(x0-dx+gx, y0-dy+gy) != ' ': +## # rotate around the corner +## self.glueddown = -dx, -dy +## self.step(2*gx, 2*gy) +## else: +## del self.glueddown +## elif bget(x0-gx, y0-gy) == ' ': +## # attach to the wall into which we are running +## if (self.x*dx | self.y*dy) % CELL != 0: +## if ((((self.x-2*dx)*dx | (self.y-2*dy)*dy) & +## ((self.x-4*dx)*dx | (self.y-4*dy)*dy)) +## % CELL == 0): +## self.step(-2*dx, -2*dy) +## else: +## del self.glueddown +## else: +## self.glueddown = dx, dy +## else: +## del self.glueddown +## self.vertical_warp() +## hfp -= 0.82 # slightly faster than usual + # normal left or right movements + breakwalls = dcap['breakwalls'] + while hfp > 0: + hfp -= 1 + dir = 0 + if wannago == -1: + x0 = (self.x+1)//CELL + y0 = (self.y+4 - bottom_up*(CELL+4)) // CELL + 1 + y0bis = (self.y+CELL-1) // CELL + 1 - bottom_up + if bget(x0,y0) == ' ' == bget(x0,y0bis): + dir = -1 + elif breakwalls: + self.breakwalls(x0, y0bis, -1) + elif wannago == 1: + x0 = (self.x-3)//CELL + 2 + y0 = self.y // CELL + 1 - bottom_up + y0bis = (self.y+CELL-1) // CELL + 1 - bottom_up + if bget(x0,y0) == ' ' == bget(x0,y0bis): + dir = +1 + elif breakwalls: + self.breakwalls(x0, y0bis, 1) + self.step(2*dir, 0) + if dir: + mytime = (mytime+dcap['lookforward']) % 12 + else: + f = - dcap['vslippy'] * (dcap['slippy']+1)/3.0 + dcap['vslippy'] = max(min(f, 10.0), -10.0) + hfp = 0 + onbubble = None + if not dcap['infinite_shield']: + touching = images.touching(self.x+1, self.y+1, 30, 30) + touching.reverse() + for s in touching: + if s.touched(self): + onbubble = s + elif bubber.key_left or bubber.key_right or bubber.key_jump or bubber.key_fire: + dcap['infinite_shield'] = 0 + + dir = self.dir + icons = self.getcurrenticons('vflip' * bottom_up) + + if self.playing_fish: + mode = self.one_fish_frame(onground1, bottom_up) + elif self.up: + # going up + mode = 9 + self.up -= dcap['gravity'] + if (self.up,-self.up)[bottom_up] < 4.0: + self.up = 0.0 + mode = 10 + else: + ny = self.y + yfp - self.up + self.move(self.x, int(ny)) + yfp = ny - self.y + self.vertical_warp() + if wannago and dcap['teleport']: + for t in self.teleport(wannago, icons, 0): + yield t + else: + # going down or staying on ground + if wannajump and onbubble: + ground = True + onbubble.dragon_jumped = True, bottom_up + else: + ground = onground1(self.x, self.y) + if ground: + if wannajump: + self.play(images.Snd.Jump) + if dcap['jumpdown'] and not onbubble: + self.step(0, (1, -1)[bottom_up]) + mode = 10 + bubber.emotic(self, 4) + else: + yfp = 0.0 + self.up = (7.5,-7.5)[bottom_up] + mode = 9 + else: + mode = mytime // 4 + if wannago and dcap['teleport']: + for t in self.teleport(wannago, icons): + yield t + else: + mode = 10 + if dcap['fly']: + self.fly_counter += 1 + if self.fly_counter < dcap['fly']: + ny = self.y + else: + del self.fly_counter + ny = self.y+(1,-1)[bottom_up] + else: + ny = (self.y+(4,-1)[bottom_up]) & ~3 + nx = self.x + if nx < 32: + nx += 2 + elif nx > boards.bwidth - 64: + nx -= 2 + self.move(nx, ny) + self.vertical_warp() + if wannago and dcap['teleport']: + for t in self.teleport(wannago, icons, 0): + yield t + + if wannafire and not self.fire: + self.firenow() + self.hatangle = 1 + if self.fire: + if self.fire <= 5: + mode = 3 + self.hatangle = 2 + elif self.fire <= 10: + mode = 4 + self.hatangle = 3 + self.fire += 1 + if self.fire >= 64 // dcap['firerate']: + self.fire = 0 + + s = dcap['shield'] + if s: + if dcap['infinite_shield'] and s < 20: + s += 4 + s -= 1 + if dcap['overlayglasses']: + self.overlayyoffset = ({3: 2, 4: 0, + 9: 3, 10: 5}.get(mode, 4) + + self.playing_fish * 2) + elif s & 2: + mode = 11 + dcap['shield'] = s + if dcap['ring']:# and random.random() > 0.1: + if dcap['ring'] > 1: + mode = 12 + else: + mode = 11 + self.seticon(icons[mode, dir]) + self.watermoveable = not wannajump + + privatetime += BubPlayer.PlayersPrivateTime + while privatetime >= 100: + yield None + privatetime -= 100 + + if self.angry: + if angryticks == 0: + s = ActiveSprite(icons[11, self.dir], self.x, self.y) + s.gen.append(s.die([None], speed=10)) + angryticks = 6 + angryticks -= 1 + #if BubPlayer.Moebius and BubPlayer.FrameCounter % 5 == 0: + # s = ActiveSprite(icons[11, -self.dir], + # boards.bwidth - 2*CELL - self.x, self.y) + # s.gen.append(s.die([None], speed=2)) + + def teleport(self, wannago, icons, max_delta_y=CELL): + #if self.dcap['shield']: + # return + from bonuses import Bonus, Megabonus + best_dx = boards.bwidth + centerx = self.x + self.ico.w // 2 + basey = (self.y + self.ico.h + 8) & ~15 + for s in images.ActiveSprites: + if (isinstance(s, Bonus) and s.touchable + and abs(s.y+s.ico.h - basey) <= max_delta_y + and s.is_on_ground()): + dx = (s.x + (wannago < 0 and s.ico.w)) - centerx + if dx*wannago > 0: + dx = abs(dx) + CELL + if dx < best_dx: + best_dx = dx + best = s + if not (42 <= best_dx < boards.bwidth): + return + self.play(images.Snd.Shh) + self.up = 0.0 + s = best + dx = best_dx + basey = s.y+s.ico.h + desty = basey - self.ico.h + dy_dx = float(desty - self.y) / dx + self.dir = wannago + ico = images.make_darker(icons[0, wannago], True) + # speed up + fx = self.x + fy = self.y + curdx = 0.0 + stepx = 2.0 + t = 0 + while 1: + if curdx < 0.5*dx: + stepx *= 1.13 + else: + stepx /= 1.13 + fx += wannago * stepx + fy += dy_dx * stepx + curdx += stepx + if curdx >= dx or stepx < 2.0: + fx += wannago * (dx - curdx) + break + self.move(int(fx), int(fy), ico) + # make the target bonus bounce a bit + if s.alive: + dy = (t & 7) * 4 + if dy > 16: + dy = 32-dy + s.move(s.x, basey - s.ico.h - dy) + t += 1 + yield None + self.move(int(fx), desty) + self.dcap['shield'] = 50 + + def breakwalls(self, x, y0, dir): + if self.dcap['breakwalls'] > BubPlayer.FrameCounter: + return # wait before breaking more walls + if not (2 <= x < boards.curboard.width-2): + return + ys = [] + for y in (y0, y0-1): + if 0 <= y < boards.curboard.height and bget(x, y) == '#': + ys.append(y) + if len(ys) == 2: + from bonuses import DustStar + dir *= self.dcap['hspeed'] + for y in ys: + w = boards.curboard.killwall(x, y) + s = ActiveSprite(w.ico, w.x, w.y) + dxy = [dir+random.random()-0.5, + -random.random()*3.0] + DustStar(w.x, w.y, dxy[0], dxy[1], big=0) + s.gen.append(s.parabolic(dxy)) + self.dcap['breakwalls'] = BubPlayer.FrameCounter + 40 + + def enter_new_board(self): + self.playing_fish = False + self.lifegained = 0 + + def become_fish(self): + self.playing_fish = True + icons = self.getcurrenticons() + self.seticon(icons[11, self.dir]) + + def one_fish_frame(self, onground1, bottom_up): + if self.bubber.getkey(self.dcap, 'key_jump'): + # swimming up + self.step(0, (-2, 2)[bottom_up]) + else: + if random.random() < 0.05: + bubbles.FishBubble(self) + if not onground1(self.x, self.y): + # swimming down + ny = (self.y+(2,-1)[bottom_up]) & ~1 + self.move(self.x, ny) + self.vertical_warp() + return ((BubPlayer.FrameCounter // 3) % 6) * 0.5 + + def to_front(self): + ActiveSprite.to_front(self) + if self.dcap['overlayglasses']: + ico = images.sprget(('glasses', self.dir)) + y = self.y + self.overlayyoffset + if self.overlaysprite is None or not self.overlaysprite.alive: + self.overlaysprite = images.ActiveSprite(ico, self.x, y) + else: + self.overlaysprite.to_front() + self.overlaysprite.move(self.x, y, ico) + self.overlaysprite.gen = [self.overlaysprite.die([None])] + + def bottom_up(self): + return self.dcap['gravity'] < 0.0 + + def watermove(self, x, y): + # for WaterCell.flooding() + if self in BubPlayer.DragonList and self.watermoveable: + self.watermoveable = 0 + self.move(x, y) + self.up = 0.0 + if self.dcap['shield'] < 6: + self.dcap['shield'] = 6 + if self.fire <= 10: + self.fire = 11 + + def become_monster(self, clsname, big=0, immed=0): + if self in BubPlayer.DragonList: + BubPlayer.DragonList.remove(self) + + import monsters, mnstrmap + mcls = getattr(monsters, clsname) + mdef = getattr(mnstrmap, clsname) + m = mcls(mdef, self.x, self.y, self.dir, in_list=self.bubber.dragons) + m.become_monster(self.bubber, self.dcap, big, immed) + self.seticon(m.ico) + self.gen = [self.killing()] + + def become_bubblingeyes(self, bubble): + if self in BubPlayer.DragonList: + self.bubber.emotic(self, 4) + BubPlayer.DragonList.remove(self) + + import bubbles + bubble.to_front() + m = bubbles.BubblingEyes(self.bubber, self.dcap, bubble) + self.bubber.dragons.append(m) + self.gen = [self.killing()] + return 1 + else: + return 0 + + def firenow(self): + self.fire = 1 + #if boards.curboard.wastingplay is None: + shootbubbles = self.dcap['shootbubbles'] + special_bubbles = shootbubbles and shootbubbles.pop() + thrustfactors = None + N = self.dcap['flower'] + if N == 1: + angles = [0] + elif N > 1: + angles = [i*(2.0*math.pi/N) for i in range(N)] + self.dcap['flower'] = N*2//3 + elif N > -16: # triple fire, possibly cumulative + angles = [0] + for i in range(1, -N+1): + angles.append(i * 0.19) + angles.append(i * -0.19) + else: # heptuple fire + c = 0.17 + a = math.sqrt(1-c+c*c) + alpha = math.atan2(math.sqrt(3)/2*c, 1-c/2) + b = math.sqrt(1+c+c*c) + beta = math.atan2(math.sqrt(3)/2*c, 1+c/2) + angles = [0, 0, 0, alpha, -alpha, beta, -beta] + thrustfactors = [1, 1-c, 1+c, a, a, b, b] + dir = self.dir + x = self.x +## if self.glueddown: +## gx, gy = self.glueddown +## dir = dir*gy +## if not dir: +## dir = 1 +## delta = self.dir*gx * math.pi/2 +## angles = [angle-delta for angle in angles] +## x -= 16 + if self.dcap['hotstuff'] > 1: + base = (random.random()-0.5)*math.pi + angles = [a + base for a in angles] + if self.dcap['bigflower'] is not None: + N = 45 + angle = BubPlayer.FrameCounter - self.dcap['bigflower'] + if not (0 <= angle < N): + self.dcap['bigflower'] = BubPlayer.FrameCounter + angle = 0 + angles = [-angle * (2.0*math.pi/N) * self.dir] + thrustfactors = None + self.fire = max(1, 64 // self.dcap['firerate'] - 2) + if self.dcap['autofire'] >= 1: + self.dcap['autofire'] -= 1 + if not thrustfactors: + thrustfactors = [None] * len(angles) + import bonuses + for angle, thrustfactor in zip(angles, thrustfactors): + args = (self, x + 4*dir, self.y, dir, + special_bubbles, angle, thrustfactor, + self.dcap['shootthrust']) + bonuses.record_shot(args) + bubbles.DragonBubble(*args) + #else: + # from monsters import DragonShot + # DragonShot(self) + +class BubPlayer(gamesrv.Player): + # global state + FrameCounter = 0 + PlayerList = [] + DragonList = [] + MonsterList = [] + LimitScore = 0 + LimitScoreColor = None + LimitTime = None + PlayersPrivateTime = 100 + SuperSheep = False + SuperFish = False + #HighScore = 0 + #HighScoreColor = None + + INIT_BOARD_CAP = { + #'LatestLetsGo': -999, + 'BubblesBecome': None, + 'MegaBonus': None, + 'BaseFrametime': 1.0, + 'LeaveBonus': None, +## 'Moebius': 0, + 'OverridePlayerIcon': None, + 'DisplayPoints': None, + 'SuperSheep': False, + 'SuperFish' : False, + } + TRANSIENT_DATA = ('_client', 'key_left', 'key_right', + 'key_jump', 'key_fire', 'pn', 'nameicons', + 'icons', 'transformedicons', + 'standardplayericon', 'iconnames') + + FISH_MODE_MAP = {0: ('', 0), # swim + 0.5: ('', 1), + 1: ('', 2), + 1.5: ('', 3), + 2: ('', 2), + 2.5: ('', 1), + 3: ('', 4), # lancer de bulle + 4: ('', 5), + 5: ('', 3), # mort + 6: ('cw', 3), + 7: ('rot180', 3), + 8: ('ccw', 3), + 9: ('', 3), # saut, montant + 10: ('', 3), # saut, descend + 11: ('', 6), # shielded + 12: 'black', + } + + def __init__(self, n): + self.pn = n + self.icons = {} + self.transformedicons = {'': self.icons} + self.standardplayericon = images.sprget(GreenAndBlue.players[n][3]) + self.iconnames = { + (0, -1): GreenAndBlue.players[n][0], # walk + (0, +1): GreenAndBlue.players[n][3], + (1, -1): GreenAndBlue.players[n][1], + (1, +1): GreenAndBlue.players[n][4], + (2, -1): GreenAndBlue.players[n][2], + (2, +1): GreenAndBlue.players[n][5], + (3, -1): GreenAndBlue.players[n][6], # lancer de bulle + (3, +1): GreenAndBlue.players[n][8], + (4, -1): GreenAndBlue.players[n][7], + (4, +1): GreenAndBlue.players[n][9], + (5, -1): GreenAndBlue.players[n][0], # mort + (5, +1): GreenAndBlue.players[n][0], + (6, -1): GreenAndBlue.players[n][11], + (6, +1): GreenAndBlue.players[n][10], + (7, -1): GreenAndBlue.players[n][12], + (7, +1): GreenAndBlue.players[n][12], + (8, -1): GreenAndBlue.players[n][10], + (8, +1): GreenAndBlue.players[n][11], + (9, -1): GreenAndBlue.jumping_players[n][2], # saut, montant + (9, +1): GreenAndBlue.jumping_players[n][3], + (10,-1): GreenAndBlue.jumping_players[n][0], # saut, descend + (10,+1): GreenAndBlue.jumping_players[n][1], + (11,-1): 'shield-left', # shielded + (11,+1): 'shield-right', + (12,-1): 'black', # totally invisible + (12,+1): 'black', + } + self.nameicons = [] + self.team = -1 + self.reset() + + def reset(self): + self.letters = {} + #self.bonbons = 0 + self.points = 0 + self.nextextralife = gamesrv.game.extralife + self.lives = boards.get_lives() + self.lifegained = 0 + #self.badpoints = 0 + self.pcap = {} + self.dragons = [] + self.keepalive = None + self.stats = {'bubble': 0, 'die': 0} + + def loadicons(self, flip): + icons = self.transformedicons[flip] + if flip == 'fish': + for dir in (-1, 1): + for key, value in self.FISH_MODE_MAP.items(): + if value == 'black': + flip = '' + else: + flip, index = value + value = GreenAndBlue.fish[self.pn][index] + if dir > 0: + flip = flip or 'hflip' + icons[key, dir] = images.sprget((flip, value)) + else: + for key, value in self.iconnames.items(): + icons[key] = images.sprget((flip, value)) + + def setplayername(self, name): + name = name.strip() + for t in [0, 1]: + if name.endswith('(%d)' % (t+1)): + self.team = t + name = name[:-3].strip() + #print "New player in team", t, "with name", name + break + else: + self.team = -1 + #print "New player with no team:", name + icons = [images.sprcharacterget(c) for c in name] + self.nameicons = [ico for ico in icons if ico is not None][:16] + self.nameicons.reverse() + scoreboard() + + def playerjoin(self): + n = self.pn + if not self.icons: + self.loadicons(flip='') + self.keepalive = None + if self.points or self.letters: + print 'New player continues at position #%d.' % n + else: + print 'New player is at position #%d.' % n + self.reset() + self.key_left = 0 + self.key_right = 0 + self.key_jump = 0 + self.key_fire = 0 + players = [p for p in BubPlayer.PlayerList + if p.isplaying() and p is not self] + self.enterboard(players) + scoreboard() + #if BubPlayer.LatestLetsGo < BubPlayer.FrameCounter - 30: + images.Snd.LetsGo.play() + #BubPlayer.LatestLetsGo = BubPlayer.FrameCounter + + def playerleaves(self): + print 'Closing position #%d.' % self.pn + self.savecaps() + self.zarkoff() + self.keepalive = time.time() + KEEPALIVE + scoreboard() + + def sameteam(self, other): + return self.team != -1 and self.team == other.team + + def enterboard(self, players): + players = [p for p in players if not p.sameteam(self)] + leftplayers = [p for p in players if p.start_left] + rightplayers = [p for p in players if not p.start_left] + self.start_left = (len(leftplayers) + random.random() < + len(rightplayers) + random.random()) + self.lifegained = 0 + + def savecaps(self): + self.pcap = {} + dragons = self.dragons + if dragons: + for key, minimum in Dragon.SAVE_CAP.items(): + self.pcap[key] = max(minimum, + max([d.dcap[key] for d in dragons])) + + def zarkoff(self): + for d in self.dragons[:]: + d.kill() + del self.dragons[:] + + def zarkon(self): + if self.key_left + self.key_right >= 1999997: + for dragon in self.dragons: + self.emotic(dragon, 6) + self.key_left = self.key_right = 900000 + if self.key_left: self.key_left -= 1 + if self.key_right: self.key_right -= 1 + if self.key_jump: self.key_jump -= 1 + if self.key_fire: self.key_fire -= 1 + #if self.badpoints and not (self.FrameCounter & 7): + # percent = (int(self.points*0.0000333)+1) * 100 + # decr = min(self.badpoints, percent) + # self.badpoints -= decr + # self.givepoints(-decr) + if boards.curboard and not self.dragons and self.lives != 0: + #wasting = boards.curboard.wastingplay + #if wasting is not None and self in wasting: + # return + if self.start_left: + x0 = 3*CELL + dir = 1 + else: + x0 = boards.bwidth - 5*CELL + dir = -1 + y = boards.bheight - 3*CELL + for x in [x0, x0+4*dir, x0+8*dir, x0+12*dir, x0+16*dir, + x0-4*dir, x0-8*dir, x0]: + if onground(x,y): + for d in BubPlayer.DragonList: + if d.y == y and abs(d.x-x) <= 5: + break + else: + break + self.dragons.append(Dragon(self, x, y, dir)) + for key in self.pcap.keys(): + if key not in ('teleport', 'jumpdown'): + del self.pcap[key] + + def kLeft(self): + if self.key_left <= 1: + self.key_left = 1000000 + def kmLeft(self): + self.key_left = (self.key_left == 1000000) + def kRight(self): + if self.key_right <= 1: + self.key_right = 1000000 + def kmRight(self): + self.key_right = (self.key_right == 1000000) + def kJump(self): + if self.key_jump <= 1: + self.key_jump = 1000000 + def kmJump(self): + self.key_jump = (self.key_jump == 1000000) + def kFire(self): + if self.key_fire <= 1: + self.key_fire = 1000000 + def kmFire(self): + self.key_fire = (self.key_fire == 1000000) + + def bubberdie(self): + self.stats['die'] += 1 + if self.lives is not None and self.lives > 0: + self.lives -= 1 + scoreboard() + + def getkey(self, dcap, key_name): + return getattr(self, dcap[key_name]) + + def setkey(self, dcap, key_name, value): + setattr(self, dcap[key_name], value) + + def wannago(self, dcap): + return dcap['left2right'] * cmp(self.getkey(dcap, 'key_right'), + self.getkey(dcap, 'key_left')) + + def turn_single_shot(self, dcap): + for name in ('key_left', 'key_right'): + n = self.getkey(dcap, name) + if n < 999997 and n != 1: + self.setkey(dcap, name, 0) + wannago = self.wannago(dcap) + for name in ('key_left', 'key_right'): + self.setkey(dcap, name, 0) + return wannago + + def givepoints(self, points): + self.points += points + if self.points < 0: + self.points = 0 + while self.points >= self.nextextralife: + if self.lives is not None and self.lives > 0: + if gamesrv.game.lifegainlimit is None or self.lifegained < gamesrv.game.lifegainlimit: + if self.dragons: + dragon = random.choice(self.dragons) + dragon.play(images.Snd.Extralife) + else: + images.Snd.Extralife.play() + self.lives += 1 + self.lifegained += 1 + self.nextextralife += gamesrv.game.extralife + if self.LimitScoreColor is not None and self.points >= self.LimitScore: + boards.replace_boardgen(boards.game_over(), 1) + #if self.points > BubPlayer.HighScore: + # BubPlayer.HighScore = self.points + # BubPlayer.HighScoreColor = self.pn + scoreboard() + + def giveletter(self, l, promize=100000): + + #logf = open('log', 'a') + #print >> logf 'giveletter %d:' % self.pn, l + #logf.close() + + lettername = bubbles.extend_name(l) + if lettername not in self.letters: + self.letters[lettername] = 1 +## nimage = getattr(LetterBubbles, lettername) +## x0, y0 = self.infocoords() +## s = images.ActiveSprite(images.sprget(nimage[1]), x0+l*(CELL-1), y0 - 3*CELL) +## s.gen.append(s.cyclic([nimage[1], nimage[2], nimage[1], nimage[0]], 7)) + scoreboard() + if len(self.letters) == 6: + import monsters + monsters.argh_em_all() + import bonuses + if self.dragons: + for i in range(3): + dragon = random.choice(self.dragons) + bonuses.starexplosion(dragon.x, dragon.y, 1) + for lettername in self.letters: + dragon = random.choice(self.dragons) + nimages = getattr(LetterBubbles, lettername) + bonuses.Parabolic2(dragon.x, dragon.y, nimages) + dragon = random.choice(self.dragons) + dragon.play(images.Snd.Extralife) + music = [images.music_old] + boards.replace_boardgen(boards.last_monster_killed(460, music)) + self.givepoints(promize) + + def emotic(self, dragon, strenght): + bottom_up = hasattr(dragon, 'bottom_up') and dragon.bottom_up() + vshift = getattr(dragon, 'up', 0.0) + for i in range(7): + angle = math.pi/6 * i + dx, dy = -math.cos(angle), -math.sin(angle) + nx = random.randrange(3,12)*dx + ny = random.randrange(3,9)*dy - 12 + if bottom_up: + dy = -dy + ny = -ny + e = ActiveSprite(images.sprget(('vflip'*bottom_up, ('emotic', i))), + int(dragon.x + 8 + nx), + int(dragon.y + 8 + ny - vshift)) + e.gen.append(e.straightline((3.3+random.random())*dx, (2.3+random.random())*dy)) + e.gen.append(e.die([None], strenght)) + + +def upgrade(p): + p.__class__ = BubPlayer + p.key_left = 0 + p.key_right = 0 + p.key_jump = 0 + p.key_fire = 0 + p.dragons = [] + + +def xyiconumber(digits, x, y, pts, lst, width=7): + if pts >= 10**width: + pts = 10**width-1 + for l in range(width): + ico = images.sprget(digits[pts % 10]) + lst.append((x + (ico.w+1)*(width-1-l), y, ico)) + pts = pts//10 + if not pts: + break + return lst[-1][0] + +def scoreboard(reset=0, inplace=0, compresslimittime=0): + endgame = 1 + if reset: + for p in BubPlayer.PlayerList: + if inplace: + for s in p.letters.values(): + if isinstance(s, ActiveSprite): + s.kill() + if len(p.letters) == 6: + p.letters.clear() + for key in p.letters: + p.letters[key] = 2 + brd = boards.curboard + if not brd or not gamesrv.sprites_by_n: + return + lst = [] + bubblesshown = {} + plist = [] + teamslist = [[], []] + teamspoints = [0, 0] + for p in BubPlayer.PlayerList: + if p.isplaying(): + if p.lives != 0: + endgame = 0 + else: + if not p.keepalive: + continue + if p.keepalive < time.time(): + p.reset() + continue + if BubPlayer.DisplayPoints is not None: + points = BubPlayer.DisplayPoints(p) + else: + points = p.points + if p.team == -1: + plist.append((points, p, None)) + else: + teamslist[p.team].append((points,p)) + teamspoints[p.team] += points + teamslist[0].sort() + teamslist[1].sort() + plist.append((teamspoints[0], None, teamslist[0])) + plist.append((teamspoints[1], None, teamslist[1])) + plist.sort() + x0 = boards.bwidth + y0 = boards.bheight + for score, p, t in plist: + if p: + if p.lives == 0: + ico = images.sprget(GreenAndBlue.gameover[p.pn][0]) + elif p.icons: + if p.isplaying(): + mode = 0 + else: + mode = 11 + ico = BubPlayer.OverridePlayerIcon or p.icons[mode, -1] + lst.append((x0+9*CELL-ico.w, y0-ico.h, ico)) + #if boards.curboard.wastingplay is None: + for l in range(6): + name = bubbles.extend_name(l) + if name in p.letters: + x, y = x0+l*(CELL-1), y0-3*CELL + imglist = getattr(LetterBubbles, name) + ico = images.sprget(imglist[1]) + if gamesrv.game.End in (0, 1): + s = p.letters[name] + if (isinstance(s, ActiveSprite) and + BubPlayer.FrameCounter <= s.timeout): + s.move(x, y) + bubblesshown[s] = 1 + continue + if s == 1: + s = ActiveSprite(ico, x, y) + s.setimages(s.cyclic([imglist[0], imglist[1], + imglist[2], imglist[1]])) + s.timeout = BubPlayer.FrameCounter + 500 + p.letters[name] = s + bubblesshown[s] = 1 + continue + lst.append((x, y, ico)) + ## else: + ## ico = images.sprget(Bonuses.blue_sugar) + ## lst.append((x0+12, y0-3*CELL-8, ico)) + ## xyiconumber(DigitsMisc.digits_white, x0-19, y0-3*CELL+5, + ## p.bonbons, lst) + xyiconumber(GreenAndBlue.digits[p.pn], x0+2, y0-18, score, lst) + if p.lives is not None and p.lives > 0: + xyiconumber(DigitsMisc.digits_white, x0+7*CELL, y0-18, + p.lives, lst, width=2) + x = x0+13*HALFCELL + for ico in p.nameicons: + x -= 7 + lst.append((x, y0-35, ico)) + y0 -= 7*HALFCELL + else: # Team + for pscore, p in t: + if p.lives == 0: + ico = images.sprget(GreenAndBlue.gameover[p.pn][0]) + elif p.icons: + if p.isplaying(): + mode = 0 + else: + mode = 11 + ico = BubPlayer.OverridePlayerIcon or p.icons[mode, -1] + lst.append((x0+9*CELL-ico.w, y0-ico.h, ico)) + for l in range(6): + name = bubbles.extend_name(l) + if name in p.letters: + x, y = x0+l*(CELL-1), y0-2*CELL + imglist = getattr(LetterBubbles, name) + ico = images.sprget(imglist[1]) + if gamesrv.game.End in (0, 1): + s = p.letters[name] + if (isinstance(s, ActiveSprite) and + BubPlayer.FrameCounter <= s.timeout): + s.move(x, y) + bubblesshown[s] = 1 + continue + if s == 1: + s = ActiveSprite(ico, x, y) + s.setimages(s.cyclic([imglist[0], imglist[1], + imglist[2], imglist[1]])) + s.timeout = BubPlayer.FrameCounter + 500 + p.letters[name] = s + bubblesshown[s] = 1 + continue + lst.append((x, y, ico)) + x = x0+13*HALFCELL + for ico in p.nameicons: + x -= 7 + lst.append((x, y0-19, ico)) + y0 -= 4*HALFCELL + if t != []: + xyiconumber(GreenAndBlue.digits[t[-1][1].pn], x0+2, y0-18, score, lst) + ico = images.sprget(('hat', p.team, -1, 1)) + lst.append((x0+9*CELL-ico.w, y0-ico.h+16, ico)) + y0 -= 5*HALFCELL + for p in BubPlayer.PlayerList: + for name, s in p.letters.items(): + if isinstance(s, ActiveSprite) and s not in bubblesshown: + p.letters[name] = 2 + s.kill() + compressable = len(lst) + #if BubPlayer.HighScoreColor is not None: + # x = xyiconumber(GreenAndBlue.digits[BubPlayer.HighScoreColor], + # x0+2*CELL, HALFCELL, BubPlayer.HighScore, lst) + # ico = images.sprget(GreenAndBlue.players[BubPlayer.HighScoreColor][3]) + # lst.append((x-5*HALFCELL, 1, ico)) + if BubPlayer.LimitScoreColor is not None: + xyiconumber(GreenAndBlue.digits[BubPlayer.LimitScoreColor], + x0+2*CELL, HALFCELL, BubPlayer.LimitScore, lst) + if BubPlayer.LimitTime is not None: + seconds = int(BubPlayer.LimitTime) + xyiconumber(DigitsMisc.digits_white, x0+2*CELL, HALFCELL, + seconds // 60, lst, width=3) + ico = images.sprget('colon') + lst.append((x0+5*CELL-1, HALFCELL+1, ico)) + seconds = seconds % 60 + ico = images.sprget(DigitsMisc.digits_white[seconds // 10]) + lst.append((x0+6*CELL, HALFCELL, ico)) + ico = images.sprget(DigitsMisc.digits_white[seconds % 10]) + lst.append((x0+6*CELL+ico.w, HALFCELL, ico)) + ymin = HALFCELL + ico.h + elif compresslimittime: + ico = images.sprget(DigitsMisc.digits_white[0]) + ymin = HALFCELL + ico.h + else: + ymin = 0 + if not brd.bonuslevel: + if brd.num < 99: + xyiconumber(DigitsMisc.digits_white, 2, 2, brd.num+1, lst, width=2) + else: + xyiconumber(DigitsMisc.digits_white, 2, 2, brd.num+1, lst, width=3) + + # compress the scoreboard vertically if it doesn't fit + ymin += HALFCELL + if y0 < ymin: + factor = float(boards.bheight-ymin) / (boards.bheight-y0) + shift = ymin - y0*factor + 0.5 + for i in range(compressable): + x, y, ico = lst[i] + lst[i] = x, int((y+ico.h)*factor+shift)-ico.h, ico + + brd.writesprites('scoreboard', lst) + + if gamesrv.game.End in (0, 1): + gamesrv.game.End = endgame + + +# initialize global board data +def reset_global_board_state(): + for key, value in BubPlayer.INIT_BOARD_CAP.items(): + setattr(BubPlayer, key, value) +reset_global_board_state() diff --git a/bubbob/ranking.py b/bubbob/ranking.py new file mode 100644 index 0000000..d4a4d22 --- /dev/null +++ b/bubbob/ranking.py @@ -0,0 +1,391 @@ +from __future__ import generators +import random +import boards, images, gamesrv +from boards import CELL, HALFCELL +from mnstrmap import DigitsMisc, Flood, GreenAndBlue +from bubbles import Bubble +from bonuses import Points +from player import BubPlayer + +MARGIN = 22 +VMARGIN = 12 + +class RPicture: + def __init__(self): + self.icons = [] + def put(self, ico, dx=0, dy=0): + self.icons.append((dx, dy, ico)) + def getsize(self): + if self.icons: + return (max([dx+ico.w for dx, dy, ico in self.icons]) + MARGIN, + max([dy+ico.h for dx, dy, ico in self.icons])) + else: + return 0, 0 + def render(self, x, y): + return [gamesrv.Sprite(ico, x+dx, y+dy) for dx, dy, ico in self.icons] + +class RPoints: + def __init__(self, bubber, nbpoints): + self.bubber = bubber + self.nbpoints = nbpoints + def getsize(self): + return 0, 0 + def render(self, x, y): + Points(x, y, self.bubber.pn, self.nbpoints) + return [] + +class RNumber(RPicture): + map = {'%': 'percent'} + for digit in range(10): + map[str(digit)] = DigitsMisc.digits_white[digit] + + def __init__(self, text): + RPicture.__init__(self) + x = 0 + for c in text: + ico = images.sprget(self.map[c]) + self.put(ico, dx=x) + x += ico.w+1 + +class RText(RPicture): + def __init__(self, text, margin=VMARGIN): + RPicture.__init__(self) + x = 0 + for c in text: + ico = images.sprcharacterget(c) + if ico is not None: + self.put(ico, dx=x) + x += 7 + self.margin = margin + def getsize(self): + w, h = RPicture.getsize(self) + h -= (VMARGIN-self.margin) + return w, h + + +def linesize(line): + width = MARGIN + height = 0 + for item in line: + w, h = item.getsize() + width += w + if h > height: + height = h + return width, height + +def display(lines, timeleft, bgen=None, black=0): + waves = [] + if lines: + totalwidth = 0 + totalheight = 0 + for line in lines: + w, h = linesize(line) + if w > totalwidth: + totalwidth = w + totalheight += h + heightmargin = (boards.bheight-2*CELL - totalheight) // (len(lines)+1) + if heightmargin > VMARGIN: + heightmargin = VMARGIN + totalheight += heightmargin * (len(lines)+1) + + # size in number of CELLs + cwidth = (totalwidth+CELL-1) // CELL + cheight = (totalheight+CELL-1) // CELL + + x0 = ((boards.width - cwidth) // 2) * CELL + HALFCELL + y0 = ((boards.height - cheight) // 2) * CELL + HALFCELL + extras = boards.curboard.sprites.setdefault('ranking', []) + #while extras: + # extras.pop().kill() + # yield 0.12 + vspeed = -4 + while extras: + nextras = [] + for s in extras: + s.step(0, vspeed) + if s.y + s.ico.h <= 0: + s.kill() + else: + nextras.append(s) + extras[:] = nextras + yield 1 + vspeed -= 1 + + # draw the box filled with water + original_y0 = y0 + wallicon = boards.patget((boards.curboard.num, 0, 0), images.KEYCOL) + if black: + fillicon = images.sprget('gameoverbkgnd') + waveicons = [wallicon] + y0 = boards.bheight+CELL + else: + fillicon = images.sprget(Flood.fill) + waveicons = [images.sprget(n) for n in Flood.waves] + for y in range(y0-CELL, y0+cheight*CELL+CELL, CELL): + w = gamesrv.Sprite(wallicon, x0-CELL, y) + extras.append(w) + for x in range(x0, x0+cwidth*CELL, CELL): + w = gamesrv.Sprite(wallicon, x, y0+cheight*CELL) + extras.append(w) + w = gamesrv.Sprite(waveicons[-1], x, y0-CELL) + extras.append(w) + waves.append(w) + for y in range(y0, y0+cheight*CELL, CELL): + w = gamesrv.Sprite(fillicon, x, y) + extras.append(w) + for y in range(y0-CELL, y0+cheight*CELL+CELL, CELL): + w = gamesrv.Sprite(wallicon, x0+cwidth*CELL, y) + extras.append(w) + + # draw the individual items inside + y = y0 + totalheight + lines.reverse() + for line in lines: + linew, lineh = linesize(line) + x = x0 + MARGIN + y -= (lineh + heightmargin) + for item in line: + w, h = item.getsize() + extras += item.render(x, y+(lineh-h)//2) + x += w + + vspeed = 0 + while y0 > original_y0: + vspeed = max(vspeed-1, original_y0 - y0) + y0 += vspeed + for s in extras: + s.step(0, vspeed) + yield 1 + + while timeleft > 0.0: + if waves: + ico = waveicons.pop(0) + waveicons.append(ico) + for w in waves: + w.seticon(ico) + for i in range(2): + if bgen is None: + t = boards.normal_frame() + else: + try: + t = bgen.next() + except StopIteration: + timeleft = 0.0 + break + timeleft -= t + yield t + +# ____________________________________________________________ + +def ranking_picture(results, maximum, givepoints): + if maximum is None: + maximum = 0 + for n in results.values(): + maximum += n + maximum = maximum or 1 + ranking = [] + teamrank = [0, 0] + teamplayers = [[], []] + for p, n in results.items(): + if p.team != -1: + teamrank[p.team] += n + teamplayers[p.team].append((n,p)) + else: + ranking.append((n, random.random(), p)) + teamplayers[0].sort() + teamplayers[0].reverse() + teamplayers[1].sort() + teamplayers[1].reverse() + if teamplayers[0] != []: + ranking.append((teamrank[0], random.random(), teamplayers[0])) + if teamplayers[1] != []: + ranking.append((teamrank[1], random.random(), teamplayers[1])) + ranking.sort() + ranking.reverse() + + nbpoints = givepoints and ((len(ranking)+1)//2)*10000 + lines = [] + for (n, dummy, bubber), i in zip(ranking, range(len(ranking))): + pic = RPicture() + if isinstance(bubber, list): + fraction = (nbpoints//(10*len(bubber))) * 10 + total = fraction * len(bubber) + for n, bub in bubber: + bub.givepoints(fraction) + bubber = bubber[0][1] + pic.put(images.sprget(('hat', bubber.team))) + else: + if len(ranking) == 1: + icon = 0 + elif i == 0: + icon = 10 + elif i == len(ranking) - 1: + icon = 9 + else: + icon = 0 + pic.put(bubber.icons[icon, +1]) + total = 0 + line = [] + if nbpoints > 0: + line.append(RPoints(bubber, nbpoints)) + bubber.givepoints(nbpoints - total) + nbpoints -= 10000 + line.append(pic) + line.append(RNumber(str(int(n*100.00001/maximum)) + '%')) + lines.append(line) + return lines + + +def just_wait(): + while 1: + yield 2 + +def screen_scores(): + results = {} + for p in BubPlayer.PlayerList: + if p.points: + results[p] = p.points + lines = ranking_picture(results, None, 0) + lines.insert(0, [RText(" THE END")]) + return lines + +def screen_monster(): + pairs = [] + for p in BubPlayer.PlayerList: + catch = p.stats.get('monster', {}) + for p2, count in catch.items(): + if count: + pairs.append((count, p, p2)) + random.shuffle(pairs) + pairs.sort() + pairs.reverse() + del pairs[5:] + lines = [] + if pairs: + lines.append([RText('Best Monster Bubblers')]) + for count, p, p2 in pairs: + pic = RPicture() + pic.put(p.icons[4,+1], 0, 6) + pic.put(images.sprget(GreenAndBlue.new_bubbles[p.pn][1]), 31, 6) + pic.put(images.sprget(GreenAndBlue.new_bubbles[p.pn][3]), 69, 6) + pic.put(images.sprget(GreenAndBlue.normal_bubbles[p.pn][0]), 101) + pic.put(images.sprget(p2), 101) + lines.append([pic, RNumber(str(count))]) + return lines + +def screen_catch(): + pairs = [] + for p in BubPlayer.PlayerList: + catch = p.stats.get('catch', {}) + for p2, count in catch.items(): + if count: + pairs.append((count, p, p2)) + random.shuffle(pairs) + pairs.sort() + pairs.reverse() + del pairs[5:] + lines = [] + if pairs: + lines.append([RText('Best Dragon Bubblers')]) + for count, p, p2 in pairs: + pic = RPicture() + pic.put(p.icons[4,+1], 0, 6) + pic.put(images.sprget(GreenAndBlue.new_bubbles[p.pn][1]), 31, 6) + pic.put(images.sprget(GreenAndBlue.new_bubbles[p.pn][3]), 69, 6) + pic.put(images.sprget(GreenAndBlue.normal_bubbles[p2.pn][0]), 101) + pic.put(images.sprget(('eyes', 0,0)), 101) + lines.append([pic, RNumber(str(count))]) + return lines + +def screen_bonus(): + pairs = [] + for p in BubPlayer.PlayerList: + catch = p.stats.get('bonus', {}) + for p2, count in catch.items(): + if count > 1: + pairs.append((count, p, p2)) + random.shuffle(pairs) + pairs.sort() + pairs.reverse() + seen = {} + npairs = [] + for count, p, p2 in pairs: + if p2 not in seen: + npairs.append((count, p, p2)) + seen[p2] = 1 + pairs = npairs + del pairs[5:] + lines = [] + if pairs: + lines.append([RText('Best Bonus Catchers')]) + for count, p, p2 in pairs: + pic = RPicture() + pic.put(p.icons[1,+1], 0) + pic.put(images.sprget(p2), 44) + lines.append([pic, RNumber(str(count))]) + return lines + +def screen_bubble(): + pairs = [] + for p in BubPlayer.PlayerList: + count = p.stats['bubble'] + if count: + pairs.append((count, p)) + random.shuffle(pairs) + pairs.sort() + pairs.reverse() + del pairs[5:] + lines = [] + if pairs: + lines.append([RText('Best Bubble Exploders')]) + for count, p in pairs: + pic = RPicture() + pic.put(p.icons[1,+1], 0) + pic.put(images.sprget(Bubble.exploding_bubbles[1]), 27) + lines.append([pic, RNumber(str(count))]) + return lines + +def screen_die(): + pairs = [] + for p in BubPlayer.PlayerList: + count = p.stats['die'] + if count: + pairs.append((count, p)) + random.shuffle(pairs) + pairs.sort() + pairs.reverse() + del pairs[5:] + lines = [] + if pairs: + lines.append([RText('Top Deaths')]) + n = 0 + for count, p in pairs: + pic = RPicture() + pic.put(p.icons[6+(n%3),+1], 0) + lines.append([pic, RNumber(str(count))]) + n += 1 + return lines + +def screen_authors(): + return [ + [RText('programming', 6)], + [RText(' Armin & Odie')], + [RText('art', 6)], + [RText(' David Gowers, based on McSebi')], + [RText('levels', 6)], + [RText(' Gio & Odie & MS & Armin')], + [RText('special thanks', 6)], + [RText(' Odie & Brachamutanda')], + [RText('beta-testers', 6)], + [RText(' IMA Connection')], + ] + +def game_over(): + while 1: + for screen in [screen_scores, screen_monster, screen_catch, + screen_bonus, screen_bubble, screen_die, + screen_authors]: + lines = screen() + if lines: + for t in display(lines, 300, just_wait(), 1): + yield t diff --git a/bubbob/save_rnglevel.py b/bubbob/save_rnglevel.py new file mode 100644 index 0000000..8b09571 --- /dev/null +++ b/bubbob/save_rnglevel.py @@ -0,0 +1,125 @@ +# +# This script outputs the random levels in a format you can +# save into a file in the levels-directory and bub'n'bros +# will be able to use it. +# +# this accepts the following parameters: +# -seed N use random seed N for the generation +# + +import sys +import random +import string +sys.path.append('..') +sys.path.append('../common') + +n_lvls = 1 + +idx = 0 +while idx < len(sys.argv): + arg = sys.argv[idx] + idx += 1 + if arg == '-seed': + arg = sys.argv[idx] + idx += 1 + print "# Using seed: " + arg + "\n" + random.seed(arg) + +def printlvl(level): + mons = {} + monconv = {} + tmpmons = {} + + # Populate monster tables + for y in range(0,level.HEIGHT): + for x in range(0,level.WIDTH): + wm = level.wmap[y][x] + if wm >= 'a': + m = getattr(level, wm) + if m.dir == 1: + dir = 'L' + else: + dir = 'R' + s = dir + m.cls.__name__ + if tmpmons.has_key(s): + tmpmons[s].append(wm) + else: + tmpmons[s] = [wm] + # Build monster character conversion tables + lettr = 'a' + for m in tmpmons: + for n in tmpmons[m]: + monconv[n] = lettr + mons[lettr] = m + lettr = chr(ord(lettr) + 1) + + # Build walls, replacing monsters from mons[] + walls = "" + + for y in range(0,level.HEIGHT-1): + walls += "##" + for x in range(0,level.WIDTH): + wm = level.wmap[y][x] + if wm >= 'a': + if monconv.has_key(wm): + walls += monconv[wm] + else: + walls += '?' + else: + walls += wm + walls += "##\n" + walls += "##" + for x in range(0,level.WIDTH): + if level.wmap[0][x] == '#' or level.wmap[level.HEIGHT-1][x] == '#': + walls += "#" + else: + walls += " " + walls += "##\n" + + # Build winds + winds = "" + for y in range(0,level.HEIGHT): + for x in range(0,level.WIDTH+4): + winds += level.winds[y][x] + winds += "\n" + + for m in mons: + print " " + m + " = " + mons[m] + + if level.letter: + print " letter = 1" + if level.fire: + print " fire = 1" + if level.lightning: + print " lightning = 1" + if level.water: + print " water = 1" + if level.top: + print " top = 1" + + print " walls = \"\"\"\n" + walls + "\"\"\"" + print " winds = \"\"\"\n" + winds + "\"\"\"" + + +for i in range(n_lvls): + print """ +import boarddef, mnstrmap, random +from boarddef import LNasty, LMonky, LGhosty, LFlappy +from boarddef import LSpringy, LOrcy, LGramy, LBlitzy +from boarddef import RNasty, RMonky, RGhosty, RFlappy +from boarddef import RSpringy, ROrcy, RGramy, RBlitzy +""" + + d = {'__name__': 'RandomLevels'} + execfile('levels/RandomLevels.py', d) + + for i, Lvl in enumerate(d['GenerateLevels']()): + level = Lvl(i) + + if level.monsters: + print "\n\nclass level%02d(boarddef.Level):" % (i+1) + else: + print "\n\nclass levelFinal(boarddef.Level):" + + printlvl(level) + print diff --git a/bubbob/setup.py b/bubbob/setup.py new file mode 100755 index 0000000..577d3c5 --- /dev/null +++ b/bubbob/setup.py @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +from distutils.core import setup +from distutils.extension import Extension + +##setup ( name="gencopy", +## version="0.1", +## description="generator and iterator state", +## author="Armin", +## author_email="arigo@tunes.org", +## ext_modules=[Extension(name = 'gencopy', +## sources = ['gencopy.c'])] +## ) + +setup ( name="statesaver", + version="0.1", + description="object duplicator working on generators and iterators", + author="Armin", + author_email="arigo@tunes.org", + ext_modules=[Extension(name = 'statesaver', + sources = ['statesaver.c'])] + ) diff --git a/bubbob/sounds/die.wav b/bubbob/sounds/die.wav Binary files differnew file mode 100644 index 0000000..bea33d9 --- /dev/null +++ b/bubbob/sounds/die.wav diff --git a/bubbob/sounds/extra.wav b/bubbob/sounds/extra.wav Binary files differnew file mode 100644 index 0000000..a95c01b --- /dev/null +++ b/bubbob/sounds/extra.wav diff --git a/bubbob/sounds/extralife.wav b/bubbob/sounds/extralife.wav Binary files differnew file mode 100644 index 0000000..9afd8b2 --- /dev/null +++ b/bubbob/sounds/extralife.wav diff --git a/bubbob/sounds/fruit.wav b/bubbob/sounds/fruit.wav Binary files differnew file mode 100644 index 0000000..19eafd3 --- /dev/null +++ b/bubbob/sounds/fruit.wav diff --git a/bubbob/sounds/hell.wav b/bubbob/sounds/hell.wav Binary files differnew file mode 100644 index 0000000..aad531b --- /dev/null +++ b/bubbob/sounds/hell.wav diff --git a/bubbob/sounds/hurry.wav b/bubbob/sounds/hurry.wav Binary files differnew file mode 100644 index 0000000..c566e54 --- /dev/null +++ b/bubbob/sounds/hurry.wav diff --git a/bubbob/sounds/jump.wav b/bubbob/sounds/jump.wav Binary files differnew file mode 100644 index 0000000..8d7c10d --- /dev/null +++ b/bubbob/sounds/jump.wav diff --git a/bubbob/sounds/letsgo.wav b/bubbob/sounds/letsgo.wav Binary files differnew file mode 100644 index 0000000..3216cdf --- /dev/null +++ b/bubbob/sounds/letsgo.wav diff --git a/bubbob/sounds/pop.wav b/bubbob/sounds/pop.wav Binary files differnew file mode 100644 index 0000000..efddf3d --- /dev/null +++ b/bubbob/sounds/pop.wav diff --git a/bubbob/sounds/shh.wav b/bubbob/sounds/shh.wav Binary files differnew file mode 100644 index 0000000..8ea1ca9 --- /dev/null +++ b/bubbob/sounds/shh.wav diff --git a/bubbob/sounds/yippee.wav b/bubbob/sounds/yippee.wav Binary files differnew file mode 100644 index 0000000..1598140 --- /dev/null +++ b/bubbob/sounds/yippee.wav diff --git a/bubbob/sprmap.py b/bubbob/sprmap.py new file mode 100644 index 0000000..ce34264 --- /dev/null +++ b/bubbob/sprmap.py @@ -0,0 +1,533 @@ +sprmap = { + 10:('ice_cyan_big.ppm', (0, 0, 90, 90)), + 11:('ice_violet_big.ppm', (0, 0, 90, 90)), + 12:('peach_big.ppm', (0, 0, 90, 90)), + 13:('pastec_big.ppm', (0, 0, 90, 90)), + 14:('cream_pie_big.ppm', (0, 0, 90, 90)), + 15:('sugar_pie_big.ppm', (0, 0, 90, 90)), + 16:('diamond_big_purple.ppm', (0, 0, 90, 90)), + 17:('diamond_big_blue.ppm', (0, 0, 90, 90)), + 18:('diamond_big_red.ppm', (0, 0, 90, 90)), + 19:('diamond_big_yellow.ppm', (0, 0, 90, 90)), + 30:('lightning_large.ppm', (0, 0, 90, 66)), + 31:('yellow_Hurry_up.ppm', (0, 0, 136, 24)), + 32:('red_Hurry_up.ppm', (0, 0, 136, 24)), + 128:('extend.ppm', (0, 0, 32, 32)), + 129:('extend.ppm', (0, 32, 32, 32)), + 130:('extend.ppm', (0, 64, 32, 32)), + 131:('bubble.ppm', (0, 0, 32, 32)), + 132:('bubble.ppm', (0, 32, 32, 32)), + 133:('bubble.ppm', (0, 64, 32, 32)), + 134:('bubble.ppm', (0, 96, 32, 32)), + 135:('bubble.ppm', (0, 128, 32, 32)), + 136:('extend.ppm', (0, 96, 32, 32)), + 137:('extend.ppm', (0, 128, 32, 32)), + 138:('extend.ppm', (0, 160, 32, 32)), + 139:('door.ppm', (0, 0, 32, 32)), + 140:('water_surface.ppm', (0, 0, 16, 16)), + 141:('water_surface.ppm', (0, 16, 16, 16)), + 142:('water_surface.ppm', (0, 32, 16, 16)), + 143:('water_surface.ppm', (0, 48, 16, 16)), + 144:('extend.ppm', (0, 192, 32, 32)), + 145:('extend.ppm', (0, 224, 32, 32)), + 146:('extend.ppm', (0, 256, 32, 32)), + 152:('extend.ppm', (0, 288, 32, 32)), + 153:('extend.ppm', (0, 320, 32, 32)), + 154:('extend.ppm', (0, 352, 32, 32)), + 155:('bubble.ppm', (0, 352, 32, 32)), + 156:('bubble.ppm', (0, 384, 32, 32)), + 157:('bubble.ppm', (0, 416, 32, 32)), + 160:('extend.ppm', (0, 384, 32, 32)), + 161:('extend.ppm', (0, 416, 32, 32)), + 162:('extend.ppm', (0, 448, 32, 32)), + 163:('bubble.ppm', (0, 160, 32, 32)), + 164:('bubble.ppm', (0, 192, 32, 32)), + 165:('bubble.ppm', (0, 224, 32, 32)), + 168:('extend.ppm', (0, 480, 32, 32)), + 169:('extend.ppm', (0, 512, 32, 32)), + 170:('extend.ppm', (0, 544, 32, 32)), + 171:('bubble.ppm', (0, 256, 32, 32)), + 172:('bubble.ppm', (0, 288, 32, 32)), + 173:('bubble.ppm', (0, 320, 32, 32)), + 239:('nasty.ppm', (0, 0, 32, 32)), + 240:('nasty.ppm', (0, 32, 32, 32)), + 241:('nasty.ppm', (0, 64, 32, 32)), + 242:('nasty.ppm', (0, 96, 32, 32)), + 243:('nasty.ppm', (0, 128, 32, 32)), + 244:('nasty.ppm', (0, 160, 32, 32)), + 245:('nasty.ppm', (0, 192, 32, 32)), + 246:('nasty.ppm', (0, 224, 32, 32)), + 247:('nasty.ppm', (0, 256, 32, 32)), + 248:('nasty.ppm', (0, 288, 32, 32)), + 249:('nasty.ppm', (0, 320, 32, 32)), + 253:('nasty.ppm', (0, 352, 32, 32)), + 254:('nasty.ppm', (0, 384, 32, 32)), + 255:('nasty.ppm', (0, 416, 32, 32)), + 256:('nasty.ppm', (0, 448, 32, 32)), + 265:('monky.ppm', (0, 0, 32, 32)), + 266:('monky.ppm', (0, 32, 32, 32)), + 267:('monky.ppm', (0, 64, 32, 32)), + 268:('monky.ppm', (0, 96, 32, 32)), + 269:('monky.ppm', (0, 128, 32, 32)), + 270:('monky.ppm', (0, 160, 32, 32)), + 271:('monky.ppm', (0, 192, 32, 32)), + 272:('monky.ppm', (0, 224, 32, 32)), + 273:('monky.ppm', (0, 256, 32, 32)), + 274:('monky.ppm', (0, 288, 32, 32)), + 275:('monky.ppm', (0, 320, 32, 32)), + 279:('monky.ppm', (0, 352, 32, 32)), + 280:('monky.ppm', (0, 384, 32, 32)), + 281:('monky.ppm', (0, 416, 32, 32)), + 282:('monky.ppm', (0, 448, 32, 32)), + 291:('ghosty.ppm', (0, 0, 32, 32)), + 292:('ghosty.ppm', (0, 32, 32, 32)), + 293:('ghosty.ppm', (0, 64, 32, 32)), + 294:('ghosty.ppm', (0, 96, 32, 32)), + 295:('ghosty.ppm', (0, 128, 32, 32)), + 296:('ghosty.ppm', (0, 160, 32, 32)), + 297:('ghosty.ppm', (0, 192, 32, 32)), + 298:('ghosty.ppm', (0, 224, 32, 32)), + 299:('ghosty.ppm', (0, 256, 32, 32)), + 300:('ghosty.ppm', (0, 288, 32, 32)), + 301:('ghosty.ppm', (0, 320, 32, 32)), + 305:('ghosty.ppm', (0, 352, 32, 32)), + 306:('ghosty.ppm', (0, 384, 32, 32)), + 307:('ghosty.ppm', (0, 416, 32, 32)), + 308:('ghosty.ppm', (0, 448, 32, 32)), + 317:('flappy.ppm', (0, 0, 32, 32)), + 318:('flappy.ppm', (0, 32, 32, 32)), + 319:('flappy.ppm', (0, 64, 32, 32)), + 320:('flappy.ppm', (0, 96, 32, 32)), + 321:('flappy.ppm', (0, 128, 32, 32)), + 322:('flappy.ppm', (0, 160, 32, 32)), + 323:('flappy.ppm', (0, 192, 32, 32)), + 324:('flappy.ppm', (0, 224, 32, 32)), + 325:('flappy.ppm', (0, 256, 32, 32)), + 326:('flappy.ppm', (0, 288, 32, 32)), + 327:('flappy.ppm', (0, 320, 32, 32)), + 331:('flappy.ppm', (0, 352, 32, 32)), + 332:('flappy.ppm', (0, 384, 32, 32)), + 333:('flappy.ppm', (0, 416, 32, 32)), + 334:('flappy.ppm', (0, 448, 32, 32)), + 343:('springy.ppm', (0, 0, 32, 32)), + 344:('springy.ppm', (0, 32, 32, 32)), + 345:('springy.ppm', (0, 64, 32, 32)), + 346:('springy.ppm', (0, 96, 32, 32)), + 347:('springy.ppm', (0, 128, 32, 32)), + 348:('springy.ppm', (0, 160, 32, 32)), + 349:('springy.ppm', (0, 192, 32, 32)), + 350:('springy.ppm', (0, 224, 32, 32)), + 351:('springy.ppm', (0, 256, 32, 32)), + 352:('springy.ppm', (0, 288, 32, 32)), + 353:('springy.ppm', (0, 320, 32, 32)), + 357:('springy.ppm', (0, 352, 32, 32)), + 358:('springy.ppm', (0, 384, 32, 32)), + 359:('springy.ppm', (0, 416, 32, 32)), + 360:('springy.ppm', (0, 448, 32, 32)), + 369:('springy.ppm', (0, 480, 32, 32)), + 370:('springy.ppm', (0, 512, 32, 32)), + 371:('springy.ppm', (0, 544, 32, 32)), + 372:('springy.ppm', (0, 576, 32, 32)), + 373:('orcy.ppm', (0, 0, 32, 32)), + 374:('orcy.ppm', (0, 32, 32, 32)), + 375:('orcy.ppm', (0, 64, 32, 32)), + 376:('orcy.ppm', (0, 96, 32, 32)), + 377:('orcy.ppm', (0, 128, 32, 32)), + 378:('orcy.ppm', (0, 160, 32, 32)), + 379:('orcy.ppm', (0, 192, 32, 32)), + 380:('orcy.ppm', (0, 224, 32, 32)), + 381:('orcy.ppm', (0, 256, 32, 32)), + 382:('orcy.ppm', (0, 288, 32, 32)), + 383:('orcy.ppm', (0, 320, 32, 32)), + 387:('orcy.ppm', (0, 352, 32, 32)), + 388:('orcy.ppm', (0, 384, 32, 32)), + 389:('orcy.ppm', (0, 416, 32, 32)), + 390:('orcy.ppm', (0, 448, 32, 32)), + 399:('gramy.ppm', (0, 0, 32, 32)), + 400:('gramy.ppm', (0, 32, 32, 32)), + 401:('gramy.ppm', (0, 64, 32, 32)), + 402:('gramy.ppm', (0, 96, 32, 32)), + 403:('gramy.ppm', (0, 128, 32, 32)), + 404:('gramy.ppm', (0, 160, 32, 32)), + 405:('gramy.ppm', (0, 192, 32, 32)), + 406:('gramy.ppm', (0, 224, 32, 32)), + 407:('gramy.ppm', (0, 256, 32, 32)), + 408:('gramy.ppm', (0, 288, 32, 32)), + 409:('gramy.ppm', (0, 320, 32, 32)), + 413:('gramy.ppm', (0, 352, 32, 32)), + 414:('gramy.ppm', (0, 384, 32, 32)), + 415:('gramy.ppm', (0, 416, 32, 32)), + 416:('gramy.ppm', (0, 448, 32, 32)), + 425:('blitzy.ppm', (0, 0, 32, 32)), + 426:('blitzy.ppm', (0, 32, 32, 32)), + 427:('blitzy.ppm', (0, 64, 32, 32)), + 428:('blitzy.ppm', (0, 96, 32, 32)), + 429:('blitzy.ppm', (0, 128, 32, 32)), + 430:('blitzy.ppm', (0, 160, 32, 32)), + 431:('blitzy.ppm', (0, 192, 32, 32)), + 435:('blitzy.ppm', (0, 224, 32, 32)), + 436:('blitzy.ppm', (0, 256, 32, 32)), + 437:('blitzy.ppm', (0, 288, 32, 32)), + 438:('blitzy.ppm', (0, 320, 32, 32)), + 443:('ghost.ppm', (0, 0, 32, 32)), + 444:('ghost.ppm', (0, 32, 32, 32)), + 445:('ghost.ppm', (0, 64, 32, 32)), + 446:('ghost.ppm', (0, 96, 32, 32)), + 447:('ghost.ppm', (0, 128, 32, 32)), + 448:('ghost.ppm', (0, 160, 32, 32)), + 449:('ghost.ppm', (0, 192, 32, 32)), + 450:('ghost.ppm', (0, 224, 32, 32)), + 451:('monky.ppm', (0, 480, 32, 32)), + 452:('monky.ppm', (0, 512, 32, 32)), + 453:('monky.ppm', (0, 544, 32, 32)), + 454:('monky.ppm', (0, 576, 32, 32)), + 455:('monky.ppm', (0, 608, 32, 32)), + 456:('orcy.ppm', (0, 480, 32, 32)), + 457:('orcy.ppm', (0, 512, 32, 32)), + 458:('orcy.ppm', (0, 544, 32, 32)), + 459:('orcy.ppm', (0, 576, 32, 32)), + 460:('orcy.ppm', (0, 608, 32, 32)), + 461:('orcy.ppm', (0, 640, 32, 32)), + 462:('orcy.ppm', (0, 672, 32, 32)), + 463:('orcy.ppm', (0, 704, 32, 32)), + 464:('shot.ppm', (0, 0, 32, 32)), + 465:('shot.ppm', (0, 32, 32, 32)), + 466:('shot.ppm', (0, 64, 32, 32)), + 467:('shot.ppm', (0, 96, 32, 32)), + 468:('shot.ppm', (0, 128, 32, 32)), + 469:('shot.ppm', (0, 160, 32, 32)), + 470:('shot.ppm', (0, 192, 32, 32)), + 471:('shot.ppm', (0, 224, 32, 32)), + 472:('gramy.ppm', (0, 480, 32, 32)), + 473:('gramy.ppm', (0, 512, 32, 32)), + 474:('gramy.ppm', (0, 544, 32, 32)), + 475:('gramy.ppm', (0, 576, 32, 32)), + 476:('blitzy_shot.ppm', (0, 0, 16, 32)), + 477:('bonus_0.ppm', (0, 0, 32, 32)), + 478:('bonus_0.ppm', (0, 32, 32, 32)), + 479:('bonus_0.ppm', (0, 64, 32, 32)), + 480:('bonus_0.ppm', (0, 96, 32, 32)), + 481:('bonus_0.ppm', (0, 128, 32, 32)), + 482:('spinning_drop.ppm', (0, 0, 16, 16)), + 483:('spinning_drop.ppm', (0, 16, 16, 16)), + 484:('spinning_drop.ppm', (0, 32, 16, 16)), + 485:('spinning_drop.ppm', (0, 48, 16, 16)), + 486:('spinning_drop.ppm', (0, 64, 16, 16)), + 487:('spinning_drop.ppm', (0, 80, 16, 16)), + 488:('lightning_small.ppm', (0, 0, 24, 24)), + 489:('fire_drop.ppm', (0, 0, 9, 16)), + 490:('fire_surface.ppm', (0, 0, 16, 16)), + 491:('fire_surface.ppm', (0, 16, 16, 16)), + 492:('fire_surface.ppm', (0, 32, 16, 16)), + 493:('fire_surface.ppm', (0, 48, 16, 16)), + 495:('water_still.ppm', (0, 0, 16, 16)), + 496:('bonus_0.ppm', (0, 160, 32, 20)), + 519:('level_digits.ppm', (0, 0, 14, 20)), + 520:('level_digits.ppm', (0, 20, 14, 20)), + 521:('level_digits.ppm', (0, 40, 14, 20)), + 522:('level_digits.ppm', (0, 60, 14, 20)), + 523:('level_digits.ppm', (0, 80, 14, 20)), + 524:('level_digits.ppm', (0, 100, 14, 20)), + 525:('level_digits.ppm', (0, 120, 14, 20)), + 526:('level_digits.ppm', (0, 140, 14, 20)), + 527:('level_digits.ppm', (0, 160, 14, 20)), + 528:('level_digits.ppm', (0, 180, 14, 20)), + 593:('bonus_1.ppm', (0, 0, 32, 32)), + 594:('bonus_1.ppm', (0, 32, 32, 32)), + 595:('bonus_1.ppm', (0, 64, 32, 32)), + 596:('bonus_1.ppm', (0, 96, 32, 32)), + 597:('bonus_1.ppm', (0, 128, 32, 32)), + 598:('bonus_1.ppm', (0, 160, 32, 32)), + 599:('bonus_1.ppm', (0, 192, 32, 32)), + 600:('bonus_1.ppm', (0, 224, 32, 32)), + 601:('bonus_2.ppm', (0, 0, 32, 32)), + 602:('bonus_2.ppm', (0, 32, 32, 32)), + 603:('bonus_2.ppm', (0, 64, 32, 32)), + 604:('bonus_2.ppm', (0, 96, 32, 32)), + 605:('bonus_2.ppm', (0, 128, 32, 32)), + 606:('bonus_2.ppm', (0, 160, 32, 32)), + 607:('bonus_2.ppm', (0, 192, 32, 32)), + 608:('bonus_2.ppm', (0, 224, 32, 32)), + 609:('bonus_3.ppm', (0, 0, 32, 32)), + 610:('bonus_3.ppm', (0, 32, 32, 32)), + 611:('bonus_3.ppm', (0, 64, 32, 32)), + 612:('bonus_3.ppm', (0, 96, 32, 32)), + 613:('bonus_3.ppm', (0, 128, 32, 32)), + 614:('bonus_3.ppm', (0, 160, 32, 32)), + 615:('bonus_3.ppm', (0, 192, 32, 32)), + 616:('bonus_3.ppm', (0, 224, 32, 32)), + 617:('bonus_4.ppm', (0, 0, 32, 32)), + 618:('bonus_4.ppm', (0, 32, 32, 32)), + 619:('bonus_4.ppm', (0, 64, 32, 32)), + 620:('bonus_4.ppm', (0, 96, 32, 32)), + 621:('bonus_4.ppm', (0, 128, 32, 32)), + 622:('bonus_4.ppm', (0, 160, 32, 32)), + 623:('bonus_4.ppm', (0, 192, 32, 32)), + 624:('bonus_4.ppm', (0, 224, 32, 32)), + 625:('bonus_5.ppm', (0, 0, 32, 32)), + 626:('bonus_5.ppm', (0, 32, 32, 32)), + 627:('bonus_5.ppm', (0, 64, 32, 32)), + 628:('bonus_5.ppm', (0, 96, 32, 32)), + 629:('bonus_5.ppm', (0, 128, 32, 32)), + 630:('bonus_5.ppm', (0, 160, 32, 32)), + 631:('bonus_5.ppm', (0, 192, 32, 32)), + 632:('bonus_5.ppm', (0, 224, 32, 32)), + 633:('bonus_6.ppm', (0, 0, 32, 32)), + 634:('bonus_6.ppm', (0, 32, 32, 32)), + 635:('bonus_6.ppm', (0, 64, 32, 32)), + 636:('bonus_6.ppm', (0, 96, 32, 32)), + 637:('bonus_6.ppm', (0, 128, 32, 32)), + 638:('bonus_6.ppm', (0, 160, 32, 32)), + 639:('bonus_6.ppm', (0, 192, 32, 32)), + 640:('bonus_6.ppm', (0, 224, 32, 32)), + 641:('bonus_7.ppm', (0, 0, 32, 32)), + 642:('bonus_7.ppm', (0, 32, 32, 32)), + 643:('bonus_7.ppm', (0, 64, 32, 32)), + 644:('bonus_7.ppm', (0, 96, 32, 32)), + 645:('bonus_7.ppm', (0, 128, 32, 32)), + 646:('bonus_7.ppm', (0, 160, 32, 32)), + 647:('bonus_7.ppm', (0, 192, 32, 32)), + 648:('bonus_7.ppm', (0, 224, 32, 32)), + 649:('bonus_8.ppm', (0, 0, 32, 32)), + 650:('bonus_8.ppm', (0, 32, 32, 32)), + 651:('bonus_8.ppm', (0, 64, 32, 32)), + 652:('bonus_8.ppm', (0, 96, 32, 32)), + 653:('bonus_8.ppm', (0, 128, 32, 32)), + 654:('bonus_8.ppm', (0, 160, 32, 32)), + 655:('bonus_8.ppm', (0, 192, 32, 32)), + 656:('bonus_8.ppm', (0, 224, 32, 32)), + 657:('bonus_9.ppm', (0, 0, 32, 32)), + 658:('bonus_9.ppm', (0, 32, 32, 32)), + 659:('bonus_9.ppm', (0, 64, 32, 32)), + 660:('bonus_9.ppm', (0, 96, 32, 32)), + 661:('bonus_9.ppm', (0, 128, 32, 32)), + 662:('bonus_9.ppm', (0, 160, 32, 32)), + 663:('bonus_9.ppm', (0, 192, 32, 32)), + 664:('bonus_9.ppm', (0, 224, 32, 32)), + 665:('bonus_10.ppm', (0, 0, 32, 32)), + 666:('bonus_10.ppm', (0, 32, 32, 32)), + 667:('bonus_10.ppm', (0, 64, 32, 32)), + 668:('bonus_10.ppm', (0, 96, 32, 32)), + 669:('bonus_10.ppm', (0, 128, 32, 32)), + 670:('bonus_10.ppm', (0, 160, 32, 32)), + 671:('bonus_10.ppm', (0, 192, 32, 32)), + 672:('bonus_10.ppm', (0, 224, 32, 32)), + 673:('bonus_11.ppm', (0, 0, 32, 32)), + 674:('bonus_11.ppm', (0, 32, 32, 32)), + 675:('bonus_11.ppm', (0, 64, 32, 32)), + 676:('bonus_11.ppm', (0, 96, 32, 32)), + 677:('bonus_11.ppm', (0, 128, 32, 32)), + 678:('bonus_11.ppm', (0, 160, 32, 32)), + 679:('bonus_11.ppm', (0, 192, 32, 32)), + 680:('bonus_11.ppm', (0, 224, 32, 32)), + 681:('bonus_12.ppm', (0, 0, 32, 32)), + 682:('bonus_12.ppm', (0, 32, 32, 32)), + 691:('bonus_12.ppm', (0, 64, 32, 32)), + 692:('bonus_12.ppm', (0, 96, 32, 32)), + 800:('nasty_angry.ppm', (0, 0, 32, 32)), + 801:('nasty_angry.ppm', (0, 32, 32, 32)), + 802:('nasty_angry.ppm', (0, 64, 32, 32)), + 803:('nasty_angry.ppm', (0, 96, 32, 32)), + 804:('nasty_angry.ppm', (0, 128, 32, 32)), + 805:('nasty_angry.ppm', (0, 160, 32, 32)), + 806:('nasty_angry.ppm', (0, 192, 32, 32)), + 807:('nasty_angry.ppm', (0, 224, 32, 32)), + 808:('monky_angry.ppm', (0, 0, 32, 32)), + 809:('monky_angry.ppm', (0, 32, 32, 32)), + 810:('monky_angry.ppm', (0, 64, 32, 32)), + 811:('monky_angry.ppm', (0, 96, 32, 32)), + 812:('monky_angry.ppm', (0, 128, 32, 32)), + 813:('monky_angry.ppm', (0, 160, 32, 32)), + 814:('monky_angry.ppm', (0, 192, 32, 32)), + 815:('monky_angry.ppm', (0, 224, 32, 32)), + 816:('ghosty_angry.ppm', (0, 0, 32, 32)), + 817:('ghosty_angry.ppm', (0, 32, 32, 32)), + 818:('ghosty_angry.ppm', (0, 64, 32, 32)), + 819:('ghosty_angry.ppm', (0, 96, 32, 32)), + 820:('ghosty_angry.ppm', (0, 128, 32, 32)), + 821:('ghosty_angry.ppm', (0, 160, 32, 32)), + 822:('ghosty_angry.ppm', (0, 192, 32, 32)), + 823:('ghosty_angry.ppm', (0, 224, 32, 32)), + 824:('flapy_angry.ppm', (0, 0, 32, 32)), + 825:('flapy_angry.ppm', (0, 32, 32, 32)), + 826:('flapy_angry.ppm', (0, 64, 32, 32)), + 827:('flapy_angry.ppm', (0, 96, 32, 32)), + 828:('flapy_angry.ppm', (0, 128, 32, 32)), + 829:('flapy_angry.ppm', (0, 160, 32, 32)), + 830:('flapy_angry.ppm', (0, 192, 32, 32)), + 831:('flapy_angry.ppm', (0, 224, 32, 32)), + 832:('springy_angry.ppm', (0, 0, 32, 32)), + 833:('springy_angry.ppm', (0, 32, 32, 32)), + 834:('springy_angry.ppm', (0, 64, 32, 32)), + 835:('springy_angry.ppm', (0, 96, 32, 32)), + 836:('springy_angry.ppm', (0, 128, 32, 32)), + 837:('springy_angry.ppm', (0, 160, 32, 32)), + 838:('springy_angry.ppm', (0, 192, 32, 32)), + 839:('springy_angry.ppm', (0, 224, 32, 32)), + 840:('springy_angry.ppm', (0, 256, 32, 32)), + 841:('springy_angry.ppm', (0, 288, 32, 32)), + 842:('springy_angry.ppm', (0, 320, 32, 32)), + 843:('springy_angry.ppm', (0, 352, 32, 32)), + 844:('orcy_angry.ppm', (0, 0, 32, 32)), + 845:('orcy_angry.ppm', (0, 32, 32, 32)), + 846:('orcy_angry.ppm', (0, 64, 32, 32)), + 847:('orcy_angry.ppm', (0, 96, 32, 32)), + 848:('orcy_angry.ppm', (0, 128, 32, 32)), + 849:('orcy_angry.ppm', (0, 160, 32, 32)), + 850:('orcy_angry.ppm', (0, 192, 32, 32)), + 851:('orcy_angry.ppm', (0, 224, 32, 32)), + 852:('gramy_angry.ppm', (0, 0, 32, 32)), + 853:('gramy_angry.ppm', (0, 32, 32, 32)), + 854:('gramy_angry.ppm', (0, 64, 32, 32)), + 855:('gramy_angry.ppm', (0, 96, 32, 32)), + 856:('gramy_angry.ppm', (0, 128, 32, 32)), + 857:('gramy_angry.ppm', (0, 160, 32, 32)), + 858:('gramy_angry.ppm', (0, 192, 32, 32)), + 859:('gramy_angry.ppm', (0, 224, 32, 32)), + 860:('blitzy_angry.ppm', (0, 0, 32, 32)), + 861:('blitzy_angry.ppm', (0, 32, 32, 32)), + 862:('blitzy_angry.ppm', (0, 64, 32, 32)), + 863:('blitzy_angry.ppm', (0, 96, 32, 32)), + 900:('water_flow.ppm', (0, 0, 16, 16)), + 901:('water_flow.ppm', (0, 16, 16, 16)), + 902:('water_flow.ppm', (0, 32, 16, 16)), + 903:('water_flow.ppm', (0, 48, 16, 16)), + 904:('water_flow.ppm', (0, 64, 16, 16)), + 905:('water_flow.ppm', (0, 80, 16, 16)), + 906:('water_flow.ppm', (0, 96, 16, 16)), + 907:('water_flow.ppm', (0, 112, 16, 16)), + 908:('water_flow.ppm', (0, 128, 16, 16)), + 909:('water_flow.ppm', (0, 144, 16, 16)), + 910:('big_bubble.ppm', (0, 0, 64, 64)), + 911:('big_bubble.ppm', (0, 64, 64, 64)), + 912:('big_bubble.ppm', (0, 128, 64, 64)), + 913:('big_bubble.ppm', (0, 192, 64, 64)), + 914:('big_bubble.ppm', (0, 256, 64, 64)), + 915:('big_bubble.ppm', (0, 320, 64, 64)), + 920:('level_digits.ppm', (0, 200, 14, 20)), + 921:('level_digits.ppm', (0, 220, 14, 20)), + 922:('level_digits.ppm', (0, 240, 14, 20)), + 923:('level_digits.ppm', (0, 260, 14, 20)), + 924:('level_digits.ppm', (0, 280, 14, 20)), + 925:('level_digits.ppm', (0, 300, 14, 20)), + 926:('level_digits.ppm', (0, 320, 14, 20)), + 927:('level_digits.ppm', (0, 340, 14, 20)), + 928:('level_digits.ppm', (0, 360, 14, 20)), + 929:('level_digits.ppm', (0, 380, 14, 20)), + 930:('level_digits.ppm', (0, 400, 14, 20)), + 931:('level_digits.ppm', (0, 420, 14, 20)), + 932:('level_digits.ppm', (0, 440, 14, 20)), + 933:('level_digits.ppm', (0, 460, 14, 20)), + 934:('level_digits.ppm', (0, 480, 14, 20)), + 935:('level_digits.ppm', (0, 500, 14, 20)), + 936:('level_digits.ppm', (0, 520, 14, 20)), + 937:('level_digits.ppm', (0, 540, 14, 20)), + 938:('level_digits.ppm', (0, 560, 14, 20)), + 939:('level_digits.ppm', (0, 580, 14, 20)), + 940:('star_large.ppm', (0, 0, 32, 32)), + 941:('star_large.ppm', (0, 32, 32, 32)), + 942:('star_large.ppm', (0, 64, 32, 32)), + 943:('star_large.ppm', (0, 96, 32, 32)), + 944:('star_large.ppm', (0, 128, 32, 32)), + 945:('star_large.ppm', (0, 160, 32, 32)), + 946:('star_large.ppm', (0, 192, 32, 32)), + 947:('star_large.ppm', (0, 224, 32, 32)), + 948:('star_large.ppm', (0, 256, 32, 32)), + 949:('star_large.ppm', (0, 288, 32, 32)), + 950:('star_large.ppm', (0, 320, 32, 32)), + 951:('star_large.ppm', (0, 352, 32, 32)), + 952:('big_bubble_2.ppm', (0, 0, 64, 64)), + 953:('big_bubble_2.ppm', (0, 64, 64, 64)), + 954:('big_bubble_2.ppm', (0, 128, 64, 64)), + 955:('big_bubble_2.ppm', (0, 192, 64, 64)), + 956:('big_bubble_2.ppm', (0, 256, 64, 64)), + + 20:('10000_%d.ppm', (0, 0, 90, 40)), + 21:('20000_%d.ppm', (0, 0, 98, 45)), + 22:('30000_%d.ppm', (0, 0, 98, 45)), + 23:('40000_%d.ppm', (0, 0, 98, 45)), + 24:('50000_%d.ppm', (0, 0, 95, 45)), + 25:('60000_%d.ppm', (0, 0, 96, 45)), + 26:('70000_%d.ppm', (0, 0, 96, 45)), + 176:('dragon_bubble_%d.ppm', (0, 0, 32, 32)), + 177:('dragon_bubble_%d.ppm', (0, 32, 32, 32)), + 178:('dragon_bubble_%d.ppm', (0, 64, 32, 32)), + 179:('dragon_bubble_%d.ppm', (0, 96, 32, 32)), + 180:('dragon_bubble_%d.ppm', (0, 128, 32, 32)), + 181:('dragon_bubble_%d.ppm', (0, 160, 32, 32)), + 182:('dragon_bubble_%d.ppm', (0, 192, 32, 32)), + 183:('dragon_bubble_%d.ppm', (0, 224, 32, 32)), + 184:('dragon_bubble_%d.ppm', (0, 256, 32, 32)), + 188:('dragon_bubble_%d.ppm', (0, 288, 32, 32)), + 189:('dragon_bubble_%d.ppm', (0, 320, 32, 32)), + 190:('dragon_bubble_%d.ppm', (0, 352, 32, 32)), + 191:('dragon_bubble_%d.ppm', (0, 384, 32, 32)), + 192:('dragon_bubble_%d.ppm', (0, 416, 32, 32)), + 193:('dragon_bubble_%d.ppm', (0, 448, 32, 32)), + 194:('dragon_bubble_%d.ppm', (0, 480, 32, 32)), + 210:('dragon_%d.ppm', (0, 0, 32, 32)), + 211:('dragon_%d.ppm', (0, 32, 32, 32)), + 212:('dragon_%d.ppm', (0, 64, 32, 32)), + 213:('dragon_%d.ppm', (0, 96, 32, 32)), + 214:('dragon_%d.ppm', (0, 128, 32, 32)), + 215:('dragon_%d.ppm', (0, 160, 32, 32)), + 216:('dragon_%d.ppm', (0, 192, 32, 32)), + 217:('dragon_%d.ppm', (0, 224, 32, 32)), + 218:('dragon_%d.ppm', (0, 256, 32, 32)), + 219:('dragon_%d.ppm', (0, 288, 32, 32)), + 220:('dragon_%d.ppm', (0, 320, 32, 32)), + 221:('dragon_%d.ppm', (0, 352, 32, 32)), + 222:('dragon_%d.ppm', (0, 384, 32, 32)), + 497:('game_over_%d.ppm', (0, 0, 64, 32)), + 499:('digits_%d.ppm', (0, 0, 14, 17)), + 500:('digits_%d.ppm', (0, 17, 14, 17)), + 501:('digits_%d.ppm', (0, 34, 14, 17)), + 502:('digits_%d.ppm', (0, 51, 14, 17)), + 503:('digits_%d.ppm', (0, 68, 14, 17)), + 504:('digits_%d.ppm', (0, 85, 14, 17)), + 505:('digits_%d.ppm', (0, 102, 14, 17)), + 506:('digits_%d.ppm', (0, 119, 14, 17)), + 507:('digits_%d.ppm', (0, 136, 14, 17)), + 508:('digits_%d.ppm', (0, 153, 14, 17)), + 529:('point_%d.ppm', (0, 0, 48, 24)), + 530:('point_%d.ppm', (0, 24, 48, 24)), + 531:('point_%d.ppm', (0, 48, 48, 24)), + 532:('point_%d.ppm', (0, 72, 48, 24)), + 533:('point_%d.ppm', (0, 96, 48, 24)), + 534:('point_%d.ppm', (0, 120, 48, 24)), + 535:('point_%d.ppm', (0, 144, 48, 24)), + 536:('point_%d.ppm', (0, 168, 48, 24)), + 537:('point_%d.ppm', (0, 192, 48, 24)), + 538:('point_%d.ppm', (0, 216, 48, 24)), + 539:('point_%d.ppm', (0, 240, 48, 24)), + 540:('point_%d.ppm', (0, 264, 48, 24)), + 541:('point_%d.ppm', (0, 288, 48, 24)), + 542:('point_%d.ppm', (0, 312, 48, 24)), + 543:('point_%d.ppm', (0, 336, 48, 24)), + 544:('point_%d.ppm', (0, 360, 48, 24)), + 545:('point_%d.ppm', (0, 384, 48, 24)), + 546:('point_%d.ppm', (0, 408, 48, 24)), + 547:('point_%d.ppm', (0, 432, 48, 24)), + 548:('point_%d.ppm', (0, 456, 48, 24)), + 549:('point_%d.ppm', (0, 480, 48, 24)), + 550:('point_%d.ppm', (0, 504, 48, 24)), + 551:('point_%d.ppm', (0, 528, 48, 24)), + 552:('point_%d.ppm', (0, 552, 48, 24)), + 553:('point_%d.ppm', (0, 576, 48, 24)), + 683:('dragon_%d.ppm', (0, 416, 32, 32)), + 684:('dragon_%d.ppm', (0, 448, 32, 32)), + 685:('dragon_%d.ppm', (0, 480, 32, 32)), + 686:('dragon_%d.ppm', (0, 512, 32, 32)), + 693:('dragon_%d.ppm', (0, 544, 32, 32)), + 694:('dragon_%d.ppm', (0, 576, 32, 32)), + 695:('dragon_%d.ppm', (0, 608, 32, 32)), + + # custom images + 700:('fish_%d.ppm', (0, 0, 32, 32)), + 701:('fish_%d.ppm', (0, 32, 32, 32)), + 702:('fish_%d.ppm', (0, 64, 32, 32)), + 703:('fish_%d.ppm', (0, 96, 32, 32)), + 704:('fish_%d.ppm', (0, 128, 32, 32)), + 705:('fish_%d.ppm', (0, 160, 32, 32)), + 706:('fish_%d.ppm', (0, 192, 32, 32)), + } diff --git a/bubbob/statesaver.c b/bubbob/statesaver.c new file mode 100644 index 0000000..5baf033 --- /dev/null +++ b/bubbob/statesaver.c @@ -0,0 +1,610 @@ +/** High-performance deep copy. + + This one can copy running generators and their frames! + + statesaver.copy(x) -> recursive copy of x + + You have precise control over what is copied and what should be shared. + By default, *only* common built-in types are copied. Unrecognized + object types are shared. The copied built-in types are: + + - tuple + - list + - dict + - functions, for possibly mutable func_defaults (func_globals is shared) + - methods, for im_self and im_func (im_class is shared) + - running or stopped generators (yeah!) + - sequence iterators + + Old-style class instances are only copied if they have an + inst_build() method, which is called with no argument and must + return a new instance whose __dict__ is not filled (it will be + filled by the copying mecanisms). Suggested implementation: + + def inst_build(self): + return new.instance(self.__class__) + + New-style class instances are not supported (i.e. always shared). +**/ + +#include <Python.h> +#include <compile.h> +#include <frameobject.h> +#include <eval.h> + + +static PyObject* copyrec(PyObject* o); /* forward */ + +static PyObject* empty_iterator; + + +static PyObject* genbuild(PyObject* g) +{ + PyObject* x; + PyFrameObject* f; + PyCodeObject* co; + PyObject** dummy; + int i, res, ncells, nfrees; + + x = PyObject_GetAttrString(g, "gi_running"); + if (x == NULL) + return NULL; + res = PyObject_IsTrue(x); + Py_DECREF(x); + if (res < 0) + return NULL; + if (res) { + PyErr_SetString(PyExc_ValueError, "generator is running"); + return NULL; + } + + x = PyObject_GetAttrString(g, "gi_frame"); + if (x == NULL) + return NULL; + if (!PyFrame_Check(x)) { + if (x == Py_None) { + /* Python 2.5 only: exhausted generators have g.gi_frame == None */ + Py_DECREF(x); + Py_INCREF(empty_iterator); + return empty_iterator; + } + PyErr_SetString(PyExc_TypeError, "g.gi_frame must be a frame object"); + goto error; + } + f = (PyFrameObject*) x; + co = f->f_code; + + if (!(co->co_flags & CO_GENERATOR)) { + PyErr_SetString(PyExc_ValueError, "the frame is not from a generator"); + goto error; + } + if (f->f_stacktop == NULL) { + Py_DECREF(f); + Py_INCREF(g); /* exhausted -- can return 'g' itself */ + return g; + } + ncells = PyTuple_GET_SIZE(co->co_cellvars); + nfrees = PyTuple_GET_SIZE(co->co_freevars); + if (nfrees || ncells) { + PyErr_SetString(PyExc_ValueError, "generator has cell or free vars"); + goto error; + } + + if (co->co_argcount == 0) + dummy = NULL; + else + { + dummy = (PyObject**) malloc(co->co_argcount * sizeof(PyObject*)); + if (dummy == NULL) + { + PyErr_NoMemory(); + goto error; + } + for (i=0; i<co->co_argcount; i++) + dummy[i] = Py_None; + } + x = PyEval_EvalCodeEx(co, f->f_globals, f->f_locals, + dummy, co->co_argcount, NULL, 0, + NULL, 0, NULL); + if (dummy) + free(dummy); + Py_DECREF(f); + return x; + + error: + Py_DECREF(x); + return NULL; +} + +static int gencopy(PyObject* g2, PyObject* g) +{ + PyObject* x; + PyFrameObject* f = NULL; + PyFrameObject* f2 = NULL; + PyCodeObject* co; + int i, res; + + if (g != g2) + { + if (g2->ob_type != g->ob_type) + { + if (g2 == empty_iterator) + return 0; + PyErr_SetString(PyExc_TypeError, "type mismatch"); + return -1; + } + + x = PyObject_GetAttrString(g, "gi_frame"); + if (x == NULL) + return -1; + if (!PyFrame_Check(x)) { + PyErr_SetString(PyExc_TypeError, "g.gi_frame must be a frame object"); + Py_DECREF(x); + goto error; + } + f = (PyFrameObject*) x; + co = f->f_code; + + x = PyObject_GetAttrString(g2, "gi_frame"); + if (x == NULL) + return -1; + if (!PyFrame_Check(x)) { + PyErr_SetString(PyExc_TypeError, "returned gi_frame"); + Py_DECREF(x); + goto error; + } + f2 = (PyFrameObject*) x; + + if (f2->f_code != co) { + PyErr_SetString(PyExc_TypeError, "generator code mismatch"); + goto error; + } + + if (f2->f_stacktop != NULL) + while (f2->f_stacktop != f2->f_localsplus) + { + f2->f_stacktop--; + Py_XDECREF(*f2->f_stacktop); + } + + res = f->f_stacktop - f->f_localsplus; + f2->f_lasti = f->f_lasti; + f2->f_iblock = f->f_iblock; + memcpy(f2->f_blockstack, f->f_blockstack, sizeof(PyTryBlock)*f->f_iblock); + f2->f_stacktop = f2->f_localsplus; + for (i=0; i<res; i++) + { + x = f->f_localsplus[i]; + if (x != NULL) + x = copyrec(x); + *f2->f_stacktop++ = x; + } + } + return 0; + + error: + Py_XDECREF(f); + Py_XDECREF(f2); + return -1; +} + + +typedef struct { + PyObject_HEAD + long it_index; + PyObject *it_seq; /* Set to NULL when iterator is exhausted */ +} seqiterobject; +static PyObject* seqiterbuild(PyObject* o) +{ + seqiterobject* iter = (seqiterobject*) o; + if (iter->it_seq == NULL) + { + Py_INCREF(iter); /* exhausted */ + return (PyObject*) iter; + } + else + return PySeqIter_New(iter->it_seq); +} +static int seqitercopy(PyObject* o2, PyObject* o) +{ + PyObject* x; + seqiterobject* iter = (seqiterobject*) o; + seqiterobject* iter2 = (seqiterobject*) o2; + + iter2->it_index = iter->it_index; + if (iter->it_seq != NULL) + { + x = copyrec(iter->it_seq); + Py_XDECREF(iter2->it_seq); + iter2->it_seq = x; + } + return 0; +} + +#if PY_VERSION_HEX >= 0x02030000 /* 2.3 */ +/* pff */ +typedef struct { + PyObject_HEAD + long it_index; + PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ +} listiterobject; +static PyTypeObject* PyListIter_TypePtr; +static PyObject* listiterbuild(PyObject* o) +{ + listiterobject* iter = (listiterobject*) o; + if (iter->it_seq == NULL) + { + Py_INCREF(iter); /* exhausted */ + return (PyObject*) iter; + } + else + return PyList_Type.tp_iter((PyObject*) iter->it_seq); +} +static int listitercopy(PyObject* o2, PyObject* o) +{ + PyObject* x; + listiterobject* iter = (listiterobject*) o; + listiterobject* iter2 = (listiterobject*) o2; + + iter2->it_index = iter->it_index; + if (iter->it_seq != NULL) + { + x = copyrec((PyObject*) iter->it_seq); + Py_XDECREF(iter2->it_seq); + iter2->it_seq = (PyListObject*) x; + } + return 0; +} + +typedef struct { + PyObject_HEAD + long it_index; + PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */ +} tupleiterobject; +static PyTypeObject* PyTupleIter_TypePtr; +static PyObject* tupleiterbuild(PyObject* o) +{ + tupleiterobject* iter = (tupleiterobject*) o; + if (iter->it_seq == NULL) + { + Py_INCREF(iter); /* exhausted */ + return (PyObject*) iter; + } + else + return PyTuple_Type.tp_iter((PyObject*) iter->it_seq); +} +static int tupleitercopy(PyObject* o2, PyObject* o) +{ + PyObject* x; + tupleiterobject* iter = (tupleiterobject*) o; + tupleiterobject* iter2 = (tupleiterobject*) o2; + + iter2->it_index = iter->it_index; + if (iter->it_seq != NULL) + { + x = copyrec((PyObject*) iter->it_seq); + Py_XDECREF(iter2->it_seq); + iter2->it_seq = (PyTupleObject*) x; + } + return 0; +} +#endif /* PY_VERSION_HEX >= 0x02030000 */ + + +/* HACKS HACKS HACKS */ + +typedef struct { + PyObject_HEAD + PyObject* o; +} KeyObject; + +#define KEYS_BY_BLOCK 1024 + +struct key_block { + KeyObject keys[KEYS_BY_BLOCK]; + struct key_block* next; +}; + +static long key_hash(KeyObject* k) +{ + return (long)(k->o); +} + +static PyObject* key_richcmp(KeyObject* k1, KeyObject* k2, int op) +{ + PyObject* r; + assert(op == 2 /*PyCmp_EQ*/ ); + r = k1->o == k2->o ? Py_True : Py_False; + Py_INCREF(r); + return r; +} + +static PyTypeObject keytype = { + PyObject_HEAD_INIT(NULL) + 0, + "key", + sizeof(KeyObject), + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)key_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)key_richcmp, /* tp_richcompare */ +}; + + +/* global state */ +static PyObject* ss_memo; +static struct key_block* ss_block; +static int ss_next_in_block; +static PyObject *ss_error, *ss_errinst, *ss_errtb; + +static PyObject* str_inst_build; +static PyTypeObject* GeneratorType; + +/* never returns NULL, and never returns with a Python exception set! */ +static PyObject* copyrec(PyObject* o) +{ + PyTypeObject* t; + PyObject* n; + PyObject* key; + KeyObject* fkey; + + if (o == Py_None || o->ob_type == &PyInt_Type || + o->ob_type == &PyString_Type || o->ob_type == &PyFloat_Type || + o == empty_iterator) + { + Py_INCREF(o); + return o; + } + if (ss_next_in_block < 0) + { + struct key_block* b = (struct key_block*) malloc(sizeof(struct key_block)); + if (!b) { PyErr_NoMemory(); goto fail1; } + b->next = ss_block; + ss_block = b; + ss_next_in_block = KEYS_BY_BLOCK - 1; + } + fkey = ss_block->keys + ss_next_in_block; + fkey->ob_refcnt = 1; + fkey->ob_type = &keytype; + fkey->o = o; + key = (PyObject*) fkey; + n = PyDict_GetItem(ss_memo, key); + if (n) + { + Py_INCREF(n); + return n; + } + ss_next_in_block--; + Py_INCREF(o); /* reference stored in 'fkey->o' */ + t = o->ob_type; + if (t == &PyTuple_Type) + { + int i, count = PyTuple_GET_SIZE(o); + n = PyTuple_New(count); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + for (i=0; i<count; i++) + PyTuple_SET_ITEM(n, i, copyrec(PyTuple_GET_ITEM(o, i))); + return n; + } + if (t == &PyList_Type) + { + int i, count = PyList_GET_SIZE(o); + n = PyList_New(count); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + for (i=0; i<count; i++) + PyList_SET_ITEM(n, i, copyrec(PyList_GET_ITEM(o, i))); + return n; + } + if (t == &PyDict_Type) + { + int i = 0; + PyObject* dictkey; + PyObject* dictvalue; + n = PyDict_New(); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + while (PyDict_Next(o, &i, &dictkey, &dictvalue)) + if (PyDict_SetItem(n, copyrec(dictkey), copyrec(dictvalue))) + goto fail; + return n; + } + if (t == &PyInstance_Type) + { + int i = 0; + PyObject* dictkey; + PyObject* dictvalue; + PyObject* dsrc; + PyObject* ddest; + PyObject* inst_build = PyObject_GetAttr(o, str_inst_build); + if (inst_build == NULL) + { + PyErr_Clear(); + goto unmodified; + } + n = PyObject_CallObject(inst_build, NULL); + Py_DECREF(inst_build); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + dsrc = ((PyInstanceObject*) o)->in_dict; + ddest = ((PyInstanceObject*) n)->in_dict; + while (PyDict_Next(dsrc, &i, &dictkey, &dictvalue)) + if (PyDict_SetItem(ddest, copyrec(dictkey), copyrec(dictvalue))) + goto fail; + return n; + } + if (t == &PyFunction_Type) + { + int i, count; + PyObject* tsrc = PyFunction_GET_DEFAULTS(o); + PyObject* tdest; + if (!tsrc) goto unmodified; + count = PyTuple_GET_SIZE(tsrc); + if (count == 0) goto unmodified; + n = PyFunction_New(PyFunction_GET_CODE(o), PyFunction_GET_GLOBALS(o)); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + tdest = PyTuple_New(count); + if (!tdest) goto fail; + for (i=0; i<count; i++) + PyTuple_SET_ITEM(tdest, i, copyrec(PyTuple_GET_ITEM(tsrc, i))); + i = PyFunction_SetDefaults(n, tdest); + Py_DECREF(tdest); + if (i) goto fail; + return n; + } + if (t == &PyMethod_Type) + { + PyObject* x; + n = PyMethod_New(PyMethod_GET_FUNCTION(o), + PyMethod_GET_SELF(o), + PyMethod_GET_CLASS(o)); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + x = copyrec(PyMethod_GET_FUNCTION(n)); + Py_DECREF(PyMethod_GET_FUNCTION(n)); + PyMethod_GET_FUNCTION(n) = x; + x = copyrec(PyMethod_GET_SELF(n)); + Py_DECREF(PyMethod_GET_SELF(n)); + PyMethod_GET_SELF(n) = x; + return n; + } + if (t == GeneratorType) + { + n = genbuild(o); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + if (gencopy(n, o)) goto fail; + return n; + } + if (t == &PySeqIter_Type) + { + n = seqiterbuild(o); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + if (seqitercopy(n, o)) goto fail; + return n; + } + #if PY_VERSION_HEX >= 0x02030000 /* 2.3 */ + if (t == PyListIter_TypePtr) + { + n = listiterbuild(o); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + if (listitercopy(n, o)) goto fail; + return n; + } + if (t == PyTupleIter_TypePtr) + { + n = tupleiterbuild(o); + if (!n || PyDict_SetItem(ss_memo, key, n)) goto fail; + if (tupleitercopy(n, o)) goto fail; + return n; + } + #endif + + ss_next_in_block++; + return o; /* reference no longer stored in 'fkey->o' */ + + unmodified: + PyDict_SetItem(ss_memo, key, o); + Py_INCREF(o); + return o; + + fail1: + n = NULL; + fail: + Py_INCREF(o); + Py_XDECREF(n); + if (ss_error == NULL) + PyErr_Fetch(&ss_error, &ss_errinst, &ss_errtb); + else + PyErr_Clear(); + return o; +} + +static PyObject* sscopy(PyObject* self, PyObject* o) +{ + PyObject* n; + ss_memo = PyDict_New(); + if (!ss_memo) + return NULL; + + ss_block = NULL; + ss_next_in_block = -1; + ss_error = NULL; + ss_errinst = NULL; + ss_errtb = NULL; + n = copyrec(o); + Py_DECREF(ss_memo); + while (ss_block) + { + int i; + struct key_block* b = ss_block; + ss_block = b->next; + for (i=ss_next_in_block+1; i<KEYS_BY_BLOCK; i++) + Py_DECREF(b->keys[i].o); + free(b); + ss_next_in_block = -1; + } + if (ss_error && !PyErr_Occurred()) + PyErr_Restore(ss_error, ss_errinst, ss_errtb); + else + { + Py_XDECREF(ss_error); + Py_XDECREF(ss_errinst); + Py_XDECREF(ss_errtb); + } + if (PyErr_Occurred()) + { + Py_DECREF(n); + n = NULL; + } + return n; +} + + +static PyMethodDef StateSaverMethods[] = { + {"copy", sscopy, METH_O}, + {NULL, NULL} /* Sentinel */ +}; + +void initstatesaver(void) +{ + PyObject* m; + PyObject* x, *y; + m = Py_InitModule("statesaver", StateSaverMethods); + if (m == NULL) + return; + keytype.ob_type = &PyType_Type; + str_inst_build = PyString_InternFromString("inst_build"); + + m = PyImport_ImportModule("types"); + if (!m) return; + GeneratorType = (PyTypeObject*) PyObject_GetAttrString(m, "GeneratorType"); + if (!GeneratorType) return; + + x = PyTuple_New(0); + if (!x) return; + empty_iterator = PyObject_GetIter(x); + Py_DECREF(x); + if (!empty_iterator) return; + PyTupleIter_TypePtr = empty_iterator->ob_type; + + x = PyList_New(0); + if (!x) return; + y = PyList_Type.tp_iter(x); + if (y) PyListIter_TypePtr = y->ob_type; + Py_XDECREF(y); + Py_DECREF(x); + if (!y) return; +} diff --git a/bubbob/statesaver.py b/bubbob/statesaver.py new file mode 100644 index 0000000..85d3425 --- /dev/null +++ b/bubbob/statesaver.py @@ -0,0 +1,135 @@ +""" +A pure Python implementation of statesaver.c that runs on top of PyPy. +See description in statesaver.c. +Difference: this supports new-style instances too. +Use statesaver.standard_build() as the inst_build() if you want. +""" + +from _pickle_support import generator_new +import types + +def standard_build(self): + if type(self) is types.InstanceType: + # old-style instance + return types.InstanceType(self.__class__) + else: + # new-style instance + return type(self).__new__(type(self)) + +# ____________________________________________________________ + +def not_copied(x, memo): + return x + +def copy_custom_instance(x, memo): + try: + return memo[id(x)] + except KeyError: + y = x.inst_build() + memo[id(x)] = y + for key, value in x.__dict__.items(): + y.__dict__[key] = copyrec(value, memo) + return y + +def copy_tuple(x, memo): + return tuple([copyrec(item, memo) for item in x]) + +def copy_list(x, memo): + try: + return memo[id(x)] + except KeyError: + y = [] + memo[id(x)] = y + for item in x: + y.append(copyrec(item, memo)) + return y + +def copy_dict(x, memo): + try: + return memo[id(x)] + except KeyError: + y = {} + memo[id(x)] = y + for key, value in x.items(): + y[copyrec(key, memo)] = copyrec(value, memo) + return y + +def copy_function(x, memo): + if not x.func_defaults: + return x # not copied + try: + return memo[id(x)] + except KeyError: + y = types.FunctionType(x.func_code, x.func_globals, x.func_name) + memo[id(x)] = y + y.func_defaults = copyrec(x.func_defaults, memo) + return y + +def copy_method(x, memo): + return types.MethodType(copyrec(x.im_func, memo), + copyrec(x.im_self, memo), + x.im_class) + +def copy_generator(x, memo): + try: + return memo[id(x)] + except KeyError: + y = generator_new(copyrec(x.gi_frame, memo), x.gi_running) + memo[id(x)] = y + return y + +def copy_frame(x, memo): + try: + return memo[id(x)] + except KeyError: + frame_new, args, state = x.__reduce__() + y = frame_new(*args) + memo[id(x)] = y + newstate = [] + for item in state: + if not (item is x.f_globals or item is x.f_builtins): + item = copyrec(item, memo) + newstate.append(item) + y.__setstate__(newstate) + return y + +def copy_seqiter(x, memo): + try: + return memo[id(x)] + except KeyError: + # XXX self-recursion is not correctly handled here + seqiter_new, args = x.__reduce__() + args = [copyrec(item, memo) for item in args] + y = seqiter_new(*args) + memo[id(x)] = y + return y + +# ____________________________________________________________ + +type_handlers = {tuple: copy_tuple, + list: copy_list, + dict: copy_dict, + types.FunctionType: copy_function, + types.MethodType: copy_method, + types.GeneratorType: copy_generator, + types.FrameType: copy_frame, + type(iter([])): copy_seqiter, + } + +def no_handler_found(x, memo): + if hasattr(x, '__dict__') and hasattr(x.__class__, 'inst_build'): + handler = copy_custom_instance + else: + handler = not_copied + type_handlers[x.__class__] = handler + return handler(x, memo) + +def copyrec(x, memo): + try: + cls = x.__class__ + except AttributeError: + return x # 'cls' is likely an old-style class object + return type_handlers.get(cls, no_handler_found)(x, memo) + +def copy(x): + return copyrec(x, {}) diff --git a/bubbob/statesaver.so b/bubbob/statesaver.so Binary files differnew file mode 100755 index 0000000..bf805e5 --- /dev/null +++ b/bubbob/statesaver.so diff --git a/bubbob/test_rnglevel.py b/bubbob/test_rnglevel.py new file mode 100644 index 0000000..6b65514 --- /dev/null +++ b/bubbob/test_rnglevel.py @@ -0,0 +1,64 @@ +# +# This test generates 100 times 25 random levels and checks +# that it doesn't crash, and that it gives levels that are +# possible (in the limited sense of not having any full- +# column walls) +# +# this test accepts the following parameters: +# -wall show the level layout +# -wind show the level wind pattern +# -seed N use random seed N for the generation +# + +import sys +import random +sys.path.append('..') +sys.path.append('../common') + +n_lvls = 100 +show_lvl = 0 + +idx = 0 +while idx < len(sys.argv): + arg = sys.argv[idx] + idx += 1 + if arg == '-wall': + show_lvl |= 1 + n_lvls = 2 + if arg == '-wind': + show_lvl |= 2 + n_lvls = 2 + if arg == '-seed': + arg = sys.argv[idx] + idx += 1 + print "Using seed: " + arg + "\n" + random.seed(arg) + +def printlvl(level): + if show_lvl: + print "\n\n" + for y in range(level.HEIGHT): + str = "" + if show_lvl & 1: + str = level.walls[y] + if show_lvl & 2: + if str: + str += " | " + str += level.winds[y] + print str + +for i in range(n_lvls): + print '%4d:' % i, + d = {'__name__': 'RandomLevels'} + execfile('levels/RandomLevels.py', d) + for i, Lvl in enumerate(d['GenerateLevels']()): + level = Lvl(i) + printlvl(level) + for x in range(2, level.width-2): + for y in range(0, level.height): + if level.walls[y][x] == ' ': + break + else: + for line in level.walls: + print line + raise AssertionError("full height wall in column %d" % x) diff --git a/bubbob/test_statesaver.py b/bubbob/test_statesaver.py new file mode 100644 index 0000000..3fadcd7 --- /dev/null +++ b/bubbob/test_statesaver.py @@ -0,0 +1,127 @@ +import py +import statesaver +import new + +def test_list(): + lst = [None, 12, "hello", 3.4, ("foo", (), [])] + lst1 = statesaver.copy(lst) + assert lst1 == lst + assert lst1 is not lst + assert lst1[-1][-1] is not lst[-1][-1] + +def test_dict(): + dct = {1: "hi", 2: {}} + dct1 = statesaver.copy(dct) + assert dct1 == dct + assert dct1 is not dct + assert dct1[2] is not dct[2] + +def test_instance(): + class Foo: + def inst_build(self): + return Bar() + class Bar: + pass + x = Foo() + x.attr = [1, 2, 3] + y = statesaver.copy(x) + assert y.__class__ is Bar + assert y.attr == [1, 2, 3] + assert y.attr is not x.attr + +glob = 2 +def test_function(): + # XXX closures not supported + def func(x, y=[]): + assert glob == 2 + y.append(x) + return y + l = func(5) + l = func(6) + assert l == [5, 6] + func1 = statesaver.copy(func) + l = func(7) + l = func(8) + assert l == [5, 6, 7, 8] + l = func1(9) + l = func1(10) + assert l == [5, 6, 9, 10] + +def test_method(): + def func(x, y=[]): + assert glob == 2 + y.append(x) + return y + m = new.instancemethod(func, {}) + assert m() == [{}] + m1 = statesaver.copy(m) + assert m() == [{}, {}] + assert m() == [{}, {}, {}] + assert m1() == [{}, {}] + assert m1() == [{}, {}, {}] + l = m1() + assert l[0] is l[1] is l[2] is l[3] + +def test_generator(): + def gfunc(): + lst = [5, 6] + yield lst.pop() + yield lst.pop() + g = gfunc() + assert g.next() == 6 + g1 = statesaver.copy(g) + assert g.next() == 5 + py.test.raises(StopIteration, g.next) + assert g1.next() == 5 + py.test.raises(StopIteration, g1.next) + +def test_exhausted_gen(): + def gfunc(): + yield 5 + g = gfunc() + for i in g: + print i + g1 = statesaver.copy(g) + assert iter(g1) is g1 + py.test.raises(StopIteration, g1.next) + g2 = statesaver.copy(g1) + assert iter(g2) is g2 + py.test.raises(StopIteration, g2.next) + +def test_seqiter(): + from UserList import UserList + seq = UserList([2, 4, 6, 8]) + it = iter(seq) + assert it.next() == 2 + assert it.next() == 4 + it1 = statesaver.copy(it) + assert list(it) == [6, 8] + assert list(it1) == [6, 8] + +def test_tupleiter(): + tup = (2, 4, 6, 8) + it = iter(tup) + assert it.next() == 2 + assert it.next() == 4 + it1 = statesaver.copy(it) + assert list(it) == [6, 8] + assert list(it1) == [6, 8] + +def test_listiter(): + lst = [2, 4, 6, 8] + it = iter(lst) + assert it.next() == 2 + assert it.next() == 4 + it1 = statesaver.copy(it) + lst.append(10) + assert list(it) == [6, 8, 10] + assert list(it1) == [6, 8] + +def test_stringiter(): + s = "hello" + it = iter(s) + assert it.next() == 'h' + assert it.next() == 'e' + it1 = statesaver.copy(it) + assert list(it) == ['l', 'l', 'o'] + assert list(it1) == ['l', 'l', 'o'] diff --git a/bubbob/tmp/pat00.ppm b/bubbob/tmp/pat00.ppm Binary files differnew file mode 100644 index 0000000..9b8ce4d --- /dev/null +++ b/bubbob/tmp/pat00.ppm diff --git a/bubbob/tmp/pat01.ppm b/bubbob/tmp/pat01.ppm Binary files differnew file mode 100644 index 0000000..236b6ea --- /dev/null +++ b/bubbob/tmp/pat01.ppm diff --git a/bubbob/tmp/pat02.ppm b/bubbob/tmp/pat02.ppm Binary files differnew file mode 100644 index 0000000..341ecac --- /dev/null +++ b/bubbob/tmp/pat02.ppm diff --git a/bubbob/tmp/pat03.ppm b/bubbob/tmp/pat03.ppm Binary files differnew file mode 100644 index 0000000..ad98a6f --- /dev/null +++ b/bubbob/tmp/pat03.ppm diff --git a/bubbob/tmp/pat04.ppm b/bubbob/tmp/pat04.ppm Binary files differnew file mode 100644 index 0000000..c35f525 --- /dev/null +++ b/bubbob/tmp/pat04.ppm diff --git a/bubbob/tmp/pat05.ppm b/bubbob/tmp/pat05.ppm Binary files differnew file mode 100644 index 0000000..a7745ca --- /dev/null +++ b/bubbob/tmp/pat05.ppm diff --git a/bubbob/tmp/pat06.ppm b/bubbob/tmp/pat06.ppm Binary files differnew file mode 100644 index 0000000..ed4e844 --- /dev/null +++ b/bubbob/tmp/pat06.ppm diff --git a/bubbob/tmp/pat07.ppm b/bubbob/tmp/pat07.ppm Binary files differnew file mode 100644 index 0000000..fbe1e7f --- /dev/null +++ b/bubbob/tmp/pat07.ppm diff --git a/bubbob/tmp/pat08.ppm b/bubbob/tmp/pat08.ppm Binary files differnew file mode 100644 index 0000000..40c750a --- /dev/null +++ b/bubbob/tmp/pat08.ppm diff --git a/bubbob/tmp/pat09.ppm b/bubbob/tmp/pat09.ppm Binary files differnew file mode 100644 index 0000000..5821f76 --- /dev/null +++ b/bubbob/tmp/pat09.ppm diff --git a/bubbob/tmp/pat10.ppm b/bubbob/tmp/pat10.ppm Binary files differnew file mode 100644 index 0000000..651c51b --- /dev/null +++ b/bubbob/tmp/pat10.ppm diff --git a/bubbob/tmp/pat11.ppm b/bubbob/tmp/pat11.ppm Binary files differnew file mode 100644 index 0000000..4854c3e --- /dev/null +++ b/bubbob/tmp/pat11.ppm diff --git a/bubbob/tmp/pat12.ppm b/bubbob/tmp/pat12.ppm Binary files differnew file mode 100644 index 0000000..7482e32 --- /dev/null +++ b/bubbob/tmp/pat12.ppm diff --git a/bubbob/tmp/pat13.ppm b/bubbob/tmp/pat13.ppm Binary files differnew file mode 100644 index 0000000..8fbe9a4 --- /dev/null +++ b/bubbob/tmp/pat13.ppm diff --git a/bubbob/tmp/pat14.ppm b/bubbob/tmp/pat14.ppm Binary files differnew file mode 100644 index 0000000..54625b2 --- /dev/null +++ b/bubbob/tmp/pat14.ppm diff --git a/bubbob/tmp/pat15.ppm b/bubbob/tmp/pat15.ppm Binary files differnew file mode 100644 index 0000000..d50dd6f --- /dev/null +++ b/bubbob/tmp/pat15.ppm diff --git a/bubbob/tmp/pat16.ppm b/bubbob/tmp/pat16.ppm new file mode 100644 index 0000000..f3a1881 --- /dev/null +++ b/bubbob/tmp/pat16.ppm @@ -0,0 +1,4 @@ +P6 +40 256 +255 +ªªªªªªªªªªªªªªªªªªÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆ»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwwwˆˆˆªªªˆˆˆªªªˆˆˆªªª•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªªªªªªªªÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwwwˆˆˆªªªˆˆˆªªªˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªªªªªÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝ݈ˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwwwˆˆˆªªªˆˆˆªªª•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªªÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»ªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwwwˆˆˆªªªˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ªªªªªªÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwwwˆˆˆªªª•••‚‚‚ooo]]]JJJ777%%%ªªªÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝ݈ˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwwwˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ÝÝÝ»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»ˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUwww•••‚‚‚ooo]]]JJJ777%%%»»»wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUU•••‚‚‚ooo]]]JJJ777%%%wwwwwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ•••‚‚‚ooo]]]JJJ777%%%wwwwwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆˆˆˆ•••‚‚‚ooo]]]JJJ777%%%wwwUUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªÝÝ݈ˆˆwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆˆˆˆ•••‚‚‚ooo]]]JJJ777%%%UUUwwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªÝÝÝ»»»ˆˆˆˆˆˆwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆˆˆˆ•••‚‚‚ooo]]]JJJ777%%%wwwUUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªÝÝÝ»»»»»»ˆˆˆˆˆˆˆˆˆwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwwwˆˆˆ•••‚‚‚ooo]]]JJJ777%%%UUUDDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªÝÝÝ»»»ÝÝÝ»»»ˆˆˆˆˆˆˆˆˆˆˆˆwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUUwww•••‚‚‚ooo]]]JJJ777%%%DDDˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªÝÝÝ»»»ÝÝÝ»»»ÝÝ݈ˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªª»»»UUU•••‚‚‚ooo]]]JJJ777%%%ˆˆˆîîîÝÝÝ»»»»»»ÝÝÝ»»»»»»ˆˆˆªªªÝÝÝ»»»ÝÝÝ»»»ÝÝÝîîªˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwUUUˆˆˆˆˆˆˆˆˆˆˆˆªªªˆˆˆªªªˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ˆˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆªªªˆˆˆªªªˆˆˆªªªˆˆˆDDDwwwUUUwwwUUUwwwUUUDDDwwwwwwUUUwwwUUUUUUDDDªªª•••‚‚‚ooo]]]JJJ777%%%ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆªªªˆˆˆªªªˆˆˆªªªwwwUUUwwwUUUwwwUUUDDDwwwwwwUUUwwwUUUUUUDDDªªªîîî•••‚‚‚ooo]]]JJJ777%%%»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆªªªˆˆˆªªªˆˆˆUUUwwwUUUwwwUUUDDDwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ•••‚‚‚ooo]]]JJJ777%%%»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆªªªˆˆˆªªªwwwUUUwwwUUUDDDwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»•••‚‚‚ooo]]]JJJ777%%%ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆªªªˆˆˆUUUwwwUUUDDDwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»•••‚‚‚ooo]]]JJJ777%%%»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆªªªwwwUUUDDDwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»•••‚‚‚ooo]]]JJJ777%%%ªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwˆˆˆUUUDDDwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ªªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwDDDwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»•••‚‚‚ooo]]]JJJ777%%%ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªªªªwwwwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»www•••‚‚‚ooo]]]JJJ777%%%»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwªªªwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUU•••‚‚‚ooo]]]JJJ777%%%ªªª»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwUUUwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUUªªª•••‚‚‚ooo]]]JJJ777%%%ªªªªªª»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªˆˆˆwwwUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUUªªªˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªª»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwªªªUUUUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUUªªªˆˆˆªªª•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªªªªª»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUwwwUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUUªªªˆˆˆªªªˆˆˆ•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªªªªªªªª»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆUUUDDDªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUUªªªˆˆˆªªªˆˆˆªªª•••‚‚‚ooo]]]JJJ777%%%ªªªªªªªªªªªªªªªªªª»»»ÝÝݪªªªªª»»»ªªª»»»»»»ÝÝ݈ˆˆªªªîîîÝÝÝ»»»»»»»»»ˆˆˆ»»»wwwUUUªªªˆˆˆªªªˆˆˆªªªwww•••‚‚‚ooo]]]JJJ777%%%ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff£££ŽŽŽzzzeeeQQQ===(((ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª™™™ªªªªªªªªª»»»»»»»»»ÝÝÝ»»»ÝÝÝÝÝÝÝÝÝîîîîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™ªªªªªªªªª»»»ªªª»»»»»»»»»ÝÝÝÝÝÝÝÝÝîîîÝÝÝîîîîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª™™™ªªªªªªªªª»»»™™™™™™™™™™™™wwwwwwÝÝÝîîîîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™ªªªªªª™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªwwwwwwîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª™™™™™™ªªª»»»»»»»»»»»»»»»»»»»»»»»»ªªªªªª™™™wwwÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™™™™ªªª»»»ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ»»»»»»ªªªªªª™™™wwwÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™ªªª»»»ÝÝÝÝÝÝîîîîîîîîîÝÝÝÝÝÝÝÝÝÝÝÝ»»»»»»ªªªªªª™™™wwwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª»»»ÝÝÝÝÝÝîîîîîîÿÿÿîîîîîîÝÝÝÝÝÝÝÝÝ»»»»»»»»»ªªªªªª™™™wwwÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª»»»ÝÝÝÝÝÝÝÝÝîîîîîîîîîÝÝÝÝÝÝÝÝÝÝÝÝ»»»»»»»»»ªªªªªª™™™™™™wwwÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™ªªªªªª»»»ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ»»»»»»»»»»»»ªªªªªª™™™™™™wwwÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™ªªªªªª»»»ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ»»»»»»»»»»»»»»»ªªªªªª™™™™™™wwwÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™ˆˆˆ™™™ªªªªªª»»»»»»ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ»»»»»»»»»»»»»»»ªªªªªªªªª™™™™™™ˆˆˆwwwÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™ˆˆˆ™™™ªªªªªªªªª»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ªªªªªª™™™™™™™™™ˆˆˆwwwÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™ˆˆˆ™™™™™™ªªªªªªªªª»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ªªªªªªªªª™™™™™™™™™ˆˆˆwwwÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™ˆˆˆ™™™™™™ªªªªªªªªªªªª»»»»»»»»»»»»»»»»»»»»»ªªªªªªªªªªªª™™™™™™™™™™™™ˆˆˆwwwÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™wwwˆˆˆ™™™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª™™™™™™™™™™™™ˆˆˆˆˆˆwwwÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™wwwˆˆˆ™™™™™™™™™™™™ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª™™™™™™™™™™™™ˆˆˆˆˆˆˆˆˆwwwÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™ˆˆˆˆˆˆ™™™™™™™™™™™™™™™ªªªªªªªªªªªªªªª™™™™™™™™™™™™™™™™™™ˆˆˆˆˆˆˆˆˆwwwÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™wwwˆˆˆˆˆˆ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ˆˆˆˆˆˆˆˆˆˆˆˆwwwÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™wwwˆˆˆˆˆˆˆˆˆ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™wwwˆˆˆˆˆˆˆˆˆˆˆˆ™™™™™™™™™™™™™™™™™™™™™™™™ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™wwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªªwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™ªªªwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª™™™ªªªwwwwwwwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆwwwwwwwwwîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™ªªªªªªªªª»»»ªªªwwwwwwwwwwwwwwwwwwîîîÝÝÝîîîîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™ªªª™™™ªªªªªªªªª»»»»»»»»»ÝÝÝ»»»ÝÝÝÝÝÝÝÝÝîîîîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffff™™™™™™™™™™™™™™™™™™ªªªªªªªªª»»»ªªª»»»»»»»»»ÝÝÝÝÝÝÝÝÝîîîÝÝÝîîîîîîîîîÿÿÿîîîÿÿÿÿÿÿÿÿÿÿÿÿffffff£££ŽŽŽzzzeeeQQQ===(((ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff£££ŽŽŽzzzeeeQQQ===(((ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff£££ŽŽŽzzzeeeQQQ===(((ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌfffÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww™™Ì™™ÌfffÌÌÌwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ»»»ÝÝÝ™™Ì™™Ì™™ÌfffÌÌÌwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ»»»ÝÝÝ™™Ì™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ì™™ÌfffÌÌÌwwwˆˆˆªªªªªªªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ì™™Ì™™ÌfffÌÌÌwwwˆˆˆªªªªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ì™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ì™™Ì™™Ì™™ÌfffÌÌÌwwwˆˆˆªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ì™™Ì™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™ÌfffÌÌÌ™™Ì™™ÌfffÌÌÌwwwˆˆˆªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™ÌfffÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìffff™ÌÌÌÌ™™Ì™™ÌfffÌÌÌwwwˆˆˆªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™ÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªª»»»ÝÝÝ™™Ì™™Ì™™Ìffff™Ìf™ÌÌÌÌ™™Ì™™ÌfffÌÌÌwwwˆˆˆªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆ»»»ÝÝÝ™™Ì™™Ì™™Ìffff™Ìf™Ìf™ÌÌÌÌ™™Ì™™ÌfffÌÌÌwwwˆˆˆ»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwww»»»ÝÝÝ™™Ì™™Ì™™Ìffff™Ìf™Ìf™Ìf™ÌÌÌÌ™™Ì™™ÌfffÌÌÌwww»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwÝÝÝ™™Ì™™Ì™™Ìffff™Ìf™Ìf™Ìf™Ìf™ÌÌÌÌ™™Ì™™ÌfffÌÌÌwwwÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwww™™Ì™™Ì™™Ì™™ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ™™Ì™™ÌfffÌÌÌwww™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌ™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™ÌfffÌÌÌ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌ™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ìfff™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-fffffffffffffffffffffffffffffffffffffffffffff™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªªªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆªªª»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwˆˆˆ»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwww»»»ÝÝÝ™™Ì™™Ì™™Ìfffff™f™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ìf™Ì™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwwwÝÝÝ™™Ì™™Ì™™Ìfffff™™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌ™ÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌwww™™Ì™™Ì™™Ì™™ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌ™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-ÌÌÌ™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ì™™Ìfff‘œ´ˆžlu‡ZaqHNZ6:C$'-fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffwww‘œ´ˆžlu‡ZaqHNZ6:C$'- diff --git a/bubbob/tmp/pat17.ppm b/bubbob/tmp/pat17.ppm Binary files differnew file mode 100644 index 0000000..dcc159e --- /dev/null +++ b/bubbob/tmp/pat17.ppm diff --git a/bubbob/tmp/pat18.ppm b/bubbob/tmp/pat18.ppm Binary files differnew file mode 100644 index 0000000..f36ff3d --- /dev/null +++ b/bubbob/tmp/pat18.ppm diff --git a/bubbob/tmp/pat19.ppm b/bubbob/tmp/pat19.ppm Binary files differnew file mode 100644 index 0000000..e44e5c7 --- /dev/null +++ b/bubbob/tmp/pat19.ppm diff --git a/bubbob/tmp/pat20.ppm b/bubbob/tmp/pat20.ppm Binary files differnew file mode 100644 index 0000000..eba7c3b --- /dev/null +++ b/bubbob/tmp/pat20.ppm |