Untitled

 avatar
unknown
plain_text
a year ago
20 kB
12
Indexable
import random
import tkinter as tk
from tkinter import simpledialog, font
try:
    from PIL import Image, ImageTk
    PIL_OK = True
except Exception:
    PIL_OK = False

# ==============================
#       INTERFACE (Tkinter)
# ==============================
class GameUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("RPG Livro-Jogo — Ruínas de Nhar")
        self.root.geometry("1000x650")
        self.root.minsize(860, 580)

        # --- Fundo com arte ---
        self.canvas = tk.Canvas(self.root, highlightthickness=0)
        self.canvas.pack(fill="both", expand=True)
        self.bg_img = None
        if PIL_OK:
            try:
                from PIL import Image
                img = Image.open("background.jpg")
                self._orig_bg = img
            except Exception:
                self._orig_bg = None
        else:
            self._orig_bg = None

        self.root.bind("<Configure>", self._on_resize)

        # --- Fonte custom / fallback ---
        self.font_size = 16
        try:
            self.custom_font = font.Font(file="medieval.ttf", size=self.font_size)
        except Exception:
            self.custom_font = font.Font(family="Georgia", size=self.font_size)

        # --- Área de texto (com scrollbar) ---
        self.text_frame = tk.Frame(self.root, bg="#00000000")
        self.text_win = self.canvas.create_window(
            0, 0, anchor="nw", window=self.text_frame
        )

        self.text = tk.Text(
            self.text_frame,
            wrap="word",
            bg="#151515",
            fg="#f7f1e3",
            insertbackground="white",
            font=self.custom_font,
            relief="flat",
            padx=14,
            pady=14,
        )
        self.scroll = tk.Scrollbar(self.text_frame, command=self.text.yview)
        self.text.configure(yscrollcommand=self.scroll.set)
        self.text.grid(row=0, column=0, sticky="nsew")
        self.scroll.grid(row=0, column=1, sticky="ns")
        self.text_frame.grid_rowconfigure(0, weight=1)
        self.text_frame.grid_columnconfigure(0, weight=1)

        # --- Barra de botões ---
        self.buttons_frame = tk.Frame(self.root, bg="#1f1f1f")
        self.buttons_win = self.canvas.create_window(
            0, 0, anchor="nw", window=self.buttons_frame
        )

        self.status = tk.StringVar(value="")
        self.choice = tk.StringVar(value="")

        self.continue_btn = tk.Button(
            self.buttons_frame,
            text="▶ Continuar",
            font=self.custom_font,
            bg="#333333",
            fg="white",
            relief="flat",
            padx=12,
            pady=6,
            command=lambda: self.status.set("go"),
        )
        self.continue_btn.pack(side="right", padx=6, pady=6)

        # --- Menu ---
        menubar = tk.Menu(self.root)
        game_menu = tk.Menu(menubar, tearoff=0)
        game_menu.add_command(label="Novo Jogo", command=self._menu_novo_jogo)
        game_menu.add_separator()
        game_menu.add_command(label="Sair", command=self.root.quit)
        menubar.add_cascade(label="Jogo", menu=game_menu)

        view_menu = tk.Menu(menubar, tearoff=0)
        view_menu.add_command(label="A+", command=self._font_up)
        view_menu.add_command(label="A−", command=self._font_down)
        menubar.add_cascade(label="Visual", menu=view_menu)

        self.root.config(menu=menubar)

    # ---------- util UI ----------
    def _on_resize(self, event=None):
        w = self.root.winfo_width()
        h = self.root.winfo_height()

        # fundo
        if self._orig_bg and PIL_OK:
            try:
                from PIL import ImageTk
                img = self._orig_bg.resize((w, h))
                self.bg_img = ImageTk.PhotoImage(img)
                self.canvas.delete("bg")
                self.canvas.create_image(0, 0, image=self.bg_img, anchor="nw", tags="bg")
            except Exception:
                self.canvas.delete("bg")
                self.canvas.create_rectangle(0, 0, w, h, fill="black", outline="", tags="bg")
        else:
            self.canvas.delete("bg")
            self.canvas.create_rectangle(0, 0, w, h, fill="black", outline="", tags="bg")

        # posicionar caixas
        margin = 18
        text_h = int(h * 0.72)
        btn_h = h - text_h - (margin * 3)

        self.canvas.coords(self.text_win, margin, margin)
        self.canvas.itemconfig(self.text_win, width=w - margin * 2, height=text_h)

        self.canvas.coords(self.buttons_win, margin, margin * 2 + text_h)
        self.canvas.itemconfig(self.buttons_win, width=w - margin * 2, height=btn_h)

    def _font_up(self):
        self.font_size = min(self.font_size + 2, 26)
        self.custom_font.configure(size=self.font_size)

    def _font_down(self):
        self.font_size = max(self.font_size - 2, 12)
        self.custom_font.configure(size=self.font_size)

    def _menu_novo_jogo(self):
        self.text.delete("1.0", "end")
        self.status.set("reset")
        start_new_game()

    def write(self, s: str):
        if s:
            if not s.endswith("\n"):
                s += "\n"
            self.text.insert("end", s)
            self.text.see("end")
            self.root.update_idletasks()

    def pause(self, msg="\n[▶ Pressione Continuar]"):
        self.write(msg)
        self.status.set("")
        self.root.wait_variable(self.status)
        self.write("")

    def choose(self, options: dict, prompt: str = "Escolha: ") -> str:
        # limpa botões anteriores (menos o continuar)
        for w in list(self.buttons_frame.children.values()):
            if w is not self.continue_btn:
                w.destroy()

        self.write("\n" + prompt)
        for k, v in options.items():
            self.write(f"{k}) {v}")

        self.choice.set("")
        for k, v in options.items():
            b = tk.Button(
                self.buttons_frame,
                text=f"{k}) {v}",
                font=self.custom_font,
                bg="#444444",
                fg="white",
                relief="flat",
                padx=10, pady=6,
                command=lambda key=k: self.choice.set(key),
            )
            b.pack(side="left", padx=4, pady=6)

        self.continue_btn.pack_forget()
        self.root.wait_variable(self.choice)
        self.continue_btn.pack(side="right", padx=6, pady=6)
        return self.choice.get()

