diff options
Diffstat (limited to 'bubbob/boards.py')
-rw-r--r-- | bubbob/boards.py | 1470 |
1 files changed, 1470 insertions, 0 deletions
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) |