scrapeboard/NS.py

552 lines
18 KiB
Python
Raw Normal View History

2017-12-14 03:49:02 -05:00
from random import randint, choice
from copy import copy
from pygame import Surface, Color
from pygame.mixer import Sound
from pygame.image import load
from pygame.transform import rotate
from pygame.time import get_ticks
from pygame.font import Font
from pygame.locals import *
from lib.pgfw.pgfw.Game import Game
from lib.pgfw.pgfw.GameChild import GameChild
from lib.pgfw.pgfw.Sprite import Sprite, RainbowSprite
from lib.pgfw.pgfw.Animation import Animation
from lib.pgfw.pgfw.extension import get_step_relative
from lib.pgfw.pgfw.gfx_extension import aa_filled_polygon
class SoundEffect(GameChild, Sound):
def __init__(self, parent, path, volume=1.0):
GameChild.__init__(self, parent)
Sound.__init__(self, path)
self.display_surface = self.get_display_surface()
self.initial_volume = volume
self.set_volume(volume)
def play(self, loops=0, maxtime=0, fade_ms=0, position=None, x=None):
self.set_volume(self.initial_volume *
self.get_configuration("audio", "sfx-volume"))
channel = Sound.play(self, loops, maxtime, fade_ms)
if x is not None:
position = float(x) / self.display_surface.get_width()
if position is not None and channel is not None:
channel.set_volume(*self.get_panning(position))
return channel
def get_panning(self, position):
return 1 - max(0, ((position - .5) * 2)), \
1 + min(0, ((position - .5) * 2))
class NS(Game, Animation):
NW, NE, SE, SW = range(4)
2017-12-20 18:56:58 -05:00
N, E, S, W, NE, NW = range(6)
2017-12-14 03:49:02 -05:00
FRONT_WIDTH = 230
BACK_WIDTH = 500
LENGTH = 150
FRONT = 300
STEP = .4
def __init__(self):
Game.__init__(self)
Animation.__init__(self, self)
self.subscribe(self.respond, KEYDOWN)
self.subscribe(self.respond, KEYUP)
self.subscribe(self.respond)
ds = self.get_display_surface()
self.background = Surface(ds.get_size())
self.background.fill((0, 0, 0))
2017-12-20 18:56:58 -05:00
self.platform = Platform(self)
2017-12-14 03:49:02 -05:00
self.chemtrails = Chemtrails(self)
self.boss = Boss(self)
self.last_press = get_ticks()
self.register(self.unsuppress_restart)
self.reset()
def reset(self):
2017-12-20 18:56:58 -05:00
self.suppressing_input = False
2017-12-14 03:49:02 -05:00
self.suppress_restart = False
self.game_over = False
self.boss.reset()
self.chemtrails.reset()
2017-12-20 18:56:58 -05:00
self.platform.reset()
2017-12-14 03:49:02 -05:00
def respond(self, event):
2017-12-20 18:56:58 -05:00
if not self.suppressing_input and event.type in (KEYDOWN, KEYUP):
2017-12-14 03:49:02 -05:00
# if self.last_press <= get_ticks() - int(self.get_configuration("input", "buffer")):
pressed = True if event.type == KEYDOWN else False
2017-12-20 18:56:58 -05:00
lights = self.platform.lights
if event.key in (K_UP, K_o):
lights[0].pressed = pressed
2017-12-14 03:49:02 -05:00
if self.game_over and not self.suppress_restart:
self.reset()
2017-12-20 18:56:58 -05:00
elif event.key in (K_RIGHT, K_p):
lights[1].pressed = pressed
elif event.key in (K_DOWN, K_SEMICOLON):
lights[2].pressed = pressed
elif event.key in (K_LEFT, K_l):
lights[3].pressed = pressed
2017-12-14 03:49:02 -05:00
self.last_press = get_ticks()
else:
if self.get_delegate().compare(event, "reset-game"):
self.reset()
def finish_battle(self, win):
self.game_over = True
font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 128)
text = font.render("YOU WIN" if win else "GAME OVER", True, Color("white"))
self.message = Sprite(self)
self.message.add_frame(text)
self.message.location.center = self.get_display_surface().get_rect().center
self.boss.halt(self.boss.brandish)
self.boss.sword.reset()
self.boss.queue = []
self.boss.brandish_complete = True
if win:
self.boss.set_frameset(0)
self.suppress_restart = True
self.play(self.unsuppress_restart, delay=4000, play_once=True)
def unsuppress_restart(self):
self.suppress_restart = False
font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 48)
text = font.render("(PRESS BLUE TO RESTART)", True, Color("white"))
self.restart_message = Sprite(self)
self.restart_message.add_frame(text)
dsr = self.get_display_surface().get_rect()
self.restart_message.location.center = dsr.centerx, dsr.centery + 80
def update(self):
Animation.update(self)
self.get_display_surface().blit(self.background, (0, 0))
self.boss.update()
2017-12-20 18:56:58 -05:00
self.platform.update()
2017-12-14 03:49:02 -05:00
self.chemtrails.update()
if self.game_over:
self.message.update()
if not self.suppress_restart:
self.restart_message.update()
2017-12-20 18:56:58 -05:00
class Title(GameChild):
def __init__(self, parent):
GameChild.__init__(self, parent)
def reset(self):
self.activate()
def activate(self):
self.active = True
def deactivate(self):
self.active = False
def update(self):
if self.active:
pass
class Introduction(Animation):
def __init__(self, parent):
Animation.__init__(self, parent)
def reset(self):
self.deactivate()
def deactivate(self):
self.active = False
def activate(self):
self.active = True
def update(self):
if self.active:
Animation.update(self)
class Wipe(Animation):
def __init__(self, parent):
Animation.__init__(self, parent)
def reset(self):
self.deactivate()
def deactivate(self):
self.active = False
def activate(self):
self.active = True
def update(self):
if self.active:
Animation.update(self)
class Platform(GameChild):
def __init__(self, parent):
GameChild.__init__(self, parent)
self.lights = [
Light(self, "cyan", NS.NW),
Light(self, "magenta", NS.NE),
Light(self, "yellow", NS.SE),
Light(self, "white", NS.SW)
]
def reset(self):
for light in self.lights:
light.reset()
def get_pressed(self):
return [light.position for light in self.lights if light.pressed]
2017-12-20 19:31:25 -05:00
def get_edge_pressed(self):
pressed = self.get_pressed()
if NS.NW in pressed and NS.NE in pressed:
return NS.N
elif NS.NE in pressed and NS.SW in pressed:
return NS.NE
elif NS.NE in pressed and NS.SE in pressed:
return NS.E
elif NS.NW in pressed and NS.SE in pressed:
return NS.NW
elif NS.SE in pressed and NS.SW in pressed:
return NS.S
elif NS.SW in pressed and NS.NW in pressed:
return NS.W
2017-12-20 18:56:58 -05:00
def update(self):
for light in self.lights:
light.update()
2017-12-14 03:49:02 -05:00
class Light(Animation):
def __init__(self, parent, color, position):
Animation.__init__(self, parent)
self.color = Color(color)
self.position = position
self.pressed = False
ds = self.get_display_surface()
frontleft = ds.get_width() / 2 - NS.FRONT_WIDTH / 2, NS.FRONT
backleft = ds.get_width() / 2 - NS.BACK_WIDTH / 2, NS.FRONT + NS.LENGTH
left_step = get_step_relative(frontleft, backleft, NS.STEP)
midleft = frontleft[0] + left_step[0], frontleft[1] + left_step[1]
frontmid = ds.get_width() / 2, NS.FRONT
mid = ds.get_width() / 2, NS.FRONT + NS.LENGTH * NS.STEP
backmid = ds.get_width() / 2, NS.FRONT + NS.LENGTH
frontright = ds.get_width() / 2 + NS.FRONT_WIDTH / 2, NS.FRONT
backright = ds.get_width() / 2 + NS.BACK_WIDTH / 2, NS.FRONT + NS.LENGTH
right_step = get_step_relative(frontright, backright, NS.STEP)
midright = frontright[0] + right_step[0], frontright[1] + right_step[1]
if self.position == NS.NW:
self.points = frontleft, frontmid, mid, midleft
elif self.position == NS.NE:
self.points = frontmid, frontright, midright, mid
elif self.position == NS.SE:
self.points = mid, midright, backright, backmid
elif self.position == NS.SW:
self.points = midleft, mid, backmid, backleft
self.register(self.blink, interval=300)
def reset(self):
self.hidden = False
self.halt(self.blink)
2017-12-20 18:56:58 -05:00
self.reset_timer()
2017-12-14 03:49:02 -05:00
def blink(self):
self.hidden = not self.hidden
def update(self):
Animation.update(self)
boss = self.get_game().boss
chemtrails = self.get_game().chemtrails
if boss.queue and boss.brandish_complete and not self.is_playing(self.blink) \
and self.in_orientation(boss.queue[chemtrails.queue_index]):
self.play(self.blink)
elif self.is_playing(self.blink) and (not boss.queue or
not self.in_orientation(boss.queue[chemtrails.queue_index])):
self.reset()
if not self.hidden:
aa_filled_polygon(self.get_display_surface(), self.points, self.color)
def in_orientation(self, orientation):
if self.position == NS.NW:
2017-12-20 18:56:58 -05:00
return orientation in (NS.N, NS.NW, NS.W)
2017-12-14 03:49:02 -05:00
elif self.position == NS.NE:
2017-12-20 18:56:58 -05:00
return orientation in (NS.N, NS.NE, NS.E)
2017-12-14 03:49:02 -05:00
elif self.position == NS.SE:
2017-12-20 18:56:58 -05:00
return orientation in (NS.NW, NS.E, NS.S)
2017-12-14 03:49:02 -05:00
elif self.position == NS.SW:
2017-12-20 18:56:58 -05:00
return orientation in (NS.S, NS.NE, NS.W)
2017-12-14 03:49:02 -05:00
class Chemtrails(GameChild):
TIME_LIMIT = 8000
TIME_ADDITION = 1000
def __init__(self, parent):
GameChild.__init__(self, parent)
self.image = load(self.get_resource("Chemtrails.png")).convert_alpha()
self.life = Life(self)
def reset(self):
self.life.reset()
self.timer_remaining = self.TIME_LIMIT
def challenge(self):
self.timer_remaining = self.TIME_LIMIT
self.queue_index = 0
def update(self):
self.orient()
if self.get_game().boss.queue:
self.timer_remaining -= self.get_game().time_filter.get_last_frame_duration()
self.attack()
if self.timer_remaining < 0:
self.life.decrease()
if not self.get_game().game_over:
self.timer_remaining = self.TIME_LIMIT
self.get_game().boss.combo()
font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 24)
text = font.render("%.2f" % max(0, self.timer_remaining / 1000.0), True, Color("white"))
rect = text.get_rect()
ds = self.get_display_surface()
rect.topright = ds.get_rect().topright
ds.blit(text, rect)
self.life.update()
def attack(self):
boss = self.get_game().boss
queue = boss.queue
if self.orientation == queue[self.queue_index]:
self.timer_remaining += self.TIME_ADDITION
boss.health.decrease(5)
self.queue_index += 1
if self.queue_index == len(queue):
self.timer_remaining = self.TIME_LIMIT
self.get_game().boss.combo()
2017-12-20 18:56:58 -05:00
self.get_game().platform.reset()
2017-12-14 03:49:02 -05:00
def orient(self):
ds = self.get_display_surface()
2017-12-20 19:31:25 -05:00
edge = self.get_game().platform.get_edge_pressed()
if edge == NS.N:
2017-12-14 03:49:02 -05:00
rect = self.image.get_rect()
rect.center = ds.get_width() / 2, NS.FRONT - 30
ds.blit(self.image, rect.topleft)
2017-12-20 18:56:58 -05:00
self.orientation = NS.N
2017-12-20 19:31:25 -05:00
elif edge == NS.E:
2017-12-14 03:49:02 -05:00
image = rotate(self.image, 270)
rect = image.get_rect()
rect.center = ds.get_width() / 2 + NS.FRONT_WIDTH / 2, NS.FRONT + NS.LENGTH * NS.STEP + 10
ds.blit(image, rect.topleft)
2017-12-20 18:56:58 -05:00
self.orientation = NS.E
2017-12-20 19:31:25 -05:00
elif edge == NS.S:
2017-12-14 03:49:02 -05:00
rect = self.image.get_rect()
rect.center = ds.get_width() / 2, NS.FRONT + NS.LENGTH - NS.LENGTH * NS.STEP - 20
ds.blit(self.image, rect.topleft)
2017-12-20 18:56:58 -05:00
self.orientation = NS.S
2017-12-20 19:31:25 -05:00
elif edge == NS.W:
2017-12-14 03:49:02 -05:00
image = rotate(self.image, 270)
rect = image.get_rect()
rect.center = ds.get_width() / 2 - NS.FRONT_WIDTH / 2 + 70, NS.FRONT + NS.LENGTH * NS.STEP + 10
ds.blit(image, rect.topleft)
2017-12-20 18:56:58 -05:00
self.orientation = NS.W
2017-12-20 19:31:25 -05:00
elif edge == NS.NW:
2017-12-14 03:49:02 -05:00
image = rotate(self.image, 315)
rect = image.get_rect()
rect.center = ds.get_width() / 2 + 45, NS.FRONT + NS.LENGTH * NS.STEP - 40
ds.blit(image, rect.topleft)
2017-12-20 18:56:58 -05:00
self.orientation = NS.NW
2017-12-20 19:31:25 -05:00
elif edge == NS.NE:
2017-12-14 03:49:02 -05:00
image = rotate(self.image, 45)
rect = image.get_rect()
rect.center = ds.get_width() / 2 - 30, NS.FRONT + NS.LENGTH * NS.STEP - 50
ds.blit(image, rect.topleft)
2017-12-20 18:56:58 -05:00
self.orientation = NS.NE
2017-12-14 03:49:02 -05:00
else:
self.orientation = None
class Life(GameChild):
SPACING = 30
MARGIN = 0
def __init__(self, parent):
GameChild.__init__(self, parent)
self.heart = load(self.get_resource("Heart.png")).convert_alpha()
def reset(self):
self.count = 3
def decrease(self):
if self.count > 0:
self.count -= 1
if self.count <= 0:
self.count = 0
self.get_game().finish_battle(False)
def update(self):
ds = self.get_display_surface()
dsr = ds.get_rect()
hr = self.heart.get_rect()
rect = Rect(0, 0, hr.w * self.count + self.SPACING * (self.count - 1), hr.h)
rect.midbottom = dsr.centerx, dsr.h - self.MARGIN
for x in xrange(rect.left, rect.right, hr.w + self.SPACING):
ds.blit(self.heart, (x, rect.top))
class Boss(RainbowSprite):
def __init__(self, parent):
RainbowSprite.__init__(self, parent, load("resource/Koolaid.png").convert_alpha(), 30)
self.health = Health(self)
self.sword = Sword(self)
self.register(self.brandish, self.cancel_flash)
self.add_frameset([0], name="normal")
def cancel_flash(self):
self.set_frameset("normal")
def reset(self):
self.unhide()
self.cancel_flash()
self.halt(self.cancel_flash)
self.health.reset()
self.halt(self.brandish)
self.sword.reset()
self.combo()
self.queue = None
self.brandish_complete = True
def combo(self):
self.queue = None
self.play(self.brandish, delay=2500, play_once=True)
def brandish(self):
self.queue = []
choices = range(6)
if self.health.amount > 90:
length = 1
elif self.health.amount > 70:
length = 2
elif self.health.amount > 40:
length = 3
else:
length = 4
while len(self.queue) < length:
while True:
orientation = randint(0, 5)
if not self.queue or orientation != self.queue[-1]:
self.queue.append(orientation)
break
self.unbrandished = copy(self.queue)
self.brandish_complete = False
self.sword.play(self.sword.brandish, play_once=True)
self.get_game().chemtrails.challenge()
def update(self):
RainbowSprite.update(self)
# self.get_display_surface().blit(self.image, (0, 0))
self.sword.update()
self.health.update()
class Sword(Sprite):
def __init__(self, parent):
Sprite.__init__(self, parent)
image = load(self.get_resource("Sword.png")).convert_alpha()
self.add_frame(image)
for angle in 270, 315, 45:
self.add_frame(rotate(image, angle))
self.add_frameset([0], name="vertical")
self.add_frameset([1], name="horizontal")
self.add_frameset([2], name="rdiagonal")
self.add_frameset([3], name="ldiagonal")
self.set_frameset("vertical")
self.location.center = self.get_display_surface().get_rect().center
self.register(self.brandish, self.lower)
def reset(self):
self.halt(self.brandish)
self.halt(self.lower)
self.hide()
def brandish(self):
self.unhide()
position = self.parent.unbrandished.pop(0)
dsr = self.get_display_surface().get_rect()
2017-12-20 18:56:58 -05:00
if position in (NS.W, NS.E):
2017-12-14 03:49:02 -05:00
self.set_frameset("vertical")
self.location.centery = dsr.centery - 100
2017-12-20 18:56:58 -05:00
if position == NS.W:
2017-12-14 03:49:02 -05:00
self.location.centerx = dsr.centerx - 100
else:
self.location.centerx = dsr.centerx + 100
2017-12-20 18:56:58 -05:00
elif position in (NS.N, NS.S):
2017-12-14 03:49:02 -05:00
self.set_frameset("horizontal")
self.location.centerx = dsr.centerx
2017-12-20 18:56:58 -05:00
if position == NS.N:
2017-12-14 03:49:02 -05:00
self.location.centery = dsr.centery - 200
else:
self.location.centery = dsr.centery
else:
2017-12-20 18:56:58 -05:00
if position == NS.NW:
2017-12-14 03:49:02 -05:00
self.set_frameset("ldiagonal")
else:
self.set_frameset("rdiagonal")
self.location.center = dsr.centerx, dsr.centery - 100
self.play(self.lower, delay=400, play_once=True)
if len(self.parent.unbrandished) > 0:
self.play(self.brandish, delay=600, play_once=True)
def lower(self):
self.hide()
if len(self.parent.unbrandished) == 0:
self.parent.brandish_complete = True
def update(self):
Sprite.update(self)
class Health(GameChild):
WIDTH = 200
HEIGHT = 32
COLOR = "yellow"
def __init__(self, parent):
GameChild.__init__(self, parent)
def reset(self):
self.amount = 100
def decrease(self, damage):
self.amount -= damage
if self.amount <= 0:
self.amount = 0
self.get_game().finish_battle(True)
else:
self.parent.play(self.parent.cancel_flash, delay=1000, play_once=True)
self.parent.set_frameset(0)
def update(self):
surface = Surface((int(self.WIDTH * (self.amount / 100.0)), self.HEIGHT))
surface.fill(Color(self.COLOR))
surface.set_alpha(255)
font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 14)
text = font.render("HEALTH", True, Color("black"))
# surface.blit(text, (8, 0))
self.get_display_surface().blit(surface, (0, 0))