import pygame import sys pygame.init() pygame.mixer.init() WIDTH, HEIGHT = 1920, 1080 SCREEN = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Goblin & Knight") CLOCK = pygame.time.Clock() BLACK = (0, 0, 0) GRAVITY = 0.9 GROUND_Y = HEIGHT - 60 FPS = 60 BACKGROUND = pygame.image.load("images/back_main.jpg") BACKGROUND = pygame.transform.scale(BACKGROUND, (WIDTH, HEIGHT)) BACK_MAIN = pygame.image.load("images/back_main.jpg") BACK_MAIN = pygame.transform.scale(BACK_MAIN, (WIDTH, HEIGHT)) BG_MUSIC = None try: BG_MUSIC = pygame.mixer.music pygame.mixer.music.load('music/musica.mp3') pygame.mixer.music.set_volume(1) pygame.mixer.music.play(-1) except Exception: BG_MUSIC = None FONT = pygame.font.SysFont(None, 32) score_goblin = 0 score_knight = 0 paused = False floor_score = 1 MAX_FLOOR_SCORE = 8 class Player: def __init__(self, x, y, sprites, scale, controls): self.start_x = x self.start_y = y self.x = float(x) self.y = float(y) self.y_velocity = 0.0 self.jump_power = 18 self.jumping = False self.facing_right = True self.moving = False self.speed = 5.5 self.controls = controls self.anim_counter = 0 self.ANIM_SPEED = 8 self.stand = pygame.image.load(sprites["stand"]).convert_alpha() self.walk_left = pygame.image.load(sprites["walk_left"]).convert_alpha() self.walk_right = pygame.image.load(sprites["walk_right"]).convert_alpha() jump_entry = sprites.get("jump") if isinstance(jump_entry, (list, tuple)) and len(jump_entry) == 3: self.jump_start = pygame.image.load(jump_entry[0]).convert_alpha() self.jump_mid = pygame.image.load(jump_entry[1]).convert_alpha() self.jump_end = pygame.image.load(jump_entry[2]).convert_alpha() elif all(k in sprites for k in ("jump_start", "jump_mid", "jump_end")): self.jump_start = pygame.image.load(sprites["jump_start"]).convert_alpha() self.jump_mid = pygame.image.load(sprites["jump_mid"]).convert_alpha() self.jump_end = pygame.image.load(sprites["jump_end"]).convert_alpha() else: single_path = jump_entry if isinstance(jump_entry, str) else sprites.get("jump", sprites["stand"]) single = pygame.image.load(single_path).convert_alpha() self.jump_start = self.jump_mid = self.jump_end = single self.stand = pygame.transform.scale(self.stand, (int(self.stand.get_width() * scale), int(self.stand.get_height() * scale))) self.walk_left = pygame.transform.scale(self.walk_left, (int(self.walk_left.get_width() * scale), int(self.walk_left.get_height() * scale))) self.walk_right = pygame.transform.scale(self.walk_right, (int(self.walk_right.get_width() * scale), int(self.walk_right.get_height() * scale))) self.jump_start = pygame.transform.scale(self.jump_start, (int(self.jump_start.get_width() * scale), int(self.jump_start.get_height() * scale))) self.jump_mid = pygame.transform.scale(self.jump_mid, (int(self.jump_mid.get_width() * scale), int(self.jump_mid.get_height() * scale))) self.jump_end = pygame.transform.scale(self.jump_end, (int(self.jump_end.get_width() * scale), int(self.jump_end.get_height() * scale))) self.surface = self.stand self.rect = self.surface.get_rect(midbottom=(self.x, self.y)) self.prev_y = self.y def reset(self): self.x = self.start_x self.y = self.start_y self.y_velocity = 0 self.jumping = False self.moving = False self.anim_counter = 0 self.facing_right = True self.rect = self.surface.get_rect(midbottom=(self.x, self.y)) def handle_input(self, keys): self.moving = False if keys[self.controls["right"]]: self.x += self.speed self.facing_right = True self.moving = True if keys[self.controls["left"]]: self.x -= self.speed self.facing_right = False self.moving = True if keys[self.controls["jump"]] and not self.jumping: self.jumping = True self.y_velocity = -self.jump_power if self.x < 0: self.x = 0 if self.x > WIDTH: self.x = WIDTH def apply_gravity(self): self.prev_y = self.y self.y_velocity += GRAVITY self.y += self.y_velocity if self.y >= GROUND_Y: self.y = GROUND_Y self.y_velocity = 0 self.jumping = False if self.y < 0: self.y = 0 self.y_velocity = 0 def update_animation(self): if self.jumping: up_thresh = -self.jump_power * 0.4 down_thresh = self.jump_power * 0.4 if self.y_velocity < up_thresh: self.surface = self.jump_start elif self.y_velocity > down_thresh: self.surface = self.jump_end else: self.surface = self.jump_mid elif self.moving: self.anim_counter += 1 frame = (self.anim_counter // self.ANIM_SPEED) % 2 self.surface = self.walk_left if frame == 0 else self.walk_right else: self.surface = self.stand if not self.facing_right: self.surface = pygame.transform.flip(self.surface, True, False) self.rect = self.surface.get_rect(midbottom=(int(self.x), int(self.y))) def draw(self, screen): screen.blit(self.surface, self.rect) goblin = Player( 200, GROUND_Y, { "stand": "images/GoblinWorker/spritePics/stand_gob.png", "walk_left": "images/GoblinWorker/spritePics/walkleft.png", "walk_right": "images/GoblinWorker/spritePics/walkright.png", "jump": "images/GoblinWorker/spritePics/Jump.png" }, 3, { "left": pygame.K_a, "right": pygame.K_d, "jump": pygame.K_SPACE } ) knight = Player( 500, GROUND_Y, { "stand": "images/knight-named/stand.png", "walk_left": "images/knight-named/walkleft.png", "walk_right": "images/knight-named/walkright.png", "jump": [ "images/knight-named/roll1.png", "images/knight-named/roll2.png", "images/knight-named/roll3.png" ] }, 3, { "left": pygame.K_KP4, "right": pygame.K_KP6, "jump": pygame.K_KP8 } ) door_image = pygame.image.load("images/door.png") door_image = pygame.transform.scale(door_image,(int(door_image.get_width() * 3), int(door_image.get_height() * 3))) DOOR_POSITIONS = { 1: (100, GROUND_Y - 645), 2: (1800, GROUND_Y-565), 3: (1550,GROUND_Y-525), 4: (50, GROUND_Y - 725), 5: (900, GROUND_Y - 685), 6: (200,GROUND_Y-625), 7: (1700, GROUND_Y - 745), 8: (1300, GROUND_Y - 875) } door_rect = door_image.get_rect(center=(WIDTH//2, GROUND_Y)) def place_door_for_level(lvl): global door_rect if lvl in DOOR_POSITIONS: x, y = DOOR_POSITIONS[lvl] door_rect = door_image.get_rect(midbottom=(x, y)) else: door_rect = door_image.get_rect(center=(WIDTH // 2, GROUND_Y - door_image.get_height() // 2)) door_rect.clamp_ip(pygame.Rect(0, 0, WIDTH, HEIGHT)) button_play = pygame.image.load("images/play_button.png").convert_alpha() button_play.set_colorkey((0,0,0)) button_play = pygame.transform.scale( button_play, (int(button_play.get_width() * 2.5), int(button_play.get_height() * 2.5)) ) button_rect = button_play.get_rect(center=(WIDTH // 2, HEIGHT // 2)) buttonlvl1 = pygame.image.load("images/button_lvl/F1.png") buttonlvl2 = pygame.image.load("images/button_lvl/F2.png") buttonlvl3 = pygame.image.load("images/button_lvl/F3.png") buttonlvl4 = pygame.image.load("images/button_lvl/F4.png") buttonlvl5 = pygame.image.load("images/button_lvl/F5.png") buttonlvl6 = pygame.image.load("images/button_lvl/F6.png") buttonlvl7 = pygame.image.load("images/button_lvl/F7.png") buttonlvl8 = pygame.image.load("images/button_lvl/F8.png") BUTTON_BIG_SIZE = (220, 140) buttonlvl1_big = pygame.transform.scale(buttonlvl1, BUTTON_BIG_SIZE) buttonlvl2_big = pygame.transform.scale(buttonlvl2, BUTTON_BIG_SIZE) buttonlvl3_big = pygame.transform.scale(buttonlvl3, BUTTON_BIG_SIZE) buttonlvl4_big = pygame.transform.scale(buttonlvl4, BUTTON_BIG_SIZE) buttonlvl5_big = pygame.transform.scale(buttonlvl5, BUTTON_BIG_SIZE) buttonlvl6_big = pygame.transform.scale(buttonlvl6, BUTTON_BIG_SIZE) buttonlvl7_big = pygame.transform.scale(buttonlvl7, BUTTON_BIG_SIZE) buttonlvl8_big = pygame.transform.scale(buttonlvl8, BUTTON_BIG_SIZE) buttonlvl1_rect = buttonlvl1_big.get_rect(center=(300, 300)) buttonlvl2_rect = buttonlvl2_big.get_rect(center=(600, 300)) buttonlvl3_rect = buttonlvl3_big.get_rect(center=(900, 300)) buttonlvl4_rect = buttonlvl4_big.get_rect(center=(300, 600)) buttonlvl5_rect = buttonlvl5_big.get_rect(center=(600, 600)) buttonlvl6_rect = buttonlvl6_big.get_rect(center=(900, 600)) buttonlvl7_rect = buttonlvl7_big.get_rect(center=(300, 900)) buttonlvl8_rect = buttonlvl8_big.get_rect(center=(600, 900)) level_buttons = [ None, (buttonlvl1_big, buttonlvl1_rect), (buttonlvl2_big, buttonlvl2_rect), (buttonlvl3_big, buttonlvl3_rect), (buttonlvl4_big, buttonlvl4_rect), (buttonlvl5_big, buttonlvl5_rect), (buttonlvl6_big, buttonlvl6_rect), (buttonlvl7_big, buttonlvl7_rect), (buttonlvl8_big, buttonlvl8_rect), ] level1 = pygame.image.load("images/Levels/floor1.png") level2 = pygame.image.load("images/Levels/floor2.png") level3 = pygame.image.load("images/Levels/floor3.png") level4 = pygame.image.load("images/Levels/floor4.png") level5 = pygame.image.load("images/Levels/floor5.png") level6 = pygame.image.load("images/Levels/floor6.png") level7 = pygame.image.load("images/Levels/floor7.png") level8 = pygame.image.load("images/Levels/floor8.png") level_bgs = [ None, pygame.transform.scale(level1, (WIDTH, HEIGHT)), pygame.transform.scale(level2, (WIDTH, HEIGHT)), pygame.transform.scale(level3, (WIDTH, HEIGHT)), pygame.transform.scale(level4, (WIDTH, HEIGHT)), pygame.transform.scale(level5, (WIDTH, HEIGHT)), pygame.transform.scale(level6, (WIDTH, HEIGHT)), pygame.transform.scale(level7, (WIDTH, HEIGHT)), pygame.transform.scale(level8, (WIDTH, HEIGHT)), ] KEY_TO_LEVEL = { pygame.K_F1: 1, pygame.K_F2: 2, pygame.K_F3: 3, pygame.K_F4: 4, pygame.K_F5: 5, pygame.K_F6: 6, pygame.K_F7: 7, pygame.K_F8: 8, } dirt_smol = pygame.image.load("images/platforms-named/dirt_smol.png") dirt_smol = pygame.transform.scale(dirt_smol, (int(dirt_smol.get_width() * 5), int(dirt_smol.get_height() * 3))) dirt_big = pygame.image.load("images/platforms-named/dirt_big.png") dirt_big = pygame.transform.scale(dirt_big, (int(dirt_big.get_width() * 5), int(dirt_big.get_height() * 3))) grass_smol = pygame.image.load("images/platforms-named/grass_smol.png") grass_smol = pygame.transform.scale(grass_smol, (int(grass_smol.get_width() * 5), int(grass_smol.get_height() * 3))) grass_big = pygame.image.load("images/platforms-named/grass_big.png") grass_big = pygame.transform.scale(grass_big, (int(grass_big.get_width() * 5), int(grass_big.get_height() * 3))) sand_smol = pygame.image.load("images/platforms-named/sand_smol.png") sand_smol = pygame.transform.scale(sand_smol, (int(sand_smol.get_width() * 5), int(sand_smol.get_height() * 3))) sand_big = pygame.image.load("images/platforms-named/sand_big.png") sand_big = pygame.transform.scale(sand_big, (int(sand_big.get_width() * 5), int(sand_big.get_height() * 3))) ice_smol = pygame.image.load("images/platforms-named/ice_smol.png") ice_smol = pygame.transform.scale(ice_smol, (int(ice_smol.get_width() * 5), int(ice_smol.get_height() * 3))) ice_big = pygame.image.load("images/platforms-named/ice_big.png") ice_big = pygame.transform.scale(ice_big, (int(ice_big.get_width() * 5), int(ice_big.get_height() * 3))) PLATFORM_IMAGES = { "dirt_smol": dirt_smol, "dirt_big": dirt_big, "grass_smol": grass_smol, "grass_big": grass_big, "sand_smol": sand_smol, "sand_big": sand_big, "ice_smol": ice_smol, "ice_big": ice_big, } LEVEL_BLOCKS = { 1: [ ("dirt_big", (100, GROUND_Y-120)), ("grass_smol",(400, GROUND_Y-180)), ("grass_big", (700, GROUND_Y - 240)), ("sand_smol", (1000, GROUND_Y - 300)), ("ice_smol", (1300, GROUND_Y - 360)), ("grass_big", (1600, GROUND_Y - 220)), ("sand_big", (1800, GROUND_Y - 340)), ("dirt_smol", (1600, GROUND_Y - 460)), ("grass_smol",(1800, GROUND_Y - 580)), ("ice_big", (1600, GROUND_Y- 700)), ("dirt_big", (1200, GROUND_Y - 700)), ("sand_smol", (900, GROUND_Y - 700)), ("grass_smol",(600, GROUND_Y - 700)), ("ice_smol", (250, GROUND_Y - 520)), ("dirt_smol", (100, GROUND_Y - 620)), ], 2: [ ("dirt_big",(100,GROUND_Y-100)), ("grass_smol",(400,GROUND_Y-240)), ("grass_smol",(600,GROUND_Y-380)), ("sand_smol",(400,GROUND_Y-520)), ("grass_big",(100,GROUND_Y-660)), ("sand_smol",(400,GROUND_Y-720)), ("sand_big",(600,GROUND_Y-720)), ("sand_smol",(480,GROUND_Y-720)), ("sand_big",(1000,GROUND_Y-420)), ("dirt_smol",(1300,GROUND_Y-220)), ("grass_smol",(1500,GROUND_Y-360)), ("sand_smol",(1800,GROUND_Y-420)), ("ice_smol",(1800,GROUND_Y-540)), ], 3: [ ("ice_big",(400,GROUND_Y-120)), ("ice_smol",(100,GROUND_Y-240)), ("ice_smol",(400,GROUND_Y-360)), ("ice_smol",(100,GROUND_Y-480)), ("grass_big",(400,GROUND_Y-600)), ("sand_big",(100,GROUND_Y-720)), ("ice_big",(400,GROUND_Y-800)), ("grass_smol",(700,GROUND_Y-800)), ("ice_smol",(900,GROUND_Y-800)), ("grass_smol",(1100,GROUND_Y-800)), ("sand_big",(1400,GROUND_Y-800)), ("dirt_smol",(1600,GROUND_Y-800)), ("grass_smol",(1520,GROUND_Y-800)), ("sand_smol",(1550,GROUND_Y-500)), ], 4: [ ("dirt_big", (400, GROUND_Y - 120)), ("grass_smol", (700, GROUND_Y - 240)), ("grass_smol", (1000, GROUND_Y - 360)), ("sand_big", (1300, GROUND_Y - 480)), ("sand_big", (1600, GROUND_Y - 600)), ("ice_smol", (1300, GROUND_Y - 700)), ("ice_smol", (1000, GROUND_Y - 780)), ("dirt_big", (700, GROUND_Y - 840)), ("dirt_big", (400, GROUND_Y - 840)), ("grass_smol", (100, GROUND_Y - 900)), ("grass_smol", (50, GROUND_Y - 700)), ], 5: [ ("grass_big", (150, GROUND_Y - 120)), ("sand_smol", (450, GROUND_Y - 240)), ("sand_smol", (750, GROUND_Y - 360)), ("ice_big", (1050, GROUND_Y - 360)), ("ice_big", (1350, GROUND_Y - 360)), ("dirt_smol", (1650, GROUND_Y - 360)), ("dirt_smol", (1650, GROUND_Y - 500)), ("dirt_smol", (1650, GROUND_Y - 640)), ("grass_big", (1400, GROUND_Y - 720)), ("sand_smol", (1100, GROUND_Y - 720)), ("sand_smol", (900, GROUND_Y - 660)), ], 6: [ ("grass_big",(200,GROUND_Y-120)), ("sand_big",(500,GROUND_Y-240)), ("ice_big",(800,GROUND_Y-360)), ("dirt_big",(1100,GROUND_Y-480)), ("grass_big",(1400,GROUND_Y-600)), ("sand_big",(1700,GROUND_Y-720)), ("ice_smol",(1400,GROUND_Y-840)), ("grass_smol",(1100,GROUND_Y-900)), ("sand_smol",(800,GROUND_Y-840)), ("ice_smol",(500,GROUND_Y-720)), ("grass_big",(200,GROUND_Y-600)), ], 7: [ ("ice_big", (150, GROUND_Y - 120)), ("ice_big", (150, GROUND_Y - 260)), ("dirt_smol", (150, GROUND_Y - 400)), ("dirt_smol", (150, GROUND_Y - 540)), ("ice_smol", (150, GROUND_Y - 680)), ("grass_big", (600, GROUND_Y - 200)), ("sand_smol", (700, GROUND_Y - 340)), ("sand_smol", (700, GROUND_Y - 480)), ("ice_smol", (700, GROUND_Y - 620)), ("ice_big", (950, GROUND_Y - 620)), ("dirt_smol", (1150, GROUND_Y - 720)), ("grass_smol", (1450, GROUND_Y - 720)), ("sand_big", (1750, GROUND_Y - 720)), ("sand_big", (1700, GROUND_Y - 600)), ], 8: [ ("dirt_big", (700, GROUND_Y - 120)), ("sand_big", (1000, GROUND_Y - 240)), ("sand_big", (1300, GROUND_Y - 240)), ("ice_smol", (1600, GROUND_Y - 370)), ("ice_big", (1300, GROUND_Y - 500)), ("ice_big", (1000, GROUND_Y - 500)), ("dirt_smol", (700, GROUND_Y - 620)), ("grass_big", (400, GROUND_Y - 740)), ("sand_smol", (700, GROUND_Y - 850)), ("ice_big", (1000, GROUND_Y - 850)), ("ice_big", (1300, GROUND_Y - 850)), ], } current_level_bg = None def restart_level(): goblin.reset() knight.reset() def build_platform_rects(lvl): rects = [] for name, pos in LEVEL_BLOCKS.get(lvl, []): img = PLATFORM_IMAGES.get(name) if img: rects.append(img.get_rect(midbottom=pos)) return rects def resolve_platform_collision(player, platform_rects): cur_rect = player.surface.get_rect(midbottom=(int(player.x), int(player.y))) prev_rect = player.surface.get_rect(midbottom=(int(player.x), int(player.prev_y))) for plat in platform_rects: if cur_rect.colliderect(plat): if prev_rect.bottom <= plat.top: player.y = plat.top player.y_velocity = 0 player.jumping = False cur_rect = player.surface.get_rect(midbottom=(int(player.x), int(player.y))) elif prev_rect.top >= plat.bottom: top_offset = player.surface.get_rect().top player.y = plat.bottom + (player.surface.get_height() - player.surface.get_rect().bottom) player.y_velocity = 0.1 cur_rect = player.surface.get_rect(midbottom=(int(player.x), int(player.y))) else: if prev_rect.centerx < plat.centerx: player.x = plat.left - cur_rect.width / 2 else: player.x = plat.right + cur_rect.width / 2 cur_rect = player.surface.get_rect(midbottom=(int(player.x), int(player.y))) player.rect = player.surface.get_rect(midbottom=(int(player.x), int(player.y))) level = 0 level_start_time = None while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_p: pygame.quit() sys.exit() if event.key == pygame.K_ESCAPE and level > 0: paused = not paused if event.key == pygame.K_r: restart_level() level = 0 if level == -1 and event.key in KEY_TO_LEVEL: target = KEY_TO_LEVEL[event.key] if target <= floor_score: restart_level() level = target level_start_time = pygame.time.get_ticks() current_level_bg = level_bgs[level] place_door_for_level(level) if BG_MUSIC: try: pygame.mixer.music.play(-1) except Exception: pass keys = pygame.key.get_pressed() mouse_pos = pygame.mouse.get_pos() SCREEN.fill((0, 0, 0)) if level == 0: SCREEN.blit(BACKGROUND, (0, 0)) SCREEN.blit(button_play, button_rect) title_surf = FONT.render('Goblin & Knight', True, (255,255,255)) SCREEN.blit(title_surf, (WIDTH//2 - title_surf.get_width()//2, 120)) if button_rect.collidepoint(mouse_pos) and pygame.mouse.get_pressed()[0]: level = -1 elif level == -1: SCREEN.blit(BACK_MAIN, (0, 0)) title_surf = FONT.render('Select Level (F1..F8) — unlocked in order', True, (255,255,255)) SCREEN.blit(title_surf, (WIDTH//2 - title_surf.get_width()//2, 80)) for i in range(1, 9): btn_surf, btn_rect = level_buttons[i] if i <= floor_score: SCREEN.blit(btn_surf, btn_rect) else: locked = btn_surf.copy() locked.set_alpha(90) SCREEN.blit(locked, btn_rect) lock_txt = FONT.render('LOCKED', True, (200, 40, 40)) SCREEN.blit(lock_txt, (btn_rect.centerx - lock_txt.get_width()//2, btn_rect.centery - 10)) if pygame.mouse.get_pressed()[0]: for i in range(1, 9): btn_surf, btn_rect = level_buttons[i] if btn_rect.collidepoint(mouse_pos) and i <= floor_score: restart_level() level = i level_start_time = pygame.time.get_ticks() current_level_bg = level_bgs[level] place_door_for_level(level) if BG_MUSIC: try: pygame.mixer.music.play(-1) except Exception: pass break elif level > 0: if current_level_bg: SCREEN.blit(current_level_bg, (0, 0)) else: SCREEN.blit(BACKGROUND, (0, 0)) for name, pos in LEVEL_BLOCKS.get(level, []): img = PLATFORM_IMAGES.get(name) if img: rect = img.get_rect(midbottom=pos) SCREEN.blit(img, rect) SCREEN.blit(door_image, door_rect) if not paused: goblin.handle_input(keys) goblin.apply_gravity() goblin.update_animation() knight.handle_input(keys) knight.apply_gravity() knight.update_animation() platform_rects = build_platform_rects(level) resolve_platform_collision(goblin, platform_rects) resolve_platform_collision(knight, platform_rects) goblin.draw(SCREEN) knight.draw(SCREEN) if not paused: if goblin.rect.colliderect(door_rect) and knight.rect.colliderect(door_rect): score_goblin += 1 score_knight += 1 if level == floor_score and floor_score < MAX_FLOOR_SCORE: floor_score += 1 restart_level() level = -1 level_start_time = None current_level_bg = None pygame.time.delay(150) pygame.display.update() CLOCK.tick(FPS)