# instancia UI e redireciona prints
ui = GameUI()
import builtins
def _gui_print(*args, **kwargs):
    ui.write(" ".join(str(a) for a in args))
builtins.print = _gui_print

def escolher(opcoes: dict, prompt: str = "Escolha: "):
    return ui.choose(opcoes, prompt)

def pausar(msg="\n[▶ Pressione Continuar]"):
    ui.pause(msg)

# ==============================
#         SISTEMA / REGRAS
# ==============================
class Personagem:
    def __init__(self, nome, classe, vida, forca, magia):
        self.nome = nome
        self.classe = classe
        self.vida_max = vida
        self.vida = vida
        self.forca = forca
        self.magia = magia
        self.nivel = 1
        self.xp = 0
        self.itens = {"Poção de Cura": 2}
        # eixos narrativos
        self.honra = 0
        self.compaixao = 0
        self.trevas = 0
        self.reliquias = set()

    # combate
    def atacar_fisico(self, alvo):
        dano = random.randint(1 + self.nivel, self.forca + self.nivel)
        alvo.vida -= dano
        print(f"{self.nome} ataca {alvo.nome} e causa {dano} de dano!")

    def atacar_magia(self, alvo):
        base = random.randint(1 + self.nivel, self.magia + 3 + self.nivel)
        crit = 2 if random.random() < 0.15 else 1
        dano = base * crit
        alvo.vida -= dano
        print(f"{self.nome} lança magia em {alvo.nome} e causa {dano}!{' (Crítico!)' if crit==2 else ''}")

    def usar_item(self, nome_item):
        if self.itens.get(nome_item, 0) <= 0:
            print("Você não tem esse item.")
            return False
        if nome_item == "Poção de Cura":
            cura = random.randint(10, 18) + self.nivel
            self.vida = min(self.vida + cura, self.vida_max)
            self.itens[nome_item] -= 1
            print(f"{self.nome} usa Poção de Cura e recupera {cura} de vida. ({self.itens[nome_item]} restantes)")
            return True
        print("Nada acontece...")
        return False

    def ganhar_xp(self, qtd):
        self.xp += qtd
        while self.xp >= self.nivel * 20:
            self.xp -= self.nivel * 20
            self.nivel += 1
            self.vida_max += 6
            self.forca += 2
            self.magia += 1
            self.vida = self.vida_max
            print(f"\n✨ {self.nome} subiu para o Nível {self.nivel}! Atributos melhorados e vida restaurada.\n")

    def vivo(self):
        return self.vida > 0

