summaryrefslogtreecommitdiff
path: root/bubbob/ext4/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'bubbob/ext4/__init__.py')
-rw-r--r--bubbob/ext4/__init__.py440
1 files changed, 440 insertions, 0 deletions
diff --git a/bubbob/ext4/__init__.py b/bubbob/ext4/__init__.py
new file mode 100644
index 0000000..1efa86e
--- /dev/null
+++ b/bubbob/ext4/__init__.py
@@ -0,0 +1,440 @@
+from __future__ import generators
+import os, math, random
+import images, gamesrv
+from images import ActiveSprite
+from boards import CELL, HALFCELL, bget
+from mnstrmap import GreenAndBlue
+from bubbles import BubblingEyes, Bubble
+from bonuses import Bonus, points
+
+LocalDir = os.path.basename(os.path.dirname(__file__))
+
+
+localmap = {
+ 't-brick1': ('image1-%d.ppm', (0, 0, 16, 16)),
+ 't-brick2': ('image1-%d.ppm', (16, 0, 16, 16)),
+ }
+
+music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav'))
+
+
+class BrickEyes(BubblingEyes):
+
+ Patterns = [[(-2,0), (-1,0), (0,0), (1,0)],
+ [(-2,0), (-1,0), (0,0), (0,-1)],
+ [(-1,-1), (-1,0), (0,0), (1,0)],
+ [(-1,0), (0,0), (0,-1), (1,0)],
+ [(-1,-1), (0,-1), (0,0), (1,0)],
+ [(-2,0), (-1,0), (-1,-1), (0,-1)],
+ [(-1,-1), (-1,0), (0,-1), (0,0)]]
+
+ def __init__(self, tetris, bubber, saved_caps, olddragon):
+ BubblingEyes.__init__(self, bubber, saved_caps, olddragon)
+ self.tetris = tetris
+ self.bricks = []
+
+ def playing_bubble(self, oldsprite):
+ import boards
+ self.pat = random.choice(self.Patterns)
+ self.orientation = 1,0
+ xmin = 2 - min([x for x,y in self.pat])
+ xmax = boards.width-3 - max([x for x,y in self.pat])
+ x = int(random.normalvariate(oldsprite.x, boards.bwidth/4))
+ x = (x+HALFCELL) // CELL
+ if x<xmin: x=xmin
+ if x>xmax: x=xmax
+ y = -1
+ self.tx = x
+ self.ty = y
+ self.move((x-1)*CELL, (y-1)*CELL)
+ for i in range(5):
+ yield None
+ self.bricks = [Brick(self.bubber, px, py)
+ for px, py in self.brick_positions()]
+ self.gen.append(self.step_control())
+ self.gen.append(self.rotate_control())
+ self.gen.append(self.fall_control())
+ self.gen.append(self.move_eyes())
+
+ def bottom_up(self):
+ return 0
+
+ def kill(self):
+ for b in self.bricks:
+ b.stop(self.tetris)
+ b.remove()
+ self.bricks = []
+ BubblingEyes.kill(self)
+
+ def brick_positions(self):
+ ox, oy = self.orientation
+ result = []
+ cx = self.tx*CELL - HALFCELL
+ cy = self.ty*CELL - HALFCELL
+ for px, py in self.pat:
+ px = px*CELL + HALFCELL
+ py = py*CELL + HALFCELL
+ result.append((cx+px*ox-py*oy, cy+px*oy+py*ox))
+ return result
+
+ def save_position(self):
+ return self.tx, self.ty, self.orientation
+
+ def restore_position(self, p):
+ self.tx, self.ty, self.orientation = p
+
+ def moved(self, old_position):
+ for b in self.bricks:
+ b.set(' ')
+ try:
+ for px, py in self.brick_positions():
+ if bget(px//CELL, py//CELL) != ' ':
+ self.restore_position(old_position)
+ return 0
+ for b, (px, py) in zip(self.bricks, self.brick_positions()):
+ b.follow(px, py)
+ finally:
+ for b in self.bricks:
+ b.set('!') # note: we need '!' < '#'
+ return 1
+
+ def onground(self):
+ for b in self.bricks:
+ if b.gen: # brick still moving
+ return 0
+ for px, py in self.brick_positions():
+ if bget(px//CELL, py//CELL+1) >= '#':
+ return 1
+ return 0
+
+ def step_control(self):
+ while 1:
+ while not self.bubber.wannago(self.dcap):
+ yield None
+ pos = self.save_position()
+ self.tx += self.bubber.wannago(self.dcap)
+ if self.moved(pos):
+ for i in range(4):
+ yield None
+ yield None
+
+ def fall_control(self):
+ delay = 1
+ while 1:
+ for i in range(delay and 14):
+ if self.bubber.key_fire:
+ break
+ yield None
+ pos = self.save_position()
+ self.ty += 1
+ delay = self.moved(pos)
+ if delay:
+ for i in range(3):
+ yield None
+ elif self.onground() and self.tetris.ready:
+ self.gen = [self.stopping()]
+ yield None
+
+ def rotate_control(self):
+ while 1:
+ while not self.bubber.key_jump:
+ yield None
+ pos = self.save_position()
+ ox, oy = self.orientation
+ self.orientation = oy, -ox
+ if self.moved(pos):
+ for i in range(7):
+ yield None
+ yield None
+
+ def stopping(self):
+ self.move(self.x, -self.ico.h)
+ positions = [(py//CELL, px//CELL) for px, py in self.brick_positions()
+ if py >= 0]
+ positions.sort()
+ positions = [(px, py) for py, px in positions]
+ for b in self.bricks:
+ b.stop(self.tetris)
+ if b.ty < 0:
+ b.remove()
+ self.bricks = []
+ staticbricks = self.tetris.staticbricks
+ pts = 500
+ while 1:
+ for px, py in positions:
+ y = py
+ x1 = px
+ while (x1-1, y) in staticbricks:
+ x1 -= 1
+ if bget(x1-1, y) != '#':
+ continue
+ x2 = px
+ while (x2, y) in staticbricks:
+ x2 += 1
+ if bget(x2, y) != '#':
+ continue
+ if x2-x1 < 2:
+ continue
+ # full line
+ ico = images.sprget(Bubble.exploding_bubbles[0])
+ self.tetris.score[self.bubber] = self.tetris.score.get(
+ self.bubber, 0) + 1
+ xlist = range(x1, x2)
+ for x in xlist:
+ s = ActiveSprite(ico,
+ x*CELL + random.randrange(CELL) - CELL,
+ y*CELL + random.randrange(CELL) - CELL)
+ s.gen.append(s.die(Bubble.exploding_bubbles))
+ s = staticbricks[x, y]
+ points(x*CELL + HALFCELL, y*CELL + HALFCELL, s, pts)
+ s.remove()
+ if pts == 500:
+ self.play(images.Snd.Fruit)
+ elif pts == 4000:
+ self.play(images.Snd.Extralife)
+ else:
+ self.play(images.Snd.Extra)
+ pts *= 2
+ for y in range(py-1, -1, -1):
+ if not [x for x in xlist if (x, y) in staticbricks]:
+ break
+ for t in range(4):
+ yield None
+ if [x for x in xlist if (x, y+1) in staticbricks]:
+ break
+ for x in xlist:
+ if (x, y) in staticbricks:
+ staticbricks[x, y].shiftdown()
+ yield None
+ break
+ else:
+ break
+ if self.tetris.ready < 2:
+ self.gen.append(self.playing_bubble(self))
+
+ def move_eyes(self):
+ while 1:
+ tx = (self.tx-1) * CELL
+ ty = (self.ty-1) * CELL
+ for i in range(3):
+ if tx < self.x:
+ dx = -1
+ elif tx > self.x:
+ dx = +1
+ else:
+ dx = 0
+ if ty > self.y:
+ dy = +1
+ else:
+ dy = 0
+ self.step(2*dx, 2*dy)
+ key = ('eyes', dx, 0)
+ self.seticon(images.sprget(key))
+ yield None
+
+
+class Brick(ActiveSprite):
+
+ def __init__(self, bubber, x, y):
+ ico = images.sprget(('t-brick1', bubber.pn))
+ ActiveSprite.__init__(self, ico, x, y)
+ self.tx = x//CELL
+ self.ty = y//CELL
+ self.bubber = bubber
+
+ def follow(self, x, y):
+ self.tx = x//CELL
+ self.ty = y//CELL
+ self.gen = [self.following(x, y)]
+
+ def following(self, nx, ny):
+ dx = (nx - self.x) / 7.0
+ dy = (ny - self.y) / 7.0
+ for i in range(6, 0, -1):
+ self.move(nx - int(i*dx), ny - int(i*dy))
+ yield None
+ self.move(nx, ny)
+
+ def set(self, c):
+ x, y = self.tx, self.ty
+ if 0 <= x < curboard.width and 0 <= y < curboard.height:
+ line = curboard.walls[y]
+ curboard.walls[y] = line[:x] + c + line[x+1:]
+
+ def stop(self, tetris):
+ self.set('X')
+ self.seticon(images.sprget(('t-brick2', self.bubber.pn)))
+ images.ActiveSprites.remove(self)
+ tetris.staticbricks[self.tx, self.ty] = self
+ self.staticbricks = tetris.staticbricks
+
+ def remove(self):
+ del self.staticbricks[self.tx, self.ty]
+ self.set(' ')
+ gamesrv.Sprite.kill(self)
+
+ def shiftdown(self):
+ del self.staticbricks[self.tx, self.ty]
+ self.set(' ')
+ self.ty += 1
+ self.set('X')
+ self.staticbricks[self.tx, self.ty] = self
+ self.step(0, CELL)
+
+
+class Tetris:
+
+ def bgen(self, limittime = 90.1): # 1:30
+ import boards
+ from player import BubPlayer
+
+ self.score = {}
+ for t in boards.initsubgame(music, self.displaypoints):
+ yield t
+
+ tc = boards.TimeCounter(limittime)
+ self.ready = 0
+ self.staticbricks = {}
+ finished = 0
+ for t in self.frame():
+ t = boards.normal_frame()
+ self.build_eyes()
+ yield t
+ tc.update(t)
+ if tc.time == 0.0:
+ self.ready = 2
+ finished += not self.still_playing()
+ if finished > 16:
+ break
+ if (BubPlayer.FrameCounter & 15) == 7:
+ for s in images.ActiveSprites:
+ if isinstance(s, Bubble):
+ s.pop()
+ elif isinstance(s, Bonus):
+ s.kill()
+
+ tc.restore()
+ for t in boards.result_ranking(self.score):
+ self.remove_eyes()
+ yield t
+ for s in self.staticbricks.values():
+ s.remove()
+
+ def displaypoints(self, bubber):
+ return self.score.get(bubber, 0)
+
+ def frame(self):
+ heights = {1: curboard.height,
+ curboard.width-2: curboard.height}
+ ymax = curboard.height-1
+ maxheight = curboard.height*3//4
+ for x in range(2, curboard.width-2):
+ if bget(x, ymax) == ' ':
+ curboard.putwall(x, ymax)
+ height = 1
+ for y in range(ymax-1, -1, -1):
+ if bget(x, y) == '#':
+ if height == maxheight:
+ curboard.killwall(x, y)
+ else:
+ height += 1
+ heights[x] = height
+ xlist = range(2, curboard.width-2)
+ random.shuffle(xlist)
+ for x in xlist:
+ h = heights[x]
+ x1 = x2 = x
+ while heights[x1-1] == h:
+ x1 -= 1
+ while heights[x2] == h:
+ x2 += 1
+ parts = (x2-x1) // 8
+ if not parts:
+ continue
+ left = 0
+ if heights[x1-1] > h:
+ x1 -= 1
+ left += 1
+ right = parts+1
+ if heights[x2] > h:
+ x2 += 1
+ right -= 1
+ for p in range(left, right):
+ x = x1 + ((x2-x1-1)*p+parts//2)//parts
+ y = ymax
+ for i in range(2):
+ while bget(x, y) == '#':
+ y -= 1
+ if y >= 3:
+ curboard.putwall(x, y)
+ heights[x] += 1
+ curboard.reorder_walls()
+
+ walls_by_pos = curboard.walls_by_pos
+ moves = 1
+ s = 8.0
+ while moves:
+ moves = 0
+ for y in range(curboard.height-3, -1, -1):
+ for x in range(2, curboard.width-2):
+ if ((y,x) in walls_by_pos and
+ (y+1,x) not in walls_by_pos):
+ y0 = y
+ while (y0-1,x) in walls_by_pos:
+ y0 -= 1
+ w = curboard.killwall(x, y0, 0)
+ curboard.putwall(x, y+1, w)
+ moves = 1
+ curboard.reorder_walls()
+ for i in range(int(s)+2):
+ yield None
+ s *= 0.95
+ self.ready = 1
+ while 1:
+ yield None
+
+ def build_eyes(self):
+ from player import BubPlayer
+ for p in BubPlayer.PlayerList:
+ dragons = [d for d in p.dragons if not isinstance(d, BrickEyes)]
+ if dragons and len(p.dragons) == len(dragons):
+ dragon = random.choice(dragons)
+ eyes = BrickEyes(self, p, dragon.dcap, dragon)
+ p.dragons.append(eyes)
+ #p.emotic(dragon, 4)
+ for d in dragons:
+ d.kill()
+
+ def still_playing(self):
+ from player import BubPlayer
+ for p in BubPlayer.PlayerList:
+ for d in p.dragons:
+ if d.gen:
+ return 1
+ return 0
+
+ def remove_eyes(self):
+ from player import BubPlayer
+ for p in BubPlayer.PlayerList:
+ for d in p.dragons:
+ d.kill()
+
+# This game is suitable for at least min_players players
+min_players = 1
+
+def run():
+ global curboard
+ import boards
+ from boards import curboard
+ boards.replace_boardgen(Tetris().bgen())
+
+def setup():
+ from player import BubPlayer
+ for key, (filename, rect) in localmap.items():
+ filename = os.path.join(LocalDir, filename)
+ if filename.find('%d') >= 0:
+ for p in BubPlayer.PlayerList:
+ images.sprmap[key, p.pn] = (filename % p.pn, rect)
+ else:
+ images.sprmap[key] = (filename, rect)
+setup()