New.space.name / index.html
Vitorts's picture
Upload 5 files
334e859 verified
Raw
History Blame Contribute Delete
17.1 kB
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>O Pilar dos Deuses</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=EB+Garamond:ital,wght@0,400;0,500;1,400&display=swap');
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#0a0806;--bg2:#110e0a;--bg3:#1a150f;
--border:#2a1e0e;--gold:#c9a84c;--gold2:#e8c97a;
--red:#c0392b;--text:#d4c5a0;--text2:#a09070;--text3:#6b5a3e;--white:#f0e8d0;
}
body{background:var(--bg);color:var(--text);font-family:'EB Garamond',serif;height:100vh;display:flex;flex-direction:column;overflow:hidden;font-size:15px;}
::-webkit-scrollbar{width:3px}::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
/* MODAL */
#modal{position:fixed;inset:0;background:rgba(0,0,0,0.92);display:flex;align-items:center;justify-content:center;z-index:100}
.modal-box{background:var(--bg2);border:1px solid var(--gold);border-radius:10px;padding:28px 22px;width:92%;max-width:400px;box-shadow:0 0 60px rgba(201,168,76,0.15)}
.modal-title{text-align:center;margin-bottom:20px}
.modal-title .icon{font-size:40px;margin-bottom:8px}
.modal-title h1{font-family:'Cinzel',serif;color:var(--gold2);font-size:20px;letter-spacing:0.05em}
.modal-title p{color:var(--text3);font-size:10px;letter-spacing:0.2em;margin-top:6px}
.field-label{color:var(--text3);font-size:10px;letter-spacing:0.18em;text-transform:uppercase;margin-bottom:5px;margin-top:12px;display:block}
.field{width:100%;background:var(--bg3);border:1px solid var(--border);border-radius:5px;padding:9px 12px;color:var(--white);font-family:'EB Garamond',serif;font-size:14px;outline:none}
.field:focus{border-color:rgba(201,168,76,0.5)}
select.field option{background:var(--bg3)}
.start-btn{width:100%;background:linear-gradient(135deg,#8b1a00,#c0390b);border:1px solid var(--red);color:var(--white);font-family:'Cinzel',serif;font-size:14px;padding:13px;border-radius:6px;cursor:pointer;margin-top:16px;letter-spacing:0.06em}
/* HEADER */
#header{flex-shrink:0;background:var(--bg2);border-bottom:1px solid var(--border)}
.header-title{text-align:center;padding:8px 16px 6px;border-bottom:1px solid var(--border);font-family:'Cinzel',serif;color:var(--gold2);font-size:15px;letter-spacing:0.07em;text-shadow:0 0 20px rgba(201,168,76,0.3)}
.status-bar{display:flex;overflow-x:auto}
.stat-cell{padding:5px 10px;border-right:1px solid var(--border);min-width:fit-content;flex-shrink:0}
.stat-cell .sl{font-size:8px;color:var(--text3);letter-spacing:0.1em}
.stat-cell .sv{font-size:12px;font-weight:bold;white-space:nowrap;font-family:'Cinzel',serif}
.hp-bar{height:3px;background:#120e07}
.hp-fill{height:100%;background:linear-gradient(to right,#6b0000,#e05050);transition:width .6s}
/* CHAT */
#chat{flex:1;overflow-y:auto;padding:14px 12px;display:flex;flex-direction:column;gap:12px}
.empty{text-align:center;color:var(--text3);padding:40px 20px;font-style:italic;font-size:13px}
.empty .e-icon{font-size:34px;opacity:.25;margin-bottom:10px}
.msg-player{display:flex;justify-content:flex-end;animation:fi .3s ease}
.msg-player .bubble{background:rgba(201,168,76,0.08);border:1px solid rgba(201,168,76,0.2);border-radius:8px 0 8px 8px;padding:10px 12px;line-height:1.7;max-width:83%}
.msg-player .name{font-family:'Cinzel',serif;font-size:10px;color:var(--gold);text-align:right;margin-bottom:3px;letter-spacing:0.1em}
.msg-mj{display:flex;gap:9px;animation:fi .3s ease}
.mj-avatar{width:28px;height:28px;border-radius:50%;background:rgba(139,26,26,0.35);border:1px solid var(--red);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;margin-top:2px}
.mj-content{flex:1}
.mj-name{font-family:'Cinzel',serif;font-size:9px;color:var(--red);letter-spacing:0.14em;margin-bottom:4px}
.dice-badge{display:inline-flex;align-items:center;gap:6px;background:#140f08;border:1px solid var(--border);border-radius:4px;padding:3px 10px;margin-bottom:7px;font-size:11px}
.dice-num{font-size:20px;font-weight:bold;font-family:'Cinzel',serif}
.dice-label{color:var(--text3)}
.mj-bubble{background:#160f09;border:1px solid var(--border);border-radius:0 8px 8px 8px;padding:11px 13px;line-height:1.75;white-space:pre-wrap}
.msg-sys{animation:fi .3s ease}
.sys-bubble{background:rgba(46,109,164,0.08);border:1px solid rgba(46,109,164,0.25);border-radius:6px;padding:9px 12px;font-size:12px;color:#7aabcc;font-family:'Cinzel',serif}
.typing{display:flex;gap:9px}
.typing-dots{background:#160f09;border:1px solid var(--border);border-radius:0 8px 8px 8px;padding:14px 16px;display:flex;gap:6px;align-items:center}
.dot{width:8px;height:8px;border-radius:50%;background:var(--red);animation:bl 1.2s infinite}
/* INPUT */
#quick{flex-shrink:0;display:flex;gap:5px;padding:6px 10px 5px;overflow-x:auto;border-top:1px solid var(--border);background:var(--bg2)}
.qa{background:var(--bg3);border:1px solid var(--border);color:var(--text2);font-family:'Cinzel',serif;font-size:11px;padding:5px 9px;border-radius:3px;cursor:pointer;white-space:nowrap;flex-shrink:0}
.qa:disabled{opacity:.4}
#input-row{flex-shrink:0;display:flex;gap:8px;padding:7px 10px 11px;background:var(--bg2)}
#inp{flex:1;background:var(--bg3);border:1px solid var(--border);border-radius:5px;padding:9px 12px;color:var(--white);font-family:'EB Garamond',serif;font-size:15px;resize:none;outline:none;min-height:42px}
#inp:focus{border-color:rgba(201,168,76,0.4)}
#send-btn{background:linear-gradient(135deg,#8b1a00,#c0390b);border:1px solid var(--red);color:var(--white);font-family:'Cinzel',serif;font-size:13px;padding:9px 16px;border-radius:5px;cursor:pointer;white-space:nowrap}
#send-btn:disabled{opacity:.4;cursor:not-allowed}
#attrs{flex-shrink:0;display:flex;border-top:1px solid var(--border);background:#090706}
.attr-cell{flex:1;padding:4px 4px;text-align:center;border-right:1px solid var(--border)}
.attr-cell .al{font-size:8px;color:var(--text3)}
.attr-cell .av{font-size:11px;color:var(--text);font-weight:bold;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:'Cinzel',serif}
@keyframes fi{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}
@keyframes bl{0%,80%,100%{opacity:.15}40%{opacity:1}}
</style>
</head>
<body>
<!-- MODAL -->
<div id="modal">
<div class="modal-box">
<div class="modal-title">
<div class="icon">🔱</div>
<h1>O Pilar dos Deuses</h1>
<p>DEFINA SEU DESTINO</p>
</div>
<label class="field-label">Nome do Personagem</label>
<input class="field" id="f-name" type="text" placeholder="Como você é chamado..." maxlength="40">
<label class="field-label">Origem</label>
<select class="field" id="f-origin">
<option>Boreas — Norte de Ferro e Gelo</option>
<option>Austros — Sul de Areia e Luz</option>
<option>Fronteira do Pilar</option>
</select>
<label class="field-label">Linhagem</label>
<select class="field" id="f-lineage">
<option>Meio-Demônio (linhagem adormecida)</option>
<option>Humano comum</option>
<option>Tiefling exilado</option>
<option>Meio-Elfo errante</option>
<option>Aasimar renegado</option>
<option>Orc sem clã</option>
</select>
<label class="field-label">Vocação</label>
<select class="field" id="f-voc">
<option>Guerreiro Sem Nome</option>
<option>Ladrão de Informações</option>
<option>Mago Aprendiz</option>
<option>Mercenário</option>
<option>Nobre Empobrecido</option>
<option>Caçador de Recompensas</option>
</select>
<button class="start-btn" onclick="startGame()">✦ Despertar no Mundo ✦</button>
</div>
</div>
<!-- HEADER -->
<div id="header">
<div class="header-title">⚔ O Pilar dos Deuses ⚔</div>
<div class="status-bar" id="status-bar">
<div class="stat-cell"><div class="sl">👤 JOGADOR</div><div class="sv" id="s-name" style="color:#e8c97a"></div></div>
<div class="stat-cell"><div class="sl">❤️ HP</div><div class="sv" id="s-hp" style="color:#7ab87a">100/100</div></div>
<div class="stat-cell"><div class="sl">⭐ RANK</div><div class="sv" id="s-rank" style="color:#888">E</div></div>
<div class="stat-cell"><div class="sl">💰 OURO</div><div class="sv" id="s-gold" style="color:#c9a84c">50g</div></div>
<div class="stat-cell"><div class="sl">📅 DIA</div><div class="sv" id="s-dia">Dia 1</div></div>
<div class="stat-cell"><div class="sl">🔄 CICLO</div><div class="sv" id="s-ciclo">Despertar</div></div>
<div class="stat-cell"><div class="sl">📍 LOCAL</div><div class="sv" id="s-local" style="color:#e08020"></div></div>
</div>
<div class="hp-bar"><div class="hp-fill" id="hp-fill" style="width:100%"></div></div>
</div>
<!-- CHAT -->
<div id="chat">
<div class="empty"><div class="e-icon">🔱</div><div>O mundo aguarda sua chegada...</div></div>
</div>
<!-- QUICK ACTIONS -->
<div id="quick">
<button class="qa" onclick="qa('Examino o ambiente ao redor com cuidado.')">🔍 Examinar</button>
<button class="qa" onclick="qa('Procuro rumores e informações com os locais.')">🗣 Rumores</button>
<button class="qa" onclick="qa('Descanso para recuperar forças e mana.')">😴 Descansar</button>
<button class="qa" onclick="qa('Verifico o calendário e eventos importantes.')">📅 Agenda</button>
<button class="qa" onclick="qa('Analiso as relações diplomáticas entre os reinos.')">⚔️ Política</button>
<button class="qa" onclick="qa('Verifico meu inventário e status completo.')">🎒 Status</button>
</div>
<!-- INPUT -->
<div id="input-row">
<textarea id="inp" placeholder="O que você faz?" rows="1"
onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMsg()}"
oninput="this.style.height='auto';this.style.height=Math.min(this.scrollHeight,100)+'px'"></textarea>
<button id="send-btn" onclick="sendMsg()">Agir →</button>
</div>
<!-- ATTRS -->
<div id="attrs">
<div class="attr-cell"><div class="al">FOR</div><div class="av" id="a-for">10</div></div>
<div class="attr-cell"><div class="al">AGI</div><div class="av" id="a-agi">10</div></div>
<div class="attr-cell"><div class="al">MAN</div><div class="av" id="a-mana">5</div></div>
<div class="attr-cell"><div class="al">CAR</div><div class="av" id="a-car">8</div></div>
<div class="attr-cell"><div class="al">MEN</div><div class="av" id="a-men">10</div></div>
<div class="attr-cell"><div class="al"></div><div class="av" id="a-hora">06:00</div></div>
<div class="attr-cell"><div class="al">🌤</div><div class="av" id="a-clima"></div></div>
</div>
<script>
const RANK_COLORS = {E:"#888",D:"#a07040",C:"#60a060",B:"#5599cc",A:"#b080e0",S:"#e0b020","SS":"#e04080","SS+":"#c060ff"};
let gs = {hp:100,maxHp:100,gold:50,rank:"E",local:"",hora:"06:00",dia:"1",ciclo:"Despertar",clima:"—",for:10,agi:10,mana:5,car:8,men:10,name:"",lineage:""};
let messages = [];
let busy = false;
function parseStatus(text) {
const m = text.match(/STATUS\|(.+)/);
if (!m) return;
m[1].split("|").forEach(p => {
const idx = p.indexOf("=");
if (idx < 0) return;
const k = p.slice(0,idx).trim(), v = p.slice(idx+1).trim();
gs[k] = isNaN(v) ? v : Number(v);
});
}
function getDice(text) {
const m = text.match(/\[DADO:(\d+)\]/i);
return m ? parseInt(m[1]) : null;
}
function cleanText(text) {
return text.replace(/\[DADO:\d+\]\s*/i,"").replace(/STATUS\|.*/g,"").trim();
}
function updateUI() {
document.getElementById("s-name").textContent = gs.name || "—";
const hpPct = Math.max(0,(gs.hp/gs.maxHp)*100);
document.getElementById("s-hp").textContent = gs.hp+"/"+gs.maxHp;
document.getElementById("s-hp").style.color = hpPct<30?"#c0392b":hpPct<60?"#e08020":"#7ab87a";
document.getElementById("hp-fill").style.width = hpPct+"%";
document.getElementById("hp-fill").style.background = hpPct<30?"linear-gradient(to right,#6b0000,#c0392b)":"linear-gradient(to right,#6b0000,#e05050)";
document.getElementById("s-rank").textContent = gs.rank;
document.getElementById("s-rank").style.color = RANK_COLORS[gs.rank]||"#888";
document.getElementById("s-gold").textContent = gs.gold+"g";
document.getElementById("s-dia").textContent = "Dia "+gs.dia;
document.getElementById("s-ciclo").textContent = gs.ciclo||"—";
document.getElementById("s-local").textContent = gs.local||"—";
document.getElementById("a-for").textContent = gs.for;
document.getElementById("a-agi").textContent = gs.agi;
document.getElementById("a-mana").textContent = gs.mana;
document.getElementById("a-car").textContent = gs.car;
document.getElementById("a-men").textContent = gs.men;
document.getElementById("a-hora").textContent = gs.hora;
document.getElementById("a-clima").textContent = gs.clima||"—";
}
function addMsg(role, text) {
const chat = document.getElementById("chat");
const empty = chat.querySelector(".empty");
if (empty) empty.remove();
const div = document.createElement("div");
if (role === "player") {
div.className = "msg-player";
div.innerHTML = `<div><div class="name">${gs.name} ⚔️</div><div class="bubble">${esc(text)}</div></div>`;
} else if (role === "sys") {
div.className = "msg-sys";
div.innerHTML = `<div class="sys-bubble">📜 ${esc(text)}</div>`;
} else {
const dice = getDice(text);
const body = cleanText(text);
let diceHtml = "";
if (dice !== null) {
const dc = dice===20?"#7af07a":dice===1?"#c0392b":"#e8c97a";
const dl = dice===20?"⚡ CRÍTICO!":dice===1?"💀 FALHA CATASTRÓFICA":dice>=15?"✓ Sucesso":dice>=8?"〜 Parcial":"✗ Falha";
diceHtml = `<div class="dice-badge"><span class="dice-num" style="color:${dc}">${dice}</span><span class="dice-label">/20 — ${dl}</span></div>`;
}
div.className = "msg-mj";
div.innerHTML = `
<div class="mj-avatar">👁</div>
<div class="mj-content">
<div class="mj-name">MESTRE DE JOGO</div>
${diceHtml}
<div class="mj-bubble">${esc(body)}</div>
</div>`;
}
chat.appendChild(div);
setTimeout(()=>div.scrollIntoView({behavior:"smooth",block:"end"}),50);
}
function addTyping() {
const chat = document.getElementById("chat");
const div = document.createElement("div");
div.id = "typing";
div.className = "typing";
div.innerHTML = `<div class="mj-avatar">👁</div><div class="typing-dots"><span class="dot" style="animation-delay:0s"></span><span class="dot" style="animation-delay:.2s"></span><span class="dot" style="animation-delay:.4s"></span></div>`;
chat.appendChild(div);
div.scrollIntoView({behavior:"smooth"});
}
function removeTyping() {
const t = document.getElementById("typing");
if (t) t.remove();
}
function setDisabled(v) {
busy = v;
document.getElementById("send-btn").disabled = v;
document.querySelectorAll(".qa").forEach(b=>b.disabled=v);
}
function esc(t) {
return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\n/g,"<br>");
}
async function callAPI(userText) {
messages.push({role:"user", content: userText});
const res = await fetch("/api/chat", {
method:"POST",
headers:{"Content-Type":"application/json"},
body: JSON.stringify({ messages, char: gs })
});
const data = await res.json();
if (data.error) throw new Error(data.error);
messages.push({role:"assistant", content: data.reply});
return data.reply;
}
async function startGame() {
const name = document.getElementById("f-name").value.trim();
if (!name) { alert("Digite seu nome!"); return; }
gs.name = name;
gs.lineage = document.getElementById("f-lineage").value;
gs.local = document.getElementById("f-origin").value.split("—")[0].trim();
document.getElementById("modal").style.display = "none";
updateUI();
setDisabled(true);
addTyping();
try {
const intro = `Inicie o RPG. Sou ${name}, ${gs.lineage}, vocação ${document.getElementById("f-voc").value}, de ${gs.local}. Crie cena de abertura épica e preencha STATUS com atributos iniciais.`;
const reply = await callAPI(intro);
parseStatus(reply);
updateUI();
removeTyping();
addMsg("mj", reply);
} catch(e) {
removeTyping();
addMsg("sys", "Erro ao iniciar: "+e.message);
}
setDisabled(false);
}
async function sendMsg() {
const inp = document.getElementById("inp");
const text = inp.value.trim();
if (!text || busy) return;
inp.value = "";
inp.style.height = "auto";
addMsg("player", text);
setDisabled(true);
addTyping();
try {
const reply = await callAPI(text);
parseStatus(reply);
updateUI();
removeTyping();
addMsg("mj", reply);
} catch(e) {
removeTyping();
addMsg("sys", "Erro: "+e.message);
}
setDisabled(false);
inp.focus();
}
function qa(text) {
if (busy) return;
document.getElementById("inp").value = text;
sendMsg();
}
</script>
</body>
</html>