mirror of
https://github.com/abakh/nbsdgames
synced 2025-04-28 14:09:32 -04:00
1471 lines
46 KiB
Python
1471 lines
46 KiB
Python
|
|
import random, os, sys, math
|
|
import gamesrv
|
|
import images
|
|
|
|
CELL = 16 # this constant is inlined at some places, don't change
|
|
HALFCELL = CELL//2
|
|
FRAME_TIME = 0.025
|
|
#DEFAULT_LEVEL_FILE = 'levels/scratch.py'
|
|
|
|
BOARD_BKGND = 1 # 0 = black, 1 = darker larger wall tiles
|
|
|
|
|
|
class Copyable:
|
|
pass # see bonuses.py, class Clock
|
|
|
|
|
|
class Board(Copyable):
|
|
letter = 0
|
|
fire = 0
|
|
lightning = 0
|
|
water = 0
|
|
top = 0
|
|
|
|
WIND_DELTA = HALFCELL
|
|
|
|
def __init__(self, num):
|
|
# the subclasses should define 'walls', 'winds', 'monsters'
|
|
self.walls = walls = [line for line in self.walls.split('\n') if line]
|
|
self.winds = winds = [line for line in self.winds.split('\n') if line]
|
|
self.num = num
|
|
self.width = len(walls[0])
|
|
self.height = len(walls)
|
|
for line in walls:
|
|
assert len(line) == self.width, "some wall lines are longer than others"
|
|
for line in winds:
|
|
assert len(line) == self.width, "some wind lines are longer than others"
|
|
#assert walls[0] == walls[-1], "first and last lines must be identical"
|
|
assert len(winds) == self.height, "wall and wind heights differ"
|
|
self.walls_by_pos = {}
|
|
self.sprites = {}
|
|
if self.top:
|
|
testline = self.walls[0]
|
|
else:
|
|
testline = self.walls[-1]
|
|
self.holes = testline.find(' ') >= 0
|
|
self.playingboard = 0
|
|
self.bonuslevel = not self.monsters or (gamesrv.game.finalboard is not None and self.num >= gamesrv.game.finalboard)
|
|
self.cleaning_gen_state = 0
|
|
|
|
def set_musics(self, prefix=[]):
|
|
if (self.num+1) % 20 < 10:
|
|
gamesrv.set_musics(prefix + [images.music_intro], [images.music_game],
|
|
reset=0)
|
|
else:
|
|
gamesrv.set_musics(prefix + [], [images.music_game2], reset=0)
|
|
|
|
def writesprites(self, name, xyicolist):
|
|
sprlist = self.sprites.setdefault(name, [])
|
|
xyicolist = xyicolist[:]
|
|
xyicolist.reverse()
|
|
for s in sprlist[:]:
|
|
if xyicolist:
|
|
s.move(*xyicolist.pop())
|
|
else:
|
|
s.kill()
|
|
sprlist.remove(s)
|
|
while xyicolist:
|
|
x, y, ico = xyicolist.pop()
|
|
sprlist.append(gamesrv.Sprite(ico, x, y))
|
|
|
|
def enter(self, complete=1, inplace=0, fastreenter=False):
|
|
global curboard
|
|
if inplace:
|
|
print("Re -", end=' ')
|
|
print("Entering board", self.num+1)
|
|
self.set_musics()
|
|
# add board walls
|
|
l = self.sprites.setdefault('walls', [])
|
|
bl = self.sprites.setdefault('borderwalls', [])
|
|
if inplace:
|
|
deltay = 0
|
|
else:
|
|
deltay = bheight
|
|
wnx = wny = 1
|
|
while haspat((self.num, wnx, 0)):
|
|
wnx += 1
|
|
while haspat((self.num, 0, wny)):
|
|
wny += 1
|
|
self.wnx = wnx
|
|
self.wny = wny
|
|
|
|
if haspat((self.num, 'l')):
|
|
lefticon = patget((self.num, 'l'))
|
|
if haspat((self.num, 'r')):
|
|
righticon = patget((self.num, 'r'))
|
|
else:
|
|
righticon = lefticon
|
|
xrange = list(range(2, self.width-2))
|
|
else:
|
|
xrange = list(range(self.width))
|
|
lefticon = righticon = None
|
|
|
|
if BOARD_BKGND == 1:
|
|
gl = self.sprites.setdefault('background', [])
|
|
xmax = (self.width-2)*CELL
|
|
ymax = self.height*CELL
|
|
y = -HALFCELL
|
|
ystep = 0
|
|
firstextra = 1
|
|
while y < ymax:
|
|
x = 2*CELL+HALFCELL
|
|
xstep = 0
|
|
while x < xmax:
|
|
bitmap, rect = loadpattern((self.num, xstep, ystep),
|
|
images.KEYCOL)
|
|
bitmap, rect = images.makebkgndpattern(bitmap, rect)
|
|
if firstextra:
|
|
# special position where a bit of black might show up
|
|
x -= rect[2]
|
|
xstep = (xstep-1) % wnx
|
|
firstextra = 0
|
|
continue
|
|
bkgndicon = bitmap.geticon(*rect)
|
|
w = gamesrv.Sprite(bkgndicon, x, y + deltay)
|
|
gl.append(w)
|
|
x += rect[2]
|
|
xstep = (xstep+1) % wnx
|
|
y += rect[3]
|
|
ystep = (ystep+1) % wny
|
|
else:
|
|
gl = []
|
|
|
|
if lefticon is not None:
|
|
for y in range(0, self.height, lefticon.h // CELL):
|
|
bl.append(gamesrv.Sprite(lefticon, 0, y*CELL + deltay))
|
|
|
|
for y in range(self.height):
|
|
for x in xrange:
|
|
c = self.walls[y][x]
|
|
if c == '#':
|
|
wallicon = patget((self.num, x%wnx, y%wny), images.KEYCOL)
|
|
w = gamesrv.Sprite(wallicon, x*CELL, y*CELL + deltay)
|
|
l.append(w)
|
|
self.walls_by_pos[y,x] = w
|
|
|
|
if not l:
|
|
# self.sprites['walls'] must not be empty, for putwall
|
|
wallicon = patget((self.num, 0, 0), images.KEYCOL)
|
|
w = gamesrv.Sprite(wallicon, 0, -wallicon.h)
|
|
l.append(w)
|
|
|
|
if righticon is not None:
|
|
for y in range(0, self.height, lefticon.h // CELL):
|
|
bl.append(gamesrv.Sprite(righticon, (self.width-2)*CELL, y*CELL + deltay))
|
|
|
|
while deltay:
|
|
dy = -min(deltay, 8)
|
|
for w in gl:
|
|
w.step(0, dy)
|
|
for w in l:
|
|
w.step(0, dy)
|
|
for w in bl:
|
|
w.step(0, dy)
|
|
deltay += dy
|
|
yield 1
|
|
|
|
if inplace:
|
|
for w in images.ActiveSprites:
|
|
w.to_front()
|
|
|
|
curboard = self
|
|
if gamesrv.game:
|
|
gamesrv.game.updateboard()
|
|
if not complete:
|
|
return
|
|
# add players
|
|
from player import BubPlayer, scoreboard
|
|
if not inplace:
|
|
random.shuffle(BubPlayer.PlayerList)
|
|
scoreboard(1, inplace=inplace)
|
|
if not fastreenter:
|
|
random.shuffle(BubPlayer.PlayerList)
|
|
playing = []
|
|
for p in BubPlayer.PlayerList:
|
|
if p.isplaying():
|
|
p.enterboard(playing)
|
|
p.zarkon()
|
|
playing.append(p)
|
|
for d in BubPlayer.DragonList:
|
|
d.enter_new_board()
|
|
else:
|
|
# kill stuff left over from leave(inplace=1) (Big Clock bonus only)
|
|
import bonuses
|
|
keepme = bonuses.Points
|
|
dragons = {}
|
|
playing = []
|
|
for p in BubPlayer.PlayerList:
|
|
if p.isplaying():
|
|
for d in p.dragons:
|
|
if hasattr(d, 'dcap'):
|
|
d.dcap['shield'] = 90
|
|
dragons[d] = True
|
|
playing.append(p)
|
|
for s in images.ActiveSprites[:]:
|
|
if isinstance(s, keepme) or s in dragons:
|
|
pass
|
|
else:
|
|
s.kill()
|
|
# add monsters
|
|
if not self.bonuslevel:
|
|
import monsters
|
|
f_monsters = gamesrv.game.f_monsters
|
|
if f_monsters < 0.1:
|
|
f_monsters = max(1.0, min(2.0, (len(playing)-2)/2.2+1.0))
|
|
for mdef in self.monsters:
|
|
if not fastreenter:
|
|
yield 2
|
|
cls = getattr(monsters, mdef.__class__.__name__)
|
|
dir = mdef.dir
|
|
i = random.random()
|
|
while i < f_monsters:
|
|
cls(mdef, dir=dir)
|
|
dir = -dir
|
|
i += 1.0
|
|
self.playingboard = 1
|
|
|
|
def putwall(self, x, y, w=None):
|
|
wallicon = patget((self.num, x%self.wnx, y%self.wny), images.KEYCOL)
|
|
if w is None:
|
|
w = gamesrv.Sprite(wallicon, 0, bheight)
|
|
l = self.sprites['walls']
|
|
w.to_back(l[-1])
|
|
l.append(w)
|
|
self.walls_by_pos[y,x] = w
|
|
if y >= 0:
|
|
line = self.walls[y]
|
|
self.walls[y] = line[:x] + '#' + line[x+1:]
|
|
|
|
def killwall(self, x, y, kill=1):
|
|
w = self.walls_by_pos[y,x]
|
|
if kill:
|
|
l = self.sprites['walls']
|
|
if len(l) > 1:
|
|
l.remove(w)
|
|
w.kill()
|
|
else:
|
|
# self.sprites['walls'] must never be empty
|
|
# or putwall will crash!
|
|
w.move(0, -bheight)
|
|
del self.walls_by_pos[y,x]
|
|
line = self.walls[y]
|
|
self.walls[y] = line[:x] + ' ' + line[x+1:]
|
|
return w
|
|
|
|
def reorder_walls(self):
|
|
walls_by_pos = self.walls_by_pos
|
|
items = [(yx, w1.ico) for yx, w1 in list(walls_by_pos.items())]
|
|
if not items:
|
|
return # otherwise self.sprites['walls'] would be emptied
|
|
items.sort()
|
|
l = self.sprites['walls']
|
|
while len(l) > len(items):
|
|
l.pop().kill()
|
|
assert len(items) == len(l)
|
|
for ((y,x), ico), w2 in zip(items, l):
|
|
w2.move(x*CELL, y*CELL, ico)
|
|
walls_by_pos[y,x] = w2
|
|
|
|
def leave(self, inplace=0):
|
|
global curboard
|
|
if not gamesrv.has_loop_music():
|
|
gamesrv.fadeout(1.5)
|
|
from player import BubPlayer
|
|
for p in BubPlayer.PlayerList:
|
|
if p.isplaying():
|
|
p.savecaps()
|
|
if BubPlayer.LeaveBonus:
|
|
for t in BubPlayer.LeaveBonus:
|
|
yield t
|
|
BubPlayer.LeaveBonus = None
|
|
curboard = None
|
|
if inplace:
|
|
i = -1
|
|
else:
|
|
while images.ActiveSprites:
|
|
s = random.choice(images.ActiveSprites)
|
|
s.kill()
|
|
yield 0.9
|
|
i = 0
|
|
sprites = []
|
|
for l in list(self.sprites.values()):
|
|
sprites += l
|
|
self.sprites.clear()
|
|
self.walls_by_pos.clear()
|
|
random.shuffle(sprites)
|
|
for s in sprites:
|
|
s.kill()
|
|
if i:
|
|
i -= 1
|
|
else:
|
|
yield 0.32
|
|
i = 3
|
|
if not inplace:
|
|
for p in BubPlayer.PlayerList:
|
|
if p.isplaying():
|
|
p.zarkoff()
|
|
yield 4
|
|
|
|
def clean_gen_state(self):
|
|
self.cleaning_gen_state = 1
|
|
while len(BoardGen) > 1:
|
|
#yield force_singlegen()
|
|
#if 'flood' in self.sprites:
|
|
# for s in self.sprites['flood']:
|
|
# s.kill()
|
|
# del self.sprites['flood']
|
|
yield normal_frame()
|
|
self.cleaning_gen_state = 0
|
|
|
|
def bget(x, y):
|
|
if 0 <= x < curboard.width:
|
|
if y < 0 or y >= curboard.height:
|
|
y = 0
|
|
return curboard.walls[y][x]
|
|
else:
|
|
return '#'
|
|
|
|
def wget(x, y):
|
|
delta = curboard.WIND_DELTA
|
|
x = (x + delta) // 16
|
|
y = (y + delta) // 16
|
|
if 0 <= x < curboard.width:
|
|
if y < 0:
|
|
y = 0
|
|
elif y >= curboard.height:
|
|
y = -1
|
|
return curboard.winds[y][x]
|
|
elif x < 0:
|
|
return '>'
|
|
else:
|
|
return '<'
|
|
|
|
def onground(x, y):
|
|
if y & 15:
|
|
return 0
|
|
x0 = (x+5) // 16
|
|
x1 = (x+16) // 16
|
|
x2 = (x+27) // 16
|
|
y0 = y // 16 + 2
|
|
|
|
if x0 < 0 or x2 >= curboard.width:
|
|
return 0
|
|
y1 = y0 - 1
|
|
if not (0 < y0 < curboard.height):
|
|
if y0 != curboard.height:
|
|
y1 = 0
|
|
y0 = 0
|
|
y0 = curboard.walls[y0]
|
|
y1 = curboard.walls[y1]
|
|
return (' ' == y1[x0] == y1[x1] == y1[x2] and
|
|
not (' ' == y0[x0] == y0[x1] == y0[x2]))
|
|
#return (' ' == bget(x0,y0-1) == bget(x1,y0-1) == bget(x2,y0-1) and
|
|
# not (' ' == bget(x0,y0) == bget(x1,y0) == bget(x2,y0)))
|
|
#return (bget(x1,y0-1)==' ' and
|
|
# ((bget(x1,y0)=='#') or
|
|
# (bget(x0,y0)=='#' and bget(x0,y0-1)==' ') or
|
|
# (bget(x2,y0)=='#' and bget(x2,y0-1)==' ')))
|
|
|
|
def onground_nobottom(x, y):
|
|
return onground(x, y) and y+32 < bheight
|
|
|
|
def underground(x, y):
|
|
if y % CELL:
|
|
return 0
|
|
x0 = (x+5) // CELL
|
|
x1 = (x+CELL) // CELL
|
|
x2 = (x+2*CELL-5) // CELL
|
|
y0 = y // CELL
|
|
|
|
if x0 < 0 or x2 >= curboard.width:
|
|
return 0
|
|
y1 = y0 - 1
|
|
if not (0 < y0 < curboard.height):
|
|
if y0 != curboard.height:
|
|
y1 = 0
|
|
y0 = 0
|
|
y0 = curboard.walls[y0]
|
|
y1 = curboard.walls[y1]
|
|
return (' ' == y0[x0] == y0[x1] == y0[x2] and
|
|
not (' ' == y1[x0] == y1[x1] == y1[x2]))
|
|
|
|
def x2bounds(x):
|
|
if x < 32:
|
|
return 32
|
|
elif x > bwidth - 64:
|
|
return bwidth - 64
|
|
else:
|
|
return x
|
|
|
|
def vertical_warp(nx, ny):
|
|
if ny >= bheight:
|
|
ny -= bheightmod
|
|
elif ny < -32:
|
|
ny += bheightmod
|
|
return nx, ny
|
|
|
|
def vertical_warp_sprite(spr):
|
|
if spr.y >= bheight:
|
|
spr.step(0, -bheightmod)
|
|
elif spr.y < -32:
|
|
spr.step(0, bheightmod)
|
|
|
|
##def vertical_warp(nx, ny):
|
|
## if ny >= bheight:
|
|
## ny -= bheightmod
|
|
## elif ny < -32:
|
|
## ny += bheightmod
|
|
## else:
|
|
## return (nx, ny), 0
|
|
## from player import BubPlayer
|
|
## if BubPlayer.Moebius:
|
|
## nx = bwidth - 2*CELL - nx
|
|
## return (nx, ny), 1
|
|
## else:
|
|
## return (nx, ny), 0
|
|
|
|
|
|
MODULES = ['boards', 'bonuses', 'bubbles', 'images',
|
|
'mnstrmap', 'monsters', 'player', 'ranking',
|
|
'binboards', 'macbinary', 'boarddef',
|
|
'ext1', 'ext2', 'ext3', 'ext4', 'ext5', 'ext6', 'ext7']
|
|
|
|
def loadmodules(force=0):
|
|
levelfilename = gamesrv.game.levelfile
|
|
modulefiles = {None: levelfilename}
|
|
for m in MODULES:
|
|
if os.path.isfile(m+'.py'):
|
|
modulefiles[m] = m+'.py'
|
|
elif os.path.isfile(os.path.join(m, '__init__.py')):
|
|
modulefiles[m] = os.path.join(m, '__init__.py')
|
|
mtimes = {}
|
|
for m, mfile in list(modulefiles.items()):
|
|
mtimes[m] = os.stat(mfile).st_mtime
|
|
reload = force or (mtimes != getattr(sys, 'ST_MTIMES', None))
|
|
import player
|
|
playerlist = player.BubPlayer.PlayerList
|
|
if reload:
|
|
delete = hasattr(sys, 'ST_MTIMES')
|
|
sys.ST_MTIMES = mtimes
|
|
if delete:
|
|
print("Reloading modules.")
|
|
for m, mfile in list(modulefiles.items()):
|
|
if m is not None and m in sys.modules:
|
|
del sys.modules[m]
|
|
|
|
# Clear
|
|
clearallsprites()
|
|
|
|
import player
|
|
for p in playerlist:
|
|
player.upgrade(p)
|
|
for n in range(len(playerlist), images.MAX):
|
|
playerlist.append(player.BubPlayer(n))
|
|
player.BubPlayer.PlayerList = playerlist
|
|
if reload:
|
|
import boards
|
|
from images import haspat, loadpattern
|
|
boards.haspat = haspat
|
|
boards.loadpattern = loadpattern
|
|
del boards.BoardList[:]
|
|
if levelfilename.lower().endswith('.py'):
|
|
levels = {}
|
|
print('Source level file:', levelfilename)
|
|
exec(compile(open(levelfilename, "rb").read(), levelfilename, 'exec'), levels)
|
|
if 'GenerateLevels' in levels:
|
|
levels = levels['GenerateLevels']()
|
|
if isinstance(levels, list):
|
|
levels = dict(list(zip(list(range(len(levels))), levels)))
|
|
else:
|
|
import binboards
|
|
levels = binboards.load(levelfilename)
|
|
boards.register(levels)
|
|
return reload
|
|
|
|
def clearallsprites():
|
|
gamesrv.clearsprites()
|
|
import images
|
|
del images.ActiveSprites[:]
|
|
images.SpritesByLoc.clear()
|
|
|
|
def wait_for_one_player():
|
|
from player import BubPlayer
|
|
clearallsprites()
|
|
nimages = None
|
|
while not [p for p in BubPlayer.PlayerList if p.isplaying()]:
|
|
yield 3
|
|
if not nimages:
|
|
desc = getattr(gamesrv.game, 'FnDesc', '?')
|
|
host, port = getattr(gamesrv.game, 'address', ('?', '?'))
|
|
images.writestrlines([
|
|
"Welcome to",
|
|
desc.upper(),
|
|
"at %s:%s" % (host.lower(), port),
|
|
None,
|
|
"Click on your Favorite Color's Dragon",
|
|
"Choose four keys: Right, Left, Jump, Shoot",
|
|
"and Let's Go!",
|
|
None,
|
|
"Click again for more than one player",
|
|
"on the same machine.",
|
|
])
|
|
|
|
from mnstrmap import PlayerBubbles
|
|
nimages = [PlayerBubbles.bubble[0],
|
|
PlayerBubbles.bubble[1],
|
|
PlayerBubbles.bubble[1],
|
|
PlayerBubbles.bubble[0],
|
|
PlayerBubbles.bubble[2],
|
|
PlayerBubbles.bubble[2]]
|
|
screenwidth = bwidth + 9*CELL
|
|
screenheight = bheight
|
|
|
|
def welcomebubbling(self):
|
|
fx = self.x
|
|
dx = (random.random() - 0.5) * 1.9
|
|
for y in range(self.y-3, -self.ico.h, -3):
|
|
fx += dx
|
|
self.move(int(fx), y)
|
|
yield None
|
|
if y == self.ypop:
|
|
from mnstrmap import PlayerBubbles
|
|
self.setimages(None)
|
|
self.gen.append(self.die(PlayerBubbles.explosion))
|
|
self.kill()
|
|
|
|
yield 10
|
|
gamesrv.set_musics([], [images.music_game2], reset=0)
|
|
|
|
if ((not images.ActiveSprites or random.random() < 0.05678) and
|
|
gamesrv.clients):
|
|
# make sure the extension's images are loaded too
|
|
# NB. this is also needed for import auto-detection
|
|
import ext1; import ext2; import ext3
|
|
import ext4; import ext5; import ext6
|
|
import ext7
|
|
|
|
ico = images.sprget(nimages[0])
|
|
s = images.ActiveSprite(ico,
|
|
random.randrange(0, screenwidth-ico.w),
|
|
screenheight)
|
|
s.ypop = random.randrange(-ico.h, screenheight)
|
|
s.gen = [welcomebubbling(s)]
|
|
s.setimages(s.cyclic(nimages, speed=1))
|
|
if random.random() > 0.4321:
|
|
try:
|
|
key, (filename, (x, y, w, h)) = random.choice(
|
|
list(images.sprmap.items()))
|
|
except:
|
|
w = h = 0
|
|
if w == h == 32:
|
|
s2 = images.ActiveSprite(images.sprget(key), -32, 0)
|
|
s2.gen = [s2.following(s, (s.ico.w-32)//2, (s.ico.h-32)//2)]
|
|
s.ypop = None
|
|
images.action(images.ActiveSprites[:])
|
|
|
|
def patget(n, keycol=None):
|
|
bitmap, rect = loadpattern(n, keycol)
|
|
return bitmap.geticon(*rect)
|
|
|
|
def get_lives():
|
|
return gamesrv.game.limitlives
|
|
|
|
def do_nothing():
|
|
while True:
|
|
yield 5
|
|
|
|
BoardList = []
|
|
curboard = None
|
|
BoardGen = [do_nothing()]
|
|
|
|
def next_board(num=0, complete=1, fastreenter=False):
|
|
yield force_singlegen()
|
|
set_frametime(1.0)
|
|
brd = curboard
|
|
inplace = 0
|
|
if brd:
|
|
inplace = brd.bonuslevel or fastreenter
|
|
num = brd.num
|
|
if not inplace:
|
|
num += gamesrv.game.stepboard
|
|
if num >= len(BoardList):
|
|
num = len(BoardList)-1
|
|
if (gamesrv.game.finalboard is not None and num > gamesrv.game.finalboard):
|
|
num = gamesrv.game.finalboard
|
|
for t in brd.leave(inplace=inplace):
|
|
yield t
|
|
|
|
# reset global board state
|
|
from player import BubPlayer, reset_global_board_state
|
|
reset_global_board_state()
|
|
if not inplace:
|
|
del BubPlayer.MonsterList[:]
|
|
# wait for at least one player
|
|
for t in wait_for_one_player():
|
|
yield t
|
|
# reload modules if changed
|
|
if loadmodules():
|
|
import boards
|
|
boards.BoardGen = [boards.next_board(num)]
|
|
return
|
|
|
|
if num < 0:
|
|
num = 0
|
|
elif num >= len(BoardList):
|
|
num = len(BoardList)-1
|
|
brd = BoardList[num](num)
|
|
for t in brd.enter(complete, inplace=inplace, fastreenter=fastreenter):
|
|
yield t
|
|
|
|
if brd.bonuslevel:
|
|
gen = bonus_play
|
|
else:
|
|
gen = normal_play
|
|
BoardGen[0] = gen()
|
|
|
|
def set_frametime(ft, privtime=100):
|
|
from player import BubPlayer
|
|
BubPlayer.BaseFrametime = ft
|
|
BubPlayer.PlayersPrivateTime = privtime
|
|
images.loadsounds(1.0 / ft)
|
|
|
|
def extra_boardgen(gen, at_end=0):
|
|
if curboard.playingboard:
|
|
if at_end or not BoardGen:
|
|
BoardGen.append(gen)
|
|
else:
|
|
BoardGen.insert(1, gen)
|
|
|
|
def replace_boardgen(gen, force=0):
|
|
if curboard.playingboard or force:
|
|
curboard.playingboard = 0
|
|
BoardGen[0] = gen
|
|
|
|
def force_singlegen():
|
|
del BoardGen[1:]
|
|
return 0
|
|
|
|
def has_singlegen():
|
|
return len(BoardGen) <= 1
|
|
|
|
def display_hat(p, d):
|
|
if p.team == -1 or getattr(d,'isdying',0) or hasattr(d,'no_hat'):
|
|
return
|
|
try:
|
|
bottom_up = d.bottom_up()
|
|
except AttributeError:
|
|
bottom_up = 0
|
|
try:
|
|
image = ('hat', p.team, d.dir, d.hatangle)
|
|
except AttributeError:
|
|
image = ('hat', p.team)
|
|
if bottom_up:
|
|
image = 'vflip', image
|
|
y = d.y
|
|
else:
|
|
y = d.y - 16
|
|
ico = images.sprget(image)
|
|
if (getattr(d,'hatsprite',None) is None or
|
|
not d.hatsprite.alive):
|
|
d.hatsprite = images.ActiveSprite(ico, d.x, y)
|
|
else:
|
|
d.hatsprite.to_front()
|
|
d.hatsprite.move(d.x, y, ico)
|
|
d.hatsprite.gen = [d.hatsprite.die([None])]
|
|
|
|
def normal_frame():
|
|
from player import BubPlayer
|
|
BubPlayer.FrameCounter += 1
|
|
|
|
# main generator dispatch loop
|
|
images.action(images.ActiveSprites[:])
|
|
|
|
frametime = 10
|
|
for p in BubPlayer.PlayerList:
|
|
if p.isplaying():
|
|
frametime = BubPlayer.BaseFrametime
|
|
p.zarkon()
|
|
for d in p.dragons:
|
|
d.to_front()
|
|
display_hat(p, d)
|
|
d.prefix(p.pn)
|
|
if not (BubPlayer.FrameCounter & 31):
|
|
gamesrv.compactsprites()
|
|
reset = getattr(BubPlayer, 'MultiplyerReset', 0)
|
|
if reset and BubPlayer.FrameCounter >= reset:
|
|
BubPlayer.MultiplyerReset = 0
|
|
set_frametime(1.0)
|
|
return frametime
|
|
|
|
def normal_play():
|
|
from player import BubPlayer
|
|
import bonuses
|
|
framecounter = 0
|
|
bonus_callback = bonuses.start_normal_play()
|
|
while BubPlayer.MonsterList:
|
|
bonus_callback()
|
|
yield normal_frame()
|
|
if not BubPlayer.DragonList:
|
|
continue
|
|
framecounter += 1
|
|
BASE = 500
|
|
if not (framecounter % BASE):
|
|
if framecounter == 4*BASE:
|
|
from monsters import Monster
|
|
from mnstrmap import BigImages
|
|
ico = images.sprget(BigImages.hurryup[1])
|
|
s = images.ActiveSprite(ico, (bwidth-ico.w)//2, (bheight-ico.h)//2)
|
|
s.setimages(s.die(BigImages.hurryup * 12, 2))
|
|
images.Snd.Hurry.play()
|
|
mlist = [s for s in images.ActiveSprites
|
|
if (isinstance(s, Monster) and s.regular() and
|
|
not s.angry)]
|
|
if mlist:
|
|
s = random.choice(mlist)
|
|
s.angry = [s.genangry()]
|
|
s.resetimages()
|
|
if framecounter >= 6*BASE:
|
|
mlist = [s for s in images.ActiveSprites
|
|
if isinstance(s, Monster) and s.regular() and s.angry]
|
|
if mlist:
|
|
images.Snd.Hell.play()
|
|
gamesrv.set_musics([], [])
|
|
s = random.choice(mlist)
|
|
s.become_ghost()
|
|
framecounter = -200
|
|
else:
|
|
framecounter = 2*BASE
|
|
if framecounter == 0:
|
|
curboard.set_musics()
|
|
replace_boardgen(last_monster_killed(), 1)
|
|
|
|
##def normal_play():
|
|
## # TESTING!!
|
|
## from player import BubPlayer
|
|
## for p in BubPlayer.PlayerList:
|
|
## if not p.icons:
|
|
## p.loadicons(p.icons, images.sprget)
|
|
## results = {BubPlayer.PlayerList[0]: 100,
|
|
## BubPlayer.PlayerList[1]: 200,
|
|
## BubPlayer.PlayerList[2]: 300,
|
|
## BubPlayer.PlayerList[3]: 400,
|
|
## BubPlayer.PlayerList[4]: 100,
|
|
## BubPlayer.PlayerList[5]: 200,
|
|
## BubPlayer.PlayerList[6]: 300,
|
|
## BubPlayer.PlayerList[7]: 400,
|
|
## BubPlayer.PlayerList[8]:1000,
|
|
## BubPlayer.PlayerList[9]:1000,
|
|
## }
|
|
## maximum = None
|
|
## for t in result_ranking(results, maximum):
|
|
## yield t
|
|
|
|
def last_monster_killed(end_delay=390, music=None):
|
|
from player import BubPlayer
|
|
for t in exit_board(music=music):
|
|
yield t
|
|
if curboard.bonuslevel:
|
|
curboard.playingboard = 1
|
|
for t in bonus_play():
|
|
yield t
|
|
end_delay -= 1
|
|
if end_delay <= 0:
|
|
replace_boardgen(next_board(), 1)
|
|
break
|
|
else:
|
|
for i in range(end_delay):
|
|
yield normal_frame()
|
|
replace_boardgen(next_board(), 1)
|
|
|
|
##def bonus_play():
|
|
## from player import BubPlayer
|
|
## import bubbles
|
|
## while BubPlayer.LimitScoreColor is None:
|
|
## yield normal_frame()
|
|
## players = [(p.points, p.pn) for p in BubPlayer.PlayerList
|
|
## if p.isplaying()]
|
|
## if players:
|
|
## players.sort()
|
|
## points, BubPlayer.LimitScoreColor = players[-1]
|
|
## BubPlayer.LimitScore = ((points + limit) // 100000) * 100000
|
|
## for p in BubPlayer.PlayerList:
|
|
## if p.isplaying():
|
|
## p.givepoints(0) # check LimitScore and update scoreboard()
|
|
## while not (BubPlayer.BubblesBecome or BubPlayer.MegaBonus):
|
|
## if random.random() < 0.06:
|
|
## bubbles.newbonusbubble()
|
|
## yield normal_frame()
|
|
## # special board end
|
|
## import monsters
|
|
## monsters.argh_em_all()
|
|
## replace_boardgen(last_monster_killed(), 1)
|
|
|
|
class TimeCounter(Copyable):
|
|
def __init__(self, limittime, blink=0):
|
|
from player import BubPlayer
|
|
self.saved_time = BubPlayer.LimitTime
|
|
self.time = limittime / FRAME_TIME
|
|
self.prev = None
|
|
self.blink = blink
|
|
def update(self, t):
|
|
from player import BubPlayer, scoreboard
|
|
self.time -= t
|
|
if self.time < 0.0:
|
|
self.time = 0.0
|
|
BubPlayer.LimitTime = self.time * FRAME_TIME
|
|
next = int(BubPlayer.LimitTime)
|
|
if self.blink and BubPlayer.LimitTime - next >= 0.5:
|
|
BubPlayer.LimitTime = next = None
|
|
if self.prev != next:
|
|
scoreboard(compresslimittime=1)
|
|
self.prev = next
|
|
def restore(self):
|
|
from player import BubPlayer
|
|
BubPlayer.LimitTime = self.saved_time
|
|
|
|
def bonus_play():
|
|
from player import BubPlayer
|
|
import bubbles
|
|
BubPlayer.MegaBonus = None
|
|
BubPlayer.BubblesBecome = None
|
|
Time0 = 5.0 / FRAME_TIME # when to slow down time
|
|
tc = TimeCounter(BubPlayer.LimitTime or 180.9) # 3:00
|
|
prev = None
|
|
while not (BubPlayer.BubblesBecome or BubPlayer.MegaBonus):
|
|
if random.random() < 0.099:
|
|
bubbles.newbonusbubble()
|
|
t = normal_frame()
|
|
tc.update(t)
|
|
if tc.time < Time0:
|
|
if tc.time <= 0.5:
|
|
tc.time = 0.5
|
|
BubPlayer.LimitTime = 0.0
|
|
t *= math.sqrt(Time0 / tc.time)
|
|
yield t
|
|
if tc.time == 0.5:
|
|
gamesrv.game.End = 'gameover'
|
|
gamesrv.game.updateboard()
|
|
replace_boardgen(game_over(), 1)
|
|
return
|
|
# special board end
|
|
import monsters
|
|
monsters.argh_em_all()
|
|
replace_boardgen(last_monster_killed(), 1)
|
|
|
|
def game_over():
|
|
yield force_singlegen()
|
|
from player import scoreboard
|
|
import ranking
|
|
images.Snd.Extralife.play()
|
|
gamesrv.set_musics([], [images.music_potion])
|
|
scoreboard()
|
|
for t in ranking.game_over():
|
|
yield t
|
|
|
|
def game_reset():
|
|
import time
|
|
from player import BubPlayer
|
|
t1 = time.time()
|
|
while 1:
|
|
yield 0
|
|
if BubPlayer.LimitTime and BubPlayer.LimitTime >= 1.0:
|
|
# someone else ticking the clock, try again later
|
|
return
|
|
if abs(time.time() - t1) > 2.0:
|
|
break
|
|
# anyone playing ?
|
|
if not gamesrv.game.End:
|
|
return # yes -> cancel game_reset()
|
|
# let's tick the clock !
|
|
tc = TimeCounter(60.9, blink=1) # 1:00
|
|
t1 = time.time()
|
|
while tc.time:
|
|
yield 0
|
|
# anyone playing now ?
|
|
if not gamesrv.game.End:
|
|
tc.restore()
|
|
return # yes -> cancel game_reset()
|
|
t = time.time() # use real time
|
|
deltat = (t-t1)/FRAME_TIME
|
|
if deltat < 1.0:
|
|
deltat = 1.0
|
|
elif deltat > 100.0:
|
|
deltat = 100.0
|
|
tc.update(deltat)
|
|
t1 = t
|
|
gamesrv.game.reset()
|
|
|
|
##def wasting_play():
|
|
## from player import BubPlayer, scoreboard
|
|
## import bubbles
|
|
## curboard.wastingplay = {}
|
|
## for p in BubPlayer.PlayerList:
|
|
## if p.isplaying():
|
|
## p.letters = {}
|
|
## p.bonbons = p.points // 50000
|
|
## scoreboard()
|
|
|
|
## while len(BubPlayer.DragonList) > 1:
|
|
## if random.random() < 0.03:
|
|
## bubbles.newbubble(1)
|
|
## yield normal_frame()
|
|
## for d in BubPlayer.DragonList:
|
|
## curboard.wastingplay[d.bubber] = len(curboard.wastingplay)
|
|
## for i in range(50):
|
|
## yield normal_frame()
|
|
|
|
## total = len(curboard.wastingplay)
|
|
## results = [(total-n, p) for p, n in curboard.wastingplay.items()]
|
|
## results.sort()
|
|
## results = [(p, str(n)) for n, p in results]
|
|
## for t in display_ranking(results):
|
|
## yield t
|
|
## # never ending
|
|
|
|
def skiplevels(blink, skip):
|
|
# (not used any more)
|
|
saved = BoardGen[:]
|
|
while skip:
|
|
skip -= 1
|
|
BoardGen[:] = saved
|
|
for i in range(10): # frozen pause
|
|
yield 3
|
|
if blink:
|
|
blink.step(-bwidth, 0)
|
|
yield 3.33
|
|
blink.step(bwidth, 0)
|
|
blink = None
|
|
for t in next_board(complete=(skip==0)):
|
|
yield t
|
|
|
|
def exit_board(delay=8, music=None, repeatmusic=[]):
|
|
from bubbles import Bubble
|
|
from bonuses import RandomBonus, end_normal_play
|
|
from player import BubPlayer
|
|
from monsters import Monster
|
|
end_normal_play()
|
|
curboard.playingboard = 0
|
|
actives = images.ActiveSprites[:]
|
|
for s in actives:
|
|
if ((isinstance(s, Monster) and s.still_playing())
|
|
or isinstance(s, RandomBonus)):
|
|
s.kill()
|
|
music = music or []
|
|
if BubPlayer.MegaBonus:
|
|
music[:1] = [images.music_modern]
|
|
if music or repeatmusic:
|
|
gamesrv.set_musics(music, repeatmusic)
|
|
for i in range(delay):
|
|
yield normal_frame()
|
|
bubble_outcome = BubPlayer.BubblesBecome or Bubble.pop
|
|
for s in actives:
|
|
if isinstance(s, Bubble):
|
|
bubble_outcome(s)
|
|
yield normal_frame()
|
|
if BubPlayer.MegaBonus:
|
|
BubPlayer.MegaBonus()
|
|
|
|
def potion_fill(blist, big=0):
|
|
from player import BubPlayer
|
|
from bonuses import Bonus
|
|
#timeleft = 1680.0
|
|
for t in exit_board(0, music=[images.music_potion]):
|
|
#timeleft -= t
|
|
yield t
|
|
notes = all_notes = []
|
|
y = 1
|
|
while y < 11 or (y < height-2 and (len(all_notes) < 10 or big)):
|
|
for x in range(2, width-3, 2):
|
|
if ' ' == bget(x,y) == bget(x+1,y) == bget(x,y+1) == bget(x+1,y+1):
|
|
b = Bonus(x*CELL, y*CELL, falling=0, *blist[((x+y)//2)%len(blist)])
|
|
b.timeout = (444,666)[big]
|
|
all_notes.append(b)
|
|
for i in range(2):
|
|
t = normal_frame()
|
|
#timeleft -= t
|
|
yield t
|
|
y += 2
|
|
while notes: #and timeleft > 0.0:
|
|
notes = [b for b in notes if b.alive]
|
|
t = normal_frame()
|
|
#timeleft -= t
|
|
yield t
|
|
for i in range(10):
|
|
t = normal_frame()
|
|
#timeleft -= t
|
|
yield t
|
|
results = {}
|
|
for b in all_notes:
|
|
for d in b.taken_by:
|
|
bubber = d.bubber
|
|
results[bubber] = results.get(bubber, 0) + 1
|
|
for t in result_ranking(results, len(all_notes)):
|
|
yield t
|
|
#fadeouttime = 3.33
|
|
#fullsoundframes = bonusframes - 10 - int(fadeouttime / FRAME_TIME)
|
|
#for i in range(fullsoundframes):
|
|
# yield normal_frame()
|
|
#gamesrv.fadeout(fadeouttime)
|
|
#for i in range(fullsoundframes, 490):
|
|
# yield normal_frame()
|
|
|
|
def result_ranking(results, maximum=None, timeleft=200):
|
|
import ranking
|
|
results = ranking.ranking_picture(results, maximum, timeleft is not None)
|
|
if curboard.bonuslevel and timeleft is not None:
|
|
play_again = bonus_play()
|
|
else:
|
|
play_again = None
|
|
for t in ranking.display(results, timeleft, play_again):
|
|
yield t
|
|
if gamesrv.game.End != 'gameover':
|
|
gamesrv.set_musics([], [])
|
|
replace_boardgen(next_board(), 1)
|
|
|
|
def extra_water_flood():
|
|
from mnstrmap import Flood
|
|
from monsters import Monster
|
|
waves_icons = [images.sprget(n) for n in Flood.waves]
|
|
fill_icon = images.sprget(Flood.fill)
|
|
bspr = []
|
|
if 'flood' in curboard.sprites:
|
|
return # only one flooding at a time
|
|
curboard.sprites['flood'] = bspr
|
|
waves_sprites = [gamesrv.Sprite(waves_icons[0], x, bheight-CELL)
|
|
for x in range(0, bwidth, CELL)]
|
|
bspr += waves_sprites
|
|
fill_by_line = []
|
|
poplist = [None]
|
|
while waves_sprites[0].y > 0:
|
|
yield 0
|
|
waves_icons.insert(0, waves_icons.pop())
|
|
for s in waves_sprites:
|
|
s.seticon(waves_icons[0])
|
|
yield 0
|
|
sprites = [gamesrv.Sprite(fill_icon, s.x, s.y) for s in waves_sprites]
|
|
bspr += sprites
|
|
fill_by_line.append(sprites)
|
|
for s in waves_sprites:
|
|
s.step(0, -16)
|
|
for s in images.touching(0, waves_sprites[0].y, bwidth, bheight):
|
|
if isinstance(s, Monster):
|
|
s.argh(poplist)
|
|
while 1:
|
|
for i in range(2):
|
|
yield 0
|
|
waves_icons.insert(0, waves_icons.pop())
|
|
for s in waves_sprites:
|
|
s.seticon(waves_icons[0])
|
|
if not fill_by_line:
|
|
break
|
|
for s in fill_by_line.pop():
|
|
s.kill()
|
|
for s in waves_sprites:
|
|
s.step(0, 16)
|
|
for s in waves_sprites:
|
|
s.kill()
|
|
del curboard.sprites['flood']
|
|
|
|
def extra_aquarium():
|
|
from mnstrmap import Flood
|
|
from player import BubPlayer
|
|
for i in range(200):
|
|
if 'flood' not in curboard.sprites: # only one flooding at a time
|
|
break
|
|
yield 0
|
|
if curboard.cleaning_gen_state:
|
|
return
|
|
else:
|
|
return
|
|
curboard.sprites['flood'] = []
|
|
gl = curboard.sprites.setdefault('background', [])
|
|
curboard.holes = True # so that random PlainBubbles show up anyway
|
|
walls = curboard.sprites['walls']
|
|
seen = {}
|
|
|
|
def newsprite(ico, x, y):
|
|
s = gamesrv.Sprite(ico, x, y)
|
|
s.to_back(walls[0])
|
|
gl.append(s)
|
|
return s
|
|
|
|
def fishplayers(ymin):
|
|
for d in BubPlayer.DragonList:
|
|
if d not in seen and d.y >= ymin:
|
|
seen[d] = True
|
|
d.become_fish()
|
|
d.bubber.emotic(d, 4)
|
|
|
|
waves_icons = [images.sprget(n) for n in Flood.waves]
|
|
fill_icon = images.sprget(Flood.fill)
|
|
waves_sprites = [newsprite(waves_icons[0], x, bheight-CELL)
|
|
for x in range(2*CELL + HALFCELL, bwidth - 2*CELL, CELL)]
|
|
while waves_sprites[0].y > -fill_icon.h:
|
|
fishplayers(waves_sprites[0].y)
|
|
yield 0
|
|
waves_icons.append(waves_icons.pop(0))
|
|
for s in waves_sprites:
|
|
s.seticon(waves_icons[0])
|
|
fishplayers(waves_sprites[0].y)
|
|
yield 0
|
|
for s in waves_sprites:
|
|
newsprite(fill_icon, s.x, s.y)
|
|
for s in waves_sprites:
|
|
s.step(0, -16)
|
|
for s in waves_sprites:
|
|
s.kill()
|
|
BubPlayer.SuperFish = True
|
|
fishplayers(-sys.maxsize)
|
|
|
|
def extra_walls_falling():
|
|
walls_by_pos = curboard.walls_by_pos
|
|
moves = 1
|
|
while moves and not curboard.cleaning_gen_state:
|
|
moves = 0
|
|
for y in range(height-3, -1, -1):
|
|
for x in range(2, width-2):
|
|
if ((y,x) in walls_by_pos and
|
|
(y+1,x) not in walls_by_pos and
|
|
(y+2,x) not in walls_by_pos):
|
|
y0 = y
|
|
while (y0-1,x) in walls_by_pos:
|
|
y0 -= 1
|
|
w = curboard.killwall(x, y0, 0)
|
|
curboard.putwall(x, y+1, w)
|
|
moves = 1
|
|
curboard.reorder_walls()
|
|
for y in range(6):
|
|
yield 0
|
|
|
|
def single_blocks_falling(xylist):
|
|
walls_by_pos = curboard.walls_by_pos
|
|
while xylist:
|
|
newlist = []
|
|
for x, y in xylist:
|
|
if ((y,x) in walls_by_pos and (y+1,x) not in walls_by_pos and
|
|
y < curboard.height-1):
|
|
newlist.append((x, y+1))
|
|
for x, y in newlist:
|
|
w = curboard.killwall(x, y-1, 0)
|
|
curboard.putwall(x, y, w)
|
|
xylist = newlist
|
|
curboard.reorder_walls()
|
|
for i in range(7):
|
|
yield 0
|
|
|
|
def extra_display_repulse(cx, cy, dlimit=5000, dfactor=1000):
|
|
offsets = {}
|
|
for s in list(gamesrv.sprites_by_n.values()):
|
|
x, y = s.getdisplaypos()
|
|
if x is not None:
|
|
dx = x - cx
|
|
dy = y - cy
|
|
d = dx*dx + dy*dy + 100
|
|
if d <= dlimit:
|
|
dx = (dx*dfactor)//d
|
|
dy = (dy*dfactor)//d
|
|
offsets[s] = dx, dy
|
|
s.setdisplaypos(int(x+dx), int(y+dy))
|
|
yield 0
|
|
yield 0
|
|
while offsets:
|
|
prevoffsets = offsets
|
|
offsets = {}
|
|
for s, (dx, dy) in list(prevoffsets.items()):
|
|
if s.alive:
|
|
if dx < 0:
|
|
dx += max(1, (-dx)//5)
|
|
elif dx:
|
|
dx -= max(1, dx//5)
|
|
if dy < 0:
|
|
dy += max(1, (-dy)//5)
|
|
elif dy:
|
|
dy -= max(1, dy//5)
|
|
if dx or dy:
|
|
offsets[s] = dx, dy
|
|
s.setdisplaypos(int(s.x+dx), int(s.y+dy))
|
|
yield 0
|
|
|
|
def extra_bkgnd_black(cx, cy):
|
|
gl = curboard.sprites.get('background')
|
|
dist = 0
|
|
while gl:
|
|
dist += 17
|
|
dist2 = dist * dist
|
|
gl2 = []
|
|
for s in gl:
|
|
if (s.x-cx)*(s.x-cx) + (s.y-cy)*(s.y-cy) < dist2:
|
|
s.kill()
|
|
else:
|
|
gl2.append(s)
|
|
gl[:] = gl2
|
|
yield 0
|
|
|
|
def extra_light_off(timeout, icocache={}):
|
|
for i in range(timeout):
|
|
if curboard.cleaning_gen_state:
|
|
break
|
|
dragons = {}
|
|
import player
|
|
playerlist = player.BubPlayer.PlayerList
|
|
for bubber in playerlist:
|
|
for dragon in bubber.dragons:
|
|
dragons[dragon] = True
|
|
for s in list(gamesrv.sprites_by_n.values()):
|
|
try:
|
|
ico = icocache[s.ico, s in dragons]
|
|
except KeyError:
|
|
ico = images.make_darker(s.ico, s in dragons)
|
|
icocache[s.ico, s in dragons] = ico
|
|
s.setdisplayicon(ico)
|
|
yield 0
|
|
for s in list(gamesrv.sprites_by_n.values()):
|
|
s.setdisplayicon(s.ico)
|
|
|
|
def extra_swap_up_down(N=27):
|
|
# unregister all walls
|
|
walls = list(curboard.walls_by_pos.items())
|
|
walls.sort()
|
|
if not walls:
|
|
return
|
|
curboard.walls_by_pos.clear()
|
|
emptyline = '##' + ' '*(width-4) + '##'
|
|
curboard.walls = [emptyline] * height
|
|
l = curboard.sprites['walls']
|
|
wallicon = l[0].ico
|
|
wallpool = l[:]
|
|
l[:] = [gamesrv.Sprite(wallicon, 0, -wallicon.h)]
|
|
|
|
# force the top half of the walls on front
|
|
#for (y,x), w in walls:
|
|
# if y*2 < height:
|
|
|
|
# show the walls swapping up/down
|
|
ycenter = ((height-1)*CELL) // 2
|
|
for i in range(N):
|
|
alpha = math.cos((math.pi*(i+1))/N)
|
|
ymap = {}
|
|
for y in range(height):
|
|
ymap[y] = int(alpha*(y*CELL-ycenter)) + ycenter
|
|
for (y,x), w in walls:
|
|
if y in ymap:
|
|
w.move(x*CELL, ymap[y])
|
|
yield 0
|
|
if i == (N+1)//2:
|
|
# reorder the wall sprites in the middle of the swap
|
|
walls = [((-y,x), w) for (y,x), w in walls]
|
|
walls.sort()
|
|
for i in range(len(walls)):
|
|
(y,x), w = walls[i]
|
|
walls[i] = (y,x), wallpool[i]
|
|
walls = [((-y,x), w) for (y,x), w in walls]
|
|
walls.sort()
|
|
# reverse all dragons!
|
|
from player import BubPlayer
|
|
for dragon in BubPlayer.DragonList:
|
|
dragon.dcap['gravity'] *= -1.0
|
|
|
|
# freeze the walls in their new position
|
|
i = 0
|
|
for (y,x), w in walls:
|
|
y = height-1 - y
|
|
if 0 <= y < height and (y,x) not in curboard.walls_by_pos:
|
|
w = wallpool[i]
|
|
i += 1
|
|
curboard.putwall(x, y, w)
|
|
l[:0] = wallpool[:i]
|
|
for w in wallpool[i:]:
|
|
w.kill()
|
|
curboard.reorder_walls()
|
|
|
|
def extra_catch_all_monsters(dragons=[], everything=False):
|
|
from monsters import Monster
|
|
from bubbles import BigBubbleCatcher
|
|
from bonuses import Bonus, BonusMaker
|
|
if not dragons:
|
|
from player import BubPlayer
|
|
dragons = BubPlayer.DragonList
|
|
i = 0
|
|
give_up = 33
|
|
for s in images.ActiveSprites[:]:
|
|
if curboard.cleaning_gen_state:
|
|
break
|
|
while not dragons:
|
|
give_up -= 1
|
|
if give_up == 0:
|
|
return
|
|
yield 0
|
|
if not s.alive or not s.touchable:
|
|
continue
|
|
if isinstance(s, Bonus):
|
|
ok = s.bubblable
|
|
elif isinstance(s, BonusMaker):
|
|
ok = everything
|
|
else:
|
|
ok = isinstance(s, Monster)
|
|
if ok:
|
|
dragon = dragons[i%len(dragons)]
|
|
BigBubbleCatcher(dragon, s, 542 + 17*i)
|
|
i += 1
|
|
yield 0
|
|
yield 0
|
|
|
|
def extra_make_random_level(cx=None, cy=None, repeat_delay=200):
|
|
from bonuses import DustStar
|
|
# generate any random level
|
|
localdir = os.path.dirname(__file__)
|
|
filename = os.path.join(localdir, 'levels', 'RandomLevels.py')
|
|
d = {}
|
|
exec(compile(open(filename, "rb").read(), filename, 'exec'), d)
|
|
Level = d['GenerateSingleLevel'](curboard.width, curboard.height)
|
|
lvl = Level(curboard.num)
|
|
walllist = []
|
|
if cx is None: cx = bwidth // 2
|
|
if cy is None: cy = bheight // 2
|
|
for y in range(curboard.height):
|
|
dy = cy - (y*16+8)
|
|
dy2 = dy*dy*0.75
|
|
for x in range(2, curboard.width-2):
|
|
dx = cx - (x*16+8)
|
|
d2 = dx*dx + dy2
|
|
walllist.append((d2, x, y))
|
|
walllist.sort()
|
|
|
|
# dynamically replace the current level's walls with the new level's
|
|
dist = 0
|
|
speedf = 15.0
|
|
added = 0
|
|
for d2, x, y in walllist:
|
|
while d2 > dist*dist:
|
|
if added:
|
|
curboard.reorder_walls()
|
|
added = 0
|
|
yield 0
|
|
dist += 4.1
|
|
speedf *= 0.99
|
|
if curboard.walls[y][x] == ' ':
|
|
if lvl.walls[y][x] == ' ':
|
|
continue
|
|
else:
|
|
curboard.putwall(x, y)
|
|
added = 1
|
|
big = 1
|
|
else:
|
|
if lvl.walls[y][x] == ' ':
|
|
curboard.killwall(x, y)
|
|
big = 1
|
|
else:
|
|
big = 0
|
|
sx = x*16
|
|
sy = y*16
|
|
DustStar(sx - 8*big,
|
|
sy - 8*big,
|
|
speedf * (sx-cx) / dist,
|
|
speedf * (sy-cy) / dist,
|
|
big=big)
|
|
# patch the winds too
|
|
curboard.winds = lvl.winds
|
|
curboard.reorder_walls()
|
|
yield 0
|
|
# wait a bit and restart
|
|
if repeat_delay < 1000:
|
|
for i in range(repeat_delay):
|
|
yield 0
|
|
if curboard.cleaning_gen_state:
|
|
return
|
|
extra_boardgen(extra_make_random_level(
|
|
repeat_delay = repeat_delay * 3 // 2))
|
|
|
|
def extra_bubbles(timeout):
|
|
from bubbles import newforcedbubble
|
|
falloff = 0.25
|
|
L = math.log(0.965) # same speed falloff rate as in throwing_bubble()
|
|
cx = (bwidth - CELL) // 2
|
|
cy = (bheight - CELL) // 2
|
|
for i in range(timeout):
|
|
if curboard.cleaning_gen_state:
|
|
return
|
|
if random.random() < falloff:
|
|
bubble = newforcedbubble()
|
|
if bubble:
|
|
tx = random.randrange(CELL, bwidth - 2*CELL) - cx
|
|
ty = random.randrange(CELL, bheight - 2*CELL) - cy
|
|
if ty == 0:
|
|
ty = 1
|
|
dist = math.sqrt(tx * tx + ty * ty)
|
|
acos = tx / dist
|
|
asin = ty / dist
|
|
hspeed = 4 - dist * L
|
|
bubble.thrown_bubble(cx, cy, hspeed, (acos, asin))
|
|
falloff *= 0.998
|
|
yield 0
|
|
|
|
|
|
def initsubgame(music, displaypoints):
|
|
from player import BubPlayer, scoreboard
|
|
for t in exit_board(0, repeatmusic=[music]):
|
|
yield t
|
|
BubPlayer.DisplayPoints = displaypoints
|
|
scoreboard()
|
|
for t in curboard.clean_gen_state():
|
|
yield t
|
|
|
|
def register(dict):
|
|
global width, height, bwidth, bheight, bheightmod
|
|
items = list(dict.items())
|
|
items.sort()
|
|
for name, board in items:
|
|
try:
|
|
if not issubclass(board, Board) or board is Board:
|
|
continue
|
|
except TypeError:
|
|
continue
|
|
BoardList.append(board)
|
|
# check sizes
|
|
assert BoardList, "board file does not define any board"
|
|
B = BoardList[0]
|
|
try:
|
|
test = B(-1)
|
|
width = test.width
|
|
height = test.height
|
|
for B in BoardList[1:]:
|
|
test = B(-1)
|
|
assert test.width == width, "some boards have a different width"
|
|
assert test.height == height, "some boards have a different height"
|
|
except Exception as e:
|
|
print('Caught "%s" in level "%s":' % (e, B.__name__))
|
|
raise e
|
|
bwidth = width*CELL
|
|
bheight = height*CELL
|
|
bheightmod = (height+2)*CELL
|
|
|
|
##def define_boards(filename):
|
|
## global curboard, boards, width, height, bwidth, bheight, bheightmod
|
|
## curboard = None
|
|
## boards = []
|
|
## def board((wallfile, wallrect), shape):
|
|
## lines = shape.strip().split('\n')
|
|
## bmp = gamesrv.getbitmap(wallfile)
|
|
## wallicon = bmp.geticon(*wallrect)
|
|
## boards.append(Board(lines, wallicon))
|
|
## d = {'board': board}
|
|
## execfile(filename, d)
|
|
## assert boards, "board file does not define any board"
|
|
## width = boards[0].width
|
|
## height = boards[0].height
|
|
## for b in boards[1:]:
|
|
## assert b.width == width, "some boards have a different width"
|
|
## assert b.height == height, "some boards have a different height"
|
|
## bwidth = width*CELL
|
|
## bheight = height*CELL
|
|
## bheightmod = len(boards[0].lines)*CELL
|
|
|
|
|
|
#try:
|
|
# import psyco
|
|
#except ImportError:
|
|
# pass
|
|
#else:
|
|
# psyco.bind(normal_frame)
|