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/bonuses.py |
initial commit from cvs 1.6.2
Diffstat (limited to 'bubbob/bonuses.py')
-rw-r--r-- | bubbob/bonuses.py | 2504 |
1 files changed, 2504 insertions, 0 deletions
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 |