mirror of
https://github.com/abakh/nbsdgames
synced 2025-04-28 14:09:32 -04:00
2505 lines
84 KiB
Python
2505 lines
84 KiB
Python
|
|
import random, os, math
|
|
import random as random_module
|
|
import gamesrv
|
|
import images
|
|
import boards
|
|
from boards import *
|
|
from images import ActiveSprite
|
|
from mnstrmap import GreenAndBlue, Bonuses, Diamonds, Stars, BigImages
|
|
from mnstrmap import PotionBonuses, Fire
|
|
from player import BubPlayer
|
|
|
|
|
|
questionmarklist = ['questionmark3',
|
|
'questionmark4',
|
|
'questionmark5',
|
|
'questionmark4',
|
|
'questionmark3',
|
|
'questionmark2',
|
|
'questionmark1',
|
|
'questionmark2']
|
|
|
|
class Bonus(ActiveSprite):
|
|
bubblable = 1
|
|
touchable = 1
|
|
points = 750
|
|
timeout = 250
|
|
sound = 'Fruit'
|
|
endaction = None
|
|
multiply = 1
|
|
killgens = 1
|
|
|
|
def __init__(self, x, y, nimage=None, points=None, falling=1):
|
|
if nimage is not None:
|
|
self.nimage = nimage
|
|
if points is not None:
|
|
self.points = points
|
|
ActiveSprite.__init__(self, images.sprget(self.nimage), x, y)
|
|
self.taken_by = []
|
|
self.gen.append(self.timeouter())
|
|
if falling:
|
|
self.gen.append(self.faller())
|
|
|
|
def buildoutcome(self):
|
|
return (self.__class__,)
|
|
|
|
def faller(self):
|
|
while self.y < boards.bheight:
|
|
if onground_nobottom(self.x, self.y):
|
|
yield None
|
|
yield None
|
|
else:
|
|
self.move(self.x, (self.y+4) & ~3)
|
|
yield None
|
|
self.kill()
|
|
|
|
def timeouter(self):
|
|
for i in range(self.timeout):
|
|
yield None
|
|
if self.timeout:
|
|
self.kill()
|
|
|
|
def touched(self, dragon):
|
|
dx, dy, dw, dh = dragon.x, dragon.y, dragon.ico.w, dragon.ico.h
|
|
if (dx + dw > self.x + 10 and
|
|
dy + dh > self.y + 8 and
|
|
self.x + self.ico.w > dx + 10 and
|
|
self.y + self.ico.h > dy + 10):
|
|
self.reallytouched(dragon)
|
|
|
|
def reallytouched(self, dragon):
|
|
if not self.taken_by:
|
|
if self.killgens:
|
|
self.gen = []
|
|
self.gen.append(self.taking())
|
|
sound = self.sound
|
|
if sound:
|
|
if isinstance(sound, str):
|
|
sound = getattr(images.Snd, sound)
|
|
self.play(sound)
|
|
if dragon not in self.taken_by:
|
|
self.taken_by.append(dragon)
|
|
if isinstance(self, (RandomBonus, MonsterBonus)):
|
|
s_bonus = dragon.bubber.stats.setdefault('bonus', {})
|
|
s_bonus[self.nimage] = s_bonus.get(self.nimage, 0) + 1
|
|
|
|
def taking(self, follow_dragons=0, delay=1):
|
|
from player import Dragon
|
|
for t in range(delay):
|
|
yield None # time to be taken by several dragons
|
|
if self.points:
|
|
for p in self.taken_by:
|
|
if follow_dragons and p.alive:
|
|
s = p
|
|
else:
|
|
s = self
|
|
points(s.x + s.ico.w//2, s.y + s.ico.h//2 - CELL, p, self.points)
|
|
dragons = [d for d in self.taken_by if isinstance(d, Dragon)]
|
|
if self.taken1(dragons) != -1:
|
|
self.kill()
|
|
|
|
def taken1(self, dragons):
|
|
for d in dragons * self.multiply:
|
|
if d.alive:
|
|
self.taken(d)
|
|
|
|
def taken(self, dragon):
|
|
pass
|
|
|
|
def in_bubble(self, bubble):
|
|
self.untouchable()
|
|
bubble.move(self.x, self.y)
|
|
bubble.to_front()
|
|
self.to_front()
|
|
self.gen = [self.bubbling(bubble, self.ico)]
|
|
self.move(bubble.x+8, bubble.y+8, images.sprget('questionmark3'))
|
|
self.setimages(self.cyclic(questionmarklist, 2))
|
|
|
|
def bubbling(self, bubble, ico):
|
|
while not hasattr(bubble, 'poplist'):
|
|
self.move(bubble.x+8, bubble.y+8)
|
|
yield None
|
|
if bubble.poplist is not None:
|
|
dragon = bubble.poplist[0]
|
|
if dragon is not None:
|
|
self.play(images.Snd.Yippee)
|
|
if dragon not in self.taken_by:
|
|
self.taken_by.append(dragon)
|
|
if self.points > 10:
|
|
dragon.bubber.givepoints(self.points - 10)
|
|
pn = dragon.bubber.pn
|
|
if self.points in GreenAndBlue.points[pn]:
|
|
Points(bubble.x + bubble.ico.w//2, bubble.y, pn, self.points)
|
|
self.taken1(BubPlayer.DragonList)
|
|
p = Parabolic(ico, bubble.x, bubble.y)
|
|
p.gen.append(p.moving(-1.0))
|
|
self.kill()
|
|
|
|
def is_on_ground(self):
|
|
return onground(self.x, self.y)
|
|
|
|
|
|
def points(x, y, dragon, points):
|
|
dragon.bubber.givepoints(abs(points))
|
|
pn = dragon.bubber.pn
|
|
if points in GreenAndBlue.points[pn]:
|
|
Points(x, y, pn, points)
|
|
|
|
class Points(ActiveSprite):
|
|
|
|
def __init__(self, x, y, pn, points):
|
|
ico = images.sprget(GreenAndBlue.points[pn][points])
|
|
ActiveSprite.__init__(self, ico, x - ico.w//2, max(8, y))
|
|
self.nooverlap = 1
|
|
self.gen.append(self.raiser())
|
|
|
|
def raiser(self):
|
|
wait = 0
|
|
for s in images.ActiveSprites:
|
|
if s is self:
|
|
break
|
|
if (isinstance(s, Points) and s.nooverlap and
|
|
abs(self.x-s.x)<self.ico.w*2//3 and
|
|
abs(self.y-s.y)<self.ico.h):
|
|
wait += 5
|
|
for t in range(wait):
|
|
yield None
|
|
for i in range(25):
|
|
if i == 7:
|
|
self.nooverlap = 0
|
|
self.step(0, -2)
|
|
yield None
|
|
if self.y <= 0:
|
|
break
|
|
for i in range(20):
|
|
yield None
|
|
self.kill()
|
|
|
|
|
|
class Parabolic(ActiveSprite):
|
|
fallstraight = 0
|
|
fallspeed = 4
|
|
|
|
def moving(self, y_amplitude = -8.0):
|
|
bottom_up = self.fallspeed < 0
|
|
dxy = [(random.random()-0.5) * 15.0,
|
|
(random.random()+0.5) * y_amplitude * (1,-1)[bottom_up]]
|
|
if bottom_up:
|
|
kw = {'gravity': -0.3}
|
|
else:
|
|
kw = {}
|
|
for n in self.parabolic(dxy, self.fallstraight, **kw):
|
|
progress = self.parabole_progress = dxy[1] * (1,-1)[bottom_up]
|
|
yield n
|
|
if progress >= 4.0 and self.fallstraight:
|
|
del self.parabole_progress
|
|
self.gen.append(self.falling())
|
|
return
|
|
self.kill()
|
|
|
|
def falling(self):
|
|
nx, ny = vertical_warp(self.x, self.y & ~3)
|
|
if self.fallspeed < 0:
|
|
groundtest = underground
|
|
else:
|
|
groundtest = onground
|
|
while not groundtest(nx, ny):
|
|
ny += self.fallspeed
|
|
nx, ny1 = vertical_warp(nx, ny)
|
|
if ny1 != ny:
|
|
ny = ny1
|
|
self.wrapped_around()
|
|
self.move(nx, ny)
|
|
yield None
|
|
self.move(nx, ny)
|
|
self.build()
|
|
self.kill()
|
|
|
|
def killmonsters(self, poplist):
|
|
from monsters import Monster
|
|
while 1:
|
|
for s in self.touching(0):
|
|
if isinstance(s, Monster):
|
|
s.argh(poplist)
|
|
yield None
|
|
|
|
def build(self):
|
|
pass
|
|
|
|
def wrapped_around(self):
|
|
pass
|
|
|
|
|
|
class Parabolic2(Parabolic):
|
|
points = 0
|
|
|
|
def __init__(self, x, y, imglist, imgspeed=3, onplace=0, y_amplitude=-8.0):
|
|
Parabolic.__init__(self, images.sprget(imglist[0]), x, y)
|
|
if onplace:
|
|
self.gen.append(self.falling())
|
|
else:
|
|
self.gen.append(self.moving(y_amplitude))
|
|
if len(imglist) > 1:
|
|
self.setimages(self.cyclic(imglist, imgspeed))
|
|
|
|
def touched(self, dragon, rect=None):
|
|
if self.points:
|
|
points(self.x + self.ico.w/2, self.y + self.ico.h/2 - CELL,
|
|
dragon, self.points)
|
|
self.kill()
|
|
|
|
|
|
class BonusMaker(Parabolic2):
|
|
fallstraight = 1
|
|
touchable = 1
|
|
|
|
def __init__(self, x, y, imglist, imgspeed=3, onplace=0, outcome=None):
|
|
assert outcome
|
|
self.outcome = outcome
|
|
if outcome == (Flower2,):
|
|
self.fallspeed = -self.fallspeed
|
|
Parabolic2.__init__(self, x, y, imglist, imgspeed, onplace)
|
|
|
|
def falling(self):
|
|
cls = self.outcome[0]
|
|
if issubclass(cls, Megabonus):
|
|
self.build()
|
|
return self.die([])
|
|
else:
|
|
return Parabolic2.falling(self)
|
|
|
|
def wrapped_around(self):
|
|
cls = self.outcome[0]
|
|
if issubclass(cls, RandomBonus) and not boards.curboard.playingboard:
|
|
self.kill()
|
|
|
|
def build(self):
|
|
cls = self.outcome[0]
|
|
args = self.outcome[1:]
|
|
if issubclass(cls, RandomBonus) and not boards.curboard.playingboard:
|
|
return None
|
|
else:
|
|
return cls(self.x, self.y, *args)
|
|
|
|
def touched(self, dragon, rect=None):
|
|
pass
|
|
|
|
def in_bubble(self, bubble):
|
|
bonus = self.build()
|
|
self.kill()
|
|
if bonus:
|
|
bonus.in_bubble(bubble)
|
|
return bonus
|
|
|
|
class BonusMakerExtraStar(ActiveSprite):
|
|
|
|
def __init__(self, x, y, sx, sy, colorname):
|
|
self.sx = sx
|
|
self.sy = sy
|
|
imglist = [('smstar', colorname, k) for k in range(2)]
|
|
ActiveSprite.__init__(self, images.sprget(imglist[-1]),
|
|
x + HALFCELL, y + HALFCELL)
|
|
self.setimages(self.cyclic(imglist, speed=2))
|
|
|
|
def follow_bonusmaker(self, bm):
|
|
for t in range(4):
|
|
yield None
|
|
if hasattr(bm, 'parabole_progress'):
|
|
break
|
|
else:
|
|
self.kill()
|
|
return
|
|
start = bm.parabole_progress
|
|
if start < 3.9:
|
|
while bm.alive and hasattr(bm, 'parabole_progress'):
|
|
f = (bm.parabole_progress-start) / (4.0-start)
|
|
self.move(bm.x + HALFCELL + int(f*self.sx),
|
|
bm.y + HALFCELL + int(f*self.sy))
|
|
yield None
|
|
self.kill()
|
|
|
|
|
|
class MonsterBonus(Bonus):
|
|
|
|
def __init__(self, x, y, multiple, forceimg=0):
|
|
self.level = multiple
|
|
if multiple >= len(Bonuses.monster_bonuses):
|
|
multiple = len(Bonuses.monster_bonuses) - 1
|
|
img, pts = Bonuses.monster_bonuses[multiple]
|
|
Bonus.__init__(self, x, y, forceimg or img, pts)
|
|
|
|
def buildoutcome(self):
|
|
return (self.__class__, self.level)
|
|
|
|
def taken(self, dragon):
|
|
dragon.carrybonus(self, 543)
|
|
|
|
class IceMonsterBonus(MonsterBonus):
|
|
|
|
def __init__(self, x, y, multiple):
|
|
self.level = multiple
|
|
if multiple >= 1:
|
|
img, pts = Bonuses.violet_ice, 750
|
|
else:
|
|
img, pts = Bonuses.cyan_ice, 700
|
|
Bonus.__init__(self, x, y, img, pts)
|
|
|
|
|
|
|
|
class DustStar(ActiveSprite):
|
|
localrandom = random.Random()
|
|
|
|
def __init__(self, x, y, basedx, basedy, big=1, clock=0):
|
|
self.colorname = self.localrandom.choice(Stars.COLORS)
|
|
self.imgspeed = self.localrandom.randrange(3, 6)
|
|
self.rotation_reversed = self.localrandom.random() < 0.5
|
|
ico, imggen = self.select_ico(getattr(Stars, self.colorname))
|
|
ActiveSprite.__init__(self, ico, x, y)
|
|
self.setimages(imggen)
|
|
self.gen.append(self.fly(basedx, basedy, big))
|
|
if not big:
|
|
self.make_small()
|
|
elif clock:
|
|
self.setimages(None)
|
|
self.seticon(images.sprget(Bonuses.clock))
|
|
|
|
def select_ico(self, imglist):
|
|
if self.rotation_reversed:
|
|
imglist = list(imglist)
|
|
imglist.reverse()
|
|
return (images.sprget(imglist[-1]),
|
|
self.cyclic(imglist, self.imgspeed))
|
|
|
|
def make_small(self):
|
|
images = [('smstar', self.colorname, k) for k in range(2)]
|
|
ico, imggen = self.select_ico(images)
|
|
self.seticon(ico)
|
|
self.setimages(imggen)
|
|
|
|
def fly(self, dx, dy, big):
|
|
random = self.localrandom
|
|
dx += (random.random() - 0.5) * 2.8
|
|
dy += (random.random() - 0.5) * 2.8
|
|
fx = self.x
|
|
fy = self.y
|
|
if big:
|
|
j = 0
|
|
else:
|
|
j = 2
|
|
while j < 3:
|
|
ttl = random.expovariate(1.0 / 12)
|
|
if ttl > 35:
|
|
ttl = 35
|
|
for i in range(int(ttl)+4):
|
|
fx += dx
|
|
fy += dy
|
|
self.move(int(fx), int(fy))
|
|
yield None
|
|
if j == 0:
|
|
self.make_small()
|
|
fx += 8
|
|
fy += 8
|
|
j += 1
|
|
self.kill()
|
|
|
|
|
|
class RandomBonus(Bonus):
|
|
timeout = 500
|
|
|
|
class TemporaryBonus(RandomBonus):
|
|
captime = 0
|
|
bonusleveldivider = 2
|
|
def taken(self, dragon):
|
|
dragon.dcap[self.capname] += 1
|
|
self.carried(dragon)
|
|
def carried(self, dragon):
|
|
captime = self.captime
|
|
if boards.curboard.bonuslevel:
|
|
captime = (captime or 999) // self.bonusleveldivider
|
|
if captime:
|
|
dragon.carrybonus(self, captime)
|
|
else:
|
|
dragon.carrybonus(self)
|
|
self.endaction = None
|
|
def endaction(self, dragon):
|
|
if dragon.dcap[self.capname] >= 1:
|
|
dragon.dcap[self.capname] -= 1
|
|
|
|
|
|
class ShoeSpeed(RandomBonus):
|
|
"Fast Runner. Cumulative increase of horizontal speed."
|
|
nimage = Bonuses.shoe
|
|
bigbonus = {'multiply': 3}
|
|
bigdoc = "Run Really Fast."
|
|
def taken(self, dragon):
|
|
dragon.dcap['hspeed'] += 1
|
|
dragon.carrybonus(self)
|
|
|
|
class CoffeeSpeed(RandomBonus):
|
|
"Caffeine. Cumulative increase of the horizontal speed and fire rate."
|
|
nimage = Bonuses.coffee
|
|
big = 0
|
|
bigbonus = {'big': 1, 'multiply': 3}
|
|
bigdoc = "Super-Excited! Break through walls!"
|
|
def taken(self, dragon):
|
|
dragon.dcap['hspeed'] += 0.5
|
|
dragon.dcap['firerate'] += 1
|
|
if self.big:
|
|
dragon.dcap['breakwalls'] = 1
|
|
dragon.carrybonus(self)
|
|
|
|
class Butterfly(TemporaryBonus):
|
|
"Lunar Gravity. Allows you to jump twice as high as before."
|
|
nimage = Bonuses.butterfly
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Butterflies all around."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
import mnstrmap, monsters
|
|
for i in range(17):
|
|
monsters.Butterfly(mnstrmap.Butterfly,
|
|
self.x + random.randrange(-40, 41),
|
|
self.y + random.randrange(-30, 31),
|
|
random.choice([-1, 1]))
|
|
else:
|
|
TemporaryBonus.taken1(self, dragons)
|
|
def taken(self, dragon):
|
|
dragon.dcap['gravity'] *= 0.5
|
|
self.carried(dragon)
|
|
def endaction(self, dragon):
|
|
dragon.dcap['gravity'] *= 2.0
|
|
|
|
class Cocktail(TemporaryBonus):
|
|
"Short Lived Bubbles. Makes your bubbles explode more quickly."
|
|
nimage = Bonuses.cocktail
|
|
points = 2000
|
|
capname = 'bubbledelay'
|
|
bigbonus = {'multiply': 3}
|
|
bigdoc = "Makes your bubbles explode at once. Dangerous!"
|
|
|
|
class Extend(RandomBonus):
|
|
"E X T E N D. Gives you your missing letters and clear the level. "
|
|
nimage = Bonuses.extend
|
|
points = 0
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "A lot of letter bubbles! Run! Run!"
|
|
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
self.letterexplosion()
|
|
else:
|
|
RandomBonus.taken1(self, dragons)
|
|
|
|
def taken(self, dragon):
|
|
from bubbles import extend_name
|
|
names = [extend_name(l) for l in range(6)]
|
|
missing = [name for name in names if name not in dragon.bubber.letters]
|
|
x = dragon.x + dragon.ico.w//2
|
|
y = dragon.y
|
|
points(x, y, dragon, 10000*len(missing))
|
|
for l in range(6):
|
|
if extend_name(l) in missing:
|
|
dragon.bubber.giveletter(l, promize=0)
|
|
|
|
def letterexplosion(self):
|
|
from bubbles import LetterBubble
|
|
playercount = len([p for p in BubPlayer.PlayerList if p.isplaying()])
|
|
N = 3 + (playercount > 3)
|
|
angles = [i*(2.0*math.pi/N) for i in range(N)]
|
|
for l, dx, dy in [(0, 5, 9), (1, 16, 10), (2, 26, 8),
|
|
(3, 7, 23), (4, 15, 24), (5, 25, 24)]:
|
|
delta = 2.0*math.pi * random.random()
|
|
angles = [angle-delta for angle in angles]
|
|
x = self.x + self.ico.w//2 + 3*(dx-16)
|
|
y = self.y + self.ico.h//2 + 3*(dy-16)
|
|
for angle in angles:
|
|
bubble = LetterBubble(None, l)
|
|
bubble.thrown_bubble(x, y, 7.0 + 4.0 * random.random(),
|
|
(math.cos(angle), math.sin(angle)))
|
|
|
|
class HeartPoison(RandomBonus):
|
|
"Heart Poison. Freeze all free monsters."
|
|
nimage = Bonuses.heart_poison
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Freeze all other players too!"
|
|
def taken1(self, dragons):
|
|
import monsters
|
|
monsters.freeze_em_all()
|
|
if self.big:
|
|
def heart_pause(dragon, gen):
|
|
for i in range(222):
|
|
yield None
|
|
dragon.gen = gen
|
|
for d in BubPlayer.DragonList:
|
|
if d not in dragons:
|
|
d.gen = [heart_pause(d, d.gen)]
|
|
|
|
class VioletNecklace(RandomBonus):
|
|
"Monster Duplicator. Double the number of free monsters."
|
|
points = 650
|
|
nimage = Bonuses.violet_necklace
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "This level's boring, let's bring even more monsters..."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
import monsters, mnstrmap
|
|
mlist = 2*['Nasty', 'Monky', 'Springy', 'Orcy', 'Gramy', 'Blitzy']
|
|
wrange = (boards.bwidth - 8*CELL) // 2
|
|
for dir in [1, -1]:
|
|
for i in range(len(mlist)):
|
|
name = mlist[i]
|
|
mdef = getattr(mnstrmap, name)
|
|
cls = getattr(monsters, name)
|
|
x = wrange * i // len(mlist)
|
|
if dir == 1:
|
|
x = 2*CELL + HALFCELL + x
|
|
else:
|
|
x = boards.bwidth - 4*CELL - HALFCELL - x
|
|
y = -2*CELL - i * 2*CELL
|
|
cls(mdef, x, y, dir)
|
|
else:
|
|
for s in BubPlayer.MonsterList[:]:
|
|
if s.regular():
|
|
for i in range(self.multiply):
|
|
s.__class__(s.mdef, s.x, s.y, -s.dir * (-1)**i)
|
|
|
|
class WandBonus(RandomBonus):
|
|
"Wand/Chest. Turn the bubble into bonuses at the end of the level."
|
|
nimages = [Bonuses.brown_wand, Bonuses.yellow_wand, Bonuses.green_wand,
|
|
Bonuses.violet_wand, Bonuses.blue_wand, Bonuses.red_wand,
|
|
Bonuses.violet_chest, Bonuses.blue_chest, Bonuses.red_chest,
|
|
Bonuses.yellow_chest,
|
|
]
|
|
Modes = [
|
|
(Bonuses.brown_wand, 750, Bonuses.cyan_ice, 700, BigImages.cyan_ice, 20000),
|
|
(Bonuses.yellow_wand, 750, Bonuses.violet_ice, 750, BigImages.violet_ice, 20000),
|
|
(Bonuses.green_wand, 750, Bonuses.peach2, 800, BigImages.peach2, 30000),
|
|
(Bonuses.violet_wand, 750, Bonuses.pastec2, 850, BigImages.pastec2, 30000),
|
|
(Bonuses.blue_wand, 750, Bonuses.cream_pie, 900, BigImages.cream_pie, 40000),
|
|
(Bonuses.red_wand, 750, Bonuses.sugar_pie, 950, BigImages.sugar_pie, 40000),
|
|
(Bonuses.violet_chest, 2000, Diamonds.violet, 6000, BigImages.violet, 60000),
|
|
(Bonuses.blue_chest, 2000, Diamonds.blue, 7000, BigImages.blue, 60000),
|
|
(Bonuses.red_chest, 2000, Diamonds.red, 8000, BigImages.red, 70000),
|
|
(Bonuses.yellow_chest, 2000, Diamonds.yellow, 9000, BigImages.yellow, 70000),
|
|
]
|
|
def __init__(self, x, y):
|
|
self.mode = random.choice(WandBonus.Modes)
|
|
RandomBonus.__init__(self, x, y, *self.mode[:2])
|
|
def taken1(self, dragons):
|
|
BubPlayer.BubblesBecome = self.bubble_outcome
|
|
BubPlayer.MegaBonus = self.mega_bonus
|
|
def bubble_outcome(self, bubble):
|
|
if bubble.pop():
|
|
x = bubble.x
|
|
if x < 2*CELL:
|
|
x = 2*CELL
|
|
elif x > boards.bwidth - 4*CELL:
|
|
x = boards.bwidth - 4*CELL
|
|
Bonus(x, bubble.y, *self.mode[2:4])
|
|
def mega_bonus(self):
|
|
nico, npoints = self.mode[4:6]
|
|
ico = images.sprget(nico)
|
|
x = random.randrange(0, boards.bwidth-ico.w)
|
|
mb = Megabonus(x, -ico.h, nico, npoints)
|
|
mb.outcome = (Bonus,) + self.mode[2:4]
|
|
mb.outcome_image = self.mode[2]
|
|
WandBonus1 = WandBonus # increase probability
|
|
|
|
class Megabonus(Bonus):
|
|
touchable = 0
|
|
vspeed = 6
|
|
sound = 'Extra'
|
|
coverwithbonus = 99
|
|
fallerdelay = 71
|
|
|
|
def faller(self):
|
|
self.fullpoints = self.points
|
|
self.bubbles = {}
|
|
for t in range(self.fallerdelay):
|
|
yield None
|
|
self.ready_to_go()
|
|
self.bubbles_pos = list(self.bubbles_position())
|
|
self.gen.append(self.animate_bubbles())
|
|
y0 = self.y - HALFCELL
|
|
ymax = boards.bheight - CELL - self.ico.h
|
|
self.touchable = 1
|
|
ny = self.y
|
|
while self.y >= y0:
|
|
if self.vspeed:
|
|
ny += self.vspeed
|
|
if ny > ymax:
|
|
ny = ymax
|
|
self.vspeed = 0
|
|
self.move(self.x, int(ny))
|
|
yield None
|
|
self.kill()
|
|
|
|
def ready_to_go(self):
|
|
pass
|
|
|
|
def is_on_ground(self):
|
|
return self.y == boards.bheight - CELL - self.ico.h
|
|
|
|
def kill(self):
|
|
for bubble in list(self.bubbles.values()):
|
|
bubble.pop()
|
|
Bonus.kill(self)
|
|
|
|
def taken(self, dragon):
|
|
poplist = [dragon]
|
|
for bubble in list(self.bubbles.values()):
|
|
bubble.pop(poplist)
|
|
|
|
def bubbles_position(self):
|
|
import time; start=time.time()
|
|
cx = self.ico.w//2 - CELL
|
|
cy = self.ico.h//2 - CELL
|
|
positions = []
|
|
pi2 = math.pi * 2
|
|
dist = 10.0
|
|
for i in range(31):
|
|
while 1:
|
|
angle = random.random() * pi2
|
|
nx = cx + int(dist*math.sin(angle))
|
|
ny = cy + int(dist*math.cos(angle))
|
|
for ox, oy in positions:
|
|
if (nx-ox)*(nx-ox) + (ny-oy)*(ny-oy) < 220:
|
|
dist += 0.3
|
|
break
|
|
else:
|
|
break
|
|
positions.append((nx, ny))
|
|
#print time.time()-start
|
|
return positions
|
|
## nx = 5
|
|
## ny = 6
|
|
## xmargin = 2
|
|
## ymargin = 7
|
|
## xstep = (self.ico.w+2*xmargin-2*CELL) / float(nx-1)
|
|
## ystep = (self.ico.h+2*ymargin-2*CELL) / float(ny-1)
|
|
## for dx in range(nx):
|
|
## corner = dx in [0, nx-1]
|
|
## for dy in range(corner, ny-corner):
|
|
## dx1 = int(dx*xstep)-xmargin
|
|
## dy1 = int(dy*ystep)-ymargin
|
|
## yield (dx1 + random.randrange(-2,3),
|
|
## dy1 + random.randrange(-2,3))
|
|
|
|
def nearest_free_point(self, x0, y0):
|
|
distlst = [((x0-x)*(x0-x)+(y0-y)*(y0-y)+random.random(), x, y)
|
|
for x, y in self.bubbles_pos if (x, y) not in self.bubbles]
|
|
if distlst:
|
|
ignored, dx, dy = min(distlst)
|
|
return dx, dy
|
|
else:
|
|
return None, None
|
|
|
|
def in_bubble(self, bubble):
|
|
if not self.touchable:
|
|
return # bubbling a BonusMaker about to make a big bonus
|
|
dx, dy = self.nearest_free_point(bubble.x-self.x, bubble.y-self.y)
|
|
if dx is not None:
|
|
self.cover_bubble(dx, dy, bubble.d.bubber)
|
|
self.gen.append(self.cover_bubbles(bubble.d.bubber))
|
|
bubble.kill()
|
|
|
|
def cover_bubbles(self, bubber):
|
|
while 1:
|
|
for t in range(2):
|
|
yield None
|
|
bubbles = [dxy for dxy, b in list(self.bubbles.items())
|
|
if b.bubber is bubber]
|
|
if not bubbles:
|
|
break
|
|
dx, dy = self.nearest_free_point(*random.choice(bubbles))
|
|
if dx is None:
|
|
break
|
|
self.cover_bubble(dx, dy, bubber)
|
|
self.untouchable()
|
|
|
|
def cover_bubble(self, dx, dy, bubber):
|
|
if (dx, dy) in self.bubbles:
|
|
return
|
|
from bubbles import Bubble
|
|
if len(self.bubbles) & 1:
|
|
MegabonusBubble = Bubble
|
|
elif self.coverwithbonus:
|
|
self.coverwithbonus -= 1
|
|
outcome = self.outcome
|
|
outcome_image = self.outcome_image
|
|
class MegabonusBubble(Bubble):
|
|
def popped(self, dragon):
|
|
BonusMaker(self.x, self.y, [outcome_image], outcome=outcome)
|
|
return 10
|
|
else:
|
|
MegabonusBubble = Bubble
|
|
|
|
nimages = GreenAndBlue.normal_bubbles[bubber.pn]
|
|
b = MegabonusBubble(images.sprget(nimages[1]), self.x+dx, self.y+dy)
|
|
b.dx = dx
|
|
b.dy = dy
|
|
b.bubber = bubber
|
|
b.nimages = nimages
|
|
self.bubbles[dx, dy] = b
|
|
self.timeout = 0
|
|
f = float(len(self.bubbles)) / len(self.bubbles_pos)
|
|
self.vspeed = -0.73*f + self.vspeed*(1.0-f)
|
|
self.points = int(self.fullpoints*(1.0-f) / 10000.0 + 0.9999) * 10000
|
|
|
|
def animate_bubbles(self):
|
|
if 0: # disabled clipping
|
|
d = {}
|
|
for dx, dy in self.bubbles_pos:
|
|
d[dx] = d[dy] = None
|
|
north = d.copy()
|
|
south = d.copy()
|
|
west = d.copy()
|
|
east = d.copy()
|
|
del d
|
|
for dx, dy in self.bubbles_pos:
|
|
lst = [y for x, y in self.bubbles_pos if x==dx and y<dy]
|
|
if lst: north[dy] = max(lst)
|
|
lst = [y for x, y in self.bubbles_pos if x==dx and y>dy]
|
|
if lst: south[dy] = min(lst)
|
|
lst = [x for x, y in self.bubbles_pos if x<dx and y==dy]
|
|
if lst: west[dx] = max(lst)
|
|
lst = [x for x, y in self.bubbles_pos if x>dx and y==dy]
|
|
if lst: east[dx] = min(lst)
|
|
W = 2*CELL
|
|
H = 2*CELL
|
|
bubbles = self.bubbles
|
|
while 1:
|
|
for cycle in [1]*8 + [2]*10 + [1]*8 + [0]*10:
|
|
yield None
|
|
for (dx, dy), bubble in list(bubbles.items()):
|
|
if not hasattr(bubble, 'poplist'):
|
|
if 0: # disabled clipping
|
|
if (dx, north[dy]) in bubbles:
|
|
margin_n = (north[dy]+H-dy)//2
|
|
else:
|
|
margin_n = 0
|
|
if (dx, south[dy]) in bubbles:
|
|
margin_s = (dy+H-south[dy])//2
|
|
else:
|
|
margin_s = 0
|
|
if (west[dx], dy) in bubbles:
|
|
margin_w = (west[dx]+W-dx)//2
|
|
else:
|
|
margin_w = 0
|
|
if (east[dx], dy) in bubbles:
|
|
margin_e = (dx+W-east[dx])//2
|
|
else:
|
|
margin_e = 0
|
|
r = (margin_w,
|
|
margin_n,
|
|
W-margin_w-margin_e,
|
|
H-margin_n-margin_s)
|
|
bubble.move(self.x + bubble.dx + margin_w,
|
|
self.y + bubble.dy + margin_n,
|
|
images.sprget_subrect(
|
|
bubble.nimages[cycle], r))
|
|
else:
|
|
bubble.move(self.x + bubble.dx,
|
|
self.y + bubble.dy,
|
|
images.sprget(bubble.nimages[cycle]))
|
|
elif len(bubbles) == len(self.bubbles_pos):
|
|
self.pop_bubbles(bubble.poplist)
|
|
return
|
|
|
|
def pop_bubbles(self, poplist):
|
|
def bubble_timeout(bubble, vspeed):
|
|
ny = bubble.y
|
|
for t in range(random.randrange(15,25)):
|
|
if hasattr(bubble, 'poplist'):
|
|
return
|
|
ny += vspeed
|
|
bubble.move(bubble.x, int(ny))
|
|
yield None
|
|
bubble.pop(poplist)
|
|
|
|
for bubble in list(self.bubbles.values()):
|
|
bubble.gen.append(bubble_timeout(bubble, self.vspeed))
|
|
self.bubbles.clear()
|
|
self.kill()
|
|
|
|
class Cactus(RandomBonus):
|
|
"Cactus. Drop a big version of a random bonus."
|
|
points = 600
|
|
nimage = 'cactus'
|
|
extra_cheat_arg = None
|
|
bigbonus = {'multiply': 3}
|
|
bigdoc = "Let's get more big bonuses!"
|
|
|
|
def taken1(self, dragons):
|
|
count = 0
|
|
while count < self.multiply:
|
|
args = ()
|
|
if self.extra_cheat_arg:
|
|
cls = globals()[self.extra_cheat_arg]
|
|
self.extra_cheat_arg = None
|
|
elif bigclockticker and bigclockticker.state == 'pre':
|
|
cls = Clock
|
|
args = (1,)
|
|
else:
|
|
cls = random.choice(Classes)
|
|
if makecactusbonus(cls, *args):
|
|
count += 1
|
|
cactusbonussound()
|
|
|
|
#Cactus1 = Cactus # increase probability
|
|
|
|
OFFSCREEN = -3*CELL
|
|
def makecactusbonus(cls, *args):
|
|
bonus = cls(OFFSCREEN, 0, *args)
|
|
if not bonus.alive or getattr(bonus, 'bigbonus', None) is None:
|
|
if bonus.alive:
|
|
bonus.kill()
|
|
return None
|
|
bonus.__dict__.update(bonus.bigbonus)
|
|
bonus.untouchable()
|
|
bonus.gen = []
|
|
megacls = bonus.bigbonus.get('megacls', Cactusbonus)
|
|
mb = megacls(0, -3*CELL, 'cactus', 10000) # temp image
|
|
mb.outcome = (cls,) + (args or bonus.bigbonus.get('outcome_args', ()))
|
|
mb.outcome_image = bonus.nimage
|
|
mb.bonus = bonus
|
|
mb.gen.append(mb.prepare_image())
|
|
mb.gen.append(mb.remove_if_no_bonus())
|
|
return mb
|
|
|
|
def cactusbonussound():
|
|
gamesrv.set_musics([], [])
|
|
boards.curboard.set_musics(prefix=[images.music_modern])
|
|
boards.curboard.set_musics()
|
|
|
|
class Cactusbonus(Megabonus):
|
|
coverwithbonus = 5
|
|
|
|
def prepare_image(self):
|
|
while images.computebiggericon(self.bonus.ico) is None:
|
|
yield None
|
|
|
|
def remove_if_no_bonus(self):
|
|
while self.bonus.alive:
|
|
yield None
|
|
self.kill()
|
|
|
|
def ready_to_go(self):
|
|
ico = images.biggericon(self.bonus.ico)
|
|
x = random.randrange(0, boards.bwidth-ico.w)
|
|
self.move(x, -ico.h, ico)
|
|
|
|
def taken1(self, dragons):
|
|
d1 = list(dragons)
|
|
Megabonus.taken1(self, dragons)
|
|
if self.bonus.alive:
|
|
x = self.x + self.ico.w//2 - CELL
|
|
y = self.y + self.ico.h//2 - CELL
|
|
self.bonus.move(x, y)
|
|
res = self.bonus.taken1(d1)
|
|
self.untouchable()
|
|
if res == -1:
|
|
self.taken_by = []
|
|
self.gen.append(self.touchdelay(10))
|
|
self.bonus.move(OFFSCREEN, 0)
|
|
else:
|
|
self.bonus.kill()
|
|
return res
|
|
|
|
def kill(self):
|
|
Megabonus.kill(self)
|
|
if self.bonus.alive:
|
|
self.bonus.kill()
|
|
|
|
class LongDurationCactusbonus(Cactusbonus):
|
|
timeout = 500
|
|
killgens = 0
|
|
|
|
def starexplosion(x, y, multiplyer, killmonsters=0, outcomes=[]):
|
|
outcomes = list(outcomes)
|
|
poplist = [None]
|
|
for i in range(multiplyer):
|
|
colors = list(Stars.COLORS)
|
|
random.shuffle(colors)
|
|
for colorname in colors:
|
|
images = getattr(Stars, colorname)
|
|
if outcomes:
|
|
outcome = outcomes.pop()
|
|
extra_stars = []
|
|
if hasattr(outcome[0], 'extra_stars_location'):
|
|
for sx, sy in outcome[0].extra_stars_location:
|
|
extra_stars.append(BonusMakerExtraStar(x, y, sx, sy,
|
|
colorname))
|
|
bm = BonusMaker(x, y, images, outcome=outcome)
|
|
for star in extra_stars:
|
|
star.gen.append(star.follow_bonusmaker(bm))
|
|
else:
|
|
b = Parabolic2(x, y, images)
|
|
if killmonsters:
|
|
b.gen.append(b.killmonsters(poplist))
|
|
|
|
class HomingStar(ActiveSprite):
|
|
def __init__(self, x, y, colorname, poplist):
|
|
imglist = getattr(Stars, colorname)
|
|
ActiveSprite.__init__(self, images.sprget(imglist[0]), x, y)
|
|
self.colorname = colorname
|
|
self.setimages(self.cyclic(imglist, 2))
|
|
self.gen.append(self.homing(poplist))
|
|
|
|
def homing(self, poplist):
|
|
from monsters import Monster
|
|
target = None
|
|
vx = (random.random() - 0.5) * 6.6
|
|
vy = (random.random() - 0.5) * 4.4
|
|
nx = self.x
|
|
ny = self.y
|
|
counter = 10
|
|
while 1:
|
|
if random.random() < 0.02:
|
|
target = None
|
|
if target is None or not target.alive:
|
|
bestdist = 1E10
|
|
for s in BubPlayer.MonsterList:
|
|
if isinstance(s, Monster):
|
|
dx = s.x - nx
|
|
dy = s.y - ny
|
|
dist = dx*dx + dy*dy + (random.random() * 25432.1)
|
|
if dist < bestdist:
|
|
bestdist = dist
|
|
target = s
|
|
if target is None:
|
|
break
|
|
dx = target.x - nx
|
|
dy = target.y - ny
|
|
dist = dx*dx + dy*dy
|
|
if dist <= 3*CELL*CELL:
|
|
target.argh(poplist)
|
|
break
|
|
yield None
|
|
vx = (vx + dx * 0.005) * 0.96
|
|
vy = (vy + dy * 0.005) * 0.96
|
|
nx += vx
|
|
ny += vy
|
|
self.move(int(nx), int(ny))
|
|
if counter:
|
|
counter -= 1
|
|
else:
|
|
img = ('smstar', self.colorname, random.randrange(2))
|
|
s = ActiveSprite(images.sprget(img), self.x + 8, self.y + 8)
|
|
s.gen.append(s.die([None], speed=10))
|
|
counter = 3
|
|
self.kill()
|
|
|
|
class Book(RandomBonus):
|
|
"Magic Bomb. Makes a magical explosion killing touched monsters."
|
|
points = 2000
|
|
nimage = Bonuses.book
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Homing Magical Stars."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
poplist = [None]
|
|
x = self.x + (self.ico.w - 2*CELL) // 2
|
|
y = self.y + (self.ico.h - 2*CELL) // 2
|
|
colors = list(Stars.COLORS)
|
|
random.shuffle(colors)
|
|
for colorname in colors + colors[len(colors)//2:]:
|
|
HomingStar(x, y, colorname, poplist)
|
|
else:
|
|
starexplosion(self.x, self.y, self.multiply, killmonsters=1)
|
|
|
|
class Potion(RandomBonus):
|
|
"Potions. Clear the level and fill its top with bonuses."
|
|
nimages = [Bonuses.red_potion, Bonuses.green_potion, Bonuses.yellow_potion,
|
|
'potion4']
|
|
Potions = [(Bonuses.red_potion, 150, [(PotionBonuses.coin, 350),
|
|
(PotionBonuses.rainbow, 600)]),
|
|
(Bonuses.green_potion, 350, [(PotionBonuses.flower, 1000),
|
|
(PotionBonuses.trefle, 2000)]),
|
|
(Bonuses.yellow_potion, 550, [(PotionBonuses.green_note, 2000),
|
|
(PotionBonuses.blue_note, 3000)]),
|
|
('potion4', 750, None),
|
|
]
|
|
LocalDir = os.path.dirname(__file__) or os.curdir
|
|
Extensions = [s for s in os.listdir(LocalDir)
|
|
if s.startswith('ext') and
|
|
os.path.isdir(os.path.join(LocalDir, s))]
|
|
random.shuffle(Extensions)
|
|
extra_cheat_arg = None
|
|
big = 0
|
|
bigdoc = "Fill the whole level with bonuses."
|
|
|
|
def __init__(self, x, y):
|
|
p_normal = 3
|
|
if boards.curboard.bonuslevel:
|
|
p_extension = 2 # make extensions rare in the bonus level
|
|
else:
|
|
p_extension = 5
|
|
if self.extra_cheat_arg:
|
|
Potion.Extensions.append(self.extra_cheat_arg)
|
|
p_normal = 0
|
|
if not Potion.Extensions:
|
|
p_extension = 0
|
|
choices = []
|
|
for mode in Potion.Potions:
|
|
if mode[2] is None:
|
|
p = p_extension
|
|
else:
|
|
p = p_normal
|
|
choices += [mode] * p
|
|
self.mode = random.choice(choices)
|
|
if self.mode[2] is not None:
|
|
self.bigbonus = {'big': 1}
|
|
RandomBonus.__init__(self, x, y, *self.mode[:2])
|
|
def taken1(self, dragons):
|
|
blist = self.mode[2]
|
|
if blist is not None:
|
|
if random.random() < 0.6:
|
|
blist = [random.choice(blist)]
|
|
boards.replace_boardgen(boards.potion_fill(blist, self.big))
|
|
else:
|
|
n_players = len([p for p in BubPlayer.PlayerList if p.isplaying()])
|
|
while Potion.Extensions:
|
|
ext = Potion.Extensions.pop()
|
|
#print "Trying potion:", ext
|
|
ext = __import__(ext, globals(),locals(), ['run','min_players'])
|
|
if n_players >= ext.min_players:
|
|
ext.run()
|
|
boards.BoardGen.append(boards.extra_bkgnd_black(self.x, self.y))
|
|
#print "Accepted because:", n_players, ">=", ext.min_players
|
|
break
|
|
else:
|
|
#print "Rejected because:", n_players, "<", ext.min_players
|
|
pass
|
|
|
|
class FireBubble(RandomBonus):
|
|
"Fire Bubbles. Makes you fire napalm bubbles."
|
|
nimage = Bonuses.hamburger
|
|
bubkind = 'FireBubble'
|
|
bubcount = 10
|
|
bigbonus = {'bubkind': 'BigFireBubble'}
|
|
bigdoc = "Makes you shoot fire - you're a dragon after all."
|
|
def taken(self, dragon):
|
|
dragon.dcap['shootbubbles'] = [self.bubkind] * self.bubcount
|
|
dragon.carrybonus(self)
|
|
|
|
class WaterBubble(FireBubble):
|
|
"Water Bubbles. Your bubbles will now be filled with water."
|
|
nimage = Bonuses.beer
|
|
bubkind = 'WaterBubble'
|
|
bigbonus = {'bubkind': 'SnookerBubble'}
|
|
bigdoc = "Snooker balls."
|
|
|
|
class LightningBubble(FireBubble):
|
|
"Lightning Bubbles."
|
|
nimage = Bonuses.french_fries
|
|
bubkind = 'LightningBubble'
|
|
bigbonus = {'bubkind': 'BigLightBubble'}
|
|
bigdoc = "Even-more-lightning Bubbles."
|
|
|
|
class Megadiamond(Megabonus):
|
|
nimage = BigImages.red
|
|
points = 20000
|
|
fallerdelay = 0
|
|
outcome = (MonsterBonus, -1)
|
|
outcome_image = Bonuses.monster_bonuses[-1][0]
|
|
extra_stars_location = [ (-24,-28),(0,-28),(24,-28),
|
|
(-40,-11), (40,-11),
|
|
(-32, 8), (32, 8),
|
|
(-16,23), (16,23),
|
|
(0,38), ]
|
|
def __init__(self, x, y):
|
|
ico = images.sprget(self.nimage)
|
|
x -= (ico.w - 2*CELL) // 2
|
|
y -= (ico.h - 2*CELL) // 2
|
|
Megabonus.__init__(self, x, y)
|
|
|
|
class Door(RandomBonus):
|
|
"Magic Door. Let bonuses come in!"
|
|
points = 1000
|
|
nimage = Bonuses.door
|
|
diamond_outcome = (MonsterBonus, -1)
|
|
bigbonus = {'diamond_outcome': (Megadiamond,)}
|
|
bigdoc = "Let bigger bonuses come in!"
|
|
def taken1(self, dragons):
|
|
starexplosion(self.x, self.y, 2,
|
|
outcomes = [self.diamond_outcome] * 10)
|
|
|
|
class LongFire(RandomBonus):
|
|
"Long Fire. Increase the range of your bubble throw out."
|
|
nimage = Bonuses.softice1
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Throw bubbles that split into more bubbles."
|
|
def taken(self, dragon):
|
|
if self.big:
|
|
dragon.dcap['shootbubbles'] = ['MoreBubblesBubble'] * 10
|
|
else:
|
|
dragon.dcap['shootthrust'] *= 1.5
|
|
dragon.carrybonus(self)
|
|
|
|
class Glue(RandomBonus):
|
|
"Glue. Triple fire."
|
|
nimage = 'glue'
|
|
points = 850
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Heptuple fire. (That's 7.)"
|
|
def taken(self, dragon):
|
|
if self.big:
|
|
dragon.dcap['flower'] = -16 # heptuple fire
|
|
elif dragon.dcap['flower'] >= 0:
|
|
dragon.dcap['flower'] = -1 # triple fire
|
|
else:
|
|
dragon.dcap['flower'] -= 1 # cumulative effect
|
|
dragon.carrybonus(self)
|
|
|
|
class ShortFire(RandomBonus):
|
|
"Short Fire. Shorten the range of your bubble throw out."
|
|
nimage = Bonuses.softice2
|
|
points = 300
|
|
factor = 1 / 1.5
|
|
bigbonus = {'factor': 0}
|
|
bigdoc = "What occurs if you throw bubbles at range zero?"
|
|
def taken(self, dragon):
|
|
dragon.dcap['shootthrust'] *= self.factor
|
|
dragon.carrybonus(self)
|
|
|
|
class HighSpeedFire(RandomBonus):
|
|
"High Speed Fire. Increase your fire rate."
|
|
nimage = Bonuses.custard_pie
|
|
points = 700
|
|
bigbonus = {'multiply': 4}
|
|
bigdoc = "Machine-gun speed!"
|
|
def taken(self, dragon):
|
|
dragon.dcap['firerate'] += 1.5
|
|
dragon.carrybonus(self)
|
|
|
|
class Mushroom(TemporaryBonus):
|
|
"Bouncy Bouncy. Makes you jump continuously."
|
|
nimage = Bonuses.mushroom
|
|
points = 900
|
|
capname = 'pinball'
|
|
captime = 625
|
|
bigbonus = {'captime': captime*2, 'multiply': 2}
|
|
bigdoc = "The same, but even more annoying."
|
|
|
|
class AutoFire(TemporaryBonus):
|
|
"Auto Fire. Makes you fire continuously."
|
|
nimage = Bonuses.rape
|
|
points = 800
|
|
capname = 'autofire'
|
|
captime = 675
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Adds many bubbles to the level."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
boards.extra_boardgen(boards.extra_bubbles(900))
|
|
else:
|
|
TemporaryBonus.taken1(self, dragons)
|
|
|
|
class Insect(RandomBonus):
|
|
"Crush World."
|
|
nimage = Bonuses.insect
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "What if the level looked like that instead... Or like that... Or..."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
if dragons:
|
|
d = random.choice(dragons)
|
|
cx, cy = d.x, d.y
|
|
else:
|
|
cx, cy = None, None
|
|
boards.extra_boardgen(boards.extra_make_random_level(cx, cy))
|
|
else:
|
|
boards.extra_boardgen(boards.extra_walls_falling())
|
|
|
|
class Ring(TemporaryBonus):
|
|
"The One Ring."
|
|
nimage = Bonuses.ring
|
|
points = 4000
|
|
capname = 'ring'
|
|
captime = 700
|
|
bonusleveldivider = 5
|
|
bigbonus = {'multiply': 3}
|
|
bigdoc = "Where am I?"
|
|
|
|
class GreenPepper(TemporaryBonus):
|
|
"Hot Pepper. Run! Run! That burns."
|
|
nimage = Bonuses.green_pepper
|
|
capname = 'hotstuff'
|
|
captime = 100
|
|
bigbonus = {'captime': 250, 'multiply': 2}
|
|
bigdoc = "That burns a lot!"
|
|
|
|
class Lollipop(TemporaryBonus):
|
|
"Yo Man! Makes you walk backward."
|
|
nimage = Bonuses.lollipop
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Just swapping 'left' and 'right' is not confusing enough."
|
|
def taken(self, dragon):
|
|
dragon.dcap['left2right'] = -dragon.dcap['left2right']
|
|
if self.big:
|
|
perm = list(range(4))
|
|
while perm[0] == 0 or perm[1] == 1 or perm[2] == 2 or perm[3] == 3:
|
|
random.shuffle(perm)
|
|
names = ('key_left', 'key_right', 'key_jump', 'key_fire')
|
|
dragon.dcap['key_right'] = names[perm[0]]
|
|
dragon.dcap['key_left'] = names[perm[1]]
|
|
dragon.dcap['key_jump'] = names[perm[2]]
|
|
dragon.dcap['key_fire'] = names[perm[3]]
|
|
self.carried(dragon)
|
|
def endaction(self, dragon):
|
|
dragon.dcap['left2right'] = -dragon.dcap['left2right']
|
|
for name in ('key_left', 'key_right', 'key_jump', 'key_fire'):
|
|
dragon.dcap[name] = name
|
|
|
|
class Chickpea(TemporaryBonus):
|
|
"Basilik. Allows you to touch the monsters."
|
|
nimage = Bonuses.chickpea
|
|
points = 800
|
|
capname = 'overlayglasses'
|
|
captime = 400
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Turn off the light."
|
|
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
boards.extra_boardgen(boards.extra_light_off(597), 1)
|
|
else:
|
|
TemporaryBonus.taken1(self, dragons)
|
|
|
|
def taken(self, dragon):
|
|
TemporaryBonus.taken(self, dragon)
|
|
dragon.dcap['shield'] += 420
|
|
|
|
class IceCream(RandomBonus):
|
|
"Icecream. An icecream which is so good you'll always want more."
|
|
nimages = [Bonuses.icecream6, Bonuses.icecream5,
|
|
Bonuses.icecream4, Bonuses.icecream3]
|
|
IceCreams = [(Bonuses.icecream6, 250),
|
|
(Bonuses.icecream5, 500),
|
|
(Bonuses.icecream4, 1000),
|
|
(Bonuses.icecream3, 2000)]
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "BIG ice creams!"
|
|
def __init__(self, x, y, generation=0):
|
|
self.generation = generation
|
|
RandomBonus.__init__(self, x, y, *self.IceCreams[generation])
|
|
def taken1(self, dragons):
|
|
nextgen = self.generation + 1
|
|
if nextgen < len(self.IceCreams):
|
|
for i in range(2):
|
|
if self.big:
|
|
makecactusbonus(IceCream, nextgen)
|
|
else:
|
|
x, y = chooseground(200)
|
|
if x is None:
|
|
return
|
|
IceCream(x, y, nextgen)
|
|
if self.big:
|
|
cactusbonussound()
|
|
|
|
class Grenade(RandomBonus):
|
|
"Barbecue."
|
|
nimage = Bonuses.grenade
|
|
points = 550
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "360-degree flames."
|
|
def taken1(self, dragons):
|
|
from bubbles import FireFlame
|
|
poplist = [None]
|
|
for y in range(1, boards.height-1):
|
|
for x in range(2, boards.width-2):
|
|
if bget(x,y) != ' ':
|
|
continue
|
|
if bget(x,y+1) == '#':
|
|
FireFlame(x, y, poplist)
|
|
elif self.big:
|
|
if bget(x,y-1) == '#':
|
|
FireFlame(x, y, poplist, flip='vflip')
|
|
elif bget(x-1,y) == '#':
|
|
FireFlame(x, y, poplist, flip='cw')
|
|
elif bget(x+1,y) == '#':
|
|
FireFlame(x, y, poplist, flip='ccw')
|
|
|
|
class Conch(RandomBonus):
|
|
"Sea Shell. Let's bring the sea here!"
|
|
nimage = Bonuses.conch
|
|
points = 650
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Aquarium."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
gen = boards.extra_aquarium
|
|
else:
|
|
gen = boards.extra_water_flood
|
|
boards.extra_boardgen(gen())
|
|
|
|
def fire_rain(x, poplist):
|
|
from bubbles import FireDrop
|
|
FireDrop(x, -CELL, poplist)
|
|
|
|
def water_rain(x, poplist):
|
|
from bubbles import watercell
|
|
watercell(x, 0, poplist)
|
|
|
|
def ball_rain(x, poplist):
|
|
from bubbles import SpinningBall
|
|
SpinningBall(x, -CELL, poplist)
|
|
|
|
class Umbrella(RandomBonus):
|
|
"Umbrellas. Beware of what's going to fall on everyone's head!"
|
|
nimages = [Bonuses.brown_umbrella, Bonuses.grey_umbrella,
|
|
Bonuses.violet_umbrella]
|
|
Umbrellas = [(Bonuses.brown_umbrella, 900, fire_rain, 10, 60),
|
|
(Bonuses.grey_umbrella, 950, water_rain, 5, 60),
|
|
(Bonuses.violet_umbrella, 1000, ball_rain, 9, 120)]
|
|
bigbonus = {'multiply': 3.1416}
|
|
bigdoc = "It's raining hard."
|
|
def __init__(self, x, y):
|
|
self.mode = random.choice(Umbrella.Umbrellas)
|
|
RandomBonus.__init__(self, x, y, *self.mode[:2])
|
|
def taken1(self, dragons):
|
|
for i in range(self.multiply):
|
|
boards.extra_boardgen(self.raining())
|
|
def raining(self):
|
|
builder, drops, timemax = self.mode[2:]
|
|
timemax = int(timemax * math.sqrt(self.multiply))
|
|
drops = int(drops * self.multiply)
|
|
times = [random.randrange(0, timemax) for i in range(drops)]
|
|
poplist = [None]
|
|
for t in range(timemax):
|
|
for i in range(times.count(t)):
|
|
x = random.randrange(2*CELL, bwidth-3*CELL+1)
|
|
builder(x, poplist)
|
|
yield 0
|
|
|
|
class Fruits(RandomBonus):
|
|
"Fruits. A small little bonus. But the size doesn't matter, does it? If you're lucky enough you might get a great shower!"
|
|
nimages = [Bonuses.kirsh, Bonuses.erdbeer, Bonuses.tomato,
|
|
Bonuses.apple, Bonuses.corn, Bonuses.radish]
|
|
bubblable = 0
|
|
sound = 'Extra'
|
|
Fruits = [(Bonuses.kirsh, 100),
|
|
#(Bonuses.icecream1, 150),
|
|
(Bonuses.erdbeer, 150),
|
|
#(Bonuses.fish1, 250),
|
|
(Bonuses.tomato, 200),
|
|
#(Bonuses.donut, 250),
|
|
(Bonuses.apple, 250),
|
|
(Bonuses.corn, 300),
|
|
#(Bonuses.icecream2, 600),
|
|
(Bonuses.radish, 350),
|
|
]
|
|
def __init__(self, x, y): # x and y ignored !
|
|
fine = 0
|
|
for i in range(20):
|
|
x0 = random.randint(3, boards.width-5)
|
|
y0 = random.randint(1, boards.height-3)
|
|
for xt in range(x0-1, x0+3):
|
|
if xt == x0-1 or xt == x0+2:
|
|
yplus = 1
|
|
else:
|
|
yplus = 0
|
|
for yt in range(y0+yplus, y0+4-yplus):
|
|
if bget(xt,yt) != ' ':
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
else:
|
|
x, y = x0*CELL, y0*CELL
|
|
fine = 1
|
|
break
|
|
mode = random.choice(Fruits.Fruits)
|
|
RandomBonus.__init__(self, x, y, falling=0, *mode)
|
|
self.repeatcount = 0
|
|
if not fine:
|
|
self.kill()
|
|
elif random.random() < 0.04:
|
|
self.superfruit = mode
|
|
self.sound = 'Shh'
|
|
self.points = 0
|
|
self.repeatcount = random.randrange(50,100)
|
|
def taken1(self, dragons):
|
|
if self.repeatcount:
|
|
image, points = self.superfruit
|
|
f = Parabolic2(self.x, self.y, [image], y_amplitude = -1.5)
|
|
f.points = points
|
|
f.touchable = 1
|
|
self.repeatcount -= 1
|
|
self.gen.append(self.taking(1, 2))
|
|
return -1
|
|
Fruits1 = Fruits # increase probability
|
|
Fruits2 = Fruits
|
|
Fruits3 = Fruits
|
|
Fruits4 = Fruits
|
|
Fruits5 = Fruits
|
|
Fruits6 = Fruits
|
|
|
|
class BlueNecklace(RandomBonus):
|
|
"Self Duplicator. Mirror yourself."
|
|
points = 1000
|
|
nimage = Bonuses.blue_necklace
|
|
copies = 1
|
|
bigbonus = {'copies': 3}
|
|
bigdoc = "Mirrors vertically too."
|
|
def taken(self, dragon):
|
|
dragons = [dragon]
|
|
modes = [(-1, 1), (1, -1), (-1, -1)][:self.copies]
|
|
modes.reverse()
|
|
dcap = dragon.dcap.copy()
|
|
for sign, gravity in modes:
|
|
if len(dragon.bubber.dragons) >= 7:
|
|
break # avoid burning the server with two much dragons
|
|
d1 = self.makecopy(dragon, sign, gravity, dcap)
|
|
dragons.append(d1)
|
|
d1 = random.choice(dragons)
|
|
d1.carrybonus(self, 250)
|
|
|
|
def makecopy(self, dragon, sign=-1, gravity=1, dcap=None):
|
|
from player import Dragon
|
|
dcap = dcap or dragon.dcap
|
|
d = Dragon(dragon.bubber, dragon.x, dragon.y, -dragon.dir, dcap)
|
|
d.dcap['left2right'] = sign * dcap['left2right']
|
|
d.dcap['gravity'] = gravity * dcap['gravity']
|
|
d.up = dragon.up
|
|
s = (dcap['shield'] + 12) & ~3
|
|
dragon.dcap['shield'] = s+2
|
|
if sign*gravity > 0:
|
|
s += 2
|
|
d.dcap['shield'] = s
|
|
dragon.bubber.dragons.append(d)
|
|
return d
|
|
|
|
class Monsterer(RandomBonus):
|
|
"Monsterificator. Let's play on the other side!"
|
|
nimages = [Bonuses.red_crux, Bonuses.blue_crux]
|
|
Sizes = [(Bonuses.red_crux, 800), (Bonuses.blue_crux, 850)]
|
|
mlist = [['Nasty', 'Monky', 'Springy', 'Orcy'],
|
|
['Ghosty', 'Flappy', 'Gramy', 'Blitzy']
|
|
]
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Ta, ta ta, ta, taaaaaa..."
|
|
def __init__(self, x, y):
|
|
self.mode = random.choice([0,1])
|
|
RandomBonus.__init__(self, x, y, *self.Sizes[self.mode])
|
|
def taken(self, dragon):
|
|
mcls = random.choice(self.mlist[self.mode])
|
|
dragon.become_monster(mcls, self.big)
|
|
|
|
Monsterer1 = Monsterer # increase probability
|
|
|
|
class Bubblizer(RandomBonus):
|
|
"Bubblizer."
|
|
points = 750
|
|
nimage = Bonuses.gold_crux
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Special powers for your bubble."
|
|
def taken(self, dragon):
|
|
args = (dragon.bubber.pn,)
|
|
if self.big:
|
|
from bubbles import SnookerBubble, BigLightBubble
|
|
bcls = random.choice([SnookerBubble, BigLightBubble])
|
|
if bcls is SnookerBubble:
|
|
args = (dragon, dragon.x, dragon.y, 1000000)
|
|
else:
|
|
from bubbles import FireBubble, WaterBubble, LightningBubble
|
|
bcls = random.choice([FireBubble, WaterBubble, LightningBubble])
|
|
b = bcls(*args)
|
|
b.move(dragon.x, dragon.y)
|
|
if not dragon.become_bubblingeyes(b):
|
|
b.kill()
|
|
|
|
class Carrot(RandomBonus):
|
|
"Angry Monster. Turns all free monsters angry."
|
|
nimage = Bonuses.carrot
|
|
points = 950
|
|
ghost = 0
|
|
bigbonus = {'ghost': 1}
|
|
bigdoc = "What do angry monsters turn into if you don't hurry up?"
|
|
def taken1(self, dragons):
|
|
from monsters import Monster
|
|
lst = [s for s in images.ActiveSprites
|
|
if isinstance(s, Monster) and s.regular()]
|
|
if lst:
|
|
if self.ghost:
|
|
images.Snd.Hell.play()
|
|
for s in lst:
|
|
s.become_ghost()
|
|
else:
|
|
for s in lst:
|
|
s.angry = [s.genangry()]
|
|
s.resetimages()
|
|
|
|
class Egg(RandomBonus):
|
|
"Teleporter. Exchange yourself with somebody else."
|
|
nimage = Bonuses.egg
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Exchange colors too."
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
self.exchange_bubbers()
|
|
else:
|
|
self.exchange_dragons(dragons)
|
|
|
|
def exchange_dragons(self, dragons):
|
|
dragons = [d for d in dragons if d in d.bubber.dragons]
|
|
alldragons = [d for d in BubPlayer.DragonList if d in d.bubber.dragons]
|
|
others = [d for d in alldragons if d not in dragons]
|
|
xchg = {}
|
|
random.shuffle(dragons)
|
|
random.shuffle(others)
|
|
while dragons and others:
|
|
d1 = dragons.pop()
|
|
d2 = others.pop()
|
|
xchg[d1] = d2.bubber
|
|
xchg[d2] = d1.bubber
|
|
if len(dragons) > 1:
|
|
copy = dragons[:]
|
|
for i in range(10):
|
|
random.shuffle(copy)
|
|
for j in range(len(dragons)):
|
|
if dragons[j] == copy[j]:
|
|
break
|
|
else:
|
|
break
|
|
for d1, d2 in zip(dragons, copy):
|
|
xchg[d1] = d2.bubber
|
|
elif len(dragons) == 1:
|
|
x, y = chooseground(200)
|
|
if x is not None:
|
|
d1 = dragons[0]
|
|
d1.move(x, y)
|
|
d1.dcap['shield'] = 50
|
|
for d1, bubber2 in list(xchg.items()):
|
|
d1.bubber.dragons.remove(d1)
|
|
d1.bubber = bubber2
|
|
bubber2.dragons.append(d1)
|
|
d1.dcap['shield'] = 50
|
|
|
|
def exchange_bubbers(self):
|
|
self.exchange_dragons(list(BubPlayer.DragonList))
|
|
players = [p for p in BubPlayer.PlayerList
|
|
if p.isplaying()]
|
|
if len(players) > 1:
|
|
while 1:
|
|
copy = players[:]
|
|
random.shuffle(copy)
|
|
for j in range(len(players)):
|
|
if players[j] is copy[j]:
|
|
break
|
|
else:
|
|
break
|
|
for b1, b2 in zip(players, copy):
|
|
for d in b1.dragons:
|
|
d.dcap['bubbericons'] = b2
|
|
|
|
class Bomb(RandomBonus):
|
|
"Baaoouuuummmm! Explode that wall!"
|
|
nimage = Bonuses.bomb
|
|
bigbonus = {'multiply': 3.8}
|
|
bigdoc = "Makes a BIG hole."
|
|
def taken1(self, dragons):
|
|
bomb_explosion(self.x, self.y, self.multiply)
|
|
|
|
def bomb_explosion(x0, y0, multiply=1, starmul=2):
|
|
RADIUS = 3.9 * CELL * math.sqrt(multiply)
|
|
Radius2 = RADIUS * RADIUS
|
|
brd = boards.curboard
|
|
cx = x0 + HALFCELL
|
|
cy = y0 + HALFCELL - RADIUS/2
|
|
for y in range(0, brd.height):
|
|
dy1 = abs(y*CELL - cy)
|
|
dy2 = abs((y-(brd.height-1))*CELL - cy)
|
|
dy3 = abs((y+(brd.height-1))*CELL - cy)
|
|
dy = min(dy1, dy2, dy3)
|
|
for x in range(2, brd.width-2):
|
|
dx = x*CELL - cx
|
|
if dx*dx + dy*dy < Radius2:
|
|
try:
|
|
brd.killwall(x,y)
|
|
except KeyError:
|
|
pass
|
|
brd.reorder_walls()
|
|
starexplosion(x0, y0, starmul)
|
|
gen = boards.extra_display_repulse(x0+CELL, y0+CELL,
|
|
15000 * multiply,
|
|
1000 * multiply)
|
|
boards.extra_boardgen(gen)
|
|
|
|
class Ham(RandomBonus):
|
|
"Protein. Let's build something!"
|
|
nimage = Bonuses.ham
|
|
bigbonus = {'multiply': 3.4}
|
|
bigdoc = "Builds something BIG."
|
|
def taken1(self, dragons):
|
|
RADIUS = 3.9 * CELL * math.sqrt(self.multiply)
|
|
Radius2 = RADIUS * RADIUS
|
|
brd = boards.curboard
|
|
cx = self.x + HALFCELL
|
|
cy = self.y + HALFCELL - RADIUS/2
|
|
xylist = []
|
|
for y in range(0, brd.height):
|
|
dy1 = abs(y*CELL - cy)
|
|
dy2 = abs((y-(brd.height-1))*CELL - cy)
|
|
dy3 = abs((y+(brd.height-1))*CELL - cy)
|
|
dy = min(dy1, dy2, dy3)
|
|
for x in range(2, brd.width-2):
|
|
dx = x*CELL - cx
|
|
if dx*dx + dy*dy < Radius2:
|
|
if (y,x) not in brd.walls_by_pos and random.random() < 0.5:
|
|
brd.putwall(x,y)
|
|
xylist.append((x, y))
|
|
brd.reorder_walls()
|
|
boards.extra_boardgen(boards.single_blocks_falling(xylist))
|
|
gen = boards.extra_display_repulse(self.x+CELL, self.y+CELL,
|
|
5000 * self.multiply,
|
|
1000 * self.multiply)
|
|
boards.extra_boardgen(gen)
|
|
|
|
class Chestnut(RandomBonus):
|
|
"Relativity. Speed up or slow down the game."
|
|
nimage = Bonuses.chestnut
|
|
sound = None
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Relative relativity - not the same one for players and monsters."
|
|
def taken1(self, dragons):
|
|
timeout = 500
|
|
if not self.big:
|
|
ft = random.choice([0.5, 2.0])
|
|
boards.set_frametime(ft)
|
|
if ft == 2.0:
|
|
timeout = 430
|
|
else:
|
|
if random.randrange(0, 2) == 1:
|
|
# super-fast game
|
|
boards.set_frametime(0.25)
|
|
timeout = 1800
|
|
else:
|
|
# board unchanged, players slower
|
|
boards.set_frametime(1.0, privtime=250)
|
|
timeout = 800
|
|
BubPlayer.MultiplyerReset = BubPlayer.FrameCounter + timeout
|
|
self.play(images.Snd.Fruit)
|
|
|
|
|
|
try:
|
|
import statesaver
|
|
except ImportError:
|
|
print("'statesaver' module not compiled, no clock bonus")
|
|
Clock = None
|
|
else:
|
|
import new
|
|
try:
|
|
from statesaver import standard_build # PyPy
|
|
except ImportError:
|
|
def standard_build(self):
|
|
return new.instance(self.__class__)
|
|
boards.Copyable.inst_build = standard_build
|
|
gamesrv.Sprite.inst_build = standard_build
|
|
|
|
def copygamestate():
|
|
# makes a copy of the game state.
|
|
ps = []
|
|
for p1 in BubPlayer.PlayerList:
|
|
#if p1.isplaying():
|
|
d = p1.__dict__.copy()
|
|
for key in BubPlayer.TRANSIENT_DATA:
|
|
if key in d:
|
|
del d[key]
|
|
ps.append(d)
|
|
#else:
|
|
# ps.append(None)
|
|
topstate = (
|
|
[g for g in boards.BoardGen if not g.gi_running],
|
|
boards.curboard,
|
|
images.ActiveSprites,
|
|
images.SpritesByLoc,
|
|
list(BubPlayer.__dict__.items()),
|
|
gamesrv.sprites,
|
|
gamesrv.sprites_by_n,
|
|
ps,
|
|
list(images.Snd.__dict__.items()),
|
|
)
|
|
#import pdb; pdb.set_trace()
|
|
return statesaver.copy(topstate)
|
|
|
|
def restoregamestate(savedstate):
|
|
(boards.BoardGen,
|
|
boards.curboard,
|
|
images.ActiveSprites,
|
|
images.SpritesByLoc,
|
|
BubPlayerdictitems,
|
|
gamesrv.sprites,
|
|
gamesrv.sprites_by_n,
|
|
ps,
|
|
imagesSnddictitems,
|
|
) = savedstate
|
|
for key, value in BubPlayerdictitems:
|
|
try:
|
|
setattr(BubPlayer, key, value)
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
for key, value in imagesSnddictitems:
|
|
try:
|
|
setattr(images.Snd, key, value)
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
|
|
for p, d in zip(BubPlayer.PlayerList, ps):
|
|
#if d is None:
|
|
# p.reset()
|
|
#else:
|
|
p.__dict__.update(d)
|
|
if not p.isplaying():
|
|
p.zarkoff()
|
|
|
|
class Clock(RandomBonus):
|
|
"Time Machine. Let's do it again!"
|
|
touchable = 0
|
|
points = 0
|
|
nimage = Bonuses.clock
|
|
ticker = None
|
|
bigdoc = "Let's do the whole level again - with the help of ghosts from your own past."
|
|
|
|
def __init__(self, x, y, big=0):
|
|
RandomBonus.__init__(self, -boards.bwidth, 0)
|
|
#print "starting clock"
|
|
self.savedstate = None
|
|
self.savedscreens = []
|
|
if bigclockticker:
|
|
if not big:
|
|
self.kill() # confusion between the two levels of saving
|
|
return
|
|
if x == OFFSCREEN:
|
|
if bigclockticker.state == 'pre':
|
|
self.bigbonus = {'ticker': bigclockticker}
|
|
bigclockticker.state = 'seen'
|
|
else:
|
|
# when taken, this has the same effect as the big clock
|
|
self.ticker = bigclockticker
|
|
self.move(x, y)
|
|
self.touchable = 1
|
|
return
|
|
self.gen = [self.delayed_show()]
|
|
|
|
def delayed_show(self):
|
|
boards.extra_boardgen(self.state_saver())
|
|
for i in range(10):
|
|
yield None
|
|
if self.savedstate is not None:
|
|
for i in range(55):
|
|
yield None
|
|
x, y = chooseground(200)
|
|
if x is not None:
|
|
self.move(x, y)
|
|
self.touchable = 1
|
|
self.gen.append(self.timeouter())
|
|
self.gen.append(self.faller())
|
|
return
|
|
self.kill()
|
|
|
|
def taken1(self, dragons):
|
|
if self.ticker:
|
|
return self.ticker.taken(dragons)
|
|
savedstate = self.savedstate
|
|
self.savedstate = None
|
|
if savedstate is not None:
|
|
boards.replace_boardgen(self.state_restorer(savedstate,
|
|
self.savedscreens,
|
|
self))
|
|
self.untouchable()
|
|
return -1
|
|
|
|
def state_saver(self):
|
|
# called from BoardGen
|
|
self.savedstate = copygamestate()
|
|
while self.alive:
|
|
gamesrv.sprites[0] = ''
|
|
data = ''.join(gamesrv.sprites)
|
|
self.savedscreens.append(data)
|
|
yield 0
|
|
yield 0
|
|
self.savedscreens.append(data)
|
|
yield 0
|
|
yield 0
|
|
self.savedscreens = []
|
|
def state_restorer(self, savedstate, savedscreens, blinkme):
|
|
# called from BoardGen
|
|
from player import scoreboard
|
|
status = 0
|
|
for t in range(10):
|
|
if not (t & 1):
|
|
gamesrv.sprites[0] = ''
|
|
savedscreens.append(''.join(gamesrv.sprites))
|
|
time = boards.normal_frame()
|
|
for i in range(t):
|
|
status += 1
|
|
if status % 3 == 0 and blinkme.alive:
|
|
if status % 6 == 0:
|
|
blinkme.step(boards.bwidth, 0)
|
|
else:
|
|
blinkme.step(-boards.bwidth, 0)
|
|
yield time
|
|
yield boards.force_singlegen()
|
|
yield 15.0
|
|
for p1 in BubPlayer.PlayerList:
|
|
del p1.dragons[:]
|
|
delay = 8.5
|
|
gamesrv.clearsprites()
|
|
while savedscreens:
|
|
gamesrv.sprites[:] = ['', savedscreens.pop()]
|
|
if delay > 0.6:
|
|
delay *= 0.9
|
|
yield delay
|
|
yield 15.0
|
|
restoregamestate(savedstate)
|
|
scoreboard()
|
|
yield 2.5
|
|
|
|
class DragonGhost(ActiveSprite):
|
|
def __init__(self, entry):
|
|
ActiveSprite.__init__(self, entry.ico, entry.x, entry.y)
|
|
|
|
def setentry(self, entry):
|
|
#ico = images.make_darker(entry.ico, True)
|
|
self.lastx = self.x
|
|
self.lasty = self.y
|
|
self.move(entry.x, entry.y, entry.ico)
|
|
self.entry = entry
|
|
self.bubber = entry.d.bubber
|
|
self.dir = entry.dir
|
|
self.poplist = [self]
|
|
|
|
def integrate(self):
|
|
self.play(images.Snd.Shh)
|
|
for j in range(15):
|
|
DustStar(self.x, self.y, 0, -3, clock=j==14)
|
|
|
|
def disintegrate(self):
|
|
self.play(images.Snd.Shh)
|
|
dx = self.x - self.lastx
|
|
dy = self.y - self.lasty
|
|
if dx < -4: dx = -4
|
|
if dy < -4: dy = -4
|
|
if dx > 4: dx = 4
|
|
if dy > 4: dy = 4
|
|
for j in range(15):
|
|
DustStar(self.x, self.y, dx, dy, clock=j==14)
|
|
self.kill()
|
|
|
|
def bottom_up(self):
|
|
return self.entry.dcap['gravity'] < 0.0
|
|
|
|
class SavedDragonEntry(object):
|
|
__slots__ = ['d', 'x', 'y', 'ico', 'flag', 'dir', 'dcap']
|
|
def __init__(self, d, flag, dir, dcap):
|
|
self.d = d
|
|
self.x = d.x
|
|
self.y = d.y
|
|
self.ico = d.ico
|
|
self.flag = flag
|
|
self.dir = dir
|
|
self.dcap = dcap
|
|
|
|
class SavedFrameEntry(object):
|
|
__slots__ = ['saved_next', 'tick', 'dragonlist', 'shoots1']
|
|
def __init__(self, tick, dragonlist):
|
|
self.saved_next = None
|
|
self.tick = tick
|
|
self.dragonlist = dragonlist
|
|
self.shoots1 = []
|
|
|
|
class BigClockTicker:
|
|
dragonlist = None
|
|
tick = 1000
|
|
|
|
def __init__(self):
|
|
global random
|
|
random = random_module.Random()
|
|
localrandom = DustStar.localrandom
|
|
self.state = 'pre'
|
|
self.randombase1 = hash(localrandom.random()) * 914971
|
|
self.randombase2 = hash(localrandom.random()) * 914971
|
|
self.saved_next = None
|
|
self.saved_last = self
|
|
random.seed(self.randombase1)
|
|
random_module.seed(self.randombase2)
|
|
self.latest_entries = {}
|
|
|
|
def common_tick(self, entry):
|
|
self.dragonlist = entry.dragonlist
|
|
random.seed(self.randombase1 - entry.tick)
|
|
random_module.seed(self.randombase2 - entry.tick)
|
|
bonus_frame_tick()
|
|
random.seed(self.randombase1 + entry.tick)
|
|
random_module.seed(self.randombase2 + entry.tick)
|
|
|
|
def save_frame_tick(self):
|
|
entry = self.save_frame()
|
|
self.common_tick(entry)
|
|
|
|
def save_frame(self):
|
|
from player import Dragon
|
|
from bubbles import DragonBubble
|
|
tick = self.saved_last.tick + 1
|
|
dragonlist = []
|
|
new_entries = {}
|
|
for bubber in BubPlayer.PlayerList:
|
|
if bubber.isplaying():
|
|
for d in bubber.dragons:
|
|
try:
|
|
dcap = self.latest_entries[d].dcap
|
|
except KeyError:
|
|
dcap = None
|
|
dir = getattr(d, 'dir', 1)
|
|
cur_dcap = getattr(d, 'dcap', Dragon.DCAP)
|
|
if dcap != cur_dcap:
|
|
dcap = cur_dcap.copy()
|
|
if isinstance(d, Dragon):
|
|
if d.monstervisible():
|
|
flag = 'visible'
|
|
else:
|
|
flag = 'hidden'
|
|
else:
|
|
flag = 'other'
|
|
entry = SavedDragonEntry(d, flag, dir, dcap)
|
|
new_entries[d] = entry
|
|
dragonlist.append(entry)
|
|
self.latest_entries = new_entries
|
|
entry = SavedFrameEntry(tick, dragonlist)
|
|
self.saved_last.saved_next = entry
|
|
self.saved_last = entry
|
|
return entry
|
|
|
|
def taken(self, dragons):
|
|
boards.replace_boardgen(self.jump_to_past())
|
|
|
|
def jump_to_past(self):
|
|
self.state = 'restoring'
|
|
boards.replace_boardgen(boards.next_board(fastreenter=True), 1)
|
|
|
|
def restore(self):
|
|
self.ghosts = {}
|
|
random.seed(self.randombase1)
|
|
random_module.seed(self.randombase2)
|
|
|
|
def show_ghosts(self, dragonlist, interact):
|
|
new_ghosts = {}
|
|
for entry in dragonlist:
|
|
try:
|
|
ghost = self.ghosts[entry.d]
|
|
except KeyError:
|
|
ghost = DragonGhost(entry)
|
|
ghost.setentry(entry)
|
|
new_ghosts[entry.d] = ghost
|
|
if (interact and entry.flag != 'other' and
|
|
not entry.dcap.get('infinite_shield')):
|
|
touching = images.touching(entry.x+1, entry.y+1, 30, 30)
|
|
touching.reverse()
|
|
for s in touching:
|
|
if isinstance(s, interact):
|
|
s.touched(ghost)
|
|
for d, ghost in list(self.ghosts.items()):
|
|
if d not in new_ghosts:
|
|
ghost.kill()
|
|
self.ghosts = new_ghosts
|
|
|
|
def restore_frame_tick(self):
|
|
from bubbles import Bubble, DragonBubble
|
|
interact = (Bonus, Parabolic2, Bubble)
|
|
self.save_frame()
|
|
entry = self.saved_next
|
|
self.saved_next = entry.saved_next
|
|
self.common_tick(entry)
|
|
self.show_ghosts(entry.dragonlist, interact)
|
|
for args in entry.shoots1:
|
|
DragonBubble(*args)
|
|
if self.state == 'restoring' and self.ghosts:
|
|
self.state = 'post'
|
|
for ghost in list(self.ghosts.values()):
|
|
ghost.integrate()
|
|
|
|
def flush_ghosts(self):
|
|
if self.latest_entries:
|
|
for ghost in list(self.ghosts.values()):
|
|
ghost.disintegrate()
|
|
self.latest_entries.clear()
|
|
self.dragonlist = None
|
|
|
|
bigclockticker = None
|
|
|
|
class MultiStones(RandomBonus):
|
|
"Gems. Very demanded stones. It will take time to pick it up."
|
|
nimages = [Bonuses.emerald, Bonuses.sapphire, Bonuses.ruby]
|
|
Stones = [(Bonuses.emerald, 1000),
|
|
(Bonuses.sapphire, 2000),
|
|
(Bonuses.ruby, 3000),
|
|
]
|
|
killgens = 0
|
|
big = 0
|
|
bigdoc = "Stones so big you will jump of joy picking them up."
|
|
def __init__(self, x, y, mode=None):
|
|
mode = mode or random.choice(MultiStones.Stones)
|
|
RandomBonus.__init__(self, x, y, *mode)
|
|
self.bigbonus = {'big': 1, 'outcome_args': (mode,),
|
|
'megacls': LongDurationCactusbonus}
|
|
self.multi = 10
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
self.repulse(dragons)
|
|
self.multi -= (len(dragons) or 1)
|
|
if self.multi > 0:
|
|
self.taken_by = []
|
|
self.untouchable()
|
|
self.gen.append(self.touchdelay(5))
|
|
return -1 # don't go away
|
|
def repulse(self, dragons):
|
|
for d in dragons:
|
|
repulse_dragon(d)
|
|
|
|
def repulse_dragon(d):
|
|
if hasattr(d, 'become_bubblingeyes'):
|
|
from bubbles import Bubble
|
|
ico = images.sprget(GreenAndBlue.normal_bubbles[d.bubber.pn][0])
|
|
b = Bubble(ico, d.x, d.y)
|
|
d.become_bubblingeyes(b)
|
|
b.pop()
|
|
|
|
class Slippy(TemporaryBonus):
|
|
"Greased Feet. Do you want some ice skating?"
|
|
nimage = Bonuses.orange_thing
|
|
points = 900
|
|
capname = 'slippy'
|
|
captime = 606
|
|
bigbonus = {'multiply': 3}
|
|
bigdoc = "Zip zip zip bouncing off walls!"
|
|
|
|
class Aubergine(TemporaryBonus):
|
|
"Mirror. The left hand is the one with the thumb on the right, right?"
|
|
nimage = Bonuses.aubergine
|
|
big = 0
|
|
bigbonus = {'big': 1, 'multiply': 2}
|
|
bigdoc = "Super Bonus-catching teleport ability."
|
|
def taken(self, dragon):
|
|
if self.big:
|
|
dragon.dcap['teleport'] = dragon.bubber.pcap['teleport'] = 1
|
|
else:
|
|
dragon.dcap['lookforward'] = -dragon.dcap['lookforward']
|
|
self.carried(dragon)
|
|
def endaction(self, dragon):
|
|
if self.big:
|
|
pass
|
|
else:
|
|
dragon.dcap['lookforward'] = -dragon.dcap['lookforward']
|
|
|
|
class WhiteCarrot(TemporaryBonus):
|
|
"Fly. Become a great flying dragon!"
|
|
nimage = Bonuses.white_carrot
|
|
points = 650
|
|
capname = 'fly'
|
|
captime = 650
|
|
bigbonus = {'capname': 'jumpdown',
|
|
'captime': 999999}
|
|
bigdoc = "Jump down off the ground!"
|
|
def taken(self, dragon):
|
|
TemporaryBonus.taken(self, dragon)
|
|
dragon.bubber.pcap['jumpdown'] = dragon.dcap['jumpdown']
|
|
|
|
class AmphetamineSpeed(TemporaryBonus):
|
|
"Amphetamine Dose. Increase of your general speed!"
|
|
nimage = Bonuses.tin
|
|
points = 700
|
|
bigbonus = {'multiply': 3}
|
|
bigdoc = "Let's move!"
|
|
def taken(self, dragon):
|
|
dragon.angry = dragon.angry + [dragon.genangry()]
|
|
dragon.carrybonus(self, 633)
|
|
def endaction(self, dragon):
|
|
dragon.angry = dragon.angry[1:]
|
|
|
|
class Sugar1(Bonus):
|
|
nimage = Bonuses.yellow_sugar
|
|
timeout = 2600
|
|
points = 250
|
|
def taken(self, dragon):
|
|
#if boards.curboard.wastingplay is None:
|
|
dragon.carrybonus(self, 99999)
|
|
#else:
|
|
# from player import scoreboard
|
|
# dragon.bubber.bonbons += 1
|
|
# scoreboard()
|
|
|
|
class Sugar2(Sugar1):
|
|
timeout = 2500
|
|
points = 500
|
|
nimage = Bonuses.blue_sugar
|
|
|
|
class Pear(RandomBonus):
|
|
"Pear. Will explode into sugars for your pockets but watch out or you'll lose them!"
|
|
points = 1000
|
|
nimage = Bonuses.green_thing
|
|
bigbonus = {'multiply': 4}
|
|
bigdoc = "The more the better."
|
|
def taken1(self, dragons):
|
|
starexplosion(self.x, self.y, 3 * self.multiply,
|
|
outcomes = [random.choice([(Sugar1,), (Sugar2,)])
|
|
for i in range(18 * self.multiply)])
|
|
|
|
class Megalightning(ActiveSprite):
|
|
def __init__(self, dragon):
|
|
ActiveSprite.__init__(self, images.sprget(BigImages.blitz),
|
|
gamesrv.game.width, gamesrv.game.height)
|
|
self.gen.append(self.killing(dragon))
|
|
|
|
def killing(self, dragon):
|
|
from monsters import Monster
|
|
from bubbles import Bubble
|
|
poplist = [dragon]
|
|
while 1:
|
|
for s in self.touching(10):
|
|
if isinstance(s, Monster):
|
|
s.argh(poplist)
|
|
elif isinstance(s, Bubble):
|
|
s.pop(poplist)
|
|
yield None
|
|
yield None
|
|
|
|
def moving_to(self, x1, y1):
|
|
x0 = self.x
|
|
y0 = self.y
|
|
x1 += CELL - self.ico.w//2
|
|
y1 += CELL - self.ico.h//2
|
|
deltax = x1 - x0
|
|
if deltax > -100:
|
|
deltax = -100
|
|
deltay = y1 - y0
|
|
a = - deltay / float(deltax*deltax)
|
|
b = 2 * deltay / float(deltax)
|
|
for x in range(self.x, -self.ico.w, -13):
|
|
x1 = x - x0
|
|
self.move(x, y0 + int((a*x1+b)*x1))
|
|
yield None
|
|
self.kill()
|
|
|
|
class Fish2(RandomBonus):
|
|
"Rotten Fish. Will blast monsters up to here, so move it around!"
|
|
points = 3000
|
|
nimage = Bonuses.fish2
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Gives seven blasts."
|
|
def taken1(self, dragons):
|
|
dragon = random.choice(dragons or [None])
|
|
if not self.big:
|
|
m = Megalightning(dragon)
|
|
m.gen.append(m.moving_to(self.x, self.y))
|
|
else:
|
|
N = 7
|
|
base = random.random() * 2*math.pi
|
|
angles = [base + (math.pi*2 * n)/N for n in range(N)]
|
|
random.shuffle(angles)
|
|
for angle in angles:
|
|
m = Megalightning(dragon)
|
|
dx = 13 * math.cos(angle)
|
|
dy = 12 * math.sin(angle)
|
|
maxlive = max((gamesrv.game.width + m.ico.w) // 13,
|
|
(gamesrv.game.height + m.ico.h) // 12)
|
|
m.move(self.x + (self.ico.w - m.ico.w) // 2 - int(dx*maxlive),
|
|
self.y + (self.ico.h - m.ico.h) // 2 - int(dy*maxlive))
|
|
m.gen.append(m.straightline(dx, dy))
|
|
m.gen.append(m.die([None], maxlive*2))
|
|
|
|
|
|
class Sheep(RandomBonus):
|
|
"Sheep. What a stupid beast!"
|
|
nimage = 'sheep-sm'
|
|
points = 800
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "You're a sheep. Let's bounce around."
|
|
def __init__(self, x, y):
|
|
RandomBonus.__init__(self, x, y)
|
|
if boards.curboard.bonuslevel:
|
|
self.kill()
|
|
def taken1(self, dragons):
|
|
if not self.big:
|
|
self.points0 = {}
|
|
for p in BubPlayer.PlayerList:
|
|
self.points0[p] = p.points
|
|
BubPlayer.LeaveBonus = self.boardleave()
|
|
else:
|
|
from player import Dragon
|
|
BubPlayer.SuperSheep = True
|
|
for p in BubPlayer.PlayerList:
|
|
for d in p.dragons:
|
|
if isinstance(d, Dragon):
|
|
d.become_monster('Sheep')
|
|
|
|
def boardleave(self):
|
|
from player import BubPlayer
|
|
BubPlayer.OverridePlayerIcon = images.sprget(self.nimage)
|
|
gamesrv.set_musics([], [])
|
|
images.Snd.Yippee.play()
|
|
slist = []
|
|
ico = images.sprget('sheep-big')
|
|
for p in BubPlayer.PlayerList:
|
|
if p.isplaying() and p.dragons:
|
|
d = random.choice(p.dragons)
|
|
dx = (d.ico.w - ico.w) // 2
|
|
dy = (d.ico.h - ico.h) // 2
|
|
s = ActiveSprite(ico, d.x + dx, d.y + dy)
|
|
dir = getattr(d, 'dir', None)
|
|
if dir not in [-1, 1]:
|
|
dir = random.choice([-1, 1])
|
|
s.gen.append(s.parabolic([dir, -2.0]))
|
|
slist.append(s)
|
|
for d in p.dragons[:]:
|
|
d.kill()
|
|
delta = {}
|
|
for p in BubPlayer.PlayerList:
|
|
if p.points or p.isplaying():
|
|
delta[p] = 2 * (self.points0[p] - p.points)
|
|
vy = 0
|
|
while delta or slist:
|
|
ndelta = {}
|
|
for p, dp in list(delta.items()):
|
|
if dp:
|
|
d1 = max(-250, min(250, dp))
|
|
p.givepoints(d1)
|
|
if p.points > 0:
|
|
ndelta[p] = dp - d1
|
|
delta = ndelta
|
|
images.action(slist)
|
|
slist = [s for s in slist if s.y < boards.bheight]
|
|
yield 1
|
|
|
|
class Flower(RandomBonus):
|
|
"Flower. Fire in all directions."
|
|
nimage = 'flower'
|
|
points = 800
|
|
big = 0
|
|
bigbonus = {'big': 1, 'multiply': 5}
|
|
bigdoc = "Rotational Bubble Thrower (tm)."
|
|
def taken(self, dragon):
|
|
if self.big:
|
|
dragon.dcap['bigflower'] = -99
|
|
dragon.dcap['autofire'] = 22
|
|
else:
|
|
dragon.dcap['flower'] += 12
|
|
dragon.carrybonus(self)
|
|
|
|
class Flower2(TemporaryBonus):
|
|
"Bottom-up Flower. Turn you upside-down."
|
|
nimage = 'flower2'
|
|
points = 1000
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Turn the level upside-down."
|
|
def __init__(self, *args):
|
|
RandomBonus.__init__(self, *args)
|
|
if self.x != OFFSCREEN:
|
|
while not underground(self.x, self.y):
|
|
self.step(0, -CELL)
|
|
if self.y < 0:
|
|
self.kill()
|
|
return
|
|
def taken1(self, dragons):
|
|
if self.big:
|
|
boards.extra_boardgen(boards.extra_swap_up_down())
|
|
else:
|
|
RandomBonus.taken1(self, dragons)
|
|
def faller(self):
|
|
while self.y >= 0:
|
|
if underground(self.x, self.y):
|
|
yield None
|
|
yield None
|
|
else:
|
|
self.move(self.x, (self.y-1) & ~3)
|
|
yield None
|
|
self.kill()
|
|
def taken(self, dragon):
|
|
dragon.dcap['gravity'] *= -1.0
|
|
self.carried(dragon)
|
|
def endaction(self, dragon):
|
|
dragon.dcap['gravity'] *= -1.0
|
|
def is_on_ground(self):
|
|
return underground(self.x, self.y)
|
|
|
|
##class Moebius(RandomBonus):
|
|
## "Moebius Band. Bottom left is top right and bottom right is top left... or vice-versa."
|
|
## nimage = 'moebius'
|
|
## points = 900
|
|
## def taken1(self, dragons):
|
|
## BubPlayer.Moebius = not BubPlayer.Moebius
|
|
|
|
class StarBubble(FireBubble):
|
|
"Star Bubbles. Makes you fire bonus bubbles."
|
|
nimage = 'moebius'
|
|
bubkind = 'StarBubble'
|
|
bubcount = 3
|
|
bigbonus = {'bubcount': 10}
|
|
bigdoc = "More bonus bubbles => more confusion."
|
|
|
|
class Donut(RandomBonus):
|
|
"Donut. Catch every free monster in a bubble."
|
|
nimage = Bonuses.donut
|
|
points = 950
|
|
big = 0
|
|
bigbonus = {'big': 1}
|
|
bigdoc = "Catch dragons too."
|
|
|
|
def taken1(self, dragons):
|
|
extra_boardgen(boards.extra_catch_all_monsters(dragons, self.big))
|
|
if self.big:
|
|
# catch all dragons as well
|
|
from bubbles import NormalBubble
|
|
for dragon in BubPlayer.DragonList[:]:
|
|
b = NormalBubble(dragon, dragon.x, dragon.y, 542)
|
|
if not dragon.become_bubblingeyes(b):
|
|
b.kill()
|
|
|
|
|
|
Classes = [c for c in list(globals().values())
|
|
if type(c)==type(RandomBonus) and issubclass(c, RandomBonus)]
|
|
Classes.remove(RandomBonus)
|
|
Classes.remove(TemporaryBonus)
|
|
Cheat = []
|
|
#Classes = [Cactus, Insect] # CHEAT
|
|
|
|
AllOutcomes = ([(c,) for c in Classes if c is not Fruits] +
|
|
2 * [(MonsterBonus, lvl)
|
|
for lvl in range(len(Bonuses.monster_bonuses))])
|
|
|
|
for c in Classes:
|
|
assert (getattr(c, 'points', 0) or 100) in GreenAndBlue.points[0], c
|
|
|
|
def getdragonlist():
|
|
if bigclockticker and bigclockticker.dragonlist is not None:
|
|
return [entry for entry in bigclockticker.dragonlist
|
|
if entry.flag != 'other']
|
|
else:
|
|
return BubPlayer.DragonList
|
|
|
|
def getvisibledragonlist():
|
|
if bigclockticker and bigclockticker.dragonlist is not None:
|
|
return [entry for entry in bigclockticker.dragonlist
|
|
if entry.flag == 'visible']
|
|
else:
|
|
return [d for d in BubPlayer.DragonList if d.monstervisible()]
|
|
|
|
def record_shot(args):
|
|
if bigclockticker:
|
|
entry = bigclockticker.saved_last
|
|
if hasattr(entry, 'shoots1'):
|
|
entry.shoots1.append(args)
|
|
|
|
|
|
def chooseground(tries=15):
|
|
avoidlist = getdragonlist()
|
|
for i in range(tries):
|
|
x0 = random.randint(2, boards.width-4)
|
|
y0 = random.randint(1, boards.height-3)
|
|
if (' ' == bget(x0,y0+1) == bget(x0+1,y0+1) and
|
|
'#' == bget(x0,y0+2) == bget(x0+1,y0+2)):
|
|
x0 *= CELL
|
|
y0 *= CELL
|
|
for dragon in avoidlist:
|
|
if abs(dragon.x-x0) < 3*CELL and abs(dragon.y-y0) < 3*CELL:
|
|
break
|
|
else:
|
|
return x0, y0
|
|
else:
|
|
return None, None
|
|
|
|
def newbonus():
|
|
others = [s for s in images.ActiveSprites if isinstance(s, RandomBonus)]
|
|
if others:
|
|
return
|
|
if BubPlayer.SuperSheep:
|
|
return
|
|
x, y = chooseground()
|
|
if x is None:
|
|
return
|
|
cls = random.choice(Classes)
|
|
cls(x, y)
|
|
|
|
##def newbonus():
|
|
## others = [s for s in images.ActiveSprites if isinstance(s, RandomBonus)]
|
|
## if others:
|
|
## return
|
|
## for cls in Classes:
|
|
## x, y = chooseground(200)
|
|
## if x is not None:
|
|
## cls(x, y)
|
|
|
|
def cheatnew():
|
|
if Cheat:
|
|
x, y = chooseground()
|
|
if x is None:
|
|
return
|
|
cls = random.choice(Cheat)
|
|
if not isinstance(cls, tuple):
|
|
cls = cls,
|
|
else:
|
|
Cheat.remove(cls)
|
|
if len(cls) > 1:
|
|
class C(cls[0]):
|
|
extra_cheat_arg = cls[1]
|
|
cls = (C,)
|
|
cls[0](x, y)
|
|
|
|
def bonus_frame_tick():
|
|
if random.random() < 0.04:
|
|
cheatnew()
|
|
if random.random() < 0.15:
|
|
newbonus()
|
|
else:
|
|
import bubbles
|
|
bubbles.newbubble()
|
|
|
|
def start_normal_play():
|
|
global bigclockticker
|
|
if bigclockticker and bigclockticker.state == 'restoring':
|
|
bigclockticker.restore()
|
|
return bigclockticker.restore_frame_tick
|
|
if (Clock and not boards.curboard.bonuslevel and
|
|
random.choice(Classes) is Clock):
|
|
bigclockticker = BigClockTicker()
|
|
return bigclockticker.save_frame_tick
|
|
else:
|
|
bigclockticker = None
|
|
return bonus_frame_tick
|
|
|
|
def end_normal_play():
|
|
if bigclockticker and bigclockticker.state == 'post':
|
|
bigclockticker.flush_ghosts()
|
|
|
|
# hack hack hack!
|
|
def __cheat(c):
|
|
c = c.split(',')
|
|
c[0] = globals()[c[0]]
|
|
assert issubclass(c[0], Bonus)
|
|
Cheat.append(tuple(c))
|
|
import builtins
|
|
builtins.__cheat = __cheat
|