class Inimigo:
    def __init__(self, nome, vida, forca, xp):
        self.nome = nome
        self.vida = vida
        self.forca = forca
        self.xp = xp

    def atacar(self, alvo):
        dano = random.randint(1, self.forca)
        alvo.vida -= dano
        print(f"{self.nome} ataca {alvo.nome} e causa {dano} de dano!")

    def vivo(self):
        return self.vida > 0

def batalha(jogador: Personagem, inimigo: Inimigo) -> bool:
    print(f"\n⚔️  BATALHA: {jogador.nome} (HP {jogador.vida}/{jogador.vida_max}) vs {inimigo.nome} (HP {inimigo.vida})\n")
    while jogador.vivo() and inimigo.vivo():
        print(f"Seu HP: {jogador.vida}/{jogador.vida_max} | Inimigo: {inimigo.vida}")
        acao = escolher({
            "1": "Atacar (Físico)",
            "2": "Usar Magia",
            "3": "Usar Poção de Cura",
            "4": "Tentar Fugir"
        }, "Ação: ")
        if acao == "1":
            jogador.atacar_fisico(inimigo)
        elif acao == "2":
            jogador.atacar_magia(inimigo)
        elif acao == "3":
            jogador.usar_item("Poção de Cura")
        elif acao == "4":
            if random.random() < 0.35 + (jogador.nivel * 0.03):
                print("Você escapa às pressas!")
                return False
            else:
                print("A fuga falhou!")
        if inimigo.vivo():
            inimigo.atacar(jogador)
        print("-")

    if jogador.vivo():
        print(f"\n Vitória sobre {inimigo.nome}!")
        jogador.ganhar_xp(inimigo.xp)
        return True
    else:
        print("\n Você foi derrotado...")
        return False

# ==============================
#        MUNDO / HISTÓRIA
# ==============================
class Mundo:
    def __init__(self):
        self.salvou_camponesa = False
        self.fez_pacto = False
        self.pegou_reliquia_fogo = False
        self.pegou_reliquia_luz = False
        self.derrotou_dragao = False

# --- Cenas ---
def cena_intro(jog: Personagem, mundo: Mundo):
    print("""
=== PRÓLOGO ===
Você desperta entre as ruínas da Vila de Arvendel. Uma névoa fria envolve as casas queimadas.
Dizem que o Lich Supremo abriu fendas de trevas na Floresta Sombria.
Seu caminho começa na encruzilhada: à esquerda, a Floresta; à direita, a Estrada Velha.
""")
    op = escolher({"1": "Seguir pela Floresta Sombria", "2": "Tomar a Estrada Velha"}, "Seu caminho: ")
    if op == "1":
        jog.honra += 1
        return cena_floresta
    else:
        return cena_estrada

def cena_floresta(jog: Personagem, mundo: Mundo):
    print("""
A copa fecha o céu. Sussurros entre as árvores. Um Goblin salta do mato.
""")
    goblin = Inimigo("Goblin Emboscador", 16, 5, 10)
    venceu = batalha(jog, goblin)
    if not venceu:
        print("Você recua, ferido, para a Estrada Velha.")
        return cena_estrada
    print("Ao vasculhar a clareira, você encontra uma pequena RELÍQUIA ardente.")
    op = escolher({"1": "Pegar a Relíquia do Fogo", "2": "Deixar onde está"}, "Decisão: ")
    if op == "1":
        mundo.pegou_reliquia_fogo = True
        jog.reliquias.add("Fogo")
        print("A relíquia aquece sua mão. Você sente poder fluir.")
    else:
        print("Você respeita o santuário natural e segue em frente.")
        jog.compaixao += 1
    return cena_cabana

