diff options
Diffstat (limited to 'bubbob/monsters.py')
-rw-r--r-- | bubbob/monsters.py | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/bubbob/monsters.py b/bubbob/monsters.py new file mode 100644 index 0000000..88983d0 --- /dev/null +++ b/bubbob/monsters.py @@ -0,0 +1,937 @@ +from __future__ import generators +import random +import gamesrv +import images +import boards +from boards import * +from images import ActiveSprite +from mnstrmap import GreenAndBlue, Bonuses, Ghost +from player import BubPlayer +import bonuses + + +class Monster(ActiveSprite): + touchable = 1 + special_prob = 0.2 + shootcls = None + vx = 2 + vy = 0 + vdir = -1 + is_ghost = 0 + MonsterBonus = bonuses.MonsterBonus + + def __init__(self, mnstrdef, x=None, y=None, dir=None, in_list=None): + self.mdef = mnstrdef + self.ptag = None + if dir is None: dir = mnstrdef.dir + if x is None: x = mnstrdef.x*CELL + if y is None: y = mnstrdef.y*CELL + self.dir = dir + ActiveSprite.__init__(self, images.sprget(self.imgrange()[0]), x, y) + self.gen.append(self.waiting()) + if in_list is None: + in_list = BubPlayer.MonsterList + self.in_list = in_list + self.in_list.append(self) + self.no_shoot_before = 0 + #images.ActiveSprites.remove(self) + + def unlist(self): + try: + self.in_list.remove(self) + return 1 + except ValueError: + return 0 + + def kill(self): + self.unlist() + ActiveSprite.kill(self) + + def tagdragon(self): + lst = bonuses.getvisibledragonlist() + if lst: + return random.choice(lst) + else: + return None + + def imgrange(self): + if self.is_ghost: + if self.dir > 0: + return Ghost.right + else: + return Ghost.left + elif self.angry: + if self.dir > 0: + return self.mdef.right_angry + else: + return self.mdef.left_angry + else: + if self.dir > 0: + return self.mdef.right + else: + return self.mdef.left + + def imgrange1(self): + # normally this is self.imgrange()[1] + lst = self.imgrange() + return lst[len(lst) > 1] + + def resetimages(self, is_ghost=0): + self.is_ghost = is_ghost + if self.gen: + self.setimages(self.cyclic(self.imgrange(), 3)) + else: # frozen monster + self.seticon(images.sprget(self.imgrange()[0])) + + def blocked(self): + if self.dir < 0: + x0 = (self.x-1)//16 + else: + x0 = (self.x+33)//16 + y0 = self.y // 16 + 1 + y1 = (self.y + 31) // 16 + return bget(x0,y0) == '#' or bget(x0,y1) == '#' + + def tryhstep(self): + if self.blocked(): + self.dir = -self.dir + self.resetimages() + return 0 + else: + self.step(self.vx*self.dir, 0) + return 1 + + def vblocked(self): + if self.vdir < 0: + y0 = self.y//16 + else: + y0 = (self.y+1)//16 + 2 + x0 = self.x // 16 + x1 = self.x // 16 + 1 + x2 = (self.x+31) // 16 + return bget(x0,y0) == '#' or bget(x1,y0) == '#' or bget(x2,y0) == '#' + + def tryvstep(self): + if self.vblocked(): + self.vdir = -self.vdir + return 0 + else: + self.step(0, self.vy*self.vdir) + self.vertical_warp() + return 1 + + def waiting(self, delay=20): + for i in range(delay): + yield None + self.resetimages() + self.gen.append(self.default_mode()) + + def overlapping(self): + if self.in_list is BubPlayer.MonsterList: + for s in self.in_list: + if (-6 <= s.x-self.x <= 6 and -6 <= s.y-self.y < 6 and + #s.dir == self.dir and s.vdir == self.vdir and + s.vx == self.vx and s.vy == self.vy and + (not s.angry) == (not self.angry)): + return s is not self + return 0 + + def walking(self): + while onground(self.x, self.y): + yield None + if random.random() < 0.2 and self.overlapping(): + yield None + x1 = self.x + if self.dir > 0: + x1 += self.vx + if (x1 & 15) < self.vx and random.random() < self.special_prob: + self.move(x1 & -16, self.y) + if self.special(): + return + self.tryhstep() + if self.seedragon(): + self.gen.append(self.hjumping()) + else: + self.gen.append(self.falling()) + + def seedragon(self, dragon=None): + dragon = dragon or self.tagdragon() + if dragon is None: + return False + return abs(dragon.y - self.y) < 16 and self.dir*(dragon.x-self.x) > 0 + + def special(self): + dragon = self.tagdragon() + if dragon is None: + return 0 + if self.seedragon(dragon) and self.shoot(): + return 1 + if dragon.y < self.y-CELL: + #and abs(dragon.x-self.x) < 2*(self.y-dragon.y): + for testy in range(self.y-2*CELL, self.y-6*CELL, -CELL): + if onground(self.x, testy): + if random.random() < 0.5: + ndir = self.dir + elif dragon.x < self.x: + ndir = -1 + else: + ndir = 1 + self.gen.append(self.vjumping(testy, ndir)) + return 1 + return 0 + + def shooting(self, pause): + for i in range(pause): + yield None + self.shootcls(self) + yield None + self.gen.append(self.default_mode()) + + def shoot(self, pause=10): + if (self.shootcls is None or + self.no_shoot_before > BubPlayer.FrameCounter): + return 0 + else: + self.gen.append(self.shooting(pause)) + self.no_shoot_before = BubPlayer.FrameCounter + 29 + return 1 + + def falling(self): + bubber = getattr(self, 'bubber', None) + while not onground(self.x, self.y): + yield None + ny = self.y + 3 + if (ny & 15) > 14: + ny = (ny//16+1)*16 + elif (ny & 15) < 3: + ny = (ny//16)*16 + nx = self.x + if nx < 32: + nx += 1 + (self.vx-1) * (bubber is not None) + elif nx > boards.bwidth - 64: + nx -= 1 + (self.vx-1) * (bubber is not None) + elif bubber: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + self.setimages(None) + if dx and not self.blocked(): + nx += self.vx*dx + self.seticon(images.sprget(self.imgrange1())) + self.move(nx, ny) + if self.y >= boards.bheight: + self.vertical_warp() + if bubber: + nextgen = self.playing_monster + else: + nextgen = self.walking + self.gen.append(nextgen()) + +## def moebius(self): +## self.dir = -self.dir +## self.resetimages() +## if hasattr(self, 'dcap'): +## self.dcap['left2right'] *= -1 + + def hjumping(self): + y0 = self.y + vspeed = -2.2 + ny = y0-1 + while ny <= y0 and not self.blocked(): + self.move(self.x+2*self.dir, int(ny)) + yield None + vspeed += 0.19 + ny = self.y + vspeed + self.gen.append(self.default_mode()) + + def vjumping(self, limity, ndir): + self.setimages(None) + yield None + self.dir = -self.dir + self.seticon(images.sprget(self.imgrange()[0])) + for i in range(9): + yield None + self.dir = -self.dir + self.seticon(images.sprget(self.imgrange()[0])) + for i in range(4): + yield None + self.dir = ndir + self.seticon(images.sprget(self.imgrange1())) + for ny in range(self.y-4, limity-4, -4): + self.move(self.x, ny) + if ny < -32: + self.vertical_warp() + yield None + self.resetimages() + self.gen.append(self.default_mode()) + + def regular(self): + return self.still_playing() and self.touchable and not self.is_ghost + + def still_playing(self): + return (self.in_list is BubPlayer.MonsterList and + self in self.in_list) + + def touched(self, dragon): + if self.gen: + self.killdragon(dragon) + if self.is_ghost and not hasattr(self, 'bubber'): + self.gen = [self.default_mode()] + self.resetimages() + else: + self.argh(getattr(self, 'poplist', None)) # frozen monster + + def killdragon(self, dragon): + dragon.die() + + def in_bubble(self, bubble): + if not hasattr(self.mdef, 'jailed'): + return + self.untouchable() + self.angry = [] + bubble.move(self.x, self.y) + if not hasattr(bubble, 'withmonster'): + bubble.to_front() + self.to_front() + img = self.mdef.jailed + self.gen = [self.bubbling(bubble)] + self.setimages(self.cyclic([img[1], img[2], img[1], img[0]], 4)) + + def bubbling(self, bubble): + counter = 0 + while not hasattr(bubble, 'poplist'): + self.move(bubble.x, bubble.y) + yield None + counter += 1 + if counter == 50 and hasattr(self, 'bubber'): + bubble.setimages(bubble.bubble_red()) + if bubble.poplist is None: + self.touchable = 1 + self.angry = [self.genangry()] + self.resetimages() + self.gen.append(self.default_mode()) + else: + previous_len = len(BubPlayer.MonsterList) + self.argh(bubble.poplist) + dragon = bubble.poplist[0] + if dragon is not None: + if previous_len and not BubPlayer.MonsterList: + points = 990 + else: + points = 90 + dragon.bubber.givepoints(points) + + def argh(self, poplist=None, onplace=0): + if self not in self.in_list: + return + if not poplist: + poplist = [None] + poplist.append(self) + level = len(poplist) - 2 + bonuses.BonusMaker(self.x, self.y, self.mdef.dead, onplace=onplace, + outcome=(self.MonsterBonus, level)) + self.kill() + + def freeze(self, poplist): + # don't freeze monsters largely out of screen, or they'll never come in + if self.regular() and -self.ico.h < self.y < boards.bheight: + self.gen = [] + self.poplist = poplist + + def flying(self): + blocked = 0 + while 1: + if random.random() < 0.2 and self.overlapping(): + yield None + hstep = self.tryhstep() + vstep = self.tryvstep() + if hstep or vstep: + blocked = 0 + elif blocked: + # blocked! go up or back to the play area + if self.x < 32: + self.step(self.vy, 0) + elif self.x > boards.bwidth - 64: + self.step(-self.vy, 0) + else: + self.step(0, -self.vy) + self.vertical_warp() + else: + blocked = 1 + yield None + + def becoming_monster(self, big=0, immed=0): + if big: + self.is_ghost = 1 + self.seticon(images.sprget(self.imgrange()[0])) + images.Snd.Hell.play() + for i in range(5): + ico = self.ico + self.seticon(self.bubber.icons[11 + immed, self.dir]) + yield None + yield None + self.seticon(ico) + yield None + yield None + self.resetimages(is_ghost=big) + self.gen.append(self.playing_monster()) + + def become_monster(self, bubber, saved_caps, big=0, immed=0): + self.timeoutgen = self.back_to_dragon() + self.default_mode = self.playing_monster + self.bubber = bubber + self.dcap = saved_caps + self.gen = [self.becoming_monster(big, immed)] + + def back_to_dragon(self): + for t in range(259): + yield None + if bonuses.getdragonlist(): + yield None + yield None + yield None + from player import Dragon + d = Dragon(self.bubber, self.x, self.y, self.dir, self.dcap) + d.dcap['shield'] = 50 + self.bubber.dragons.append(d) + self.kill() + + def playing_monster(self): + if self.timeoutgen not in self.gen: + self.gen.append(self.timeoutgen) + bubber = self.bubber + while self.is_ghost: + # ghost + self.angry = [] + key, dx, dy = max([(bubber.key_left, -1, 0), + (bubber.key_right, 1, 0), + (bubber.key_jump, 0, -1), + (bubber.key_fire, 0, 1)]) + if key: + if dx and self.dir != dx: + self.dir = dx + self.resetimages(is_ghost=1) + nx = self.x + 10*dx + ny = self.y + 9*dy + if nx < 0: nx = 0 + if nx > boards.bwidth-2*CELL: nx = boards.bwidth-2*CELL + if ny < -CELL: ny = -CELL + if ny > boards.bheight-CELL: ny = boards.bheight-CELL + self.move(nx, ny) + yield None + if self.vy: + # flying monster + while 1: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + if bubber.key_jump and bubber.key_jump > bubber.key_fire: + dy = self.vdir = -1 + elif bubber.key_fire: + dy = self.vdir = 1 + else: + dy = 0 + hstep = dx and self.tryhstep() + vstep = dy and self.tryvstep() + if dx and dy and not (hstep or vstep): + # blocked? + self.dir = -self.dir + self.vdir = -self.vdir + blocked = self.blocked() and self.vblocked() + self.dir = -self.dir + self.vdir = -self.vdir + if blocked: + # completely blocked! accept move or force back to + # play area + if self.x < 32: + self.step(self.vy, 0) + elif self.x > boards.bwidth - 64: + self.step(-self.vy, 0) + else: + self.step(self.vx*dx, self.vy*dy) + self.vertical_warp() + yield None + elif not isinstance(self, Springy): + # walking monster + jumping_y = 0 + imgsetter = self.imgsetter + while onground(self.x, self.y) or jumping_y: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + imgsetter = self.imgsetter + if dx and not self.blocked(): + self.step(self.vx*dx, 0) + if not jumping_y: + self.setimages(imgsetter) + else: + self.seticon(images.sprget(self.imgrange1())) + self.setimages(None) + else: + self.setimages(None) + dx = 0 + yield None + if not jumping_y: + wannafire = bubber.key_fire + wannajump = bubber.key_jump + if wannafire and self.shoot(1): + return + if wannajump: + jumping_y = CELL + if jumping_y: + self.step(0, -4) + if self.y < -32: + self.vertical_warp() + if onground(self.x, self.y): + jumping_y = 0 + else: + jumping_y -= 1 + self.gen.append(self.falling()) + else: + # springy + if not onground(self.x, self.y): + self.gen.append(self.falling()) + return + prevx = self.x + for t in self.walking(): + dx = bubber.wannago(self.dcap) + if dx: + if dx != self.dir: + self.dir = dx + self.resetimages() + if self.blocked() and (self.x-prevx)*dx <= 0: + dx = 0 + self.move(prevx + self.vx*dx, self.y) + yield None + prevx = self.x + + def become_ghost(self): + self.gen = [self.ghosting()] + self.resetimages(is_ghost=1) + + def ghosting(self): + counter = 0 + while counter < 5: + for i in range(50): + yield None + dragon = self.tagdragon() + if dragon is None: + counter += 1 + else: + counter = 0 + px, py = dragon.x, dragon.y + if abs(px-self.x) < abs(py-self.y): + dx = 0 + if py > self.y: + dy = 1 + else: + dy = -1 + else: + dy = 0 + if px > self.x: + dx = 1 + else: + dx = -1 + self.dir = dx + self.resetimages(is_ghost=1) + dx *= 10 + dy *= 9 + distance = 1E10 + while 1: + self.angry = [] + self.step(dx, dy) + yield None + dist1 = (px-self.x)*(px-self.x)+(py-self.y)*(py-self.y) + if dist1 > distance: + break + distance = dist1 + self.angry = [] + self.gen = [self.default_mode()] + self.resetimages() + + default_mode = falling + + +def argh_em_all(): + poplist = [None] + for s in images.ActiveSprites[:]: + if isinstance(s, Monster): + s.argh(poplist) + +def freeze_em_all(): + poplist = [None] + for s in images.ActiveSprites: + if isinstance(s, Monster): + s.freeze(poplist) + + +class MonsterShot(ActiveSprite): + speed = 6 + touchable = 1 + + def __init__(self, owner, dx=CELL, dy=0): + self.owner = owner + self.speed = owner.dir * self.speed + if owner.dir < 0: + nimages = owner.mdef.left_weapon + else: + nimages = owner.mdef.right_weapon + ActiveSprite.__init__(self, images.sprget(nimages[0]), + owner.x, owner.y + dy) + self.step((owner.ico.w - self.ico.w) // 2, + (owner.ico.h - self.ico.h) // 2) + if not self.blocked(): + self.step(dx*owner.dir, 0) + if len(nimages) > 1: + self.setimages(self.cyclic(nimages, 3)) + self.gen.append(self.moving()) + + def blocked(self): + if self.speed < 0: + x0 = (self.x-self.speed-8)//16 + else: + x0 = (self.x+self.ico.w+self.speed-8)//16 + y0 = (self.y+8) // 16 + 1 + return not (' ' == bget(x0,y0) == bget(x0+1,y0)) + + def moving(self): + while not self.blocked(): + yield None + self.step(self.speed, 0) + self.hitwall() + + def hitwall(self): + self.untouchable() + self.gen.append(self.die(self.owner.mdef.decay_weapon, 2)) + + def touched(self, dragon): + dragon.die() + + +class BoomerangShot(MonsterShot): + speed = 8 + + def hitwall(self): + self.gen.append(self.moveback()) + + def moveback(self): + owner = self.owner + if self.speed > 0: + nimages = owner.mdef.left_weapon + else: + nimages = owner.mdef.right_weapon + self.setimages(self.cyclic(nimages, 3)) + while (owner.x-self.x) * self.speed < 0: + yield None + self.step(-self.speed, 0) + if self.blocked(): + break + self.kill() + +class FastShot(MonsterShot): + speed = 15 + + +class DownShot(MonsterShot): + + def __init__(self, owner): + MonsterShot.__init__(self, owner, 0, CELL) + + def moving(self): + while self.y < boards.bheight: + yield None + self.step(0, 7) + self.kill() + + +##class DragonShot(MonsterShot): +## speed = 8 + +## def __init__(self, owner): +## MonsterShot.__init__(self, owner) +## self.untouchable() +## self.gen.append(self.touchdelay(4)) + +## def touched(self, dragon): +## if dragon is not self.owner: +## if dragon.bubber.bonbons == 0: +## dragon.die() +## else: +## from player import scoreboard +## from bonuses import Sugar1, Sugar2 +## from bonuses import BonusMaker +## if random.random() < 0.2345: +## start = 1 +## else: +## start = 0 +## loose = min(2, dragon.bubber.bonbons) +## for i in range(start, loose): +## cls = random.choice([Sugar1, Sugar2]) +## BonusMaker(self.x, self.y, [cls.nimage], +## outcome=(cls,)) +## dragon.bubber.bonbons -= loose +## scoreboard() +## dragon.dcap['shield'] = 25 +## self.owner.play(images.Snd.Yippee) +## self.kill() + +## def blocked(self): +## return self.x < -self.ico.w or self.x >= gamesrv.game.width +## #return self.x < CELL or self.x >= boards.bwidth - 3*CELL + + +class Nasty(Monster): + pass + +class Monky(Monster): + shootcls = MonsterShot + +class Ghosty(Monster): + default_mode = Monster.flying + vy = 2 + +class Flappy(Monster): + default_mode = Monster.flying + vy = 1 + +class Springy(Monster): + spring_down = 0 + def imgrange(self): + if self.spring_down and not self.is_ghost: + if self.angry: + if self.dir > 0: + r = self.mdef.right_jump_angry + else: + r = self.mdef.left_jump_angry + else: + if self.dir > 0: + r = self.mdef.right_jump + else: + r = self.mdef.left_jump + return [r[self.spring_down-1]] + else: + return Monster.imgrange(self) + def walking(self): + self.spring_down = 1 + self.resetimages() + for t in range(2+self.overlapping()): + yield None + self.spring_down = 2 + self.resetimages() + for t in range(4+2*self.overlapping()): + yield None + self.spring_down = 1 + self.resetimages() + for t in range(2+self.overlapping()): + yield None + self.spring_down = 0 + self.resetimages() + g = 10.0/43 + vy = -20*g + yf = self.y + for t in range(40): + yf += vy + vy += g + if self.blocked(): + self.dir = -self.dir + self.resetimages() + nx = self.x + self.dir*self.vx + if self.y//16 < int(yf)//16: + if onground(self.x, (self.y//16+1)*16): + break + if onground(nx, (self.y//16+1)*16): + self.move(nx, self.y) + break + nx, yf = vertical_warp(nx, yf) + self.move(nx, int(yf)) +## if moebius: +## self.moebius() + yield None + self.gen.append(self.falling()) + +class Orcy(Monster): + shootcls = FastShot + +class Gramy(Monster): + shootcls = BoomerangShot + vx = 3 + +class Blitzy(Monster): + shootcls = DownShot + vx = 3 + + def seedragon(self, dragon=None): + return 0 + + def special(self): + if random.random() < 0.3: + self.shootcls(self) + return 0 + + def shoot(self, pause=0): + # no pause (only used when controlled by the player) + if self.no_shoot_before > BubPlayer.FrameCounter: + pass + else: + self.shootcls(self) + self.no_shoot_before = BubPlayer.FrameCounter + 29 + return 0 + +MonsterClasses = [c for c in globals().values() + if type(c)==type(Monster) and issubclass(c, Monster)] +MonsterClasses.remove(Monster) + + +class Butterfly(Monster): + MonsterBonus = bonuses.IceMonsterBonus + fly_away = False + + def waiting(self, delay=0): + return Monster.waiting(self, delay) + + def imgrange(self): + self.angry = [] + return Monster.imgrange(self) + + def killdragon(self, dragon): + if self.is_ghost: + Monster.killdragon(self, dragon) + else: + self.fly_away = True, dragon.x + + def flying(self): + repeat = 0 + while 1: + r = random.random() + if self.x < 64: + bump = self.dir < 0 + elif self.x > boards.bwidth - 64: + bump = self.dir > 0 + elif self.fly_away: + wannago = self.x - self.fly_away[1] + if self.x < 100: + wannago = 1 + elif self.x > boards.bwidth - 100: + wannago = -1 + bump = self.dir * wannago < 0 + if repeat: + self.fly_away = False + repeat = 0 + else: + repeat = 1 + else: + bump = r < 0.07 + if bump: + self.dir = -self.dir + self.resetimages() + elif r > 0.92: + self.vdir = -self.vdir + self.step(self.dir * (2 + (r < 0.5)), self.vdir * 2) + self.vertical_warp() + if not repeat: + yield None + + default_mode = flying + + +class Sheep(Monster): + + def playing_monster(self): + from bonuses import Bonus + bubber = self.bubber + vy = None + imgsetter = self.imgsetter + poplist = [None] + while 1: + dx = bubber.wannago(self.dcap) + if dx and dx != self.dir: + self.dir = dx + self.resetimages() + imgsetter = self.imgsetter + if dx and vy is None: + self.setimages(imgsetter) + else: + self.setimages(None) + if vy is not None: + if vy < 0: + n = 1 + else: + n = 3 + self.seticon(images.sprget(self.imgrange()[n])) + if dx and not self.blocked(): + self.step(self.vx*dx, 0) + yield None + impulse = 0.0 + wannajump = bubber.key_jump + if vy is not None: + vy += 0.33 + if vy > 12.0: + vy = 12.0 + yf = self.y + yfp + vy + yfp = yf - int(yf) + delta = int(yf) - self.y + if delta > 0: + by_y = {} + for s in images.ActiveSprites: + if isinstance(s, Bonus) and s.touchable: + if abs(s.x - self.x) <= 22: + by_y[s.y] = s + for monster in BubPlayer.MonsterList: + if abs(monster.x - self.x) <= 22: + if monster.regular(): + by_y[monster.y] = monster + for ny in range(self.y - 1, self.y + delta + 1): + self.move(self.x, ny) + self.vertical_warp() + if onground(self.x, self.y): + poplist = [None] + impulse = vy + vy = None + break + key = self.y + 29 + if key in by_y: + s = by_y[key] + if isinstance(s, Monster): + self.play(images.Snd.Extra) + s.argh(poplist) + elif isinstance(s, Bonus): + s.reallytouched(self) + yfp = 0.0 + vy = -3.3 + break + else: + self.step(0, delta) + self.vertical_warp() + if vy is None: + if onground(self.x, self.y): + if wannajump: + yfp = 0.0 + vy = - max(1.0, impulse) - 2.02 + impulse = 0.0 + self.play(images.Snd.Jump) + else: + yfp = vy = 0.0 + if impulse > 8.1: + break + self.play(images.Snd.Pop) + for n in range(2): + for letter in 'abcdefg': + ico = images.sprget(('sheep', letter)) + nx = self.x + random.randrange(-1, self.ico.w - ico.w + 2) + ny = self.y + random.randrange(0, self.ico.h - ico.h + 2) + dxy = [random.random() * 5.3 - 2.65, random.random() * 4 - 4.4] + s = images.ActiveSprite(ico, nx, ny) + s.gen.append(s.parabolic(dxy)) + s.gen.append(s.die([None], random.randrange(35, 54))) + self.move(-99, 0) + for t in range(68): + yield None + self.kill() + + default_mode = falling = playing_monster + + def argh(self, *args, **kwds): + pass |