627 lines
23 KiB
Python
627 lines
23 KiB
Python
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.png")
|
||
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
|
||
|
||
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_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))
|
||
|
||
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", (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
|
||
|
||
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_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) |