def cena_estrada(jog: Personagem, mundo: Mundo):
    print("""
A Estrada Velha serpenteia por campos secos. Uma carroça tombada bloqueia o caminho.
Você ouve um pedido de socorro vindo do matagal.
""")
    op = escolher({"1": "Ajudar quem pede socorro", "2": "Ignorar e prosseguir"}, "Sua ação: ")
    if op == "1":
        print("Você resgata uma camponesa presa sob galhos. Ela lhe dá uma Poção de Cura.")
        jog.itens["Poção de Cura"] = jog.itens.get("Poção de Cura", 0) + 1
        jog.compaixao += 1
        mundo.salvou_camponesa = True
    else:
        print("Você endurece o coração e segue em frente.")
        jog.trevas += 1
    return cena_cruzamento_estrada

def cena_cabana(jog: Personagem, mundo: Mundo):
    print("""
Você encontra uma cabana coberta de musgo. Uma bruxa anciana oferece um pacto:
"Entrarei em sua sombra e lhe darei poder. Em troca, um pedaço de sua alma."
""")
    op = escolher({"1": "Aceitar o pacto (poder imediato)", "2": "Recusar (manter sua alma)"}, "Decisão: ")
    if op == "1":
        mundo.fez_pacto = True
        jog.trevas += 2
        jog.forca += 2
        jog.magia += 2
        print("A sombra sussurra segredos proibidos. Você sente-se mais forte... e mais frio.")
    else:
        jog.honra += 1
        print("Você recusa com firmeza. A bruxa ri e some na neblina.")
    return cena_colina

def cena_cruzamento_estrada(jog: Personagem, mundo: Mundo):
    print("""
Mais adiante, um bando de salteadores aparece. O líder exige tributo.
"Nada pessoal, viajante. Deixe ouro e vá." Você sente que lutar fará fama... ou feridas.
""")
    op = escolher({"1": "Enfrentar os salteadores", "2": "Pagar e evitar conflito"}, "Escolha: ")
    if op == "1":
        b1 = Inimigo("Salteador", 14, 5, 8)
        b2 = Inimigo("Chefe Salteador", 22, 7, 14)
        if not batalha(jog, b1):
            return cena_derrota
        if not batalha(jog, b2):
            return cena_derrota
        jog.honra += 1
        print("Os camponeses escondidos te aplaudem. Alguém deixa uma RELÍQUIA de Luz aos seus pés.")
        mundo.pegou_reliquia_luz = True
        jog.reliquias.add("Luz")
    else:
        print("Você paga algumas moedas e passa. Vive para lutar outro dia.")
        jog.compaixao += 1
    return cena_colina

def cena_colina(jog: Personagem, mundo: Mundo):
    print("""
No alto da colina, o vento uiva. O céu escurece e um Dragão desce em espiral.
"Prove seu valor, mortal!" ruge a criatura.
""")
    op = escolher({"1": "Enfrentar o Dragão", "2": "Tentar se esconder"}, "Decisão: ")
    if op == "2":
        print("Você rola para trás de uma rocha. O Dragão sopra fogo, mas você escapa. A vergonha pesa.")
        jog.trevas += 1
        return cena_ruinas
    bonus_vida = 6 if "Luz" in jog.reliquias else 0
    bonus_dano = 2 if "Fogo" in jog.reliquias else 0
    dragao = Inimigo("Dragão Ancião", 40 + bonus_vida, 10 + bonus_dano, 30)
    venceu = batalha(jog, dragao)
    if venceu:
        mundo.derrotou_dragao = True
        jog.honra += 1
        print("As escamas do Dragão brilham. Entre elas, um amuleto radiante pulsa energia.")
        op = escolher({"1": "Tomar o amuleto", "2": "Deixar como oferenda"}, "Sua decisão: ")
        if op == "1":
            jog.reliquias.add("Amuleto do Vento")
            print("O vento agora sussurra ao seu favor.")
        else:
            jog.compaixao += 1
            print("Você respeita o espírito da criatura.")
    else:
        print("Você cai do penhasco e sobrevive por pouco. O Dragão alça voo, orgulhoso.")
    return cena_ruinas

