1
0
mirror of https://github.com/abakh/nbsdgames.git synced 2025-02-02 15:07:27 -05:00
nbsdgames/bubbob/bubbles.py
2022-02-03 13:46:22 +03:30

1287 lines
44 KiB
Python

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 = list(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 range(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, xxx_todo_changeme=(1,0)):
(acos,asin) = xxx_todo_changeme
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 = list(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, xxx_todo_changeme1, restore_img):
(acos,asin) = xxx_todo_changeme1
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 list(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 list(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 list(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)