diff --git a/images/door.png b/images/door.png index 2b73718..6d50c0b 100644 Binary files a/images/door.png and b/images/door.png differ diff --git a/images/sprites/coin.png b/images/sprites/coin.png deleted file mode 100644 index 01ae33d..0000000 Binary files a/images/sprites/coin.png and /dev/null differ diff --git a/images/sprites/fruit.png b/images/sprites/fruit.png deleted file mode 100644 index b9cc7d6..0000000 Binary files a/images/sprites/fruit.png and /dev/null differ diff --git a/images/sprites/knight.png b/images/sprites/knight.png deleted file mode 100644 index 5b0eb4f..0000000 Binary files a/images/sprites/knight.png and /dev/null differ diff --git a/images/sprites/platforms.png b/images/sprites/platforms.png deleted file mode 100644 index 9be41db..0000000 Binary files a/images/sprites/platforms.png and /dev/null differ diff --git a/images/sprites/slime_green.png b/images/sprites/slime_green.png deleted file mode 100644 index a21cb6f..0000000 Binary files a/images/sprites/slime_green.png and /dev/null differ diff --git a/images/sprites/slime_purple.png b/images/sprites/slime_purple.png deleted file mode 100644 index 76f564e..0000000 Binary files a/images/sprites/slime_purple.png and /dev/null differ diff --git a/images/sprites/world_tileset.png b/images/sprites/world_tileset.png deleted file mode 100644 index 36bbf07..0000000 Binary files a/images/sprites/world_tileset.png and /dev/null differ diff --git a/main.py b/main.py index a65fecb..3be9813 100644 --- a/main.py +++ b/main.py @@ -87,6 +87,9 @@ class Player: 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 @@ -120,6 +123,8 @@ class Player: self.x = WIDTH def apply_gravity(self): + + self.prev_y = self.y self.y_velocity += GRAVITY self.y += self.y_velocity @@ -190,7 +195,27 @@ knight = Player( ) door_image = pygame.image.load("images/door.png") -door_rect = door_image.get_rect(center=(800, 700)) +door_image = pygame.transform.scale(door_image, (int(door_image.get_width() * 3), int(door_image.get_height() * 3))) + +door_rect = door_image.get_rect(center=(WIDTH//2, GROUND_Y - door_image.get_height()//2)) + +def place_door_for_level(lvl): + """Разместить дверь на одной из платформ уровня (предпочтительно над землёй, ближе к центру).""" + global door_rect + plats = build_platform_rects(lvl) + if not plats: + door_rect = door_image.get_rect(center=(WIDTH//2, GROUND_Y - door_image.get_height()//2)) + return + candidates = [p for p in plats if p.top < GROUND_Y - 10] + if not candidates: + candidates = plats + + candidates.sort(key=lambda p: abs(p.centerx - WIDTH//2)) + chosen = candidates[0] + + door_rect = door_image.get_rect(midbottom=(chosen.centerx, chosen.top)) + + door_rect.clamp_ip(pygame.Rect(0, 0, WIDTH, HEIGHT)) button_play = pygame.image.load("images/play_button.png") button_rect = button_play.get_rect(center=(500, 400)) @@ -267,12 +292,221 @@ KEY_TO_LEVEL = { 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", (80, GROUND_Y)), + ("grass_smol",(220, GROUND_Y - 120)), + ("dirt_smol", (360, GROUND_Y - 200)), + ("sand_smol", (500, GROUND_Y - 260)), + ("ice_smol", (640, GROUND_Y - 320)), + ("grass_big", (780, GROUND_Y - 80)), + ("sand_big", (920, GROUND_Y)), + ("dirt_smol", (1060, GROUND_Y - 240)), + ("grass_smol",(1200, GROUND_Y - 140)), + ("ice_big", (1340, GROUND_Y)), + ("dirt_big", (1480, GROUND_Y - 60)), + ("sand_smol", (1620, GROUND_Y - 200)), + ("grass_smol",(1760, GROUND_Y - 280)), + ("ice_smol", (1000, GROUND_Y - 420)), + ("dirt_smol", (400, GROUND_Y - 360)), + ], + 2: [ + ("sand_big", (120, GROUND_Y)), + ("dirt_smol", (260, GROUND_Y - 160)), + ("grass_smol",(400, GROUND_Y - 120)), + ("ice_smol", (540, GROUND_Y - 300)), + ("dirt_big", (680, GROUND_Y - 40)), + ("sand_smol", (820, GROUND_Y - 220)), + ("grass_big", (960, GROUND_Y)), + ("ice_big", (1100, GROUND_Y - 20)), + ("dirt_smol", (1240, GROUND_Y - 260)), + ("sand_smol", (1380, GROUND_Y - 320)), + ("grass_smol",(1520, GROUND_Y - 180)), + ("dirt_big", (1660, GROUND_Y - 80)), + ("ice_smol", (1800, GROUND_Y - 360)), + ("grass_smol",(900, GROUND_Y - 420)), + ("dirt_smol", (520, GROUND_Y - 360)), + ], + 3: [ + ("grass_big", (100, GROUND_Y)), + ("sand_smol", (260, GROUND_Y - 200)), + ("dirt_smol", (420, GROUND_Y - 140)), + ("ice_smol", (580, GROUND_Y - 320)), + ("grass_smol",(740, GROUND_Y - 240)), + ("dirt_big", (900, GROUND_Y - 60)), + ("sand_big", (1060, GROUND_Y)), + ("ice_big", (1220, GROUND_Y - 40)), + ("grass_smol",(1380, GROUND_Y - 300)), + ("dirt_smol", (1540, GROUND_Y - 220)), + ("sand_smol", (1700, GROUND_Y - 160)), + ("grass_smol",(860, GROUND_Y - 420)), + ("ice_smol", (480, GROUND_Y - 420)), + ("dirt_big", (1320, GROUND_Y - 120)), + ("grass_smol",(200, GROUND_Y - 320)), + ], + 4: [ + ("ice_big", (140, GROUND_Y)), + ("ice_smol", (300, GROUND_Y - 140)), + ("grass_smol",(460, GROUND_Y - 220)), + ("sand_smol", (620, GROUND_Y - 180)), + ("dirt_smol", (780, GROUND_Y - 360)), + ("grass_big", (940, GROUND_Y - 80)), + ("sand_big", (1100, GROUND_Y)), + ("dirt_big", (1260, GROUND_Y - 40)), + ("ice_smol", (1420, GROUND_Y - 300)), + ("grass_smol",(1580, GROUND_Y - 240)), + ("dirt_smol", (1740, GROUND_Y - 320)), + ("ice_big", (820, GROUND_Y - 420)), + ("sand_smol", (980, GROUND_Y - 260)), + ("grass_smol",(540, GROUND_Y - 360)), + ("dirt_big", (200, GROUND_Y - 80)), + ], + 5: [ + ("dirt_big", (60, GROUND_Y)), + ("grass_big", (240, GROUND_Y - 80)), + ("sand_smol", (420, GROUND_Y - 220)), + ("ice_smol", (600, GROUND_Y - 320)), + ("grass_smol",(780, GROUND_Y - 260)), + ("dirt_smol", (960, GROUND_Y - 280)), + ("sand_big", (1140, GROUND_Y)), + ("ice_big", (1320, GROUND_Y - 40)), + ("grass_smol",(1500, GROUND_Y - 200)), + ("dirt_smol", (1680, GROUND_Y - 360)), + ("sand_smol", (900, GROUND_Y - 420)), + ("grass_big", (540, GROUND_Y - 140)), + ("dirt_big", (1260, GROUND_Y - 120)), + ("ice_smol", (300, GROUND_Y - 300)), + ("grass_smol",(1740, GROUND_Y - 240)), + ], + 6: [ + ("sand_big", (140, GROUND_Y)), + ("dirt_smol", (320, GROUND_Y - 160)), + ("dirt_smol", (500, GROUND_Y - 160)), + ("grass_smol",(680, GROUND_Y - 320)), + ("ice_smol", (860, GROUND_Y - 300)), + ("sand_smol", (1040, GROUND_Y - 300)), + ("grass_big", (1220, GROUND_Y - 80)), + ("dirt_big", (1400, GROUND_Y)), + ("ice_big", (1580, GROUND_Y - 40)), + ("grass_smol",(1760, GROUND_Y - 240)), + ("dirt_smol", (940, GROUND_Y - 420)), + ("sand_smol", (260, GROUND_Y - 260)), + ("grass_smol",(460, GROUND_Y - 220)), + ("dirt_big", (1100, GROUND_Y - 120)), + ("ice_smol", (660, GROUND_Y - 360)), + ], + 7: [ + ("grass_big", (80, GROUND_Y)), + ("ice_smol", (260, GROUND_Y - 180)), + ("ice_smol", (440, GROUND_Y - 180)), + ("sand_smol", (620, GROUND_Y - 260)), + ("dirt_smol", (800, GROUND_Y - 350)), + ("grass_smol",(980, GROUND_Y - 420)), + ("sand_big", (1160, GROUND_Y)), + ("dirt_big", (1340, GROUND_Y - 60)), + ("ice_big", (1520, GROUND_Y - 20)), + ("grass_smol",(1700, GROUND_Y - 300)), + ("dirt_smol", (560, GROUND_Y - 320)), + ("sand_smol", (920, GROUND_Y - 220)), + ("grass_big", (1240, GROUND_Y - 100)), + ("ice_smol", (420, GROUND_Y - 420)), + ("dirt_smol", (140, GROUND_Y - 240)), + ], + 8: [ + ("ice_big", (100, GROUND_Y)), + ("dirt_big", (300, GROUND_Y)), + ("grass_smol",(500, GROUND_Y - 200)), + ("sand_smol", (700, GROUND_Y - 320)), + ("ice_smol", (900, GROUND_Y - 400)), + ("dirt_smol", (1100, GROUND_Y - 360)), + ("grass_smol",(1300, GROUND_Y - 300)), + ("sand_big", (1500, GROUND_Y)), + ("dirt_big", (1700, GROUND_Y - 40)), + ("ice_big", (600, GROUND_Y - 420)), + ("grass_big", (800, GROUND_Y - 120)), + ("dirt_smol", (1000, GROUND_Y - 220)), + ("sand_smol", (1200, GROUND_Y - 260)), + ("ice_smol", (1400, GROUND_Y - 320)), + ("grass_smol",(1600, GROUND_Y - 180)), + ], +} + current_level_bg = None def restart_level(): goblin.reset() knight.reset() +def build_platform_rects(lvl): + """Построить список rect'ов платформ текущего уровня.""" + 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 @@ -294,6 +528,7 @@ while True: 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 @@ -338,6 +573,7 @@ while True: 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 @@ -349,6 +585,12 @@ while True: 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: @@ -360,9 +602,26 @@ while True: 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) \ No newline at end of file