Game/main.py

398 lines
14 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))
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):
# classic per-frame movement (no dt)
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):
# classic per-frame gravity (no dt)
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_rect = door_image.get_rect(center=(800, 700))
button_play = pygame.image.load("images/play_button.png")
button_rect = button_play.get_rect(center=(500, 400))
# BUTTONS PLAY (original images)
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")
# make level buttons larger
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)
# rects (centers kept as before)
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),
]
# Levels (backgrounds) - scale to screen size so entering level shows its background
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)),
]
# helper: map F-key constants to level numbers
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,
}
current_level_bg = None
def restart_level():
goblin.reset()
knight.reset()
level = 0 # 0 = initial splash (BACKGROUND + Play), -1 = level select (BACK_MAIN), 1..8 = playing levels
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:
# ESC only toggles pause while playing a level
if event.key == pygame.K_ESCAPE and level > 0:
paused = not paused
# R returns to initial splash
if event.key == pygame.K_r:
restart_level()
level = 0
# Level hotkeys: F1..F8 available on level-select screen (level == -1)
if level == -1 and event.key in KEY_TO_LEVEL:
target = KEY_TO_LEVEL[event.key]
if target <= floor_score: # only if unlocked
restart_level()
level = target
level_start_time = pygame.time.get_ticks()
current_level_bg = level_bgs[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))
# Initial splash: BACKGROUND with only Play button
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]:
# go to level-select screen
level = -1
# Level-select screen: BACK_MAIN with level buttons (F1..F8)
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))
# draw level buttons (dim locked ones)
for i in range(1, 9):
btn_surf, btn_rect = level_buttons[i]
if i <= floor_score:
SCREEN.blit(btn_surf, btn_rect)
else:
# draw dimmed (locked) version
locked = btn_surf.copy()
locked.set_alpha(90)
SCREEN.blit(locked, btn_rect)
# draw small lock text
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))
# mouse clicks on unlocked buttons start levels
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]
if BG_MUSIC:
try: pygame.mixer.music.play(-1)
except Exception: pass
break
# Playing any level (1..8)
elif level > 0:
if current_level_bg:
SCREEN.blit(current_level_bg, (0, 0))
else:
SCREEN.blit(BACKGROUND, (0, 0))
SCREEN.blit(door_image, door_rect)
if not paused:
# reverted to per-frame calls (no dt)
goblin.handle_input(keys)
goblin.apply_gravity()
goblin.update_animation()
knight.handle_input(keys)
knight.apply_gravity()
knight.update_animation()
goblin.draw(SCREEN)
knight.draw(SCREEN)
# ...existing HUD and level-complete logic...
pygame.display.update()
#CLOCK.tick(FPS)