Загрузка данных


import random
import sqlite3
import pgzrun
import os
import math

# ─── Диагностика: проверка файлов ───
def verify_assets():
    required_images = ['meni.png', 'mon.png', 'map.png']
    missing = []
    for img in required_images:
        path = os.path.join('images', img)
        if not os.path.exists(path):
            missing.append(img)
    if missing:
        print(f"Не найдены файлы: {missing}")
        print("Убедитесь, что папка 'images' с файлами лежит рядом с game.py")
        return False
    return True

if not verify_assets():
    exit()

# ─── Настройки окна ───
WIDTH = 1280
HEIGHT = 720

# ─── База данных ───
def init_db():
    conn = sqlite3.connect('game.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS player_stats
                 (id INTEGER PRIMARY KEY, hp INTEGER, damage INTEGER)''')
    c.execute('SELECT COUNT(*) FROM player_stats')
    if c.fetchone()[0] == 0:
        c.execute('INSERT INTO player_stats (hp, damage) VALUES (300, 15)')
    conn.commit()
    conn.close()

def load_stats():
    conn = sqlite3.connect('game.db')
    c = conn.cursor()
    c.execute('SELECT hp, damage FROM player_stats WHERE id = 1')
    hp, damage = c.fetchone()
    conn.close()
    return hp, damage

def save_stats(hp, damage):
    conn = sqlite3.connect('game.db')
    c = conn.cursor()
    c.execute('UPDATE player_stats SET hp = ?, damage = ? WHERE id = 1', (hp, damage))
    conn.commit()
    conn.close()

# ─── Инициализация ───
init_db()
player_hp, player_damage = load_stats()

try:
    player = Actor('meni', (100, 100))
    background = Actor('map', (WIDTH // 2, HEIGHT // 2))
except Exception as e:
    print(f"Критическая ошибка загрузки изображений: {e}")
    exit()

monster = None  # глобальная переменная: пока нет монстра

# ─── Константы ───
PLAYER_SPEED = 5
MONSTER_SPEED = 1.5
ATTACK_RANGE = 120
MONSTER_ATTACK_RANGE = 60
MONSTER_ATTACK_COOLDOWN = 90
MONSTER_DAMAGE_MIN = 8
MONSTER_DAMAGE_MAX = 18
BASE_MONSTER_HP = 500
BASE_PLAYER_HP = 300
BASE_PLAYER_DAMAGE = 15
SHIELD_DURATION_MAX = 300
SHIELD_REDUCTION = 0.5
COLLISION_DAMAGE = player_damage // 2
COLLISION_KNOCKBACK = 0
COLLISION_COOLDOWN_FRAMES = 30
SPAWN_DELAY_FRAMES = 30  # небольшая пауза после смерти монстра

# ─── Переменные игры ───
game_state = 'start'
monster_hp = 0
score = 0
monster_attack_timer = 0
monster_initial_delay = 60
has_shield = False
has_gun = True
shield_timer = 0
last_collision_frame = -100
frame_counter = 0
spawn_delay_counter = 0  # счётчик задержки перед спавном нового монстра

# ─── Вспомогательные функции ───
def spawn_monster():
    global monster, monster_hp
    # ищем позицию подальше от игрока
    x = random.randint(100, WIDTH - 100)
    y = random.randint(100, HEIGHT - 100)
    while math.hypot(x - player.x, y - player.y) < 400:  # минимум 400 px от игрока
        x = random.randint(100, WIDTH - 100)
        y = random.randint(100, HEIGHT - 100)

    monster = Actor('mon', (x, y))
    monster_hp = BASE_MONSTER_HP + score * 150
    print(f"[DEBUG] Спавн монстра: HP={monster_hp}, позиция=({x}, {y})")

def move_monster_toward_player():
    if monster is None:
        return
    dx = player.x - monster.x
    dy = player.y - monster.y
    dist = math.hypot(dx, dy)
    if dist > 0:
        dx /= dist
        dy /= dist
        monster.x += dx * MONSTER_SPEED
        monster.y += dy * MONSTER_SPEED

# ─── Отрисовка ───
def draw():
    screen.clear()
    background.draw()

    if game_state == 'start':
        screen.draw.text(
            "НАЖМИТЕ ПРОБЕЛ ДЛЯ НАЧАЛА ИГРЫ",
            center=(WIDTH // 2, HEIGHT // 2 - 50),
            color='white', fontsize=36
        )
        screen.draw.text(
            "F11 — ПОЛНОЭКРАННЫЙ РЕЖИМ",
            center=(WIDTH // 2, HEIGHT // 2 + 50),
            color='yellow', fontsize=24
        )

    elif game_state == 'playing':
        player.draw()
        if monster is not None and monster_hp > 0:
            monster.draw()
            screen.draw.text(
                f"HP: {monster_hp}",
                (monster.x - 20, monster.y - 50),
                color='red', fontsize=20
            )

        # HUD игрока
        screen.draw.text(f"HP: {player_hp}", (10, 10), color='red', fontsize=24)
        screen.draw.text(f"УРОН: {player_damage}", (10, 40), color='yellow', fontsize=24)
        screen.draw.text(f"МОНСТРОВ УБИТО: {score}", (10, 70), color='green', fontsize=24)

        y_offset = 100
        if has_shield:
            screen.draw.text(
                f"ЩИТ АКТИВЕН ({shield_timer // 60 + 1}с)",
                (10, y_offset), color='blue', fontsize=20
            )
            y_offset += 30
        if not has_gun:
            screen.draw.text("ОРУЖИЕ УБРАНО", (10, y_offset), color='orange', fontsize=20)

    elif game_state == 'game_over':
        screen.draw.text(
            "ИГРА ОКОНЧЕНА! НАЖМИТЕ R ДЛЯ ПЕРЕЗАПУСКА",
            center=(WIDTH // 2, HEIGHT // 2),
            color='white', fontsize=30
        )
        screen.draw.text(
            f"Финальный счёт: {score}",
            center=(WIDTH // 2, HEIGHT // 2 + 50),
            color='yellow', fontsize=28
        )

# ─── Обновление ───
def update():
    global player_hp, monster_hp, score, game_state
    global monster_attack_timer, monster_initial_delay
    global has_shield, has_gun, shield_timer
    global player_damage, last_collision_frame, frame_counter
    global spawn_delay_counter, monster

    frame_counter += 1

    if game_state != 'playing':
        return

    # Щит
    if has_shield and shield_timer > 0:
        shield_timer -= 1
        if shield_timer <= 0:
            has_shield = False

    # Движение игрока
    if keyboard.left and player.x > 30:
        player.x -= PLAYER_SPEED
    if keyboard.right and player.x < WIDTH - 30:
        player.x += PLAYER_SPEED
    if keyboard.up and player.y > 30:
        player.y -= PLAYER_SPEED
    if keyboard.down and player.y < HEIGHT - 30:
        player.y += PLAYER_SPEED

    # Столкновение и урон от касания
    if monster is not None and monster_hp > 0:
        dist_to_monster = math.hypot(player.x - monster.x, player.y - monster.y)

        if dist_to_monster <= COLLISION_DAMAGE:
            if frame_counter - last_collision_frame >= COLLISION_COOLDOWN_FRAMES:
                last_collision_frame = frame_counter
                monster_hp -= COLLISION_DAMAGE

    # Движение монстра к игроку
    if monster is not None and monster_hp > 0:
        move_monster_toward_player()

        # Атака монстра
        dist_to_player = math.hypot(monster.x - player.x, monster.y - player.y)

        if monster_initial_delay > 0:
            monster_initial_delay -= 1
        elif dist_to_player <= MONSTER_ATTACK_RANGE:
            if monster_attack_timer <= 0:
                damage = random.randint(MONSTER_DAMAGE_MIN, MONSTER_DAMAGE_MAX)
                if has_shield:
                    damage = max(1, int(damage * SHIELD_REDUCTION))
                player_hp -= damage
                monster_attack_timer = MONSTER_ATTACK_COOLDOWN
            else:
                monster_attack_timer -= 1
        else:
            if monster_attack_timer > 0:
                monster_attack_timer -= 1

    # Логика спавна нового монстра после смерти
    if spawn_delay_counter > 0:
        spawn_delay_counter -= 1
        # Во время задержки можно, например, мигать надписью «НОВЫЙ МОНСТР…»
        if spawn_delay_counter == 0 and game_state == 'playing':
            spawn_monster()

    # Смерть игрока
    if player_hp <= 0:
        player_hp = 0
        game_state = 'game_over'
        save_stats(player_hp, player_damage)

# ─── Клик мыши (атака игрока) ───
def on_mouse_down(pos, button):
    global monster_hp, score, player_damage, monster_attack_timer, monster_initial_delay, monster, spawn_delay_counter

    if game_state != 'playing':
        return
    if not has_gun or monster is None or monster_hp <= 0:
        return
    if button != mouse.LEFT:
        return

    dist = math.hypot(player.x - monster.x, player.y - monster.y)
    if dist > ATTACK_RANGE:
        return

    monster_hp -= player_damage

    if monster_hp <= 0:
        monster_hp = 0
        score += 1
        if score % 3 == 0:
            player_damage += 1

        # Ставим задержку перед спавном, чтобы игрок увидел смерть монстра
        spawn_delay_counter = SPAWN_DELAY_FRAMES
        monster_attack_timer = 0
        monster_initial_delay = 60
        print("[DEBUG] Монстр убит. Запущена задержка спавна.")

# ─── Клавиши ───
def on_key_down(key):
    global game_state, player_hp, player_damage, score, monster_hp
    global monster_attack_timer, monster_initial_delay
    global has_shield, has_gun, shield_timer, monster, spawn_delay_counter

    if key == keys.SPACE and game_state == 'start':
        game_state = 'playing'
        monster_initial_delay = 60
        monster_attack_timer = 0
        spawn_monster()  # первый монстр при старте

    elif key == keys.R and game_state == 'game_over':
        game_state = 'start'
        player_hp = BASE_PLAYER_HP
        player_damage = BASE_PLAYER_DAMAGE
        score = 0
        monster_hp = 0
        monster_attack_timer = 0
        monster_initial_delay = 60
        has_shield = False
        has_gun = True
        shield_timer = 0
        spawn_delay_counter = 0
        save_stats(player_hp, player_damage)
        player.pos = (100, 100)
        monster = None

    elif key == keys.M and game_state == 'playing':
        has_shield = True
        shield_timer = SHIELD_DURATION_MAX

    elif key == keys.L and game_state == 'playing':
        has_gun = not has_gun

pgzrun.go()