# -*- coding: utf-8 -*- from random import randint, choice, random from math import pi from copy import copy from glob import iglob from os.path import basename, join from threading import Thread from serial import Serial, SerialException from serial.tools import list_ports from time import sleep from PIL import Image import pygame from pygame import Surface, Color, mixer from pygame.event import clear from pygame.mixer import Sound from pygame.image import load, fromstring from pygame.transform import rotate, flip, scale, smoothscale from pygame.time import get_ticks from pygame.font import Font from pygame.draw import aalines, lines from pygame.gfxdraw import aapolygon, arc, polygon, aaellipse, ellipse, filled_ellipse, filled_circle 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, render_box, get_hsla_color, get_hue_shifted_surface, get_color_swapped_surface ) from lib.pgfw.pgfw.gfx_extension import aa_filled_polygon class NS(Game, Animation): LNW, LNE, LSE, LSW = range(4) N, NE, E, NW, S, W = range(6) FRONT_WIDTH = 230 BACK_WIDTH = 500 LENGTH = 150 FRONT = 300 STEP = .4 IDLE_TIMEOUT = 60000 * 5 CHANNEL_COUNT = 8 NO_RESET_TIMEOUT = 3000 def __init__(self): pygame.mixer.pre_init(44100, -16, 2, 1024) Game.__init__(self) self.get_configuration().type_declarations.add_chart( { "time": { "int": ["timer-max-time", "timer-start-time", "timer-addition", "sword-delay", "attract-gif-length", "attract-board-length"] }, "input": { "bool": "serial" }, "display": { "float": "attract-gif-alpha" } }) Animation.__init__(self, self) self.subscribe(self.respond, KEYDOWN) self.subscribe(self.respond, KEYUP) self.subscribe(self.respond) for bgm in self.audio.bgm.values(): bgm.volume = 0.65 ds = self.get_display_surface() self.background = Surface(ds.get_size()) self.background.fill((0, 0, 0)) self.platform = Platform(self) self.tony = Tony(self) self.logo = Logo(self) self.title = Title(self) self.introduction = Introduction(self) self.ending = Ending(self) self.wipe = Wipe(self) self.dialogue = Dialogue(self) self.chemtrails = Chemtrails(self) self.boss = Boss(self) if self.serial_enabled(): self.serial_kill = False self.serial_data = 0 self.serial_reader = Serial(self.get_configuration("input", "arduino-port"), timeout=.3) self.reset_arduino() # for port in list_ports.comports(): # print port.device # print "---" # ports = list_ports.grep(self.get_configuration("input", "arduino-port")) # for port in ports: # print port.device self.serial_thread = Thread(target=self.read_serial) self.serial_thread.start() self.last_press = get_ticks() self.register(self.blink_score, interval=500) self.play(self.blink_score) self.reset() self.most_recent_time = None clear() def serial_enabled(self): return self.get_configuration("input", "serial") and not self.check_command_line("-no-serial") def read_serial(self): while not self.serial_kill: name = self.get_configuration("input", "arduino-port") try: transmission = self.serial_reader.readline().strip() print(transmission) except SerialException: print("Serial not ready... passing...") transmission = "" if len(transmission) == 4: try: self.serial_data = int(transmission, 2) except ValueError: print("Value error checking four digit serial transmission") self.handle_garbage(transmission) self.reset_arduino() self.idle_elapsed = 0 elif len(transmission) > 0: try: int(transmission, 2) except ValueError: print("Received a non-four digit serial transmission") self.handle_garbage(transmission) else: self.serial_data = 0 def handle_garbage(self, transmission): self.serial_data = 0 print("Garbage detected: %s" % transmission) self.serial_reader.reset_input_buffer() def reset_arduino(self): if self.serial_enabled(): self.serial_reader.dtr = False self.serial_reader.reset_input_buffer() self.serial_reader.dtr = True def end(self, evt): if evt.type == QUIT or self.delegate.compare(evt, "quit"): self.serial_kill = True Game.end(self, evt) def apply_serial(self): for ii, light in enumerate(self.platform.lights): light.pressed = bool(self.serial_data & (2 ** ii)) # reset idle timer is a light is detected as pressed in serial data if light.pressed: self.idle_elapsed = 0 def reset(self, leave_wipe_running=False): self.score_hidden = False self.idle_elapsed = 0 self.suppressing_input = False self.title.reset() if not leave_wipe_running: self.wipe.reset() self.introduction.reset() self.ending.reset() self.boss.reset() self.chemtrails.reset() self.platform.reset() self.dialogue.reset() self.no_reset_elapsed = 0 self.title.activate() def set_most_recent_time(self, score): self.most_recent_time = score def blink_score(self): self.score_hidden = not self.score_hidden 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 self.idle_elapsed = 0 if event.key in (K_UP, K_o): lights[NS.LNW].pressed = pressed 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 elif event.key == K_z: self.reset() elif event.key == K_a: self.reset_arduino() self.last_press = get_ticks() else: if self.get_delegate().compare(event, "reset-game"): self.reset() def update(self): Animation.update(self) last_frame_duration = self.time_filter.get_last_frame_duration() if self.serial_enabled(): self.apply_serial() if self.title.active or self.introduction.active or self.ending.active or \ self.dialogue.active: self.no_reset_elapsed += last_frame_duration # if we received good input, reset the auto reset timer if 0b11 <= self.serial_data <= 0b1100: self.no_reset_elapsed = 0 if self.no_reset_elapsed >= self.NO_RESET_TIMEOUT: print("auto arduino reset triggered") self.reset_arduino() self.no_reset_elapsed = 0 self.title.update() self.introduction.update() self.ending.update() self.boss.update() if not self.title.active: self.platform.update() self.chemtrails.update() self.boss.update_dialogue() self.wipe.update() self.idle_elapsed += self.time_filter.get_last_frame_duration() if self.idle_elapsed >= self.IDLE_TIMEOUT: self.reset() class Button(Sprite): MARGIN = 2 BLANK = (200, 200, 200) def __init__(self, parent, edge, size, border): Sprite.__init__(self, parent) colors = self.get_game().platform.get_color_pair_from_edge(edge) width = size * 2 + self.MARGIN + border * 4 step = width / 2 + self.MARGIN / 2 rect_width = width / 2 - self.MARGIN / 2 rects = Rect(0, 0, rect_width, rect_width), \ Rect(step, 0, rect_width, rect_width), \ Rect(step, step, rect_width, rect_width), \ Rect(0, step, rect_width, rect_width) if edge == NS.N: colored = rects[0], rects[1] elif edge == NS.NE: colored = rects[1], rects[3] elif edge == NS.E: colored = rects[1], rects[2] elif edge == NS.NW: colored = rects[0], rects[2] elif edge == NS.S: colored = rects[3], rects[2] elif edge == NS.W: colored = rects[0], rects[3] for lightness in range(30, 90, 5): frame = Surface((width, width), SRCALPHA) for topleft in (0, 0), (step, 0), (step, step), (0, step): rect = Rect(topleft, (rect_width, rect_width)) border_color = Color(*self.BLANK) border_color.a = 179 frame.fill(border_color, rect) frame.fill((0, 0, 0, 0), rect.inflate(-border * 2, -border * 2)) for ii in range(2): original_color = Color(*colors[ii]) original_color.a = 255 edited_color = Color(0, 0, 0) edited_color.hsla = int(original_color.hsla[0]), int(original_color.hsla[1]), \ lightness, 70 frame.fill(edited_color, colored[ii]) frame.fill(original_color, colored[ii].inflate(-border * 2, -border * 2)) self.add_frame(frame) class Meter(GameChild): SPACING = 12 def __init__(self, parent): GameChild.__init__(self, parent) def setup(self, background, rect, indent, color, units, path): self.background = background self.rect = rect self.icons = [] x = rect.left + indent base = get_color_swapped_surface( load(self.get_resource(path)).convert_alpha(), (0, 0, 0), color) while x <= self.rect.right - base.get_width() - self.SPACING: icon = Sprite(self) icon.add_frame(base) icon.location.midleft = x, self.rect.centery self.icons.append(icon) x += icon.location.w + self.SPACING self.units = units def reset(self): self.amount = self.units for icon in self.icons: icon.unhide() def change(self, delta): self.amount += delta cutoff = float(self.amount) / self.units * len(self.icons) for ii, icon in enumerate(self.icons): if ii < cutoff: icon.unhide() else: icon.hide() def update(self): ds = self.get_display_surface() ds.blit(self.background, self.rect) for icon in self.icons: icon.update() class Tony(Sprite): def __init__(self, parent): Sprite.__init__(self, parent, 100, False) self.board = Sprite(self, 100) self.board.load_from_path(self.get_resource("newTony/TonyArms"), True) self.effect = Sprite(self) dsr = self.get_display_surface().get_rect() for offset in range(12): w, h = dsr.w + 40, int(dsr.h * .65) glow = Surface((w, h), SRCALPHA) for ii, y in enumerate(range(h, 0, -8)): hue = range(200, 140, -5)[(ii - offset) % 12] alpha = min(100, int(round(y / float(h - 10) * 100))) color = get_hsla_color(hue, 100, 50, alpha) if ii == 0: aaellipse(glow, w // 2, y, w // 2 - 4, h // 20, color) ellipse(glow, w // 2, y, w // 2 - 4, h // 20, color) filled_ellipse(glow, w // 2, y, w // 2 - 4, h // 20, color) self.effect.add_frame(glow) self.effect.location.topleft = -20, int(dsr.h * .35) self.add_frame(load(self.get_resource("Big_Tony.png")).convert_alpha()) self.load_from_path(self.get_resource("newTony/TonyShirtHead"), True) self.add_frameset([0], name="static") self.add_frameset(range(1, len(self.frames)), name="board") self.taunts = [] for sfx_name in self.get_audio().sfx: if sfx_name.startswith("TonyTauntsBend_"): self.taunts.append(sfx_name) def set_frameset(self, name): Sprite.set_frameset(self, name) self.get_current_frameset().reset() self.set_framerate(100) if name == "board": self.board.get_current_frameset().reset() self.board.unhide() self.board.set_framerate(100) self.board.halt() elif name == "static": self.board.hide() def shift_frame(self): Sprite.shift_frame(self) frameset = self.get_current_frameset() if frameset.name == "board" and frameset.current_index == 1: self.get_audio().play_sfx(choice(self.taunts)) def update(self): save = self.get_display_surface() intermediate_surface = Surface(self.location.size, SRCALPHA) self.display_surface = intermediate_surface Sprite.update(self) self.display_surface = save self.effect.display_surface = intermediate_surface self.effect.update(flags=BLEND_RGBA_SUB) self.get_display_surface().blit(intermediate_surface, self.location.topleft) if self.get_game().title.active: self.get_game().platform.update() self.get_game().chemtrails.update() frameset = self.get_current_frameset() if frameset.name == "board": self.board.get_current_frameset().current_index = frameset.current_index if frameset.current_index == len(frameset.order) - 1: self.set_framerate(3000) else: self.set_framerate(100) self.board.update() class Video(Sprite): def __init__(self, parent, diameter, next_video_chance=.01): Sprite.__init__(self, parent, 100) self.next_video_chance = next_video_chance pattern = join(self.get_resource("gif"), "Boarding_*.gif") self.gifs = [] for path in iglob(pattern): self.gifs.append(Image.open(path)) print(self.gifs[-1].info) self.gif = self.gifs[1] self.mask = Surface([diameter] * 2, SRCALPHA) rect = self.mask.get_rect() alpha = int(self.get_configuration("display", "attract-gif-alpha") * 255) filled_circle(self.mask, rect.centerx, rect.centery, rect.centerx, (0, 0, 0, alpha)) filled_circle(self.mask, rect.centerx, rect.centery, rect.centerx - 2, (255, 255, 255, alpha)) self.add_frame(self.mask) self.play() def shift_frame(self): Sprite.shift_frame(self) if random() < self.next_video_chance: while True: selection = choice(self.gifs) if selection != self.gif: self.gif = selection break self.gif.seek((self.gif.tell() + 1) % self.gif.n_frames) frame = smoothscale( fromstring(self.gif.convert("RGBA").tobytes(), self.gif.size, "RGBA"), (self.mask.get_width(), int(self.gif.width * self.gif.height / self.mask.get_width()))) # frame = scale( # fromstring(self.gif.convert("RGBA").tobytes(), self.gif.size, "RGBA"), # (self.mask.get_width(), int(self.gif.width * self.gif.height / self.mask.get_width()))) copy = self.mask.copy() rect = frame.get_rect() rect.bottom = copy.get_rect().bottom copy.blit(frame, rect, None, BLEND_RGBA_MIN) # copy.blit(frame, rect) self.clear_frames() self.add_frame(copy) class Logo(Sprite): def __init__(self, parent): Sprite.__init__(self, parent) dsr = self.get_display_surface().get_rect() self.load_from_path(self.get_resource("Title_tile.png"), True) for y in range(0, dsr.h + self.location.h, self.location.h): for x in range(0, dsr.w + self.location.w, self.location.w): if x != 0 or y != 0: self.add_location((x, y)) self.effect = Sprite(self, 100) palette = (255, 255, 255), (255, 255, 128), (255, 255, 0) thickness = 8 for offset in range(len(palette)): frame = Surface(dsr.size) for x in range(0, dsr.w, thickness): frame.fill(palette[(offset + x) % len(palette)], (x, 0, thickness, dsr.h)) self.effect.add_frame(frame) def update(self): self.effect.update() # tiled background self.move(-2, 2) if self.location.right < 0: self.move(self.location.w) if self.location.top > 0: self.move(dy=-self.location.h) Sprite.update(self, flags=BLEND_RGBA_MIN) class Title(Animation): UNLOCK_MOVES = NS.NW, NS.N, NS.NE, NS.NW def __init__(self, parent): Animation.__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.angle = pi / 8 self.video = Video(self, 320) self.video.location.center = 329, 182 self.register(self.show_video, self.hide_video) self.show_video() def reset(self): self.unlock_index = 0 self.get_game().platform.set_glowing(self.get_game().platform.get_buttons_from_edges([self.UNLOCK_MOVES[0]])) self.halt() self.show_video() def activate(self): self.active = True platform = self.get_game().platform platform.activate() platform.set_glowing(platform.get_buttons_from_edges([self.UNLOCK_MOVES[self.unlock_index]])) self.get_game().chemtrails.activate() self.get_game().tony.set_frameset("static") self.get_audio().play_bgm("title") def deactivate(self): self.active = False self.halt() def start_game(self): self.deactivate() self.get_game().set_most_recent_time(None) self.get_game().boss.start_level(0) def draw_scores(self): step = 75 ds = self.get_display_surface() lines = map(int, open(self.get_resource("scores")).readlines()) entries = ["BEST"] + sorted(lines)[:9] for ii, entry in enumerate(entries): if ii == 0 or ii == 5: y = 30 font = Font(self.get_resource(Dialogue.FONT_PATH), 18) if ii > 0: text = self.get_formatted_time(entry) else: text = entry message = render_box(font, text, True, Color(255, 255, 255), Color(128, 128, 128), Color(0, 0, 0), padding=2) message.set_alpha(230) rect = message.get_rect() rect.top = y if ii < 5: rect.left = -1 else: rect.right = ds.get_width() + 1 if not entry == self.get_game().most_recent_time or not self.get_game().score_hidden: ds.blit(message, rect) y += step def get_formatted_time(self, entry): if int(entry) == 5999999: return "--:--.-" else: minutes, milliseconds = divmod(int(entry), 60000) seconds, fraction = divmod(milliseconds, 1000) return "%i:%02i.%i" % (minutes, seconds, fraction / 100) def show_video(self): self.video.unhide() self.play(self.hide_video, delay=self.get_configuration("time", "attract-gif-length"), play_once=True) self.get_game().tony.set_frameset("static") self.unlock_index = 0 self.get_game().platform.set_glowing(self.get_game().platform.get_buttons_from_edges([self.UNLOCK_MOVES[0]])) def hide_video(self): self.video.hide() self.play(self.show_video, delay=self.get_configuration("time", "attract-board-length"), play_once=True) self.get_game().tony.set_frameset("board") def update(self): ''' Move title, check button presses, and draw screen ''' Animation.update(self) if self.active: ds = self.get_display_surface() dsr = ds.get_rect() self.get_game().logo.update() # advance unlock pattern platform = self.get_game().platform if not self.get_game().wipe.is_playing() and platform.get_edge_pressed() == self.UNLOCK_MOVES[self.unlock_index]: # self.first_pressed = True # self.first_pressed_elapsed = 0 if self.unlock_index == len(self.UNLOCK_MOVES) - 1: platform.set_glowing([]) self.get_game().wipe.start(self.start_game) self.get_audio().play_sfx("confirm") else: self.unlock_index += 1 platform.set_glowing(platform.get_buttons_from_edges([self.UNLOCK_MOVES[self.unlock_index]])) self.get_audio().play_sfx("land_0") # reset unlock pattern if idle # if self.first_pressed: # self.first_pressed_elapsed += self.get_game().time_filter.get_last_frame_duration() # if self.first_pressed_elapsed > 1000 * 10: # self.reset() self.get_game().tony.update() # bounce the gif around the screen if self.video.location.right > dsr.right or self.video.location.left < dsr.left: self.angle = reflect_angle(self.angle, 0) if self.video.location.right > dsr.right: self.video.move(dsr.right - self.video.location.right) else: self.video.move(dsr.left - self.video.location.left) if self.video.location.bottom > dsr.bottom or self.video.location.top < dsr.top: self.angle = reflect_angle(self.angle, pi) if self.video.location.bottom > dsr.bottom: self.video.move(dy=dsr.bottom - self.video.location.bottom) else: self.video.move(dy=dsr.top - self.video.location.top) dx, dy = get_delta(self.angle, 5, False) self.video.move(dx, dy) if platform.get_pressed(): self.video.hide() self.get_game().tony.set_frameset("static") self.halt() self.play(self.show_video, delay=10000, play_once=True) self.video.update() self.draw_scores() 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) 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 self.speech_channel = None def reset(self): self.stop_speech() self.halt() self.deactivate() self.first_pressed = False self.first_press_elapsed = 0 def stop_speech(self): if self.speech_channel is not None: self.speech_channel.stop() self.speech_channel = None def deactivate(self): self.stop_speech() 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).convert_alpha()) 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.speech_channel = self.get_audio().play_sfx("talk", -1) self.play() def build_frame(self): self.text_index += 2 if self.text_index >= len(self.full_text): self.show_all() def show_all(self): self.stop_speech() 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).convert_alpha() 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" + \ "Before you play, show me you can scrape! Use your board to touch\n" + \ "the glowing pads on the platform!", \ # "Good job, lizard scum! Maybe now you're ready to take on Kool\n" + \ "Good job, slime bag! 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 def __init__(self, parent): Animation.__init__(self, parent) self.words = [] for word in "hey you lizard slime bag show me you can scrape".split(" "): font = Font(self.get_resource(Dialogue.FONT_PATH), 96) sprite = RainbowSprite(self, font.render(word, True, (255, 0, 0)).convert_alpha(), 30) self.words.append(sprite) 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, self.speak) 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.words_index = 0 self.advance_prompt.reset() self.skip_prompt.reset() for word in self.words: word.location.center = self.get_display_surface().get_rect().centerx, 100 word.hide() def deactivate(self): self.active = False def activate(self): self.active = True self.play(self.start, delay=3000, play_once=True) self.words[0].unhide() self.play(self.speak) # self.get_game().platform.unpress() def speak(self): for ii in range(self.words_index + 1): self.words[ii].move(0, 12) if ii == self.words_index and self.words[ii].location.bottom > self.get_display_surface().get_rect().bottom - 40: if self.words_index < len(self.words) - 1: self.words_index += 1 self.words[self.words_index].unhide() self.get_audio().play_sfx("talk") 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]) dialogue.show_text(self.TEXT[2]) self.text_index = 0 # temporary dialogue skip dialogue.set_name("Tony") 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() self.text_index = 2 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) self.get_audio().play_sfx("go") 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(0) 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 not wipe.is_playing() and self.text_index == 2: platform = self.get_game().platform if platform.get_edge_pressed() == self.TUTORIAL_MOVES[self.tutorial_index]: self.tutorial_index += 1 self.get_audio().play_sfx("land_0") if self.tutorial_index == len(self.TUTORIAL_MOVES): # self.text_index += 1 # self.advance_prompt.cancel_first_press() platform.set_glowing([]) self.start_wipe() # 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_game().tony.update() self.slime_bag.update() self.skateboard.update() for word in self.words: word.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 wipe.is_playing() and not self.text_index == 2: self.skip_prompt.update() class SkipPrompt(GameChild): def __init__(self, parent, callback): GameChild.__init__(self, parent) self.callback = callback self.buttons = [] self.pluses = [] top = 3 left = 3 for ii, edge in enumerate((NS.S, NS.NE, NS.W)): self.buttons.append(Button(self, edge, AdvancePrompt.BUTTON_SIZE, AdvancePrompt.BUTTON_BORDER)) self.buttons[-1].location.topleft = left, top if ii < 2: self.pluses.append(Sprite(self)) self.pluses[-1].load_from_path(self.get_resource("Plus.png"), True) self.pluses[-1].location.center = ( self.buttons[-1].location.right + AdvancePrompt.BUTTON_SPACING / 2, self.buttons[-1].location.centery) left += self.buttons[-1].location.width + AdvancePrompt.BUTTON_SPACING self.text = Sprite(self) font = Font(self.get_resource(Dialogue.FONT_PATH), 18) self.text.add_frame(font.render("TO SKIP", True, (0, 0, 0)).convert_alpha()) self.text.location.midleft = ( self.buttons[2].location.right + 5, self.buttons[2].location.centery) self.button_sound = self.get_audio().sfx["button"] def reset(self): self.press_index = 0 self.press_elapsed = 0 for button in self.buttons: button.unhide() for plus in self.pluses: plus.unhide() 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.button_sound.play() self.buttons[0].hide() self.pluses[0].hide() elif self.press_index == 1 and platform.get_edge_pressed() == NS.NE: self.press_index += 1 self.button_sound.play() self.buttons[1].hide() self.pluses[1].hide() elif self.press_index == 2 and platform.get_edge_pressed() == NS.W: self.callback() self.get_audio().play_sfx("confirm") elif self.press_index > 0: self.press_elapsed += self.get_game().time_filter.get_last_frame_duration() if self.press_elapsed > 4000: self.reset() for button in self.buttons: button.update() for plus in self.pluses: plus.update() self.text.update() class AdvancePrompt(GameChild): BUTTON_SIZE = 30 BUTTON_BORDER = 3 BUTTON_SPACING = 64 def __init__(self, parent): GameChild.__init__(self, parent) dsr = self.get_display_surface().get_rect() self.buttons = Button(self, NS.N, self.BUTTON_SIZE, self.BUTTON_BORDER), \ Button(self, NS.NW, self.BUTTON_SIZE, self.BUTTON_BORDER) self.plus = Sprite(self) self.plus.load_from_path(self.get_resource("Plus.png"), True) dsr = self.get_display_surface().get_rect() self.plus.location.center = dsr.centerx, dsr.centery + 70 self.buttons[1].location.center = self.plus.location.move(self.BUTTON_SPACING, 0).center self.buttons[0].location.center = self.plus.location.move(-self.BUTTON_SPACING, 0).center self.background_rect = Rect( self.buttons[0].location.topleft, (self.buttons[1].location.right - self.buttons[0].location.left, self.buttons[0].location.height)) self.background_rect.inflate_ip((10, 10)) def reset(self): self.cancel_first_press() for button in self.buttons: button.unhide() self.plus.unhide() def cancel_first_press(self): self.first_pressed = False self.first_pressed_elapsed = 0 self.buttons[0].unhide() self.plus.unhide() 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.buttons[0].hide() self.plus.hide() self.get_audio().play_sfx("button") def check_second_press(self): pressed = self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.NW if pressed: self.get_audio().play_sfx("confirm") return pressed def update(self): if self.first_pressed: self.first_pressed_elapsed += self.get_game().time_filter.get_last_frame_duration() self.get_display_surface().fill((255, 255, 255), self.background_rect) for button in self.buttons: button.update() self.plus.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) self.sound = self.get_audio().sfx["wipe"] 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() self.sound.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 range(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) dsr = self.get_display_surface().get_rect() self.border = Sprite(self) self.border.load_from_path(self.get_resource("DancePadClear.png"), True) self.border.location.midbottom = dsr.centerx, dsr.bottom 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 get_steps_from_edge(self, edge): if edge == NS.N: return NS.NE, NS.NW elif edge == NS.NE: return NS.N, NS.E, NS.S, NS.W elif edge == NS.E: return NS.NE, NS.NW elif edge == NS.NW: return NS.N, NS.E, NS.S, NS.W elif edge == NS.S: return NS.NE, NS.NW elif edge == NS.W: return NS.NE, NS.NW def get_right_angles_from_edge(self, edge): if edge == NS.N: return NS.E, NS.W elif edge == NS.NE: return None elif edge == NS.E: return NS.N, NS.S elif edge == NS.NW: return None elif edge == NS.S: return NS.E, NS.W elif edge == NS.W: return NS.N, NS.S def get_opposite_of_edge(self, edge): if edge == NS.N: return NS.S elif edge == NS.NE: return NS.NW elif edge == NS.E: return NS.W elif edge == NS.NW: return NS.NE elif edge == NS.S: return NS.N elif edge == NS.W: return NS.E def get_color_pair_from_edge(self, edge): if edge == NS.N: return self.lights[NS.LNW].color, self.lights[NS.LNE].color elif edge == NS.NE: return self.lights[NS.LNE].color, self.lights[NS.LSW].color elif edge == NS.E: return self.lights[NS.LNE].color, self.lights[NS.LSE].color elif edge == NS.NW: return self.lights[NS.LNW].color, self.lights[NS.LSE].color elif edge == NS.S: return self.lights[NS.LSW].color, self.lights[NS.LSE].color elif edge == NS.W: return self.lights[NS.LNW].color, self.lights[NS.LSW].color 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() # self.border.update() for light in self.lights: light.draw_glow() class Light(Animation): MAX_GLOW_INDEX = 25 TITLE_OFFSET = 0 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().title.active: boss = self.get_game().boss chemtrails = self.get_game().chemtrails if boss.queue and boss.brandish_complete and not self.is_playing(self.glow) \ and self.in_orientation(boss.queue[chemtrails.queue_index]): self.play(self.glow) elif self.is_playing(self.glow) and (not boss.queue or not self.in_orientation(boss.queue[chemtrails.queue_index])): self.reset() if not self.hidden: ds = self.get_display_surface() aa_filled_polygon(ds, self.get_points(), self.color) def get_points(self): if self.get_game().title.active: points = [] for point in self.points: points.append((point[0], point[1] - self.TITLE_OFFSET)) return points else: return self.points def draw_glow(self): for ii, y in enumerate(range(0, self.glow_index, 3)): shifted = [] for point in self.get_points(): shifted.append((point[0], point[1] - y)) # ratio = 1 - float(y + 1) / (self.MAX_GLOW_INDEX + 1) # alpha = int(ratio * 255) # color = Color(self.color.r, self.color.g, self.color.b, alpha) # ds = self.get_display_surface() # intermediate = Surface(ds.get_size(), SRCALPHA) if self.position == NS.LSW: saturation = 0 else: saturation = int((self.color.hsla[1] + 80) % 100) if not ii % 2: lightness = 0 else: lightness = 40 lines( self.get_display_surface(), get_hsla_color( int(self.color.hsla[0]), saturation, lightness ), True, shifted, 3 ) # ds.blit(intermediate, (0, 0)) 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(Sprite): def __init__(self, parent): Sprite.__init__(self, parent) self.load_from_path(self.get_resource("littleSlimeGoop"), True) for direction in (NS.N, NS.NE, NS.E, NS.NW, NS.S, NS.W): self.add_frameset([direction], switch=(direction == NS.N)) self.life = Life(self) self.boys = Boys(self) self.timer = Timer(self) def reset(self): self.deactivate() self.life.reset() self.boys.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() Sprite.update(self) if not self.get_game().title.active: boss = self.get_game().boss if boss.queue: self.timer.tick() self.attack() if self.timer.amount < 0: self.life.decrease() if not boss.is_playing(boss.show_end_dialogue, include_delay=True): self.timer.reset() boss.combo() if not boss.is_playing(boss.show_introduction_dialogue, include_delay=True): self.timer.update() self.life.update() # self.boys.update() def attack(self): boss = self.get_game().boss queue = boss.queue if self.orientation == queue[self.queue_index]: self.timer.add_time(self.get_configuration("time", "timer-addition")) if boss.level_index == 0: boss.health.decrease(4) elif boss.level_index == 1: boss.health.decrease(4) elif boss.level_index == 2: boss.health.decrease(4) self.queue_index += 1 boss.last_attack = self.orientation boss.sword.block() if self.queue_index == len(queue): self.timer.reset() if not boss.is_playing(boss.show_end_dialogue, include_delay=True): boss.combo() self.get_audio().play_sfx("complete_pattern_3") else: self.get_audio().play_sfx("land_0") self.get_game().platform.reset_lights() def orient(self): ds = self.get_display_surface() edge = self.get_game().platform.get_edge_pressed() dy = -Light.TITLE_OFFSET if self.get_game().title.active else 0 if edge is not None: self.set_frameset(edge + 1) self.unhide() else: self.hide() if edge == NS.N: self.location.center = ds.get_width() / 2, NS.FRONT + 15 + dy self.orientation = NS.N elif edge == NS.E: self.location.center = ds.get_width() / 2 + NS.FRONT_WIDTH / 2 - 115, \ NS.FRONT + NS.LENGTH * NS.STEP - 75 + dy self.orientation = NS.E elif edge == NS.S: self.location.center = ds.get_width() / 2, \ NS.FRONT + NS.LENGTH - NS.LENGTH * NS.STEP - 110 + dy self.orientation = NS.S elif edge == NS.W: self.location.center = ds.get_width() / 2 - NS.FRONT_WIDTH / 2 + 100, \ NS.FRONT + NS.LENGTH * NS.STEP - 85 + dy self.orientation = NS.W elif edge == NS.NW: self.location.center = ds.get_width() / 2 + 5, \ NS.FRONT + NS.LENGTH * NS.STEP - 75 + dy self.orientation = NS.NW elif edge == NS.NE: self.location.center = ds.get_width() / 2 + 10, \ NS.FRONT + NS.LENGTH * NS.STEP - 80 + dy self.orientation = NS.NE else: self.orientation = None class Timer(Meter): def __init__(self, parent): Meter.__init__(self, parent) dsr = self.get_display_surface().get_rect() background = load(self.get_resource("HUD_timer.png")).convert() rect = background.get_rect() rect.bottomright = dsr.right - 6, dsr.bottom - 4 self.setup(background, rect, 53, (0, 0, 255), self.get_configuration("time", "timer-start-time"), "scrapeIcons/scrapeIcons_07.png") def add_time(self, amount): self.change(amount) def tick(self): self.change(-self.get_game().time_filter.get_last_frame_duration()) class Life(Meter): def __init__(self, parent): Meter.__init__(self, parent) dsr = self.get_display_surface().get_rect() background = load(self.get_resource("HUD_health.png")).convert() rect = background.get_rect() rect.bottomleft = 172, dsr.bottom - 4 self.setup(background, rect, 70, (255, 0, 0), 3, "scrapeIcons/scrapeIcons_03.png") def decrease(self): self.get_audio().play_sfx("hurt") self.change(-1) if self.amount <= 0: self.amount = 0 self.parent.boys.change(-1) self.get_game().boss.finish_battle(False) class Boys(Meter): def __init__(self, parent): Meter.__init__(self, parent) dsr = self.get_display_surface().get_rect() background = load(self.get_resource("HUD_lives.png")).convert() rect = background.get_rect() rect.bottomleft = 6, dsr.bottom - 4 self.setup(background, rect, 60, (0, 255, 0), 3, "scrapeIcons/scrapeIcons_01.png") class Boss(Animation): def __init__(self, parent): Animation.__init__(self, parent) self.kool_man = RainbowSprite(self, load(self.get_resource("Kool_man_waah.png")).convert_alpha(), 30) self.visitor = RainbowSprite(self, load(self.get_resource("Visitor.png")).convert_alpha(), 30) self.spoopy = RainbowSprite(self, load(self.get_resource("Spoopy.png")).convert_alpha(), 30) for sprite in self.kool_man, self.visitor, self.spoopy: sprite.location.topleft = 100, 0 self.health = Health(self) self.sword = Sword(self) self.register(self.brandish, self.cancel_flash, self.show_introduction_dialogue, self.show_end_dialogue, self.end_dialogue) self.kool_man.add_frameset([0], name="normal", switch=True) self.visitor.add_frameset([0], name="normal", switch=True) self.spoopy.add_frameset([0], name="normal", switch=True) self.kool_man_avatar = load(self.get_resource("Kool_man_avatar.png")).convert() self.visitor_avatar = load(self.get_resource("Visitor_avatar.png")).convert() self.spoopy_avatar = load(self.get_resource("Spoopy_avatar.png")).convert() self.advance_prompt = AdvancePrompt(self) self.backgrounds = [Sprite(self), Sprite(self), Sprite(self)] self.backgrounds[0].load_from_path(self.get_resource("bg/bg001.png")) self.backgrounds[1].load_from_path(self.get_resource("bg/bg002.png")) self.backgrounds[2].load_from_path(self.get_resource("bg/bg003.png")) self.countdown = Countdown(self) def cancel_flash(self): if self.level_index == 0: self.kool_man.set_frameset("normal") elif self.level_index == 1: self.visitor.set_frameset("normal") elif self.level_index == 2: self.spoopy.set_frameset("normal") def start_level(self, index): self.level_index = index self.battle_finished = False self.player_defeated = False self.health.reset() self.get_game().chemtrails.timer.reset() self.get_game().chemtrails.life.reset() self.activate() dialogue = self.get_game().dialogue dialogue.deactivate() if index == 0: dialogue.set_avatar(self.kool_man_avatar) dialogue.set_name("Kool Man") self.kool_man.unhide() self.kool_man.set_frameset("normal") elif index == 1: dialogue.set_avatar(self.visitor_avatar) dialogue.set_name("Visitor") self.visitor.unhide() self.visitor.set_frameset("normal") elif index == 2: dialogue.set_avatar(self.spoopy_avatar) dialogue.set_name("Spoopy") self.spoopy.unhide() self.spoopy.set_frameset("normal") self.get_audio().play_bgm(f"level_{index}") self.play(self.show_introduction_dialogue, delay=3000, play_once=True) self.get_game().platform.activate() self.get_game().chemtrails.activate() self.last_attack = NS.NW def show_introduction_dialogue(self): dialogue = self.get_game().dialogue dialogue.activate() if self.level_index == 0: dialogue.show_text("You'll never be able to block my sword, you lizard slime!" + " See\nif you can keep up with these moves!") elif self.level_index == 1: dialogue.show_text("We're just warming up, slime breath! Prepare to get spun" + " by\nthese combos!") elif self.level_index == 2: dialogue.show_text("Lizard! My moves are so unpredictable you might as well" + " give\nup now!") self.play(self.end_dialogue, delay=5000, play_once=True) def reset(self): self.level_index = 0 self.kills = 0 self.time_elapsed = 0 self.deactivate() self.cancel_flash() self.halt(self.cancel_flash) self.health.reset() self.halt(self.brandish) self.sword.reset() self.advance_prompt.reset() self.queue = None self.brandish_complete = True self.countdown.reset() self.halt(self.end_dialogue) def deactivate(self): self.active = False def activate(self): self.active = True def combo(self, delay=2500): self.queue = None if self.get_game().serial_enabled(): self.get_game().reset_arduino() self.play(self.brandish, delay=delay, play_once=True) def brandish(self): self.queue = [] platform = self.get_game().platform if self.level_index == 0: if self.health.amount > 90: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first] elif self.health.amount > 70: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, choice(platform.get_steps_from_edge(first))] elif self.health.amount > 30: choices = [0] if self.last_attack in (NS.NE, NS.NW): choices.append(1) else: choices.extend((2, 3)) result = choice(choices) if result == 0: first = choice(platform.get_steps_from_edge(self.last_attack)) second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, first, second] elif result == 1: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, choice(platform.get_steps_from_edge(first)), choice(platform.get_right_angles_from_edge(first))] elif result == 2: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, choice(platform.get_steps_from_edge(first)), platform.get_opposite_of_edge(first)] elif result == 3: first = choice(platform.get_steps_from_edge(self.last_attack)) second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, choice(platform.get_right_angles_from_edge(second))] else: choices = [0, 1] if self.last_attack in (NS.NE, NS.NW): choices.extend((2, 3, 4)) else: choices.append(5) result = choice(choices) if result == 0 or result == 1: first = choice(platform.get_steps_from_edge(self.last_attack)) second = choice(platform.get_steps_from_edge(first)) last = second if result else platform.get_opposite_of_edge(second) self.queue = [first, second, platform.get_opposite_of_edge(first), last] elif result == 2: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, choice(platform.get_right_angles_from_edge(first)), platform.get_opposite_of_edge(first)] elif result == 3: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, choice(platform.get_steps_from_edge(first)), choice(platform.get_right_angles_from_edge(first)), platform.get_opposite_of_edge(first)] elif result == 4: first = choice(platform.get_steps_from_edge(self.last_attack)) second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, choice(platform.get_right_angles_from_edge(first)), platform.get_opposite_of_edge(second)] elif result == 5: first = choice(platform.get_steps_from_edge(self.last_attack)) second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, platform.get_opposite_of_edge(first), choice(platform.get_right_angles_from_edge(second))] elif self.level_index == 1: if self.health.amount > 85: if self.last_attack in (NS.NE, NS.NW): choices = 1, 2 else: choices = 0, result = choice(choices) if result == 0: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, platform.get_opposite_of_edge(first)] elif result == 1: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, choice(platform.get_right_angles_from_edge(first)), platform.get_opposite_of_edge(first)] elif result == 2: first = choice(platform.get_steps_from_edge(self.last_attack)) self.queue = [first, platform.get_opposite_of_edge(first)] elif self.health.amount > 60: if self.last_attack in (NS.NE, NS.NW): choices = 2, 3 else: choices = 0, 1 result = choice(choices) first = choice(platform.get_steps_from_edge(self.last_attack)) if result == 0: second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, platform.get_opposite_of_edge(second)] elif result == 1: second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, choice(platform.get_right_angles_from_edge(second)), platform.get_opposite_of_edge(first)] elif result == 2: second = platform.get_opposite_of_edge(first) self.queue = [first, second, choice(platform.get_right_angles_from_edge(second))] elif result == 3: second = choice(platform.get_right_angles_from_edge(first)) self.queue = [first, second, platform.get_opposite_of_edge(first), platform.get_opposite_of_edge(second)] elif self.health.amount > 30: result = choice(range(3)) if result == 0: first = self.choose_new_edge((NS.N, NS.E, NS.S, NS.W)) self.queue = [first, choice(platform.get_steps_from_edge(first)), platform.get_opposite_of_edge(first), first] elif result == 1: first = self.choose_new_edge((NS.NE, NS.NW)) second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, platform.get_opposite_of_edge(second), choice(platform.get_right_angles_from_edge(second))] elif result == 2: first = self.choose_new_edge((NS.NE, NS.NW)) second = choice(platform.get_steps_from_edge(first)) self.queue = [first, second, choice(platform.get_right_angles_from_edge(second)), platform.get_opposite_of_edge(second)] else: result = choice(range(4)) if result == 0: first = self.choose_new_edge((NS.NE, NS.NW)) second = platform.get_opposite_of_edge(first) self.queue = [first, second, first, second] elif result == 1: first = self.choose_new_edge((NS.N, NS.E, NS.S, NS.W)) self.queue = [first, platform.get_opposite_of_edge(first), first] elif result == 2: first = self.choose_new_edge((NS.N, NS.E, NS.S, NS.W)) self.queue = [first, choice(platform.get_steps_from_edge(first)), choice(platform.get_right_angles_from_edge(first)), platform.get_opposite_of_edge(first), first] elif result == 3: first = self.choose_new_edge((NS.N, NS.E, NS.S, NS.W)) second = platform.get_opposite_of_edge(first) third = choice(platform.get_right_angles_from_edge(first)) self.queue = [first, second, third, platform.get_opposite_of_edge(second), platform.get_opposite_of_edge(third)] elif self.level_index == 2: if self.health.amount > 90: length = 3 elif self.health.amount > 70: length = 4 elif self.health.amount > 40: length = 5 else: length = 6 while len(self.queue) < length: while True: orientation = randint(0, 5) if (not self.queue and orientation != self.last_attack) or \ (len(self.queue) > 0 and orientation != self.queue[-1]): self.queue.append(orientation) break self.unbrandished = copy(self.queue) self.brandish_complete = False self.sword.reset() self.sword.play(self.sword.brandish, play_once=True) self.get_game().chemtrails.challenge() def choose_new_edge(self, edges): while True: edge = choice(edges) if edge != self.last_attack: return edge def finish_battle(self, win): self.battle_finished = True self.halt(self.brandish) self.halt(self.cancel_flash) self.sword.reset() self.queue = [] self.brandish_complete = True if win: if self.level_index == 0: self.kool_man.set_frameset(0) elif self.level_index == 1: self.visitor.set_frameset(0) elif self.level_index == 2: self.spoopy.set_frameset(0) self.add_score() self.player_defeated = not win self.kills += not win self.play(self.show_end_dialogue, delay=3000, play_once=True) def add_score(self): self.get_game().set_most_recent_time(self.time_elapsed) open(self.get_resource("scores"), "a").write(str(self.time_elapsed) + "\n") def show_end_dialogue(self): dialogue = self.get_game().dialogue dialogue.activate() if self.level_index == 0: if self.player_defeated: dialogue.show_text("Maybe next time!") else: dialogue.show_text("Hey! Wow! Lizard!") elif self.level_index == 1: if self.player_defeated: dialogue.show_text("Wiped out!") else: dialogue.show_text("Well done! But it's not over yet!") elif self.level_index == 2: if self.player_defeated: dialogue.show_text("Just like I thought!") else: dialogue.show_text("H-how? But you're only a lizard! How could you" + " manage to defeat\nall of us?") if self.player_defeated: self.countdown.activate() else: self.play(self.end_dialogue, delay=5000, play_once=True) def transition_to_battle(self): index = self.level_index + (not self.player_defeated) if self.kills >= 3: self.get_game().reset(True) elif index < 3: self.start_level(index) else: game = self.get_game() game.boss.reset() game.chemtrails.reset() game.platform.reset() game.ending.activate() def transition_to_title(self): self.get_game().reset(True) def damage(self): if self.level_index == 0: self.kool_man.set_frameset(0) elif self.level_index == 1: self.visitor.set_frameset(0) elif self.level_index == 2: self.spoopy.set_frameset(0) def end_dialogue(self): self.get_game().dialogue.deactivate() if not self.battle_finished: self.combo(delay=1300) else: self.get_game().wipe.start(self.transition_to_battle) def update(self): if self.active: self.backgrounds[self.level_index].update() dialogue = self.get_game().dialogue if self.countdown.active and dialogue.active and self.get_game().chemtrails.boys.amount > 0: if self.advance_prompt.check_first_press(): self.advance_prompt.press_first() elif self.advance_prompt.check_second_press(): self.countdown.deactivate() if dialogue.is_playing(): dialogue.show_all() else: self.get_game().dialogue.deactivate() if not self.battle_finished: self.combo() else: self.get_game().wipe.start(self.transition_to_battle) self.advance_prompt.cancel_first_press() else: self.time_elapsed += self.get_game().time_filter.get_last_frame_duration() Animation.update(self) if self.level_index == 0: self.kool_man.update() elif self.level_index == 1: self.visitor.update() elif self.level_index == 2: self.spoopy.update() self.sword.update() self.health.update() self.countdown.update() def update_dialogue(self): if self.active: dialogue = self.get_game().dialogue if dialogue.active: self.get_game().dialogue.update() if self.countdown.active: self.advance_prompt.update() class Countdown(GameChild): def __init__(self, parent): GameChild.__init__(self, parent) dsr = self.get_display_surface().get_rect() font = Font(self.get_resource(Dialogue.FONT_PATH), 76) self.heading = Sprite(self) self.heading.add_frame(font.render("CONTINUE?", True, (0, 0, 0), (255, 255, 255)).convert_alpha()) self.heading.location.midtop = dsr.centerx, 50 self.game_over = Sprite(self) self.game_over.add_frame(font.render("GAME OVER", True, (0, 0, 0), (255, 255, 255)).convert_alpha()) self.game_over.location.center = dsr.centerx, dsr.centery - 40 self.glyphs = [] for ii in range(10): glyph = Sprite(self) frame = Surface((140, 140)) frame.fill((255, 255, 255)) digits = font.render("%i" % ii, True, (0, 0, 0), (255, 255, 255)).convert_alpha() rect = digits.get_rect() rect.center = frame.get_rect().center frame.blit(digits, rect) glyph.add_frame(frame) glyph.location.center = dsr.centerx, dsr.centery - 30 self.glyphs.append(glyph) def reset(self): self.deactivate() def deactivate(self): self.active = False def activate(self): self.remaining = 9999 self.active = True def end_game(self): self.get_game().reset(True) def update(self): if self.active: if self.get_game().chemtrails.boys.amount > 0: self.heading.update() self.glyphs[int(self.remaining / 1000)].update() else: self.game_over.update() if not self.get_game().wipe.is_playing(): if self.remaining <= 0: self.get_game().wipe.start(self.end_game) self.remaining = 0 else: self.remaining -= self.get_game().time_filter.get_last_frame_duration() class Sword(Animation): SHIFT = 15 SPRITE_COUNT = 6 def __init__(self, parent): Animation.__init__(self, parent) swords = self.swords = [] for root in "Sword_kool_man/", "Sword_visitor/", "Sword_spoopy/": swords.append([[], [], [], [], [], []]) for path in sorted(iglob(join(self.get_resource(root), "*.png"))): base = load(self.get_resource(path)).convert_alpha() for position in range(6): if position == NS.N or position == NS.S: rotated = rotate(base, 270) elif position == NS.NW: rotated = rotate(base, 45) elif position == NS.NE: rotated = rotate(base, 310) else: rotated = base surface = rotated.copy() colors = self.get_game().platform.get_color_pair_from_edge(position) color_a = Color(colors[0].r, colors[0].g, colors[0].b, 255) color_b = Color(colors[1].r, colors[1].g, colors[1].b, 255) rect = surface.get_rect() if position == NS.N or position == NS.S: surface.fill(color_a, (0, 0, rect.w / 2, rect.h), BLEND_RGBA_MIN) surface.fill(color_b, (rect.centerx, 0, rect.w / 2, rect.h), BLEND_RGBA_MIN) else: surface.fill(color_a, (0, 0, rect.w, rect.h / 2), BLEND_RGBA_MIN) surface.fill(color_b, (0, rect.centery, rect.w, rect.h / 2), BLEND_RGBA_MIN) swords[-1][position].append(surface) masks = self.masks = [] for alpha in range(16, 255, 16): surface = Surface((300, 300), SRCALPHA) surface.fill((255, 255, 255, alpha)) masks.append(surface) self.register(self.brandish, self.lower) def reset(self): self.halt(self.brandish) self.halt(self.lower) self.next_index = 0 self.sprites = [] def brandish(self): position = self.parent.unbrandished.pop(0) offset = -self.SHIFT for ii, queued in enumerate(self.parent.queue): offset += self.SHIFT * (queued == position) if len(self.parent.unbrandished) == len(self.parent.queue) - ii - 1: break dsr = self.get_display_surface().get_rect() sprite = Sprite(self) for frame in self.swords[self.parent.level_index][position]: sprite.add_frame(frame) if position in (NS.W, NS.E): sprite.location.centery = dsr.centery - 100 + offset if position == NS.W: sprite.location.centerx = dsr.centerx - 100 - offset else: sprite.location.centerx = dsr.centerx + 100 - offset elif position in (NS.N, NS.S): sprite.location.centerx = dsr.centerx - offset if position == NS.N: sprite.location.centery = dsr.centery - 170 + offset else: sprite.location.centery = dsr.centery + offset else: sprite.location.center = dsr.centerx - offset, dsr.centery - 100 self.sprites.append(sprite) self.get_audio().play_sfx("brandish") self.play(self.lower, delay=400, play_once=True) if len(self.parent.unbrandished) > 0: self.play(self.brandish, delay=self.get_configuration("time", "sword-delay"), play_once=True) def lower(self): if len(self.parent.unbrandished) == 0: self.parent.brandish_complete = True def block(self): if len(self.sprites): self.sprites.pop(0) def update(self): Animation.update(self) for ii, sprite in enumerate(reversed(self.sprites)): if ii == len(self.sprites) - 1: substitute = None else: mask = self.masks[int(float(ii + 1) / len(self.sprites) * len(self.masks))] frame = sprite.get_current_frame() copy = frame.copy() copy.blit(mask, (0, 0), None, BLEND_RGBA_MIN) substitute = copy sprite.update(substitute=substitute) class Health(Meter): OFFSET = 4 def __init__(self, parent): Meter.__init__(self, parent) dsr = self.get_display_surface().get_rect() self.background = load(self.get_resource("HUD_boss.png")).convert() self.rect = self.background.get_rect() self.rect.midtop = dsr.centerx, self.OFFSET def setup(self): level_index = self.get_game().boss.level_index if level_index == 0: icon_index = 22 elif level_index == 1: icon_index = 17 elif level_index == 2: icon_index = 19 Meter.setup(self, self.background, self.rect, 52, (255, 0, 255), 100, "scrapeIcons/scrapeIcons_%i.png" % icon_index) def reset(self): self.setup() Meter.reset(self) def decrease(self, damage): self.change(-damage) self.parent.damage() if self.amount <= 0: self.amount = 0 self.get_audio().play_sfx("complete_pattern_1") self.get_audio().play_sfx("defeat") self.get_game().boss.finish_battle(True) else: self.parent.play(self.parent.cancel_flash, delay=1000, play_once=True) class Ending(Animation): TEXT = "Wow! You vanquished all the goons and skated like a pro, slime bag.\n" + \ "You made your father proud today. I love you, child.", def __init__(self, parent): Animation.__init__(self, parent) 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().centerx, 300 self.tony_avatar = load(self.get_resource("Introduction_tony_avatar.png")).convert() self.advance_prompt = AdvancePrompt(self) self.register(self.start, self.start_wipe) def reset(self): self.deactivate() self.slime_bag.unhide() self.halt() self.text_index = 0 self.advance_prompt.reset() self.angle = choice((pi / 4, 3 * pi / 4, 5 * pi / 4, 7 * pi / 4)) def deactivate(self): self.active = False def activate(self): self.active = True self.play(self.start, delay=3000, play_once=True) font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 64) time = self.get_game().title.get_formatted_time(self.get_game().most_recent_time) foreground = font.render(time, False, (180, 150, 20), (255, 255, 255)).convert_alpha() dsr = self.get_display_surface().get_rect() self.text = RainbowSprite(self, foreground, 180, 200) self.text.location.midtop = dsr.centerx, 80 self.get_game().tony.set_frameset("static") dialogue = self.get_game().dialogue dialogue.activate() dialogue.set_avatar(self.tony_avatar) dialogue.set_name("???") dialogue.show_text("") self.play(self.start_wipe, delay=20000, play_once=True) self.get_audio().play_bgm("end") 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("Tony") dialogue.show_text(self.TEXT[0]) self.text_index = 0 def end_game(self): self.deactivate() self.get_game().reset(True) def start_wipe(self): self.get_game().wipe.start(self.end_game) 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: # pass # else: # self.start_wipe() # self.advance_prompt.cancel_first_press() self.get_game().logo.update() self.get_game().tony.update() self.slime_bag.update() dsr = self.get_display_surface().get_rect() if self.text.location.right > dsr.right or self.text.location.left < dsr.left: self.angle = reflect_angle(self.angle, 0) if self.text.location.right > dsr.right: self.text.move(dsr.right - self.text.location.right) else: self.text.move(dsr.left - self.text.location.left) if self.text.location.bottom > self.get_game().dialogue.avatar_box.location.top \ or self.text.location.top < dsr.top: self.angle = reflect_angle(self.angle, pi) if self.text.location.top < dsr.top: self.text.move(dy=dsr.top - self.text.location.top) else: self.text.move( dy=self.get_game().dialogue.avatar_box.location.top - \ self.text.location.bottom) dx, dy = get_delta(self.angle, 5, False) self.text.move(dx, dy) self.text.update() self.get_game().dialogue.update() # if not wipe.is_playing() and not self.is_playing(self.start): # self.advance_prompt.update()