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 list(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