diff --git a/NS.py b/NS.py index f3b1ac6..6886582 100644 --- a/NS.py +++ b/NS.py @@ -8,13 +8,14 @@ from pygame.image import load from pygame.transform import rotate 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_relative, get_delta, reflect_angle +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): @@ -43,7 +44,7 @@ class SoundEffect(GameChild, Sound): class NS(Game, Animation): - NW, NE, SE, SW = range(4) + LNW, LNE, LSE, LSW = range(4) N, E, S, W, NE, NW = range(6) FRONT_WIDTH = 230 BACK_WIDTH = 500 @@ -96,15 +97,15 @@ class NS(Game, Animation): pressed = True if event.type == KEYDOWN else False lights = self.platform.lights if event.key in (K_UP, K_o): - lights[0].pressed = pressed + 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[1].pressed = pressed + lights[NS.LNE].pressed = pressed elif event.key in (K_DOWN, K_SEMICOLON): - lights[2].pressed = pressed + lights[NS.LSE].pressed = pressed elif event.key in (K_LEFT, K_l): - lights[3].pressed = pressed + lights[NS.LSW].pressed = pressed self.last_press = get_ticks() else: if self.get_delegate().compare(event, "reset-game"): @@ -140,8 +141,10 @@ class NS(Game, Animation): self.title.update() self.introduction.update() self.boss.update() - self.platform.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: @@ -321,32 +324,40 @@ 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." + "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.skateboard.hide() 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 = Sprite(self) - self.advance_prompt.load_from_path(self.get_resource("Dialogue_buttons_full.png"), True, - False, (255, 255, 255)) - self.advance_prompt.load_from_path(self.get_resource("Dialogue_buttons_half.png"), True, - False, (255, 255, 255)) - self.advance_prompt.add_frameset([0], name="full", switch=True) - self.advance_prompt.add_frameset([1], name="half") - dsr = self.get_display_surface().get_rect() - self.advance_prompt.location.bottomright = dsr.right - 3, dsr.bottom - 3 - self.register(self.start) + 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.cancel_first_press() + 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 @@ -356,7 +367,7 @@ class Introduction(Animation): self.play(self.start, delay=3000, play_once=True) def start(self): - self.cancel_first_press() + self.advance_prompt.cancel_first_press() dialogue = self.get_game().dialogue dialogue.activate() dialogue.set_avatar(self.tony_avatar) @@ -364,37 +375,152 @@ class Introduction(Animation): dialogue.show_text(self.TEXT[0]) self.text_index = 0 - def cancel_first_press(self): - self.first_pressed = False - self.first_pressed_elapsed = 0 - self.advance_prompt.set_frameset("full") + def 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 - if not self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.N: - self.first_pressed = True - self.advance_prompt.set_frameset("half") - elif self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.NW: - if dialogue.is_playing(): - dialogue.show_all() - else: - self.text_index += 1 - if self.text_index == 1: - dialogue.set_name("Tony") - dialogue.show_text(self.TEXT[self.text_index]) - self.get_game().platform.unpress() - self.cancel_first_press() - elif self.first_pressed: - self.first_pressed_elapsed += self.get_game().time_filter.get_last_frame_duration() - if self.first_pressed_elapsed > 4000: - self.cancel_first_press() + 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 self.is_playing(self.start) and not dialogue.is_playing(): + 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): @@ -459,14 +585,17 @@ class Platform(GameChild): def __init__(self, parent): GameChild.__init__(self, parent) self.lights = [ - Light(self, "cyan", NS.NW), - Light(self, "magenta", NS.NE), - Light(self, "yellow", NS.SE), - Light(self, "white", NS.SW) + 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() @@ -485,19 +614,43 @@ class Platform(GameChild): def get_edge_pressed(self): pressed = self.get_pressed() - if NS.NW in pressed and NS.NE in pressed: + if NS.LNW in pressed and NS.LNE in pressed: return NS.N - elif NS.NE in pressed and NS.SW in pressed: + elif NS.LNE in pressed and NS.LSW in pressed: return NS.NE - elif NS.NE in pressed and NS.SE in pressed: + elif NS.LNE in pressed and NS.LSE in pressed: return NS.E - elif NS.NW in pressed and NS.SE in pressed: + elif NS.LNW in pressed and NS.LSE in pressed: return NS.NW - elif NS.SE in pressed and NS.SW in pressed: + elif NS.LSE in pressed and NS.LSW in pressed: return NS.S - elif NS.SW in pressed and NS.NW in pressed: + 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: @@ -507,9 +660,13 @@ class Platform(GameChild): 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() @@ -524,45 +681,67 @@ class Light(Animation): backright = ds.get_width() / 2 + NS.BACK_WIDTH / 2, NS.FRONT + NS.LENGTH right_step = get_step_relative(frontright, backright, NS.STEP) midright = frontright[0] + right_step[0], frontright[1] + right_step[1] - if self.position == NS.NW: + if self.position == NS.LNW: self.points = frontleft, frontmid, mid, midleft - elif self.position == NS.NE: + elif self.position == NS.LNE: self.points = frontmid, frontright, midright, mid - elif self.position == NS.SE: + elif self.position == NS.LSE: self.points = mid, midright, backright, backmid - elif self.position == NS.SW: + 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) - boss = self.get_game().boss - chemtrails = self.get_game().chemtrails - if boss.queue and boss.brandish_complete and not self.is_playing(self.blink) \ - and self.in_orientation(boss.queue[chemtrails.queue_index]): - self.play(self.blink) - elif self.is_playing(self.blink) and (not boss.queue or - not self.in_orientation(boss.queue[chemtrails.queue_index])): - self.reset() + if not self.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: - aa_filled_polygon(self.get_display_surface(), self.points, self.color) + 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.NW: + if self.position == NS.LNW: return orientation in (NS.N, NS.NW, NS.W) - elif self.position == NS.NE: + elif self.position == NS.LNE: return orientation in (NS.N, NS.NE, NS.E) - elif self.position == NS.SE: + elif self.position == NS.LSE: return orientation in (NS.NW, NS.E, NS.S) - elif self.position == NS.SW: + elif self.position == NS.LSW: return orientation in (NS.S, NS.NE, NS.W) @@ -594,21 +773,22 @@ class Chemtrails(GameChild): def update(self): if self.active: self.orient() - if self.get_game().boss.queue: - self.timer_remaining -= self.get_game().time_filter.get_last_frame_duration() - self.attack() - if self.timer_remaining < 0: - self.life.decrease() - if not self.get_game().game_over: - self.timer_remaining = self.TIME_LIMIT - self.get_game().boss.combo() - font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 24) - text = font.render("%.2f" % max(0, self.timer_remaining / 1000.0), True, Color("white")) - rect = text.get_rect() - ds = self.get_display_surface() - rect.topright = ds.get_rect().topright - ds.blit(text, rect) - self.life.update() + if not self.get_game().introduction.active: + if self.get_game().boss.queue: + self.timer_remaining -= self.get_game().time_filter.get_last_frame_duration() + self.attack() + if self.timer_remaining < 0: + self.life.decrease() + if not self.get_game().game_over: + self.timer_remaining = self.TIME_LIMIT + self.get_game().boss.combo() + font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 24) + text = font.render("%.2f" % max(0, self.timer_remaining / 1000.0), True, Color("white")) + rect = text.get_rect() + ds = self.get_display_surface() + rect.topright = ds.get_rect().topright + ds.blit(text, rect) + self.life.update() def attack(self): boss = self.get_game().boss @@ -620,43 +800,49 @@ class Chemtrails(GameChild): if self.queue_index == len(queue): self.timer_remaining = self.TIME_LIMIT self.get_game().boss.combo() - self.get_game().platform.reset() + 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 + 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 + 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 + 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 + 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 + 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 + 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: @@ -695,15 +881,33 @@ class Life(GameChild): class Boss(RainbowSprite): def __init__(self, parent): - RainbowSprite.__init__(self, parent, load("resource/Koolaid.png").convert_alpha(), 30) + 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.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() @@ -712,7 +916,8 @@ class Boss(RainbowSprite): self.health.reset() self.halt(self.brandish) self.sword.reset() - self.combo() + self.advance_prompt.reset() + # self.combo() self.queue = None self.brandish_complete = True @@ -750,11 +955,29 @@ class Boss(RainbowSprite): 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() 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): diff --git a/resource/Introduction_slime_bag_board.png b/resource/Introduction_slime_bag_board.png new file mode 100644 index 0000000..f084956 Binary files /dev/null and b/resource/Introduction_slime_bag_board.png differ diff --git a/resource/Koolaid.png b/resource/Kool_man.png similarity index 100% rename from resource/Koolaid.png rename to resource/Kool_man.png diff --git a/resource/Kool_man_avatar.png b/resource/Kool_man_avatar.png new file mode 100644 index 0000000..1abc6c3 Binary files /dev/null and b/resource/Kool_man_avatar.png differ diff --git a/resource/Skip_0.png b/resource/Skip_0.png new file mode 100644 index 0000000..8f87771 Binary files /dev/null and b/resource/Skip_0.png differ diff --git a/resource/Skip_1.png b/resource/Skip_1.png new file mode 100644 index 0000000..82fd769 Binary files /dev/null and b/resource/Skip_1.png differ diff --git a/resource/Skip_2.png b/resource/Skip_2.png new file mode 100644 index 0000000..de0c96f Binary files /dev/null and b/resource/Skip_2.png differ