def draw(self, canvas, time_dict: dict): time_dict['bars'] -= self.__metadata['empty_bars'] if time_dict['bars'] < 0: font = pygame.font.Font('Assets/Fonts/Patapon.ttf', 40) center = [a / 2 for a in canvas.get_size()] ready_text = font.render('Get ready', 1, (255,) * 3) ready_sprite = Sprite(ready_text) ready_sprite.transform(center=center, opacity=1 - (time_dict['beats'] + time_dict['delta'] + 0.5) / time_dict[ 'beat_size'] if time_dict['bars'] == -1 else 1) ready_sprite.draw(canvas) self.game.draw(time_dict, canvas)
class StubMinigame(Engine.MiniGame.AbstractMiniGame): def configure(self, config_json): if 'sprite' in config_json: self.spr = Sprite(config_json['sprite']) self.spr.transform(center=(400, 300)) def reset(self): pass def __init__(self, life_time, sprite=Sprite('Assets/Artwork/bullet.png'), base_color=(0, 0, 0)): super().__init__(life_time) self.spr: Sprite = sprite sprite.transform(center=(400, 300)) self.color = None self.base_color = base_color self.bp = pygame.mixer.Sound('Assets/Sound/wrong.wav') def update(self, time: dict): self.color = (255 - 2 * abs(time['delta']) * (255 - self.base_color[0]), 255 - 2 * abs(time['delta']) * (255 - self.base_color[1]), 255 - 2 * abs(time['delta']) * (255 - self.base_color[2])) self.spr.transform(opacity=(2 * (0.5 - abs(time['delta'])))**2.1, scale=1 + (0.5 - abs(time['delta']))**2.1 * (0.5 if time['beats'] == 0 else 0.2)) return {'delta_health': 0, 'delta_score': 0} def draw(self, time: dict, canvas): self.spr.draw(canvas) def handle(self, event): return { 'delta_health': -10, 'delta_score': (1 - (2 * abs(event['time']['delta']))**0.3) * 30 }
class GameUI(AbstractUI): def update(self): self.info = self.runtime.update() def __init__(self, canvas, runtime: LevelRuntime = None): self.views = None super().__init__(canvas) self.runtime = runtime self.control_keys = [pygame.K_p, pygame.K_ESCAPE, pygame.K_k, pygame.K_m] self.info = None self.settings_popup = None self.display_controls = True heart_image = pygame.image.load('Assets/Artwork/UI/Game/heart.png') heart_image = pygame.transform.scale(heart_image, (int(RADIUS * 2 * 0.8),) * 2) self.heart_sprite = Sprite(heart_image) self.heart_sprite.transform(center=(canvas.get_size()[0] - MARGIN - RADIUS, MARGIN + RADIUS)) def key_press(self, event): if self.info['over']: if event['key'] == pygame.K_ESCAPE: self.runtime.pause() return self.views['menu'] else: return if not (event['key'] in self.control_keys) and (self.runtime is not None) and not self.info['pause']: self.runtime.key_pressed(event) return # Toggle pause if event['key'] == pygame.K_p: if self.info['pause']: if not self.settings_popup: self.runtime.play() else: self.runtime.pause() if event['key'] == pygame.K_ESCAPE: self.runtime.pause() return self.views['menu'] if event['key'] == pygame.K_k: if self.info['pause']: if self.settings_popup: self.settings_popup = None self.runtime.play() else: self.settings_popup = SettingsPopup(self.canvas) if not self.info['pause']: self.runtime.pause() self.settings_popup = SettingsPopup(self.canvas) if event['key'] == pygame.K_m: self.display_controls = not self.display_controls if self.settings_popup: self.settings_popup.key_press(event) def draw_controls(self): size = self.canvas.get_size() # Количество очков в левом верхнем углу font = pygame.font.Font('Assets/Fonts/Patapon.ttf', MARGIN) sc_text = font.render('Score: {}'.format(self.info['stats']['global_score']), 1, WHITE) l_sc_text = font.render('Current mini-game score: {}'.format(self.info['stats']['current_score']), 1, WHITE) self.canvas.blit(sc_text, (0, 0)) self.canvas.blit(l_sc_text, (0, 40)) # Индикатор здоровья в правом верхнем углу pygame.draw.ellipse(self.canvas, BLACK, [size[0] - MARGIN - RADIUS * 2, MARGIN, RADIUS * 2, RADIUS * 2]) if self.info['over']: # Полоса здоровья становится белым кругом в конце игры pygame.draw.ellipse(self.canvas, WHITE, [size[0] - MARGIN - RADIUS * 2, MARGIN, RADIUS * 2, RADIUS * 2], 5) else: # А пока она красная pygame.draw.arc(self.canvas, RED, [size[0] - MARGIN - RADIUS * 2, MARGIN, RADIUS * 2, RADIUS * 2], pi / 2, pi / 2 + 2 * pi * self.info['stats']['health_info']['health'] / self.info['stats']['health_info']['max'], 5) scale_arg = self.runtime.get_time_dict()['delta'] # Рисуем пульсирующее сердечко if not abs(scale_arg) > 0.5 * HEART_PEAK_T: scale_factor = ((cos(2 * pi * scale_arg / HEART_PEAK_T) * 0.5 + 1) * (1 - HEART_MIN_R) + HEART_MIN_R) else: scale_factor = HEART_MIN_R if self.info['over']: scale_factor = HEART_MIN_R if self.info['stats']['health_info']['health'] == 0: # Здоровье на нуле - сердечко разбито :( heart_image = pygame.image.load('Assets/Artwork/UI/Game/heart_broken.png') heart_image = pygame.transform.scale(heart_image, (int(RADIUS * 2 * 0.8),) * 2) self.heart_sprite = Sprite(heart_image) self.heart_sprite.transform(center=(size[0] - MARGIN - RADIUS, MARGIN + RADIUS)) self.heart_sprite.transform(scale=scale_factor) self.heart_sprite.draw(self.canvas) # Индикатор прогресса внизу экрана pointer_coords = MARGIN + (size[0] - MARGIN * 2) * self.info['stats']['progress'], size[1] - MARGIN pygame.draw.polygon(self.canvas, GREEN, [(pointer_coords[0], pointer_coords[1] - 5), (pointer_coords[0] - 20, pointer_coords[1] - 30), (pointer_coords[0] + 20, pointer_coords[1] - 30)]) pygame.draw.line(self.canvas, WHITE, (MARGIN, size[1] - MARGIN), (size[0] - MARGIN, size[1] - MARGIN), 5) waypts = self.runtime.level.get_waypoints() for wp in waypts: # Нарисуем точки резкого изменения мини-игр pygame.draw.line(self.canvas, WHITE, [MARGIN + (size[0] - MARGIN * 2) * wp, size[1] - MARGIN + 20], [MARGIN + (size[0] - 80) * wp, size[1] - MARGIN], 5) def draw_widgets(self): center = [a / 2 for a in self.canvas.get_size()] if not self.info['over']: self.runtime.draw(self.canvas) if self.display_controls: self.draw_controls() font = pygame.font.Font('Assets/Fonts/Patapon.ttf', 40) if self.info['over']: # Нарисовать экран конца игры over_text = font.render('Game over. Press Esc to exit.', 1, WHITE) self.canvas.blit(over_text, over_text.get_rect(center=center)) elif self.runtime.paused: if self.settings_popup: # Всплывающее меню настроек self.settings_popup.draw_widgets() else: # Игра стоит на паузе pause_text = font.render('Paused', 1, WHITE) info_text = font.render('K - settings, M - toggle show controls, Esc - quit', 1, WHITE) self.canvas.blit(pause_text, pause_text.get_rect(center=center)) self.canvas.blit(info_text, (center[0] - info_text.get_size()[0] / 2, center[1] + 40))
class LetaMiniGame(AbstractMiniGame): def configure(self, config_json): pass # TODO def reset(self): pass # TODO def __init__(self, life_time, letters=None): super().__init__(life_time) self.letters = letters if letters is None: self.letters = self.create_letters(life_time) self.len_letters = len(self.letters) # whether received data is correct for game logic and fixing it if len(self.letters) > (life_time - 1) // 2: self.letters = self.letters[:(life_time - 1) // 2] self.len_letters = len(self.letters) elif self.len_letters < (life_time - 1) // 2: self.letters.append( self.create_letters((life_time - 1) // 2 - self.len_letters)) self.len_letters = len(self.letters) for i in range(self.len_letters): if len(self.letters[i]) > beats_number: self.letters[i] = self.letters[i][:beats_number] elif len(self.letters[i]) < beats_number: for j in range(beats_number - len(self.letters[i])): self.letters[i].append(choice(characters)) self.counter = 0 self.letter_counter = 0 self.background = Sprite( pygame.image.load(path.join(img_dir, 'orange.jpg')).convert()) self.height, self.width = 0, 0 self.got_or_not = [[0] * beats_number for i in range(self.len_letters)] self.font_size = 0 def update(self, time: dict): if time['bars'] > self.counter: self.counter += 1 if time['beats'] > self.letter_counter or time['beats'] == 0: self.letter_counter = time['beats'] delta_health = 0 delta_score = 0 if time['beat_type'] in (1, 2) and self.len_letters < self.counter and \ self.got_or_not[(time['bars'] - 1) % self.len_letters][time['beats']] == 0: self.got_or_not[(time['bars'] - 1) % self.len_letters][time['beats']] = -1 delta_health = -5 return {'delta_health': delta_health, 'delta_score': delta_score} def draw(self, time: dict, canvas): # getting proper sizes and background self.width, self.height = canvas.get_size() background_rect = [a / 2 for a in canvas.get_size()] self.background.transform(center=background_rect, scale=(self.height - 2 * bar_width) / self.background.image.get_size()[1]) self.background.draw(canvas) self.font_size = int( (self.height - 2 * bar_width) / (2 * self.len_letters)) # displaying letters if self.counter <= self.len_letters: self.write_letters(canvas, self.counter) else: # when it s time for printing self.write_letters(canvas, self.len_letters) # checking some or all lines if self.counter < 2 * self.len_letters: number_of_lines = (self.counter - 1) % self.len_letters + 1 else: number_of_lines = self.len_letters # marking letters for i in range(number_of_lines): for j in range(beats_number): # circle for pressed ones if self.got_or_not[i][j] == 1: pygame.draw.circle(canvas, (250, 250, 250), [ self.width // 2 + int( (j - (len(self.letters[i]) - 1) / 2) * self.font_size), int(1.05 * bar_width) + int( (i + 1) * (self.height - 2 * bar_width) / (self.len_letters + 1)) ], int(self.font_size // 2.5), int(self.font_size // 20)) # crosses for missed ones elif self.got_or_not[i][j] == -1: # center x and y for a letter x = self.width // 2 + int( (j - (len(self.letters[i]) - 1) / 2) * self.font_size) y = bar_width + int( (i + 1) * (self.height - 2 * bar_width) / (self.len_letters + 1)) pygame.draw.line( canvas, (250, 250, 250), [x + self.font_size // 3, y + self.font_size // 3], [x - self.font_size // 3, y - self.font_size // 3], 5) pygame.draw.line( canvas, (250, 250, 250), [x - self.font_size // 3, y + self.font_size // 3], [x + self.font_size // 3, y - self.font_size // 3], 5) def handle(self, event): delta_score = 0 delta_health = 0 if self.counter > self.len_letters and event['key']['unicode'] in characters \ and math.fabs(event['time']['delta']) < 0.2: bars = (event['time']['bars'] - 1) % self.len_letters beats = event['time']['beats'] # new press on already pressed correctly letter also counts if event['key']['unicode'] == self.letters[bars][beats]: if self.got_or_not[bars][beats] == -1: # if the letter was already marked wrong we return health back delta_health += 5 self.got_or_not[bars][beats] = 1 delta_score += 5 # if the event_key was already accepted we dont charge for new wrong elif self.letters[bars][beats] != 1: delta_health -= 5 self.got_or_not[bars][beats] = -1 return {'delta_health': delta_health, 'delta_score': delta_score} def write_letters(self, canvas, counter): font = pygame.font.Font('Assets/Fonts/Patapon.ttf', self.font_size) for i in range(counter): # last line only already beated letters if it's first half if i == counter - 1 and self.counter <= self.len_letters: local_counter = self.letter_counter + 1 else: local_counter = beats_number for j in range(local_counter): pause_text = font.render(self.letters[i][j], 1, (250, 250, 250)) canvas.blit( pause_text, pause_text.get_rect(center=[ self.width // 2 + (j - (len(self.letters[i]) - 1) / 2) * self.font_size, bar_width + (i + 1) * (self.height - 2 * bar_width) / (self.len_letters + 1) ])) def create_letters(self, life_time): letters = [] for i in range((life_time - 1) // 2): line = [] for j in range(beats_number): line.append(choice(characters)) letters.append(line) return letters
class Disclaimer(AbstractUI): def __init__(self, canvas): super().__init__(canvas) self.runtime = AnimationRuntime() self.status = Status.TRIANGLE ball_surf = pygame.Surface((150, 150)) pygame.draw.ellipse(ball_surf, (0, 255, 255), (0, 0, 150, 150)) self.ball_sprite = Sprite(ball_surf) self.ball_sprite.transform(center=(400, 400), scale=2) def schedule_animations(self): center = tuple(a / 2 for a in self.canvas.get_size()) pygame_logo = Sprite('Assets/Artwork/UI/Disclaimer/pygame_logo.png') pygame_logo.transform(center=center, opacity=0) noblow_logo = Sprite('Assets/Artwork/UI/Disclaimer/cat_blow.png') noblow_logo.transform(center=center, opacity=0) times_font = pygame.font.Font('Assets/Fonts/Times New Roman.ttf', 30) greet_sprite = Sprite(times_font.render('From creators of lab3.1 and lab3.2', 1, (255,) * 3)) greet_sprite.transform(center=center, opacity=0) self.runtime.add_animation_by_keyframes(self.ball_sprite, { 1: {'scale': 0} }) self.runtime.add_animation_by_keyframes(pygame_logo, { 2: {}, 4: {'opacity': 1, 'scale': 1.2}, 6: {'opacity': 0, 'scale': 1.4} }) self.runtime.add_animation_by_keyframes(noblow_logo, { 7: {}, 9: {'opacity': 1, 'scale': 1.2}, 11: {'opacity': 0, 'scale': 1.4} }) self.runtime.add_animation_by_keyframes(greet_sprite, { 12: {}, 14: {'opacity': 1, 'scale': 1.2}, 16: {'opacity': 0, 'scale': 1.4}, 17: {} }) def key_press(self, event): if self.status == Status.TRIANGLE: if event['key'] == pygame.K_ESCAPE: return 'EXIT' self.status = Status.PRESENT self.schedule_animations() else: self.runtime.delete_all_animations() return self.views['menu'] def update(self): self.runtime.update_all(self.canvas) if self.status == Status.PRESENT and not self.runtime.is_animating(): self.status = Status.AWAIT if self.status == Status.AWAIT and not self.runtime.is_animating(): center = tuple(a / 2 for a in self.canvas.get_size()) times_font = pygame.font.Font('Assets/Fonts/Times New Roman.ttf', 30) continue_sprite = Sprite(times_font.render('Press any key to continue', 1, (255,) * 3)) continue_sprite.transform(center=center, opacity=0) self.runtime.add_animation_by_keyframes(continue_sprite, { 1: {'opacity': 1}, 2: {'opacity': 0} }) def draw_widgets(self): if self.status == Status.TRIANGLE: delta = (time.time() % 0.5 - 0.25) * 2 r = (0.5 - abs(delta)) ** 2.1 * 50 + 100 self.ball_sprite.transform(scale=r / 75) self.ball_sprite.draw(self.canvas)