summaryrefslogtreecommitdiff
path: root/bubbob/ext2/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'bubbob/ext2/__init__.py')
-rw-r--r--bubbob/ext2/__init__.py578
1 files changed, 578 insertions, 0 deletions
diff --git a/bubbob/ext2/__init__.py b/bubbob/ext2/__init__.py
new file mode 100644
index 0000000..81cd833
--- /dev/null
+++ b/bubbob/ext2/__init__.py
@@ -0,0 +1,578 @@
+from __future__ import generators
+import os, math, random
+import images, gamesrv
+from images import ActiveSprite
+from boards import CELL, HALFCELL, bget
+from mnstrmap import GreenAndBlue, Ghost
+from bonuses import Bonus
+from bubbles import Bubble
+
+LocalDir = os.path.basename(os.path.dirname(__file__))
+
+
+localmap = {
+ ('pac-lg', -1,0) : ('image1.ppm', ( 0, 0, 32, 32)),
+ ('pac-sm', -1,0) : ('image1.ppm', (32, 0, 32, 32)),
+ ('pac-lg', 0,-1) : ('image1.ppm', ( 0, 32, 32, 32)),
+ ('pac-sm', 0,-1) : ('image1.ppm', (32, 32, 32, 32)),
+ ('pac-lg', 1,0) : ('image1.ppm', ( 0, 64, 32, 32)),
+ ('pac-sm', 1,0) : ('image1.ppm', (32, 64, 32, 32)),
+ ('pac-lg', 0,1) : ('image1.ppm', ( 0, 96, 32, 32)),
+ ('pac-sm', 0,1) : ('image1.ppm', (32, 96, 32, 32)),
+ 'pac-black' : ('image2.ppm', ( 0, 0, 32, 32)),
+ 'pac-dot' : ('image2.ppm', ( 0, 32, 8, 8)),
+ }
+
+music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav'))
+
+
+class PacSprite(ActiveSprite):
+
+ def __init__(self, ico, x, y):
+ import boards
+ if y < -2*CELL:
+ y = -2*CELL
+ elif y > boards.bheight:
+ y = boards.bheight
+ x = (x+HALFCELL) & -CELL
+ y = (y+HALFCELL) & -CELL
+ ActiveSprite.__init__(self, ico, x, y)
+ self.wannadx = self.wannady = 0
+
+ def moving(self):
+ import boards
+ dx = dy = 0
+ turned = None
+ was_clear = 0
+ while 1:
+ if dx or dy:
+ frontx = self.x+CELL+dx*(CELL+1)
+ fronty = self.y+CELL+dy*(CELL+1)
+ clear = (bget((frontx+dy)//CELL, (fronty-dx)//CELL) == ' ' and
+ bget((frontx-dy)//CELL, (fronty+dx)//CELL) == ' ')
+ if clear:
+ blocked = 0
+ else:
+ blocked = (was_clear or (self.x<=2*CELL and dx<0) or
+ (self.x>=boards.bwidth-4*CELL and dx>0))
+ else:
+ blocked = 1
+ if blocked:
+ if turned:
+ dx, dy = turned
+ turned = None
+ continue
+ self.lastmove = None
+ else:
+ if turned:
+ self.resetimages(dx, dy)
+ turned = None
+ self.lastmove = dx, dy
+ self.step(2*dx, 2*dy)
+ self.vertical_warp()
+ was_clear = clear
+ yield None
+ if self.wannadx != dx or self.wannady != dy:
+ if ((self.wannadx and not (self.y % CELL)) or
+ (self.wannady and not (self.x % CELL))):
+ turned = dx, dy
+ dx = self.wannadx
+ dy = self.wannady
+
+
+class Pac(PacSprite):
+ no_hat = 1
+
+ def __init__(self, pacman, bubber, x, y, dcap):
+ ico = GreenAndBlue.normal_bubbles[bubber.pn][1]
+ PacSprite.__init__(self, images.sprget(('eyes', 0, 0)), x, y)
+ self.bubble = ActiveSprite(images.sprget(ico), x, y)
+ self.bubber = bubber
+ self.pacman = pacman
+ self.ready = 0
+ self.gen.append(self.playing())
+ self.pacman.pacs.append(self)
+ self.dcap = dcap
+
+ def resetimages(self, dx, dy):
+ self.ready = 1
+ self.setimages(self.cyclic([('pac-lg', dx, dy),
+ 'pac-black',
+ ('pac-sm', dx, dy)], 5))
+
+ def to_front(self):
+ self.bubble.to_front()
+ ActiveSprite.to_front(self)
+
+ def kill(self):
+ self.play(images.Snd.Pop)
+ self.bubble.gen = [self.bubble.die(Bubble.exploding_bubbles)]
+ self.pacman.latestposition[self.bubber] = self.x, self.y
+ try:
+ self.bubber.dragons.remove(self)
+ except ValueError:
+ pass
+ try:
+ self.pacman.pacs.remove(self)
+ except ValueError:
+ pass
+ ActiveSprite.kill(self)
+
+ def playing(self):
+ bubber = self.bubber
+ for t in self.moving():
+ if self.pacman.ready:
+ d = [(bubber.key_left, -1, 0),
+ (bubber.key_right, 1, 0),
+ (bubber.key_jump, 0,-1),
+ (bubber.key_fire, 0, 1)]
+ d.sort()
+ if d[-1][0] > d[-2][0]:
+ self.wannadx, self.wannady = d[-1][1:]
+
+ self.bubble.move(self.x, self.y)
+ yield None
+
+ if self.ready:
+ touching = images.touching(self.x+CELL-3, self.y+CELL-3, 6, 6)
+ touching.reverse()
+ for s in touching:
+ if isinstance(s, Bonus):
+ s.touched(self)
+ elif isinstance(s, PacGhost):
+ self.kill()
+ return
+
+
+class PacGhost(PacSprite):
+
+ def __init__(self, pacman, x, y):
+ left = random.random() < 0.5
+ if left:
+ ico = Ghost.left[0]
+ else:
+ ico = Ghost.right[0]
+ PacSprite.__init__(self, images.sprget(ico), x, y)
+ self.pacman = pacman
+ self.gen.append(self.waiting())
+ if left:
+ self.wannadx = -1
+ else:
+ self.wannadx = 1
+ self.resetimages(self.wannadx, self.wannady)
+
+ def resetimages(self, dx, dy):
+ if dx > 0:
+ self.setimages(self.cyclic(Ghost.right, 3))
+ elif dx < 0:
+ self.setimages(self.cyclic(Ghost.left, 3))
+ #else: don't change image
+
+ def waiting(self, delay=45):
+ while not self.pacman.ready:
+ yield None
+ for i in range(delay):
+ yield None
+ self.gen.append(self.walking())
+
+ def walking(self):
+ round = 0
+ self.touchable = 1
+ lastmove_x = 0
+ for t in self.moving():
+ if random.random() < 0.1:
+ lastmove_x = self.wannadx
+ if self.lastmove is None and random.random() < 0.75:
+ round = 0 # try to move again immediately
+ if self.lastmove is None or random.random() < 0.01:
+ dragons = self.pacman.pacs or images.ActiveSprites
+ distances = [(abs(dragon.x-self.x)+abs(dragon.y-self.y),
+ dragon) for dragon in dragons]
+ distance, dragon = min(distances)
+ if lastmove_x:
+ self.wannadx = 0
+ if (dragon.y < self.y) ^ (random.random() < 0.3):
+ self.wannady = -1
+ else:
+ self.wannady = 1
+ else:
+ self.wannady = 0
+ if (dragon.x < self.x) ^ (random.random() < 0.3):
+ self.wannadx = -1
+ else:
+ self.wannadx = 1
+ else:
+ lastmove_x = self.lastmove[0]
+## for i in range(10):
+## dragon = random.choice(dragons)
+## dx = dragon.x - self.x
+## dy = dragon.y - self.y
+## if dx or dy:
+## dist = math.sqrt(dx*dx+dy*dy)
+## dx /= dist
+## dy /= dist
+## wx, wy = random.choice([(-1,0), (1,0), (0,-1), (0,1)])
+## ex = wx-dx
+## ey = wy-dy
+## if ex*ex + ey*ey < random.random()*3.14:
+## break
+## self.wannadx = wx
+## self.wannady = wy
+ if round == 0: # go just a bit faster than the players
+ round = 6
+ else:
+ round -= 1
+ yield None
+
+
+class FruitBonus(Bonus):
+ pass
+
+
+class Pacman:
+
+ def bgen(self, limittime = 45.1): # 0:45
+ import boards
+ from player import BubPlayer
+
+ self.ready = 0
+ self.dots = []
+ monsters = BubPlayer.MonsterList[:]
+ random.shuffle(monsters)
+ keep = len([p for p in BubPlayer.PlayerList if p.isplaying()])
+ monsters = monsters[:2 + keep//2]
+ for d in monsters:
+ PacGhost(self, d.x, d.y)
+
+ for t in boards.initsubgame(music, self.displaypoints):
+ yield t
+
+ tc = boards.TimeCounter(limittime)
+ self.builddelay = {}
+ self.latestposition = {}
+ self.pacs = []
+ #finish = 0
+ for t in self.frame():
+ t = boards.normal_frame()
+ self.build_pacs()
+ yield t
+ #if len(self.pacs) == 0:
+ # finish += 1
+ # if finish == 20:
+ # break
+ #else:
+ # finish = 0
+ tc.update(t)
+ if tc.time == 0.0:
+ break
+ if (BubPlayer.FrameCounter & 15) == 7:
+ for s in images.ActiveSprites:
+ if isinstance(s, Bubble):
+ s.pop()
+
+ tc.restore()
+ self.ready = 0
+ results = {}
+ for b in self.dots:
+ for d in b.taken_by:
+ bubber = d.bubber
+ results[bubber] = results.get(bubber, 0) + 1
+ for t in boards.result_ranking(results, len(self.dots)):
+ self.remove_pacs()
+ yield t
+ for s in images.ActiveSprites[:]:
+ if isinstance(s, Bonus):
+ s.kill()
+
+ def displaypoints(self, bubber):
+ result = 0
+ for b in self.dots:
+ for d in b.taken_by:
+ if d.bubber is bubber:
+ result += 1
+ return result
+
+ def frame(self):
+ import boards
+ from bonuses import Fruits
+ for t in self.digwalls():
+ yield t
+
+ def anywall(x1,y1,x2,y2):
+ for tx in range(x1, x2):
+ for ty in range(y1, y2):
+ if bget(tx, ty) == '#':
+ return 1
+ return 0
+
+ def give_one_point(dragon):
+ dragon.bubber.displaypoints += 1
+ scoreboard()
+
+ dots = self.dots
+ ico = images.sprget('pac-dot')
+ for x in range(boards.width):
+ for y in range(boards.height):
+ if not anywall(x, y, x+2, y+2):
+ if anywall(x-1, y-1, x+3, y+3):
+ b = Bonus((x+1)*CELL - ico.w//2,
+ (y+1)*CELL - ico.h//2,
+ 'pac-dot', points=-100, falling=0)
+ b.sound = 'Extra'
+ b.timeout = 0
+ b.taken = give_one_point
+ dots.append(b)
+
+ for s in images.ActiveSprites:
+ if isinstance(s, PacGhost):
+ s.to_front()
+ yield None
+
+ self.ready = 1
+
+ for i in range(len([s for s in images.ActiveSprites
+ if isinstance(s, PacGhost)])):
+ for j in range(32):
+ yield None
+ for j in range(100):
+ x = random.randrange(4, boards.width-6)
+ y = random.randrange(3, boards.height-5)
+ if not anywall(x-2, y-2, x+4, y+4):
+ nimage, points = random.choice(Fruits.Fruits)
+ points += 650 # boost to the range 750-1000
+ b = FruitBonus(x*CELL, y*CELL,
+ nimage, points, falling=0)
+ b.timeout = 0
+ break
+
+ while dots:
+ dots = [b for b in dots if b.alive]
+ yield None
+
+ def build_pacs(self):
+ from player import BubPlayer
+ for p in BubPlayer.PlayerList:
+ dragons = [d for d in p.dragons if not isinstance(d, Pac)]
+ if dragons and len(p.dragons) == len(dragons):
+ if self.builddelay.get(p):
+ self.builddelay[p] -= 1
+ else:
+ self.builddelay[p] = 109
+ dragon = random.choice(dragons)
+ if p in self.latestposition:
+ dragon.move(*self.latestposition[p])
+ pac = Pac(self, p, dragon.x, dragon.y, dragon.dcap)
+ p.dragons.append(pac)
+ p.emotic(dragon, 4)
+ for d in dragons:
+ d.kill()
+
+ def remove_pacs(self):
+ from player import Dragon
+ killclasses = (PacSprite, FruitBonus, Dragon)
+ for s in images.ActiveSprites[:]:
+ if isinstance(s, killclasses):
+ s.kill()
+
+ def digwalls(self):
+ import boards
+ holes = {}
+ for x in range(2, boards.width-2):
+ y = boards.height-1
+ if bget(x, 0) == '#' or bget(x, y) == '#':
+ if bget(x, 0) == ' ': curboard.putwall(x, 0)
+ if bget(x, y) == ' ': curboard.putwall(x, y)
+ curboard.reorder_walls()
+ for y in range(1, boards.height-1):
+ if bget(x, y) == ' ':
+ holes[x, y] = 0
+ if x % 7 == 0:
+ yield None
+
+ # 'holes' maps coordinates (x,y) to 0 (not processed)
+ # or 1 (processed).
+ # All processed holes are pacman-connected.
+
+ def rdig(x1,y1,x2,y2, reversed, holes=holes):
+ # digs the rectangle (x1,y1,x2,y2) and marks it as
+ # processed. Also recursively mark as processed all existing
+ # holes that are pacman-connected to the rectangle.
+ xrange = range(x1,x2)
+ yrange = range(y1,y2)
+ if not reversed:
+ xrange.reverse()
+ yrange.reverse()
+ if len(xrange) > len(yrange):
+ xylist = [(x, y) for x in xrange for y in yrange]
+ else:
+ xylist = [(x, y) for y in yrange for x in xrange]
+ t = 0
+ for x, y in xylist:
+ if bget(x, y) == '#':
+ curboard.killwall(x, y)
+ if t == 0:
+ yield None
+ t = 2
+ else:
+ t -= 1
+ holes.setdefault((x, y), 0)
+ fill = []
+ for x in range(x1,x2-1):
+ for y in range(y1,y2-1):
+ fill.append((x, y))
+ for x, y in fill:
+ if ((x, y) in holes and
+ (x, y+1) in holes and
+ (x+1, y) in holes and
+ (x+1, y+1) in holes):
+ if (holes[x, y] == 0 or
+ holes[x, y+1] == 0 or
+ holes[x+1, y] == 0 or
+ holes[x+1, y+1] == 0):
+
+ holes[x, y] = 1
+ holes[x, y+1] = 1
+ holes[x+1, y] = 1
+ holes[x+1, y+1] = 1
+ fill.append((x+1,y))
+ fill.append((x-1,y))
+ fill.append((x,y+1))
+ fill.append((x,y-1))
+
+ def joined(x1,y1,x2,y2, holes=holes, boards=boards):
+ # returns
+ # 1 if the rectangle (x1,y1,x2,y2) is pac-connected to
+ # some already-processed holes
+ # 0 if it is not
+ # -1 if (x1,y1,x2,y2) is out of the screen
+ if x1<2 or y1<1 or x2>boards.width-2 or y2>boards.height-1:
+ return -1
+ accum1 = accum2 = 0
+ for x in range(x1,x2):
+ if holes.get((x,y1-1)):
+ accum1 += 1
+ if accum1 == 2:
+ return 1
+ else:
+ accum1 = 0
+ if holes.get((x,y2)):
+ accum2 += 1
+ if accum2 == 2:
+ return 1
+ else:
+ accum2 = 0
+ accum1 = accum2 = 0
+ for y in range(y1,y2):
+ if holes.get((x1-1,y)):
+ accum1 += 1
+ if accum1 == 2:
+ return 1
+ else:
+ accum1 = 0
+ if holes.get((x2,y)):
+ accum2 += 1
+ if accum2 == 2:
+ return 1
+ else:
+ accum2 = 0
+ return 0
+
+ if not holes:
+ holes[boards.width//2, boards.height//2] = 0
+ holeslist = holes.keys()
+ random.shuffle(holeslist)
+ startx, starty = holeslist.pop()
+ # make the hole larger (2x2) towards the center of the board
+ if startx > boards.width//2:
+ startx -= 1
+ if starty > boards.height//2:
+ starty -= 1
+ # initial 2x2 hole
+ for t in rdig(startx, starty, startx+2, starty+2, 0):
+ yield t
+
+ dlist = [
+ (0,0,1,0, 0,-1), (0,0,1,0, 0,0), # right
+ (0,0,0,1, -1,0), (0,0,0,1, 0,0), # bottom
+ (-1,0,0,0, -1,-1), (-1,0,0,0, -1,0), # left
+ (0,-1,0,0, -1,-1), (0,-1,0,0, 0,-1), # top
+ ]
+ while holeslist:
+ random.shuffle(dlist)
+ pending = holeslist
+ holeslist = []
+ progress = 0
+ for x, y in pending:
+ if holes[x, y] != 0:
+ continue
+ for dx1, dy1, dx2, dy2, dx, dy in dlist:
+ x1 = x + dx
+ y1 = y + dy
+ x2 = x1 + 2
+ y2 = y1 + 2
+ result = 0
+ while result == 0:
+ result = joined(x1,y1,x2,y2)
+ if result == 1:
+ # rectangle (x1,y1,x2,y2) is good
+ for t in rdig(x1, y1, x2, y2,
+ dx1<0 or dy1<0 or dx2<0 or dy2<0):
+ yield t
+ progress = 1
+ break
+ x1 += dx1
+ y1 += dy1
+ x2 += dx2
+ y2 += dy2
+ else:
+ # rectangle (x1,y1,x2,y2) is too large for the screen
+ # failure
+ continue
+ break
+ else:
+ # no successful direction found from this point
+ holeslist.append((x, y)) # try again later
+ if not progress:
+ # deadlocked situation, add a new random hole
+ x = random.randrange(2, boards.width-2)
+ y = random.randrange(1, boards.height-1)
+ holeslist.insert(0, (x, y))
+ holes.setdefault((x, y), 0)
+ yield None
+
+ # pattern transformation:
+ # X. ..
+ # ... --> ...
+ # .X .X
+ progress = 1
+ while progress:
+ progress = 0
+ for y in range(1, boards.height-1):
+ for x in range(3, boards.width-3):
+ if (' ' == bget(x, y)
+ == bget(x+1, y)
+ == bget(x-1, y)
+ == bget(x, y+1)
+ == bget(x, y-1)):
+ if '#' == bget(x-1, y-1) == bget(x+1, y+1):
+ curboard.killwall(x-1, y-1)
+ progress = 1
+ elif '#' == bget(x+1, y-1) == bget(x-1, y+1):
+ curboard.killwall(x+1, y-1)
+ progress = 1
+ yield None
+
+# This game is suitable for at least min_players players
+min_players = 1
+
+def run():
+ global curboard
+ import boards
+ from boards import curboard
+ boards.replace_boardgen(Pacman().bgen())
+
+def setup():
+ for key, (filename, rect) in localmap.items():
+ filename = os.path.join(LocalDir, filename)
+ images.sprmap[key] = (filename, rect)
+setup()