scrapeboard/NS.py

1138 lines
42 KiB
Python

# -*- coding: utf-8 -*-
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, flip
from pygame.time import get_ticks
from pygame.font import Font
from pygame.gfxdraw import aapolygon
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, 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):
LNW, LNE, LSE, LSW = 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[NS.LNW].pressed = pressed
if self.game_over and not self.suppress_restart:
self.reset()
elif event.key in (K_RIGHT, K_p):
lights[NS.LNE].pressed = pressed
elif event.key in (K_DOWN, K_SEMICOLON):
lights[NS.LSE].pressed = pressed
elif event.key in (K_LEFT, K_l):
lights[NS.LSW].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()
if not self.introduction.active:
self.platform.update()
self.chemtrails.update()
self.boss.update_dialogue()
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.", \
"Before you go, show me you can scrape! Use your board to touch\n" + \
"the glowing pads on the platform!", \
"Great job, lizard scum! Maybe now you're ready to take on Kool\n" + \
"Man and his friends. Don't let me down!"
SKATEBOARD_START = -30, -20
TUTORIAL_MOVES = NS.S, NS.NE, NS.N, NS.E, NS.NW, NS.W, NS.E, NS.W
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.slime_bag = Sprite(self)
self.slime_bag.load_from_path(self.get_resource("Introduction_slime_bag.png"), True)
self.slime_bag.load_from_path(self.get_resource("Introduction_slime_bag_board.png"), True)
self.slime_bag.add_frameset([0], name="standing", switch=True)
self.slime_bag.add_frameset([1], name="board")
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 = AdvancePrompt(self)
self.skip_prompt = SkipPrompt(self, self.start_wipe)
self.register(self.start, self.move_board, self.take_board)
def reset(self):
self.deactivate()
self.slime_bag.set_frameset("standing")
self.slime_bag.unhide()
self.halt()
self.skateboard.hide()
self.text_index = 0
self.tutorial_index = 0
self.advance_prompt.reset()
self.skip_prompt.reset()
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.advance_prompt.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 give_board(self):
self.skateboard.location.center = self.SKATEBOARD_START
self.skateboard_step = get_step(self.skateboard.location.center, self.slime_bag.location.center, 2)
self.skateboard.unhide()
self.play(self.move_board)
def move_board(self):
self.skateboard.move(*self.skateboard_step)
if self.skateboard.location.colliderect(self.slime_bag.location.inflate(-30, -30)):
self.halt(self.move_board)
self.play(self.take_board, delay=2000, play_once=True)
def take_board(self):
self.skateboard.hide()
self.slime_bag.set_frameset("board")
def activate_boss(self):
self.deactivate()
self.get_game().boss.start_level()
def start_wipe(self):
self.get_game().wipe.start(self.activate_boss)
def update(self):
if self.active:
Animation.update(self)
dialogue = self.get_game().dialogue
wipe = self.get_game().wipe
if not wipe.is_playing() and not self.is_playing(self.start) and not self.text_index == 2:
if self.advance_prompt.check_first_press():
self.advance_prompt.press_first()
elif self.advance_prompt.check_second_press():
if dialogue.is_playing():
dialogue.show_all()
else:
if self.text_index < len(self.TEXT) - 1:
self.text_index += 1
if self.text_index == 1:
dialogue.set_name("Tony")
self.give_board()
elif self.text_index == 2:
self.slime_bag.hide()
self.halt(self.move_board)
self.take_board()
platform = self.get_game().platform
platform.activate()
platform.set_glowing(platform.get_buttons_from_edges(
[self.TUTORIAL_MOVES[self.tutorial_index]]))
self.get_game().chemtrails.activate()
dialogue.show_text(self.TEXT[self.text_index])
else:
self.start_wipe()
self.get_game().platform.unpress()
self.advance_prompt.cancel_first_press()
elif self.text_index == 2:
platform = self.get_game().platform
if platform.get_edge_pressed() == self.TUTORIAL_MOVES[self.tutorial_index]:
self.tutorial_index += 1
if self.tutorial_index == len(self.TUTORIAL_MOVES):
self.text_index += 1
self.advance_prompt.cancel_first_press()
platform.set_glowing([])
dialogue.show_text(self.TEXT[self.text_index])
else:
platform.set_glowing(platform.get_buttons_from_edges(
[self.TUTORIAL_MOVES[self.tutorial_index]]))
self.get_display_surface().blit(self.tony, (0, 0))
self.slime_bag.update()
self.skateboard.update()
self.get_game().platform.update()
self.get_game().dialogue.update()
if not wipe.is_playing() and not self.is_playing(self.start) and \
not self.text_index == 2:
self.advance_prompt.update()
if not self.text_index == 2:
self.skip_prompt.update()
class SkipPrompt(Sprite):
def __init__(self, parent, callback):
Sprite.__init__(self, parent)
self.callback = callback
for ii in xrange(3):
self.load_from_path(self.get_resource("Skip_%i.png" % ii), True)
self.add_frameset([ii])
def reset(self):
self.press_index = 0
self.press_elapsed = 0
self.set_frameset(1)
def update(self):
platform = self.get_game().platform
if self.press_index == 0 and platform.get_edge_pressed() == NS.S:
self.press_index += 1
self.set_frameset(2)
elif self.press_index == 1 and platform.get_edge_pressed() == NS.NE:
self.press_index += 1
self.set_frameset(3)
elif self.press_index == 2 and platform.get_edge_pressed() == NS.W:
self.callback()
elif self.press_index > 0:
self.press_elapsed += self.get_game().time_filter.get_last_frame_duration()
if self.press_elapsed > 4000:
self.reset()
Sprite.update(self)
class AdvancePrompt(Sprite):
def __init__(self, parent):
Sprite.__init__(self, parent)
self.load_from_path(self.get_resource("Dialogue_buttons_full.png"), True,
False, (255, 255, 255))
self.load_from_path(self.get_resource("Dialogue_buttons_half.png"), True,
False, (255, 255, 255))
self.add_frameset([0], name="full", switch=True)
self.add_frameset([1], name="half")
dsr = self.get_display_surface().get_rect()
self.location.bottomright = dsr.right - 3, dsr.bottom - 3
def reset(self):
self.cancel_first_press()
def cancel_first_press(self):
self.first_pressed = False
self.first_pressed_elapsed = 0
self.set_frameset("full")
def check_first_press(self):
return not self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.N
def press_first(self):
self.first_pressed = True
self.set_frameset("half")
def check_second_press(self):
return self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.NW
def update(self):
if 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()
Sprite.update(self)
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.LNW),
Light(self, "magenta", NS.LNE),
Light(self, "yellow", NS.LSE),
Light(self, "white", NS.LSW)
]
def reset(self):
self.deactivate()
self.reset_lights()
def reset_lights(self):
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.LNW in pressed and NS.LNE in pressed:
return NS.N
elif NS.LNE in pressed and NS.LSW in pressed:
return NS.NE
elif NS.LNE in pressed and NS.LSE in pressed:
return NS.E
elif NS.LNW in pressed and NS.LSE in pressed:
return NS.NW
elif NS.LSE in pressed and NS.LSW in pressed:
return NS.S
elif NS.LSW in pressed and NS.LNW in pressed:
return NS.W
def get_buttons_from_edges(self, edges):
buttons = set()
for edge in edges:
if edge == NS.N:
buttons = buttons.union((NS.LNW, NS.LNE))
elif edge == NS.NE:
buttons = buttons.union((NS.LNE, NS.LSW))
elif edge == NS.E:
buttons = buttons.union((NS.LNE, NS.LSE))
elif edge == NS.NW:
buttons = buttons.union((NS.LNW, NS.LSE))
elif edge == NS.S:
buttons = buttons.union((NS.LSE, NS.LSW))
elif edge == NS.W:
buttons = buttons.union((NS.LSW, NS.LNW))
return list(buttons)
def set_glowing(self, selected):
for ii, light in enumerate(self.lights):
light.glow_index = 0
light.halt(light.glow)
if ii in selected:
light.play(light.glow)
def update(self):
if self.active:
for light in self.lights:
light.update()
class Light(Animation):
MAX_GLOW_INDEX = 16
INTRODUCTION_OFFSET = 80
def __init__(self, parent, color, position):
Animation.__init__(self, parent)
self.color = Color(color)
self.color.a = 225
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.LNW:
self.points = frontleft, frontmid, mid, midleft
elif self.position == NS.LNE:
self.points = frontmid, frontright, midright, mid
elif self.position == NS.LSE:
self.points = mid, midright, backright, backmid
elif self.position == NS.LSW:
self.points = midleft, mid, backmid, backleft
self.register(self.blink, interval=300)
self.register(self.glow)
def reset(self):
self.hidden = False
self.halt(self.blink)
self.halt(self.glow)
self.reset_timer()
self.glow_index = 0
def blink(self):
self.hidden = not self.hidden
def glow(self):
self.glow_index += 1
if self.glow_index > self.MAX_GLOW_INDEX:
self.glow_index = 0
def update(self):
Animation.update(self)
if not self.get_game().introduction.active:
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()
points = self.points
else:
points = []
for point in self.points:
points.append((point[0], point[1] - self.INTRODUCTION_OFFSET))
if not self.hidden:
ds = self.get_display_surface()
aa_filled_polygon(ds, points, self.color)
for ii in reversed(xrange(self.glow_index)):
shifted = []
for point in points:
shifted.append((point[0], point[1] - 1 * (ii + 1)))
alpha = (1 - float(ii + 1) / (self.MAX_GLOW_INDEX + 1)) * 255
color = Color(self.color.r, self.color.g, self.color.b, int(alpha))
aapolygon(ds, shifted, color)
def in_orientation(self, orientation):
if self.position == NS.LNW:
return orientation in (NS.N, NS.NW, NS.W)
elif self.position == NS.LNE:
return orientation in (NS.N, NS.NE, NS.E)
elif self.position == NS.LSE:
return orientation in (NS.NW, NS.E, NS.S)
elif self.position == NS.LSW:
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)
self.timer = Timer(self)
def reset(self):
self.deactivate()
self.life.reset()
self.timer.reset()
def deactivate(self):
self.active = False
def activate(self):
self.active = True
def challenge(self):
self.timer.reset()
self.queue_index = 0
def update(self):
if self.active:
self.orient()
if not self.get_game().introduction.active:
if self.get_game().boss.queue:
self.timer.tick()
self.attack()
if self.timer.time_remaining < 0:
self.life.decrease()
if not self.get_game().game_over:
self.timer.reset()
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.timer.update()
self.life.update()
def attack(self):
boss = self.get_game().boss
queue = boss.queue
if self.orientation == queue[self.queue_index]:
self.timer.add_time(self.TIME_ADDITION)
boss.health.decrease(5)
self.queue_index += 1
if self.queue_index == len(queue):
self.timer.reset()
self.get_game().boss.combo()
self.get_game().platform.reset_lights()
def orient(self):
ds = self.get_display_surface()
edge = self.get_game().platform.get_edge_pressed()
dy = -Light.INTRODUCTION_OFFSET if self.get_game().introduction.active else 0
if edge == NS.N:
rect = self.image.get_rect()
rect.center = ds.get_width() / 2, NS.FRONT - 30 + dy
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 + dy
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 + dy
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 + dy
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 + dy
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 + dy
ds.blit(image, rect.topleft)
self.orientation = NS.NE
else:
self.orientation = None
class Timer(GameChild):
TEXT = u"\u25F7"
BAR_POSITION = 448, 11
MAX_TIME = 12000
START_TIME = 8000
def __init__(self, parent):
GameChild.__init__(self, parent)
self.background = Sprite(self)
image = load(self.get_resource("HUD_background.png")).convert_alpha()
image = flip(image, True, False)
self.background.add_frame(image)
self.background.location.topright = self.get_display_surface().get_rect().topright
self.bar = load(self.get_resource("HUD_bar.png")).convert_alpha()
self.bar = flip(self.bar, True, False)
self.label = Sprite(self)
self.label.load_from_path(self.get_resource("Timer_label.png"), True)
dsr = self.get_display_surface().get_rect()
self.label.location.topright = dsr.right - 2, 5
def reset(self):
self.time_remaining = self.START_TIME
def add_time(self, amount):
self.time_remaining += amount
def tick(self):
self.time_remaining -= self.get_game().time_filter.get_last_frame_duration()
def update(self):
self.background.update()
self.label.update()
if self.time_remaining > 5500:
color = 0, 255, 0
elif self.time_remaining > 3000:
color = Color("orange")
else:
color = Color("red")
mask = Surface(self.bar.get_size())
mask.fill((128, 128, 128))
width = min(mask.get_width(), mask.get_width() * self.time_remaining / float(self.MAX_TIME))
mask.fill(color, (mask.get_width() - width, 0, width, mask.get_height()))
surface = self.bar.copy()
surface.blit(mask, (0, 0), None, BLEND_RGBA_MIN)
self.get_display_surface().blit(surface, self.BAR_POSITION)
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/Kool_man.png").convert_alpha(), 30)
self.health = Health(self)
self.sword = Sword(self)
self.register(self.brandish, self.cancel_flash, self.show_dialogue)
self.add_frameset([0], name="normal")
self.kool_man_avatar = load(self.get_resource("resource/Kool_man_avatar.png")).convert()
self.advance_prompt = AdvancePrompt(self)
def cancel_flash(self):
self.set_frameset("normal")
def start_level(self):
self.activate()
dialogue = self.get_game().dialogue
dialogue.deactivate()
dialogue.set_avatar(self.kool_man_avatar)
dialogue.set_name("Kool Man")
self.play(self.show_dialogue, delay=3000, play_once=True)
self.get_game().platform.activate()
self.get_game().chemtrails.activate()
def show_dialogue(self):
dialogue = self.get_game().dialogue
dialogue.activate()
dialogue.show_text("You'll never be able to block my sword, you lizard slime!" +
" See if you\ncan keep up with these moves!")
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.advance_prompt.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:
self.get_display_surface().fill((0, 0, 0))
dialogue = self.get_game().dialogue
if dialogue.active:
if self.advance_prompt.check_first_press():
self.advance_prompt.press_first()
elif self.advance_prompt.check_second_press():
if dialogue.is_playing():
dialogue.show_all()
else:
self.get_game().dialogue.deactivate()
self.combo()
self.advance_prompt.cancel_first_press()
RainbowSprite.update(self)
# self.get_display_surface().blit(self.image, (0, 0))
self.sword.update()
self.health.update()
def update_dialogue(self):
if self.active:
dialogue = self.get_game().dialogue
if dialogue.active:
self.get_game().dialogue.update()
self.advance_prompt.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):
TEXT = "HP"
BAR_POSITION = 23, 11
BACKGROUND_ALPHA = 125
def __init__(self, parent):
GameChild.__init__(self, parent)
self.background = Sprite(self)
self.background.load_from_path(self.get_resource("HUD_background.png"), True)
self.label = Sprite(self)
font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 24)
text = font.render(self.TEXT, True, Color("white"))
self.label.add_frame(text)
self.label.location.topleft = 2, -2
self.bar = load(self.get_resource("HUD_bar.png")).convert_alpha()
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):
self.background.update()
self.label.update()
if self.amount > 50:
color = 0, 255, 0
elif self.amount > 25:
color = Color("orange")
else:
color = Color("red")
mask = Surface(self.bar.get_size(), SRCALPHA)
mask.fill((128, 128, 128))
mask.fill(color, (0, 0, int(mask.get_width() * (self.amount / 100.0)),
mask.get_height()))
surface = self.bar.copy()
surface.blit(mask, (0, 0), None, BLEND_RGBA_MIN)
self.get_display_surface().blit(surface, self.BAR_POSITION)