diff options
Diffstat (limited to 'bubbob/bubbles.py')
-rw-r--r-- | bubbob/bubbles.py | 1284 |
1 files changed, 1284 insertions, 0 deletions
diff --git a/bubbob/bubbles.py b/bubbob/bubbles.py new file mode 100644 index 0000000..9734826 --- /dev/null +++ b/bubbob/bubbles.py @@ -0,0 +1,1284 @@ +from __future__ import generators +import random, math +import gamesrv +import images +import boards +from boards import * +from images import ActiveSprite +from mnstrmap import GreenAndBlue, LetterBubbles, Stars +from mnstrmap import Lightning, Water, Fire, SpinningBalls, PlayerBubbles + + +bubble_wind = { + '<': (-1, 0), + '>': (+1, 0), + '^': ( 0,-1), + 'v': ( 0,+1), + 'x': ( 0, 0), + } + + +class Bubble(ActiveSprite): + exploding_bubbles = range(131,136) + red_bubbles = [156, 157, 156, 155] + white_bubbles = [164, 165, 164, 163] + pink_bubbles = [172, 173, 172, 171] + check_onbubble = ([(0,-1)], [(0,1)]) + + touchable = 1 + warp = 0 + default_windless = None + catch_dragons = None + nimages = GreenAndBlue.normal_bubbles + + def touched(self, dragon): + dx, dy = dragon.x, dragon.y + o = [] + if abs(self.x - dx) >= 25: + if self.x < dx: + o.append((1,0)) + else: + o.append((-1,0)) + if abs(self.y - dy) >= 25: + if self.y < dy: + o.append((0,1)) + else: + o.append((0,-1)) + if o: + self.obstacle = o + elif (self.catch_dragons and + abs(self.x - dx) < 15 and abs(self.y - dy) < 15): + if dragon not in self.catch_dragons: + self.catch_dragons.append(dragon) +## elif not self.pop(getattr(dragon, 'poplist', None)): +## if self.x < dx: +## o.append((1,0)) +## else: +## o.append((-1,0)) +## if self.y < dy: +## o.append((0,1)) +## else: +## o.append((0,-1)) +## self.obstacle = o + else: + self.pop(getattr(dragon, 'poplist', None)) + return o == self.check_onbubble[dragon.bottom_up()] + + def can_catch_dragons(self, author, catch_myself=0): + self.catch_dragons = [author] + if catch_myself: + self.catch_dragons.append(author) + self.move(author.x, author.y) + self.gen.append(self.catching_dragons()) + + def catching_dragons(self): + from player import Dragon + yield None # time to catch several dragons + dragons = [d for d in self.catch_dragons if isinstance(d, Dragon)] + self.catch_dragons = None + if len(dragons) >= 2: + import bonuses + author = dragons.pop(0) + imglist = [self.nimages[d.dcap.get('bubbericons', + d.bubber).pn][i] + for d in dragons for i in [1,2,1,0]] + self.setimages(self.cyclic(imglist)) + self.warp = 1 + caught = [(bonus.points, bonus) for d in dragons + if d.bubber is not author.bubber + for bonus in d.listcarrybonuses() + if isinstance(bonus, CatchNote)] + caught.sort() + # count caught dragons, excluding team mates, but including self + count = 0 + for d in dragons: + if (d.bubber is author.bubber or + not d.bubber.sameteam(author.bubber)): + count += 1 + if count: + if count == 1: + points = 250 + else: + self.play(images.Snd.Extra) + if count == 2: + points = 10000 + elif count == 3: + points = 30000 + else: + points = 70000 + caught.append((points, CatchNote(points))) + caught = caught[-3:] + for points, bonus in caught: + author.carrybonus(bonus, 111) + bonuses.points(self.x, self.y-HALFCELL, author, points) + for d in dragons: + d.become_bubblingeyes(self) + s_catch = author.bubber.stats.setdefault('catch', {}) + s_catch[d.bubber] = s_catch.get(d.bubber, 0) + 1 + + def pop(self, poplist=None): + if self.touchable: + self.play(images.Snd.Pop) + self.poplist = poplist + self.untouchable() + self.gen = [self.die(Bubble.exploding_bubbles)] + if poplist: + dragon = poplist[0] + points = self.popped(dragon) + if dragon: + dragon.bubber.givepoints(points) + dragon.bubber.stats['bubble'] += 1 + self.gen.append(self.poprec()) + return 1 + else: + return 0 + + def popped(self, dragon): + return 10 + + def poprec(self): + yield None + for s in self.touching(0): + if isinstance(s, Bubble): + s.pop(self.poplist) + + def normal_movements(self, dx=0, dy=-1, timeout=800): + self.obstacle = [] + time = 0 + touchbubble = timeout = (timeout or 0) * 2 + while 1: + del self.obstacle[:] + yield None + timeout -= 2 + if not timeout: + self.setimages(self.bubble_red()) + if timeout > touchbubble: + continue + if timeout&2: + for s in self.touching(13+(timeout&6)): + if isinstance(s, Bubble) and s is not self: + if (s.x-self.x)*dx > 0 or (s.y-self.y)*dy > 0: + touchbubble = timeout - (timeout&12) - 3 + break + if timeout > touchbubble: + continue + if (dx,dy) not in self.obstacle: + if dx==dy==0: + if len(self.obstacle)==1: + dx1, dy1 = self.obstacle[0] + self.step(-dx1, -dy1) + else: + self.step(dx, dy) + if self.y < -32 or self.y >= boards.bheight: + if not self.warp: + self.poplist = None + self.kill() + return + self.vertical_warp() +## dx = -dx + w = wget(self.x, self.y) + if w != ' ': + dx, dy = bubble_wind[w] + elif self.default_windless: + dx, dy = self.default_windless + + if dx == dy == 0: + # this is the same as the whole loop but runs faster + while len(self.obstacle) != 1: + del self.obstacle[:] + yield None + timeout -= 2 + if not timeout: + self.setimages(self.bubble_red()) + + def bubble_red(self, speed=5): + for n in self.imgseq(Bubble.white_bubbles, repeat=3): + yield n + for n in self.imgseq(Bubble.pink_bubbles, repeat=4): + yield n + for n in self.imgseq(Bubble.red_bubbles, repeat=4): + yield n + for n in self.imgseq([Bubble.pink_bubbles[0], Bubble.red_bubbles[0]], + speed=2, repeat=10): + yield n + self.pop() + + def startnormalbubble(self, dx=0, dy=-1, timeout=800): + self.touchable = 1 + self.gen.append(self.normal_movements(dx=dx, dy=dy, timeout=timeout)) + imglist = GreenAndBlue.normal_bubbles[self.d.bubber.pn] + self.setimages(self.cyclic([imglist[1], + imglist[2], + imglist[1], + imglist[0]])) + + def startsnookerbubble(self, timeout, hspeed, monsterpoplist=None): + self.gen.append(self.snooker_movements(dir=hspeed, timeout=timeout)) + self.gen.append(self.kill_touching_monsters(monsterpoplist)) + colorname = random.choice(Stars.COLORS) + imglist = [('smstar', colorname, k) for k in range(2)] + self.to_front() + s = images.ActiveSprite(images.sprget(imglist[-1]), self.x, self.y) + s.setimages(s.cyclic(imglist, speed=2)) + s.gen.append(s.following(self)) + def to_front(): + Bubble.to_front(self) + s.to_front() + self.to_front = to_front + + def snooker_movements(self, dir, dy=0.3, timeout=500): + icons = [images.sprget(n) + for n in GreenAndBlue.normal_bubbles[self.d.bubber.pn]] + icotimeout = 0 + ico = icons[1] + yfrac = 0.0 + self.dragon_jumped = False + for i in xrange(timeout): + hspeed = random.randrange(2, 4) + if ico is not icons[1]: + icotimeout += 1 + if icotimeout >= 16: + ico = icons[1] + icotimeout = 0 + elif abs(dy) > 16: + if dy > 0: + dy = 16.0 + else: + dy = -16.0 + self.touchable = 1 + if self.dragon_jumped: + self.untouchable() + if self.dragon_jumped[1]: # bottom-up dragon + dy = -abs(dy) + else: + dy = abs(dy) + self.dragon_jumped = False + stepx = 0 + y1 = (self.y + 27) // CELL + if dir < 0: + x1 = (self.x + 5) // CELL + if bget(x1, y1) == ' ' == bget(x1, y1-1): + stepx = -hspeed + else: + ico = icons[0] + dir = 1 + else: + x1 = (self.x + 26) // CELL + if bget(x1, y1) == ' ' == bget(x1, y1-1): + stepx = hspeed + else: + ico = icons[0] + dir = -1 + x = self.x + deltay = yfrac + if x < 32: + x += hspeed + dir = 1 + elif x > boards.bwidth - 64: + x -= hspeed + dir = -1 + else: + x += stepx + dy += 0.21 + deltay = yfrac + dy + y = self.y + while deltay >= 1.0: + deltay -= 1.0 + if onground(x, y-4): + ico = icons[2] + deltay = -deltay + dy = -abs(dy) + else: + y += 1 + while deltay < 0.0: + deltay += 1.0 + if underground(x, y+4): + ico = icons[2] + dy = abs(dy) * 0.95 + break + y -= 1 + self.move(x, y, ico) + self.vertical_warp() + yfrac = deltay + yield None + self.pop() + + def kill_touching_monsters(self, poplist=None): + from monsters import Monster + poplist = poplist or [self.d] + while 1: + yield None + for s in self.touching(10): + if isinstance(s, Monster): + s.argh(poplist) + yield None + + +class NormalBubble(Bubble): + warp = 1 + + def __init__(self, dragon, x, y, timeout=800): + imglist1 = GreenAndBlue.new_bubbles[dragon.bubber.pn] + Bubble.__init__(self, images.sprget(imglist1[0]), x, y) + self.d = dragon + self.startnormalbubble(timeout=timeout) + +class SnookerBubble(Bubble): + warp = 1 + touchable = 0 + + def __init__(self, dragon, x, y, timeout=500): + imglist1 = GreenAndBlue.new_bubbles[dragon.bubber.pn] + Bubble.__init__(self, images.sprget(imglist1[0]), x, y) + self.d = dragon + self.startsnookerbubble(timeout=timeout, hspeed=dragon.dir) + + +class BigBubbleCatcher(ActiveSprite): + numlist = [(PlayerBubbles.explosion[2], 1), + (PlayerBubbles.explosion[1], 2), + (PlayerBubbles.explosion[0], 2), + (PlayerBubbles.bubble[1], 5), + (PlayerBubbles.appearing[4], 3), + (PlayerBubbles.appearing[3], 3), + (PlayerBubbles.appearing[2], 2), + (PlayerBubbles.appearing[1], 2), + (PlayerBubbles.appearing[0], 2)] + + def __init__(self, dragon, target, timeout): + img = images.sprget(PlayerBubbles.explosion[2]) + ActiveSprite.__init__(self, img, -img.w, 0) + self.dragon = dragon + self.target = target + self.timeout = timeout + self.gen.append(self.follow()) + self.recenter(PlayerBubbles.explosion[2]) + for imgnum, delay in self.numlist: + images.sprget(imgnum) # preload + + def recenter(self, imgnum): + s = self.target + if not s.alive or not s.touchable: + self.kill() + else: + img = images.sprget(imgnum) + self.move(s.x + (s.ico.w - img.w) // 2, + s.y + (s.ico.h - img.h) // 2, + img) + + def follow(self): + for imgnum, delay in self.numlist: + for t in range(delay): + self.recenter(imgnum) + yield None + self.recenter(imgnum) + if self.alive: + s = self.target + s.in_bubble(NormalBubble(self.dragon, s.x, s.y, self.timeout)) + self.kill() + + +class CatchNote: + def __init__(self, points): + self.points = points + def endaction(self, dragon): + pass + + +class DragonBubble(Bubble): + touchable = 0 + + def __init__(self, d, x, y, dir, special_bubble=None, angle=0, + thrustfactor=None, shootthrust=None): + self.d = d + pn = d.bubber.pn + imglist1 = GreenAndBlue.new_bubbles[pn] + imglist2 = GreenAndBlue.normal_bubbles[pn] + if angle: + asin, acos = math.sin(angle), math.cos(angle) + else: + asin, acos = 0, 1 + Bubble.__init__(self, images.sprget(imglist1[0]), x + 12*dir, y) + self.setimages(self.imgseq(imglist1[1:] + imglist2[2:3], 4)) + if shootthrust is None: + shootthrust = d.dcap['shootthrust'] + hspeed = dir * shootthrust + if thrustfactor is not None: + negative = hspeed < 0 + hspeed = (abs(hspeed) - 4.0) * thrustfactor + 4.0 + if negative: + hspeed = -hspeed + self.gen.append(self.throw_bubble(hspeed, special_bubble, (acos,asin))) + + def throw_bubble(self, hspeed, special_bubble=None, (acos,asin)=(1,0)): + from monsters import Monster + nx = self.x + ny = self.y + stop = 0 + withmonster = 0 + specialangle = (acos,asin) != (1,0) + if special_bubble == 'BigFireBubble': + if not specialangle: + BigFireBubble(self.x, self.y, hspeed, self.d) + self.kill() + return + + self.warp = 0 + monsterpoplist = [self.d] + while abs(hspeed) >= 4.0: + touched_monsters = [s for s in self.touching(9) + if isinstance(s, Monster)] + if touched_monsters: + if special_bubble == 'SnookerBubble': + for monster in touched_monsters: + monster.argh(monsterpoplist) + else: + monster = random.choice(touched_monsters) + in_bubble = monster.in_bubble(self) + withmonster = self.withmonster = 1 + if in_bubble is None: + self.warp = 1 + try: + key = monster.mdef.jailed[0] + except AttributeError: + pass + else: + s_monster = self.d.bubber.stats.setdefault( + 'monster', {}) + s_monster[key] = s_monster.get(key, 0) + 1 + break + if specialangle: + nx, ny = vertical_warp(nx + hspeed*acos, ny + hspeed*asin) +## if moebius: +## acos = -acos + else: + nx += hspeed + hspeed *= 0.965 + xc = int(nx-3.8)//CELL+1 + yc = (self.y+HALFCELL)//CELL + if bget(xc,yc) == '#' == bget(xc, yc+1): + stop += 1 + if stop <= 1: + self.move(int(nx+0.5), int(ny+0.5)) + yield None + + if special_bubble == 'SnookerBubble': + if stop > 1: + hspeed = -hspeed + self.startsnookerbubble(self.d.dcap['bubbledelay'] or 800, + hspeed, monsterpoplist) + return + if not withmonster: + from bonuses import Bonus, BonusMaker + touched_bonuses = [s for s in self.touching(15) + if isinstance(s, Bonus) and s.bubblable] + if touched_bonuses: + random.choice(touched_bonuses).in_bubble(self) + withmonster = 1 + else: + touched_bonuses = [s for s in self.touching(7) + if isinstance(s, BonusMaker)] + if touched_bonuses: + bonusmaker = random.choice(touched_bonuses) + if bonusmaker.in_bubble(self): + withmonster = 1 + if not self.alive: + return + if special_bubble: + cls = globals()[special_bubble] + if not withmonster: + b = cls(self.d.bubber.pn) + b.move(self.x, self.y) + b.can_catch_dragons(self.d, hspeed == 0) + self.kill() + return + bubbledelay = self.d.dcap['bubbledelay'] + if bubbledelay: + timeout = 1 + if bubbledelay > 1: + self.gen.append(self.delayed_pop(7)) + else: + timeout = 800 + self.startnormalbubble(timeout=timeout) + if not withmonster: + self.can_catch_dragons(self.d, hspeed == 0) + + def delayed_pop(self, delay): + for i in range(delay): + yield None + self.pop() + + +class FishBubble(Bubble): + touchable = 0 + + def __init__(self, dragon): + ico = images.sprget(GreenAndBlue.new_bubbles[dragon.bubber.pn][0]) + Bubble.__init__(self, ico, dragon.x + dragon.dir*12, dragon.y) + timeout = random.randrange(50, 150) + self.gen.append(self.normal_movements(timeout=timeout)) + self.gen.append(self.fuzz()) + + def fuzz(self): + while 1: + prevx = self.x + yield None + yield None + yield None + if prevx == self.x: + self.step(random.choice([-1, 1]), 0) + + def bubble_red(self, *args, **kwds): + self.gen.append(self.die([])) + + +class BubblingEyes(ActiveSprite): + + def __init__(self, bubber, saved_caps, bubble): + ico = images.sprget(('eyes', 0, 0)) + ActiveSprite.__init__(self, ico, bubble.x, bubble.y) + self.bubber = bubber + self.dcap = saved_caps + self.gen = [self.playing_bubble(bubble)] + + def bottom_up(self): + return self.dcap['gravity'] < 0.0 + + def playing_bubble(self, bubble): + from player import Dragon + bottom_up = self.bottom_up() + flip = 'vflip'*bottom_up + timer = 0 + red = 0 + normalbub = bubble.imgsetter + redblinker = bubble.cyclic([Bubble.pink_bubbles[0], Bubble.red_bubbles[0]], 2) + bubber = self.bubber + ndir = random.choice([-1, 1]) + prev_dx_dy = None + while not hasattr(bubble, 'poplist'): + dx = bubber.wannago(self.dcap) + if dx: + ndir = dx + if bubber.key_jump: + dy = -1 + else: + dy = 0 + if bubber.key_fire: + red += 1 + if red > 20: + d = Dragon(bubber, self.x, self.y, ndir, self.dcap) + Bubble.pop(bubble, [d]) # hack to pop SolidBubbles too + d.kill() + break + if bubble.imgsetter is not redblinker: + normalbub = bubble.imgsetter + bubble.setimages(redblinker) + else: + #red = 0 + if bubble.imgsetter is redblinker: + bubble.setimages(normalbub) + key = ('eyes', dx, dy) + if timer < 50: + if (timer % 9) < 3: + key = 'eyes-blink' + elif random.random() < 0.1: + key = 'eyes-blink' + timer += 1 + if bubble.x <= 3*HALFCELL and dx < 0: + dx = 0 + if bubble.x >= boards.bwidth - 7*HALFCELL and dx > 0: + dx = 0 + if bottom_up: + dy = -dy + nx = bubble.x + dx + ny = bubble.y + dy + if timer&1: + nx += dx + else: + ny += dy + nx, ny = vertical_warp(nx, ny) + bubble.move(nx, ny) + self.move(nx+dx, ny+dy, images.sprget((flip, key))) +## if moebius: +## self.dcap['left2right'] *= -1 + if dx == dy == 0: + bubble.default_windless = prev_dx_dy + else: + prev_dx_dy = dx, dy + bubble.default_windless = 0, 0 + yield None + # jumping out of the bubble + if bottom_up: + kw = {'gravity': -0.3} + else: + kw = {} + from player import BubPlayer + bi = self.dcap.get('bubbericons', bubber) + if BubPlayer.SuperFish and 'fish' in bi.transformedicons: + self.setimages(None) + self.seticon(bi.transformedicons['fish'][0, +1]) + else: + self.setimages(self.cyclic( + [(flip, n) for n in GreenAndBlue.comming[bi.pn]], 2)) + dxy = [(random.random()-0.5) * 9.0, + (random.random()+0.5) * (-5.0,5.0)[bottom_up]] + for n in self.parabolic(dxy, 1, **kw): + yield n + if dxy[1] * (1,-1)[bottom_up] >= 4.0: + break + if dxy[0] < 0: + ndir = -1 + else: + ndir = 1 + d = Dragon(bubber, self.x, self.y, ndir, self.dcap) + d.dcap['shield'] = 50 + bubber.dragons.append(d) + self.kill() + + def kill(self): + try: + self.bubber.dragons.remove(self) + except ValueError: + pass + ActiveSprite.kill(self) + + +class BonusBubble(Bubble): + max = None + timeout = None + flip = '' + + def __init__(self, pn, nimages=None, top=None): + if nimages is None: + nimages = self.nimages[pn] + b = boards.curboard + if top is None: + top = b.top + if top == 0: + testline = b.walls[-1] + x, y = self.findhole(testline), boards.bheight + dx, dy = 0, -1 + elif top == 1: + testline = b.walls[0] + x, y = self.findhole(testline), -2*CELL + dx, dy = 0, 1 + elif top == 2: + x, y = -2*CELL, random.randint(2*CELL, boards.bheight-4*CELL) + dx, dy = 1, 0 + else: # top == 3: + x, y = (boards.bwidth - CELL, + random.randint(2*CELL, boards.bheight-4*CELL)) + dx, dy = -1, 0 + Bubble.__init__(self, images.sprget((self.flip, nimages[0])), x, y) + self.gen.append(self.normal_movements(dx=dx, dy=dy, + timeout=self.timeout)) + if len(nimages) == 3: + nimages = [nimages[1], nimages[2], nimages[1], nimages[0]] + if len(nimages) > 1: + self.setimages(self.cyclic([(self.flip, n) for n in nimages])) + + def findhole(self, testline): + holes = [x for x in range(len(testline)-1) if testline[x:x+2]==' '] + if not holes: + holes = range(2, len(testline)-3) + return random.choice(holes) * CELL + + def thrown_bubble(self, x, y, hspeed, acossin): + self.untouchable() + self.move(x, y) + self.gen = [self.throwing_bubble(hspeed, acossin, self.imgsetter)] + + def throwing_bubble(self, hspeed, (acos,asin), restore_img): + nx = self.x + ny = self.y + while abs(hspeed) >= 4.0: + nx, ny = vertical_warp(nx + hspeed*acos, ny + hspeed*asin) +## if moebius: +## acos = -acos + if nx <= CELL: + acos = abs(acos) + if nx >= boards.bwidth-3*CELL: + acos = -abs(acos) + hspeed *= 0.965 + self.move(int(nx+0.5), int(ny+0.5)) + yield None + self.touchable = 1 + self.gen.append(self.normal_movements(timeout=self.timeout)) + self.setimages(restore_img) + + +class PlainBubble(BonusBubble): + timeout = 500 + def condition(): + return boards.curboard.holes + +def extend_name(l): + text = 'extend' + return text[:l] + text[l].upper() + text[l+1:] + +class LetterBubble(BonusBubble): + max = 2 + def condition(): + return boards.curboard.letter + def __init__(self, pn, l=None): + if l is None: + l = random.randint(0,5) + self.l = l + lettername = extend_name(self.l) + BonusBubble.__init__(self, pn, nimages=getattr(LetterBubbles, lettername)) + def popped(self, dragon): + if dragon: + dragon.bubber.giveletter(self.l) + return 50 + +class FireFlame(ActiveSprite): + timeout = 17 + def __init__(self, x0, y0, poplist, dirs=None, countdown=0, flip=''): + ico = images.sprget((flip, Fire.ground[0])) + ActiveSprite.__init__(self, ico, x0*CELL, y0*CELL) + if not countdown: + dirs = [] + self.poplist = poplist + self.gen.append(self.burning(dirs, countdown)) + self.setimages(self.cyclic([(flip, n) for n in Fire.ground], 1)) + def burning(self, dirs, countdown): + from monsters import Monster + x0 = self.x//CELL + y0 = self.y//CELL + for dir in dirs: + if bget(x0+dir, y0+1) == '#' and bget(x0+dir, y0) == ' ': + FireFlame(x0+dir, y0, self.poplist, [dir], countdown-1) + for i in range(self.timeout): + yield None + if self.poplist: + for s in self.touching(0): + if isinstance(s, Monster): + s.argh(self.poplist) + yield None + self.kill() + +class FireDrop(ActiveSprite): + def __init__(self, x, y, poplist=None): + ActiveSprite.__init__(self, images.sprget(Fire.drop), x, y) + self.poplist = poplist or [None] + self.gen.append(self.dropping()) + def dropping(self): + x0 = self.x//CELL + while bget(x0, self.y//CELL) == '#' or bget(x0, self.y//CELL+1) != '#': + if self.y >= boards.bheight: + self.kill() + return + self.move(self.x, (self.y + 8) & ~7) + yield None + y0 = self.y//CELL + #if bget(x0-1, y0) == ' ': + FireFlame(x0, y0, self.poplist, [-1, 1], 5) + self.kill() + +class FireBubble(BonusBubble): + max = 4 + nimages = GreenAndBlue.fire_bubbles + def condition(): + return boards.curboard.fire + def popped(self, dragon): + if dragon: + x0 = self.x // CELL + 1 + y0 = self.y // CELL + 1 + if bget(x0, y0) == '#': + x1 = (self.x + HALFCELL) // CELL + if x1 == x0: + tries = [x1+1, x1-1] + else: + tries = [x1, x1+2] + for x1 in tries: + if bget(x1, y0) == ' ': + x0 = x1 + break + FireDrop(x0*CELL, self.y) + return 10 + +##class BombBubble(FireBubble): +## flip = 'vflip' +## def popped(self, dragon): +## if dragon: +## import bonuses +## bonuses.bomb_explosion(self.x, self.y + CELL, starmul=1) +## return 100 + +##class WaterCell(ActiveSprite): +## ICONS = { +## ( 0,1, None) : Water.bottom, +## ( 1,0, None) : Water.start_left, +## (-1,0, None) : Water.start_right, +## ( 0,0, None) : Water.bottom, + +## (0,1, 0,1) : Water.v_flow, +## (0,1, 1,0) : Water.bl_corner, +## (0,1, -1,0) : Water.br_corner, + +## (-1,0, 0,1) : Water.tl_corner, +## (-1,0, 1,0) : Water.start_right, +## #(-1,0, -1,0) : Water.h_flow, + +## (1,0, 0,1) : Water.tr_corner, +## #(1,0, 1,0) : Water.h_flow, +## (1,0, -1,0) : Water.start_left, + +## (0,0, 0,1) : Water.top, +## (0,0, 1,0) : Water.top, +## (0,0, -1,0) : Water.top, + +## (None, 0,1) : Water.top, +## (None, -1,0) : Water.start_left, +## (None, 1,0) : Water.start_right, +## (None, 0,0) : Water.top, +## } + +## def __init__(self, x, y): +## ActiveSprite.__init__(self, images.sprget(Water.top), x, y) +## self.touchable = 1 + +## def ready(self, celllist): +## self.gen.append(self.flooding(celllist)) + +## def flooding(self, celllist): +## from monsters import Monster +## x0 = self.x // 16 +## y0 = self.y // 16 +## ping = 0 +## dir = random.choice([-1, 1]) +## take_with_us = [[] for cell in celllist] +## poplist = [None] +## icons = {} +## for key, value in self.ICONS.items(): +## icons[key] = images.sprget(value) +## icodef = images.sprget(Water.h_flow) +## stop = 0 +## while not stop: +## dx = dy = 0 +## if bget(x0, y0+1) == ' ': +## dy = y0*16 < boards.bheight +## ping = 0 +## elif bget(x0+dir, y0) == ' ': +## dx = dir +## elif bget(x0-dir, y0) == ' ': +## ping += 1 +## if ping < 3: +## dir = -dir +## dx = dir +## # change the head icon +## head = celllist[0] +## second = celllist[1] +## head.seticon(icons.get((x0-second.x//16, y0-second.y//16, +## dx, dy), icodef)) +## # move the tail to the new head position +## x0 += dx +## y0 += dy +## newhead = celllist.pop() +## celllist.insert(0, newhead) +## newhead.move(x0*16, y0*16, icons.get((dx,dy, None), icodef)) +## # change the new tail icon +## tail = celllist[-1] +## second = celllist[-2] +## tail.seticon(icons.get((None, (second.x-tail.x)//16, +## (second.y-tail.y)//16), icodef)) +## # take monsters with us +## for i in range(0, len(celllist), 3): +## for s in celllist[i].touching(0): +## if isinstance(s, Monster): +## s.untouchable() +## s.gen = [] +## take_with_us[i].append(s) +## elif isinstance(s, Bubble): +## s.pop(poplist) +## yield 0 +## stop = dx == dy == 0 +## for cell, takelist in zip(celllist, take_with_us): +## stop &= cell.x == newhead.x and cell.y == newhead.y +## for s in takelist: +## if s.alive: +## s.move(x2bounds(cell.x-8), cell.y-16) +## if stop: +## s.argh(poplist, onplace=1) +## for c in celllist: +## c.kill() +## def touched(self, dragon): +## dragon.watermove(x2bounds(self.x-HALFCELL), self.y-CELL+1) +## return 1 + +class WaterCell(ActiveSprite): + TESTLIST = [(-CELL,0), (CELL,0), (0,CELL), (0,-CELL)] + ICONS = [Water.v_flow, + Water.start_left, + Water.start_right, + Water.h_flow, + Water.top, + Water.tr_corner, + Water.tl_corner, + Water.h_flow, + + Water.bottom, + Water.br_corner, + Water.bl_corner, + Water.h_flow, + Water.v_flow, + Water.v_flow, + Water.v_flow, + Water.v_flow] + + def __init__(self, x, y, dir, watercells, poplist, repeat): + ActiveSprite.__init__(self, images.sprget(Water.top), x, y) + self.poplist = poplist + self.take_with_me = [] + self.ping = 0 + self.repeat = repeat + self.watercells = watercells + self.touchable = repeat % 3 == 1 + if (x, y, dir) not in watercells: + watercells[x,y,dir] = self + if None not in watercells or not watercells[None].alive: + self.in_charge() + else: + watercells[x,y,dir].join(self) + + def join(self, other): + self.take_with_me += other.take_with_me + self.ping = min(self.ping, other.ping) + self.repeat += other.repeat + self.touchable = self.touchable or other.touchable + del other.take_with_me[:] + other.kill() + + def in_charge(self): + self.gen = [self.flooding()] + self.watercells[None] = self + + def kill(self): + from monsters import Monster + for s in self.take_with_me[:]: + if isinstance(s, Monster) and s.alive: + s.argh(self.poplist, onplace=1) + del self.take_with_me[:] + ActiveSprite.kill(self) + if not self.watercells[None].alive: + del self.watercells[None] + for s in self.watercells.values(): + if s.alive: + s.in_charge() + break + + def flooding(self): + from monsters import Monster + watercells = self.watercells + while watercells[None] is self: + + new = [] + nwatercells = {None: self} + for key, s in watercells.items(): + if key: + x, y, dir = key + if s.repeat: + new.append((x, y, dir, watercells, + s.poplist, s.repeat-1)) + s.repeat = 0 + x0 = x // CELL + y0 = y // CELL + if bget(x0, y0+1) == ' ': + if y >= boards.bheight: + s.kill() + continue + s.ping = 0 + y += CELL + elif bget(x0+dir, y0) == ' ': + x += dir*CELL + elif bget(x0-dir, y0) == ' ': + s.ping += 1 + if s.ping == 3: + s.kill() + continue + dir = -dir + x += dir*CELL + else: + s.kill() + continue + key = x, y, dir + if key in nwatercells: + nwatercells[key].join(s) + else: + nwatercells[key] = s + + watercells.clear() + watercells.update(nwatercells) + for args in new: + WaterCell(*args) + + for key, s in watercells.items(): + if key: + x, y, dir = key + flag = 0 + for k in range(4): + dx, dy = s.TESTLIST[k] + if ((x+dx, y+dy, -1) in watercells or + (x+dx, y+dy, 1) in watercells): + flag += 1<<k + ico = images.sprget(s.ICONS[flag]) + s.move(x, y, ico) + if s.touchable: + for s1 in s.touching(0): + if isinstance(s1, Monster): + s1.untouchable() + s1.gen = [] + s.take_with_me.append(s1) + elif isinstance(s1, Bubble): + s1.pop(s.poplist) + for s1 in s.take_with_me: + if s1.alive: + s1.move(x2bounds(x-HALFCELL), y-CELL) + yield None + if not watercells[None].alive: + self.in_charge() + + def touched(self, dragon): + dragon.watermove(x2bounds(self.x-HALFCELL), self.y-CELL+1) + return 1 + +def watercell(x, y, poplist, dir=None, repeat=4): + b = boards.curboard + if not hasattr(b, 'watercells'): + b.watercells = {} + dir = dir or random.choice([-1, 1]) + WaterCell(x, y, dir, b.watercells, poplist, repeat) + +class WaterBubble(BonusBubble): + max = 4 + nimages = GreenAndBlue.water_bubbles + def condition(): + return boards.curboard.water + def popped(self, dragon): + if dragon: + x0 = self.x // CELL + 1 + y0 = self.y // CELL + 1 + for x1 in [x0, x0+1, x0-1]: + if bget(x1,y0) == ' ' or bget(x1,y0+1) == ' ': + x0 = x1 + break + watercell(x0*CELL, y0*CELL, [None], repeat=19) + return 10 + +##class SolidBubble(WaterBubble): +## timeout = 450 +## solidbubble = 1 +## flip = 'vflip' + +## def bubble_red(self, *args): +## self.solidbubble = 0 +## return WaterBubble.bubble_red(self, *args) + +## def pop(self, poplist=None): +## return (not (self.solidbubble and poplist is not None) +## and WaterBubble.pop(self, poplist)) + +## def popped(self, dragon): +## return 100 + +class FiredLightning(ActiveSprite): + def __init__(self, x, y, dir, poplist, diry=0): + ActiveSprite.__init__(self, images.sprget(Lightning.fired), x, y) + self.dir = int(13*dir) + self.diry = int(13*diry) + self.gen.append(self.moving(poplist)) + def moving(self, poplist): + from monsters import Monster + while (-2*CELL < self.x < boards.bwidth and + -2*CELL < self.y < boards.bheight): + for s in self.touching(2): + if isinstance(s, Monster): + s.argh(poplist) + elif isinstance(s, Bubble): + s.pop(poplist) + self.step(self.dir, self.diry) + yield None + self.kill() + +class LightningBubble(BonusBubble): + max = 4 + nimages = GreenAndBlue.light_bubbles + def condition(): + return boards.curboard.lightning + def popped(self, dragon): + if dragon: + FiredLightning(self.x, self.y, -dragon.dir, self.poplist) + return 10 + +class BigLightBubble(LightningBubble): + flip = 'vflip' + def popped(self, dragon): + base = random.random() * 2.0*math.pi + for a in range(7): + FiredLightning(self.x, self.y, math.cos(base+a), self.poplist, + diry = -math.sin(base+a)) + return 100 + + +class BigFireBubble(ActiveSprite): + touchable = 1 + + def __init__(self, x, y, hspeed, author): + if hspeed > 0: + imgs = PlayerBubbles.right_weapon + else: + imgs = PlayerBubbles.left_weapon + ActiveSprite.__init__(self, images.sprget(imgs[-1]), x, y) + self.setimages(self.cyclic(imgs, 2)) + self.author = author + self.gen.append(self.moving(hspeed)) + self.play(images.Snd.Shh) + + def moving(self, hspeed): + from monsters import Monster + if abs(hspeed) < 3: + if hspeed > 0: + hspeed = 3 + else: + hspeed = -3 + fx = self.x + poplist = [self.author] + while 1: + fx += hspeed + self.move(int(fx), self.y) + xc = int(fx)//CELL + 1 + yc = (self.y+HALFCELL)//CELL + if bget(xc,yc) == '#' == bget(xc, yc+1): + break + yield None + for s in self.touching(4): + if isinstance(s, Monster): + s.argh(poplist) + self.kill() + + def touched(self, dragon): + if dragon is not self.author: + import bonuses + bonuses.repulse_dragon(dragon) + + +class SpinningBall(ActiveSprite): + def __init__(self, x, y, poplist): + ActiveSprite.__init__(self, images.sprget(SpinningBalls.free[-1]), x,y) + self.poplist = poplist + self.gen.append(self.dropping()) + imgs = SpinningBalls.free + if random.random() < 0.5: + imgs = list(imgs) + imgs.reverse() + self.setimages(self.cyclic(imgs, random.randrange(2,5))) + self.touchable = 1 + def dropping(self): + from monsters import Monster + for ny in range(self.y, boards.bheight, 5): + self.move(self.x, ny) + yield None + for s in self.touching(0): + if isinstance(s, Monster): + s.argh(self.poplist) + elif isinstance(s, Bubble): + s.pop(self.poplist) + self.kill() + def touched(self, dragon): + dragon.die() + +class StarBubble(BonusBubble): + timeout = 250 + def __init__(self, pn): + self.colorname = random.choice(Stars.COLORS) + BonusBubble.__init__(self, pn, [('starbub', self.colorname, i) + for i in range(3)]) +## def __init__(self, pn): +## BonusBubble.__init__(self, pn) +## self.colorname = random.choice(Stars.COLORS) +## starimg = [('smstar', self.colorname, 0), +## ('smstar', self.colorname, 1)] +## smallstar = ActiveSprite(images.sprget(starimg[-1]), +## self.x+8, self.y+8) +## smallstar.setimages(smallstar.cyclic(starimg)) +## smallstar.gen.append(smallstar.following(self, 8, 8)) + def popped(self, dragon): + if dragon: + from bonuses import BonusMaker, AllOutcomes, Parabolic2 + BonusMaker(self.x, self.y, getattr(Stars, self.colorname), + outcome=random.choice(AllOutcomes)) + for i in range(2): + Parabolic2(self.x, self.y, [('smstar', self.colorname, i) + for i in range(2)]) + return 100 + +class MonsterBubble(BonusBubble): + timeout = 100 + def __init__(self, pn, mcls): + import monsters, mnstrmap + BonusBubble.__init__(self, pn) + mdef = getattr(mnstrmap, mcls.__name__) + m = mcls(mdef, self.x, self.y, 1) + m.in_bubble(self) + +class MoreBubblesBubble(BonusBubble): + + def can_catch_dragons(self, author, catch_myself=0): + # at this point, explode the bubble into more bubbles + d = author + for angle in [math.pi, math.pi/2, -math.pi/2, 0]: + for factor in [0.2, 0.55, 0.9, 1.25, 1.6]: + DragonBubble(d, self.x, self.y, d.dir, + angle = angle, thrustfactor = factor) + self.pop() + + +Classes = ([PlainBubble] * 7 + + [FireBubble, WaterBubble, LightningBubble] * 4 + + [LetterBubble]) + +def newbubble(): #force=0): + #if force: + #cls = PlainBubble + #else: + cls = random.choice(Classes) + if not cls.__dict__['condition'](): + return + if cls.max is not None: + others = [s for s in images.ActiveSprites if isinstance(s, cls)] + if len(others) >= cls.max: + return + sendbubble(cls) + +def newforcedbubble(): + choices = [PlainBubble] * 5 + for cls in [FireBubble, WaterBubble, LightningBubble]: + if cls.__dict__['condition'](): + n = 4 + else: + n = 1 + choices.extend([cls] * n) + cls = random.choice(choices) + return sendbubble(cls) + +def sendbubble(cls, *args, **kw): + from player import BubPlayer + players = [p for p in BubPlayer.PlayerList if p.isplaying()] + if not players: + return None + pn = random.choice(players).pn + return cls(pn, *args, **kw) + +def newbonusbubble(): + boards.curboard.top = random.choice([0,0,0, 1,1,1, 2,2, 3,3]) + r = random.random() + if r < 0.14: + sendbubble(random.choice(Classes)) + elif r < 0.16: + from player import BubPlayer + import monsters + mcls = random.choice(monsters.MonsterClasses) + for d in BubPlayer.DragonList: + sendbubble(MonsterBubble, mcls) + else: + sendbubble(StarBubble) |