diff --git a/NS.py b/NS.py index 938690c..0c416c0 100644 --- a/NS.py +++ b/NS.py @@ -86,7 +86,8 @@ class NS(Game, Animation): "time": { "int": ["timer-max-time", "timer-start-time", "timer-addition", "sword-delay", - "attract-gif-length", "attract-board-length", "attract-reset-countdown"] + "attract-gif-length", "attract-board-length", "attract-reset-countdown"], + "float": "timer-warning-start" }, "input": { @@ -434,6 +435,12 @@ class Meter(GameChild): else: icon.hide() + def percent(self): + """ + Return amount as a percent of the full amount + """ + return self.amount / self.units + def update(self): ds = self.get_display_surface() ds.blit(self.background, self.rect) @@ -1617,6 +1624,19 @@ class Boys(Meter): self.setup(background, rect, 60, (0, 255, 0), 3, "scrapeIcons/scrapeIcons_01.png") +class BossSprite(Sprite): + """ + """ + + def shift_frame(self): + """ + """ + frameset = self.get_current_frameset() + if frameset.name == "entrance" and frameset.get_current_id() == frameset.order[-1]: + self.set_frameset("normal") + super().shift_frame() + + class Boss(Animation): """ The Boss object also serves as the level object, and it is expected that only one of these objects is initialized. @@ -1640,9 +1660,11 @@ class Boss(Animation): self.spoopy = Sprite(self) self.spoopy.load_from_path("Spoopy.png", True) # Set up alien sprite with boil and hurt animations - self.visitor = Sprite(self, 42) + self.visitor = BossSprite(self, 42) self.visitor.add_frameset(name="hurt", switch=True) self.visitor.load_from_path("alienAnimations/alienHit", True) + self.visitor.add_frameset(name="entrance", switch=True) + self.visitor.load_from_path("alienAnimations/alienIntro", True) self.visitor.add_frameset(name="normal", switch=True) self.visitor.load_from_path("alienAnimations/alienBoil", True) for sprite in self.kool_man, self.visitor, self.spoopy: @@ -1650,7 +1672,7 @@ class Boss(Animation): 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.end_player_damage, self.end_hit_animation) + self.end_player_damage, self.end_hit_animation, self.warning, self.enter_boss) self.register(self.flash_player_damage, interval=100) self.kool_man.add_frameset([0], name="normal", switch=True) self.spoopy.add_frameset([0], name="normal", switch=True) @@ -1663,16 +1685,21 @@ class Boss(Animation): for ii, background in enumerate(self.backgrounds): background.add_frameset(name="normal", switch=True) background.load_from_path(f"bg/bg00{ii + 1}.png") + # Inverted background background.add_frameset(name="inverted", switch=True) frame = pygame.Surface(background.frames[0].get_size()) frame.fill((255, 255, 255)) frame.blit(background.frames[0], (0, 0), None, pygame.BLEND_RGB_SUB) background.add_frame(frame) + # Darkened background background.add_frameset(name="charging", switch=True) frame = background.frames[0].copy() - mask = frame.copy() - mask.fill((80, 80, 80)) - frame.blit(mask, (0, 0), None, pygame.BLEND_RGB_SUB) + frame.fill((80, 80, 80), None, pygame.BLEND_RGB_SUB) + background.add_frame(frame) + # Red background + background.add_frameset(name="warning", switch=True) + frame = background.frames[0].copy() + frame.fill((0, 150, 150), None, pygame.BLEND_RGB_SUB) background.add_frame(frame) background.set_frameset("normal") self.countdown = Countdown(self) @@ -1757,8 +1784,8 @@ class Boss(Animation): elif index == 1: dialogue.set_avatar(self.visitor_avatar) dialogue.set_name("Visitor") - self.visitor.unhide() - self.visitor.set_frameset("normal") + self.visitor.hide() + self.play(self.enter_boss, play_once=True, delay=2000) elif index == 2: dialogue.set_avatar(self.spoopy_avatar) dialogue.set_name("Spoopy") @@ -1804,6 +1831,7 @@ class Boss(Animation): for background in self.backgrounds: background.set_frameset("normal") self.halt(self.end_hit_animation) + self.halt(self.warning) def deactivate(self): self.active = False @@ -1984,8 +2012,8 @@ class Boss(Animation): self.get_game().chemtrails.challenge() self.backgrounds[self.level_index].set_frameset("charging") # Set each boss to its normal frameset - # for boss in (self.kool_man, self.visitor, self.spoopy): - # boss.set_frameset("normal") + for boss in (self.kool_man, self.visitor, self.spoopy): + boss.set_frameset("normal") def choose_new_edge(self, edges): while True: @@ -1997,6 +2025,9 @@ class Boss(Animation): self.battle_finished = True self.halt(self.brandish) self.halt(self.cancel_flash) + self.halt(self.warning) + self.halt(self.flash_player_damage) + self.halt(self.end_player_damage) self.sword.reset() self.queue = [] self.brandish_complete = True @@ -2004,7 +2035,7 @@ class Boss(Animation): if self.level_index == 0: self.kool_man.set_frameset(0) elif self.level_index == 1: - self.visitor.set_frameset(0) + self.visitor.set_frameset("hurt") elif self.level_index == 2: self.spoopy.set_frameset(0) self.add_score() @@ -2099,13 +2130,42 @@ class Boss(Animation): """ Return boss's animation to normal """ - for boss in (self.kool_man, self.visitor, self.spoopy): - boss.set_frameset("normal") + if not self.battle_finished: + for boss in (self.kool_man, self.visitor, self.spoopy): + boss.set_frameset("normal") + + def warning(self): + """ + Use this method as an animation to create a warning flash of the background that flashes according to the + amount of time left in the player's timer object. + """ + time_left = self.get_game().chemtrails.timer.percent() + warning_threshold = self.get_configuration("time", "timer-warning-start") + background = self.backgrounds[self.level_index] + if time_left > warning_threshold: + background.set_frameset("normal") + self.halt(self.warning) + else: + if background.get_current_frameset().name == "normal": + background.set_frameset("warning") + self.play(self.warning, play_once=True, delay=50) + else: + background.set_frameset("normal") + self.play(self.warning, play_once=True, delay=time_left / warning_threshold * 400) + + def enter_boss(self): + self.visitor.unhide() + self.visitor.get_current_frameset().reset() + self.visitor.set_frameset("entrance") def update(self): + """ + Update graphics + """ if self.active: self.backgrounds[self.level_index].update() dialogue = self.get_game().dialogue + # Handle the continue countdown or increase time elapsed if the continue screen 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() @@ -2123,12 +2183,14 @@ class Boss(Animation): else: self.time_elapsed += self.get_game().time_filter.get_last_frame_duration() Animation.update(self) + # Update boss sprite if self.level_index == 0: self.kool_man.update() elif self.level_index == 1: self.visitor.update() if self.brandish_complete: if self.queue is not None: + # Draw ghosts of the upcoming moves fading more as the move goes futher back in the queue self.alien_arm.unhide() remaining_positions = list(reversed(self.queue[self.get_game().chemtrails.queue_index:])) for ii, position in enumerate(remaining_positions): @@ -2144,6 +2206,10 @@ class Boss(Animation): self.sword.update() self.health.update() self.countdown.update() + # Trigger the warning effect if time is running out + if self.get_game().chemtrails.life.amount > 0 and not self.is_playing(self.warning, include_delay=True) and \ + self.get_game().chemtrails.timer.percent() <= self.get_configuration("time", "timer-warning-start"): + self.play(self.warning, play_once=True) def update_dialogue(self): if self.active: @@ -2431,7 +2497,6 @@ class Sword(Animation): sprite.get_current_frameset().measure_rect() sprite.update_location_size() sprite.location.center = center_save - # sprite.move(*get_step(sprite.location.center, end, 4)) elif sprite.get_current_frameset().name == "explode" and not sprite.is_hidden(): if sprite.get_current_frameset().get_current_id() == sprite.get_current_frameset().order[-1]: sprite.hide() diff --git a/config b/config index ad47897..ff9edac 100644 --- a/config +++ b/config @@ -53,6 +53,7 @@ serial = True timer-max-time = 10000 timer-start-time = 7000 timer-addition = 1000 +timer-warning-start = 0.4 sword-delay = 300 attract-gif-length = 10000 attract-board-length = 3600