Files
lolero/index.html
T

1545 lines
100 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="es-AR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="description" content="1v9: Convertite en Leyenda — simulador de carrera de pro player de League of Legends. Elegí tu región, farmeá el sueño, ganá Worlds. Todo en 2 minutos.">
<meta name="theme-color" content="#0a0a0c">
<title>1v9: Convertite en Leyenda — Simulador de carrera LoL</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='16' fill='%230a0a0c'/%3E%3Ctext x='50' y='66' font-size='46' text-anchor='middle' fill='%23d9730a' font-family='sans-serif' font-weight='bold'%3E1v9%3C/text%3E%3C/svg%3E">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
:root{
--bg-base:#0a0a0c; --bg-card:#18181b; --bg-card-alt:#1f1f23; --bg-row-active:#0e3a3f;
--accent-ovr:#d9730a; --accent-role:#7c3aed;
--text-primary:#f4f4f5; --text-secondary:#a1a1aa; --text-muted:#52525b;
--success:#22c55e; --danger:#ef4444; --border-subtle:#27272a;
--radius-card:12px; --radius-badge:6px; --radius-pill:999px;
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{background:var(--bg-base); color:var(--text-primary); font-family:'Inter',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif; display:flex; justify-content:center; min-height:100vh; overflow-x:hidden}
#app{width:100%; max-width:1100px; min-height:100dvh; display:flex; flex-direction:column; position:relative; margin:0 auto}
.screen{display:none; flex-direction:column; padding:20px 20px calc(24px + env(safe-area-inset-bottom)); flex:1; animation:fadeIn .3s ease}
.screen.active{display:flex}
@keyframes fadeIn{from{opacity:0; transform:translateY(6px)}to{opacity:1; transform:none}}
button{font-family:inherit; cursor:pointer; border:none; background:none; color:inherit}
h1,h2,h3{font-weight:800; letter-spacing:.01em}
.btn{display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:13px 22px; border-radius:var(--radius-pill); background:var(--accent-ovr); color:#1a1a1a; font-weight:800; font-size:15px; transition:transform .12s ease}
.btn:active{transform:scale(.97)}
.btn.secondary{background:var(--bg-card-alt); color:var(--text-primary); border:1px solid var(--border-subtle)}
.btn.ghost{background:transparent; border:1px solid var(--border-subtle); color:var(--text-secondary)}
.btn:disabled{opacity:.45; pointer-events:none}
.badge{display:inline-flex; align-items:center; gap:5px; padding:3px 8px; border-radius:var(--radius-badge); font-size:11px; font-weight:700; letter-spacing:.03em}
.badge-dim{background:var(--bg-card-alt); color:var(--text-secondary); border:1px solid var(--border-subtle)}
.badge-role{background:var(--accent-role); color:#fff}
.badge-ovr{background:var(--accent-ovr); color:#1a1a1a}
.pill{display:inline-flex; align-items:center; gap:4px; padding:3px 10px; border-radius:var(--radius-pill); font-size:11px; font-weight:800}
.pill.success{background:rgba(34,197,94,.14); color:var(--success); border:1px solid rgba(34,197,94,.35)}
.pill.danger{background:rgba(239,68,68,.14); color:var(--danger); border:1px solid rgba(239,68,68,.35)}
.pill.neutral{background:var(--bg-card-alt); color:var(--text-secondary); border:1px solid var(--border-subtle)}
.stat-num{font-size:24px; font-weight:800; line-height:1; color:var(--text-primary)}
.stat-lbl{font-size:9px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--text-muted); margin-top:4px}
.smallprint{font-size:10.5px; color:var(--text-muted); text-align:center; line-height:1.5; margin-top:10px}
.fallback{display:flex; align-items:center; justify-content:center; font-weight:800; color:var(--text-muted); background:var(--bg-card-alt); border:1px solid var(--border-subtle); border-radius:var(--radius-badge); overflow:hidden}
img.logo{object-fit:contain; background:var(--bg-card-alt); border:1px solid var(--border-subtle); border-radius:var(--radius-badge)}
#landing{justify-content:center; text-align:center; gap:14px}
.logo-big{font-weight:800; font-size:76px; line-height:.9; letter-spacing:-.02em; background:linear-gradient(180deg,#fff,var(--accent-ovr)); -webkit-background-clip:text; background-clip:text; color:transparent}
.logo-sub{font-size:20px; color:var(--text-secondary); letter-spacing:.14em; text-transform:uppercase; font-weight:700}
.tagline{color:var(--text-secondary); font-size:14.5px; line-height:1.55; max-width:420px; margin:0 auto}
.land-tags{display:flex; gap:8px; justify-content:center; flex-wrap:wrap; margin-top:6px}
.land-tags span{font-size:11px; color:var(--text-secondary); border:1px solid var(--border-subtle); border-radius:var(--radius-pill); padding:5px 12px; background:var(--bg-card)}
.logo-strip{display:flex; gap:10px; justify-content:center; flex-wrap:wrap; margin:18px 0 6px; opacity:.9}
.logo-strip img, .logo-strip .fallback{width:40px; height:40px; border-radius:var(--radius-badge); border:1px solid var(--border-subtle); padding:3px}
.hall{width:100%; max-width:480px; background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:14px 16px; margin:14px auto 0; text-align:left}
.hall h3{color:var(--accent-ovr); font-size:15px; letter-spacing:.05em}
.hall ol{list-style:none; margin-top:8px}
.hall li{display:flex; align-items:center; gap:10px; padding:7px 0; border-bottom:1px dashed var(--border-subtle); font-size:13.5px}
.hall li:last-child{border-bottom:none}
.hall .medal{width:26px; height:26px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:800; color:#1a1a1a; flex:none}
.hall li:first-child .medal{background:var(--accent-ovr)}
.hall li:nth-child(2) .medal{background:#b8bfcc}
.hall li:nth-child(3) .medal{background:#b07d4f}
.hall .rval{margin-left:auto; color:var(--text-primary); font-weight:800; font-size:14px}
.hall .empty{color:var(--text-muted); font-size:13px}
#create{justify-content:flex-start; gap:18px; max-width:560px; margin:0 auto; width:100%}
.create-head h2{font-size:28px}
.create-head p{color:var(--text-muted); font-size:13.5px; margin-top:2px}
.field label{display:block; font-size:11px; font-weight:700; letter-spacing:.08em; color:var(--text-secondary); text-transform:uppercase; margin-bottom:8px}
.input{width:100%; padding:13px 16px; border-radius:var(--radius-card); border:1px solid var(--border-subtle); background:var(--bg-card); color:var(--text-primary); font-size:15px; outline:none}
.input:focus{border-color:var(--accent-ovr)}
.row2{display:grid; grid-template-columns:1fr 1fr; gap:12px}
.seg{display:grid; grid-auto-flow:column; grid-auto-columns:1fr; gap:8px}
.seg .opt{padding:12px 6px; border-radius:var(--radius-card); border:1px solid var(--border-subtle); background:var(--bg-card); text-align:center; font-size:13px; font-weight:700; color:var(--text-secondary)}
.seg .opt small{display:block; font-size:10.5px; color:var(--text-muted); font-weight:600; margin-top:2px}
.seg .opt.on{border-color:var(--accent-ovr); color:var(--text-primary); background:var(--bg-card-alt)}
.grid2{display:grid; grid-template-columns:1fr 1fr; gap:10px}
.region-card{padding:13px; border-radius:var(--radius-card); border:1px solid var(--border-subtle); background:var(--bg-card); transition:all .15s; text-align:left}
.region-card h3{font-size:17px}
.region-card p{font-size:11px; color:var(--text-muted); line-height:1.45; margin-top:4px}
.region-card.on{border-color:var(--accent-ovr); background:var(--bg-card-alt); transform:translateY(-1px)}
.role-grid{display:grid; grid-template-columns:repeat(5,1fr); gap:8px}
.role-card{padding:12px 6px; border-radius:var(--radius-card); border:1px solid var(--border-subtle); background:var(--bg-card); text-align:center}
.role-card .em{font-size:24px}
.role-card .nm{font-weight:800; font-size:12px; margin-top:4px}
.role-card .st{font-size:9.5px; color:var(--text-muted); margin-top:2px; line-height:1.3}
.role-card.on{border-color:var(--accent-role); background:var(--bg-card-alt)}
.hint{font-size:11.5px; color:var(--text-muted); line-height:1.5}
#career{justify-content:flex-start; gap:16px}
.career-grid{display:grid; grid-template-columns:3fr 2fr; gap:18px; align-items:start}
.career-left{display:flex; flex-direction:column; gap:14px; min-width:0}
.player-card{display:flex; gap:14px; background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:16px}
.elo-badge{width:74px; height:74px; border-radius:var(--radius-card); background:var(--accent-ovr); display:flex; flex-direction:column; align-items:center; justify-content:center; flex:none; color:#1a1a1a}
.elo-badge .lbl{font-size:9px; font-weight:800; letter-spacing:.1em}
.elo-badge .num{font-size:34px; font-weight:800; line-height:1; margin-top:2px}
.player-meta{flex:1; min-width:0; display:flex; flex-direction:column; gap:6px}
.player-meta .meta-top{display:flex; align-items:center; gap:6px; flex-wrap:wrap}
.player-meta .meta-top .right{margin-left:auto; display:flex; align-items:center; gap:8px; font-size:11px; color:var(--text-secondary); font-weight:700}
.player-meta .org-line{display:flex; align-items:center; gap:8px; margin-top:2px}
.player-meta .org-line img, .player-meta .org-line .fallback{width:30px; height:30px; border-radius:var(--radius-badge); padding:2px}
.player-meta .org-line .org-name{font-size:17px; font-weight:800; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.player-meta .value-line{font-size:12px; color:var(--text-secondary)}
.player-meta .value-line b{color:var(--text-primary); font-weight:800}
.stats-row3{display:grid; grid-template-columns:repeat(3,1fr); gap:8px}
.stat3{background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:12px 8px; text-align:center}
.stat3 .ic{font-size:16px; margin-bottom:3px}
.stat3 .stat-num{font-size:26px}
.stat3 .stat-lbl{font-size:10px}
.achievements{background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:16px; text-align:center; min-height:66px}
.achievements .empty-ach{color:var(--text-muted); font-size:13px; font-weight:700; letter-spacing:.06em; text-transform:uppercase}
.achievements .ach-trophy{font-size:30px; opacity:.4; margin-bottom:6px}
.achievements .ach-list{display:flex; flex-wrap:wrap; gap:6px; justify-content:center}
.achievements .ach-chip{display:inline-flex; align-items:center; gap:5px; font-size:11px; font-weight:700; padding:4px 10px; border-radius:var(--radius-pill); background:var(--bg-card-alt); border:1px solid var(--border-subtle)}
.decision-card{background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:18px; animation:fadeIn .3s ease}
.decision-card .dc-cat{font-size:10px; font-weight:800; letter-spacing:.12em; color:var(--text-secondary); text-transform:uppercase; margin-bottom:6px}
.decision-card h2{font-size:20px; line-height:1.15}
.decision-card .dc-ctx{font-size:13px; color:var(--text-secondary); line-height:1.5; margin:8px 0 14px}
.opt-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:10px}
.opt-card{position:relative; display:flex; flex-direction:column; align-items:center; gap:8px; padding:14px 10px; border-radius:var(--radius-card); border:1px solid var(--border-subtle); background:var(--bg-card-alt); transition:all .15s; text-align:center}
.opt-card:active{transform:scale(.97)}
.opt-card:hover{border-color:var(--text-secondary)}
.opt-card .oc-label{font-size:9px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--text-muted)}
.opt-card .oc-emblem,.opt-card .oc-emblem-fb{width:64px; height:64px; border-radius:var(--radius-card); padding:4px}
.opt-card .oc-emblem-fb{display:flex; align-items:center; justify-content:center; font-size:18px; font-weight:800; color:var(--text-secondary); background:var(--bg-card); border:1px solid var(--border-subtle)}
.opt-card .oc-title{font-size:14px; font-weight:800; line-height:1.2}
.opt-card .oc-desc{font-size:10.5px; color:var(--text-muted); line-height:1.35}
.opt-card .oc-tag{font-size:10px; font-weight:700; color:var(--text-secondary); padding:3px 9px; border-radius:var(--radius-badge); background:var(--bg-card); border:1px solid var(--border-subtle)}
.opt-card .oc-pills{display:flex; gap:6px; justify-content:center; flex-wrap:wrap}
.opt-card.wide{grid-column:1/-1}
.timeline{background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); overflow:hidden}
.timeline .tl-head{display:flex; align-items:center; gap:8px; padding:10px 14px; font-size:9px; font-weight:800; letter-spacing:.08em; color:var(--text-muted); text-transform:uppercase; border-bottom:1px solid var(--border-subtle)}
.tl-head .c-idx{width:38px; flex:none}
.tl-head .c-org{flex:1}
.tl-head .c-num{width:40px; text-align:right; flex:none}
.tl-row{display:flex; align-items:center; gap:8px; padding:9px 14px; border-bottom:1px solid var(--border-subtle); font-size:12px}
.tl-row:last-of-type{border-bottom:none}
.tl-row .c-idx{width:38px; flex:none; display:flex; align-items:center; gap:5px}
.tl-row .c-org{flex:1; display:flex; align-items:center; gap:7px; min-width:0}
.tl-row .c-org img,.tl-row .c-org .fallback{width:22px; height:22px; border-radius:5px; padding:1px; flex:none}
.tl-row .c-org .em{font-size:11px; font-weight:700; color:var(--text-secondary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.tl-row .c-num{width:40px; text-align:right; flex:none; font-weight:700; color:var(--text-secondary)}
.tl-row .c-elo{width:40px; text-align:center; flex:none; font-weight:800; color:var(--accent-ovr)}
.tl-row .c-arr{font-size:11px; color:var(--accent-ovr); flex:none}
.tl-row.active{background:var(--bg-row-active); border-left:3px solid var(--success)}
.tl-row.active .c-org .em{color:var(--text-primary); font-weight:800}
.tl-row.future .c-org .em{color:var(--text-muted)}
.tl-row.future .c-num{color:var(--text-muted)}
.split-badge{width:26px; height:26px; border-radius:var(--radius-badge); display:inline-flex; align-items:center; justify-content:center; font-size:11px; font-weight:800; flex:none}
.split-badge.early{background:rgba(56,189,248,.18); color:#7dd3fc}
.split-badge.mid{background:rgba(59,130,246,.2); color:#93c5fd}
.split-badge.late{background:rgba(34,197,94,.2); color:#86efac}
.tl-final{display:flex; align-items:center; gap:8px; padding:10px 14px; border-top:2px solid var(--border-subtle); background:var(--bg-card-alt); font-size:12px}
.tl-final .f-flag{font-size:16px; flex:none}
.tl-final .f-name{flex:1; font-weight:800}
.tl-final .c-num{width:40px; text-align:right; flex:none; font-weight:700; color:var(--text-secondary)}
#resultOverlay{position:fixed; inset:0; background:rgba(4,5,8,.74); backdrop-filter:blur(5px); z-index:50; display:none; align-items:center; justify-content:center; padding:24px}
#resultOverlay.show{display:flex}
.result-card{width:100%; max-width:440px; background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:24px 22px; text-align:center; animation:pop .3s cubic-bezier(.2,1.4,.4,1)}
@keyframes pop{from{transform:scale(.85); opacity:0}to{transform:scale(1); opacity:1}}
.result-card .verdict{font-size:30px; letter-spacing:.02em; font-weight:800}
.result-card .verdict.bad{color:var(--danger)}
.result-card .verdict.great{color:var(--success)}
.result-card .rtext{font-size:14px; line-height:1.6; color:var(--text-secondary); margin:10px 0 6px}
.result-card .deltas{display:flex; flex-wrap:wrap; gap:6px; justify-content:center; margin:12px 0 18px}
.result-card .delta{font-size:11px; font-weight:800; padding:4px 9px; border-radius:var(--radius-badge); background:var(--bg-card-alt); border:1px solid var(--border-subtle)}
.result-card .delta.up{color:var(--success); border-color:rgba(34,197,94,.4)}
.result-card .delta.down{color:var(--danger); border-color:rgba(239,68,68,.4)}
.result-card .delta.money{color:var(--accent-ovr); border-color:rgba(217,115,10,.4)}
.team-change{display:flex; align-items:center; justify-content:center; gap:12px; margin:14px 0 4px}
.team-change .tc-logo{width:56px; height:56px; border-radius:var(--radius-card); padding:5px}
.team-change .tc-fb{width:56px; height:56px; border-radius:var(--radius-card); display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:800; color:var(--text-secondary); background:var(--bg-card-alt); border:1px solid var(--border-subtle)}
.team-change .arrow{color:var(--accent-ovr); font-size:20px; font-weight:800}
#summary{justify-content:flex-start; gap:16px}
.sum-head{text-align:center}
.sum-head h2{font-size:34px; letter-spacing:.02em; line-height:1}
.sum-head .sub{font-size:13px; color:var(--text-muted); margin-top:6px}
.sum-top{display:grid; grid-template-columns:repeat(3,1fr); gap:12px}
.sum-panel{background:var(--bg-card); border:1px solid var(--border-subtle); border-radius:var(--radius-card); padding:16px}
.sum-panel .sp-label{font-size:9px; font-weight:800; letter-spacing:.1em; color:var(--text-muted); text-transform:uppercase; margin-bottom:8px}
.sum-panel .sp-nick{font-size:22px; font-weight:800; word-break:break-word}
.sum-panel .sp-row{display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin-top:6px}
.sum-panel .sp-value{font-size:12px; color:var(--text-secondary); margin-top:6px}
.sum-panel .sp-value b{color:var(--text-primary); font-weight:800}
.sum-panel .big-elo{width:64px; height:64px; border-radius:var(--radius-card); background:var(--accent-ovr); display:flex; flex-direction:column; align-items:center; justify-content:center; color:#1a1a1a; margin-left:auto}
.sum-panel .big-elo .lbl{font-size:8px; font-weight:800; letter-spacing:.1em}
.sum-panel .big-elo .num{font-size:28px; font-weight:800; line-height:1}
.sum-panel .sp-stats{display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-top:12px}
.sum-panel .sp-stats .stat3{background:var(--bg-card-alt); border-color:var(--border-subtle)}
.sum-panel .sp-flag{font-size:34px; text-align:center; margin:6px 0 2px}
.sum-panel .sp-region{font-size:16px; font-weight:800; text-align:center}
.sum-panel .sp-region-sub{font-size:11px; color:var(--text-muted); text-align:center; margin-bottom:10px}
.ach-grid{display:flex; flex-wrap:wrap; gap:6px; justify-content:center; margin-top:10px}
.ach-grid .ach-chip{font-size:11px; font-weight:700; padding:5px 11px; border-radius:var(--radius-pill); background:var(--bg-card-alt); border:1px solid var(--border-subtle)}
.ach-empty{color:var(--text-muted); font-size:12px; text-align:center; padding:14px 0; font-weight:700; letter-spacing:.05em; text-transform:uppercase}
.ach-empty .ic{display:block; font-size:22px; margin-bottom:5px; opacity:.4}
.orgs-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:12px}
.org-card{position:relative; border-radius:var(--radius-card); padding:16px 12px; text-align:center; overflow:hidden; border:1px solid var(--border-subtle)}
.org-card .og-bg{position:absolute; inset:0; opacity:.18; z-index:0; background:radial-gradient(circle at 50% 0, var(--og-color,#7c3aed), transparent 70%)}
.org-card > *{position:relative; z-index:1}
.org-card .og-logo{width:56px; height:56px; border-radius:var(--radius-card); padding:4px; margin:0 auto 8px; background:var(--bg-card); border:1px solid var(--border-subtle)}
.org-card .og-fb{width:56px; height:56px; border-radius:var(--radius-card); display:flex; align-items:center; justify-content:center; font-size:16px; font-weight:800; color:var(--text-primary); background:var(--bg-card); border:1px solid var(--border-subtle); margin:0 auto 8px}
.org-card .og-name{font-size:13px; font-weight:800; line-height:1.2}
.org-card .og-div{font-size:9px; font-weight:700; color:var(--text-secondary); margin-top:2px}
.org-card .og-stats{display:flex; justify-content:center; gap:14px; margin-top:10px; font-size:11px; color:var(--text-secondary)}
.org-card .og-stats b{color:var(--text-primary)}
.sum-quote{font-size:13px; color:var(--text-muted); font-style:italic; text-align:center; line-height:1.5}
.sumbtns{display:flex; justify-content:flex-end; gap:10px; margin-top:4px}
#toast{position:fixed; bottom:24px; left:50%; transform:translateX(-50%) translateY(80px); z-index:99; background:var(--bg-card-alt); border:1px solid var(--accent-ovr); color:var(--text-primary); padding:12px 20px; border-radius:var(--radius-pill); font-size:13px; font-weight:700; transition:transform .3s ease; box-shadow:0 8px 30px rgba(0,0,0,.4); max-width:88vw; text-align:center}
#toast.show{transform:translateX(-50%) translateY(0)}
@media (max-width:768px){
.career-grid{grid-template-columns:1fr; gap:14px}
.timeline{order:2}
.sum-top{grid-template-columns:1fr}
.orgs-grid{grid-template-columns:repeat(2,1fr)}
.role-grid{grid-template-columns:repeat(3,1fr)}
.sumbtns{flex-direction:column}
.sumbtns .btn{width:100%}
}
.tl-collapse{display:none}
@media (max-width:768px){
.timeline .tl-collapse{display:flex; align-items:center; justify-content:space-between; padding:11px 14px; cursor:pointer; font-size:11px; font-weight:800; color:var(--text-secondary); letter-spacing:.08em; text-transform:uppercase; border-bottom:1px solid var(--border-subtle)}
.timeline .tl-body.closed{display:none}
}
</style>
</head>
<body>
<div id="app">
<section id="landing" class="screen active">
<div class="logo-big">1v9</div>
<div class="logo-sub">Convertite en leyenda</div>
<p class="tagline">Simulá tu carrera completa de pro player de <b>League of Legends</b>: de la challenger a los playoffs, del tilt al Worlds. Cada carrera dura 2 minutos, cada carrera es distinta.</p>
<div class="land-tags"><span>&#9876;&#65039; 5 roles</span><span>&#127758; 6 regiones</span><span>&#127942; Worlds</span><span>&#129398; &lt; 2 min</span></div>
<div class="logo-strip" id="stripLogos"></div>
<button class="btn" id="btnStart" style="margin:6px auto 0">Convertite en leyenda</button>
<div class="hall" id="hall" style="display:none">
<h3>&#127942; Salón de la fama</h3>
<div id="hallBody"></div>
</div>
<p class="smallprint" style="margin-top:14px">Parodia fan-made sin fines de lucro. Logos de sus respectivos dueños.</p>
</section>
<section id="create" class="screen">
<div class="create-head">
<h2>Armá tu player</h2>
<p>Todo empieza con un nick de mierda y cero miedo.</p>
</div>
<div class="field"><label>Nick / gamertag</label>
<input class="input" id="inpNick" type="text" maxlength="18" placeholder="xXFeederXx" autocomplete="off" spellcheck="false">
</div>
<div class="row2">
<div class="field"><label>N&deg; de invocador</label>
<input class="input" id="inpNum" type="number" min="0" max="99" value="13">
</div>
<div class="field"><label>Mano del mouse</label>
<div class="seg" id="segHand">
<button class="opt" data-v="diestro"><b>Diestro</b><small>el 99% de la gente</small></button>
<button class="opt" data-v="zurdo"><b>Zurdo</b><small>main Zed legendario</small></button>
</div>
</div>
</div>
<div class="field"><label>Región de servidor</label>
<div class="grid2" id="regionGrid"></div>
</div>
<div class="field"><label>Rol</label>
<div class="role-grid" id="roleGrid"></div>
</div>
<p class="hint" id="createHint">En tu región te van a tirar <b>ofertas de equipos</b> para arrancar la carrera. La rompés, te venden, subís. Eso o volvés a la academia, no hay punto medio.</p>
<button class="btn" id="btnBegin" style="margin-top:6px">Firme para el draft</button>
</section>
<section id="career" class="screen">
<div class="career-grid">
<div class="career-left">
<div class="player-card" id="playerCard"></div>
<div class="stats-row3" id="statsRow3"></div>
<div class="achievements" id="achievementsBox"></div>
<div class="decision-card" id="decisionBox"></div>
</div>
<div class="timeline" id="timelineBox"></div>
</div>
</section>
<section id="summary" class="screen">
<div class="sum-head">
<h2>Carrera terminada</h2>
<div class="sub" id="sumSub"></div>
</div>
<div class="sum-top" id="sumTop"></div>
<div class="orgs-grid" id="orgsGrid"></div>
<div class="sum-quote" id="sumQuote"></div>
<div class="sumbtns">
<button class="btn" id="btnExport">&#11015; Descargar imagen</button>
<button class="btn secondary" id="btnAgain">&#8635; Jugar de nuevo</button>
</div>
<p class="smallprint">Compartila con el que te dijo que el LoL era una perdida de tiempo.<br>Jugá tu carrera en <b>lolero.cbcren.online</b></p>
</section>
</div>
<div id="resultOverlay"><div id="resultCard"></div></div>
<div id="toast"></div>
<script>
'use strict';
/* 1v9: CONVERTITE EN LEYENDA — simulador de carrera LoL. Ui estilo Copero. */
/* ---------- REGIONES ---------- */
const REGIONS = {
LCK: { name: 'LCK', code: 'KR', country: 'Corea del Sur', flag: '🇰🇷', flavor: 'Disciplina brutal, bootcamps interminables y el estándar de oro del mundo.', threshold: 68, accent: '#e2574c' },
LPL: { name: 'LPL', code: 'CN', country: 'China', flag: '🇨🇳', flavor: 'Presupuesto infinito, scrims contra 30 equipos y fanáticos extremos.', threshold: 66, accent: '#e2574c' },
LEC: { name: 'LEC', code: 'EU', country: 'Europa', flag: '🇪🇺', flavor: 'Memes, huevos con panceta y la ilusión renovada cada año.', threshold: 61, accent: '#3a6df0' },
LCS: { name: 'LCS', code: 'NA', country: 'Norteamérica', flag: '🇺🇸', flavor: 'Sponsors raros, contenido de streaming y la eterna promesa del año del cambio.', threshold: 56, accent: '#3a6df0' },
LTA: { name: 'LTA', code: 'AM', country: 'Latinoamérica + Brasil', flag: '🇧🇷', flavor: 'Pasión, presupuesto ajustado y la afición más ruidosa del planeta.', threshold: 51, accent: '#4ecb71' },
VCS: { name: 'VCS', code: 'VN', country: 'Vietnam', flag: '🇻🇳', flavor: 'Agresividad total: 5v5 a los 3 minutos, cero miedo, cero farmeo.', threshold: 50, accent: '#4ecb71' },
};
const LOGO_EXT = {"100T":"png","6K":"webp","AllKnights":"webp","BDS":"webp","BLG":"png","BRION":"webp","C9":"png","Cerberus":"webp","DRX":"png","Dignitas":"svg","DplusKIA":"png","EDG":"png","EVOS":"webp","Estral":"webp","FPX":"svg","FURIA":"webp","FURIAAcad":"webp","FearX":"webp","Fluxo":"webp","FluxoAcad":"webp","FlyQuest":"png","Fnatic":"png","Furious":"webp","G2":"webp","GAM":"webp","GenG":"png","Hanwha":"png","IG":"png","INTZ":"webp","INTZAcad":"webp","Isurus":"webp","JDG":"png","KT":"webp","KaBuM":"webp","KaBuMAcad":"webp","Karmine":"png","Keyd":"webp","Kwangdong":"webp","LNG":"png","LOUD":"png","LOUDAcad":"webp","Leviatan":"webp","MAD":"svg","MGNBlue":"webp","MRiders":"webp","NRG":"webp","Nongshim":"webp","OMG":"webp","RED":"webp","REDAcad":"webp","RNG":"svg","RareAtom":"webp","Rebirth":"webp","Rogue":"svg","SBTC":"webp","SK":"png","ShoRebel":"webp","T1":"png","TES":"webp","TL":"svg","TSM":"png","TeamSecret":"webp","UltraPrime":"webp","Vikings":"webp","Vitality":"webp","WBG":"png","paiN":"png","paiNAcad":"webp"};
const TEAMS = {
LCK: [
{ id: 'T1', name: 'T1', div: 1 }, { id: 'GenG', name: 'Gen.G', div: 1 },
{ id: 'Hanwha', name: 'Hanwha Life Esports', div: 1 }, { id: 'KT', name: 'KT Rolster', div: 1 },
{ id: 'DplusKIA', name: 'Dplus KIA', div: 1 }, { id: 'DRX', name: 'DRX', div: 1 },
{ id: 'FearX', name: 'FearX', div: 2 }, { id: 'BRION', name: 'OKSavingsBank BRION', div: 2 },
{ id: 'Nongshim', name: 'Nongshim RedForce', div: 2 }, { id: 'Kwangdong', name: 'Kwangdong Freecs', div: 2 },
],
LPL: [
{ id: 'JDG', name: 'JD Gaming', div: 1 }, { id: 'BLG', name: 'Bilibili Gaming', div: 1 },
{ id: 'TES', name: 'Top Esports', div: 1 }, { id: 'LNG', name: 'LNG Esports', div: 1 },
{ id: 'WBG', name: 'Weibo Gaming', div: 1 }, { id: 'EDG', name: 'EDward Gaming', div: 1 },
{ id: 'IG', name: 'Invictus Gaming', div: 2 }, { id: 'RNG', name: 'Royal Never Give Up', div: 2 },
{ id: 'FPX', name: 'FunPlus Phoenix', div: 2 }, { id: 'OMG', name: 'Oh My God', div: 2 },
{ id: 'RareAtom', name: 'Rare Atom', div: 2 }, { id: 'UltraPrime', name: 'Ultra Prime', div: 2 },
],
LEC: [
{ id: 'G2', name: 'G2 Esports', div: 1 }, { id: 'Fnatic', name: 'Fnatic', div: 1 },
{ id: 'Vitality', name: 'Team Vitality', div: 1 }, { id: 'Karmine', name: 'Karmine Corp', div: 1 },
{ id: 'MAD', name: 'MAD Lions KOI', div: 1 }, { id: 'SK', name: 'SK Gaming', div: 1 },
{ id: 'BDS', name: 'Team BDS', div: 2 }, { id: 'Rogue', name: 'Rogue', div: 2 },
{ id: 'MRiders', name: 'Movistar Riders', div: 2 },
],
LCS: [
{ id: 'TL', name: 'Team Liquid', div: 1 }, { id: 'C9', name: 'Cloud9', div: 1 },
{ id: '100T', name: '100 Thieves', div: 1 }, { id: 'FlyQuest', name: 'FlyQuest', div: 1 },
{ id: 'Dignitas', name: 'Dignitas', div: 1 },
{ id: 'NRG', name: 'NRG', div: 2 }, { id: 'TSM', name: 'TSM', div: 2 },
{ id: 'ShoRebel', name: 'Shopify Rebellion', div: 2 },
],
LTA: [
{ id: 'paiN', name: 'paiN Gaming', div: 1 }, { id: 'LOUD', name: 'LOUD', div: 1 },
{ id: 'RED', name: 'RED Canids', div: 1 }, { id: 'FURIA', name: 'FURIA', div: 1 },
{ id: 'Fluxo', name: 'Fluxo', div: 1 }, { id: 'INTZ', name: 'INTZ', div: 1 },
{ id: 'KaBuM', name: 'KaBuM! Esports', div: 1 }, { id: 'Keyd', name: 'Keyd Stars', div: 1 },
{ id: 'Leviatan', name: 'Leviatán Esports', div: 1 }, { id: 'Estral', name: 'Estral Esports', div: 1 },
{ id: 'Isurus', name: 'Isurus', div: 1 },
{ id: 'paiNAcad', name: 'paiN Academy', div: 2 }, { id: 'LOUDAcad', name: 'LOUD Academy', div: 2 },
{ id: 'REDAcad', name: 'RED Canids Academy', div: 2 }, { id: 'FURIAAcad', name: 'FURIA Academy', div: 2 },
{ id: 'FluxoAcad', name: 'Fluxo Academy', div: 2 }, { id: 'INTZAcad', name: 'INTZ Academy', div: 2 },
{ id: 'KaBuMAcad', name: 'KaBuM Academy', div: 2 }, { id: '6K', name: '6K Esports', div: 2 },
{ id: 'AllKnights', name: 'All Knights', div: 2 }, { id: 'Furious', name: 'Furious Gaming', div: 2 },
{ id: 'Rebirth', name: 'Rebirth Esports', div: 2 },
],
VCS: [
{ id: 'GAM', name: 'GAM Esports', div: 1 }, { id: 'Vikings', name: 'Vikings Esports', div: 1 },
{ id: 'TeamSecret', name: 'Team Secret', div: 1 }, { id: 'Cerberus', name: 'Cerberus Esports', div: 1 },
{ id: 'EVOS', name: 'EVOS Esports', div: 2 }, { id: 'MGNBlue', name: 'MGN Blue Esports', div: 2 },
{ id: 'SBTC', name: 'SBTC Esports', div: 2 },
],
};
function teamById(id){ for (const rg of Object.values(TEAMS)){ const t = rg.find(t => t.id === id); if (t) return t; } return null; }
function logoFile(id){ const ext = LOGO_EXT[id]; return ext ? 'logos/' + id + '.' + ext : null; }
function logoImg(id, size, cls){
const f = logoFile(id); const t = teamById(id);
const inits = t ? t.name.split(/[^A-Za-z]+/).filter(Boolean).slice(0,2).map(w=>w[0].toUpperCase()).join('') : '?';
if (f) return `<img class="logo ${cls||''}" style="width:${size}px;height:${size}px" src="${f}" alt="${esc(t.name)}" onerror="this.outerHTML='<span class=&quot;fallback ${cls||''}&quot; style=&quot;width:${size}px;height:${size}px&quot;>${esc(inits)}</span>'">`;
return `<span class="fallback ${cls||''}" style="width:${size}px;height:${size}px">${esc(inits)}</span>`;
}
const ROLES = {
Top: { em: '🗡️', fl: 'isla. solo. 1v1 sin piedad.', kill: 3.0, ast: 4.2, death: 0.95, pentaW: 0.35 },
Jungla: { em: '🐺', fl: 'invadí, gankeá, tilteá al jungla rival.', kill: 2.6, ast: 6.5, death: 1.05, pentaW: 0.4 },
Mid: { em: '🧙', fl: 'el carril del 1v9. la responsabilidad.', kill: 3.8, ast: 5.5, death: 0.9, pentaW: 1.0 },
ADC: { em: '🏹', fl: 'late game. esperá el huevo, no feedees.', kill: 4.4, ast: 4.6, death: 1.0, pentaW: 1.15 },
Support: { em: '🛡️', fl: 'hacés el trabajo sucio y nadie te da crédito.', kill: 1.3, ast: 10.5, death: 1.25, pentaW: 0.2 },
};
const CATS = {
entrenamiento: { name: 'Entrenamiento', icon: '🎯' }, salud: { name: 'Salud y tilt', icon: '🩹' },
preparacion: { name: 'Preparación', icon: '💪' }, alimentacion: { name: 'Alimentación', icon: '🍜' },
escandalo: { name: 'Escándalo', icon: '😱' }, estudios: { name: 'Plan B', icon: '📚' },
};
const EVENTS = [
{ cat: 'entrenamiento', title: 'Cambió la meta', ctx: 'Parche nuevo: el campeón que grindeaste toda la temporada quedó en tier B. El coach te mira raro en la reunión de draft.',
opts: [
{ t: 'Spamear SoloQ con el champ del meta', d: { mecanica: 2.5 }, risk: 0.28,
pos: 'Doce horas de SoloQ y ya le sacás el teclado al champ nuevo. El coach sonríe (por primera vez en su vida).',
neg: 'El champ nuevo no te sale ni en entrenamiento. Dos splits de One Trick Pony sin el pony.' },
{ t: 'Chuparte todos los VODs de los pros', d: { vision: 2.2 }, risk: 0.15,
pos: 'Descubriste el pathing secreto del server. Jugás 3 niveles más arriba en el mapa.',
neg: 'Tanta VOD-review te hizo dormir con la cabeza en el teclado. Tu mecanica de siempre se oxida.' },
{ t: 'Seguir con tu main, la meta es para cagones', d: { sinergia: 2.4 }, risk: 0.45,
pos: 'Tu champ te conoce. El equipo te arma comps alrededor tuyo y funcionó. Quién dice out of meta, no?',
neg: 'Te banearon el main en el split final. Jugar con el reemplazo fue como jugar con las manos atadas.' },
]},
{ cat: 'entrenamiento', title: 'Maratón de SoloQ', ctx: 'El equipo tiene día libre. Podés hacer lo que quieras para llegar mejor al próximo split.',
opts: [
{ t: 'Grindear 12 horas seguidas', d: { mecanica: 2.8 }, risk: 0.4,
pos: 'Llegaste a challenger y tu mecanica se afiló como cuchillo. El ping era mental, dicen.',
neg: 'A las 2 AM hiciste un mal game y entraste en espiral. El tilt te dura tres días y el coach te mira feo.' },
{ t: 'Sesión de review con el coach', d: { vision: 1.6, sinergia: 1.4 }, risk: 0.12,
pos: 'Te mostró los 47 errores que nadie ve. Sos más inteligente sin cambiar de campeón.',
neg: 'La review duró 5 horas por un ward. Tu tiempo valía más que ese ward.' },
{ t: 'Descansar y resetear la cabeza', d: { sinergia: 1.2 }, risk: 0.08,
pos: 'Dormiste 9 horas y jugaste con la mente limpia. El cuerpo agradece.',
neg: 'Dormiste tanto que te perdiste el scrim. El coach sumó una marca negra.' },
{ t: 'Streaming de SoloQ a las 4 AM', d: { marca: 2.4 }, risk: 0.3,
pos: 'El clip del tilt te hizo viral. 40 mil seguidores nuevos y una entrevista para un medio de esports.',
neg: 'El tilt fue TAN épico que el clip lo vieron tus padres. No te hablan.' },
]},
{ cat: 'entrenamiento', title: 'El equipo rival te humilló', ctx: 'Perdieron 3-0 en scrims contra un equipo de segunda que nadie respetaba. Hay que reaccionar.',
opts: [
{ t: 'Revancha mental: analizar cada pick rival', d: { vision: 2.2, sinergia: 1.0 }, risk: 0.15,
pos: 'Les sacaste el patrón completo. La próxima vez que los veas en el draft, jugás vos.',
neg: 'Analizaste tanto que el estratega del equipo sintió que le pisabas el puesto. Ahora te esquiva en el pasillo.' },
{ t: 'Pechar en SoloQ hasta el amanecer', d: { mecanica: 2.2 }, risk: 0.35,
pos: 'El grind te hizo monstruo. Ahora los humillás vos, y con estilo.',
neg: 'Pecho frío el split entero. Jugaste como si tuvieras 300 de ping.' },
{ t: 'Proponer cambiar la estrategia del equipo', d: { sinergia: 2.0, marca: 1.2 }, risk: 0.3,
pos: 'La nueva estrategia les funcionó: 5 partidas ganadas en fila. Te empiezan a escuchar.',
neg: 'La estrategia era mala y lo sabías en el fondo. Te comiste el fail y el ego.' },
]},
{ cat: 'entrenamiento', title: 'Quieren cambiarte de rol', ctx: 'El nuevo fichaje juega tu rol. La directiva te propone pasar a otro carril.',
opts: [
{ t: 'Aceptar el cambio de rol', d: { sinergia: 1.8 }, risk: 0.5, x: { rolChange: true },
pos: 'El cambio te renovó: jugás con la energía del que no tiene nada que perder. La gente te redescubre.',
neg: 'Cambiaste de rol y sos un desastre en el nuevo carril. Los memes de tu gameplay vuelan en Reddit.' },
{ t: 'Negarte y pelear el puesto', d: { mecanica: 2.0, marca: 1.2 }, risk: 0.42,
pos: 'Ganaste el puesto en el scrim decisivo. Tu nivel subió porque lo defendiste con uñas y dientes.',
neg: 'Perdiste el puesto y el nuevo te revienta en todo. Tu confianza quedó en el subsuelo.' },
{ t: 'Buscar equipo en otra parte', d: { sinergia: 1.0 }, risk: 0.25, x: { transfer: 'other' },
pos: 'Echaste la carta: dos equipos se interesaron y negociás un pase con mejora.',
neg: 'Nadie te quería tanto como creías. Te quedaste donde mismo y con el ego herido.' },
]},
{ cat: 'entrenamiento', title: 'Bootcamp de élite', ctx: 'Te invitan a un bootcamp internacional con los mejores del mundo. Plazas limitadas.',
opts: [
{ t: 'Ir y aprender de los mejores', d: { mecanica: 2.6 }, risk: 0.38,
pos: 'Tres semanas jugando contra el server más duro del planeta. Volviste con otra lectura del juego.',
neg: 'La diferencia de nivel te mostró lo lejos que estás. Te pegó tan duro que jugaste peor un mes.' },
{ t: 'Quedarte y laburar tu estilo', d: { sinergia: 1.6, marca: 1.0 }, risk: 0.12,
pos: 'Le dedicaste el tiempo a tu equipo: sincronizaron jugadas nuevas que pagan en playoffs.',
neg: 'Mientras vos laburabas lo tuyo, tus rivales volvieron del bootcamp con 200 de nivel extra.' },
]},
{ cat: 'salud', title: 'Te arde la muñeca', ctx: 'Tres splits jugando 10 horas por día. Ahora cada flash te cuesta una punzada en la muñeca derecha.',
opts: [
{ t: 'Operarte ya', d: { mecanica: -1.5 }, risk: 0.5,
pos: 'La operación salió perfecta y volviste como nuevo. Jugás como si tuvieras 17 años.',
neg: 'La recuperación fue más larga de lo previsto y perdiste medio split. La mecánica se resintió.' },
{ t: 'Fisioterapia + pausas obligatorias', d: { sinergia: 1.6 }, risk: 0.15,
pos: 'Cambiaste la rutina: estiramientos, descansos y hielo. La muñeca aguanta y jugás mejor.',
neg: 'El fisio te hizo dejar de jugar a las horas que más fruta dabas. El ritmo se te rompió.' },
{ t: 'Seguir jugando como si nada', d: { mecanica: 1.2 }, risk: 0.55,
pos: 'La adrenalina del scrim tapa el dolor. Un split más aguantás.',
neg: 'Te diste cuenta tarde: la muñeca dijo basta a mitad de split y te perdiste las finales.' },
]},
{ cat: 'salud', title: 'Burnout', ctx: 'Vas 3 splits sin vacaciones. El LoL te dejó de gustar y arrancás a jugar peor de lo que rendís.',
opts: [
{ t: 'Tomarte una licencia de un mes', d: { sinergia: 1.2, marca: 0.8 }, risk: 0.22,
pos: 'Un mes sin pantallas te hizo acordar por qué amabas el juego. Volviste con hambre.',
neg: 'El equipo fichó a un reemplazo que jugó bien. Volviste y ya no sos titular seguro.' },
{ t: 'Apretar el culo y seguir', d: { mecanica: 1.8 }, risk: 0.48,
pos: 'Lo atravesaste con huevo: el juego te volvió a enganchar y el rendimiento volvió.',
neg: 'El burnout te comió por dentro. Jugabas en automático y tu nivel cayó en picada.' },
{ t: 'Cambiar la rutina completa', d: { sinergia: 2.0 }, risk: 0.18,
pos: 'Menos SoloQ, más análisis, más gimnasio, más vida. Rendís mejor y encima dormís 8 horas.',
neg: 'Menos SoloQ = menos reflejos. Te falta ese toque fino de antes.' },
]},
{ cat: 'salud', title: 'Spiral de tilt', ctx: 'Perdiste 9 partidas seguidas y ya le pifiaste al equipo en dos scrims. El chat del stream pide tu retiro.',
opts: [
{ t: 'Psicólogo de esports', d: { sinergia: 1.8, marca: 0.8 }, risk: 0.1,
pos: 'Tres sesiones y entendiste que el LoL no te define. Jugás más tranquilo y mejor.',
neg: 'El psicólogo te dijo algo que no querías escuchar: capaz no tenés lo que se necesita. Te quedó dando vueltas.' },
{ t: 'Desquitarte en SoloQ ranked', d: { mecanica: 1.6 }, risk: 0.5,
pos: 'Cargaste el tilt en 20 partidas y salió como catarsis: tu nivel de antes volvió.',
neg: 'El tilt escaló: te reportaron 47 veces y Riot te suspendió la cuenta principal 3 días. Decisión fatídica.' },
{ t: 'Apagar todo y dormir 14 horas', d: { sinergia: 1.0 }, risk: 0.2,
pos: 'Dormiste como un bebé y el reset funcionó. Nuevo día, nueva vida.',
neg: 'Dormiste hasta la scrim y te olvidaste de contestar al manager. Multa de dos días de sueldo.' },
]},
{ cat: 'salud', title: 'Problemas con la visa', ctx: 'Te seleccionaron para viajar al torneo internacional y el consulado te la está complicando.',
opts: [
{ t: 'Pelear con los papeles', d: { vision: 1.2 }, risk: 0.4,
pos: 'A fuerza de turnos y traductor, la sacaste. Volás al torneo con 48 horas de ventaja.',
neg: 'Te la negaron por un papel mal completado. El equipo viaja sin vos y te perdés la chance.' },
{ t: 'Quedarte y apoyar desde casa', d: { sinergia: 1.6, marca: 0.6 }, risk: 0.15,
pos: 'Analizaste los partidos del equipo a distancia y les pasaste scouting clave. Ganaron dos series por tus informes.',
neg: 'Ver los playoffs por stream con el corazón partido. Tu stock bajó entre los scouts internacionales.' },
]},
{ cat: 'preparacion', title: 'Te ofrecen un setup nuevo', ctx: 'El sponsor de hardware quiere que cambies de equipo: monitor, teclado, silla, todo nuevo.',
opts: [
{ t: 'Monitor 360Hz + teclado nuevo', d: { mecanica: 1.8 }, risk: 0.2,
pos: 'El cambio a 360Hz te destapó los ojos: ves los skillshots antes de que salgan.',
neg: 'El teclado nuevo tiene el shift en otro lado. Erraste 3 flashes en un split y el equipo se quejó.' },
{ t: 'Silla ergonómica + mesa a medida', d: { sinergia: 1.4 }, risk: 0.08,
pos: 'Tu espalda te agradece y jugás 2 horas más sin cansarte. El rendimiento sube solo.',
neg: 'La silla nueva es tan cómoda que te dormís en los breaks de las scrims.' },
{ t: 'No tocar nada, el setup actual es sagrado', d: { sinergia: 1.0, marca: 0.8 }, risk: 0.12,
pos: 'La constancia paga: los torneos locales no pueden usar tu config nueva igual. Seguís siendo consistente.',
neg: 'El sponsor se ofendió un poco y te retiró un beneficio de streaming.' },
]},
{ cat: 'preparacion', title: 'Gaming house vs. tu depto', ctx: 'El manager te propone mudarte a la gaming house del equipo. Convivencia total con los compañeros.',
opts: [
{ t: 'Mudarme a la gaming house', d: { sinergia: 2.2 }, risk: 0.25,
pos: 'Convivencia = macro sincronizada. El equipo juega como una mente sola.',
neg: 'Convivencia = los compas te despiertan con la música a las 9 AM. Tu paciencia llegó al límite.' },
{ t: 'Quedarme en mi depto', d: { marca: 1.4, sinergia: 0.6 }, risk: 0.15,
pos: 'Tu espacio te permite streamear tranquilo y editar contenido. La marca personal crece.',
neg: 'Los compas se arman jugadas en el living mientras vos te enterás por el replay.' },
]},
{ cat: 'preparacion', title: 'Preparación física', ctx: 'El staff médico del equipo insiste: hay que entrenar cuerpo, no solo manos.',
opts: [
{ t: 'Gimnasio 3 veces por semana', d: { sinergia: 1.6 }, risk: 0.15,
pos: 'Más fuerza, más energía, mejor postura. Jugás las 5 partidas del best of 5 sin bajar la cabeza.',
neg: 'Tanto fierro que te agarró agujetas y no podías mover el mouse. El coach no entendía nada.' },
{ t: 'Yoga y estiramiento diario', d: { vision: 1.0, sinergia: 0.8 }, risk: 0.08,
pos: 'Tu mente se despejó y tu reflejo visual mejoró. El chi fluye y el farmeo también.',
neg: 'Nadie te toma en serio en el team con tu mat de yoga. Te cargan hasta en las entrevistas.' },
{ t: 'El gimnasio es para los otros', d: {}, risk: 0.3,
pos: 'Todo el tiempo extra va al juego. Tu mecanica afilada es tu gimnasio.',
neg: 'A los 24 años ya te duelen la espalda y el cuello. Los de 17 te corren el mapa sin cansarse.' },
]},
{ cat: 'preparacion', title: 'Nuevo headset del sponsor', ctx: 'El sponsor de periféricos te ofrece un headset con cancelación de ruido y micrófono de radio.',
opts: [
{ t: 'Usarlo en scrims y entrenar las calls', d: { sinergia: 1.8 }, risk: 0.1,
pos: 'La comunicación del equipo es otra: calls claras, cero ruido, cero gritos. El macro mejora.',
neg: 'El micrófono tan bueno también graba tus críticas al jungla. Se enteró. No fue lindo.' },
{ t: 'Solo para streaming', d: { marca: 1.8 }, risk: 0.1,
pos: 'El sonido de tu stream es premium y la audiencia lo nota: más subs y más clip.',
neg: 'Te olvidaste de sacar el micrófono en una call privada y filtraste el plan táctico del equipo.' },
]},
{ cat: 'alimentacion', title: 'Energy drinks o comida real', ctx: 'Llevás 6 meses sobreviviendo a base de bebida energética y arroz con algo.',
opts: [
{ t: 'Unificar criterio: comida real', d: { sinergia: 1.6 }, risk: 0.1,
pos: 'La nutricionista te arma el plan: más foco, más energía sostenida, menos picos. Juega mejor todo el split.',
neg: 'La comida real te pareció un embole y volviste a las energéticas a las dos semanas.' },
{ t: 'Energéticas y a la mierda', d: { mecanica: 1.6 }, risk: 0.35,
pos: 'El split jugaste al palo: la cafeína te mantuvo 14 horas de grind sin bajar.',
neg: 'El estómago dijo basta en plena serie de playoffs. Tuviste que salir a comprar leche en el descanso.' },
{ t: 'Firmar con el sponsor de bebida energética', d: { marca: 1.8, sinergia: 1.0 }, risk: 0.25,
pos: 'Plata + exposición + los fans te asocian con la marca. Tu valor de mercado sube.',
neg: 'El sponsor te exigió salir en los streams con la bebida siempre a mano. El doc del equipo te ve mal.' },
]},
{ cat: 'alimentacion', title: 'El equipo se va de bootcamp a otro país', ctx: 'Comida nueva, horarios nuevos, todo nuevo. Tu estómago ya te avisó que no va a cooperar.',
opts: [
{ t: 'Adaptarme a la comida local', d: { sinergia: 1.4 }, risk: 0.2,
pos: 'Descubriste platos nuevos y encima le caíste bien a la cocinera del bootcamp. Combo completo.',
neg: 'La comida local te cayó mal y te perdiste dos días de scrim. El estómago no perdona.' },
{ t: 'Pedir delivery de tu comida de confianza', d: { marca: 1.2, sinergia: -0.6 }, risk: 0.15,
pos: 'La comida de tu país te mantiene en tu zona de confort y encima lo streamiás: los viewers se ríen.',
neg: 'El delivery del extranjero sale el triple y la mitad de las veces llega frío. Tu presupuesto llora.' },
{ t: 'Cocinar yo mismo', d: { sinergia: 1.0 }, risk: 0.2,
pos: 'Cocinar te despeja la cabeza y los compas prueban tu comida casera. Team bonding nivel chef.',
neg: 'Casi prendés fuego la cocina de la gaming house. El manager te prohibió acercarte a las hornallas.' },
]},
{ cat: 'alimentacion', title: 'Cheat day del coach', ctx: 'El coach quiere imponer dieta estricta de lunes a sábado y domingo libre. Te pregunta qué opinás.',
opts: [
{ t: 'Seguir la dieta al pie de la letra', d: { sinergia: 1.6 }, risk: 0.12,
pos: 'Disciplina total: tu energía es estable y tu rendimiento también. El coach está orgulloso.',
neg: 'La dieta era tan estricta que te bajaste toda la heladera en el cheat day. Balance perdido.' },
{ t: 'Propuesta: dieta flexible', d: { marca: 1.2, sinergia: 0.6 }, risk: 0.15,
pos: 'La dieta flexible te mantiene feliz y rendís igual. El equipo adoptó tu sistema.',
neg: 'Flexible terminó siendo "todo el tiempo". El coach te miró con decepción paterna.' },
]},
{ cat: 'alimentacion', title: 'El esponsor de suplementos', ctx: 'Un laboratorio te ofrece suplementos "milagrosos" para la concentración. Suena demasiado bueno.',
opts: [
{ t: 'Rechazar: no sé qué mierda es eso', d: { marca: 1.4, sinergia: 0.8 }, risk: 0.08,
pos: 'Públicamente te mantuviste limpio y honesto. Los fans y el equipo valoran tu criterio.',
neg: 'La oferta era de 50k. Tu contador casi te mata cuando se enteró que la rechazaste.' },
{ t: 'Probar "solo una vez"', d: { mecanica: 1.6 }, risk: 0.55,
pos: 'Concentración brutal el split. Pero ahora dependés de eso y el doc te marcó el alto.',
neg: 'Control antidopaje positivo en el torneo internacional. Descalificado y suspendido un año. Tu carrera se tambalea.' },
{ t: 'Consultar al médico del equipo', d: { vision: 1.0 }, risk: 0.1,
pos: 'El médico lo analizó: es magnesio con marketing. Lo toman todos y no hace nada, pero la anécdota es buena.',
neg: 'El médico te explicó que era trucho y vos ya le habías dicho "dale" al laboratorio. Bochorno.' },
]},
{ cat: 'escandalo', title: 'Te ofrecen un boosteo', ctx: 'Un "amigo" de un amigo te ofrece pagarle a alguien para subirte a challenger. Nadie se entera, dice.',
opts: [
{ t: 'Aceptar, nadie se entera', d: { mecanica: 1.4 }, risk: 0.75,
pos: 'Subiste a challenger y el equipo ni se enteró. Bueno... casi.',
neg: 'SE ENTERARON. Cuenta suspendida, multa de la liga y tu reputación hecha pelota. El manager te hizo hablar con Riot.' },
{ t: 'Rechazar y reportarlo', d: { marca: 1.8, sinergia: 0.6 }, risk: 0.1,
pos: 'Lo contaste en un stream y la gente te aplaudió: honestidad +50. Hasta Riot te siguió en redes.',
neg: 'El "amigo" del amigo resultó ser el primo del dueño del equipo. Incómodo.' },
{ t: 'Hacer el boosteo vos mismo por plata', d: { marca: 1.4, sinergia: 0.6 }, risk: 0.65,
pos: 'La guita extra cayó linda y nadie se enteró. La cuenta de un cliente tuyo subió a diamante.',
neg: 'Riot rastrea los juegos compartidos. Cuenta suspendida 3 meses y tus jefes te dieron un ultimátum.' },
]},
{ cat: 'escandalo', title: 'La VPN para el servidor', ctx: 'El server local está muerto a ciertas horas. Un compa te dice: "y si usás VPN para jugar en otro server?"',
opts: [
{ t: 'Usar la VPN', d: { mecanica: 1.2 }, risk: 0.7,
pos: 'Jugaste con más nivel competitivo un par de meses. El ping no era el problema, resultó ser tu mentalidad.',
neg: 'Te detectaron el cambio de IP en plena partida oficial. Penalización de la liga y el equipo tuvo que explicarse.' },
{ t: 'No usar la VPN', d: { sinergia: 0.8, marca: 0.8 }, risk: 0.1,
pos: 'Te mantuviste dentro de las reglas y el equipo te respalda. Los días malos de server son de todos.',
neg: 'Los que usaron VPN te sacaron ventaja en SoloQ. Tu ranking local no muestra tu nivel real.' },
]},
{ cat: 'escandalo', title: 'Coaching en vivo por el pinganillo', ctx: 'En un scrim importa un "asesor" que te va dictando cada jugada por el ear. La liga lo prohíbe.',
opts: [
{ t: 'Escuchar y ejecutar', d: { sinergia: 1.2 }, risk: 0.6,
pos: 'Las jugadas salieron perfectas un tiempo. El "asesor" era medio genio.',
neg: 'Lo descubrieron en playoffs. Multa, partido anulado y la serie perdida en la mesa. Bochorno internacional.' },
{ t: 'Sacarte el pinganillo', d: { marca: 1.6, sinergia: 0.6 }, risk: 0.15,
pos: 'Jugaste con lo tuyo y ganaste igual. El stream mostró el momento: la comunidad te elevó.',
neg: 'El "asesor" era el dueño del equipo. Ahora no te habla en las reuniones.' },
]},
{ cat: 'escandalo', title: 'Te acusan de elo boosting', ctx: 'Unos foros dicen que tu rank subió de golpe y que estás comprado. Es mentira, pero la comunidad arde.',
opts: [
{ t: 'Demostrarlo con gameplays en stream', d: { marca: 2.0 }, risk: 0.12,
pos: 'Stream de 10 horas mostrando tu grind real. La gente te cree y tu canal explota.',
neg: 'El stream fue tan bueno que la gente cree que el rank lo hizo tu editor. No hay con qué darle.' },
{ t: 'Ignorarlo y seguir', d: { sinergia: 1.0 }, risk: 0.3,
pos: 'Te chupó un huevo y jugaste igual. Los haters se cansaron solos.',
neg: 'Los rumores crecieron y hasta un periodista lo preguntó en una rueda de prensa. Te tocó el orgullo.' },
]},
{ cat: 'escandalo', title: 'Se filtró una rage quit tuya', ctx: 'Un clip viejo donde cerraste el juego a los 12 minutos se volvió viral. Tu org está por hacer un comunicado.',
opts: [
{ t: 'Pedir disculpas públicas con humor', d: { marca: 1.8 }, risk: 0.15,
pos: 'Te disculpaste riéndote de vos mismo y el clip hasta te hizo ganar seguidores. La gente ama la autocrítica.',
neg: 'La disculpa salió tan cringe que ahora el clip viral es tu disculpa. Día de mierda.' },
{ t: 'No decir nada, aguantar la tormenta', d: { sinergia: 0.8 }, risk: 0.35,
pos: 'El tema murió solo en una semana. La gente tiene memoria de pez dorado.',
neg: 'El tema no murió: los periodistas te lo preguntaron en cada entrevista durante un mes.' },
]},
{ cat: 'estudios', title: 'La universidad a distancia', ctx: 'Tu familia te llama: "estudiá algo, esto de los videojuegos no es un laburo". Te inscribís (o no).',
opts: [
{ t: 'Anotarme en la carrera a distancia', d: { marca: 1.2, sinergia: 0.4 }, risk: 0.25,
pos: 'Estudiás y jugás: el equilibrio te hace más maduro. Además tu familia por fin te deja de hinchar.',
neg: 'Entre el estudio y el juego no dormís. El rendimiento en scrims cayó y el coach te vio bostezando.' },
{ t: 'Mandarle a la universidad', d: { mecanica: 1.4 }, risk: 0.2,
pos: 'Todo el tiempo es para el juego y se nota: tu nivel sube sin distracciones.',
neg: 'Tu familia te dejó de hablar tres meses. Después se reconcilió cuando viste plata en serio.' },
{ t: 'Hacer un curso de inglés online', d: { sinergia: 1.2, marca: 0.8 }, risk: 0.1,
pos: 'Inglés para entrevistas y para hablar con los internacionales. Tu stock global sube.',
neg: 'El curso era para principiantes y vos ya sabías de más. Plata tirada, tiempo tirado.' },
]},
{ cat: 'estudios', title: 'La plata del contrato', ctx: 'Acumulaste unos buenos mangos y no sabés qué hacer. Tu contador te da un parte.',
opts: [
{ t: 'Plazo fijo y a la mierda', d: { marca: 1.2 }, risk: 0.08,
pos: 'La plata crece solita y vos dormís tranquilo. Tu valor de mercado sube porque sos "serio".',
neg: 'El plazo fijo rinde menos que la inflación de tu país. Tu contador llora de la risa.' },
{ t: 'Invertir en NFT de monos', d: { marca: -1.4 }, risk: 0.85,
pos: 'Lo vendiste justo antes del crash y saliste casi entero. La anécdota queda para siempre.',
neg: 'Perdiste el 80% en monos pixelados. La comunidad te lo recuerda en cada stream.' },
{ t: 'Comprarte el setup de tus sueños', d: { mecanica: 1.4, marca: 0.8 }, risk: 0.1,
pos: 'Monitor, teclado, silla, pared de paneles: tu setup es top 5 del server y tu streaming mejora.',
neg: 'El setup era tan caro que te quedaste sin plata para el alquiler. El manager te adelantó sueldo.' },
{ t: 'Ponerla en un emprendimiento con un amigo', d: { marca: 0.8 }, risk: 0.4,
pos: 'El emprendimiento (una cafetería gamer) arrancó bien. Ingreso extra y te da otra perspectiva.',
neg: 'El emprendimiento era un "amigo" que desapareció con la plata. Lección cara.' },
]},
{ cat: 'estudios', title: 'Te ofrecen un canal de YouTube', ctx: 'El manager de contenidos del equipo te propone un canal propio de resúmenes y momentos top.',
opts: [
{ t: 'Editarlo yo mismo', d: { marca: 2.0 }, risk: 0.2,
pos: 'Tu estilo edgy y auténtico engancha: 100k subs en dos splits y sponsors personales.',
neg: 'Editar te comió las horas de descanso. El rendimiento en scrims bajó y el coach te retó.' },
{ t: 'Contratar un editor', d: { sinergia: -0.8, marca: 1.6 }, risk: 0.12,
pos: 'El canal crece sin que toques nada. Plata saliendo, pero marca personal al palo.',
neg: 'El editor subió un clip de una estrategia secreta del equipo. Desastre de relaciones públicas.' },
{ t: 'Nada de contenido, solo jugar', d: { mecanica: 1.2 }, risk: 0.15,
pos: 'Todo tu tiempo al juego: el nivel sube y el equipo te valora como jugador puro.',
neg: 'Mientras tus rivales hacen marca, nadie te conoce fuera del server. Tu valor de mercado se estanca.' },
]},
{ cat: 'estudios', title: 'Clases de inglés para entrevistas', ctx: 'El equipo te anota en inglés avanzado: apuntan a entrevistas internacionales y a que te entiendan en el MSI.',
opts: [
{ t: 'Tomármelo en serio', d: { sinergia: 1.4, marca: 0.8 }, risk: 0.08,
pos: 'Inglés fluido: te manejás con la prensa internacional y hasta improvisás en las entrevistas.',
neg: 'Inglés fluido pero con acento tan fuerte que los angloparlantes creen que es otro idioma.' },
{ t: 'Ir por cumplir', d: { marca: 0.6 }, risk: 0.25,
pos: 'Aprendiste lo justo: "yes", "no" y "GG". Alcanza para los que importan.',
neg: 'En la entrevista post-partido dijiste "I am tilted" para describir la victoria. Clip eterno.' },
]},
];
/* ---------- COMPETENCIAS INTERNACIONALES ---------- */
const COMPETITIONS = {
'First Stand': { split: 2, ic: '⚡', flavor: 'El nuevo torneo internacional que abre el año, con los campeones de cada región.' },
'MSI': { split: 4, ic: '🌍', flavor: 'Mid-Season Invitational: los mejores de cada liga a mitad de temporada.' },
'EWC': { split: 6, ic: '🏜️', flavor: 'Esports World Cup: el torneo de clubes en Arabia Saudita, con premio millonario.' },
'Asian Games': { split: 8, ic: '🏅', flavor: 'Juegos Asiáticos: tu selección nacional representa a la región.' },
'Worlds': { split: 'last', ic: '🏆', flavor: 'El Campeonato Mundial: el torneo más grande del año, el que todos quieren ganar.' },
};
function compForSplit(s){
if (s === 2) return 'First Stand';
if (s === 4) return 'MSI';
if (s === 6) return 'EWC';
if (s === 8) return 'Asian Games';
if (s === G.totalSplits && G.totalSplits >= 9) return 'Worlds';
return null;
}
/* ============ ESTADO DE PARTIDA ============ */
let G = null;
function newGame(teamId){
const t = teamById(teamId);
G = {
nick: UI.nick || 'Anónimo', num: UI.num, hand: UI.hand, region: UI.region, role: UI.role,
teamId: t.id, div: t.div,
stats: { mecanica: rnd(42, 58), vision: rnd(42, 58), farmeo: rnd(42, 58), sinergia: rnd(42, 58), marca: rnd(40, 54) },
split: 0, totalSplits: rnd(10, 12),
kills: 0, assists: 0, deaths: 0, games: 0, penta: 0,
value: 120000, maxValue: 120000, salary: 8000, ratingMax: 0,
teamTitles: [], indTitles: [], intl: [],
teams: [], events: [], history: [],
regSelect: { games: 0, kills: 0, ast: 0, apps: 0 },
streamCount: 0,
retired: false, retiring: false, finalRating: 0,
};
G.teams.push({ id: t.id, name: t.name, div: t.div, from: 1, to: G.totalSplits, games: 0, kills: 0, ast: 0 });
G.ratingMax = rating();
}
function rnd(a, b){ return a + Math.floor(Math.random() * (b - a + 1)); }
function pick(arr){ return arr[Math.floor(Math.random() * arr.length)]; }
function clamp(v, a, b){ return Math.max(a, Math.min(b, v)); }
function shuffle(arr){ const a = arr.slice(); for (let i = a.length - 1; i > 0; i--){ const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; }
function statNames(){ return [['mecanica','Mecánica'],['vision','Visión'],['farmeo','Farmeo'],['sinergia','Sinergia'],['marca','Marca']]; }
function rating(){
const s = G.stats;
const w = { mecanica: 1.15, vision: 0.95, farmeo: 1.05, sinergia: 1.0, marca: 0.85 };
const t = statNames().reduce((acc, [k]) => acc + s[k] * w[k], 0);
const tw = Object.values(w).reduce((a, b) => a + b, 0);
return clamp(Math.round(t / tw), 40, 99);
}
function updateRatingMax(){ if (rating() > G.ratingMax) G.ratingMax = rating(); }
function marketValue(){
const r = rating();
const v = 50000 * Math.pow(10, (r - 40) * 0.055);
const bonus = 1 + G.indTitles.length * 0.18 + G.teamTitles.length * 0.05;
return Math.round(v * bonus / 10000) * 10000;
}
function ageNow(){ return 17 + Math.floor((G.split - 1) / 2); }
function fmtMoney(v){ return v >= 1000000 ? '$' + (v / 1000000).toFixed(2) + 'M' : '$' + Math.round(v / 1000) + 'k'; }
/* ============ UI helpers ============ */
const UI = { nick: '', num: 13, hand: 'diestro', region: null, role: null };
const $ = id => document.getElementById(id);
function showScreen(id){
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
$(id).classList.add('active');
window.scrollTo({ top: 0 });
}
function esc(s){ return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
function fallbackBadge(id, size){
const t = teamById(id);
const inits = t ? t.name.split(/[^A-Za-z]+/).filter(Boolean).slice(0,2).map(w=>w[0].toUpperCase()).join('') : '?';
return `<span class="fallback" style="width:${size}px;height:${size}px">${esc(inits)}</span>`;
}
function splitClass(s){
const total = G.totalSplits;
const ratio = s / total;
if (ratio <= 0.35) return 'early';
if (ratio <= 0.7) return 'mid';
return 'late';
}
/* ---- landing ---- */
function buildLanding(){
const strip = $('stripLogos');
const ids = ['T1','GenG','G2','Fnatic','TL','C9','JDG','BLG','paiN','LOUD','GAM','Karmine'];
strip.innerHTML = ids.map(id => {
const f = logoFile(id);
return f ? `<img src="${f}" alt="${teamById(id).name}" loading="lazy" onerror="this.outerHTML=fallbackBadge('${id}',40)">` : fallbackBadge(id, 40);
}).join('');
const best = loadHall();
$('hall').style.display = '';
$('hallBody').innerHTML = best.length
? '<ol>' + best.map((b, i) => `<li><span class="medal">${i + 1}</span><span>${esc(b.nick)}</span><span class="rval">${b.rating} / ${fmtMoney(b.value)}</span></li>`).join('') + '</ol>'
: '<div class="empty">Todavía no hay leyendas. Sé la primera.</div>';
}
/* ---- create ---- */
function buildCreate(){
$('regionGrid').innerHTML = Object.entries(REGIONS).map(([k, r]) =>
`<button class="region-card" data-r="${k}"><h3>${r.flag} ${r.name}</h3><p>${esc(r.flavor)}</p></button>`).join('');
$('regionGrid').querySelectorAll('.region-card').forEach(b => b.addEventListener('click', () => {
$('regionGrid').querySelectorAll('.region-card').forEach(x => x.classList.remove('on'));
b.classList.add('on'); UI.region = b.dataset.r;
}));
$('roleGrid').innerHTML = Object.entries(ROLES).map(([k, r]) =>
`<button class="role-card" data-r="${k}"><div class="em">${r.em}</div><div class="nm">${k}</div><div class="st">${esc(r.fl)}</div></button>`).join('');
$('roleGrid').querySelectorAll('.role-card').forEach(b => b.addEventListener('click', () => {
$('roleGrid').querySelectorAll('.role-card').forEach(x => x.classList.remove('on'));
b.classList.add('on'); UI.role = b.dataset.r;
}));
$('segHand').querySelectorAll('.opt').forEach(b => b.addEventListener('click', () => {
$('segHand').querySelectorAll('.opt').forEach(x => x.classList.remove('on'));
b.classList.add('on'); UI.hand = b.dataset.v;
}));
$('segHand').querySelector('.opt[data-v="diestro"]').classList.add('on');
$('inpNick').addEventListener('input', e => UI.nick = e.target.value.trim());
$('inpNum').addEventListener('input', e => UI.num = clamp(parseInt(e.target.value) || 0, 0, 99));
}
/* ============ FLUJO PRINCIPAL ============ */
function boot(){
buildLanding();
buildCreate();
$('btnStart').addEventListener('click', () => showScreen('create'));
$('btnBegin').addEventListener('click', () => {
if (!UI.region){ toast('Elegí tu región primero 👆'); return; }
if (!UI.role){ toast('Elegí tu rol 👆'); return; }
if (!UI.nick) UI.nick = 'xX' + ['Feeder','God','Tilter','Smurf','Rata','Pro'][Math.floor(Math.random() * 6)] + 'Xx';
startAcademy();
});
$('btnExport').addEventListener('click', exportImage);
$('btnAgain').addEventListener('click', () => showScreen('create'));
$('inpNick').addEventListener('keydown', e => { if (e.key === 'Enter') $('btnBegin').click(); });
}
document.addEventListener('DOMContentLoaded', boot);
/* Oferta de academia */
function startAcademy(){
const pool = shuffle(TEAMS[UI.region]);
const offers = pool.slice(0, 3);
const card = $('resultCard');
card.className = 'result-card';
card.innerHTML = `
<div class="verdict great">FICHÁSTE</div>
<p class="rtext">Tu trial en el server dio que hablar. <b>${offers.length} organizaciones</b> de tu liga te llamaron por el traspaso. Elegí tu destino:</p>
<div class="opt-grid" style="margin:14px 0">
${offers.map((t, i) => `
<button class="opt-card" data-team="${t.id}">
<span class="oc-label">Oferta ${['A','B','C'][i]}</span>
${logoImg(t.id, 64, 'oc-emblem')}
<span class="oc-title">${esc(t.name)}</span>
<span class="oc-tag">${t.div === 1 ? 'PRIMERA división' : 'SEGUNDA división'}</span>
</button>`).join('')}
</div>`;
$('resultOverlay').classList.add('show');
card.querySelectorAll('.opt-card').forEach(b => b.addEventListener('click', () => {
$('resultOverlay').classList.remove('show');
newGame(b.dataset.team);
showScreen('career');
nextSplit();
}));
}
/* ============ RENDER CAREER (Screen A) ============ */
function renderPlayerCard(){
const t = teamById(G.teamId);
const reg = REGIONS[G.region];
const v = G.value >= 1000000 ? '$' + (G.value / 1000000).toFixed(2) + 'M' : '$' + Math.round(G.value / 1000) + 'k';
$('playerCard').innerHTML = `
<div class="elo-badge"><span class="lbl">ELO</span><span class="num">${rating()}</span></div>
<div class="player-meta">
<div class="meta-top">
<span class="badge badge-region">${reg.flag} ${reg.code}</span>
<span class="badge badge-role">#${G.num} ${G.role}</span>
<span class="right"><span>SPLIT ${G.split}</span><span>· ${ageNow()} años</span></span>
</div>
<div class="org-line">${logoImg(G.teamId, 30)}<span class="org-name">${esc(t.name)}</span></div>
<div class="value-line">VALOR <b>${v}</b></div>
</div>`;
}
function renderStatsRow3(){
const kA = G.kills + G.assists;
$('statsRow3').innerHTML = `
<div class="stat3"><div class="ic">⚡</div><div class="stat-num">${G.games}</div><div class="stat-lbl">Partidas</div></div>
<div class="stat3"><div class="ic">🗡️</div><div class="stat-num">${kA}</div><div class="stat-lbl">Kills+Assist</div></div>
<div class="stat3"><div class="ic">💥</div><div class="stat-num">${G.penta}</div><div class="stat-lbl">Pentakills</div></div>`;
}
function renderAchievements(){
const ach = G.teamTitles.concat(G.indTitles);
if (ach.length){
$('achievementsBox').innerHTML = `<div class="ach-list">${ach.map(a => `<span class="ach-chip">${a.ic} ${esc(a.name)}</span>`).join('')}</div>`;
} else {
$('achievementsBox').innerHTML = `<div class="ach-trophy">🏆</div><div class="empty-ach">Sin logros aún</div>`;
}
}
function renderTimeline(){
const box = $('timelineBox');
const total = G.totalSplits;
let rows = '';
/* header */
rows = `<div class="tl-head"><span class="c-idx">SPLIT</span><span class="c-org">ORG</span><span class="c-elo">ELO</span><span class="c-num">GAM</span><span class="c-num">KIL</span><span class="c-num">AST</span></div>`;
let prevOrg = null;
for (let s = 1; s <= total; s++){
const h = G.history.find(x => x.split === s);
if (h){
const arr = (prevOrg && h.org !== prevOrg) ? '<span class="c-arr">↳</span>' : '';
rows += `<div class="tl-row">
<span class="c-idx"><span class="split-badge ${splitClass(s)}">${s}</span>${arr}</span>
<span class="c-org">${logoImg(h.org, 22)}<span class="em">${esc(h.orgName)}</span></span>
<span class="c-elo">${h.elo}</span>
<span class="c-num">${h.games}</span><span class="c-num">${h.kills}</span><span class="c-num">${h.ast}</span>
</div>`;
prevOrg = h.org;
} else if (s === G.split){
rows += `<div class="tl-row active">
<span class="c-idx"><span class="split-badge ${splitClass(s)}">${s}</span></span>
<span class="c-org">${logoImg(G.teamId, 22)}<span class="em">Eligiendo org...</span></span>
<span class="c-elo">${rating()}</span>
<span class="c-num" style="color:var(--text-muted)">—</span><span class="c-num" style="color:var(--text-muted)">—</span><span class="c-num" style="color:var(--text-muted)">—</span>
</div>`;
} else {
rows += `<div class="tl-row future">
<span class="c-idx"><span class="split-badge ${splitClass(s)}">${s}</span></span>
<span class="c-org"><span class="em">—</span></span>
<span class="c-elo"></span><span class="c-num"></span><span class="c-num"></span><span class="c-num"></span>
</div>`;
}
}
/* final row: selección regional */
const rs = G.regSelect;
rows += `<div class="tl-final">
<span class="f-flag">${REGIONS[G.region].flag}</span>
<span class="f-name">Selección Regional</span>
<span class="c-num">${rs.games}</span><span class="c-num">${rs.kills}</span><span class="c-num">${rs.ast}</span>
</div>`;
box.innerHTML = `<div class="tl-collapse" id="tlToggle">Carrera <span>▾</span></div><div class="tl-body" id="tlBody">${rows}</div>`;
const isMobile = window.innerWidth < 768;
const body = $('tlBody');
if (isMobile) body.classList.add('closed');
const tgl = $('tlToggle');
if (tgl){
if (isMobile) tgl.querySelector('span').textContent = '▸';
tgl.addEventListener('click', () => {
body.classList.toggle('closed');
tgl.querySelector('span').textContent = body.classList.contains('closed') ? '▸' : '▾';
});
}
}
function renderCareer(){
renderPlayerCard();
renderStatsRow3();
renderAchievements();
renderTimeline();
}
function nextSplit(){
G.split++;
const ev = nextEvent();
renderCareer();
renderDecision(ev);
}
/* ============ EVENTOS / DECISIONES ============ */
function nextEvent(){
const s = G.split;
/* competición internacional — SOLO equipos de primera división */
const comp = compForSplit(s);
if (comp){
if (G.div === 1) return intlEvent(comp);
/* equipo de 2da: no puede ir a internacional */
}
/* descenso por rating bajo */
if (G.div === 1 && rating() < 47) return demotionEvent();
/* ascenso desde segunda */
if (G.div === 2 && rating() >= 55) return promotionEvent();
/* oferta de fichaje — NO en primeros splits, y mucho menos frecuente */
if (s >= 4 && rating() >= REGIONS[G.region].threshold - 4 && Math.random() < 0.22) return transferEvent();
/* decisión random */
const ev = pick(EVENTS.filter(e => e.cat !== 'salud'));
return { kind: 'decision', ev };
}
function intlEvent(name){
const p = clamp((rating() - 36) / 60, 0.1, 0.95);
return { kind: 'intl', name, p, rolls: [p, p * 0.95, p * 0.9, p * 0.8] };
}
function transferEvent(){
const current = G.teamId;
const offers = [];
const pool = [];
Object.entries(TEAMS).forEach(([rg, ts]) => {
if (rg === G.region) ts.forEach(t => { if (t.id !== current) pool.push({ ...t, rg, delta: 0 }); });
else ts.forEach(t => {
if (t.div === 1 && rating() >= REGIONS[rg].threshold - 6) pool.push({ ...t, rg, delta: 1 });
});
});
if (!pool.length) return transferEvent2();
const n = Math.min(3, pool.length);
for (let i = 0; i < n; i++) offers.push(pool.splice(Math.floor(Math.random() * pool.length), 1)[0]);
return { kind: 'transfer', offers };
}
function transferEvent2(){
const ts = TEAMS[G.region].filter(t => t.id !== G.teamId);
const offers = [];
for (let i = 0; i < Math.min(2, ts.length); i++) offers.push(ts.splice(Math.floor(Math.random() * ts.length), 1)[0]);
return { kind: 'transfer', offers };
}
function promotionEvent(){
const pool = TEAMS[G.region].filter(t => t.div === 1);
return { kind: 'promotion', team: pick(pool) };
}
function demotionEvent(){ return { kind: 'demotion' }; }
function renderDecision(ev){
const box = $('decisionBox');
if (ev.kind === 'decision'){
const cat = CATS[ev.ev.cat];
const ctx = ev.ev.ctx.replace(/\{year\}/g, 2024 + Math.floor((G.split - 1) / 2)).replace(/\{split\}/g, G.split);
box.innerHTML = `
<div class="dc-cat">${cat.icon} ${cat.name} · Split ${G.split}</div>
<h2>${esc(ev.ev.title)}</h2>
<p class="dc-ctx">${esc(ctx)}</p>
<div class="opt-grid">
${ev.ev.opts.map((o, i) => {
const okPct = Math.round((1 - o.risk) * 100);
const failPct = Math.round(o.risk * 100);
return `<button class="opt-card" data-i="${i}">
<span class="oc-label">Opción ${['A','B','C','D'][i]}</span>
<span class="oc-title">${esc(o.t)}</span>
<span class="oc-desc">${esc(o.pos.slice(0, 60))}…</span>
<span class="oc-pills"><span class="pill success">${okPct}%</span><span class="pill danger">${failPct}%</span></span>
</button>`;
}).join('')}
${G.split >= 6 ? `<button class="opt-card wide" data-i="retire"><span class="oc-title">Retirarme y abrir un canal de streaming</span><span class="oc-desc">A los ${ageNow()} años sos veterano. Dijeron que se podía.</span></button>` : ''}
</div>`;
box.querySelectorAll('.opt-card').forEach(b => b.addEventListener('click', () => {
if (b.dataset.i === 'retire'){ G.retired = true; G.retiring = true; doSplitEnd(null); return; }
const res = resolveDecision(ev.ev, parseInt(b.dataset.i, 10));
showResult(res);
}));
} else if (ev.kind === 'transfer'){
renderTransfer(ev);
} else if (ev.kind === 'promotion'){
renderPromotion(ev);
} else if (ev.kind === 'demotion'){
renderDemotion(ev);
} else if (ev.kind === 'intl'){
renderIntl(ev);
}
}
function renderTransfer(ev){
const box = $('decisionBox');
box.innerHTML = `
<div class="dc-cat">🤝 Fichaje · Split ${G.split}</div>
<h2>Llamadas del agente</h2>
<p class="dc-ctx">Tu nivel ya da que hablar. Hay ${ev.offers.length} ofertas sobre la mesa y tu agente no para de mandarte mensajes con el emoji del cohete.</p>
<div class="opt-grid">
${ev.offers.map((o, i) => `
<button class="opt-card" data-i="${i}">
<span class="oc-label">Oferta ${['A','B','C'][i]}</span>
${logoImg(o.id, 64, 'oc-emblem')}
<span class="oc-title">${esc(o.name)}</span>
<span class="oc-tag">${o.rg} · ${o.div === 1 ? 'Primera' : 'Segunda'}</span>
</button>`).join('')}
<button class="opt-card wide" data-i="stay"><span class="oc-title">Quedarme donde estoy</span><span class="oc-desc">La continuidad también vale. El equipo confía en vos.</span></button>
</div>`;
box.querySelectorAll('.opt-card').forEach(b => b.addEventListener('click', () => {
const i = b.dataset.i;
let res;
if (i === 'stay'){
G.stats.sinergia = round1(clamp(G.stats.sinergia + 1.6, 20, 99));
G.stats.marca = round1(clamp(G.stats.marca + 0.6, 20, 99));
res = { kind: 'transferStay', ok: true, deltas: [{ k: 'sinergia', v: 1.6 }, { k: 'marca', v: 0.6 }], text: 'Te quedaste y la directiva lo valoró: confianza total en el proyecto. Los que se van vuelven a preguntarse si hicieron bien.' };
} else {
const off = ev.offers[parseInt(i, 10)];
const accepted = Math.random() >= (off.rg !== G.region && off.div === 1 ? 0.25 : 0.1);
if (accepted){
moveTeam(off.id, 'transfer');
G.stats.sinergia = round1(clamp(G.stats.sinergia - 1.5, 20, 99));
G.stats.marca = round1(clamp(G.stats.marca + 1.8, 20, 99));
res = { kind: 'transferMove', ok: true, delta: 'marca', deltaV: 1.8, text: `Firmaste por ${off.name}. Nuevo equipo, nuevos compas, nueva etapa. ${off.rg !== G.region ? 'Cruzás de región: todo es distinto y tu juego se renueva.' : 'La casa se cambia pero el sueño sigue igual.'}` };
} else {
G.stats.marca = round1(clamp(G.stats.marca - 1.5, 20, 99));
res = { kind: 'transferFail', ok: false, deltas: [{ k: 'marca', v: -1.5 }], text: `${off.name} te dejó esperando: al final ficharon a otro. Tu agente dice que "no era el momento".` };
}
}
showResult(res);
}));
}
function renderPromotion(ev){
const box = $('decisionBox');
box.innerHTML = `
<div class="dc-cat">🚀 Ascenso · Split ${G.split}</div>
<h2>Te llaman de ${esc(ev.team.name)}</h2>
<p class="dc-ctx">El equipo de primera de la región te quiere. Pasaste de ser una promesa a ser EL producto. Tu agente ya habla de cláusulas.</p>
<div class="opt-grid">
<button class="opt-card" data-i="go">
<span class="oc-label">Saltar a Primera</span>
${logoImg(ev.team.id, 64, 'oc-emblem')}
<span class="oc-title">${esc(ev.team.name)}</span>
<span class="oc-tag">${REGIONS[G.region].name} · Primera</span>
</button>
<button class="opt-card wide" data-i="stay"><span class="oc-title">Quedarme y liderar el ascenso</span><span class="oc-desc">Ser el rey de un reino chico también es un camino.</span></button>
</div>`;
box.querySelectorAll('.opt-card').forEach(b => b.addEventListener('click', () => {
let res;
if (b.dataset.i === 'go'){
moveTeam(ev.team.id, 'promotion');
G.stats.sinergia = round1(clamp(G.stats.sinergia + 1.8, 20, 99));
G.stats.marca = round1(clamp(G.stats.marca + 1.5, 20, 99));
res = { kind: 'promotionGo', ok: true, deltas: [{ k: 'sinergia', v: 1.8 }, { k: 'marca', v: 1.5 }], text: `Bienvenido a la Primera, ${G.nick}. ${ev.team.name} pagó el pase y el público ya corea tu nombre en la presentación.` };
} else {
G.stats.marca = round1(clamp(G.stats.marca + 1.6, 20, 99));
G.stats.sinergia = round1(clamp(G.stats.sinergia + 1.4, 20, 99));
res = { kind: 'promotionStay', ok: true, deltas: [{ k: 'marca', v: 1.6 }, { k: 'sinergia', v: 1.4 }], text: 'Te quedaste y llevaste al equipo al título de la segunda división. Los hinchas te quieren como a un ídolo de verdad.' };
}
showResult(res);
}));
}
function renderDemotion(ev){
const box = $('decisionBox');
const acad = TEAMS[G.region].filter(t => t.div === 2);
const target = acad.length ? pick(acad) : null;
box.innerHTML = `
<div class="dc-cat">📉 Crisis · Split ${G.split}</div>
<h2>El equipo no rinde y los dedos apuntan a vos</h2>
<p class="dc-ctx">Resultados malos, confianza baja, y la directiva ya habla de "reestructuración". Tu puesto en el plantel pende de un hilo.</p>
<div class="opt-grid">
<button class="opt-card" data-i="fight"><span class="oc-title">Pelearla en los scrims</span><span class="oc-desc">Responder con juego. La presión te pone en modo bestia.</span></button>
${target ? `<button class="opt-card" data-i="accept"><span class="oc-title">Préstamo a ${esc(target.name)}</span><span class="oc-desc">Bajar a segunda para recuperar confianza.</span></button>` : ''}
<button class="opt-card" data-i="leave"><span class="oc-title">Pedir la cláusula</span><span class="oc-desc">Si acá no te valoran, en otro lado sí.</span></button>
</div>`;
box.querySelectorAll('.opt-card').forEach(b => b.addEventListener('click', () => {
const i = b.dataset.i;
let res;
if (i === 'fight'){
const ok = Math.random() >= 0.35;
if (ok){
G.stats.mecanica = round1(clamp(G.stats.mecanica + 2, 20, 99));
G.stats.marca = round1(clamp(G.stats.marca + 1.5, 20, 99));
res = { kind: 'demFight', ok: true, deltas: [{ k: 'mecanica', v: 2 }, { k: 'marca', v: 1.5 }], text: 'La peleaste a puro huevo: dos semanas después sos titular indiscutido y el equipo levantó. La crisis te hizo más fuerte.' };
} else {
G.stats.marca = round1(clamp(G.stats.marca - 2, 20, 99));
res = { kind: 'demFight', ok: false, deltas: [{ k: 'marca', v: -2 }], text: 'Los scrims no te salieron y la directiva perdió la paciencia. Te bajan a la banca y el mensaje es claro: rendí o andá.' };
}
} else if (i === 'accept' && target){
moveTeam(target.id, 'demotion');
res = { kind: 'demLoan', ok: true, text: `Préstamo a ${target.name}: arrancaste de cero en segunda. Duelen el orgullo y el sueldo, pero el reset te recompuso la cabeza.` };
} else {
const ok = Math.random() >= 0.3;
if (ok){
const other = TEAMS[G.region].filter(t => t.id !== G.teamId);
const nt = other.length ? pick(other) : null;
if (nt){ moveTeam(nt.id, 'transfer'); res = { kind: 'demLeave', ok: true, text: `La cláusula se activó y fichaste por ${nt.name}. Cambio de aire, nueva chance, misma hambre.` }; }
else res = { kind: 'demLeave', ok: true, text: 'Saliste libre y negociaste: nuevo contrato, mismo sueño. A veces hay que irse para crecer.' };
} else {
res = { kind: 'demLeave', ok: false, text: 'Nadie pagó la cláusula. Te quedaste, pero la relación con la directiva quedó fría como el Meta de los últimos años.' };
}
}
showResult(res);
}));
}
function renderIntl(ev){
const box = $('decisionBox');
const comp = COMPETITIONS[ev.name];
const isWorlds = ev.name === 'Worlds';
box.innerHTML = `
<div class="dc-cat">${comp.ic} ${ev.name} · Split ${G.split}</div>
<h2>${isWorlds ? 'El torneo más grande del año' : ev.name}</h2>
<p class="dc-ctx">${esc(comp.flavor)} Tu equipo terminó ${ev.p >= 0.6 ? 'entre los mejores de la región' : 'con chances matemáticas'} y hay cupo para ${ev.name}.</p>
<div class="opt-grid">
<button class="opt-card" data-i="go"><span class="oc-title">Ir y darlo todo</span><span class="oc-desc">El escenario más grande. Tu momento.</span></button>
<button class="opt-card" data-i="focus"><span class="oc-title">Poner el foco en el split local</span><span class="oc-desc">Priorizar lo seguro: el título regional también vale.</span></button>
</div>`;
box.querySelectorAll('.opt-card').forEach(b => b.addEventListener('click', () => {
if (b.dataset.i === 'focus'){
G.stats.sinergia = round1(clamp(G.stats.sinergia + 1.5, 20, 99));
showResult({ kind: 'intlSkip', ok: true, deltas: [{ k: 'sinergia', v: 1.5 }], text: `Dejaste pasar ${ev.name} y apostaste al split local. El equipo llegó entero y peleó el título regional.` });
return;
}
runIntl(ev);
}));
}
function runIntl(ev){
const isWorlds = ev.name === 'Worlds';
const q = ev.p >= 0.35 ? 1 : (Math.random() >= 0.35 ? 1 : 0);
if (!q){
G.stats.marca = round1(clamp(G.stats.marca - 1.5, 20, 99));
G.stats.sinergia = round1(clamp(G.stats.sinergia - 1, 20, 99));
showResult({ kind: 'intlFail', ok: false, deltas: [{ k: 'marca', v: -1.5 }, { k: 'sinergia', v: -1 }], text: `${ev.name}: el equipo no clasificó en la última fecha. Viste la final en el sillón de la sala, con la bronca adentro. La región entera se acordó de tu lineup.` });
return;
}
/* participa en la competencia: suma desempeño a la selección regional */
const p = ev.p;
const r = rating();
const role = ROLES[G.role];
const games = Math.round(8 + p * 10);
const factor = (r / 60) * (games / 12) * (0.9 + Math.random() * 0.25);
const kills = Math.round(role.kill * factor * 0.8);
const ast = Math.round(role.ast * factor * 0.8);
G.regSelect.apps++;
G.regSelect.games += games;
G.regSelect.kills += kills;
G.regSelect.ast += ast;
const stageNames = ['la fase de grupos', 'cuartos de final', 'la semifinal', 'la final'];
let stage = -1;
let win = true;
for (let i = 0; i < 4; i++){
const chance = Math.max(0, ev.rolls[i]);
if (Math.random() < chance) continue;
win = false; stage = i; break;
}
const name = ev.name;
if (!win){
if (stage === 3){
G.intl.push({ name, result: 'subcampeón' });
G.teamTitles.push({ name: 'Subcampeón de ' + name, ic: '🥈' });
G.stats.marca = round1(clamp(G.stats.marca - 0.8, 20, 99));
showResult({ kind: 'intl2', ok: false, deltas: [{ k: 'marca', v: -0.8 }], text: `${ev.name}: llegaron a LA FINAL y la perdieron por detalles. Subcampeones: la región te llora y te quiere igual.` });
} else if (stage === 2){
G.intl.push({ name, result: 'semifinalista' });
G.stats.marca = round1(clamp(G.stats.marca - 1.2, 20, 99));
G.stats.sinergia = round1(clamp(G.stats.sinergia - 1, 20, 99));
showResult({ kind: 'intl3', ok: false, deltas: [{ k: 'marca', v: -1.2 }, { k: 'sinergia', v: -1 }], text: `${ev.name}: cayeron en la semifinal a manos del eventual campeón. Buen torneo, bronca eterna.` });
} else {
G.intl.push({ name, result: stage === 1 ? 'cuartos de final' : 'fase de grupos' });
G.stats.marca = round1(clamp(G.stats.marca - 1.8, 20, 99));
G.stats.sinergia = round1(clamp(G.stats.sinergia - 1.5, 20, 99));
showResult({ kind: 'intl4', ok: false, deltas: [{ k: 'marca', v: -1.8 }, { k: 'sinergia', v: -1.5 }], text: `${ev.name}: eliminados en ${stageNames[stage]}. Los memes de la región no esperan a nadie.` });
}
return;
}
G.teamTitles.push({ name: 'Campeón de ' + name, ic: isWorlds ? '🏆' : '🏅' });
G.intl.push({ name, result: 'campeón' });
const mvp = Math.random() >= 0.5;
if (mvp) G.indTitles.push({ name: 'MVP de la final de ' + name, ic: '🌟' });
showResult({ kind: 'intlWin', ok: true, text: ${name.toUpperCase()} CHAMPIONS! Tu equipo se llevó el trofeo y VOS fuiste la figura${mvp ? ' (MVP de la final, obvio)' : ''}. La región festeja, los haters guardan los memes, y tu valor de mercado explota.` });
}
/* ---------- RESOLUCIÓN ---------- */
function resolveDecision(ev, optIdx){
const opt = ev.opts[optIdx];
const effectiveRisk = Math.min(0.85, opt.risk * 1.0 + 0.03);
const ok = Math.random() >= effectiveRisk;
const res = { kind: 'decision', ev, opt, ok, text: ok ? opt.pos : opt.neg, deltas: [] };
const SCALE = 2.6;
if (ok){
Object.entries(opt.d).forEach(([k, v]) => {
G.stats[k] = round1(clamp(G.stats[k] + v * SCALE, 20, 99));
res.deltas.push({ k, v: Math.round(v * SCALE * 10) / 10 });
});
} else {
Object.entries(opt.d).forEach(([k, v]) => {
G.stats[k] = round1(clamp(G.stats[k] - v * SCALE * 0.9, 20, 99));
res.deltas.push({ k, v: -Math.round(v * SCALE * 0.9 * 10) / 10 });
});
}
if (opt.x && opt.x.rolChange) res.rolChange = true;
if (opt.x && opt.x.transfer === 'other') res.transferSearch = true;
return res;
}
function round1(x){ return Math.round(x * 10) / 10; }
function applySplitPerf(res){
const r = rating();
const role = ROLES[G.role];
const winRate = clamp(0.35 + (r - 50) / 160 + (G.div === 2 ? -0.03 : 0.03), 0.22, 0.82);
for (const k of ['mecanica','vision','farmeo','sinergia','marca']){
G.stats[k] = round1(clamp(G.stats[k] + 0.48 + Math.random() * 0.2 + (winRate - 0.45) * 1.0, 20, 99));
}
if (winRate > 0.55){
G.stats.mecanica = round1(clamp(G.stats.mecanica + 0.4, 20, 99));
G.stats.sinergia = round1(clamp(G.stats.sinergia + 0.3, 20, 99));
}
const games = 16 + Math.floor(r / 6) + rnd(0, 3);
const factor = (r / 60) * (games / 20) * (0.9 + Math.random() * 0.25);
const kills = Math.round(role.kill * factor);
const assists = Math.round(role.ast * factor * 0.95);
const deaths = Math.round(games * 0.11 * (1 + (100 - r) / 260));
G.kills += kills; G.assists += assists; G.deaths += deaths; G.games += games;
const pentaP = (r - 55) / 2600 * role.pentaW * (G.role === 'ADC' || G.role === 'Mid' ? 1.35 : 1);
if (Math.random() < Math.max(0, pentaP)){
G.penta++;
if (res) res.penta = true;
}
G.value = marketValue();
if (G.value > G.maxValue) G.maxValue = G.value;
G.salary = Math.round(G.value / 24);
updateRatingMax();
return { games, kills, assists };
}
/* ---------- CAMBIO DE EQUIPO ---------- */
function moveTeam(newId, reason){
const old = G.teams[G.teams.length - 1];
old.to = G.split;
const t = teamById(newId);
G.teamId = newId;
G.div = t.div;
G.teams.push({ id: newId, name: t.name, div: t.div, from: G.split, to: G.totalSplits, games: 0, kills: 0, ast: 0 });
if (reason === 'promotion') G.stats.sinergia = round1(clamp(G.stats.sinergia + 1.5, 20, 99));
if (reason === 'demotion') G.stats.marca = round1(clamp(G.stats.marca - 2, 20, 99));
}
/* ---------- RESULTADO + CIERRE DE SPLIT ---------- */
function showResult(res){
const card = $('resultCard');
const great = res.ok && (res.kind === 'intlWin' || (res.deltas && res.deltas.some(d => d.v >= 2)));
const bad = !res.ok && !['intlSkip','intlWin'].includes(res.kind);
const verdict = res.ok ? (great ? '¡JUGADÓN!' : 'BIEN AHÍ') : (bad ? 'QUE PEDO' : 'CÓMO SALIÓ');
const teamChange = ['transferMove','promotionGo','demLoan','demLeave'].includes(res.kind);
card.className = 'result-card';
let html = `<div class="verdict ${bad ? 'bad' : great ? 'great' : ''}">${verdict}</div>
<p class="rtext">${esc(res.text)}</p>`;
if (teamChange){
html += `<div class="team-change">${logoImg(G.teamId, 56, 'tc-logo')}<span class="arrow">→</span></div>`;
}
if (res.penta) html += `<div class="deltas"><span class="delta up">PENTAKILL</span></div>`;
if (res.deltas && res.deltas.length) html += `<div class="deltas">${res.deltas.map(d => `<span class="delta ${d.v >= 0 ? 'up' : 'down'}">${statLabel(d.k)} ${d.v >= 0 ? '+' : ''}${d.v}</span>`).join('')}</div>`;
if (res.rolChange) html += `<div class="deltas"><span class="delta money">Ahora sos ${G.role}</span></div>`;
html += `<button class="btn" id="btnContinue" style="margin-top:8px">Continuar</button>`;
card.innerHTML = html;
$('resultOverlay').classList.add('show');
$('btnContinue').addEventListener('click', () => {
$('resultOverlay').classList.remove('show');
doSplitEnd(res);
});
}
function statLabel(k){ const m = statNames().find(s => s[0] === k); return m ? m[1] : k; }
function doSplitEnd(res){
if (G.retiring){ finishCareer(); return; }
if (res && res.kind === 'decision' && res.opt && res.opt.x && res.opt.x.rolChange){
const roles = Object.keys(ROLES);
G.role = roles[(roles.indexOf(G.role) + rnd(1, roles.length - 1)) % roles.length];
}
const perf = applySplitPerf(null);
if (perf.penta) G.events.push('penta@' + G.split);
/* registrar split en historial de timeline */
const curTeam = teamById(G.teamId);
G.history.push({ split: G.split, org: G.teamId, orgName: curTeam.name, elo: rating(), games: perf.games, kills: perf.kills, ast: perf.assists });
const lastTeam = G.teams[G.teams.length - 1];
lastTeam.games += perf.games; lastTeam.kills += perf.kills; lastTeam.ast += perf.assists;
/* título de split regional (primera o segunda división) */
if (G.split % 2 === 0){
const year = 2024 + Math.floor((G.split - 1) / 2);
const need = G.div === 1 ? REGIONS[G.region].threshold - 8 : REGIONS[G.region].threshold - 15;
if (rating() >= need){
const p = rating() / 112;
const roll = Math.random();
if (roll < p){
G.teamTitles.push({ name: G.div === 1 ? `Campeón de ${REGIONS[G.region].name} (${year})` : `Campeón de la 2ª División de ${REGIONS[G.region].name} (${year})`, ic: '🏆' });
if (Math.random() < 0.4) award('MVP del Split', '🌟');
} else if (roll < p + p * 0.7){
G.teamTitles.push({ name: `Subcampeón de ${REGIONS[G.region].name} (${year})`, ic: '🥈' });
}
}
}
if (rating() >= 62) award('Guante de Oro', '🥇');
if (G.stats.farmeo >= 60) award('Rey del Farmeo', '🌾');
if (G.penta >= 1) award('Carry Definitivo', '💥');
if (G.stats.marca >= 58) award('Community Darling', '❤️');
if (G.split >= G.totalSplits){ finishCareer(); } else { nextSplit(); }
}
function award(name, ic){
if (!G.indTitles.find(t => t.name === name)) G.indTitles.push({ name, ic });
}
function intlIcon(result){
if (result === 'campeón') return '🏆';
if (result === 'subcampeón') return '🥈';
return '🌍';
}
function finishCareer(){
G.retired = true;
G.finalRating = rating();
G.value = marketValue();
updateRatingMax();
if (G.indTitles.length === 0 && rating() >= 72) G.indTitles.push({ name: 'Ídolo de la comunidad', ic: '🔥' });
saveHall();
renderSummary();
showScreen('summary');
}
/* ---------- RESUMEN (Screen B) ---------- */
function renderSummary(){
const region = REGIONS[G.region];
const rs = G.regSelect;
const kda = G.deaths ? ((G.kills + G.assists) / G.deaths).toFixed(2) : '∞';
$('sumSub').textContent = `${G.role} · ${region.name} · ${G.totalSplits} splits · ${G.retired ? 'retirado' : 'leyenda'}`;
/* Panel 1: identidad */
const eloPanel = `
<div class="sum-panel">
<div class="sp-label">Carrera terminada</div>
<div style="display:flex;align-items:center;gap:10px">
<div style="flex:1;min-width:0">
<div class="sp-nick">${esc(G.nick)}</div>
<div class="sp-row"><span class="badge badge-role">#${G.num} ${G.role}</span><span class="badge badge-region">${region.flag} ${region.code}</span></div>
<div class="sp-value">VALOR <b>${fmtMoney(G.maxValue)}</b></div>
</div>
<div class="big-elo"><span class="lbl">ELO</span><span class="num">${G.ratingMax}</span></div>
</div>
<div class="sp-stats">
<div class="stat3"><div class="stat-num">${G.games}</div><div class="stat-lbl">Partidas</div></div>
<div class="stat3"><div class="stat-num">${G.kills}</div><div class="stat-lbl">Kills</div></div>
<div class="stat3"><div class="stat-num">${G.assists}</div><div class="stat-lbl">Asist.</div></div>
</div>
</div>`;
/* Panel 2: selección regional */
const regPicks = G.intl.filter(x => x.result === 'campeón').length;
const regPanel = `
<div class="sum-panel">
<div class="sp-label">Selección regional</div>
<div class="sp-flag">${region.flag}</div>
<div class="sp-region">${region.name}</div>
<div class="sp-region-sub">${rs.apps} participaciones internacionales</div>
<div class="sp-stats">
<div class="stat3"><div class="stat-num">${rs.games}</div><div class="stat-lbl">Partidas</div></div>
<div class="stat3"><div class="stat-num">${rs.kills}</div><div class="stat-lbl">Kills</div></div>
<div class="stat3"><div class="stat-num">${rs.ast}</div><div class="stat-lbl">Asist.</div></div>
</div>
<div class="ach-grid">${regPicks ? `<span class="ach-chip">🏆 ${regPicks} internacional</span>` : ''}</div>
</div>`;
/* Panel 3: premios individuales */
const indPanel = `
<div class="sum-panel">
<div class="sp-label">Premios</div>
${G.indTitles.length ? `<div class="ach-grid">${G.indTitles.map(t => `<span class="ach-chip">${t.ic} ${esc(t.name)}</span>`).join('')}</div>` : `<div class="ach-empty"><span class="ic">🏆</span>Sin premios</div>`}
</div>`;
$('sumTop').innerHTML = eloPanel + regPanel + indPanel;
/* grid de orgs */
$('orgsGrid').innerHTML = G.teams.map(t => {
const color = REGIONS[G.region].accent;
return `<div class="org-card" style="--og-color:${color}">
<div class="og-bg"></div>
${logoImg(t.id, 56, 'og-logo')}
<div class="og-name">${esc(t.name)}</div>
<div class="og-div">${t.div === 1 ? 'Primera' : 'Segunda'} · Splits ${t.from}${t.to}</div>
<div class="og-stats"><span><b>${t.games}</b> GAM</span><span><b>${t.kills}</b> KIL</span><span><b>${t.ast}</b> AST</span></div>
</div>`;
}).join('');
$('sumQuote').textContent = quoteFor();
}
function quoteFor(){
const r = G.ratingMax;
const w = G.teamTitles.filter(x => x.name.startsWith('Campeón de Worlds')).length;
if (w) return '«La leyenda no necesita explicaciones: tiene el trofeo.» — vos, después del Worlds';
if (r >= 90) return '«Dicen que el teclado no define al jugador. Define poco, pero el tuyo era el más caro del server.»';
if (r >= 75) return '«Fuiste de las mejores en el server, pero las finales se te hicieron espejito. La próxima, dicen.»';
if (r >= 60) return '«Profesional, estable, querido por la hinchada. El podcast de la liga te va a extrañar.»';
return '«El LoL no era para cualquiera. Evidentemente.» — tus viejos, en cada cena';
}
/* ---------- HALL OF FAME ---------- */
function saveHall(){
try {
const best = loadHall();
best.push({ nick: G.nick, rating: G.ratingMax, value: G.maxValue, region: G.region, role: G.role, ts: Date.now() });
best.sort((a, b) => b.rating - a.rating || b.value - a.value);
localStorage.setItem('lolero-hall', JSON.stringify(best.slice(0, 8)));
} catch (e) {}
}
function loadHall(){
try { return JSON.parse(localStorage.getItem('lolero-hall')) || []; } catch (e) { return []; }
}
/* ---------- EXPORT IMAGEN ---------- */
function exportImage(){
const W = 1080, H = 1920;
const cv = document.createElement('canvas');
cv.width = W; cv.height = H;
const ctx = cv.getContext('2d');
const x = W / 2;
let y = 0;
const grad = ctx.createLinearGradient(0, 0, 0, H);
grad.addColorStop(0, '#0a0a0c'); grad.addColorStop(0.5, '#0f0f12'); grad.addColorStop(1, '#0a0a0c');
ctx.fillStyle = grad; ctx.fillRect(0, 0, W, H);
const gold = '#d9730a', gold2 = '#f59e0b', mut = '#a1a1aa', txt = '#f4f4f5', muted = '#52525b';
const font = (s, w) => `${w || 800} ${s}px 'Inter', system-ui, sans-serif`;
const center = t => { ctx.textAlign = 'center'; ctx.fillText(t, x, y); };
ctx.fillStyle = gold; ctx.font = font(64, 800);
y = 130; center('1v9');
ctx.fillStyle = gold2; ctx.font = font(30, 700); ctx.letterSpacing = '6px';
y += 50; center('CONVERTITE EN LEYENDA');
ctx.font = font(22, 600); ctx.fillStyle = mut; ctx.letterSpacing = '0px';
y += 44; center('Carrera de pro player · League of Legends');
ctx.strokeStyle = 'rgba(217,115,10,.4)'; ctx.lineWidth = 2;
y += 40; ctx.beginPath(); ctx.moveTo(120, y); ctx.lineTo(W - 120, y); ctx.stroke();
y += 56;
ctx.fillStyle = txt; ctx.font = font(44, 800);
center((G.nick || 'Leyenda').toUpperCase());
ctx.font = font(24, 600); ctx.fillStyle = mut;
y += 42; center(`${G.role} · ${REGIONS[G.region].name} · ${REGIONS[G.region].country} · N° ${G.num}`);
y += 64;
const boxes = [['PARTIDAS',G.games],['KILLS',G.kills],['ASIST.',G.assists],['MUERTES',G.deaths],['PENTAKILLS',G.penta],['KDA',G.deaths ? ((G.kills+G.assists)/G.deaths).toFixed(2) : '∞']];
const bw = 300, bh = 130, gap = 24, ox = (W - 3*bw - 2*gap) / 2;
boxes.forEach((b, i) => {
const cx = ox + (i % 3) * (bw + gap), cy = y + Math.floor(i / 3) * (bh + gap);
ctx.fillStyle = 'rgba(24,24,27,.9)'; ctx.strokeStyle = '#27272a'; ctx.lineWidth = 2;
roundRect(ctx, cx, cy, bw, bh, 18); ctx.fill(); ctx.stroke();
ctx.fillStyle = mut; ctx.font = font(20, 700); ctx.textAlign = 'center';
ctx.fillText(b[0], cx + bw / 2, cy + 34);
ctx.fillStyle = gold2; ctx.font = font(42, 800);
ctx.fillText(String(b[1]), cx + bw / 2, cy + 100);
});
y += 2 * (bh + gap) + 30;
ctx.fillStyle = mut; ctx.font = font(22, 700); center('ELO MÁXIMO');
ctx.fillStyle = gold; ctx.font = font(110, 800); y += 52; center(String(G.ratingMax));
ctx.font = font(22, 700); ctx.fillStyle = mut; y += 46; center('VALOR MÁXIMO');
ctx.fillStyle = gold2; ctx.font = font(44, 800); y += 50; center(fmtMoney(G.maxValue));
y += 50;
ctx.fillStyle = mut; ctx.font = font(22, 700); center('EQUIPOS EN LA CARRERA');
y += 14;
const teamRows = G.teams.map(t => `${t.name} (Splits ${t.from}${t.to}, ${t.div === 1 ? '1ª' : '2ª'})`);
ctx.fillStyle = txt; ctx.font = font(26, 700);
for (const row of teamRows.slice(0, 6)){ y += 40; center(row); }
if (teamRows.length > 6){ ctx.fillStyle = mut; ctx.font = font(20, 600); y += 36; center(`+ ${teamRows.length - 6} equipos más`); }
y += 46;
ctx.fillStyle = mut; ctx.font = font(22, 700); center('TÍTULOS');
const allTitles = G.teamTitles.concat(G.indTitles);
ctx.font = font(24, 600); y += 14;
if (!allTitles.length){ y += 38; ctx.fillStyle = mut; center('Ninguno. La gloria no era para hoy.'); }
else {
for (const t of allTitles.slice(0, 6)){ y += 40; ctx.fillStyle = gold2; center(`${t.ic} ${t.name}`); }
if (allTitles.length > 6){ ctx.fillStyle = mut; ctx.font = font(20, 600); y += 36; center(`+ ${allTitles.length - 6} más`); }
}
y += 46;
ctx.fillStyle = mut; ctx.font = font(22, 700); center('INTERNACIONAL');
ctx.font = font(24, 600); y += 14;
if (!G.intl.length){ y += 38; ctx.fillStyle = mut; center('Nunca salió del server regional'); }
else {
for (const t of G.intl){ y += 40; ctx.fillStyle = t.result === 'campeón' ? gold2 : txt; center(`${intlIcon(t.result)} ${t.name}: ${t.result}`); }
}
y += 56;
ctx.strokeStyle = 'rgba(217,115,10,.4)'; ctx.beginPath(); ctx.moveTo(120, y); ctx.lineTo(W - 120, y); ctx.stroke();
ctx.fillStyle = mut; ctx.font = font(24, 600); y += 44; center(`${G.nick || 'Leyenda'} · Fin de la carrera`);
ctx.font = font(20, 600); y += 34; center('Jugá la tuya: lolero.cbcren.online');
cv.toBlob(blob => {
if (!blob){ toast('No se pudo exportar :('); return; }
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `1v9_${(G.nick || 'leyenda').replace(/[^a-z0-9]/gi, '_')}.png`;
document.body.appendChild(a); a.click();
setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 500);
toast('Imagen descargada 📸 Compartila con el mundo');
}, 'image/png');
}
function roundRect(ctx, x, y, w, h, r){
ctx.beginPath();
ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath();
}
let toastTimer = null;
function toast(msg){
const t = $('toast'); t.textContent = msg; t.classList.add('show');
clearTimeout(toastTimer); toastTimer = setTimeout(() => t.classList.remove('show'), 2600);
}
</script>
</body>
</html>