846 lines
30 KiB
Python
846 lines
30 KiB
Python
from random import randint, choice
|
|
from math import pi
|
|
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, get_delta, reflect_angle
|
|
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)
|
|
N, E, S, W, NE, NW = range(6)
|
|
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))
|
|
self.title = Title(self)
|
|
self.introduction = Introduction(self)
|
|
self.wipe = Wipe(self)
|
|
self.platform = Platform(self)
|
|
self.dialogue = Dialogue(self)
|
|
self.chemtrails = Chemtrails(self)
|
|
self.boss = Boss(self)
|
|
self.last_press = get_ticks()
|
|
self.register(self.unsuppress_restart)
|
|
self.reset()
|
|
|
|
def reset(self):
|
|
self.suppressing_input = False
|
|
self.suppress_restart = False
|
|
self.game_over = False
|
|
self.title.reset()
|
|
self.wipe.reset()
|
|
self.introduction.reset()
|
|
self.boss.reset()
|
|
self.chemtrails.reset()
|
|
self.platform.reset()
|
|
self.dialogue.reset()
|
|
|
|
def suppress_input(self):
|
|
self.suppressing_input = True
|
|
self.platform.unpress()
|
|
|
|
def unsuppress_input(self):
|
|
self.suppressing_input = False
|
|
|
|
def respond(self, event):
|
|
if not self.suppressing_input and event.type in (KEYDOWN, KEYUP):
|
|
# if self.last_press <= get_ticks() - int(self.get_configuration("input", "buffer")):
|
|
pressed = True if event.type == KEYDOWN else False
|
|
lights = self.platform.lights
|
|
if event.key in (K_UP, K_o):
|
|
lights[0].pressed = pressed
|
|
if self.game_over and not self.suppress_restart:
|
|
self.reset()
|
|
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
|
|
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.title.update()
|
|
self.introduction.update()
|
|
self.boss.update()
|
|
self.platform.update()
|
|
self.chemtrails.update()
|
|
if self.game_over:
|
|
self.message.update()
|
|
if not self.suppress_restart:
|
|
self.restart_message.update()
|
|
self.wipe.update()
|
|
|
|
|
|
class Title(GameChild):
|
|
|
|
def __init__(self, parent):
|
|
GameChild.__init__(self, parent)
|
|
self.plank = Sprite(self)
|
|
self.plank.load_from_path(self.get_resource("Title_plank.png"), True)
|
|
ds = self.get_display_surface()
|
|
dsr = ds.get_rect()
|
|
self.plank.location.center = dsr.center
|
|
self.slime_bag = Sprite(self)
|
|
self.slime_bag.load_from_path(self.get_resource("Title_slime_bag.png"), True)
|
|
self.slime_bag.location.bottomleft = dsr.bottomleft
|
|
image = load(self.get_resource("Title_border.png")).convert()
|
|
image.set_colorkey((0, 0, 0))
|
|
self.border = RainbowSprite(self, image, 30)
|
|
self.border.location.center = dsr.centerx, dsr.bottom - 100
|
|
self.text = Sprite(self)
|
|
self.text.load_from_path(self.get_resource("Title_text.png"), True, False, (255, 0, 0))
|
|
self.text.load_from_path(self.get_resource("Title_text_half.png"), True, False, (255, 0, 0))
|
|
self.text.add_frameset([0], name="full", switch=True)
|
|
self.text.add_frameset([1], name="half")
|
|
self.text.location.center = dsr.centerx, dsr.bottom - 100
|
|
self.angle = choice((pi / 4, 3 * pi / 4, 5 * pi / 4, 7 * pi / 4))
|
|
|
|
def reset(self):
|
|
self.activate()
|
|
self.first_pressed = False
|
|
self.first_pressed_elapsed = 0
|
|
self.text.set_frameset("full")
|
|
|
|
def activate(self):
|
|
self.active = True
|
|
|
|
def deactivate(self):
|
|
self.active = False
|
|
|
|
def activate_introduction(self):
|
|
self.deactivate()
|
|
self.get_game().introduction.activate()
|
|
|
|
def update(self):
|
|
if self.active:
|
|
ds = self.get_display_surface()
|
|
ds.fill((255, 255, 255))
|
|
dsr = ds.get_rect()
|
|
if self.plank.location.right > dsr.right or self.plank.location.left < dsr.left:
|
|
self.angle = reflect_angle(self.angle, 0)
|
|
if self.plank.location.right > dsr.right:
|
|
self.plank.move(dsr.right - self.plank.location.right)
|
|
else:
|
|
self.plank.move(dsr.left - self.plank.location.left)
|
|
if self.plank.location.bottom > dsr.bottom or self.plank.location.top < dsr.top:
|
|
self.angle = reflect_angle(self.angle, pi)
|
|
if self.plank.location.bottom > dsr.bottom:
|
|
self.plank.move(dy=dsr.bottom - self.plank.location.bottom)
|
|
else:
|
|
self.plank.move(dy=dsr.top - self.plank.location.top)
|
|
dx, dy = get_delta(self.angle, 2, False)
|
|
self.plank.move(dx, dy)
|
|
self.plank.update()
|
|
self.slime_bag.update()
|
|
wipe = self.get_game().wipe
|
|
if not self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.N:
|
|
self.first_pressed = True
|
|
self.first_pressed_elapsed = 0
|
|
self.text.set_frameset("half")
|
|
elif not wipe.is_playing() and self.first_pressed and \
|
|
self.get_game().platform.get_edge_pressed() == NS.NW:
|
|
wipe.start(self.activate_introduction)
|
|
elif self.first_pressed:
|
|
self.first_pressed_elapsed += self.get_game().time_filter.get_last_frame_duration()
|
|
if self.first_pressed_elapsed > 4000:
|
|
self.first_pressed = False
|
|
self.first_pressed_elapsed = 0
|
|
self.text.set_frameset("full")
|
|
self.border.update()
|
|
self.text.update()
|
|
|
|
|
|
class Dialogue(Animation):
|
|
|
|
BACKGROUND = 255, 255, 255
|
|
BORDER = 0, 0, 0
|
|
TEXT_COLOR = 0, 0, 0
|
|
FONT_PATH = "rounded-mplus-1m-bold.ttf"
|
|
FONT_SIZE = 18
|
|
|
|
def __init__(self, parent):
|
|
Animation.__init__(self, parent, interval=70)
|
|
ds = self.get_display_surface()
|
|
dsr = ds.get_rect()
|
|
frame = Surface((640, 72))
|
|
frame.fill(self.BORDER)
|
|
frame.fill(self.BACKGROUND, (1, 1, frame.get_width() - 2, frame.get_height() - 2))
|
|
self.text_box = Sprite(self)
|
|
self.text_box.add_frame(frame)
|
|
self.text_box.location.bottomleft = dsr.bottomleft
|
|
frame = Surface((66, 66))
|
|
frame.fill(self.BORDER)
|
|
frame.fill(self.BACKGROUND, (1, 1, frame.get_width() - 2, frame.get_height() - 2))
|
|
self.avatar_box = Sprite(self)
|
|
self.avatar_box.add_frame(frame)
|
|
self.avatar_box.location.bottomleft = self.text_box.location.topleft
|
|
frame = Surface((128, 24))
|
|
frame.fill(self.BORDER)
|
|
frame.fill(self.BACKGROUND, (1, 1, frame.get_width() - 2, frame.get_height() - 2))
|
|
self.name_box = Sprite(self)
|
|
self.name_box.add_frame(frame)
|
|
self.name_box.location.bottomleft = self.avatar_box.location.bottomright
|
|
|
|
def reset(self):
|
|
self.halt()
|
|
self.deactivate()
|
|
self.first_pressed = False
|
|
self.first_press_elapsed = 0
|
|
|
|
def deactivate(self):
|
|
self.active = False
|
|
|
|
def activate(self):
|
|
self.active = True
|
|
|
|
def set_avatar(self, image):
|
|
self.avatar = Sprite(self)
|
|
self.avatar.add_frame(image)
|
|
self.avatar.location.center = self.avatar_box.location.center
|
|
|
|
def set_name(self, text):
|
|
font = Font(self.get_resource(self.FONT_PATH), self.FONT_SIZE)
|
|
self.name = Sprite(self)
|
|
self.name.add_frame(font.render(text, True, self.TEXT_COLOR))
|
|
self.name.location.midleft = self.name_box.location.left + 5, self.name_box.location.centery
|
|
|
|
def show_text(self, text):
|
|
self.full_text = text
|
|
self.text_index = 0
|
|
self.play()
|
|
|
|
def build_frame(self):
|
|
self.text_index += 1
|
|
if self.text_index == len(self.full_text):
|
|
self.halt()
|
|
|
|
def show_all(self):
|
|
self.text_index = len(self.full_text)
|
|
self.halt()
|
|
|
|
def update(self):
|
|
if self.active:
|
|
Animation.update(self)
|
|
self.avatar_box.update()
|
|
self.avatar.update()
|
|
self.name_box.update()
|
|
self.name.update()
|
|
self.text_box.update()
|
|
font = Font(self.get_resource(self.FONT_PATH), self.FONT_SIZE)
|
|
message = Sprite(self)
|
|
lines = self.full_text[:self.text_index].split("\n")
|
|
frame = Surface((self.text_box.location.w - 10, 30 * len(lines)), SRCALPHA)
|
|
for ii, line in enumerate(lines):
|
|
surface = font.render(line, True, self.TEXT_COLOR)
|
|
frame.blit(surface, (0, 30 * ii))
|
|
message.add_frame(frame)
|
|
message.location.topleft = self.text_box.location.left + 9, self.text_box.location.top + 8
|
|
message.update()
|
|
|
|
|
|
class Introduction(Animation):
|
|
|
|
TEXT = "Hey, you lizard slime bag. It's me Giant Tony. " + \
|
|
"Do you think you\ncan skate like me? Prove it!", \
|
|
"I'll even give you my board for this adventure. And ink my name\n" + \
|
|
"on it. Now the power of Giant Tony pulses through you."
|
|
|
|
def __init__(self, parent):
|
|
Animation.__init__(self, parent)
|
|
self.tony = load(self.get_resource("Big_Tony.png")).convert()
|
|
self.skateboard = Sprite(self)
|
|
self.skateboard.load_from_path(self.get_resource("Introduction_skateboard.png"), True)
|
|
self.skateboard.hide()
|
|
self.slime_bag = Sprite(self)
|
|
self.slime_bag.load_from_path(self.get_resource("Introduction_slime_bag.png"), True)
|
|
self.slime_bag.location.center = self.get_display_surface().get_rect().center
|
|
self.tony_avatar = load(self.get_resource("Introduction_tony_avatar.png")).convert()
|
|
self.advance_prompt = Sprite(self)
|
|
self.advance_prompt.load_from_path(self.get_resource("Dialogue_buttons_full.png"), True,
|
|
False, (255, 255, 255))
|
|
self.advance_prompt.load_from_path(self.get_resource("Dialogue_buttons_half.png"), True,
|
|
False, (255, 255, 255))
|
|
self.advance_prompt.add_frameset([0], name="full", switch=True)
|
|
self.advance_prompt.add_frameset([1], name="half")
|
|
dsr = self.get_display_surface().get_rect()
|
|
self.advance_prompt.location.bottomright = dsr.right - 3, dsr.bottom - 3
|
|
self.register(self.start)
|
|
|
|
def reset(self):
|
|
self.deactivate()
|
|
self.cancel_first_press()
|
|
|
|
def deactivate(self):
|
|
self.active = False
|
|
|
|
def activate(self):
|
|
self.active = True
|
|
self.play(self.start, delay=3000, play_once=True)
|
|
|
|
def start(self):
|
|
self.cancel_first_press()
|
|
dialogue = self.get_game().dialogue
|
|
dialogue.activate()
|
|
dialogue.set_avatar(self.tony_avatar)
|
|
dialogue.set_name("???")
|
|
dialogue.show_text(self.TEXT[0])
|
|
self.text_index = 0
|
|
|
|
def cancel_first_press(self):
|
|
self.first_pressed = False
|
|
self.first_pressed_elapsed = 0
|
|
self.advance_prompt.set_frameset("full")
|
|
|
|
def update(self):
|
|
if self.active:
|
|
Animation.update(self)
|
|
dialogue = self.get_game().dialogue
|
|
if not self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.N:
|
|
self.first_pressed = True
|
|
self.advance_prompt.set_frameset("half")
|
|
elif self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.NW:
|
|
if dialogue.is_playing():
|
|
dialogue.show_all()
|
|
else:
|
|
self.text_index += 1
|
|
if self.text_index == 1:
|
|
dialogue.set_name("Tony")
|
|
dialogue.show_text(self.TEXT[self.text_index])
|
|
self.get_game().platform.unpress()
|
|
self.cancel_first_press()
|
|
elif self.first_pressed:
|
|
self.first_pressed_elapsed += self.get_game().time_filter.get_last_frame_duration()
|
|
if self.first_pressed_elapsed > 4000:
|
|
self.cancel_first_press()
|
|
self.get_display_surface().blit(self.tony, (0, 0))
|
|
self.slime_bag.update()
|
|
self.get_game().dialogue.update()
|
|
if not self.is_playing(self.start) and not dialogue.is_playing():
|
|
self.advance_prompt.update()
|
|
|
|
|
|
class Wipe(Animation):
|
|
|
|
BLIND_COUNT = 4
|
|
SPEED = 6
|
|
TRANSPARENT_COLOR = 255, 0, 0
|
|
|
|
def __init__(self, parent):
|
|
Animation.__init__(self, parent)
|
|
self.image = load(self.get_resource("Ink.png")).convert()
|
|
self.image.set_colorkey(self.TRANSPARENT_COLOR)
|
|
|
|
def reset(self):
|
|
self.deactivate()
|
|
self.halt()
|
|
|
|
def deactivate(self):
|
|
self.active = False
|
|
|
|
def activate(self):
|
|
self.active = True
|
|
|
|
def start(self, callback):
|
|
self.activate()
|
|
self.up = True
|
|
self.get_game().suppress_input()
|
|
self.blind_height = self.get_display_surface().get_height() / self.BLIND_COUNT
|
|
self.callback = callback
|
|
self.play()
|
|
|
|
def build_frame(self):
|
|
if self.up:
|
|
self.blind_height -= self.SPEED
|
|
if self.blind_height <= 0:
|
|
self.up = False
|
|
self.callback()
|
|
else:
|
|
self.blind_height += self.SPEED
|
|
if self.blind_height >= self.get_display_surface().get_height() / self.BLIND_COUNT:
|
|
self.halt()
|
|
self.deactivate()
|
|
self.get_game().unsuppress_input()
|
|
|
|
def update(self):
|
|
if self.active:
|
|
Animation.update(self)
|
|
ds = self.get_display_surface()
|
|
dsr = ds.get_rect()
|
|
frame = self.image.copy()
|
|
for y in xrange(0, dsr.h, dsr.h / self.BLIND_COUNT):
|
|
if self.up:
|
|
frame.fill(self.TRANSPARENT_COLOR, (0, y, dsr.w, self.blind_height))
|
|
else:
|
|
frame.fill(self.TRANSPARENT_COLOR,
|
|
(0, y + dsr.h / self.BLIND_COUNT - self.blind_height, dsr.w, self.blind_height))
|
|
ds.blit(frame, (0, 0))
|
|
|
|
|
|
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):
|
|
self.deactivate()
|
|
for light in self.lights:
|
|
light.reset()
|
|
|
|
def deactivate(self):
|
|
self.active = False
|
|
|
|
def activate(self):
|
|
self.active = True
|
|
|
|
def unpress(self):
|
|
for light in self.lights:
|
|
light.pressed = False
|
|
|
|
def get_pressed(self):
|
|
return [light.position for light in self.lights if light.pressed]
|
|
|
|
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
|
|
|
|
def update(self):
|
|
if self.active:
|
|
for light in self.lights:
|
|
light.update()
|
|
|
|
|
|
|
|
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)
|
|
self.reset_timer()
|
|
|
|
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:
|
|
return orientation in (NS.N, NS.NW, NS.W)
|
|
elif self.position == NS.NE:
|
|
return orientation in (NS.N, NS.NE, NS.E)
|
|
elif self.position == NS.SE:
|
|
return orientation in (NS.NW, NS.E, NS.S)
|
|
elif self.position == NS.SW:
|
|
return orientation in (NS.S, NS.NE, NS.W)
|
|
|
|
|
|
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.deactivate()
|
|
self.life.reset()
|
|
self.timer_remaining = self.TIME_LIMIT
|
|
|
|
def deactivate(self):
|
|
self.active = False
|
|
|
|
def activate(self):
|
|
self.active = True
|
|
|
|
def challenge(self):
|
|
self.timer_remaining = self.TIME_LIMIT
|
|
self.queue_index = 0
|
|
|
|
def update(self):
|
|
if self.active:
|
|
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()
|
|
self.get_game().platform.reset()
|
|
|
|
def orient(self):
|
|
ds = self.get_display_surface()
|
|
edge = self.get_game().platform.get_edge_pressed()
|
|
if edge == NS.N:
|
|
rect = self.image.get_rect()
|
|
rect.center = ds.get_width() / 2, NS.FRONT - 30
|
|
ds.blit(self.image, rect.topleft)
|
|
self.orientation = NS.N
|
|
elif edge == NS.E:
|
|
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)
|
|
self.orientation = NS.E
|
|
elif edge == NS.S:
|
|
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)
|
|
self.orientation = NS.S
|
|
elif edge == NS.W:
|
|
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)
|
|
self.orientation = NS.W
|
|
elif edge == NS.NW:
|
|
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)
|
|
self.orientation = NS.NW
|
|
elif edge == NS.NE:
|
|
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)
|
|
self.orientation = NS.NE
|
|
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.deactivate()
|
|
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 deactivate(self):
|
|
self.active = False
|
|
|
|
def activate(self):
|
|
self.active = 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):
|
|
if self.active:
|
|
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()
|
|
if position in (NS.W, NS.E):
|
|
self.set_frameset("vertical")
|
|
self.location.centery = dsr.centery - 100
|
|
if position == NS.W:
|
|
self.location.centerx = dsr.centerx - 100
|
|
else:
|
|
self.location.centerx = dsr.centerx + 100
|
|
elif position in (NS.N, NS.S):
|
|
self.set_frameset("horizontal")
|
|
self.location.centerx = dsr.centerx
|
|
if position == NS.N:
|
|
self.location.centery = dsr.centery - 200
|
|
else:
|
|
self.location.centery = dsr.centery
|
|
else:
|
|
if position == NS.NW:
|
|
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))
|