from __future__ import annotations import sys from datetime import datetime from enum import Enum, auto from typing import Callable, TypedDict import cv2 import pygame from cv2 import VideoCapture from pygame import Rect, Surface from pygame.font import Font from pygame.mixer import Sound from pygame.time import Clock from common_module import CommonModule pygame.init () FPS = 30 SYSTEM_FONT = pygame.font.SysFont ('notosanscjkjp', 24, bold = True) USER_FONT = pygame.font.SysFont ('notosanscjkjp', 32, italic = True) DEERJIKA_FONT = pygame.font.SysFont ('07nikumarufont', 50) def main ( ) -> None: game = Game () bg = Bg (game) deerjika = Deerjika (game, DeerjikaPattern.RELAXED, x = CWindow.WIDTH * 3 / 4, y = CWindow.HEIGHT - 120) balloon = Balloon (game) balloon.talk ('あいうえおかきくけこさしすせそたちつてとなにぬねの', 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやいゆえよらりるれろわゐうゑを') CurrentTime (game, SYSTEM_FONT) Sound ('assets/bgm.mp3').play (loops = -1) while True: for event in pygame.event.get (): if event.type == pygame.QUIT: pygame.quit () sys.exit () game.redraw () class DeerjikaPattern (Enum): NORMAL = auto () RELAXED = auto () SLEEPING = auto () class Direction (Enum): LEFT = auto () RIGHT = auto () class Game: clock: Clock frame: int redrawers: list[Redrawer] screen: Surface def __init__ ( self, ): self.screen = pygame.display.set_mode ((CWindow.WIDTH, CWindow.HEIGHT)) self.clock = Clock () self.frame = 0 self.redrawers = [] def redraw ( self, ) -> None: for redrawer in sorted (self.redrawers, key = lambda x: x['layer']): if redrawer['obj'].enabled: redrawer['obj'].redraw () pygame.display.update () self.clock.tick (FPS) class GameObject: frame: int game: Game width: int height: int x: float y: float vx: float = 0 vy: float = 0 ax: float = 0 ay: float = 0 arg: float = 0 enabled: bool = True def __init__ ( self, game: Game, layer: int | None = None, enabled: bool = True, x: float = 0, y: float = 0, ): self.game = game self.enabled = enabled self.frame = 0 if layer is None: if self.game.redrawers: layer = max (r['layer'] for r in self.game.redrawers) + 10 else: layer = 0 self.game.redrawers.append ({ 'layer': layer, 'obj': self }) self.x = x self.y = y def redraw ( self, ) -> None: self.x += self.vx self.y += self.vy self.vx += self.ax self.vy += self.ay self.frame += 1 class Bg (GameObject): surface: Surface def __init__ ( self, game: Game, ): super ().__init__ (game) self.surface = pygame.image.load ('assets/bg.jpg') self.surface = pygame.transform.scale (self.surface, (CWindow.WIDTH, CWindow.HEIGHT)) def redraw ( self, ) -> None: self.game.screen.blit (self.surface, (self.x, self.y)) super ().redraw () class Deerjika (GameObject): surfaces: list[Surface] size: int width: int height: int scale: float = .8 def __init__ ( self, game: Game, pattern: DeerjikaPattern = DeerjikaPattern.NORMAL, direction: Direction = Direction.LEFT, layer: int | None = None, x: float = 0, y: float = 0, ): super ().__init__ (game, layer, x = x, y = y) self.pattern = pattern self.direction = direction match pattern: case DeerjikaPattern.NORMAL: ... case DeerjikaPattern.RELAXED: match direction: case Direction.LEFT: self.width = 1280 self.height = 720 surface = pygame.image.load ('assets/deerjika_relax_left.png') self.surfaces = [] for x in range (0, surface.get_width (), self.width): self.surfaces.append ( surface.subsurface (x, 0, self.width, self.height)) case Direction.RIGHT: ... self.size = len (self.surfaces) def redraw ( self, ) -> None: surface = pygame.transform.scale (self.surfaces[self.frame % self.size], (self.width * self.scale, self.height * self.scale)) self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y))) super ().redraw () class CurrentTime (GameObject): font: Font def __init__ ( self, game: Game, font: Font, ): super ().__init__ (game) self.font = font def redraw ( self, ) -> None: for i in range (4): self.game.screen.blit ( self.font.render (str (datetime.now ()), True, (0, 0, 0)), (i % 2, i // 2 * 2)) super ().redraw () class Balloon (GameObject): surface: Surface query: str = '' answer: str = '' image_url: str | None = None x_flip: bool = False y_flip: bool = False length: int = 300 def __init__ ( self, game: Game, x_flip: bool = False, y_flip: bool = False, ): super ().__init__ (game, enabled = False) self.x_flip = x_flip self.y_flip = y_flip self.surface = pygame.transform.scale (pygame.image.load ('assets/balloon.png'), (CWindow.WIDTH, CWindow.HEIGHT / 2)) self.surface = pygame.transform.flip (self.surface, self.x_flip, self.y_flip) def redraw ( self, ) -> None: if self.frame >= self.length: self.enabled = False return query = self.query if CommonModule.len_by_full (query) > 21: query = CommonModule.mid_by_full (query, 0, 19.5) + '...' answer = Surface ((800, ((CommonModule.len_by_full (self.answer) - 1) // 16 + 1) * 50), pygame.SRCALPHA) for i in range (int (CommonModule.len_by_full (self.answer) - 1) // 16 + 1): answer.blit (DEERJIKA_FONT.render ( CommonModule.mid_by_full (self.answer, 16 * i, 16), True, (192, 0, 0)), (0, 50 * i)) surface = self.surface.copy () surface.blit (USER_FONT.render ('>' + query, True, (0, 0, 0)), (120, 70)) y: int if self.frame < 30: y = 0 elif self.frame >= self.length - 90: y = answer.get_height () - 100 else: y = int ((answer.get_height () - 100) * (self.frame - 30) / (self.length - 120)) surface.blit (answer, (100, 150), Rect (0, y, 800, 100)) self.game.screen.blit (surface, (0, 0)) super ().redraw () def talk ( self, query: str, answer: str, image_url: str | None = None, ) -> None: self.query = query self.answer = answer self.image_url = image_url self.frame = 0 self.enabled = True class CWindow: WIDTH = 1024 HEIGHT = 768 class Redrawer (TypedDict): layer: int obj: GameObject def get_surfaces_from_video ( video_path: str, ) -> list[Surface]: cap = VideoCapture (video_path) if not cap.isOpened (): return [] fps = cap.get (cv2.CAP_PROP_FPS) surfaces: list[Surface] = [] while cap.isOpened (): (ret, frame) = cap.read () if not ret: break frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB) frame_surface = pygame.surfarray.make_surface (frame) frame_surface = pygame.transform.rotate (frame_surface, -90) surfaces.append (pygame.transform.flip (frame_surface, True, False)) cap.release () return surfaces if __name__ == '__main__': main ()