def cena_ruinas(jog: Personagem, mundo: Mundo):
    print("""
As Ruínas de Nhar abrem-se como uma boca de pedra. O frio aumenta. A presença do Lich é palpável.
Uma porta selada pede duas luzes: Fogo e Luz. Talvez relíquias ajudem...
""")
    if mundo.pegou_reliquia_fogo and mundo.pegou_reliquia_luz:
        print("As relíquias queimam e iluminam os sigilos. A porta se abre sem resistência.")
        facil = True
    else:
        print("Sem as duas relíquias, você força a entrada, despertando guardiões.")
        facil = False
        guardiao = Inimigo("Guardião Ósseo", 26, 8, 18)
        if not batalha(jog, guardiao):
            return cena_derrota

    vida_boss = 58 if facil else 66
    forca_boss = 11 if facil else 13
    if mundo.fez_pacto:
        print("O Lich reconhece a sombra em você e ri: 'Metade do trabalho já fizeste'.")
        forca_boss += 2
    if mundo.derrotou_dragao:
        print("O eco do rugido do Dragão fortalece seu espírito.")
        jog.forca += 1
        jog.magia += 1

    lich = Inimigo("Lich Supremo", vida_boss, forca_boss, 50)
    venceu = batalha(jog, lich)

    if not venceu:
        return cena_final_tragico

    score_luz = jog.honra + jog.compaixao + (1 if "Luz" in jog.reliquias else 0)
    score_trevas = jog.trevas + (2 if mundo.fez_pacto else 0)
    if score_trevas >= score_luz + 2:
        return cena_final_sombrio
    if score_luz >= score_trevas + 2 and mundo.salvou_camponesa and mundo.derrotou_dragao:
        return cena_final_luminoso
    return cena_final_neutro

# --- finais / derrota ---
def cena_final_tragico(jog: Personagem, mundo: Mundo):
    print("""
=== FINAL TRÁGICO ===
Você cai diante do Lich. As Ruínas ecoam um riso gélido. A noite se aprofunda sobre o reino.
Talvez em outra vida, um herói reúna luz suficiente.
""")
    return None

def cena_final_luminoso(jog: Personagem, mundo: Mundo):
    print("""
=== FINAL LUMINOSO ===
Com honra e compaixão, você desfaz os selos das trevas. A alma do Lich se dissipa como cinza ao vento.
A camponesa resgatada acende uma vela por você; o reino canta seu nome. A aurora permanece.
""")
    return None

def cena_final_neutro(jog: Personagem, mundo: Mundo):
    print("""
=== FINAL DO CREPÚSCULO ===
O Lich cai, mas cicatrizes ficam. Nem luz plena, nem sombra total. O povo sobrevive, e você parte em silêncio,
levando no peito um amuleto que sussurra histórias não contadas.
""")
    return None

def cena_final_sombrio(jog: Personagem, mundo: Mundo):
    print("""
=== FINAL SOMBRIO ===
Você triunfa... mas a sombra do pacto consome suas cores. O trono fica vazio, e um novo sussurro toma as ruínas.
Seu nome será lembrado — não como herói, mas como aviso.
""")
    return None

def cena_derrota(jog: Personagem, mundo: Mundo):
    print("""
Seus passos cessam antes do destino. A história, inacabada, retorna à poeira.
""")
    return None

# ==============================
#      LOOP E INICIALIZAÇÃO
# ==============================
def criar_personagem() -> Personagem:
    print("=== Bem-vindo ao RPG Livro-Jogo ===")
    nome = simpledialog.askstring("Nome", "Nome do herói:") or "Herói"
    print("\nEscolha sua classe:")
    escolha = escolher({
        "1": "Guerreiro (Vida 30, Força 8, Magia 2)",
        "2": "Mago (Vida 22, Força 3, Magia 10)",
        "3": "Arqueiro (Vida 26, Força 6, Magia 4)"
    }, "Classe: ")
    if escolha == "1":
        return Personagem(nome, "Guerreiro", 30, 8, 2)
    if escolha == "
Editor is loading...
Leave a Comment