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


import pygame
import math
import random
import sys

pygame.init()
WIDTH, HEIGHT = 900, 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("I love you Karina ❤")
clock = pygame.time.Clock()

# Цвета
BLACK = (8, 0, 15)
PINK = (255, 105, 180)
HOT_PINK = (255, 20, 100)
DEEP = (140, 0, 60)
WHITE = (255, 255, 255)
GOLD = (255, 220, 150)

# Шрифты
try:
    font_name = pygame.font.SysFont("georgia", 56, bold=True, italic=True)
    font_sign = pygame.font.SysFont("georgia", 22, italic=True)
except Exception:
    font_name = pygame.font.Font(None, 56)
    font_sign = pygame.font.Font(None, 22)

LINE1 = "I love you"
LINE2 = "Karina"


# ---------- Форма сердца (параметрическая) ----------
def heart_point(t, scale):
    """t от 0 до tau. Возвращает (x, y) для контура сердца."""
    x = 16 * math.sin(t) ** 3
    y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)
    return x * scale, -y * scale


def build_heart_points(scale, steps=200):
    return [heart_point(2 * math.pi * i / steps, scale) for i in range(steps + 1)]


# ---------- Частицы-искры вокруг сердца ----------
class Spark:
    def __init__(self, cx, cy):
        self.cx, self.cy = cx, cy
        self.reset()

    def reset(self):
        angle = random.uniform(0, math.tau)
        radius = random.uniform(220, 340)
        self.x = self.cx + math.cos(angle) * radius
        self.y = self.cy + math.sin(angle) * radius
        self.size = random.uniform(1, 3)
        self.alpha = random.randint(60, 200)
        self.angle = angle
        self.speed = random.uniform(0.003, 0.01)

    def update(self):
        self.angle += self.speed
        radius = math.hypot(self.x - self.cx, self.y - self.cy)
        self.x = self.cx + math.cos(self.angle) * radius
        self.y = self.cy + math.sin(self.angle) * radius
        self.alpha -= 0.3
        if self.alpha <= 0:
            self.reset()


