Загрузка данных
import pygame
import random
import math
import sys
pygame.init()
# ----------------- НАСТРОЙКИ ЭКРАНА И МИРА -----------------
SCREEN_W, SCREEN_H = 1100, 720
WORLD_W, WORLD_H = 3200, 2400
FPS = 60
screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
pygame.display.set_caption("The Witcher 2D: Hunt for the Leshen")
clock = pygame.time.Clock()
font_s = pygame.font.SysFont("georgia", 13, bold=True)
font_m = pygame.font.SysFont("georgia", 16, bold=True)
font_l = pygame.font.SysFont("georgia", 24, bold=True)
font_boss = pygame.font.SysFont("georgia", 20, bold=True)
# ----------------- КАМЕРА С ПЛАВНЫМ СЛЕДОВАНИЕМ -----------------
class Camera:
def __init__(self):
self.x = 0
self.y = 0
self.shake = 0
def follow(self, target_pos):
target_x = target_pos[0] - SCREEN_W // 2
target_y = target_pos[1] - SCREEN_H // 2
# Плавная интерполяция
self.x += (target_x - self.x) * 0.1
self.y += (target_y - self.y) * 0.1
self.x = max(0, min(self.x, WORLD_W - SCREEN_W))
self.y = max(0, min(self.y, WORLD_H - SCREEN_H))
if self.shake > 0:
self.x += random.randint(-self.shake, self.shake)
self.y += random.randint(-self.shake, self.shake)
self.shake -= 1
def apply(self, rect):
return rect.move(-int(self.x), -int(self.y))
def apply_pos(self, pos):
return (int(pos[0] - self.x), int(pos[1] - self.y))
camera = Camera()
# ----------------- ВИЗУАЛЬНЫЕ ЭФФЕКТЫ И ЧАСТИЦЫ -----------------
class FloatingText:
def __init__(self, x, y, text, color, size=18):
self.x, self.y = x, y
self.text = text
self.color = color
self.life = 45
self.f = pygame.font.SysFont("georgia", size, bold=True)
def update(self):
self.y -= 0.8
self.life -= 1
def draw(self, surf):
r = self.f.render(self.text, True, self.color)
surf.blit(r, camera.apply_pos((self.x - r.get_width()//2, self.y)))
class BloodDecal:
def __init__(self, x, y):
self.x, self.y = x, y
self.size = random.randint(10, 20)
self.color = (random.randint(100, 140), 10, 15)
def draw(self, surf):
pos = camera.apply_pos((self.x, self.y))
pygame.draw.circle(surf, self.color, pos, self.size)
class Particle:
def __init__(self, x, y, vx, vy, color, radius, decay, p_type="circle"):
self.x, self.y = x, y
self.vx, self.vy = vx, vy
self.color = color
self.radius = radius
self.decay = decay
self.p_type = p_type
def update(self):
self.x += self.vx
self.y += self.vy
self.radius -= self.decay
def draw(self, surf):
if self.radius > 0:
p = camera.apply_pos((self.x, self.y))
if self.p_type == "circle":
pygame.draw.circle(surf, self.color, p, int(self.radius))
elif self.p_type == "glow":
s = pygame.Surface((int(self.radius*4), int(self.radius*4)), pygame.SRCALPHA)
pygame.draw.circle(s, (*self.color, 90), (int(self.radius*2), int(self.radius*2)), int(self.radius*2))
surf.blit(s, (p[0] - int(self.radius*2), p[1] - int(self.radius*2)))
# ----------------- ВЕДЬМАК (ГЕРАЛЬТ) -----------------
class Witcher:
def __init__(self, x, y):
self.rect = pygame.Rect(x, y, 36, 48)
self.max_hp = 150
self.hp = 150
self.stamina = 100
self.max_stamina = 100
self.toxicity = 0
self.speed = 4.2
self.facing = pygame.Vector2(0, 1)
# Бой
self.sword_type = "SILVER" # "SILVER" или "STEEL"
self.combo = 0
self.combo_timer = 0
self.is_attacking = False
self.attack_hitbox = pygame.Rect(0, 0, 0, 0)
self.dodge_timer = 0
self.quen_shield = False
self.sense_active = False
# Зелья
self.potions = 3
def handle_input(self, keys, mouse_pressed):
# Движение
move = pygame.Vector2(0, 0)
if keys[pygame.K_w]: move.y -= 1
if keys[pygame.K_s]: move.y += 1
if keys[pygame.K_a]: move.x -= 1
if keys[pygame.K_d]: move.x += 1
if move.length() > 0:
self.facing = move.normalize()
# Ведьмачье чутье (ПКМ)
self.sense_active = mouse_pressed[2]
# Скорость
spd = self.speed * 2.2 if self.dodge_timer > 0 else (self.speed * 0.7 if self.sense_active else self.speed)
return move.normalize() * spd if move.length() > 0 else pygame.Vector2(0, 0)
def update(self, move_vec, obstacles):
# Реген выносливости
if self.stamina < self.max_stamina:
self.stamina = min(self.max_stamina, self.stamina + 0.4)
# Токсичность спадает или травит
if self.toxicity > 0:
self.toxicity = max(0, self.toxicity - 0.04)
if self.toxicity > 80:
self.hp -= 0.08 # Интоксикация наносит урон
if self.dodge_timer > 0:
self.dodge_timer -= 1
if self.combo_timer > 0:
self.combo_timer -= 1
if self.combo_timer == 0:
self.is_attacking = False
self.attack_hitbox = pygame.Rect(0, 0, 0, 0)
# Коллизии
self.rect.x += int(move_vec.x)
for obs in obstacles:
if self.rect.colliderect(obs):
if move_vec.x > 0: self.rect.right = obs.left
if move_vec.x < 0: self.rect.left = obs.right
self.rect.y += int(move_vec.y)
for obs in obstacles:
if self.rect.colliderect(obs):
if move_vec.y > 0: self.rect.bottom = obs.top
if move_vec.y < 0: self.rect.top = obs.bottom
self.rect.clamp_ip(pygame.Rect(0, 0, WORLD_W, WORLD_H))
def attack(self):
if not self.is_attacking and self.stamina >= 12:
self.is_attacking = True
self.stamina -= 12
self.combo = (self.combo % 3) + 1
self.combo_timer = 16
reach = 45
w, h = (55, 55)
ax = self.rect.centerx + int(self.facing.x * reach) - w // 2
ay = self.rect.centery + int(self.facing.y * reach) - h // 2
self.attack_hitbox = pygame.Rect(ax, ay, w, h)
def drink_potion(self, texts):
if self.potions > 0 and self.toxicity <= 85:
self.potions -= 1
self.hp = min(self.max_hp, self.hp + 50)
self.toxicity = min(100, self.toxicity + 35)
texts.append(FloatingText(self.rect.centerx, self.rect.top, "+50 HP (Ласточка)", (50, 255, 120), 16))
elif self.toxicity > 85:
texts.append(FloatingText(self.rect.centerx, self.rect.top, "Слишком высокая токсичность!", (255, 60, 60), 14))
def draw(self, surf):
r = camera.apply(self.rect)
# Тень под ногами
shadow = pygame.Surface((34, 14), pygame.SRCALPHA)
pygame.draw.ellipse(shadow, (0, 0, 0, 100), (0, 0, 34, 14))
surf.blit(shadow, (r.centerx - 17, r.bottom - 8))
# Тело (Куртка Школы Волка)
pygame.draw.rect(surf, (45, 40, 35), r, border_radius=4)
pygame.draw.rect(surf, (90, 45, 30), (r.x + 4, r.y + 10, r.w - 8, 20)) # Кожаные вставки
# Мечи за спиной (Стальной и Серебряный крестом)
pygame.draw.line(surf, (160, 160, 170), (r.left + 2, r.top + 2), (r.right - 2, r.bottom - 10), 3)
pygame.draw.line(surf, (215, 230, 245), (r.right - 2, r.top + 2), (r.left + 2, r.bottom - 10), 3)
# Белые волосы Ведьмака
pygame.draw.circle(surf, (230, 230, 240), (r.centerx, r.top + 5), 10)
# Глаза ведьмака (янтарные кошачьи)
look_pos = (r.centerx + int(self.facing.x * 4), r.top + 5 + int(self.facing.y * 3))
pygame.draw.circle(surf, (255, 200, 0), look_pos, 2)
# Щит Квен
if self.quen_shield:
shield_surf = pygame.Surface((80, 80), pygame.SRCALPHA)
pygame.draw.circle(shield_surf, (255, 215, 0, 110), (40, 40), 36, 4)
surf.blit(shield_surf, (r.centerx - 40, r.centery - 40))
# Эффект удара мечом
if self.is_attacking:
atk_r = camera.apply(self.attack_hitbox)
col = (200, 230, 255) if self.sword_type == "SILVER" else (220, 200, 160)
pygame.draw.arc(surf, col, atk_r, 0, 3.1415, 5)
# ----------------- МОНСТРЫ: УТОПЕЦ И ЛЕШИЙ (БОСС) -----------------
class Drowner:
def __init__(self, x, y):
self.rect = pygame.Rect(x, y, 32, 40)
self.hp = 70
self.max_hp = 70
self.speed = 2.8
self.hit_cd = 0
self.slow_timer = 0
def update(self, player, texts, particles, decals):
if self.slow_timer > 0:
self.slow_timer -= 1
spd = self.speed * 0.4 if self.slow_timer > 0 else self.speed
self.hit_cd = max(0, self.hit_cd - 1)
v = pygame.Vector2(player.rect.center) - pygame.Vector2(self.rect.center)
dist = v.length()
if 20 < dist < 500:
d = v.normalize()
self.rect.x += int(d.x * spd)
self.rect.y += int(d.y * spd)
# Удар
if self.rect.colliderect(player.rect) and self.hit_cd == 0 and player.dodge_timer == 0:
self.hit_cd = 45
if player.quen_shield:
player.quen_shield = False
camera.shake = 5
texts.append(FloatingText(player.rect.centerx, player.rect.top, "КВЕН ПОГЛОТИЛ УДАР", (255, 220, 50), 16))
else:
dmg = random.randint(18, 25)
player.hp -= dmg
camera.shake = 8
texts.append(FloatingText(player.rect.centerx, player.rect.top, f"-{dmg}", (255, 50, 50), 20))
decals.append(BloodDecal(player.rect.centerx, player.rect.centery))
def draw(self, surf, sense_active):
r = camera.apply(self.rect)
color = (40, 110, 120)
# Тень
pygame.draw.ellipse(surf, (0, 0, 0, 90), (r.centerx - 14, r.bottom - 6, 28, 12))
pygame.draw.rect(surf, color, r, border_radius=5)
# Глаза монстра
pygame.draw.circle(surf, (255, 60, 60), (r.centerx - 4, r.y + 8), 2)
pygame.draw.circle(surf, (255, 60, 60), (r.centerx + 4, r.y + 8), 2)
# Подсветка Ведьмачьим чутьем
if sense_active:
glow = pygame.Surface((r.w + 14, r.h + 14), pygame.SRCALPHA)
pygame.draw.rect(glow, (255, 120, 0, 130), glow.get_rect(), width=3, border_radius=6)
surf.blit(glow, (r.x - 7, r.y - 7))
# Полоса HP
pct = max(0, self.hp / self.max_hp)
pygame.draw.rect(surf, (20, 20, 20), (r.x, r.y - 8, self.rect.w, 4))
pygame.draw.rect(surf, (220, 40, 40), (r.x, r.y - 8, int(self.rect.w * pct), 4))
class LeshenBoss:
def __init__(self, x, y):
self.rect = pygame.Rect(x, y, 64, 90)
self.max_hp = 550
self.hp = 550
self.state = "CHASE"
self.teleport_cd = 180
self.root_spell_cd = 120
self.slow_timer = 0
self.alive = True
def update(self, player, roots, crows, texts, particles):
if self.hp <= 0:
self.alive = False
return
self.teleport_cd -= 1
self.root_spell_cd -= 1
if self.slow_timer > 0: self.slow_timer -= 1
spd = 1.6 if self.slow_timer == 0 else 0.7
v = pygame.Vector2(player.rect.center) - pygame.Vector2(self.rect.center)
dist = v.length()
# Телепортация в тумане
if self.teleport_cd <= 0 and dist < 700:
self.teleport_cd = 240
for _ in range(40):
particles.append(Particle(self.rect.centerx, self.rect.centery, random.uniform(-4, 4), random.uniform(-4, 4), (40, 40, 40), random.randint(6, 14), 0.2, "glow"))
# Перемещается за спину игрока
angle = random.uniform(0, 6.28)
self.rect.x = int(player.rect.centerx + math.cos(angle) * 220)
self.rect.y = int(player.rect.centery + math.sin(angle) * 220)
texts.append(FloatingText(self.rect.centerx, self.rect.top, "*Шепот чащи*", (180, 200, 180), 16))
# Атака: Вызов корней из-под земли
if self.root_spell_cd <= 0 and dist < 600:
self.root_spell_cd = 150
roots.append(RootTrap(player.rect.centerx, player.rect.centery))
texts.append(FloatingText(self.rect.centerx, self.rect.top, "ГНЕВ ПРИРОДЫ!", (130, 200, 80), 18))
# Сближение
if dist > 40:
d = v.normalize()
self.rect.x += int(d.x * spd)
self.rect.y += int(d.y * spd)
def draw(self, surf, sense_active):
if not self.alive: return
r = camera.apply(self.rect)
# Тень
pygame.draw.ellipse(surf, (0, 0, 0, 140), (r.centerx - 28, r.bottom - 10, 56, 20))
# Деревянное тело Лешего
pygame.draw.rect(surf, (40, 28, 20), r, border_radius=6)
# Череп оленя (голова)
pygame.draw.polygon(surf, (220, 215, 195), [(r.centerx, r.top + 28), (r.left + 10, r.top + 6), (r.right - 10, r.top + 6)])
# Ветвистые рога
pygame.draw.line(surf, (30, 20, 15), (r.left + 12, r.top + 6), (r.left - 16, r.top - 20), 4)
pygame.draw.line(surf, (30, 20, 15), (r.right - 12, r.top + 6), (r.right + 16, r.top - 20), 4)
pygame.draw.line(surf, (30, 20, 15), (r.left - 8, r.top - 6), (r.left - 2, r.top - 24), 3)
pygame.draw.line(surf, (30, 20, 15), (r.right + 8, r.top - 6), (r.right + 2, r.top - 24), 3)
# Зловещие белые огоньки в глазницах
pygame.draw.circle(surf, (180, 255, 220), (r.centerx - 6, r.top + 14), 3)
pygame.draw.circle(surf, (180, 255, 220), (r.centerx + 6, r.top + 14), 3)
# Ведьмачье чутье
if sense_active:
glow = pygame.Surface((r.w + 24, r.h + 24), pygame.SRCALPHA)
pygame.draw.rect(glow, (255, 40, 0, 160), glow.get_rect(), width=4, border_radius=8)
surf.blit(glow, (r.x - 12, r.y - 12))
class RootTrap:
def __init__(self, x, y):
self.rect = pygame.Rect(x - 30, y - 30, 60, 60)
self.timer = 45 # Время до срабатывания
self.active_time = 35
self.hit = False
def update(self, player, texts, decals):
self.timer -= 1
if self.timer <= 0:
self.active_time -= 1
if not self.hit and self.rect.colliderect(player.rect):
self.hit = True
if player.quen_shield:
player.quen_shield = False
texts.append(FloatingText(player.rect.centerx, player.rect.top, "КВЕН РАЗБИТ КОРНЯМИ", (255, 215, 0), 16))
else:
player.hp -= 35
camera.shake = 12
texts.append(FloatingText(player.rect.centerx, player.rect.top, "-35 (Шипы леса)", (220, 30, 30), 20))
decals.append(BloodDecal(player.rect.centerx, player.rect.centery))
def draw(self, surf):
r = camera.apply(self.rect)
if self.timer > 0:
# Предупреждающий круг под ногами
pygame.draw.circle(surf, (140, 60, 40), r.center, int(26 * (1 - self.timer / 45)), 2)
else:
# Вырывающиеся корни
pygame.draw.circle(surf, (35, 60, 25), r.center, 28)
for angle in [0, 1.2, 2.4, 3.6, 4.8]:
ex = r.centerx + int(math.cos(angle) * 25)
ey = r.centery + int(math.sin(angle) * 25)
pygame.draw.line(surf, (60, 35, 20), r.center, (ex, ey), 4)
class YrdenTrap:
def __init__(self, x, y):
self.x, self.y = x, y
self.radius = 120
self.life = 360 # 6 секунд
def update(self, enemies, boss):
self.life -= 1
for e in enemies:
if math.hypot(self.x - e.rect.centerx, self.y - e.rect.centery) < self.radius:
e.slow_timer = 20
if boss.alive and math.hypot(self.x - boss.rect.centerx, self.y - boss.rect.centery) < self.radius:
boss.slow_timer = 20
def draw(self, surf):
pos = camera.apply_pos((self.x, self.y))
s = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
pygame.draw.circle(s, (180, 50, 240, 45), (self.radius, self.radius), self.radius)
pygame.draw.circle(s, (210, 100, 255, 120), (self.radius, self.radius), self.radius, 2)
surf.blit(s, (pos[0] - self.radius, pos[1] - self.radius))
# ----------------- ГЕНЕРАЦИЯ МИРА -----------------
obstacles = []
# Деревья
for _ in range(55):
obstacles.append(pygame.Rect(random.randint(100, WORLD_W - 200), random.randint(100, WORLD_H - 200), 54, 54))
# Древние камни/руины
for _ in range(12):
obstacles.append(pygame.Rect(random.randint(300, WORLD_W - 400), random.randint(300, WORLD_H - 400), 100, 70))
witcher = Witcher(WORLD_W // 2, WORLD_H // 2)
drowners = [Drowner(random.randint(400, WORLD_W - 400), random.randint(400, WORLD_H - 400)) for _ in range(12)]
leshen = LeshenBoss(WORLD_W // 2 + 350, WORLD_H // 2 - 400)
roots = []
yrdens = []
particles = []
floating_texts = []
blood_decals = []
# ----------------- ИГРОВОЙ ЦИКЛ -----------------
running = True
while running:
mouse_buttons = pygame.mouse.get_pressed()
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
# Атака (SPACE)
if event.key == pygame.K_SPACE:
witcher.attack()
# Кувырок (LSHIFT)
if event.key == pygame.K_LSHIFT and witcher.dodge_timer == 0 and witcher.stamina >= 25:
witcher.dodge_timer = 15
witcher.stamina -= 25
# Переключение меча (R)
if event.key == pygame.K_r:
witcher.sword_type = "STEEL" if witcher.sword_type == "SILVER" else "SILVER"
col = (200, 230, 255) if witcher.sword_type == "SILVER" else (240, 200, 150)
name = "СЕРЕБРЯНЫЙ МЕЧ" if witcher.sword_type == "SILVER" else "СТАЛЬНОЙ МЕЧ"
floating_texts.append(FloatingText(witcher.rect.centerx, witcher.rect.top, name, col, 15))
# Эликсир Ласточка (F)
if event.key == pygame.K_f:
witcher.drink_potion(floating_texts)
# --- ЗНАКИ ВЕДЬМАКА ---
# 1: ИГНИ (Огонь веером)
if event.key == pygame.K_1 and witcher.stamina >= 35:
witcher.stamina -= 35
camera.shake = 4
for _ in range(40):
spr = (random.random() - 0.5) * 0.9
vec = witcher.facing.rotate_rad(spr) * random.uniform(6, 12)
particles.append(Particle(witcher.rect.centerx, witcher.rect.centery, vec.x, vec.y, (255, random.randint(70, 160), 20), random.randint(5, 10), 0.35, "glow"))
# 2: КВЕН (Щит)
if event.key == pygame.K_2 and witcher.stamina >= 40:
witcher.stamina -= 40
witcher.quen_shield = True
floating_texts.append(FloatingText(witcher.rect.centerx, witcher.rect.top, "КВЕН", (255, 215, 0), 16))
# 3: ААРД (Телекинетическая волна - стан и отталкивание)
if event.key == pygame.K_3 and witcher.stamina >= 35:
witcher.stamina -= 35
camera.shake = 6
for _ in range(35):
spr = (random.random() - 0.5) * 0.8
vec = witcher.facing.rotate_rad(spr) * random.uniform(8, 15)
particles.append(Particle(witcher.rect.centerx, witcher.rect.centery, vec.x, vec.y, (180, 220, 255), random.randint(4, 8), 0.3, "glow"))
# Отталкивание утопцев
for d in drowners:
v = pygame.Vector2(d.rect.center) - pygame.Vector2(witcher.rect.center)
if v.length() < 180 and v.dot(witcher.facing) > 0:
d.rect.x += int(v.normalize().x * 70)
d.rect.y += int(v.normalize().y * 70)
d.hp -= 15
floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, "СТАН!", (180, 220, 255), 16))
# 4: ИРДЕН (Магический круг)
if event.key == pygame.K_4 and witcher.stamina >= 35:
witcher.stamina -= 35
yrdens.append(YrdenTrap(witcher.rect.centerx, witcher.rect.centery))
floating_texts.append(FloatingText(witcher.rect.centerx, witcher.rect.top, "ИРДЕН", (200, 100, 255), 16))
# Обновление сущностей
move_vec = witcher.handle_input(keys, mouse_buttons)
witcher.update(move_vec, obstacles)
camera.follow(witcher.rect.center)
# Проверка попаданий меча
if witcher.is_attacking and witcher.attack_hitbox.width > 0:
# Урон по Утопцам
for d in drowners:
if witcher.attack_hitbox.colliderect(d.rect):
base = random.randint(28, 38)
dmg = base if witcher.sword_type == "SILVER" else int(base * 0.25) # Не серебряный меч слаб против монстров
d.hp -= dmg
blood_decals.append(BloodDecal(d.rect.centerx, d.rect.centery))
txt_color = (240, 240, 255) if witcher.sword_type == "SILVER" else (180, 180, 180)
msg = f"-{dmg}" if witcher.sword_type == "SILVER" else f"-{dmg} (Нужно Серебро!)"
floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, msg, txt_color, 16))
witcher.attack_hitbox = pygame.Rect(0, 0, 0, 0)
camera.shake = 4
break
# Урон по Боссу (Лешему)
if leshen.alive and witcher.attack_hitbox.colliderect(leshen.rect):
base = random.randint(25, 34)
dmg = base if witcher.sword_type == "SILVER" else int(base * 0.2)
leshen.hp -= dmg
camera.shake = 6
blood_decals.append(BloodDecal(leshen.rect.centerx, leshen.rect.centery))
floating_texts.append(FloatingText(leshen.rect.centerx, leshen.rect.top, f"-{dmg}", (255, 230, 200), 20))
witcher.attack_hitbox = pygame.Rect(0, 0, 0, 0)
# Урон огнем от Игни
for p in particles:
if p.decay > 0.3: # Огненные частицы
for d in drowners:
if math.hypot(p.x - d.rect.centerx, p.y - d.rect.centery) < 28:
d.hp -= 1.8
if random.random() < 0.1:
floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, "ГОРИТ", (255, 120, 20), 12))
if leshen.alive and math.hypot(p.x - leshen.rect.centerx, p.y - leshen.rect.centery) < 45:
leshen.hp -= 2.2 # Леший горит сильнее
# Обновление ловушек и магии
for yr in yrdens[:]:
yr.update(drowners, leshen)
if yr.life <= 0: yrdens.remove(yr)
for rt in roots[:]:
rt.update(witcher, floating_texts, blood_decals)
if rt.active_time <= 0: roots.remove(rt)
for d in drowners[:]:
d.update(witcher, floating_texts, particles, blood_decals)
if d.hp <= 0:
floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, "+ВЕДЬМАЧИЙ ТРОФЕЙ", (255, 215, 0), 15))
drowners.remove(d)
if leshen.alive:
leshen.update(witcher, roots, None, floating_texts, particles)
for p in particles[:]:
p.update()
if p.radius <= 0: particles.remove(p)
for ft in floating_texts[:]:
ft.update()
if ft.life <= 0: floating_texts.remove(ft)
# ----------------- РЕНДЕРИНГ -----------------
screen.fill((30, 36, 32)) # Темная земля Велена
# Следы крови на земле
for b in blood_decals:
b.draw(screen)
# Круги Ирден
for yr in yrdens:
yr.draw(screen)
# Препятствия (Деревья и Камни)
for obs in obstacles:
r = camera.apply(obs)
if obs.w > 60: # Камень
pygame.draw.rect(screen, (70, 75, 75), r, border_radius=6)
pygame.draw.rect(screen, (50, 55, 55), (r.x + 6, r.y + 6, r.w - 12, r.h - 12), border_radius=4)
else: # Дерево
pygame.draw.circle(screen, (20, 32, 20), (r.centerx, r.centery), 38)
pygame.draw.circle(screen, (15, 25, 15), (r.centerx, r.centery), 26)
pygame.draw.rect(screen, (50, 35, 25), (r.centerx - 7, r.centery - 7, 14, 18))
# Корни Лешего
for rt in roots:
rt.draw(screen)
# Монстры
for d in drowners:
d.draw(screen, witcher.sense_active)
leshen.draw(screen, witcher.sense_active)
# Геральт
witcher.draw(screen)
# Частицы
for p in particles:
p.draw(screen)
# Плавающий текст урона
for ft in floating_texts:
ft.draw(screen)
# Эффект Ведьмачьего Чутья (Затемнение краев экрана)
if witcher.sense_active:
sense_overlay = pygame.Surface((SCREEN_W, SCREEN_H), pygame.SRCALPHA)
sense_overlay.fill((10, 5, 0, 75))
pygame.draw.circle(sense_overlay, (0, 0, 0, 0), (SCREEN_W // 2, SCREEN_H // 2), 340)
screen.blit(sense_overlay, (0, 0))
sense_hint = font_m.render("[ ВЕДЬМАЧЬЕ ЧУТЬЕ АКТИВНО ]", True, (255, 140, 0))
screen.blit(sense_hint, (SCREEN_W // 2 - sense_hint.get_width() // 2, 70))
# ----------------- HUD (ИНТЕРФЕЙС ИЗ ВЕДЬМАКА 3) -----------------
# Плашка Волка
pygame.draw.circle(screen, (40, 40, 40), (60, 55), 32)
pygame.draw.polygon(screen, (200, 200, 210), [(60, 35), (42, 60), (52, 68), (60, 58), (68, 68), (78, 60)])
# Полоска HP (Красная)
pygame.draw.rect(screen, (30, 20, 20), (105, 34, 220, 16), border_radius=3)
hp_pct = max(0, witcher.hp / witcher.max_hp)
pygame.draw.rect(screen, (210, 35, 35), (105, 34, int(220 * hp_pct), 16), border_radius=3)
# Полоска Выносливости (Желтая)
pygame.draw.rect(screen, (30, 30, 20), (105, 54, 180, 10), border_radius=3)
stamina_pct = max(0, witcher.stamina / witcher.max_stamina)
pygame.draw.rect(screen, (240, 190, 30), (105, 54, int(180 * stamina_pct), 10), border_radius=3)
# Полоска Токсичности (Зеленая под выносливостью)
pygame.draw.rect(screen, (20, 30, 20), (105, 68, 120, 6), border_radius=2)
tox_pct = min(1.0, witcher.toxicity / 100)
pygame.draw.rect(screen, (40, 210, 80), (105, 68, int(120 * tox_pct), 6), border_radius=2)
# Активный меч и зелья
sword_col = (190, 220, 255) if witcher.sword_type == "SILVER" else (220, 200, 160)
sword_txt = font_s.render(f"Меч [R]: {witcher.sword_type}", True, sword_col)
pot_txt = font_s.render(f"Ласточка [F]: {witcher.potions} шт.", True, (100, 255, 140))
screen.blit(sword_txt, (105, 80))
screen.blit(pot_txt, (240, 80))
# Подсказка по Знакам
signs_txt = font_s.render("[1] Игни [2] Квен [3] Аард [4] Ирден | [ПКМ] Чутье | [SHIFT] Рывок", True, (180, 180, 180))
screen.blit(signs_txt, (SCREEN_W // 2 - signs_txt.get_width() // 2, SCREEN_H - 30))
# ХП БОССА (Если жив)
if leshen.alive:
boss_bar_w = 460
bx = SCREEN_W // 2 - boss_bar_w // 2
pygame.draw.rect(screen, (20, 20, 20), (bx, 20, boss_bar_w, 14), border_radius=2)
boss_hp_pct = max(0, leshen.hp / leshen.max_hp)
pygame.draw.rect(screen, (160, 25, 25), (bx, 20, int(boss_bar_w * boss_hp_pct), 14), border_radius=2)
b_name = font_boss.render("ДРЕВНИЙ ЛЕШИЙ ИЗ ВЕЛЕНА", True, (240, 230, 210))
screen.blit(b_name, (SCREEN_W // 2 - b_name.get_width() // 2, 38))
else:
win_txt = font_l.render("ЗАКАЗ ВЫПОЛНЕН: ЛЕШИЙ ПОВЕРЖЕН!", True, (255, 215, 0))
screen.blit(win_txt, (SCREEN_W // 2 - win_txt.get_width() // 2, 40))
# Смерть Геральта
if witcher.hp <= 0:
overlay = pygame.Surface((SCREEN_W, SCREEN_H), pygame.SRCALPHA)
overlay.fill((120, 0, 0, 170))
screen.blit(overlay, (0, 0))
d_text = font_l.render("ВЫ ПОГИБЛИ. ВЕДЬМАКИ НЕ УМИРАЮТ В СВОЕЙ ПОСТЕЛИ...", True, (255, 255, 255))
screen.blit(d_text, (SCREEN_W // 2 - d_text.get_width() // 2, SCREEN_H // 2 - 20))
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()