diff --git a/game.py b/game.py index c8edadb..9f946d3 100644 --- a/game.py +++ b/game.py @@ -2,6 +2,7 @@ import pygame import random import sys import os +import math # --- Inicializācija --- pygame.init() @@ -23,6 +24,8 @@ YELLOW = (255, 215, 0) GREEN = (50, 200, 50) CYAN = (0, 255, 255) PURPLE = (160, 32, 240) +ORANGE = (255, 165, 0) +LIGHT_BLUE = (100, 149, 237) # --- Fonti --- def get_font(name, size): @@ -42,77 +45,97 @@ FONT_TINY = get_font("PressStart2P-Regular.ttf", 14) # --- GLOBĀLIE MAINĪGIE --- STATE = "MENU" SELECTED_BG = "BEACH" +GAME_MODE = "PVP" + +# ============================================================ +# --- HIGH SCORE FUNKCIJAS --- +# ============================================================ +HIGHSCORE_FILE = "highscores.txt" + +def save_highscore(winner_name): + try: + with open(HIGHSCORE_FILE, "a") as f: + f.write(f"{winner_name}\n") + except Exception as e: + print(f"Neizdevās saglabāt rezultātu: {e}") + +def load_highscores(): + scores = [] + if os.path.exists(HIGHSCORE_FILE): + try: + with open(HIGHSCORE_FILE, "r") as f: + lines = f.readlines() + scores = [line.strip() for line in lines[-5:]] + except: + pass + return scores # ============================================================ # --- TĒLU UN FONA DATU BĀZES --- # ============================================================ -# Krāsu palešu fallback attēliem FALLBACK_COLORS = [ - (235, 64, 52), # Red - (52, 119, 235), # Blue - (52, 235, 86), # Green - (255, 215, 0), # Gold - (160, 32, 240), # Purple - (255, 105, 180) # Pink + (235, 64, 52), (52, 119, 235), (52, 235, 86), (255, 215, 0), (160, 32, 240), (255, 105, 180) ] CHARACTER_FILES = [ - "characters/first_char.png", - "characters/second_char.png", - "characters/third_char.png", - "characters/forth_char.png", - "characters/fifth_char.png", - "characters/sixth_char.png" + "characters/first_char.png", "characters/second_char.png", "characters/third_char.png", + "characters/forth_char.png", "characters/fifth_char.png", "characters/sixth_char.png" ] +BOSS_FILES = ["boss/boss1.png", "boss/boss2.png", "boss/boss3.png"] + P1_CHAR_INDEX = 0 P2_CHAR_INDEX = 1 -# Fona faili (failu nosaukumi) BG_FILES = { - "BEACH": "backgrounds/beach.jpg", - "EVENINGSTREET": "backgrounds/evening_street.jpg", - "MARKET": "backgrounds/market.jpg", - "RESTAURANT": "backgrounds/restaurant.jpg", - "SAKURASTREET": "backgrounds/sakura_street.jpg", - "SHIP": "backgrounds/ship.jpg", + "BEACH": "backgrounds/beach.jpg", "EVENINGSTREET": "backgrounds/evening_street.jpg", + "MARKET": "backgrounds/market.jpg", "RESTAURANT": "backgrounds/restaurant.jpg", + "SAKURASTREET": "backgrounds/sakura_street.jpg", "SHIP": "backgrounds/ship.jpg", "STREETMARKETS": "backgrounds/street_markets.jpg" } -# Failu ielādes funkcijas -def create_placeholder_image(color, size=(80, 160), text=""): - """Izveido vienkāršu attēlu, ja fails nav atrasts.""" +def create_placeholder_image(color, size=(80, 160), text="", is_boss=False): surf = pygame.Surface(size) surf.fill(color) pygame.draw.rect(surf, BLACK, (0, 0, size[0], size[1]), 2) - # Galva (piemērota lielākam izmēram) head_w = size[0] * 0.4 head_h = size[1] * 0.3 head_rect = pygame.Rect(size[0]//2 - head_w//2, 10, head_w, head_h) + pygame.draw.rect(surf, (200, 200, 200), head_rect) pygame.draw.rect(surf, BLACK, head_rect, 2) - # Acis eye_y = head_rect.y + head_h // 3 - pygame.draw.circle(surf, BLACK, (int(head_rect.centerx - head_w//5), int(eye_y)), 4) - pygame.draw.circle(surf, BLACK, (int(head_rect.centerx + head_w//5), int(eye_y)), 4) - - if text: - font = pygame.font.SysFont('arial', 18) - txt = font.render(text, True, WHITE) - surf.blit(txt, (size[0]//2 - txt.get_width()//2, size[1] - 30)) + eye_size = 6 if is_boss else 4 + pygame.draw.circle(surf, BLACK, (int(head_rect.centerx - head_w//5), int(eye_y)), eye_size) + pygame.draw.circle(surf, BLACK, (int(head_rect.centerx + head_w//5), int(eye_y)), eye_size) + + if is_boss: + horn_scale = size[0] // 10 + pygame.draw.polygon(surf, RED, [ + (head_rect.left, head_rect.top), + (head_rect.left - horn_scale, head_rect.top - horn_scale*2), + (head_rect.left + horn_scale, head_rect.top) + ]) + pygame.draw.polygon(surf, RED, [ + (head_rect.right, head_rect.top), + (head_rect.right + horn_scale, head_rect.top - horn_scale*2), + (head_rect.right - horn_scale, head_rect.top) + ]) + if text: + font_size = 24 if is_boss else 18 + font = pygame.font.SysFont('arial', font_size) + txt = font.render(text, True, WHITE) + surf.blit(txt, (size[0]//2 - txt.get_width()//2, size[1] - 40)) return surf def load_character_images(): chars_data = [] - - # === IZMĒRU IESTATĪJUMI (ŠEIT MAINĪTS) === SPRITE_SIZE = (80, 150) ICON_SIZE = (100, 100) - for i, filename in enumerate(CHARACTER_FILES): color = FALLBACK_COLORS[i % len(FALLBACK_COLORS)] try: @@ -123,10 +146,7 @@ def load_character_images(): else: raise FileNotFoundError except: - # Ja faila nav, veidojam lielu "placeholder" game_img = create_placeholder_image(color, SPRITE_SIZE, f"T{i+1}") - - # Ikonas placeholder icon_surf = pygame.Surface(ICON_SIZE) icon_surf.fill(color) pygame.draw.rect(icon_surf, WHITE, (0,0,ICON_SIZE[0], ICON_SIZE[1]), 2) @@ -134,38 +154,72 @@ def load_character_images(): txt = font.render(f"Char {i+1}", True, WHITE) icon_surf.blit(txt, (ICON_SIZE[0]//2 - txt.get_width()//2, ICON_SIZE[1]//2 - txt.get_height()//2)) icon_img = icon_surf - - chars_data.append({ - 'game_img': game_img, - 'icon': icon_img, - 'color': color - }) + chars_data.append({'game_img': game_img, 'icon': icon_img, 'color': color}) return chars_data -def load_image_safe(filename, fallback_color): +def load_boss_images(): + bosses = [] + BOSS_SIZE = (160, 300) + boss_colors = [(100, 0, 0), (0, 100, 0), (0, 0, 100)] + + for i, filename in enumerate(BOSS_FILES): + try: + if os.path.exists(filename): + img = pygame.image.load(filename).convert_alpha() + img = pygame.transform.scale(img, BOSS_SIZE) + bosses.append(img) + else: + raise FileNotFoundError + except: + img = create_placeholder_image(boss_colors[i % len(boss_colors)], BOSS_SIZE, f"BOSS", is_boss=True) + bosses.append(img) + return bosses + +def load_image_safe(filename, fallback_color, size=None): try: if os.path.exists(filename): img = pygame.image.load(filename) - img = pygame.transform.scale(img, (WIDTH, HEIGHT)) + if size: + img = pygame.transform.scale(img, size) + else: + img = pygame.transform.scale(img, (WIDTH, HEIGHT)) return img else: raise FileNotFoundError except: - surf = pygame.Surface((WIDTH, HEIGHT)) + surf = pygame.Surface(size if size else (WIDTH, HEIGHT)) surf.fill(fallback_color) return surf # --- RESURSU IELĀDE --- CHARACTERS = load_character_images() +BOSS_IMAGES = load_boss_images() + +# Menu bildes +MENU_PIC_WIDTH = 250 +menupic1 = load_image_safe("menupic1.png", FALLBACK_COLORS[1], (MENU_PIC_WIDTH, HEIGHT)) +menupic2 = load_image_safe("menupic2.png", FALLBACK_COLORS[0], (MENU_PIC_WIDTH, HEIGHT)) bg_images = {} +cols = {"BEACH": (194, 178, 128), "EVENINGSTREET": (30, 30, 50), "MARKET": (100, 80, 60), + "RESTAURANT": (150, 100, 50), "SAKURASTREET": (255, 183, 197), "SHIP": (0, 105, 148), + "STREETMARKETS": (90, 90, 90)} for key, path in BG_FILES.items(): - # Vienkāršotas krāsas fallback - cols = {"BEACH": (194, 178, 128), "EVENINGSTREET": (30, 30, 50), "MARKET": (100, 80, 60), - "RESTAURANT": (150, 100, 50), "SAKURASTREET": (255, 183, 197), "SHIP": (0, 105, 148), - "STREETMARKETS": (90, 90, 90)} bg_images[key] = load_image_safe(path, cols.get(key, GRAY)) +# --- BOOM. PNG IELĀDE --- +try: + if os.path.exists("boom.png"): + BOOM_IMG_ORIGINAL = pygame.image.load("boom.png").convert_alpha() + BOOM_IMG = pygame.transform.scale(BOOM_IMG_ORIGINAL, (80, 80)) + BOOM_IMG_LARGE = pygame.transform.scale(BOOM_IMG_ORIGINAL, (160, 160)) + else: + BOOM_IMG = pygame.Surface((80, 80)); BOOM_IMG.fill(YELLOW) + BOOM_IMG_LARGE = pygame.Surface((160, 160)); BOOM_IMG_LARGE.fill(ORANGE) +except: + BOOM_IMG = pygame.Surface((80, 80)); BOOM_IMG.fill(YELLOW) + BOOM_IMG_LARGE = pygame.Surface((160, 160)); BOOM_IMG_LARGE.fill(ORANGE) + # --- UI Klases --- class Button: def __init__(self, x, y, w, h, text, color, hover_color, action_code): @@ -179,7 +233,6 @@ class Button: mouse_pos = pygame.mouse.get_pos() is_hovered = self.rect.collidepoint(mouse_pos) current_color = self.hover_color if is_hovered else self.color - text_surf = FONT_SMALL.render(self.text, True, current_color) surface.blit(text_surf, (self.rect.centerx - text_surf.get_width()//2, self.rect.centery - text_surf.get_height()//2)) @@ -189,6 +242,20 @@ class Button: return True return False +# --- Palīgfunkcija Instrukciju pogām --- +def draw_key_visual(surface, x, y, w, h, key_text, action_text, color): + # Zīmē pogas rāmi + pygame.draw.rect(surface, color, (x, y, w, h), 2) + pygame.draw.rect(surface, DARK_GRAY, (x+4, y+4, w-8, h-8)) + + # Teksts uz pogas + txt_surf = FONT_SMALL.render(key_text, True, WHITE) + surface.blit(txt_surf, (x + w//2 - txt_surf.get_width()//2, y + 10)) + + # Paskaidrojums zem pogas + desc_surf = FONT_TINY.render(action_text, True, GRAY) + surface.blit(desc_surf, (x + w//2 - desc_surf.get_width()//2, y + h + 5)) + # --- Klase Spēlētājam --- class Fighter(pygame.sprite.Sprite): def __init__(self, x, y, char_data, facing_right, controls): @@ -196,14 +263,17 @@ class Fighter(pygame.sprite.Sprite): self.char_data = char_data self.image = char_data['game_img'] self.color_accent = char_data['color'] + self.start_x = x + self.start_y = y + self.facing_right_start = facing_right self.rect = self.image.get_rect() self.rect.center = (x, y) - self.rect.bottom = HEIGHT - 50 # Grīdas līmenis + self.rect.bottom = HEIGHT - 50 self.vel_y = 0 self.speed = 7 - self.jump_power = -20 # Nedaudz lielāks lecams augstums lielākam tēlam + self.jump_power = -20 self.gravity = 0.8 self.on_ground = False @@ -214,6 +284,16 @@ class Fighter(pygame.sprite.Sprite): self.attack_box = pygame.Rect(0, 0, 0, 0) self.health = 100 self.hit_timer = 0 + + def reset_round(self): + self.health = 100 + self.vel_y = 0 + self.rect.center = (self.start_x, self.start_y) + self.rect.bottom = HEIGHT - 50 + self.facing_right = self.facing_right_start + self.attacking = False + self.attack_cooldown = 0 + self.hit_timer = 0 def move(self, keys): dx = 0 @@ -244,7 +324,6 @@ class Fighter(pygame.sprite.Sprite): if self.attack_cooldown == 0: self.attacking = True self.attack_cooldown = 20 - # Uzbrukuma kaste pieregulēta lielākam tēlam if self.facing_right: self.attack_box = pygame.Rect(self.rect.right, self.rect.y + 40, 80, 80) else: @@ -253,21 +332,13 @@ class Fighter(pygame.sprite.Sprite): def update(self): if self.attack_cooldown > 0: self.attack_cooldown -= 1 else: self.attacking = False - - if self.hit_timer > 0: - self.hit_timer -= 1 + if self.hit_timer > 0: self.hit_timer -= 1 def draw(self, surface): - if self.facing_right: - img_to_draw = self.image - else: - img_to_draw = pygame.transform.flip(self.image, True, False) - + img_to_draw = pygame.transform.flip(self.image, not self.facing_right, False) surface.blit(img_to_draw, self.rect) - if self.attacking: - pygame.draw.rect(surface, YELLOW, self.attack_box) - pygame.draw.rect(surface, WHITE, self.attack_box, 2) + surface.blit(BOOM_IMG, self.attack_box) def take_damage(self, amount): self.health -= amount @@ -276,6 +347,88 @@ class Fighter(pygame.sprite.Sprite): if self.rect.centerx < WIDTH // 2: self.rect.x -= 20 else: self.rect.x += 20 +# --- Klase Bossam --- +class Boss(pygame.sprite.Sprite): + def __init__(self, image): + super().__init__() + self.image = image + self.rect = self.image.get_rect() + self.rect.center = (WIDTH // 2, HEIGHT // 2) + self.rect.bottom = HEIGHT - 50 + + self.health = 150 + self.max_health = 150 + self.speed = 3 + self.damage = 3 + + self.vel_y = 0 + self.gravity = 0.8 + self.on_ground = False + + self.facing_right = True + self.attacking = False + self.attack_cooldown = 0 + self.attack_box = pygame.Rect(0, 0, 0, 0) + self.hit_timer = 0 + + self.target = None + + def ai_move(self, targets): + closest_dist = 99999 + target = None + + for t in targets: + if t.health > 0: + dist = abs(self.rect.centerx - t.rect.centerx) + if dist < closest_dist: + closest_dist = dist + target = t + + if target: + self.target = target + if target.rect.centerx < self.rect.centerx: + self.rect.x -= self.speed + self.facing_right = False + else: + self.rect.x += self.speed + self.facing_right = True + + if closest_dist < 180: + self.attack() + + self.vel_y += self.gravity + self.rect.y += self.vel_y + if self.rect.bottom >= HEIGHT - 50: + self.rect.bottom = HEIGHT - 50 + self.vel_y = 0 + self.on_ground = True + + def attack(self): + if self.attack_cooldown == 0: + self.attacking = True + self.attack_cooldown = 45 + if self.facing_right: + self.attack_box = pygame.Rect(self.rect.right, self.rect.y + 80, 160, 160) + else: + self.attack_box = pygame.Rect(self.rect.left - 160, self.rect.y + 80, 160, 160) + + def update(self): + if self.attack_cooldown > 0: self.attack_cooldown -= 1 + else: self.attacking = False + if self.hit_timer > 0: self.hit_timer -= 1 + + def draw(self, surface): + img_to_draw = pygame.transform.flip(self.image, not self.facing_right, False) + surface.blit(img_to_draw, self.rect) + if self.attacking: + surface.blit(BOOM_IMG_LARGE, self.attack_box) + + def take_damage(self, amount): + self.health -= amount + self.hit_timer = 10 + if self.rect.centerx < WIDTH // 2: self.rect.x -= 5 + else: self.rect.x += 5 + # --- Fona Zīmēšana --- def draw_background(surface, bg_name): img = bg_images.get(bg_name, list(bg_images.values())[0]) @@ -289,35 +442,47 @@ class DialogueManager: self.active = False self.timer = 0 - def start(self): + def start(self, is_boss=False): self.active = True self.timer = pygame.time.get_ticks() - self.current_text = random.choice(self.lines) + if is_boss: + self.current_text = "BOSS APPROACHING!" + else: + self.current_text = random.choice(self.lines) def draw(self, surface): if not self.active: return - box_rect = pygame.Rect(WIDTH//2 - 300, HEIGHT//2 - 50, 600, 100) + text_surf = FONT_SMALL.render(self.current_text, True, YELLOW) + text_width = text_surf.get_width() + text_height = text_surf.get_height() + padding_x = 40 + padding_y = 25 + box_w = text_width + padding_x * 2 + box_h = text_height + padding_y * 2 + box_rect = pygame.Rect(WIDTH//2 - box_w//2, HEIGHT//2 - box_h//2, box_w, box_h) pygame.draw.rect(surface, BLACK, box_rect) pygame.draw.rect(surface, WHITE, box_rect, 4) - text_surf = FONT_MED.render(self.current_text, True, YELLOW) - surface.blit(text_surf, (box_rect.centerx - text_surf.get_width()//2, box_rect.centery - text_surf.get_height()//2)) - sub_surf = FONT_SMALL.render("Nospiediet [SPACE] lai saktu", True, WHITE) - surface.blit(sub_surf, (box_rect.centerx - sub_surf.get_width()//2, box_rect.bottom + 10)) + surface.blit(text_surf, (box_rect.centerx - text_width//2, box_rect.centery - text_height//2)) + sub_surf = FONT_TINY.render("Nospiediet [SPACE] lai saktu", True, WHITE) + surface.blit(sub_surf, (WIDTH//2 - sub_surf.get_width()//2, box_rect.bottom + 15)) # --- Galvenā Spēle --- def main(): - global STATE, P1_CHAR_INDEX, P2_CHAR_INDEX, SELECTED_BG + global STATE, P1_CHAR_INDEX, P2_CHAR_INDEX, SELECTED_BG, GAME_MODE controls_p1 = {'left': pygame.K_a, 'right': pygame.K_d, 'jump': pygame.K_w, 'attack': pygame.K_SPACE} controls_p2 = {'left': pygame.K_LEFT, 'right': pygame.K_RIGHT, 'jump': pygame.K_UP, 'attack': pygame.K_RETURN} dialogue_mgr = DialogueManager() + # Pogas (novietotas centrā, ņemot vērā malu bildes) + btn_x = WIDTH//2 - 100 menu_buttons = [ - Button(WIDTH//2 - 100, 250, 200, 50, "Sakt Cinu", BLUE, RED, "START"), - Button(WIDTH//2 - 100, 320, 200, 50, "Telu Izvele", BLUE, RED, "CHARS"), - Button(WIDTH//2 - 100, 390, 200, 50, "Fona Izvele", BLUE, RED, "BG"), - Button(WIDTH//2 - 100, 460, 200, 50, "Iziet", RED, YELLOW, "QUIT") + Button(btn_x, 180, 200, 50, "PvP", BLUE, RED, "START_PVP"), + Button(btn_x, 240, 200, 50, "Cina ar Bossu", PURPLE, ORANGE, "START_BOSS"), + Button(btn_x, 300, 200, 50, "Telu Izvele", BLUE, RED, "CHARS"), + Button(btn_x, 360, 200, 50, "Fona Izvele", BLUE, RED, "BG"), + Button(btn_x, 420, 200, 50, "Kontroles", GREEN, YELLOW, "INSTRUCTIONS"), ] bg_buttons = [ @@ -332,10 +497,19 @@ def main(): ] back_btn_chars = Button(WIDTH//2 - 100, HEIGHT - 80, 200, 50, "Atpakal", GRAY, WHITE, "BACK") + back_btn_inst = Button(WIDTH//2 - 100, HEIGHT - 80, 200, 50, "Atpakal", GRAY, WHITE, "BACK") fighter1 = None fighter2 = None - winner = "PLAYER 1" + boss = None + + p1_score = 0 + p2_score = 0 + current_round = 1 + round_winner_name = "" + match_winner_name = "" + + state_timer = 0 run = True while run: @@ -348,30 +522,43 @@ def main(): if STATE == "MENU": for btn in menu_buttons: if btn.is_clicked(event): - if btn.action_code == "START": - dialogue_mgr.start() + if btn.action_code == "START_PVP": + GAME_MODE = "PVP" + p1_score = 0 + p2_score = 0 + current_round = 1 + fighter1 = Fighter(200, 300, CHARACTERS[P1_CHAR_INDEX], True, controls_p1) + fighter2 = Fighter(800, 300, CHARACTERS[P2_CHAR_INDEX], False, controls_p2) + dialogue_mgr.start(is_boss=False) STATE = "DIALOGUE" + + elif btn.action_code == "START_BOSS": + GAME_MODE = "BOSS" + fighter1 = Fighter(150, 300, CHARACTERS[P1_CHAR_INDEX], True, controls_p1) + fighter2 = Fighter(300, 300, CHARACTERS[P2_CHAR_INDEX], True, controls_p2) + boss_img = random.choice(BOSS_IMAGES) + boss = Boss(boss_img) + dialogue_mgr.start(is_boss=True) + STATE = "DIALOGUE" + elif btn.action_code == "CHARS": STATE = "CHAR_SELECT" elif btn.action_code == "BG": STATE = "BG_SELECT" - elif btn.action_code == "QUIT": - run = False + elif btn.action_code == "INSTRUCTIONS": + STATE = "INSTRUCTIONS" elif STATE == "CHAR_SELECT": - # P1 izvēle (Augšējā rinda) icon_w = 100 gap = 10 total_w = len(CHARACTERS) * icon_w + (len(CHARACTERS)-1) * gap start_x = (WIDTH - total_w) // 2 y_p1 = 150 - for i, char in enumerate(CHARACTERS): icon_rect = pygame.Rect(start_x + i * (icon_w + gap), y_p1, icon_w, icon_w) if event.type == pygame.MOUSEBUTTONDOWN and icon_rect.collidepoint(event.pos): P1_CHAR_INDEX = i - # P2 izvēle (Apakšējā rinda) y_p2 = 350 for i, char in enumerate(CHARACTERS): icon_rect = pygame.Rect(start_x + i * (icon_w + gap), y_p2, icon_w, icon_w) @@ -388,13 +575,16 @@ def main(): STATE = "MENU" else: SELECTED_BG = btn.action_code + + elif STATE == "INSTRUCTIONS": + if back_btn_inst.is_clicked(event): + STATE = "MENU" elif STATE == "DIALOGUE": if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: dialogue_mgr.active = False - fighter1 = Fighter(200, 300, CHARACTERS[P1_CHAR_INDEX], True, controls_p1) - fighter2 = Fighter(800, 300, CHARACTERS[P2_CHAR_INDEX], False, controls_p2) - STATE = "GAME" + state_timer = pygame.time.get_ticks() + STATE = "ROUND_START" elif STATE == "GAME": if event.type == pygame.KEYDOWN: @@ -405,42 +595,88 @@ def main(): if event.type == pygame.KEYDOWN and event.key == pygame.K_r: STATE = "MENU" - # --- Zīmēšana --- + # --- Zīmēšana un Loģika --- if STATE == "MENU": SCREEN.fill(BLACK) + + # Zīmējam malu bildes (no augšas līdz lejai) + SCREEN.blit(menupic1, (0, 0)) # Kreisā puse + SCREEN.blit(menupic2, (WIDTH - MENU_PIC_WIDTH, 0)) # Labā puse + + # Virsraksts centrā title_text = "PIXEL KOMBAT" shadow_text = FONT_BIG.render(title_text, True, (180, 0, 0)) SCREEN.blit(shadow_text, (WIDTH//2 - shadow_text.get_width()//2 + 3, 53)) main_text = FONT_BIG.render(title_text, True, YELLOW) SCREEN.blit(main_text, (WIDTH//2 - main_text.get_width()//2, 50)) - sub_text = "- ULTIMATE EDITION -" - sub_surf = FONT_SMALL.render(sub_text, True, WHITE) - SCREEN.blit(sub_surf, (WIDTH//2 - sub_surf.get_width()//2, 130)) - for btn in menu_buttons: btn.draw(SCREEN) cp_text = "© PIXEL KOMBAT 2026" cp_surf = FONT_TINY.render(cp_text, True, WHITE) SCREEN.blit(cp_surf, (WIDTH//2 - cp_surf.get_width()//2, HEIGHT - 30)) - + + elif STATE == "INSTRUCTIONS": + SCREEN.fill(BLACK) + + # Virsraksts "KONTROLES" pašā augšā + title = FONT_BIG.render("KONTROLES", True, YELLOW) + SCREEN.blit(title, (WIDTH//2 - title.get_width()//2, 30)) + + # --- Izkārtojuma mainīgie --- + # Centri katrai kolonnai (simetriski attiecībā pret ekrāna centru) + p1_center_x = WIDTH // 2 - 200 + p2_center_x = WIDTH // 2 + 200 + + # Y pozīcijas (palielinātas atstarpes) + title_y = 170 # Kur sākas "PLAYER X" teksts + key_row1_y = 240 # Augšējā poga (W / UP) + key_row2_y = 320 # Vidējās pogas (A,D / LEFT, RIGHT) + key_row3_y = 420 # Apakšējā poga (SPACE / ENTER) + + # --- PLAYER 1 (Kreisā puse) --- + p1_title = FONT_MED.render("PLAYER 1", True, BLUE) + # Centrējam tekstu virs pogu kolonnas + SCREEN.blit(p1_title, (p1_center_x - p1_title.get_width()//2, title_y)) + + # W (Jump) - centrēta + draw_key_visual(SCREEN, p1_center_x - 35, key_row1_y, 70, 50, "W", "LEKT", BLUE) + # A (Left) - pa kreisi no centra + draw_key_visual(SCREEN, p1_center_x - 35 - 80, key_row2_y, 70, 50, "A", "KREISI", BLUE) + # D (Right) - pa labi no centra + draw_key_visual(SCREEN, p1_center_x - 35 + 80, key_row2_y, 70, 50, "D", "LABI", BLUE) + # SPACE (Hit) - plata poga apakšā, centrēta + space_w = 200 + draw_key_visual(SCREEN, p1_center_x - space_w//2, key_row3_y, space_w, 50, "SPACE", "SIT", RED) + + # --- PLAYER 2 (Labā puse) --- + p2_title = FONT_MED.render("PLAYER 2", True, RED) + # Centrējam tekstu virs pogu kolonnas + SCREEN.blit(p2_title, (p2_center_x - p2_title.get_width()//2, title_y)) + + # UP (Jump) + draw_key_visual(SCREEN, p2_center_x - 35, key_row1_y, 70, 50, "UP", "LEKT", RED) + # LEFT (Left) + draw_key_visual(SCREEN, p2_center_x - 35 - 80, key_row2_y, 70, 50, "LEFT", "KREISI", RED) + # RIGHT (Right) + draw_key_visual(SCREEN, p2_center_x - 35 + 80, key_row2_y, 70, 50, "RIGHT", "LABI", RED) + # ENTER (Hit) + enter_w = 150 + draw_key_visual(SCREEN, p2_center_x - enter_w//2, key_row3_y, enter_w, 50, "ENTER", "SIT", BLUE) + + back_btn_inst.draw(SCREEN) + elif STATE == "CHAR_SELECT": SCREEN.fill(DARK_GRAY) - - # Teksts title1 = FONT_SMALL.render("IZVELIES TELU", True, WHITE) SCREEN.blit(title1, (WIDTH//2 - title1.get_width()//2, 30)) - title2 = FONT_TINY.render("(Uzspied uz ikonas)", True, GRAY) - SCREEN.blit(title2, (WIDTH//2 - title2.get_width()//2, 60)) - - # Ikonu izmēri un novietojums + icon_w = 100 gap = 10 total_w = len(CHARACTERS) * icon_w + (len(CHARACTERS)-1) * gap start_x = (WIDTH - total_w) // 2 - # P1 Zīmēšana p1_text = FONT_SMALL.render("PLAYER 1", True, CHARACTERS[P1_CHAR_INDEX]['color']) SCREEN.blit(p1_text, (WIDTH//2 - p1_text.get_width()//2, 110)) @@ -452,7 +688,6 @@ def main(): border_width = 2 if i != P1_CHAR_INDEX else 4 pygame.draw.rect(SCREEN, border_color, icon_rect, border_width) - # P2 Zīmēšana p2_text = FONT_SMALL.render("PLAYER 2", True, CHARACTERS[P2_CHAR_INDEX]['color']) SCREEN.blit(p2_text, (WIDTH//2 - p2_text.get_width()//2, 300)) @@ -476,7 +711,42 @@ def main(): elif STATE == "DIALOGUE": draw_background(SCREEN, SELECTED_BG) dialogue_mgr.draw(SCREEN) + + elif STATE == "ROUND_START": + draw_background(SCREEN, SELECTED_BG) + fighter1.draw(SCREEN) + fighter2.draw(SCREEN) + if GAME_MODE == "BOSS" and boss: + boss.draw(SCREEN) + if GAME_MODE == "BOSS" and boss: + pygame.draw.rect(SCREEN, BLACK, (WIDTH//2 - 302, 18, 604, 34)) + pygame.draw.rect(SCREEN, RED, (WIDTH//2 - 300, 20, 600, 30)) + w = (boss.health / boss.max_health) * 600 + pygame.draw.rect(SCREEN, PURPLE, (WIDTH//2 - 300, 20, w, 30)) + pygame.draw.rect(SCREEN, WHITE, (WIDTH//2 - 300, 20, 600, 30), 2) + else: + pygame.draw.rect(SCREEN, BLACK, (20, 20, 400, 40)) + pygame.draw.rect(SCREEN, fighter1.color_accent, (20, 20, 400, 40)) + pygame.draw.rect(SCREEN, WHITE, (20, 20, 400, 40), 2) + + pygame.draw.rect(SCREEN, BLACK, (WIDTH-420, 20, 400, 40)) + pygame.draw.rect(SCREEN, fighter2.color_accent, (WIDTH-420, 20, 400, 40)) + pygame.draw.rect(SCREEN, WHITE, (WIDTH-420, 20, 400, 40), 2) + + elapsed = pygame.time.get_ticks() - state_timer + + if elapsed < 1500: + txt = FONT_MED.render(f"ROUND {current_round}" if GAME_MODE == "PVP" else "BOSS FIGHT", True, YELLOW) + SCREEN.blit(txt, (WIDTH//2 - txt.get_width()//2, HEIGHT//2 - 50)) + elif elapsed < 3000: + txt = FONT_BIG.render("FIGHT!", True, WHITE) + offset_x = random.randint(-2, 2) + offset_y = random.randint(-2, 2) + SCREEN.blit(txt, (WIDTH//2 - txt.get_width()//2 + offset_x, HEIGHT//2 - 50 + offset_y)) + else: + STATE = "GAME" + elif STATE == "GAME": keys = pygame.key.get_pressed() @@ -485,37 +755,148 @@ def main(): fighter1.update() fighter2.update() - if fighter1.attacking and fighter1.attack_box.colliderect(fighter2.rect): - if fighter2.hit_timer == 0: fighter2.take_damage(5) - if fighter2.attacking and fighter2.attack_box.colliderect(fighter1.rect): - if fighter1.hit_timer == 0: fighter1.take_damage(5) + if GAME_MODE == "PVP": + if fighter1.attacking and fighter1.attack_box.colliderect(fighter2.rect): + if fighter2.hit_timer == 0: fighter2.take_damage(5) + if fighter2.attacking and fighter2.attack_box.colliderect(fighter1.rect): + if fighter1.hit_timer == 0: fighter1.take_damage(5) + + if fighter1.health <= 0 or fighter2.health <= 0: + if fighter2.health <= 0: + p1_score += 1 + round_winner_name = "PLAYER 1" + else: + p2_score += 1 + round_winner_name = "PLAYER 2" + state_timer = pygame.time.get_ticks() + STATE = "ROUND_OVER" + + elif GAME_MODE == "BOSS" and boss: + targets = [fighter1, fighter2] + boss.ai_move(targets) + boss.update() - if fighter1.health <= 0 or fighter2.health <= 0: - STATE = "GAMEOVER" - winner = "PLAYER 1" if fighter2.health <= 0 else "PLAYER 2" + if fighter1.attacking and fighter1.attack_box.colliderect(boss.rect): + if boss.hit_timer == 0: boss.take_damage(5) + if fighter2.attacking and fighter2.attack_box.colliderect(boss.rect): + if boss.hit_timer == 0: boss.take_damage(5) + + if boss.attacking: + if boss.attack_box.colliderect(fighter1.rect): + if fighter1.hit_timer == 0: fighter1.take_damage(boss.damage) + if boss.attack_box.colliderect(fighter2.rect): + if fighter2.hit_timer == 0: fighter2.take_damage(boss.damage) + + if boss.health <= 0: + match_winner_name = "PLAYERS" + save_highscore("Players (Team)") + STATE = "GAMEOVER" + + if fighter1.health <= 0 and fighter2.health <= 0: + match_winner_name = "BOSS" + STATE = "GAMEOVER" draw_background(SCREEN, SELECTED_BG) + fighter1.draw(SCREEN) + fighter2.draw(SCREEN) + if GAME_MODE == "BOSS" and boss: + boss.draw(SCREEN) + + if GAME_MODE == "BOSS" and boss: + pygame.draw.rect(SCREEN, BLACK, (WIDTH//2 - 302, 18, 604, 34)) + pygame.draw.rect(SCREEN, RED, (WIDTH//2 - 300, 20, 600, 30)) + w = (boss.health / boss.max_health) * 600 + pygame.draw.rect(SCREEN, PURPLE, (WIDTH//2 - 300, 20, w, 30)) + pygame.draw.rect(SCREEN, WHITE, (WIDTH//2 - 300, 20, 600, 30), 2) + + pygame.draw.rect(SCREEN, BLACK, (20, HEIGHT - 60, 200, 20)) + pygame.draw.rect(SCREEN, fighter1.color_accent, (20, HEIGHT - 60, (fighter1.health/100)*200, 20)) + pygame.draw.rect(SCREEN, WHITE, (20, HEIGHT - 60, 200, 20), 2) + + pygame.draw.rect(SCREEN, BLACK, (WIDTH-220, HEIGHT - 60, 200, 20)) + pygame.draw.rect(SCREEN, fighter2.color_accent, (WIDTH-220, HEIGHT - 60, (fighter2.health/100)*200, 20)) + pygame.draw.rect(SCREEN, WHITE, (WIDTH-220, HEIGHT - 60, 200, 20), 2) + + else: + pygame.draw.rect(SCREEN, BLACK, (20, 20, 400, 40)) + pygame.draw.rect(SCREEN, RED, (20, 20, 400, 40)) + pygame.draw.rect(SCREEN, fighter1.color_accent, (20, 20, (fighter1.health/100)*400, 40)) + pygame.draw.rect(SCREEN, WHITE, (20, 20, 400, 40), 2) + + pygame.draw.rect(SCREEN, BLACK, (WIDTH-420, 20, 400, 40)) + pygame.draw.rect(SCREEN, RED, (WIDTH-420, 20, 400, 40)) + pygame.draw.rect(SCREEN, fighter2.color_accent, (WIDTH-420, 20, (fighter2.health/100)*400, 40)) + pygame.draw.rect(SCREEN, WHITE, (WIDTH-420, 20, 400, 40), 2) + + score_text = FONT_MED.render(f"{p1_score} - {p2_score}", True, WHITE) + SCREEN.blit(score_text, (WIDTH//2 - score_text.get_width()//2, 25)) + + elif STATE == "ROUND_OVER": + draw_background(SCREEN, SELECTED_BG) fighter1.draw(SCREEN) fighter2.draw(SCREEN) - # Health bars (saglabāts vecais stils) pygame.draw.rect(SCREEN, BLACK, (20, 20, 400, 40)) - pygame.draw.rect(SCREEN, RED, (20, 20, 400, 40)) - pygame.draw.rect(SCREEN, fighter1.color_accent, (20, 20, (fighter1.health/100)*400, 40)) + pygame.draw.rect(SCREEN, fighter1.color_accent, (20, 20, 400, 40)) pygame.draw.rect(SCREEN, WHITE, (20, 20, 400, 40), 2) pygame.draw.rect(SCREEN, BLACK, (WIDTH-420, 20, 400, 40)) - pygame.draw.rect(SCREEN, RED, (WIDTH-420, 20, 400, 40)) - pygame.draw.rect(SCREEN, fighter2.color_accent, (WIDTH-420, 20, (fighter2.health/100)*400, 40)) + pygame.draw.rect(SCREEN, fighter2.color_accent, (WIDTH-420, 20, 400, 40)) pygame.draw.rect(SCREEN, WHITE, (WIDTH-420, 20, 400, 40), 2) + score_text = FONT_MED.render(f"{p1_score} - {p2_score}", True, WHITE) + SCREEN.blit(score_text, (WIDTH//2 - score_text.get_width()//2, 25)) + + txt1 = FONT_MED.render(f"{round_winner_name} WINS!", True, YELLOW) + SCREEN.blit(txt1, (WIDTH//2 - txt1.get_width()//2, HEIGHT//2 - 50)) + + elapsed = pygame.time.get_ticks() - state_timer + + if elapsed > 2000: + if p1_score == 3 or p2_score == 3: + match_winner_name = "PLAYER 1" if p1_score == 3 else "PLAYER 2" + save_highscore(match_winner_name) + STATE = "GAMEOVER" + else: + current_round += 1 + fighter1.reset_round() + fighter2.reset_round() + state_timer = pygame.time.get_ticks() + STATE = "ROUND_START" + elif STATE == "GAMEOVER": SCREEN.fill(BLACK) - txt = FONT_BIG.render(f"{winner} WINS!", True, YELLOW) - restart = FONT_SMALL.render("Nospied [R] atgriezties uz izvelni", True, WHITE) - SCREEN.blit(txt, (WIDTH//2 - txt.get_width()//2, HEIGHT//2 - 50)) - SCREEN.blit(restart, (WIDTH//2 - restart.get_width()//2, HEIGHT//2 + 50)) + + if GAME_MODE == "BOSS": + if match_winner_name == "PLAYERS": + txt = FONT_BIG.render("PLAYERS WIN!", True, GREEN) + else: + txt = FONT_BIG.render("PLAYERS LOSE!", True, RED) + SCREEN.blit(txt, (WIDTH//2 - txt.get_width()//2, 150)) + else: + txt = FONT_BIG.render(f"{match_winner_name} WINS!", True, YELLOW) + SCREEN.blit(txt, (WIDTH//2 - txt.get_width()//2, 100)) + + hs_title = FONT_MED.render("HIGH SCORES", True, WHITE) + SCREEN.blit(hs_title, (WIDTH//2 - hs_title.get_width()//2, 200)) + + scores = load_highscores() + scores.reverse() + + y_offset = 260 + for i, score in enumerate(scores[:5]): + if score == match_winner_name: + color = YELLOW + else: + color = WHITE + + s_text = FONT_SMALL.render(f"{i+1}. {score}", True, color) + SCREEN.blit(s_text, (WIDTH//2 - s_text.get_width()//2, y_offset)) + y_offset += 35 + + restart = FONT_SMALL.render("Nospied [R] atgriezties uz izvelni", True, GRAY) + SCREEN.blit(restart, (WIDTH//2 - restart.get_width()//2, HEIGHT - 80)) pygame.display.update()