import pygame import random import sys import math pygame.init() pygame.mixer.init() pygame.joystick.init() WIDTH, HEIGHT = pygame.display.Info().current_w, pygame.display.Info().current_h SCREEN_SIZE = (WIDTH, HEIGHT) CAT_INITIAL_SIZE = [160, 160] CAT_MAX_SIZE = [320, 320] SUSHI_SIZE = (50, 50) CLOUD_SIZE = (200, 120) PETAL_SIZE = (30, 30) GAME_OVER_DELAY = 3000 WASABI_CHANCE = 0.1 def quit_game(): pygame.quit() sys.exit() class Cat: def __init__(self, cat_right_img, pos, speed): self.cat_right_base = cat_right_img self.size = CAT_INITIAL_SIZE.copy() self.speed = speed self.facing = "right" self.image_right = pygame.transform.smoothscale(self.cat_right_base, self.size) self.image_left = pygame.transform.flip(self.image_right, True, False) self.image = self.image_right self.rect = self.image.get_rect() self.rect.centerx, self.rect.bottom = pos self.grow_step = 1 def update_image(self): if self.facing == "left": self.image = pygame.transform.flip(self.image_right, True, False) else: self.image = self.image_right def move(self, dx): self.rect.x += dx * self.speed self.rect.x = max(0, min(WIDTH - self.rect.width, self.rect.x)) self.rect.bottom = HEIGHT def grow(self): self.size[0] = min(self.size[0] + self.grow_step, CAT_MAX_SIZE[0]) self.size[1] = min(self.size[1] + self.grow_step, CAT_MAX_SIZE[1]) self.image_right = pygame.transform.smoothscale(self.cat_right_base, self.size) self.update_image() mid_x = self.rect.centerx self.rect = self.image.get_rect() self.rect.centerx = mid_x self.rect.bottom = HEIGHT def draw(self, surface): surface.blit(self.image, self.rect) class Sushi: def __init__(self, image, pos, wasabi=False, speed=5): self.image = image self.rect = self.image.get_rect(midtop=pos) self.wasabi = wasabi self.speed = speed def update(self, dt): self.rect.y += self.speed * dt / 16.67 def draw(self, surface): surface.blit(self.image, self.rect) def off_screen(self): return self.rect.top > HEIGHT class Cloud: def __init__(self, image, speed_multiplier=1.0): self.image = image self.speed_multiplier = speed_multiplier self.size = self.image.get_size() self.y = 50 self.phase = random.uniform(0, 2 * math.pi) self.speed = random.choice([-1.5, 1.5]) * speed_multiplier if self.speed > 0: self.rect = self.image.get_rect(topleft=(-self.size[0], self.y)) else: self.rect = self.image.get_rect(topleft=(WIDTH + self.size[0], self.y)) self.base_y = self.y def update(self, dt, now): self.rect.x += self.speed * dt / 16.67 vertical_offset = 10 * math.sin(2 * math.pi * now / 1000 + self.phase) self.rect.y = self.base_y + vertical_offset def off_screen(self): if self.speed > 0: return self.rect.left > WIDTH else: return self.rect.right < 0 def draw(self, surface): surface.blit(self.image, self.rect) class Petal: def __init__(self, image, pos, petal_toggle, speed_multiplier=1.0): self.image = image self.rect = self.image.get_rect(topleft=pos) self.speed_multiplier = speed_multiplier self.drift = random.uniform(-0.0, 0.1) self.phase = random.uniform(0, 2 * math.pi) self.base_x = pos[0] self.spawn_time = pygame.time.get_ticks() self.petal_toggle = petal_toggle def update(self, dt, now): self.rect.y += 3 * self.speed_multiplier * dt / 16.67 sway = 15 * math.sin(0.5 * now / 1000 + self.phase) time_since_spawn = (now - self.spawn_time) / 1000 drift_speed = 1.5 * self.speed_multiplier self.rect.x = self.base_x + sway + self.drift * self.speed_multiplier + drift_speed * time_since_spawn * 60 def off_screen(self): return self.rect.top > HEIGHT def draw(self, surface): surface.blit(self.image, self.rect) class Edamame: def __init__(self, image, pos, speed=4): self.image = image self.rect = self.image.get_rect(midtop=pos) self.speed = speed self.phase = random.uniform(0, 2 * math.pi) self.base_x = self.rect.x def update(self, dt, now): self.rect.y += self.speed * dt / 16.67 sway = 20 * math.sin(0.005 * now + self.phase) self.rect.x = self.base_x + sway def off_screen(self): return self.rect.top > HEIGHT def draw(self, surface): surface.blit(self.image, self.rect) class Spawner: def __init__(self, spawn_interval_ms): self.spawn_interval = spawn_interval_ms self.last_spawn = 0 def can_spawn(self, now): return now - self.last_spawn > self.spawn_interval def update_spawn_time(self, now): self.last_spawn = now class Game: def __init__(self): self.screen = pygame.display.set_mode(SCREEN_SIZE, pygame.FULLSCREEN) pygame.mouse.set_visible(False) pygame.display.set_caption("Maguro Cat Game") self.wallpaper = pygame.image.load("image_wallpaper.png").convert() self.wallpaper = pygame.transform.scale(self.wallpaper, SCREEN_SIZE) self.cat_right_base = pygame.image.load("image_cat.png").convert_alpha() self.sushi_images = [] for i in [1, 2, 4, 3]: img = pygame.image.load(f"image_sushi{i}.png").convert_alpha() img = pygame.transform.smoothscale(img, SUSHI_SIZE) self.sushi_images.append(img) self.wasabi_img = pygame.image.load("image_wasabi.png").convert_alpha() self.wasabi_img = pygame.transform.smoothscale(self.wasabi_img, SUSHI_SIZE) self.cloud_base = pygame.image.load("image_cloud.png").convert_alpha() self.cloud_img = pygame.transform.smoothscale(self.cloud_base, CLOUD_SIZE) self.petal_base = pygame.image.load("image_petal.png").convert_alpha() self.petal_img = pygame.transform.smoothscale(self.petal_base, PETAL_SIZE) self.petal_img_flipped = pygame.transform.flip(self.petal_img, True, False) self.edamame_img = pygame.image.load("image_edamame.png").convert_alpha() self.edamame_img = pygame.transform.smoothscale(self.edamame_img, SUSHI_SIZE) self.grumpy_img = pygame.transform.smoothscale( pygame.image.load("image_grumpy.png").convert_alpha(), (200, 200) ) self.music_file = "audio_music.mp3" self.chomp_sounds = [ pygame.mixer.Sound("audio_chomp_low.mp3"), pygame.mixer.Sound("audio_chomp.mp3"), pygame.mixer.Sound("audio_chomp_high.mp3") ] self.meow_sound = pygame.mixer.Sound("audio_meow.mp3") self.gamestart_sound = pygame.mixer.Sound("audio_gamestart.mp3") self.gameover_sound = pygame.mixer.Sound("audio_gameover.mp3") self.paused_sound = pygame.mixer.Sound("audio_paused.mp3") self.unpaused_sound = pygame.mixer.Sound("audio_unpaused.mp3") self.oof_sound = pygame.mixer.Sound("audio_oof.mp3") self.joysticks = [] self.check_controllers() self.font = pygame.font.Font("font_8bit.otf", 160) self.jp_font = pygame.font.Font("font_notosansjp.ttf", 64) self.small_font = pygame.font.SysFont(None, 32) self.clock = pygame.time.Clock() self.sushis = [] self.clouds = [] self.petals = [] self.edamames = [] self.reset_game() def check_controllers(self): self.joysticks.clear() for i in range(pygame.joystick.get_count()): joy = pygame.joystick.Joystick(i) joy.init() self.joysticks.append(joy) def draw_intro(self): now = pygame.time.get_ticks() elapsed = now - self.intro_start_time if elapsed >= 3000: pygame.mixer.music.load(self.music_file) pygame.mixer.music.set_volume(0.5) pygame.mixer.music.play(-1) return False maguro_surf = self.font.render("MAGURO", True, (255, 255, 255)) jp_surf = self.jp_font.render("マグロ", True, (255, 255, 255)) self.screen.blit(maguro_surf, maguro_surf.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 80))) self.screen.blit(jp_surf, jp_surf.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 40))) return True def spawn_sushi(self): x = random.randint(50, WIDTH - 50) is_wasabi = random.random() < WASABI_CHANCE img = self.wasabi_img if is_wasabi else random.choice(self.sushi_images) sushi = Sushi(img, (x, -40), wasabi=is_wasabi, speed=self.sushi_fall_speed) self.sushis.append(sushi) def spawn_cloud(self): cloud = Cloud(self.cloud_img, self.cloud_speed_multiplier) self.clouds.append(cloud) def spawn_petal(self): x = random.randint(0, WIDTH) image = self.petal_img_flipped if self.petal_toggle else self.petal_img self.petal_toggle = not self.petal_toggle petal = Petal(image, (x, -PETAL_SIZE[1]), self.petal_toggle, self.petal_speed_multiplier) self.petals.append(petal) def spawn_edamame(self): x = random.randint(50, WIDTH - 50) flipped = random.choice([True, False]) image = pygame.transform.flip(self.edamame_img, True, False) if flipped else self.edamame_img edamame = Edamame(image, (x, -40), speed=self.edamame_fall_speed) self.edamames.append(edamame) def reset_game(self): self.score = 0 self.sushis.clear() self.clouds.clear() self.petals.clear() self.edamames.clear() self.petal_toggle = False self.game_over = False self.game_over_time = None self.paused = False self.pause_by_controller = False self.sushi_fall_speed = 5 self.cloud_speed_multiplier = 1.0 self.petal_speed_multiplier = 1.0 self.edamame_fall_speed = 2 self.cat = Cat(self.cat_right_base, (WIDTH // 2, HEIGHT), 14) self.sushi_spawner = Spawner(125) self.cloud_spawner = Spawner(4000) self.petal_spawner = Spawner(200) self.edamame_spawner = Spawner(1500) self.speed_increase_time = pygame.time.get_ticks() self.intro_start_time = pygame.time.get_ticks() self.gamestart_sound.play() def pause(self, by_controller=False): if not self.paused and not self.show_intro: self.paused = True self.pause_by_controller = by_controller pygame.mixer.music.pause() self.paused_sound.play() def unpause(self): if self.paused: self.paused = False pygame.mixer.music.unpause() self.unpaused_sound.play() def run(self): deadzone = 0.2 self.show_intro = True while True: dt = self.clock.tick(60) now = pygame.time.get_ticks() self.screen.blit(self.wallpaper, (0, 0)) keys = pygame.key.get_pressed() move = 0 self.check_controllers() for event in pygame.event.get(): if event.type == pygame.QUIT: quit_game() if event.type == pygame.KEYDOWN: if self.game_over: self.reset_game() elif event.key == pygame.K_ESCAPE: if not self.game_over and not self.show_intro: if self.paused: self.unpause() else: self.pause(by_controller=False) elif event.key == pygame.K_q: quit_game() if event.type == pygame.JOYBUTTONDOWN: if self.game_over: self.reset_game() elif event.button in [7, 6]: if not self.game_over and not self.show_intro: if self.paused: self.unpause() else: self.pause(by_controller=True) elif self.paused and event.button == 2: quit_game() if self.show_intro: if self.draw_intro(): pygame.display.flip() continue else: self.show_intro = False if self.paused: self.draw_centered_text("PAUSED", (255, 255, 255)) if self.pause_by_controller: instruction = self.small_font.render("Press START to resume or X to quit.", True, (255, 255, 255)) else: instruction = self.small_font.render("Press ESC to resume or Q to quit.", True, (255, 255, 255)) instr_rect = instruction.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 40)) self.screen.blit(instruction, instr_rect) pygame.display.flip() continue if now - self.speed_increase_time > 30000: self.sushi_fall_speed += 1 self.cloud_speed_multiplier += 0.1 self.petal_speed_multiplier += 0.1 self.edamame_fall_speed += 0.1 self.speed_increase_time = now if self.cloud_spawner.can_spawn(now): self.spawn_cloud() self.cloud_spawner.update_spawn_time(now) if self.petal_spawner.can_spawn(now): self.spawn_petal() self.petal_spawner.update_spawn_time(now) if self.edamame_spawner.can_spawn(now): self.spawn_edamame() self.edamame_spawner.update_spawn_time(now) if not self.game_over and self.sushi_spawner.can_spawn(now): self.spawn_sushi() self.sushi_spawner.update_spawn_time(now) for cloud in self.clouds[:]: cloud.update(dt, now) if cloud.off_screen(): self.clouds.remove(cloud) for petal in self.petals[:]: petal.update(dt, now) if petal.off_screen(): self.petals.remove(petal) for edamame in self.edamames[:]: edamame.update(dt, now) if edamame.off_screen(): self.edamames.remove(edamame) if not self.game_over: if keys[pygame.K_a] or keys[pygame.K_LEFT]: move = -1 self.cat.facing = "left" elif keys[pygame.K_d] or keys[pygame.K_RIGHT]: move = 1 self.cat.facing = "right" for joy in self.joysticks: axis0 = joy.get_axis(0) axis2 = joy.get_axis(2) if joy.get_numhats() > 0: hat_x, _ = joy.get_hat(0) else: hat_x = 0 if move == 0: if abs(axis0) > deadzone: move = int(axis0 / abs(axis0)) self.cat.facing = "left" if axis0 < 0 else "right" elif abs(axis2) > deadzone: move = int(axis2 / abs(axis2)) self.cat.facing = "left" if axis2 < 0 else "right" elif hat_x != 0: move = hat_x self.cat.facing = "left" if hat_x < 0 else "right" self.cat.update_image() self.cat.move(move) cat_mid_y = self.cat.rect.top + self.cat.rect.height // 2 for sushi in self.sushis[:]: sushi.speed = self.sushi_fall_speed sushi.update(dt) horizontal_overlap = sushi.rect.right > self.cat.rect.left and sushi.rect.left < self.cat.rect.right if horizontal_overlap and sushi.rect.bottom >= cat_mid_y: if sushi.wasabi: self.meow_sound.play() pygame.mixer.music.stop() self.gameover_sound.play() self.game_over = True self.game_over_time = None else: self.score += 1 random.choice(self.chomp_sounds).play() self.cat.grow() self.sushis.remove(sushi) elif sushi.off_screen(): self.sushis.remove(sushi) for edamame in self.edamames[:]: edamame.speed = self.edamame_fall_speed edamame.update(dt, now) horizontal_overlap = edamame.rect.right > self.cat.rect.left and edamame.rect.left < self.cat.rect.right if horizontal_overlap and edamame.rect.bottom >= cat_mid_y: self.oof_sound.play() self.score += 1 self.edamames.remove(edamame) elif edamame.off_screen(): self.edamames.remove(edamame) else: if self.game_over_time is None: self.game_over_time = now elif now - self.game_over_time >= GAME_OVER_DELAY: quit_game() for cloud in self.clouds: cloud.draw(self.screen) for petal in self.petals: petal.draw(self.screen) self.cat.draw(self.screen) for sushi in self.sushis: sushi.draw(self.screen) for edamame in self.edamames: edamame.draw(self.screen) self.draw_text(f"Score: {self.score}", (10, 10)) if self.game_over: big_font = pygame.font.Font(None, 96) small_font = pygame.font.Font(None, 32) grumpy_rect = self.grumpy_img.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 160)) self.screen.blit(self.grumpy_img, grumpy_rect) self.draw_centered_multiline_text(["GAME OVER"], color=(255, 0, 0), font=big_font, line_spacing=0) game_over_surf = big_font.render("GAME OVER", True, (255, 0, 0)) x_center = WIDTH // 2 y_start = (HEIGHT // 2) + game_over_surf.get_height() // 2 + 20 prompt_surf = small_font.render("Press any key to try again!", True, (255, 255, 255)) prompt_rect = prompt_surf.get_rect(center=(x_center, y_start + prompt_surf.get_height() // 2)) self.screen.blit(prompt_surf, prompt_rect) pygame.display.flip() def draw_text(self, text, pos, color=(255, 255, 255)): surf = self.small_font.render(text, True, color) self.screen.blit(surf, pos) def draw_centered_text(self, text, color=(255, 255, 255)): surf = self.small_font.render(text, True, color) rect = surf.get_rect(center=(WIDTH // 2, HEIGHT // 2)) self.screen.blit(surf, rect) def draw_centered_multiline_text(self, lines, color=(255, 255, 255), line_spacing=10, font=None): if font is None: font = self.small_font total_height = 0 rendered_lines = [] for line in lines: surf = font.render(line, True, color) rendered_lines.append(surf) total_height += surf.get_height() + line_spacing total_height -= line_spacing y = (HEIGHT - total_height) // 2 for surf in rendered_lines: rect = surf.get_rect(center=(WIDTH // 2, y + surf.get_height() // 2)) self.screen.blit(surf, rect) y += surf.get_height() + line_spacing if __name__ == "__main__": game = Game() game.reset_game() game.run()