def __init__(self, scene, location: Tuple[int, int]): """ Construct a new NPC. :param location: The top-left coordinate of the object on the screen. """ # Crash proof scaling while True: try: avatar_num = random.randint(0, Avatars.max_number) # Prevent duplicate avatars if avatar_num in self.avatars_in_use: continue else: self.avatars_in_use.append(avatar_num) avatar_image: Surface = pygame.image.load( str(Paths.avatars / f"{avatar_num}.png")) headless_image: Surface = pygame.image.load( str(Paths.headless / f"headless{avatar_num % 5}.png")) avatar_image = pygame.transform.smoothscale( avatar_image, Avatars.size) except ValueError: print(f"ERROR: Avatar {avatar_num} is not 24 or 32bit") else: break # Turn around after x frames self.frames_until_turn = random.randint(100, 500) self.frames_until_walk = random.randint(10, 150) size = (headless_image.get_width() + 20, avatar_image.get_height() + headless_image.get_height() - Avatars.offset_y) surface = Surface(size) surface.fill(Colors.black) surface.set_colorkey(Colors.black) surface.blits(((headless_image, (0, avatar_image.get_height() - Avatars.offset_y)), (avatar_image, (Avatars.offset_x, 0)))) super().__init__(scene, location, surface)
class HighScore(GameObject): def get_scores(self): with open(HIGHSCORES_BASEPATH, 'r') as scores_json: json_scores = json.loads(scores_json.read()) return json_scores def set_cursor_positions(self, cursor: Cursor): # Cursor positions cursor.add_position( self.back.rect.left - cursor.rect.width, self.back.rect.centery - int(cursor.rect.height / 2), ) def update_scores(self): # Create TExt objects for the scores font_file = 'BalooChettan2-SemiBold.ttf' self.scores = [] self.score_pos = [] for i, score in enumerate(self.get_scores()): self.scores.append( TextObject( str(score), font_file, antialias=True, font_color=colors.white, font_size=32, ), ) self.score_pos.append( TextObject( f'{str(i + 1)}.', font_file, antialias=True, font_color=colors.white, font_size=32, )) # Set position of the scores max_width = 0 separation = 150 for score in self.scores: if score.rect.width > max_width: max_width = score.rect.width for i, score in enumerate(self.scores): score.rect.move_ip( ((self.screen_w / 2) + separation + max_width - score.rect.width), self.screen_h * .3 + self.screen_h * .05 * i, ) self.score_pos[i].rect.move_ip( ((self.screen_w / 2) - separation - self.score_pos[i].rect.width), score.rect.top, ) # Draw base image self.image = Surface((self.screen_w, self.screen_h)) self.image.set_colorkey((0, 0, 0)) self.image.blits(( *[s.get_draw() for s in self.score_pos], *[s.get_draw() for s in self.scores], self.back.get_draw(), self.title_text.get_draw(), )) # Base rect self.rect = self.image.get_rect() # Update draw self.dirty = 1 def start(self): # Text creation font_file = 'BalooChettan2-SemiBold.ttf' self.title_text = TextObject( 'High Scores', font_file, antialias=True, font_color=colors.white, font_size=64, ) self.back = TextObject( 'Back', font_file, antialias=True, font_color=colors.white, font_size=32, ) # Text position self.title_text.set_pos( (self.screen_w - self.title_text.rect.width) / 2, int(self.screen_h * .1)) self.back.set_pos((self.screen_w - self.back.rect.width) / 2, self.screen_h * .75)
class TextShootObject(TextObject): def __init__(self, scene, location: Tuple[int, int], parent: GraphicalObject = None, short: bool = False): if not short and random.randint(0, 4) == 2: self.word = random.choice(Words.long).upper() else: self.word = random.choice(Words.single).upper() self.typed = 0 super().__init__(scene, location, self.word, font_path=FONT_PATH, font_size=FONT_SIZE, font_color=Colors.white) self.parent = parent self.half_width = self.surface.get_width() / 2 def key_input(self, key): key_name = pygame.key.name(key) if key_name == "space": key_name = " " if self.word[self.typed].lower() == key_name: if self.typed == len(self.word) - 1: return TextShootState.WORD_END self.typed += 1 return TextShootState.SUCCESS return TextShootState.WRONG_KEY def draw(self): if not self.typed: self.surface = FONT.render(self.word, True, Colors.white, Colors.blurple) elif self.typed >= len(self.word): self.surface = FONT.render(self.word, True, Colors.red, Colors.blurple) else: typed_text = self.word[:self.typed].replace(" ", "_") untyped_text = self.word[self.typed:] typed_surface: Surface = FONT.render(typed_text, True, Colors.red, Colors.blurple) untyped_surface: Surface = FONT.render(untyped_text, True, Colors.white, Colors.blurple) self.surface = Surface( (typed_surface.get_width() + untyped_surface.get_width(), max(typed_surface.get_height(), untyped_surface.get_height()))) self.surface.blits( ((typed_surface, (0, 0)), (untyped_surface, (typed_surface.get_width(), 0)))) surface = Surface( (self.surface.get_width() + 20, self.surface.get_height())) surface.set_colorkey((0, 0, 0)) pygame.draw.ellipse(surface, Colors.blurple, Rect(0, 0, 20, surface.get_height())) pygame.draw.ellipse( surface, Colors.blurple, Rect(surface.get_width() - 20, 0, 20, surface.get_height())) surface.blit(self.surface, (10, 0)) self.surface = surface if self.parent: self.location = (self.parent.location[0] - self.half_width + self.parent.surface.get_width() / 2, self.parent.location[1] - 40) # Out of bounds on left side if self.location[0] <= 0 and self.parent.y_direction < 0: self.parent.y_direction = abs(self.parent.y_direction) self.parent.move_absolute((self.half_width + self.parent.size[0], self.parent.location[1])) # Out of bounds on right side elif self.location[0] + self.surface.get_width() >= screen.get_width( ) and self.parent.y_direction > 0: self.parent.y_direction = -abs(self.parent.y_direction) new_x = Window.width - (self.half_width + self.parent.size[0]) self.parent.move_absolute((new_x, self.parent.location[1])) super(TextObject, self).draw() if self.parent: self.parent.draw()
class Credits(GameObject): font_file = 'BalooChettan2-SemiBold.ttf' def set_cursor_positions(self, cursor: Cursor): # Cursor positions cursor.add_position( self.back.rect.left - cursor.rect.width, self.back.rect.centery - int(cursor.rect.height / 2), ) def start(self): # Text creation self.title_text = TextObject( 'Credits', self.font_file, antialias=True, font_color=colors.white, font_size=64, ) font_size = 24 # Game Developer self.gd_title = TextObject( 'Game Developer:', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) self.gd_content = TextObject( 'Oscar Rencoret github.com/Raksoiv', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) # Assets self.a_title = TextObject( 'Art Pack:', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) self.a_content = TextObject( '« Space Shooter Redux » from Kenney.nl', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) # Music self.s_title = TextObject( 'Music:', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) self.s1_content = TextObject( '« Deep Blue » from Bensound.com', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) self.s2_content = TextObject( '« Extreme Action » from Bensound.com', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) # Back self.back = TextObject( 'Back', self.font_file, antialias=True, font_color=colors.white, font_size=font_size, ) # Set the positions of the text self.title_text.set_pos( (self.screen_w - self.title_text.rect.width) / 2, int(self.screen_h * .1)) title_x = int(self.screen_w * .4) - 10 content_x = int(self.screen_w * .4) + 10 gd_top = self.screen_h * .3 self.gd_title.set_pos(0, gd_top) # self.gd_title.rect.left = 100 self.gd_title.rect.right = title_x self.gd_content.set_pos(0, gd_top) # self.gd_content.rect.right = self.screen_w - 100 self.gd_content.rect.left = content_x a_top = gd_top + 2 * font_size self.a_title.set_pos(0, a_top) # self.a_title.rect.left = 100 self.a_title.rect.right = title_x self.a_content.set_pos(0, a_top) # self.a_content.rect.right = self.screen_w - 100 self.a_content.rect.left = content_x s1_top = a_top + 2 * font_size s2_top = s1_top + font_size self.s_title.set_pos(0, s1_top) # self.s_title.rect.left = 100 self.s_title.rect.right = title_x self.s1_content.set_pos(0, s1_top) # self.s1_content.rect.right = self.screen_w - 100 self.s1_content.rect.left = content_x self.s2_content.set_pos(0, s2_top) # self.s2_content.rect.right = self.screen_w - 100 self.s2_content.rect.left = content_x self.back.set_pos((self.screen_w - self.back.rect.width) / 2, self.screen_h * .75) # Set the base image self.image = Surface((self.screen_w, self.screen_h)) self.image.set_colorkey((0, 0, 0)) self.image.blits(( self.title_text.get_draw(), self.gd_title.get_draw(), self.gd_content.get_draw(), self.a_title.get_draw(), self.a_content.get_draw(), self.s_title.get_draw(), self.s1_content.get_draw(), self.s2_content.get_draw(), self.back.get_draw(), )) self.rect = self.image.get_rect() # Update draw self.dirty = 1
def render(self, screen: Surface) -> None: screen.blits(( (text, text_rect) for text, text_rect, text_ts in self._on_screen ))