mirror of
https://github.com/abakh/nbsdgames
synced 2025-05-18 15:59:34 -04:00
404 lines
12 KiB
Python
404 lines
12 KiB
Python
from __future__ import generators
|
|
import os, math, random
|
|
import images, gamesrv
|
|
from images import ActiveSprite
|
|
from boards import CELL, HALFCELL
|
|
from mnstrmap import GreenAndBlue
|
|
from bubbles import BubblingEyes, Bubble
|
|
from bonuses import Bonus
|
|
|
|
LocalDir = os.path.basename(os.path.dirname(__file__))
|
|
|
|
|
|
localmap = {
|
|
'ark-paddle': ('image1-%d.ppm', (0, 0, 80, 32)),
|
|
}
|
|
|
|
music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav'))
|
|
snd_wall = gamesrv.getsample(os.path.join(LocalDir, 'wall.wav'))
|
|
snd_brick = gamesrv.getsample(os.path.join(LocalDir, 'brick.wav'))
|
|
|
|
|
|
def aget(x, y):
|
|
if 0 <= x < curboard.width and y >= 0:
|
|
if y >= curboard.height:
|
|
return ' '
|
|
return curboard.walls[y][x]
|
|
else:
|
|
return '#'
|
|
|
|
def sign(x):
|
|
if x >= 0.0:
|
|
return 1
|
|
else:
|
|
return -1
|
|
|
|
|
|
class PaddleEyes(BubblingEyes):
|
|
|
|
def __init__(self, bubber, saved_caps, paddle):
|
|
BubblingEyes.__init__(self, bubber, saved_caps, paddle)
|
|
self.deltax = (paddle.ico.w - self.ico.w) // 2
|
|
self.deltay = (paddle.ico.h - self.ico.h) // 2
|
|
self.step(self.deltax, self.deltay)
|
|
|
|
def playing_bubble(self, paddle, accel=0.75, vmax=4.5):
|
|
import boards
|
|
dx = self.deltax
|
|
dy = self.deltay
|
|
bubber = paddle.bubber
|
|
vx = 0.0
|
|
fx = paddle.x
|
|
while paddle.alive:
|
|
wannago = bubber.wannago(self.dcap)
|
|
if paddle.timeleft is None:
|
|
keydy = 0
|
|
else:
|
|
keydy = -1
|
|
key = ('eyes', wannago, keydy)
|
|
if fx < 2*CELL:
|
|
if vx < 0.0:
|
|
vx = -vx * 0.45
|
|
wannago = 1
|
|
elif fx + paddle.ico.w > boards.bwidth - 2*CELL:
|
|
if vx > 0.0:
|
|
vx = -vx * 0.45
|
|
wannago = -1
|
|
if not wannago:
|
|
if -accel <= vx <= accel:
|
|
vx = 0
|
|
elif vx < 0.0:
|
|
wannago = 0.7
|
|
else:
|
|
wannago = -0.7
|
|
vx += accel * wannago
|
|
if vx < -vmax:
|
|
vx = -vmax
|
|
elif vx > vmax:
|
|
vx = vmax
|
|
fx += vx
|
|
paddle.move(int(fx), paddle.y)
|
|
self.move(paddle.x+dx, paddle.y+dy, images.sprget(key))
|
|
yield None
|
|
self.kill()
|
|
|
|
def bottom_up(self):
|
|
return 0
|
|
|
|
|
|
class Paddle(ActiveSprite):
|
|
|
|
def __init__(self, arkanoid, bubber, px, py):
|
|
ico = images.sprget(('ark-paddle', bubber.pn))
|
|
ActiveSprite.__init__(self, ico, px - (ico.w-2*CELL)//2,
|
|
py - (ico.h-2*CELL)//2)
|
|
self.arkanoid = arkanoid
|
|
self.bubber = bubber
|
|
self.timeleft = None
|
|
self.gen.append(self.bounce_down())
|
|
self.gen.append(self.bkgndstuff())
|
|
self.arkanoid.paddles.append(self)
|
|
|
|
def bounce_down(self):
|
|
import boards
|
|
target_y = boards.bheight - self.ico.h
|
|
fy = self.y
|
|
vy = 0.0
|
|
while fy < target_y or abs(vy) > 0.3:
|
|
if fy < target_y:
|
|
vy += 0.3
|
|
elif vy > 0.0:
|
|
vy = -vy / 3.0
|
|
fy += vy
|
|
self.move(self.x, int(fy))
|
|
yield None
|
|
while self.y > target_y:
|
|
self.step(0, -2)
|
|
yield None
|
|
self.move(self.x, target_y)
|
|
self.gen.append(self.wait_and_shoot())
|
|
|
|
def wait_and_shoot(self):
|
|
timeout = 30
|
|
while timeout > 0:
|
|
timeout -= self.arkanoid.ready
|
|
yield None
|
|
self.gen.append(self.catch(Ball(self)))
|
|
|
|
def catch(self, ball):
|
|
import boards
|
|
while ball.alive:
|
|
if ball.y > boards.bheight//2+1 and ball.vy > 0.0:
|
|
deltay = self.y - Ball.Y_MARGIN - ball.y
|
|
self.timeleft = deltay / ball.vy
|
|
#if -1.25 <= self.timeleft <= 0.5:
|
|
if -12 <= deltay <= 1:
|
|
ball.bouncepad(self.arkanoid.paddles)
|
|
yield None
|
|
self.timeleft = None
|
|
if ball.missed:
|
|
self.kill()
|
|
|
|
def kill(self):
|
|
images.Snd.Pop.play(1.0, pad=0.0)
|
|
images.Snd.Pop.play(1.0, pad=1.0)
|
|
ico = images.sprget(Bubble.exploding_bubbles[0])
|
|
for i in range(11):
|
|
s = ActiveSprite(ico,
|
|
self.x + random.randrange(self.ico.w) - CELL,
|
|
self.y + random.randrange(self.ico.h) - CELL)
|
|
s.gen.append(s.die(Bubble.exploding_bubbles))
|
|
try:
|
|
self.arkanoid.paddles.remove(self)
|
|
except ValueError:
|
|
pass
|
|
ActiveSprite.kill(self)
|
|
|
|
def bkgndstuff(self):
|
|
while 1:
|
|
if self.timeleft is not None:
|
|
self.arkanoid.order.append((self.timeleft, self))
|
|
yield None
|
|
touching = images.touching(self.x+1, self.y+1,
|
|
self.ico.w-2, self.ico.h-2)
|
|
touching.reverse()
|
|
for s in touching:
|
|
if isinstance(s, Bonus):
|
|
s.touched(self)
|
|
|
|
def score(self, hits):
|
|
bricks = self.arkanoid.bricks
|
|
bricks[self.bubber] = bricks.get(self.bubber, 0) + hits
|
|
self.bubber.givepoints(125*(2**hits))
|
|
|
|
|
|
class Ball(ActiveSprite):
|
|
|
|
Y_MARGIN = 20
|
|
SPEED = 5.8
|
|
|
|
def __init__(self, paddle):
|
|
self.paddle = paddle
|
|
imglist1 = GreenAndBlue.new_bubbles[paddle.bubber.pn]
|
|
ActiveSprite.__init__(self, images.sprget(imglist1[0]),
|
|
paddle.x + paddle.ico.w//2,
|
|
paddle.y - Ball.Y_MARGIN)
|
|
self.missed = 0
|
|
self.setimages(self.imgseq(imglist1[1:], 6))
|
|
self.bounceangle(0.2)
|
|
self.gen.append(self.flying())
|
|
|
|
def bouncepad(self, paddles):
|
|
for paddle in paddles:
|
|
dx = (self.x + self.ico.w//2) - (paddle.x + paddle.ico.w//2)
|
|
dxmax = paddle.ico.w//2
|
|
angle = float(dx) / dxmax
|
|
if 0.0 <= angle <= 1.0:
|
|
angle = angle * 1.111 + 0.07
|
|
elif -1.0 <= angle <= 0.0:
|
|
angle = angle * 1.111 - 0.07
|
|
else:
|
|
continue
|
|
self.bounceangle(angle)
|
|
self.play(snd_wall)
|
|
break
|
|
|
|
def bounceangle(self, angle):
|
|
self.vx = math.sin(angle) * self.SPEED
|
|
self.vy = - math.cos(angle) * self.SPEED
|
|
|
|
def flying(self):
|
|
import boards
|
|
fx = self.x
|
|
fy = self.y
|
|
while self.y < boards.bheight:
|
|
fx += self.vx
|
|
fy += self.vy
|
|
self.move(int(fx), int(fy))
|
|
yield None
|
|
cx = self.x // CELL + 1
|
|
cy = self.y // CELL + 1
|
|
dx = sign(self.vx)
|
|
dy = sign(self.vy)
|
|
hits = 0.0
|
|
if aget(cx, cy) == '#':
|
|
hits += self.ahit(cx, cy, 0, 0)
|
|
if aget(cx+dx, cy) == '#':
|
|
hits += self.ahit(cx+dx, cy, 0, dy)
|
|
self.vx = -self.vx
|
|
if aget(cx, cy+dy) == '#':
|
|
hits += self.ahit(cx, cy+dy, dx, 0)
|
|
self.vy = -self.vy
|
|
if hits:
|
|
hits = int(hits)
|
|
if hits:
|
|
self.paddle.score(hits)
|
|
self.play(snd_brick)
|
|
else:
|
|
self.play(snd_wall)
|
|
self.missed = 1
|
|
self.kill()
|
|
|
|
def ahit(self, cx, cy, dx, dy):
|
|
total = 0.01
|
|
for i in (-1, 0, 1):
|
|
x = cx + i*dx
|
|
y = cy + i*dy
|
|
if (2 <= x < curboard.width - 2 and 0 <= y < curboard.height and
|
|
aget(x, y) == '#'):
|
|
curboard.killwall(x, y)
|
|
self.paddle.arkanoid.killedbricks += 1
|
|
total += 1.0
|
|
return total
|
|
|
|
def pop(self):
|
|
self.play(images.Snd.Pop)
|
|
self.gen = [self.die(Bubble.exploding_bubbles)]
|
|
|
|
|
|
class Arkanoid:
|
|
|
|
def bgen(self, limittime = 60.1): # 0:60
|
|
import boards
|
|
from player import BubPlayer
|
|
|
|
self.bricks = {}
|
|
for t in boards.initsubgame(music, self.displaypoints):
|
|
yield t
|
|
|
|
tc = boards.TimeCounter(limittime)
|
|
self.ready = 0
|
|
self.builddelay = {}
|
|
self.nbbricks = 0
|
|
self.order = []
|
|
self.paddles = []
|
|
#finish = 0
|
|
for t in self.frame():
|
|
self.order_paddles()
|
|
t = boards.normal_frame()
|
|
self.build_paddles()
|
|
yield t
|
|
#if len(self.paddles) == 0:
|
|
# finish += 1
|
|
# if finish == 20:
|
|
# break
|
|
#else:
|
|
# finish = 0
|
|
tc.update(t)
|
|
if tc.time == 0.0:
|
|
break
|
|
if (BubPlayer.FrameCounter & 15) == 7:
|
|
for s in images.ActiveSprites:
|
|
if isinstance(s, Bonus):
|
|
s.timeout = 0 # bonuses stay
|
|
elif isinstance(s, Bubble):
|
|
s.pop()
|
|
|
|
tc.restore()
|
|
self.ready = 0
|
|
for s in images.ActiveSprites[:]:
|
|
if isinstance(s, Ball):
|
|
s.pop()
|
|
for t in boards.result_ranking(self.bricks, self.nbbricks):
|
|
self.build_paddles()
|
|
yield t
|
|
self.remove_paddles()
|
|
self.unframe()
|
|
|
|
def displaypoints(self, bubber):
|
|
return self.bricks.get(bubber, 0)
|
|
|
|
def frame(self):
|
|
for y in range(curboard.height-1, curboard.height//2, -1):
|
|
yield None
|
|
yield None
|
|
for x in range(2, curboard.width-2):
|
|
if aget(x, y) == '#':
|
|
curboard.killwall(x, y)
|
|
brickline = curboard.width-4
|
|
expected = (brickline * curboard.height) // 5
|
|
y = curboard.height//2
|
|
nbbricks = 0
|
|
while y>=0 and nbbricks + (y+1)*brickline >= expected:
|
|
yield None
|
|
for x in range(2, curboard.width-2):
|
|
if aget(x, y) == '#':
|
|
nbbricks += 1
|
|
y -= 1
|
|
while y >= -1:
|
|
yield None
|
|
yield None
|
|
for x in range(2, curboard.width-2):
|
|
if y < 0 or aget(x, y) == ' ':
|
|
curboard.putwall(x, y)
|
|
nbbricks += brickline
|
|
curboard.reorder_walls()
|
|
y -= 1
|
|
|
|
nbbricks -= brickline
|
|
self.ready = 1
|
|
self.nbbricks = nbbricks
|
|
self.killedbricks = 0
|
|
while self.killedbricks < self.nbbricks:
|
|
yield None
|
|
|
|
def unframe(self):
|
|
for x in range(2, curboard.width-2):
|
|
curboard.killwall(x, -1)
|
|
|
|
def build_paddles(self):
|
|
from player import BubPlayer
|
|
for p in BubPlayer.PlayerList:
|
|
dragons = [d for d in p.dragons if not isinstance(d, PaddleEyes)]
|
|
if dragons and len(p.dragons) == len(dragons):
|
|
if self.builddelay.get(p):
|
|
self.builddelay[p] -= 1
|
|
else:
|
|
self.builddelay[p] = 53
|
|
dragon = random.choice(dragons)
|
|
paddle = Paddle(self, p, dragon.x, dragon.y)
|
|
eyes = PaddleEyes(p, dragon.dcap, paddle)
|
|
p.dragons.append(eyes)
|
|
p.emotic(dragon, 4)
|
|
for d in dragons:
|
|
d.kill()
|
|
|
|
def order_paddles(self):
|
|
self.order.sort()
|
|
self.order.reverse()
|
|
for timeleft, paddle in self.order:
|
|
try:
|
|
self.paddles.remove(paddle)
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
self.paddles.insert(0, paddle)
|
|
paddle.to_front()
|
|
del self.order[:]
|
|
|
|
def remove_paddles(self):
|
|
killclasses = (Paddle, PaddleEyes, Ball, Bonus)
|
|
for s in images.ActiveSprites[:]:
|
|
if isinstance(s, killclasses):
|
|
s.kill()
|
|
|
|
# This game is suitable for at least min_players players
|
|
min_players = 1
|
|
|
|
def run():
|
|
global curboard
|
|
import boards
|
|
from boards import curboard
|
|
boards.replace_boardgen(Arkanoid().bgen())
|
|
|
|
def setup():
|
|
from player import BubPlayer
|
|
for key, (filename, rect) in localmap.items():
|
|
filename = os.path.join(LocalDir, filename)
|
|
if filename.find('%d') >= 0:
|
|
for p in BubPlayer.PlayerList:
|
|
images.sprmap[key, p.pn] = (filename % p.pn, rect)
|
|
else:
|
|
images.sprmap[key] = (filename, rect)
|
|
setup()
|