sparks = [Spark(WIDTH // 2, HEIGHT // 2 - 40) for _ in range(80)]


def draw_glow_text(target, text, font, center, color, glow_color, glow_radius=3):
    """Текст с неоновым свечением."""
    for dx in range(-glow_radius, glow_radius + 1):
        for dy in range(-glow_radius, glow_radius + 1):
            if dx == 0 and dy == 0:
                continue
            s = font.render(text, True, glow_color)
            s.set_alpha(120)
            target.blit(s, (center[0] - s.get_width() // 2 + dx,
                            center[1] - s.get_height() // 2 + dy))
    s = font.render(text, True, color)
    target.blit(s, (center[0] - s.get_width() // 2,
                    center[1] - s.get_height() // 2))


# ---------- Главный цикл ----------
t = 0
running = True

# Фазы анимации:
# 0..180   — рисуется контур сердца
# 180..300 — сердце заполняется и пульсирует
# 300+     — внутри появляется текст
DRAW_END = 180
FILL_END = 300

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            running = False

    # Лёгкий след вместо полной очистки — красивое размытие
    trail = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    trail.fill((8, 0, 15, 60))
    screen.blit(trail, (0, 0))

    cx, cy = WIDTH // 2, HEIGHT // 2 - 40

    # --- Искры на фоне ---
    for s in sparks:
        s.update()
        surf = pygame.Surface((6, 6), pygame.SRCALPHA)
        pygame.draw.circle(surf, (*PINK, int(s.alpha)), (3, 3), int(s.size))
        screen.blit(surf, (s.x - 3, s.y - 3))

    # --- Пульсация ---
    if t < FILL_END:
        scale = 11
    else:
        beat = (t - FILL_END) / 60
        # двойной "удар" как у настоящего сердца
        pulse = math.sin(beat * 6) * 0.5 + math.sin(beat * 12) * 0.5
        scale = 11 + pulse * 0.25

    points = build_heart_points(scale)
    heart_surf = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    heart_points_screen = [(cx + x, cy + y) for x, y in points]

    # --- Фаза 1: прорисовка контура ---
    if t < DRAW_END:
        progress = t / DRAW_END
        visible = int(progress * (len(points) - 1))
        if visible > 1:
            # Свечение контура
            for w, a in [(10, 30), (6, 60), (3, 120)]:
                s = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
                pygame.draw.lines(s, (*HOT_PINK, a), False,
                                  heart_points_screen[:visible], w)
                screen.blit(s, (0, 0))
            # Яркая линия
            pygame.draw.lines(screen, WHITE, False, heart_points_screen[:visible], 2)

    # --- Фаза 2: заполнение ---
    elif t < FILL_END:
        fill_progress = (t - DRAW_END) / (FILL_END - DRAW_END)

        # Пульсирующее свечение вокруг
        glow_color = (
            int(200 + 55 * math.sin(t * 0.15)),
            int(20 + 40 * math.sin(t * 0.15)),
            int(80 + 40 * math.sin(t * 0.15)),
        )
        for w, a in [(40, 25), (25, 45), (15, 80), (8, 130)]:
            s = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            pygame.draw.polygon(s, (*glow_color, a), heart_points_screen)
            s = pygame.transform.smoothscale(s, (WIDTH, HEIGHT))
            screen.blit(s, (0, 0))

        # Само сердце — плавно появляется
        body = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        pygame.draw.polygon(body, (*HOT_PINK, int(255 * fill_progress)),
                            heart_points_screen)
        screen.blit(body, (0, 0))

        # Контур ярко
        pygame.draw.lines(screen, WHITE, False, heart_points_screen, 3)

    # --- Фаза 3: всё + текст ---
    else:
        glow_color = (
            int(200 + 55 * math.sin(t * 0.15)),
            int(20 + 40 * math.sin(t * 0.15)),
            int(80 + 40 * math.sin(t * 0.15)),
        )
        for w, a in [(40, 25), (25, 45), (15, 80), (8, 130)]:
            s = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            pygame.draw.polygon(s, (*glow_color, a), heart_points_screen)
            screen.blit(s, (0, 0))

        body = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        pygame.draw.polygon(body, HOT_PINK, heart_points_screen)
        screen.blit(body, (0, 0))
        pygame.draw.lines(screen, WHITE, False, heart_points_screen, 3)

        # --- Текст внутри ---
        text_progress = min((t - FILL_END) / 90, 1.0)  # 1.5 сек на текст

        line1_chars = int(len(LINE1) * text_progress)
        line2_chars = int(len(LINE2) * max(0, text_progress - 0.4) / 0.6)

        shown1 = LINE1[:line1_chars]
        shown2 = LINE2[:line2_chars]

        # мягкое мерцание оттенка
        shimmer = 0.5 + 0.5 * math.sin(t * 0.1)
        text_color = (
            255,
            int(200 + 40 * shimmer),
            int(220 + 30 * shimmer),
        )

        if shown1:
            draw_glow_text(screen, shown1, font_name,
                           (cx, cy - 30), text_color, (255, 100, 180), 4)
        if shown2:
            draw_glow_text(screen, shown2, font_name,
                           (cx, cy + 45), text_color, (255, 100, 180), 4)

        # Подпись снизу
        if text_progress >= 1.0:
            sign_progress = min((t - FILL_END - 90) / 60, 1.0)
            if sign_progress > 0:
                s = font_sign.render("— навсегда твой ❤", True, GOLD)
                s.set_alpha(int(255 * sign_progress))
                screen.blit(s, (WIDTH // 2 - s.get_width() // 2, HEIGHT - 60))

    pygame.display.flip()
    t += 1
    clock.tick(60)

pygame.quit()
sys.exit()