|
|
|
@ -1,4 +1,5 @@
|
|
|
|
|
from random import randint, choice
|
|
|
|
|
from math import pi
|
|
|
|
|
from copy import copy
|
|
|
|
|
|
|
|
|
|
from pygame import Surface, Color
|
|
|
|
@ -13,7 +14,7 @@ from lib.pgfw.pgfw.Game import Game
|
|
|
|
|
from lib.pgfw.pgfw.GameChild import GameChild
|
|
|
|
|
from lib.pgfw.pgfw.Sprite import Sprite, RainbowSprite
|
|
|
|
|
from lib.pgfw.pgfw.Animation import Animation
|
|
|
|
|
from lib.pgfw.pgfw.extension import get_step_relative
|
|
|
|
|
from lib.pgfw.pgfw.extension import get_step_relative, get_delta, reflect_angle
|
|
|
|
|
from lib.pgfw.pgfw.gfx_extension import aa_filled_polygon
|
|
|
|
|
|
|
|
|
|
class SoundEffect(GameChild, Sound):
|
|
|
|
@ -59,6 +60,9 @@ class NS(Game, Animation):
|
|
|
|
|
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.chemtrails = Chemtrails(self)
|
|
|
|
|
self.boss = Boss(self)
|
|
|
|
@ -70,6 +74,9 @@ class NS(Game, Animation):
|
|
|
|
|
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()
|
|
|
|
@ -121,7 +128,8 @@ class NS(Game, Animation):
|
|
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
|
Animation.update(self)
|
|
|
|
|
self.get_display_surface().blit(self.background, (0, 0))
|
|
|
|
|
self.title.update()
|
|
|
|
|
self.introduction.update()
|
|
|
|
|
self.boss.update()
|
|
|
|
|
self.platform.update()
|
|
|
|
|
self.chemtrails.update()
|
|
|
|
@ -129,15 +137,38 @@ class NS(Game, Animation):
|
|
|
|
|
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
|
|
|
|
@ -145,15 +176,54 @@ class Title(GameChild):
|
|
|
|
|
def deactivate(self):
|
|
|
|
|
self.active = False
|
|
|
|
|
|
|
|
|
|
def activate_introduction(self):
|
|
|
|
|
self.deactivate()
|
|
|
|
|
self.get_game().introduction.activate()
|
|
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
|
if self.active:
|
|
|
|
|
pass
|
|
|
|
|
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 Introduction(Animation):
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent):
|
|
|
|
|
Animation.__init__(self, parent)
|
|
|
|
|
self.tony = load(self.get_resource("Big_Tony.png"))
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
self.deactivate()
|
|
|
|
@ -167,15 +237,23 @@ class Introduction(Animation):
|
|
|
|
|
def update(self):
|
|
|
|
|
if self.active:
|
|
|
|
|
Animation.update(self)
|
|
|
|
|
self.get_display_surface().blit(self.tony, (0, 0))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@ -183,9 +261,40 @@ class Wipe(Animation):
|
|
|
|
|
def activate(self):
|
|
|
|
|
self.active = True
|
|
|
|
|
|
|
|
|
|
def start(self, callback):
|
|
|
|
|
self.activate()
|
|
|
|
|
self.up = True
|
|
|
|
|
self.get_game().suppressing_input = True
|
|
|
|
|
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().suppressing_input = False
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
@ -200,9 +309,16 @@ class Platform(GameChild):
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
self.deactivate()
|
|
|
|
|
for light in self.lights:
|
|
|
|
|
light.reset()
|
|
|
|
|
|
|
|
|
|
def deactivate(self):
|
|
|
|
|
self.active = False
|
|
|
|
|
|
|
|
|
|
def activate(self):
|
|
|
|
|
self.active = True
|
|
|
|
|
|
|
|
|
|
def get_pressed(self):
|
|
|
|
|
return [light.position for light in self.lights if light.pressed]
|
|
|
|
|
|
|
|
|
@ -222,8 +338,9 @@ class Platform(GameChild):
|
|
|
|
|
return NS.W
|
|
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
|
for light in self.lights:
|
|
|
|
|
light.update()
|
|
|
|
|
if self.active:
|
|
|
|
|
for light in self.lights:
|
|
|
|
|
light.update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -299,30 +416,38 @@ class Chemtrails(GameChild):
|
|
|
|
|
self.life = Life(self)
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
self.deactivate()
|
|
|
|
|
self.life.reset()
|
|
|
|
|
self.timer_remaining = self.TIME_LIMIT
|
|
|
|
|
|
|
|
|
|
def deactivate(self):
|
|
|
|
|
self.active = False
|
|
|
|
|
|
|
|
|
|
def activate(self):
|
|
|
|
|
self.active = True
|
|
|
|
|
|
|
|
|
|
def challenge(self):
|
|
|
|
|
self.timer_remaining = self.TIME_LIMIT
|
|
|
|
|
self.queue_index = 0
|
|
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
|
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 self.active:
|
|
|
|
|
self.orient()
|
|
|
|
|
if self.get_game().boss.queue:
|
|
|
|
|
self.timer_remaining -= self.get_game().time_filter.get_last_frame_duration()
|
|
|
|
|
self.attack()
|
|
|
|
|
if self.timer_remaining < 0:
|
|
|
|
|
self.life.decrease()
|
|
|
|
|
if not self.get_game().game_over:
|
|
|
|
|
self.timer_remaining = self.TIME_LIMIT
|
|
|
|
|
self.get_game().boss.combo()
|
|
|
|
|
font = Font(self.get_resource("rounded-mplus-1m-bold.ttf"), 24)
|
|
|
|
|
text = font.render("%.2f" % max(0, self.timer_remaining / 1000.0), True, Color("white"))
|
|
|
|
|
rect = text.get_rect()
|
|
|
|
|
ds = self.get_display_surface()
|
|
|
|
|
rect.topright = ds.get_rect().topright
|
|
|
|
|
ds.blit(text, rect)
|
|
|
|
|
self.life.update()
|
|
|
|
|
|
|
|
|
|
def attack(self):
|
|
|
|
|
boss = self.get_game().boss
|
|
|
|
@ -419,6 +544,7 @@ class Boss(RainbowSprite):
|
|
|
|
|
self.set_frameset("normal")
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
self.deactivate()
|
|
|
|
|
self.unhide()
|
|
|
|
|
self.cancel_flash()
|
|
|
|
|
self.halt(self.cancel_flash)
|
|
|
|
@ -429,6 +555,12 @@ class Boss(RainbowSprite):
|
|
|
|
|
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)
|
|
|
|
@ -456,10 +588,11 @@ class Boss(RainbowSprite):
|
|
|
|
|
self.get_game().chemtrails.challenge()
|
|
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
|
RainbowSprite.update(self)
|
|
|
|
|
# self.get_display_surface().blit(self.image, (0, 0))
|
|
|
|
|
self.sword.update()
|
|
|
|
|
self.health.update()
|
|
|
|
|
if self.active:
|
|
|
|
|
RainbowSprite.update(self)
|
|
|
|
|
# self.get_display_surface().blit(self.image, (0, 0))
|
|
|
|
|
self.sword.update()
|
|
|
|
|
self.health.update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Sword(Sprite):
|
|
|
|
|