init
This commit is contained in:
621
tet/novel.py
Normal file
621
tet/novel.py
Normal file
@@ -0,0 +1,621 @@
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import random
|
||||
|
||||
# Инициализация Pygame
|
||||
pygame.init()
|
||||
|
||||
# Настройки окна
|
||||
WIDTH, HEIGHT = 1920, 1080
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
pygame.display.set_caption("Визуальная новелла")
|
||||
|
||||
# Цвета
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GRAY = (200, 200, 200)
|
||||
LIGHT_BLUE = (173, 216, 230)
|
||||
DARK_BLUE = (0, 0, 139)
|
||||
CHOICE_BG = (240, 240, 255)
|
||||
|
||||
NIGHT_BLUE = (5, 5, 30)
|
||||
MORNING_YELLOW = (255, 220, 100)
|
||||
MORNING_GREEN = (100, 180, 80)
|
||||
MORNING_DARK_BLUE = (20, 40, 100)
|
||||
DAY_GREEN = (100, 200, 80)
|
||||
DAY_BLUE = (100, 200, 255)
|
||||
EVENING_ORANGE = (255, 120, 50)
|
||||
EVENING_DARK_GREEN = (30, 80, 40)
|
||||
|
||||
# Шрифты
|
||||
try:
|
||||
font = pygame.font.Font(None, 36)
|
||||
character_font = pygame.font.Font(None, 32)
|
||||
choice_font = pygame.font.Font(None, 30)
|
||||
small_font = pygame.font.Font(None, 24)
|
||||
italic_font = pygame.font.Font(None, 32)
|
||||
italic_font.set_italic(True)
|
||||
except:
|
||||
font = pygame.font.Font(None, 36)
|
||||
character_font = pygame.font.Font(None, 32)
|
||||
choice_font = pygame.font.Font(None, 30)
|
||||
small_font = pygame.font.Font(None, 24)
|
||||
italic_font = pygame.font.Font(None, 32)
|
||||
italic_font.set_italic(True)
|
||||
|
||||
class NovelEngine:
|
||||
def __init__(self):
|
||||
self.script = []
|
||||
self.current_line = 0
|
||||
self.running = True
|
||||
self.background = None
|
||||
self.characters = {}
|
||||
self.variables = {}
|
||||
self.choices = []
|
||||
self.waiting_for_choice = False
|
||||
self.call_stack = [] # Стек для хранения позиций возврата
|
||||
self.text_box_rect = pygame.Rect(50, HEIGHT - 250, WIDTH - 100, 200)
|
||||
self.choice_box_rect = pygame.Rect(WIDTH//2 - 300, HEIGHT//2 - 150, 600, 300)
|
||||
self.special_background = None
|
||||
self.background_textures = {} # Текстуры для сложных фонов
|
||||
self.init_background_textures() # Инициализируем текстуры
|
||||
|
||||
def load_script(self, filename):
|
||||
"""Загрузка скрипта из файла"""
|
||||
self.script = []
|
||||
script_path = os.path.join(os.path.dirname(__file__), filename)
|
||||
|
||||
if not os.path.exists(script_path):
|
||||
print(f"Ошибка: файл {script_path} не найден!")
|
||||
return False
|
||||
|
||||
with open(script_path, 'r', encoding='utf-8') as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
self.script.append(line)
|
||||
return True
|
||||
|
||||
def parse_character(self, command):
|
||||
"""Обработка описания персонажа с дополнительными атрибутами"""
|
||||
if command.startswith("char "):
|
||||
parts = command[5:].split("|")
|
||||
char_id = parts[0].strip().split(":")[0]
|
||||
char_data = {
|
||||
"name": parts[0].split(":")[1].strip(),
|
||||
"appearance": parts[1].strip() if len(parts) > 1 else "",
|
||||
"details": parts[2].strip() if len(parts) > 2 else "",
|
||||
"scent": parts[3].strip() if len(parts) > 3 else ""
|
||||
}
|
||||
self.characters[char_id] = char_data
|
||||
return True
|
||||
return False
|
||||
|
||||
def init_background_textures(self):
|
||||
"""Создаем текстуры для сложных фонов один раз при инициализации"""
|
||||
# Ночное небо
|
||||
night_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
night_surface.fill(NIGHT_BLUE)
|
||||
for _ in range(100):
|
||||
x = random.randint(0, WIDTH)
|
||||
y = random.randint(0, HEIGHT // 2)
|
||||
size = random.randint(1, 3)
|
||||
brightness = random.randint(200, 255)
|
||||
pygame.draw.circle(night_surface, (brightness, brightness, brightness), (x, y), size)
|
||||
self.background_textures["night"] = night_surface
|
||||
|
||||
morning_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
for y in range(HEIGHT):
|
||||
ratio = y / HEIGHT
|
||||
# Верхняя часть (темно-синяя)
|
||||
if ratio < 0.3:
|
||||
r = int(MORNING_DARK_BLUE[0])
|
||||
g = int(MORNING_DARK_BLUE[1])
|
||||
b = int(MORNING_DARK_BLUE[2])
|
||||
# Средняя часть (переход)
|
||||
elif ratio < 0.7:
|
||||
r = int(MORNING_DARK_BLUE[0] + (MORNING_YELLOW[0] - MORNING_DARK_BLUE[0]) * (ratio - 0.3) / 0.4)
|
||||
g = int(MORNING_DARK_BLUE[1] + (MORNING_YELLOW[1] - MORNING_DARK_BLUE[1]) * (ratio - 0.3) / 0.4)
|
||||
b = int(MORNING_DARK_BLUE[2] + (MORNING_YELLOW[2] - MORNING_DARK_BLUE[2]) * (ratio - 0.3) / 0.4)
|
||||
# Нижняя часть (зеленая)
|
||||
else:
|
||||
r = int(MORNING_GREEN[0])
|
||||
g = int(MORNING_GREEN[1])
|
||||
b = int(MORNING_GREEN[2])
|
||||
pygame.draw.line(morning_surface, (r, g, b), (0, y), (WIDTH, y))
|
||||
pygame.draw.circle(morning_surface, MORNING_YELLOW, (WIDTH // 2, HEIGHT // 3), 50)
|
||||
self.background_textures["morning"] = morning_surface
|
||||
|
||||
day_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
# Градиент от голубого к зеленому
|
||||
for y in range(HEIGHT):
|
||||
ratio = y / HEIGHT
|
||||
if ratio < 0.8:
|
||||
r = int(DAY_BLUE[0])
|
||||
g = int(DAY_BLUE[1])
|
||||
b = int(DAY_BLUE[2])
|
||||
else:
|
||||
r = int(DAY_GREEN[0])
|
||||
g = int(DAY_GREEN[1])
|
||||
b = int(DAY_GREEN[2])
|
||||
pygame.draw.line(day_surface, (r, g, b), (0, y), (WIDTH, y))
|
||||
|
||||
# Рисуем облака
|
||||
for _ in range(5):
|
||||
x = random.randint(0, WIDTH)
|
||||
y = random.randint(50, HEIGHT // 3)
|
||||
size = random.randint(30, 70)
|
||||
pygame.draw.circle(day_surface, WHITE, (x, y), size)
|
||||
pygame.draw.circle(day_surface, WHITE, (x + size//2, y - size//3), size//2)
|
||||
pygame.draw.circle(day_surface, WHITE, (x - size//2, y - size//4), size//2)
|
||||
self.background_textures["day"] = day_surface
|
||||
|
||||
evening_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
for y in range(HEIGHT):
|
||||
ratio = y / HEIGHT
|
||||
if ratio < 0.5:
|
||||
r = int(EVENING_ORANGE[0])
|
||||
g = int(EVENING_ORANGE[1])
|
||||
b = int(EVENING_ORANGE[2])
|
||||
else:
|
||||
r = int(EVENING_ORANGE[0] + (EVENING_DARK_GREEN[0] - EVENING_ORANGE[0]) * (ratio - 0.5) / 0.5)
|
||||
g = int(EVENING_ORANGE[1] + (EVENING_DARK_GREEN[1] - EVENING_ORANGE[1]) * (ratio - 0.5) / 0.5)
|
||||
b = int(EVENING_ORANGE[2] + (EVENING_DARK_GREEN[2] - EVENING_ORANGE[2]) * (ratio - 0.5) / 0.5)
|
||||
pygame.draw.line(evening_surface, (r, g, b), (0, y), (WIDTH, y))
|
||||
|
||||
# Рисуем заходящее солнце
|
||||
pygame.draw.circle(evening_surface, (255, 200, 100), (WIDTH // 2, HEIGHT // 2), 60)
|
||||
pygame.draw.rect(evening_surface, EVENING_DARK_GREEN, (0, HEIGHT // 2, WIDTH, HEIGHT // 2))
|
||||
self.background_textures["evening"] = evening_surface
|
||||
|
||||
def draw_night_sky(self):
|
||||
"""Рисует ночное небо со звездами"""
|
||||
screen.blit(self.background_textures["night"], (0, 0))
|
||||
|
||||
def draw_morning_sky(self):
|
||||
"""Рисует утреннее небо с градиентом"""
|
||||
screen.blit(self.background_textures["morning"], (0, 0))
|
||||
|
||||
def draw_day_sky(self):
|
||||
"""Рисует дневное небо с облаками"""
|
||||
screen.blit(self.background_textures["day"], (0, 0))
|
||||
|
||||
def draw_evening_sky(self):
|
||||
"""Рисует вечернее небо с закатом"""
|
||||
screen.blit(self.background_textures["evening"], (0, 0))
|
||||
|
||||
def parse_command(self, command):
|
||||
"""Обработка команд скрипта"""
|
||||
# Случайный переход в подпрограмму
|
||||
if command.startswith("random_gosub "):
|
||||
parts = command[12:].split()
|
||||
if len(parts) >= 2:
|
||||
# Создаем список вариантов с их вероятностями
|
||||
options = []
|
||||
probabilities = []
|
||||
labels = []
|
||||
|
||||
# Разбираем части на вероятности и метки
|
||||
i = 0
|
||||
while i < len(parts):
|
||||
try:
|
||||
prob = float(parts[i])
|
||||
label = parts[i+1]
|
||||
probabilities.append(prob)
|
||||
labels.append(label)
|
||||
options.append((prob, label))
|
||||
i += 2
|
||||
except (ValueError, IndexError):
|
||||
break
|
||||
|
||||
if options:
|
||||
# Нормализуем вероятности (на случай, если они не суммируются в 1)
|
||||
total = sum(prob for prob, label in options)
|
||||
if total > 0:
|
||||
rand = random.random() * total
|
||||
cumulative = 0
|
||||
for prob, label in options:
|
||||
cumulative += prob
|
||||
if rand <= cumulative:
|
||||
# Сохраняем текущую позицию для возврата
|
||||
self.call_stack.append(self.current_line + 1)
|
||||
# Переходим к выбранной подпрограмме
|
||||
self.jump_to_label(label)
|
||||
break
|
||||
return True
|
||||
|
||||
# Возврат из подпрограммы
|
||||
elif command == "return":
|
||||
if self.call_stack:
|
||||
self.current_line = self.call_stack.pop()
|
||||
else:
|
||||
print("Ошибка: стек вызовов пуст!")
|
||||
return True
|
||||
|
||||
# Установка переменной
|
||||
elif command.startswith("set "):
|
||||
parts = command[4:].split("|")
|
||||
for part in parts:
|
||||
part = part.strip()
|
||||
if "+=" in part:
|
||||
var, val = part.split("+=", 1)
|
||||
var = var.strip()
|
||||
current = int(self.variables.get(var, 0))
|
||||
self.variables[var] = str(current + int(val.strip()))
|
||||
elif "-=" in part:
|
||||
var, val = part.split("-=", 1)
|
||||
var = var.strip()
|
||||
current = int(self.variables.get(var, 0))
|
||||
self.variables[var] = str(current - int(val.strip()))
|
||||
elif "=" in part:
|
||||
var, val = part.split("=", 1)
|
||||
var = var.strip()
|
||||
val = val.strip()
|
||||
|
||||
# Обработка инкремента (++var)
|
||||
if val.startswith("++"):
|
||||
var_to_inc = val[2:]
|
||||
self.variables[var] = str(int(self.variables.get(var_to_inc, 0)) + 1)
|
||||
# Обработка декремента (--var)
|
||||
elif val.startswith("--"):
|
||||
var_to_dec = val[2:]
|
||||
self.variables[var] = str(int(self.variables.get(var_to_dec, 0)) - 1)
|
||||
# Обычное присваивание
|
||||
else:
|
||||
self.variables[var] = val
|
||||
print(f"После выполнения '{command}': {self.variables}") # Отладочный вывод
|
||||
return True
|
||||
|
||||
# Условие
|
||||
elif command.startswith("if "):
|
||||
# ИСПРАВЛЕННЫЙ regex с обработкой пробелов
|
||||
match = re.match(r'if\s+(\w+)\s*([=!<>]+)\s*(.+?)\s+then\s+goto\s+(\w+)', command)
|
||||
if match:
|
||||
var_name, op, value, label = match.groups()
|
||||
current_value = self.variables.get(var_name, "0")
|
||||
|
||||
print(f"Отладка: if {var_name}({current_value}) {op} {value} then goto {label}") # Отладка
|
||||
|
||||
# Пробуем численное сравнение
|
||||
try:
|
||||
current_num = float(current_value)
|
||||
value_num = float(value)
|
||||
if op == ">": condition_met = current_num > value_num
|
||||
elif op == "<": condition_met = current_num < value_num
|
||||
elif op == ">=": condition_met = current_num >= value_num
|
||||
elif op == "<=": condition_met = current_num <= value_num
|
||||
elif op == "==": condition_met = current_num == value_num
|
||||
elif op == "!=": condition_met = current_num != value_num
|
||||
else: condition_met = False
|
||||
except ValueError:
|
||||
# Строковое сравнение
|
||||
if op == ">": condition_met = current_value > value
|
||||
elif op == "<": condition_met = current_value < value
|
||||
elif op == ">=": condition_met = current_value >= value
|
||||
elif op == "<=": condition_met = current_value <= value
|
||||
elif op == "==": condition_met = current_value == value
|
||||
elif op == "!=": condition_met = current_value != value
|
||||
else: condition_met = False
|
||||
|
||||
if condition_met:
|
||||
print(f"Условие выполнено, переход к {label}") # Отладка
|
||||
self.jump_to_label(label)
|
||||
else:
|
||||
print("Условие не выполнено") # Отладка
|
||||
return True
|
||||
|
||||
# Метка
|
||||
elif command.startswith("label "):
|
||||
return True
|
||||
|
||||
# Подпрограмма
|
||||
elif command.startswith("sub_"):
|
||||
if not self.call_stack or self.call_stack[-1] != self.current_line:
|
||||
self.call_stack.append(self.current_line)
|
||||
return True
|
||||
|
||||
# Возврат из подпрограммы
|
||||
elif command == "return":
|
||||
if not self.call_stack:
|
||||
print("Предупреждение: return без вызова подпрограммы, пропускаем")
|
||||
self.current_line += 1
|
||||
else:
|
||||
self.current_line = self.call_stack.pop() + 1 # Возвращаемся на следующую строку
|
||||
return True
|
||||
|
||||
# Переход
|
||||
elif command.startswith("goto "):
|
||||
label = command[5:].strip()
|
||||
self.jump_to_label(label)
|
||||
return True
|
||||
|
||||
# Фон
|
||||
elif command.startswith("bg "):
|
||||
bg_color = command[3:].strip().lower()
|
||||
if bg_color == "black":
|
||||
self.background = BLACK
|
||||
self.special_background = None
|
||||
elif bg_color == "white":
|
||||
self.background = WHITE
|
||||
self.special_background = None
|
||||
elif bg_color == "blue":
|
||||
self.background = LIGHT_BLUE
|
||||
self.special_background = None
|
||||
elif bg_color == "night":
|
||||
self.special_background = "night"
|
||||
elif bg_color == "morning":
|
||||
self.special_background = "morning"
|
||||
elif bg_color == "day":
|
||||
self.special_background = "day"
|
||||
elif bg_color == "evening" or bg_color == "sunset":
|
||||
self.special_background = "evening"
|
||||
else:
|
||||
self.background = GRAY
|
||||
self.special_background = None
|
||||
return True
|
||||
|
||||
# Персонаж
|
||||
elif command.startswith("char "):
|
||||
return self.parse_character(command)
|
||||
|
||||
return False
|
||||
|
||||
def draw_descriptions(self, text, x, y, font_obj, color):
|
||||
"""Отрисовка текста с учётом звёздочек"""
|
||||
if text.startswith("*") and text.endswith("*"):
|
||||
# Удаляем звёздочки и рисуем курсивом
|
||||
self.draw_text(text[1:-1], x, y, italic_font, (150, 150, 150))
|
||||
else:
|
||||
self.draw_text(text, x, y, font_obj, color)
|
||||
|
||||
def jump_to_label(self, label):
|
||||
"""Переход к метке в скрипте"""
|
||||
for i, line in enumerate(self.script):
|
||||
if line.startswith(f"@label {label}") or line.startswith(f"@{label}"):
|
||||
self.current_line = i
|
||||
return
|
||||
print(f"Метка '{label}' не найдена!")
|
||||
|
||||
def process_choices(self, line):
|
||||
"""Обработка строки с выбором"""
|
||||
if line.startswith("choice "):
|
||||
choices_text = line[7:].split("|")
|
||||
self.choices = []
|
||||
|
||||
for choice_text in choices_text:
|
||||
parts = choice_text.split("=>")
|
||||
if len(parts) == 2:
|
||||
choice_display = parts[0].strip()
|
||||
choice_action = parts[1].strip()
|
||||
self.choices.append((choice_display, choice_action))
|
||||
|
||||
self.waiting_for_choice = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def substitute_variables(self, text):
|
||||
"""Подстановка переменных в текст"""
|
||||
for var_name, var_value in self.variables.items():
|
||||
text = text.replace(f"{{{var_name}}}", str(var_value))
|
||||
return text
|
||||
|
||||
def get_character_info(self, char_id):
|
||||
"""Возвращает полное описание персонажа"""
|
||||
# Убираем вывод "Персонаж none" для несуществующих ID
|
||||
if char_id not in self.characters:
|
||||
return "", "" # Возвращаем пустые строки
|
||||
|
||||
char = self.characters.get(char_id)
|
||||
if isinstance(char, str):
|
||||
return char, ""
|
||||
|
||||
details = []
|
||||
if char.get("appearance"): details.append(char['appearance'])
|
||||
if char.get("details"): details.append(char['details'])
|
||||
if char.get("scent"): details.append(char['scent'])
|
||||
|
||||
return char.get("name", ""), " | ".join(details)
|
||||
|
||||
def process_line(self):
|
||||
if self.current_line >= len(self.script):
|
||||
self.running = False
|
||||
return None
|
||||
|
||||
line = self.script[self.current_line]
|
||||
line = self.substitute_variables(line)
|
||||
|
||||
# Обработка команд
|
||||
if line.startswith("@"):
|
||||
if self.parse_command(line[1:]):
|
||||
self.current_line += 1
|
||||
return self.process_line()
|
||||
return None
|
||||
|
||||
# Обработка выбора
|
||||
if self.process_choices(line):
|
||||
self.current_line += 1
|
||||
return None
|
||||
|
||||
# Обработка диалога с проверкой разделителя
|
||||
if ":" in line and not line.startswith("*"):
|
||||
parts = line.split(":", 1)
|
||||
if len(parts) == 2:
|
||||
char_id = parts[0].strip()
|
||||
text = parts[1].strip()
|
||||
return char_id, text
|
||||
|
||||
# Обработка описания или простого текста
|
||||
return None, line
|
||||
|
||||
def make_choice(self, choice_index):
|
||||
"""Обработка выбора игрока"""
|
||||
if 0 <= choice_index < len(self.choices):
|
||||
choice_action = self.choices[choice_index][1]
|
||||
|
||||
if choice_action.startswith("goto "):
|
||||
label = choice_action[5:].strip()
|
||||
self.jump_to_label(label)
|
||||
elif "+=" in choice_action:
|
||||
var, value = choice_action.split("+=", 1)
|
||||
var = var.strip()
|
||||
self.variables[var] = str(int(self.variables.get(var, 0)) + int(value.strip()))
|
||||
self.current_line += 1
|
||||
elif "-=" in choice_action:
|
||||
var, value = choice_action.split("-=", 1)
|
||||
var = var.strip()
|
||||
self.variables[var] = str(int(self.variables.get(var, 0)) - int(value.strip()))
|
||||
self.current_line += 1
|
||||
elif "=" in choice_action:
|
||||
var, value = choice_action.split("=", 1)
|
||||
self.variables[var.strip()] = value.strip()
|
||||
self.current_line += 1
|
||||
|
||||
self.choices = []
|
||||
self.waiting_for_choice = False
|
||||
|
||||
def next_line(self):
|
||||
"""Переход к следующей строке"""
|
||||
self.current_line += 1
|
||||
if self.current_line >= len(self.script):
|
||||
self.running = False
|
||||
|
||||
def draw_text_box(self):
|
||||
"""Отрисовка текстового поля"""
|
||||
pygame.draw.rect(screen, WHITE, self.text_box_rect)
|
||||
pygame.draw.rect(screen, DARK_BLUE, self.text_box_rect, 3)
|
||||
|
||||
def draw_choice_box(self):
|
||||
"""Отрисовка поля выбора"""
|
||||
# Создаем полупрозрачную поверхность
|
||||
s = pygame.Surface((600, 300), pygame.SRCALPHA)
|
||||
s.fill((240, 240, 255, 230)) # Последний параметр - прозрачность
|
||||
screen.blit(s, (self.choice_box_rect.x, self.choice_box_rect.y))
|
||||
pygame.draw.rect(screen, DARK_BLUE, self.choice_box_rect, 3)
|
||||
|
||||
def draw_text(self, text, x, y, font_obj, color=BLACK, max_width=None):
|
||||
"""Отрисовка текста с переносом строк"""
|
||||
if max_width is None:
|
||||
max_width = self.text_box_rect.width - 40
|
||||
|
||||
words = text.split(' ')
|
||||
space_width = font_obj.size(' ')[0]
|
||||
line_height = font_obj.get_height()
|
||||
|
||||
current_line = []
|
||||
current_width = 0
|
||||
|
||||
for word in words:
|
||||
word_surface = font_obj.render(word, True, color)
|
||||
word_width = word_surface.get_width()
|
||||
|
||||
if current_width + word_width <= max_width:
|
||||
current_line.append(word)
|
||||
current_width += word_width + space_width
|
||||
else:
|
||||
line_text = ' '.join(current_line)
|
||||
line_surface = font_obj.render(line_text, True, color)
|
||||
screen.blit(line_surface, (x, y))
|
||||
y += line_height
|
||||
current_line = [word]
|
||||
current_width = word_width + space_width
|
||||
|
||||
if current_line:
|
||||
line_text = ' '.join(current_line)
|
||||
line_surface = font_obj.render(line_text, True, color)
|
||||
screen.blit(line_surface, (x, y))
|
||||
|
||||
def draw(self):
|
||||
"""Отрисовка текущего состояния"""
|
||||
# Фон
|
||||
if self.special_background and self.special_background in self.background_textures:
|
||||
screen.blit(self.background_textures[self.special_background], (0, 0))
|
||||
elif self.background:
|
||||
screen.fill(self.background)
|
||||
else:
|
||||
screen.fill(LIGHT_BLUE)
|
||||
|
||||
# Текстовое поле (убираем очистку перед выбором)
|
||||
if not self.waiting_for_choice:
|
||||
self.draw_text_box()
|
||||
|
||||
# Текст
|
||||
line_data = self.process_line()
|
||||
x = self.text_box_rect.x + 20
|
||||
y = self.text_box_rect.y + 20
|
||||
|
||||
if line_data and not self.waiting_for_choice:
|
||||
if isinstance(line_data, tuple): # Диалог с персонажем
|
||||
char_id, text = line_data
|
||||
name, details = self.get_character_info(char_id)
|
||||
|
||||
# Отрисовываем имя только если оно есть
|
||||
if name:
|
||||
name_surface = character_font.render(name + ":", True, DARK_BLUE)
|
||||
screen.blit(name_surface, (x, y))
|
||||
y += 40
|
||||
|
||||
# Отрисовка текста персонажа
|
||||
self.draw_text(text, x, y, font, BLACK)
|
||||
y += 60
|
||||
|
||||
# Отрисовка деталей персонажа (если есть)
|
||||
if details:
|
||||
details_surface = small_font.render(details, True, (100, 100, 100))
|
||||
screen.blit(details_surface, (x, y))
|
||||
else: # Описание или простой текст
|
||||
_, text = line_data
|
||||
self.draw_descriptions(text, x, y, font, BLACK)
|
||||
|
||||
# Варианты выбора
|
||||
if self.waiting_for_choice and self.choices:
|
||||
self.draw_choice_box()
|
||||
choice_y = self.choice_box_rect.y + 30
|
||||
for i, (choice_text, _) in enumerate(self.choices):
|
||||
choice_surface = choice_font.render(f"{i+1}. {choice_text}", True, BLACK)
|
||||
screen.blit(choice_surface, (self.choice_box_rect.x + 30, choice_y))
|
||||
choice_y += 40
|
||||
|
||||
# Индикатор продолжения
|
||||
if self.running and not self.waiting_for_choice and line_data:
|
||||
indicator = font.render("> ПРОБЕЛ", True, DARK_BLUE)
|
||||
screen.blit(indicator, (WIDTH - 200, HEIGHT - 50))
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
def run(self):
|
||||
"""Основной цикл"""
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
while self.running:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_SPACE and not self.waiting_for_choice:
|
||||
self.next_line()
|
||||
elif event.key == pygame.K_1 and self.waiting_for_choice and len(self.choices) >= 1:
|
||||
self.make_choice(0)
|
||||
elif event.key == pygame.K_2 and self.waiting_for_choice and len(self.choices) >= 2:
|
||||
self.make_choice(1)
|
||||
elif event.key == pygame.K_3 and self.waiting_for_choice and len(self.choices) >= 3:
|
||||
self.make_choice(2)
|
||||
elif event.key == pygame.K_4 and self.waiting_for_choice and len(self.choices) >= 4:
|
||||
self.make_choice(3)
|
||||
elif event.key == pygame.K_ESCAPE:
|
||||
self.running = False
|
||||
|
||||
self.draw()
|
||||
clock.tick(60)
|
||||
|
||||
# Создаем экземпляр движка и запускаем
|
||||
if __name__ == "__main__":
|
||||
engine = NovelEngine()
|
||||
|
||||
if engine.load_script("island_script.txt"):
|
||||
engine.run()
|
||||
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
Reference in New Issue
Block a user