import pygame import random import math import os # --- Initialization --- pygame.init() WIDTH, HEIGHT = 640, 480 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Hopple - Run from the Snake!") clock = pygame.time.Clock() font = pygame.font.SysFont("Arial", 28) # --- Colors & Constants --- WHITE = (255, 255, 255) TITLE, PLAYING, GAME_OVER = "title", "playing", "game_over" GRAVITY = 0.8 JUMP_FORCE = 12 PLATFORM_WIDTH = 700 HIGH_SCORE_FILE = "highscore.txt" # --- Load Assets --- def load_and_scale(img, size): return pygame.transform.scale(pygame.image.load(img).convert_alpha(), size) skier_imgs = [ load_and_scale("skier1.png", (30, 50)), load_and_scale("skier2.png", (30, 50)), load_and_scale("skier3.png", (30, 50)), ] skier_jump = load_and_scale("skier_jump.png", (30, 50)) leaf_image = load_and_scale("leaf.png", (40, 40)) tree_image_raw = pygame.image.load("tree.png").convert_alpha() # --- Game State Variables --- game_state = TITLE high_score = 0 level = 1 # --- Read High Score --- def read_high_score(): if os.path.exists(HIGH_SCORE_FILE): with open(HIGH_SCORE_FILE, "r") as f: return int(f.read()) return 0 def write_high_score(score): with open(HIGH_SCORE_FILE, "w") as f: f.write(str(score)) # --- Player Setup --- player = pygame.Rect(160, 340, 30, 50) velocity_y = 0 on_ground = False current_skier_image = skier_imgs[0] ski_frame_index = 0 ski_frame_timer = 0 ski_frame_delay = 8 cash = 0 # --- Game Environment --- floor_tiles = [pygame.Rect(0, HEIGHT - 140, PLATFORM_WIDTH, 140)] leaves = [] trees = [] water_areas = [] floating_platforms = [] speed = 5 tiles_passed = 0 scrolling = True start_time = pygame.time.get_ticks() cycle_time = 0 cycle_duration = 3000 cycle_speed = 1 / cycle_duration # --- Snow Effect --- snowflakes = [{"x": random.randint(0, WIDTH), "y": random.randint(0, HEIGHT), "radius": random.randint(1, 3), "speed": random.uniform(1, 3)} for _ in range(100)] # --- Functions --- def reset_game(): global player, velocity_y, on_ground, floor_tiles, leaves, trees, speed, tiles_passed, scrolling global start_time, cash, level, water_areas, floating_platforms player.x, player.y = 160, 340 velocity_y = 0 on_ground = False floor_tiles = [pygame.Rect(0, HEIGHT - 140, PLATFORM_WIDTH, 140)] leaves.clear() trees.clear() water_areas.clear() floating_platforms.clear() speed = 5 tiles_passed = 0 scrolling = True start_time = pygame.time.get_ticks() cash = 0 level = 1 def get_day_night_colors(cycle): t = (math.sin(cycle * math.pi * 2 - math.pi / 2) + 1) / 2 sky_day, sky_night = (137, 207, 240), (10, 10, 40) ground_day, ground_night = (217, 217, 217), (128, 128, 128) blend = lambda c1, c2: tuple(int(c1[i] * t + c2[i] * (1 - t)) for i in range(3)) return blend(sky_day, sky_night), blend(ground_day, ground_night) def draw_world(): global cycle_time cycle_time += cycle_speed sky_color, ground_color = get_day_night_colors(cycle_time) screen.fill(sky_color) for flake in snowflakes: pygame.draw.circle(screen, WHITE, (int(flake["x"]), int(flake["y"])), flake["radius"]) for tree in trees: tree_img = pygame.transform.scale(tree_image_raw, (int(tree["scale"] * 60), int(tree["scale"] * 100))) tree_img.set_alpha(tree["alpha"]) screen.blit(tree_img, (tree["x"], tree["y"] - tree_img.get_height())) for tile in floor_tiles: pygame.draw.rect(screen, ground_color, tile) for water in water_areas: pygame.draw.rect(screen, (0, 100, 200), water) for plat in floating_platforms: pygame.draw.rect(screen, (160, 82, 45), plat) for leaf in leaves: screen.blit(leaf_image, (leaf.x, leaf.y)) screen.blit(current_skier_image, (player.x, player.y)) elapsed_time = (pygame.time.get_ticks() - start_time) // 1000 hud_y = HEIGHT - 40 screen.blit(font.render(f"Speed: {speed}", True, WHITE), (10, hud_y)) screen.blit(font.render(f"Time: {elapsed_time}s", True, WHITE), (220, hud_y)) screen.blit(leaf_image, (570, hud_y - 6)) screen.blit(font.render(f"${cash}", True, WHITE), (615, hud_y)) def get_current_platform_height(x): for tile in floor_tiles: if tile.x <= x <= tile.x + tile.width: return HEIGHT - tile.height return HEIGHT def add_platforms(): MIN_HEIGHT_DIFF = 30 MAX_HEIGHT_DIFF = 80 if len(floor_tiles) < 3: last = floor_tiles[-1] last_top = last.top attempt = 0 while True: attempt += 1 proposed_top = random.randint(HEIGHT - 200, HEIGHT - 80) height_diff = abs(proposed_top - last_top) if MIN_HEIGHT_DIFF <= height_diff <= MAX_HEIGHT_DIFF or attempt > 10: break new_tile = pygame.Rect(last.x + PLATFORM_WIDTH, proposed_top, PLATFORM_WIDTH, HEIGHT - proposed_top) floor_tiles.append(new_tile) if len(floor_tiles) >= 3: a, b, c = floor_tiles[-3], floor_tiles[-2], floor_tiles[-1] if b.top > a.top and b.top > c.top: water_top = b.top + 10 water_height = HEIGHT - water_top water_areas.append(pygame.Rect(b.x, water_top, b.width, water_height)) platform_count = 3 spacing = b.width // (platform_count + 1) for i in range(platform_count): px = b.x + spacing * (i + 1) - 20 py = b.top - 40 floating_platforms.append(pygame.Rect(px, py, 40, 10)) if random.random() < 0.7: leaf_rect = pygame.Rect(new_tile.x + random.randint(50, 600), new_tile.y - 40, 40, 40) leaves.append(leaf_rect) for _ in range(random.randint(1, 3)): scale = random.uniform(0.5, 1.2) alpha = int(255 * (0.3 + (scale - 0.5) / 0.7)) trees.append({ "x": new_tile.x + random.randint(0, new_tile.width), "y": new_tile.y, "scale": scale, "alpha": min(255, max(80, alpha)) }) def clean_world(): global tiles_passed, speed, level for tile in floor_tiles[:]: if tile.x + tile.width < 0: floor_tiles.remove(tile) tiles_passed += 1 if tiles_passed % 3 == 0: speed += 1 level += 1 leaves[:] = [leaf for leaf in leaves if leaf.right >= 0] trees[:] = [tree for tree in trees if tree["x"] >= -100] water_areas[:] = [w for w in water_areas if w.right >= 0] floating_platforms[:] = [p for p in floating_platforms if p.right >= 0] def move_world(): for tile in floor_tiles: tile.x -= speed for leaf in leaves: leaf.x -= speed for tree in trees: tree["x"] -= speed for water in water_areas: water.x -= speed for plat in floating_platforms: plat.x -= speed def check_side_collision(): for tile in floor_tiles: if player.colliderect(tile) and player.right > tile.left and player.left < tile.left and player.bottom > tile.top: return True return False def draw_text_center(text, y): surface = font.render(text, True, WHITE) rect = surface.get_rect(center=(WIDTH // 2, y)) screen.blit(surface, rect) # --- Main Game Loop --- high_score = read_high_score() running = True while running: clock.tick(60) keys = pygame.key.get_pressed() for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if game_state == TITLE: screen.fill((0, 0, 0)) draw_text_center("HOPPLE", 150) draw_text_center("Press SPACE to Start", 250) draw_text_center(f"High Score: {high_score}", 300) pygame.display.flip() if keys[pygame.K_SPACE]: reset_game() game_state = PLAYING elif game_state == PLAYING: if keys[pygame.K_SPACE] and on_ground: velocity_y = -JUMP_FORCE on_ground = False if scrolling: move_world() clean_world() add_platforms() velocity_y += GRAVITY player.y += velocity_y for leaf in leaves[:]: if player.colliderect(leaf): leaves.remove(leaf) cash += 1 if check_side_collision() or player.top > HEIGHT: scrolling = False if cash > high_score: high_score = cash write_high_score(high_score) game_state = GAME_OVER else: platform_y = get_current_platform_height(player.centerx) landed = False if player.bottom >= platform_y: player.bottom = platform_y velocity_y = 0 on_ground = True landed = True for plat in floating_platforms: if player.colliderect(plat) and velocity_y >= 0: player.bottom = plat.top velocity_y = 0 on_ground = True landed = True break if not landed: on_ground = False current_skier_image = skier_imgs[ski_frame_index] if on_ground else skier_jump if on_ground: ski_frame_timer += 1 if ski_frame_timer >= ski_frame_delay: ski_frame_index = (ski_frame_index + 1) % len(skier_imgs) ski_frame_timer = 0 for flake in snowflakes: flake["y"] += flake["speed"] flake["x"] += random.uniform(-0.5, 0.5) if flake["y"] > HEIGHT: flake["y"] = 0 flake["x"] = random.randint(0, WIDTH) draw_world() pygame.display.flip() elif game_state == GAME_OVER: screen.fill((50, 0, 0)) draw_text_center("GAME OVER", 160) draw_text_center(f"Score: ${cash}", 220) draw_text_center(f"High Score: ${high_score}", 260) draw_text_center("Press SPACE to Restart", 320) pygame.display.flip() if keys[pygame.K_SPACE]: game_state = TITLE pygame.quit()