1545 lines
100 KiB
HTML
1545 lines
100 KiB
HTML
<!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>⚔️ 5 roles</span><span>🌎 6 regiones</span><span>🏆 Worlds</span><span>🥶 < 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>🏆 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° 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">⬇ Descargar imagen</button>
|
||
<button class="btn secondary" id="btnAgain">↻ 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="fallback ${cls||''}" style="width:${size}px;height:${size}px">${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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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>
|