Files
web_template/web/dist/game2/index.html
Geliebte a84129330e feat: 全站20个模板添加国际化(i18n)支持 - 简体中文/繁体中文/英文三语切换
- 所有18套前端模板 + 管理后台共20个HTML文件添加i18n引擎
- 前端JavaScript方案:I18N_LOCALES翻译字典 + data-i18n属性 + t()函数调用
- 语言切换器UI:下拉菜单选择,匹配各模板主题风格
- 语言持久化:localStorage存储 + URL参数覆盖 + 浏览器语言自动检测
- onLangChange回调:语言切换时动态重新渲染所有内容
- 支持语言:zh-CN(简体中文) / zh-TW(繁体中文) / en(English)
2026-09-09 16:58:40 +08:00

1396 lines
78 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PIXEL GAMES</title>
<style>
:root{
--bg:#1a1a2e;
--bg-2:#16213e;
--bg-3:#0f3460;
--bg-4:#0a0a1a;
--green:#0fbc6e;
--green-d:#0a8a52;
--green-l:#3ce896;
--yellow:#f9d71c;
--yellow-d:#c9a800;
--red:#e94560;
--red-d:#b8334c;
--purple:#6c5ce7;
--purple-d:#4e3fc4;
--cyan:#00e5ff;
--text:#e8e8f0;
--text-dim:#8a8aae;
--text-dimer:#5a5a78;
--ink:#000000;
--shadow:rgba(0,0,0,.55);
--font:'Courier New',Courier,monospace;
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html{scroll-behavior:smooth}
body{font-family:var(--font);background:var(--bg);color:var(--text);font-size:14px;line-height:1.5;letter-spacing:.5px;overflow-x:hidden;image-rendering:pixelated;background-image:radial-gradient(circle at 20% 10%,rgba(108,92,231,.08),transparent 40%),radial-gradient(circle at 80% 80%,rgba(15,188,110,.07),transparent 40%);background-attachment:fixed}
img{display:block;max-width:100%;image-rendering:pixelated}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit;letter-spacing:inherit}
input,select{font-family:inherit;letter-spacing:inherit;color:inherit}
a{color:inherit;text-decoration:none}
ul{list-style:none}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:var(--bg-4)}
::-webkit-scrollbar-thumb{background:var(--green-d);border:2px solid var(--bg-4)}
::-webkit-scrollbar-thumb:hover{background:var(--green)}
.crt-overlay{position:fixed;inset:0;z-index:9998;pointer-events:none;background:repeating-linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,0) 2px,rgba(0,0,0,.22) 3px,rgba(0,0,0,.22) 3px);mix-blend-mode:multiply;opacity:.7}
.crt-vignette{position:fixed;inset:0;z-index:9997;pointer-events:none;box-shadow:inset 0 0 140px 30px rgba(0,0,0,.65),inset 0 0 40px 0 rgba(0,0,0,.4)}
.crt-flicker{position:fixed;inset:0;z-index:9999;pointer-events:none;background:rgba(15,188,110,.012);animation:flicker 6s steps(60) infinite}
@keyframes flicker{0%{opacity:.3}1%{opacity:.5}2%{opacity:.3}50%{opacity:.4}51%{opacity:.55}52%{opacity:.35}100%{opacity:.3}}
.container{max-width:1240px;margin:0 auto;padding:0 22px}
.pixel-font{font-family:var(--font);letter-spacing:2px;text-transform:uppercase}
.pbox{background:var(--bg-2);border:4px solid var(--ink);box-shadow:inset -4px -4px 0 0 rgba(0,0,0,.45),inset 4px 4px 0 0 rgba(255,255,255,.06),6px 6px 0 0 var(--shadow);position:relative}
.pbox-green{border-color:var(--green)}
.pbox-yellow{border-color:var(--yellow)}
.pbox-red{border-color:var(--red)}
.pbox-purple{border-color:var(--purple)}
.pbtn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--font);font-weight:bold;letter-spacing:2px;text-transform:uppercase;background:var(--green);color:var(--ink);border:4px solid var(--ink);box-shadow:4px 4px 0 0 var(--ink),inset -3px -3px 0 0 rgba(0,0,0,.28),inset 3px 3px 0 0 rgba(255,255,255,.25);padding:12px 22px;transition:transform .04s linear,box-shadow .04s linear;cursor:pointer;user-select:none;image-rendering:pixelated}
.pbtn:hover{filter:brightness(1.1)}
.pbtn:active{transform:translate(4px,4px);box-shadow:0 0 0 0 var(--ink),inset -3px -3px 0 0 rgba(0,0,0,.28),inset 3px 3px 0 0 rgba(255,255,255,.18)}
.pbtn-yellow{background:var(--yellow)}
.pbtn-red{background:var(--red);color:#fff}
.pbtn-purple{background:var(--purple);color:#fff}
.pbtn-ghost{background:var(--bg-3);color:var(--green);box-shadow:4px 4px 0 0 var(--ink),inset -3px -3px 0 0 rgba(0,0,0,.4),inset 3px 3px 0 0 rgba(255,255,255,.06)}
.pbtn-sm{padding:7px 12px;font-size:12px;box-shadow:3px 3px 0 0 var(--ink),inset -2px -2px 0 0 rgba(0,0,0,.28),inset 2px 2px 0 0 rgba(255,255,255,.2)}
.pbtn-sm:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink),inset -2px -2px 0 0 rgba(0,0,0,.28),inset 2px 2px 0 0 rgba(255,255,255,.15)}
.clip-corner{clip-path:polygon(0 0,calc(100% - 14px) 0,100% 14px,100% 100%,14px 100%,0 calc(100% - 14px))}
@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}
.blink{animation:blink 1s steps(1) infinite}
@keyframes blink-soft{0%,49%{opacity:1}50%,100%{opacity:.25}}
.blink-soft{animation:blink-soft 1.1s steps(1) infinite}
@keyframes flash{0%,100%{background:var(--red)}50%{background:var(--yellow)}}
@keyframes float-up{0%{transform:translateY(0)}50%{transform:translateY(-8px)}100%{transform:translateY(0)}}
@keyframes scroll-bg{from{background-position:0 0}to{background-position:48px 48px}}
@keyframes pulse-border{0%,100%{box-shadow:0 0 0 0 rgba(249,215,28,.6)}50%{box-shadow:0 0 0 6px rgba(249,215,28,0)}}
@keyframes marquee{from{transform:translateX(100%)}to{transform:translateX(-100%)}}
@keyframes slidein{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}
@keyframes coin-spin{0%{transform:scaleX(1)}50%{transform:scaleX(.1)}100%{transform:scaleX(1)}}
.pix{position:relative;display:inline-block;width:16px;height:16px;vertical-align:middle}
.pix-coin{background:var(--yellow);box-shadow:0 -4px 0 var(--yellow),0 4px 0 var(--yellow),-4px 0 0 var(--yellow-d),4px 0 0 var(--yellow-d);width:8px;height:8px;margin:4px}
.pix-heart{width:12px;height:12px;background:var(--red);clip-path:polygon(50% 100%,0 50%,0 25%,25% 0,50% 25%,75% 0,100% 25%,100% 50%)}
.pix-star{width:12px;height:12px;background:var(--yellow);clip-path:polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%)}
.pix-bolt{width:8px;height:12px;background:var(--yellow);clip-path:polygon(60% 0,0 70%,40% 70%,30% 100%,100% 30%,55% 30%,70% 0)}
.sprite{image-rendering:pixelated;display:inline-block}
.lang-switcher{position:relative;margin-left:8px}
.lang-btn{display:flex;align-items:center;gap:6px;padding:8px 14px;background:var(--bg-3);border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);font-weight:bold;color:var(--text-dim);font-size:12px;letter-spacing:1px;cursor:pointer;transition:.08s}
.lang-btn:hover{color:var(--yellow);transform:translate(-1px,-1px);box-shadow:4px 4px 0 0 var(--ink)}
.lang-btn svg{width:16px;height:16px}
.lang-dropdown{position:absolute;top:calc(100% + 8px);right:0;min-width:140px;z-index:200;background:var(--bg-2);border:4px solid var(--ink);box-shadow:6px 6px 0 0 var(--shadow);opacity:0;transform:translateY(-8px);pointer-events:none;transition:all .2s}
.lang-dropdown.open{opacity:1;transform:none;pointer-events:auto}
.lang-option{display:flex;align-items:center;gap:8px;padding:10px 14px;font-size:13px;color:var(--text-dim);cursor:pointer;transition:all .08s;border-bottom:2px solid var(--bg-4)}
.lang-option:last-child{border-bottom:none}
.lang-option:hover{background:var(--bg-3);color:var(--yellow)}
.lang-option.active{color:var(--green);font-weight:bold}
.lang-option .check{width:14px;height:14px;opacity:0;transition:opacity .2s}
.lang-option.active .check{opacity:1}
.header{position:sticky;top:0;z-index:200;background:var(--bg-4);border-bottom:4px solid var(--green);box-shadow:0 4px 0 0 rgba(15,188,110,.25)}
.header-inner{display:flex;align-items:center;gap:18px;height:74px}
.logo{display:flex;align-items:center;gap:12px;flex-shrink:0;cursor:pointer}
.logo-mark{width:44px;height:44px;background:var(--green);border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);display:grid;place-items:center;position:relative}
.logo-mark::before{content:"";position:absolute;width:10px;height:10px;background:var(--yellow);box-shadow:14px 0 0 var(--red),0 14px 0 var(--purple),14px 14px 0 var(--yellow);top:7px;left:7px}
.logo-txt{font-size:20px;font-weight:bold;letter-spacing:3px;color:var(--green);text-shadow:3px 3px 0 var(--ink),3px 0 0 var(--ink),0 3px 0 var(--ink);line-height:1}
.logo-txt small{display:block;font-size:9px;color:var(--yellow);letter-spacing:2px;margin-top:4px;text-shadow:2px 2px 0 var(--ink)}
.nav{display:flex;gap:6px;margin-left:10px}
.nav a{display:inline-block;padding:9px 14px;font-size:13px;font-weight:bold;letter-spacing:2px;color:var(--text-dim);text-transform:uppercase;border:4px solid transparent;transition:.08s}
.nav a:hover{color:var(--yellow)}
.nav a.active{color:var(--ink);background:var(--yellow);box-shadow:3px 3px 0 0 var(--ink)}
.search{flex:1;max-width:420px;margin-left:auto;display:flex}
.search input{flex:1;height:46px;background:var(--bg-2);color:var(--text);border:4px solid var(--ink);border-right:none;padding:0 14px;font-size:13px;letter-spacing:1px;box-shadow:inset 3px 3px 0 0 rgba(0,0,0,.5)}
.search input::placeholder{color:var(--text-dimer)}
.search input:focus{outline:none;background:var(--bg-3)}
.search button{height:46px;width:54px;background:var(--green);color:var(--ink);border:4px solid var(--ink);font-size:18px;font-weight:bold;box-shadow:3px 3px 0 0 var(--ink)}
.search button:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.coin-counter{display:flex;align-items:center;gap:8px;padding:8px 14px;background:var(--bg-2);border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);font-weight:bold;color:var(--yellow);font-size:13px;letter-spacing:1px}
.hero{position:relative;overflow:hidden;background:linear-gradient(180deg,var(--bg-3) 0%,var(--bg) 100%);border-bottom:4px solid var(--ink);padding:70px 0 90px}
.hero-grid-bg{position:absolute;inset:0;opacity:.25;pointer-events:none;background-image:linear-gradient(rgba(15,188,110,.18) 1px,transparent 1px),linear-gradient(90deg,rgba(15,188,110,.18) 1px,transparent 1px);background-size:32px 32px;animation:scroll-bg 8s linear infinite}
.hero-inner{position:relative;display:grid;grid-template-columns:1.2fr .8fr;gap:40px;align-items:center}
.hero-title{font-size:clamp(40px,7vw,84px);font-weight:bold;letter-spacing:6px;line-height:1;color:var(--green);text-shadow:6px 6px 0 var(--ink),6px 0 0 var(--ink),0 6px 0 var(--ink);margin-bottom:18px}
.hero-title .y{color:var(--yellow)}
.hero-title .r{color:var(--red)}
.hero-sub{font-size:14px;letter-spacing:3px;color:var(--text-dim);margin-bottom:10px}
.press-start{font-size:clamp(22px,4vw,38px);font-weight:bold;letter-spacing:5px;color:var(--yellow);text-shadow:4px 4px 0 var(--ink);margin:18px 0 26px;min-height:1.2em}
.hero-tags{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:28px}
.hero-tag{padding:6px 12px;font-size:11px;letter-spacing:1px;background:var(--bg-2);border:3px solid var(--ink);box-shadow:2px 2px 0 0 var(--ink);color:var(--green)}
.hero-cta{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
.hero-coin{font-size:12px;color:var(--text-dim);letter-spacing:1px;display:flex;align-items:center;gap:8px}
.hero-scene{display:flex;justify-content:center;align-items:flex-end;gap:18px;min-height:260px;position:relative}
.scene-cloud{position:absolute;background:#fff;opacity:.5}
.scene-cloud.c1{width:40px;height:14px;top:10px;left:10%;box-shadow:-10px 0 0 #fff,10px -8px 0 #fff}
.scene-cloud.c2{width:30px;height:10px;top:40px;right:12%;box-shadow:-8px 0 0 #fff,8px -6px 0 #fff;opacity:.35}
.scene-ground{position:absolute;bottom:0;left:0;right:0;height:34px;background:repeating-linear-gradient(90deg,var(--green-d) 0 16px,var(--green) 16px 32px);border-top:4px solid var(--ink)}
.scene-char{width:64px;height:84px;position:relative;animation:float-up 2.2s ease-in-out infinite}
.scene-brick{width:36px;height:36px;background:var(--red);border:4px solid var(--ink);box-shadow:inset 4px 4px 0 rgba(255,255,255,.25),3px 3px 0 var(--ink);position:relative}
.scene-brick::after{content:"";position:absolute;inset:6px;border:3px solid var(--red-d)}
.scene-pipe{width:44px;height:70px;background:var(--green);border:4px solid var(--ink);box-shadow:inset 4px 4px 0 rgba(255,255,255,.2),3px 3px 0 var(--ink)}
.scene-pipe::before{content:"";display:block;height:14px;background:var(--green-l);border-bottom:4px solid var(--ink)}
.scene-coin{width:18px;height:18px;background:var(--yellow);border:3px solid var(--ink);box-shadow:inset 3px 0 0 var(--yellow-d);animation:coin-spin 1.4s steps(2) infinite;margin-top:10px}
.ticker{background:var(--ink);border-bottom:4px solid var(--ink);overflow:hidden;white-space:nowrap}
.ticker-track{display:inline-block;padding:9px 0;font-size:12px;letter-spacing:2px;color:var(--yellow);animation:marquee 22s linear infinite}
.ticker-track span{margin:0 30px}
.ticker-track .r{color:var(--red)}
.ticker-track .g{color:var(--green-l)}
section{padding:54px 0}
.sec-head{display:flex;align-items:center;gap:14px;margin-bottom:30px;flex-wrap:wrap}
.sec-title{font-size:22px;font-weight:bold;letter-spacing:3px;text-transform:uppercase;color:var(--yellow);text-shadow:3px 3px 0 var(--ink);display:flex;align-items:center;gap:12px}
.sec-title::before{content:"";display:inline-block;width:14px;height:24px;background:var(--red);box-shadow:0 0 0 3px var(--ink),4px 4px 0 var(--ink)}
.sec-sub{font-size:11px;color:var(--text-dim);letter-spacing:2px}
.sec-line{flex:1;height:4px;background:repeating-linear-gradient(90deg,var(--green-d) 0 8px,transparent 8px 16px)}
.cats{display:flex;gap:10px;flex-wrap:wrap;padding:26px}
.cat-btn{padding:10px 18px;font-size:13px;font-weight:bold;letter-spacing:2px;background:var(--bg-3);color:var(--text-dim);border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink),inset -3px -3px 0 rgba(0,0,0,.4);cursor:pointer;transition:.06s;display:flex;align-items:center;gap:8px}
.cat-btn:hover{color:var(--yellow);transform:translate(-1px,-1px);box-shadow:4px 4px 0 0 var(--ink)}
.cat-btn:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.cat-btn.active{background:var(--purple);color:#fff;box-shadow:3px 3px 0 0 var(--ink),inset -3px -3px 0 rgba(0,0,0,.3),inset 3px 3px 0 rgba(255,255,255,.15)}
.cat-btn .ci{font-size:16px;line-height:1}
.hot-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:26px}
.scoreboard{background:var(--bg-2);border:4px solid var(--ink);box-shadow:6px 6px 0 0 var(--shadow),inset -4px -4px 0 rgba(0,0,0,.4),inset 4px 4px 0 rgba(255,255,255,.05)}
.score-head{display:grid;grid-template-columns:60px 1fr 130px 110px 80px;gap:10px;padding:12px 16px;background:var(--bg-4);border-bottom:4px solid var(--ink);font-size:11px;letter-spacing:2px;color:var(--green);font-weight:bold}
.score-row{display:grid;grid-template-columns:60px 1fr 130px 110px 80px;gap:10px;padding:13px 16px;border-bottom:3px solid var(--bg-4);align-items:center;cursor:pointer;transition:.08s}
.score-row:hover{background:var(--bg-3);padding-left:20px}
.score-row:last-child{border-bottom:none}
.score-rank{font-size:22px;font-weight:bold;letter-spacing:1px}
.rank-1{color:var(--yellow);text-shadow:2px 2px 0 var(--ink)}
.rank-2{color:#c0c0d0;text-shadow:2px 2px 0 var(--ink)}
.rank-3{color:var(--red);text-shadow:2px 2px 0 var(--ink)}
.rank-oth{color:var(--text-dimer)}
.score-name{font-size:14px;font-weight:bold;color:var(--text);letter-spacing:1px;display:flex;align-items:center;gap:8px}
.score-name .tag-hot{font-size:9px;background:var(--red);color:#fff;padding:2px 5px;letter-spacing:1px}
.score-rate{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--yellow)}
.score-plays{font-size:12px;color:var(--cyan);letter-spacing:1px}
.score-go{font-size:11px;color:var(--green);letter-spacing:1px;text-align:right}
.score-row:hover .score-go{color:var(--yellow)}
.hot-side{display:flex;flex-direction:column;gap:18px}
.featured-card{background:var(--bg-2);border:4px solid var(--ink);box-shadow:6px 6px 0 0 var(--shadow);overflow:hidden}
.featured-cover{height:170px;background:var(--bg-3);position:relative;overflow:hidden}
.featured-cover img{width:100%;height:100%;object-fit:cover}
.featured-badge{position:absolute;top:10px;left:10px;background:var(--red);color:#fff;font-size:10px;padding:4px 8px;letter-spacing:1px;border:3px solid var(--ink);box-shadow:2px 2px 0 var(--ink);animation:blink-soft 1.4s steps(1) infinite}
.featured-body{padding:16px}
.featured-body h3{font-size:16px;letter-spacing:1px;color:var(--yellow);margin-bottom:8px;text-shadow:2px 2px 0 var(--ink)}
.featured-body p{font-size:12px;color:var(--text-dim);line-height:1.6;letter-spacing:.5px;margin-bottom:12px}
.featured-meta{display:flex;gap:10px;flex-wrap:wrap;font-size:11px;color:var(--green)}
.new-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
.new-card{background:var(--bg-2);border:4px solid var(--ink);box-shadow:5px 5px 0 0 var(--shadow);cursor:pointer;transition:.08s;position:relative;overflow:hidden}
.new-card:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 0 var(--shadow)}
.new-cover{height:150px;background:var(--bg-3);position:relative;overflow:hidden;border-bottom:4px solid var(--ink)}
.new-cover img{width:100%;height:100%;object-fit:cover}
.new-cover .cover-px{position:absolute;inset:0}
.new-badge{position:absolute;top:-2px;right:-2px;background:var(--yellow);color:var(--ink);font-size:11px;font-weight:bold;padding:6px 10px;letter-spacing:1px;border:4px solid var(--ink);box-shadow:0 4px 0 var(--ink);animation:flash-bg 1.5s steps(1) infinite}
@keyframes flash-bg{0%,100%{background:var(--yellow);color:var(--ink)}50%{background:var(--red);color:#fff}}
.new-body{padding:14px}
.new-body h4{font-size:14px;color:var(--green);letter-spacing:1px;margin-bottom:6px;text-shadow:2px 2px 0 var(--ink)}
.new-body .dev{font-size:11px;color:var(--text-dimer);margin-bottom:8px}
.new-tags{display:flex;gap:6px;flex-wrap:wrap}
.new-tags span{font-size:10px;padding:3px 6px;background:var(--bg-4);border:2px solid var(--ink);color:var(--cyan);letter-spacing:.5px}
.lib-bar{display:flex;gap:14px;flex-wrap:wrap;align-items:center;margin-bottom:26px;padding:18px;background:var(--bg-2);border:4px solid var(--ink);box-shadow:5px 5px 0 0 var(--shadow)}
.lib-search{display:flex;flex:1;min-width:240px}
.lib-search input{flex:1;height:44px;background:var(--bg-4);border:4px solid var(--ink);border-right:none;padding:0 14px;color:var(--text);font-size:13px;box-shadow:inset 3px 3px 0 rgba(0,0,0,.5)}
.lib-search input:focus{outline:none;background:var(--bg-3)}
.lib-search button{height:44px;width:50px;background:var(--purple);color:#fff;border:4px solid var(--ink);box-shadow:3px 3px 0 var(--ink);font-size:16px}
.lib-search button:active{transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.lib-select{position:relative}
.lib-select select{height:44px;background:var(--bg-4);border:4px solid var(--ink);color:var(--text);padding:0 34px 0 14px;font-size:12px;letter-spacing:1px;font-weight:bold;box-shadow:3px 3px 0 var(--ink);appearance:none;cursor:pointer}
.lib-select::after{content:"\25BC";position:absolute;right:12px;top:50%;transform:translateY(-50%);color:var(--green);font-size:10px;pointer-events:none}
.lib-select select:focus{outline:none;background:var(--bg-3)}
.lib-count{font-size:12px;color:var(--text-dim);letter-spacing:1px}
.lib-count b{color:var(--yellow)}
.game-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px}
.game-card{background:var(--bg-2);border:4px solid var(--ink);box-shadow:5px 5px 0 0 var(--shadow);cursor:pointer;transition:.08s;display:flex;flex-direction:column}
.game-card:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 0 var(--shadow);border-color:var(--green)}
.gcard-cover{height:170px;background:var(--bg-3);position:relative;overflow:hidden;border-bottom:4px solid var(--ink)}
.gcard-cover img{width:100%;height:100%;object-fit:cover}
.gcard-cover .ph{position:absolute;inset:0;display:grid;place-items:center;font-size:11px;color:var(--text-dimer);letter-spacing:1px}
.gcard-badges{position:absolute;top:8px;left:8px;display:flex;gap:6px;flex-direction:column}
.gbadge{font-size:9px;padding:3px 6px;letter-spacing:1px;border:2px solid var(--ink);font-weight:bold}
.gbadge-hot{background:var(--red);color:#fff}
.gbadge-new{background:var(--yellow);color:var(--ink)}
.gbadge-cat{background:var(--purple);color:#fff}
.gcard-rating{position:absolute;bottom:8px;right:8px;background:var(--ink);color:var(--yellow);font-size:11px;padding:3px 7px;border:2px solid var(--yellow);letter-spacing:1px;font-weight:bold}
.gcard-body{padding:13px;flex:1;display:flex;flex-direction:column}
.gcard-name{font-size:14px;color:var(--green);letter-spacing:1px;margin-bottom:6px;font-weight:bold;text-shadow:1px 1px 0 var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gcard-dev{font-size:11px;color:var(--text-dimer);margin-bottom:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gcard-tags{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:10px}
.gcard-tags span{font-size:9px;padding:2px 5px;background:var(--bg-4);border:2px solid var(--ink);color:var(--cyan);letter-spacing:.5px}
.gcard-foot{display:flex;justify-content:space-between;align-items:center;margin-top:auto;padding-top:8px;border-top:3px solid var(--bg-4);font-size:11px}
.gcard-plays{color:var(--cyan)}
.gcard-size{color:var(--text-dimer)}
.empty-state{text-align:center;padding:60px 20px;color:var(--text-dim)}
.empty-state .es-icon{font-size:40px;margin-bottom:14px;color:var(--purple)}
.empty-state p{font-size:13px;letter-spacing:1px}
.loading-px{display:flex;justify-content:center;align-items:center;padding:50px;gap:10px}
.ld{width:14px;height:14px;background:var(--green);box-shadow:0 0 0 3px var(--ink);animation:ld 1s steps(1) infinite}
.ld:nth-child(2){animation-delay:.2s;background:var(--yellow)}
.ld:nth-child(3){animation-delay:.4s;background:var(--red)}
.ld:nth-child(4){animation-delay:.6s;background:var(--purple)}
@keyframes ld{0%,100%{opacity:.2;transform:translateY(0)}50%{opacity:1;transform:translateY(-8px)}}
.pagination{display:flex;justify-content:center;gap:8px;margin-top:34px;flex-wrap:wrap}
.page-btn{min-width:42px;height:42px;padding:0 10px;background:var(--bg-2);color:var(--text);font-weight:bold;font-size:13px;letter-spacing:1px;border:4px solid var(--ink);box-shadow:3px 3px 0 0 var(--ink);cursor:pointer;display:grid;place-items:center;transition:.06s}
.page-btn:hover:not(:disabled){background:var(--bg-3);transform:translate(-1px,-1px);box-shadow:4px 4px 0 0 var(--ink)}
.page-btn:active:not(:disabled){transform:translate(3px,3px);box-shadow:0 0 0 0 var(--ink)}
.page-btn.active{background:var(--green);color:var(--ink)}
.page-btn:disabled{opacity:.3;cursor:not-allowed;box-shadow:3px 3px 0 0 rgba(0,0,0,.3)}
.page-dots{color:var(--text-dimer);align-self:center;padding:0 4px}
.modal-mask{position:fixed;inset:0;z-index:5000;display:none;background:rgba(0,0,0,.82);align-items:center;justify-content:center;padding:20px}
.modal-mask.open{display:flex;animation:slidein .15s ease}
.modal{width:min(880px,100%);max-height:90vh;overflow-y:auto;background:var(--bg-2);border:6px solid var(--ink);box-shadow:10px 10px 0 0 var(--shadow),0 0 0 6px var(--green),0 0 0 10px var(--ink);position:relative}
.modal-titlebar{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;background:var(--green);color:var(--ink);border-bottom:6px solid var(--ink);font-weight:bold;letter-spacing:2px;font-size:14px}
.modal-titlebar .dots{display:flex;gap:6px}
.modal-titlebar .dots i{display:block;width:12px;height:12px;border:2px solid var(--ink)}
.modal-titlebar .dots i:nth-child(1){background:var(--red)}
.modal-titlebar .dots i:nth-child(2){background:var(--yellow)}
.modal-titlebar .dots i:nth-child(3){background:var(--green-l)}
.modal-close{width:32px;height:32px;background:var(--red);color:#fff;font-weight:bold;font-size:16px;border:3px solid var(--ink);box-shadow:2px 2px 0 var(--ink);display:grid;place-items:center}
.modal-close:active{transform:translate(2px,2px);box-shadow:0 0 0 0 var(--ink)}
.modal-body{padding:24px}
.modal-top{display:grid;grid-template-columns:280px 1fr;gap:24px;margin-bottom:22px}
.modal-cover{height:300px;background:var(--bg-3);border:4px solid var(--ink);overflow:hidden;position:relative;box-shadow:4px 4px 0 var(--ink)}
.modal-cover img{width:100%;height:100%;object-fit:cover}
.modal-info h2{font-size:24px;color:var(--yellow);letter-spacing:2px;margin-bottom:8px;text-shadow:3px 3px 0 var(--ink)}
.modal-info .m-dev{font-size:12px;color:var(--text-dim);margin-bottom:14px}
.modal-rate{display:flex;align-items:center;gap:10px;margin-bottom:14px}
.modal-rate .rate-val{font-size:20px;color:var(--yellow);font-weight:bold}
.modal-info .m-tags{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:16px}
.modal-info .m-tags span{font-size:11px;padding:3px 8px;background:var(--bg-4);border:2px solid var(--ink);color:var(--cyan)}
.m-meta{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:18px}
.m-meta-item{background:var(--bg-4);border:3px solid var(--ink);padding:8px 10px}
.m-meta-item label{display:block;font-size:10px;color:var(--text-dimer);letter-spacing:1px;margin-bottom:3px}
.m-meta-item span{font-size:12px;color:var(--green);font-weight:bold;letter-spacing:.5px}
.modal-desc{font-size:13px;color:var(--text);line-height:1.8;letter-spacing:.5px;margin-bottom:18px;background:var(--bg-4);border:3px solid var(--ink);padding:14px}
.modal-desc .lbl{color:var(--yellow);font-weight:bold;letter-spacing:2px;display:block;margin-bottom:6px}
.modal-features{margin-bottom:18px}
.modal-features .lbl{color:var(--purple);font-weight:bold;letter-spacing:2px;font-size:12px;margin-bottom:8px}
.modal-features ul{display:flex;flex-direction:column;gap:6px}
.modal-features li{font-size:12px;color:var(--text);letter-spacing:.5px;padding-left:18px;position:relative}
.modal-features li::before{content:"\25B6";position:absolute;left:0;color:var(--green)}
.modal-shots{margin-bottom:20px}
.modal-shots .lbl{color:var(--cyan);font-weight:bold;letter-spacing:2px;font-size:12px;margin-bottom:8px}
.shots-row{display:flex;gap:10px;overflow-x:auto;padding-bottom:6px}
.shots-row img{width:200px;height:120px;object-fit:cover;border:3px solid var(--ink);flex-shrink:0;box-shadow:3px 3px 0 var(--ink)}
.modal-actions{display:flex;gap:12px;flex-wrap:wrap;padding-top:16px;border-top:4px solid var(--bg-4)}
.footer{background:var(--bg-4);border-top:4px solid var(--green);padding:50px 0 24px;margin-top:30px}
.foot-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:34px;margin-bottom:34px}
.foot-brand .logo-txt{font-size:18px}
.foot-brand p{font-size:12px;color:var(--text-dim);line-height:1.7;margin-top:14px;letter-spacing:.5px}
.foot-col h5{font-size:12px;color:var(--yellow);letter-spacing:2px;margin-bottom:14px;text-shadow:2px 2px 0 var(--ink)}
.foot-col a{display:block;font-size:12px;color:var(--text-dim);padding:5px 0;letter-spacing:.5px;transition:.08s}
.foot-col a:hover{color:var(--green-l);transform:translateX(3px)}
.foot-bottom{border-top:3px solid var(--bg-2);padding-top:20px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px}
.foot-bottom p{font-size:11px;color:var(--text-dimer);letter-spacing:1px}
.foot-press{font-size:11px;color:var(--green);letter-spacing:2px}
.to-top{position:fixed;right:22px;bottom:22px;z-index:400;width:54px;height:54px;background:var(--yellow);color:var(--ink);border:4px solid var(--ink);box-shadow:4px 4px 0 0 var(--ink);display:none;place-items:center;font-weight:bold;font-size:18px}
.to-top.show{display:grid}
.to-top:active{transform:translate(4px,4px);box-shadow:0 0 0 0 var(--ink)}
@media (max-width:1024px){.hero-inner{grid-template-columns:1fr}.hero-scene{min-height:200px;order:-1}.hot-grid{grid-template-columns:1fr}.new-grid,.game-grid{grid-template-columns:repeat(2,1fr)}.modal-top{grid-template-columns:1fr}.modal-cover{height:220px}.foot-grid{grid-template-columns:1fr 1fr}.nav{display:none}}
@media (max-width:640px){.header-inner{height:auto;padding:12px 0;flex-wrap:wrap}.search{order:3;width:100%;max-width:none;flex-basis:100%}.coin-counter{display:none}.new-grid,.game-grid{grid-template-columns:1fr}.score-head,.score-row{grid-template-columns:40px 1fr 70px;gap:6px}.score-head .h-rate,.score-head .h-go,.score-rate,.score-go{display:none}.score-plays{font-size:11px}.m-meta{grid-template-columns:1fr}.sec-title{font-size:18px}.lib-bar{flex-direction:column;align-items:stretch}.lib-select select{width:100%}.hero{padding:40px 0 60px}.foot-grid{grid-template-columns:1fr}.foot-bottom{flex-direction:column;text-align:center}}
</style>
</head>
<body>
<!-- CRT -->
<div class="crt-overlay"></div>
<div class="crt-vignette"></div>
<div class="crt-flicker"></div>
<!-- HEADER -->
<header class="header">
<div class="container header-inner">
<div class="logo" onclick="scrollTop2()">
<div class="logo-mark"></div>
<div class="logo-txt" data-i18n="logo_title">PIXEL GAMES<small data-i18n="logo_sub">PIXEL GAMES</small></div>
</div>
<nav class="nav" id="nav">
<a href="#" class="active" data-nav="home" data-i18n="nav_home"></a>
<a href="#library" data-nav="lib" data-i18n="nav_library"></a>
<a href="#hot" data-nav="rank" data-i18n="nav_hot"></a>
<a href="#new" data-nav="new" data-i18n="nav_new"></a>
</nav>
<form class="search" id="navSearch" onsubmit="return false">
<input type="text" id="navSearchInput" data-i18n-placeholder="search_placeholder" aria-label="">
<button type="submit" aria-label="Q">Q</button>
</form>
<div class="coin-counter" title="">
<span class="pix pix-coin"></span>
<span id="coinCount">9999</span>
</div>
<div class="lang-switcher" id="langSwitcher">
<button class="lang-btn" id="langBtn" onclick="toggleLangDropdown()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/></svg>
<span id="langLabel">中文</span>
</button>
<div class="lang-dropdown" id="langDropdown">
<div class="lang-option active" data-lang="zh-CN" onclick="switchLang('zh-CN')">
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
中文
</div>
<div class="lang-option" data-lang="zh-TW" onclick="switchLang('zh-TW')">
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
繁體中文
</div>
<div class="lang-option" data-lang="en" onclick="switchLang('en')">
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
English
</div>
</div>
</div>
</div>
</header>
<!-- HERO -->
<section class="hero" id="home">
<div class="hero-grid-bg"></div>
<div class="container hero-inner">
<div class="hero-text">
<div class="hero-sub" data-i18n="hero_sub"></div>
<h1 class="hero-title"><span class="y" data-i18n="hero_title_line1"></span> &middot; <span class="r" data-i18n="hero_title_line2"></span></h1>
<div class="press-start blink" data-i18n="hero_press_start"></div>
<div class="hero-tags">
<span class="hero-tag" data-i18n="hero_tag_1"></span>
<span class="hero-tag" data-i18n="hero_tag_2"></span>
<span class="hero-tag" data-i18n="hero_tag_3"></span>
<span class="hero-tag" data-i18n="hero_tag_4"></span>
</div>
<div class="hero-cta">
<button class="pbtn pbtn-yellow" onclick="scrollTo2('library')" data-i18n="hero_cta_play"></button>
<button class="pbtn pbtn-ghost" onclick="scrollTo2('hot')" data-i18n="hero_cta_rank"></button>
<span class="hero-coin"><span class="pix pix-coin"></span> <span data-i18n="hero_daily_coin"></span></span>
</div>
</div>
<div class="hero-scene" aria-hidden="true">
<div class="scene-cloud c1"></div>
<div class="scene-cloud c2"></div>
<div class="scene-brick"></div>
<div class="scene-char" id="heroChar"></div>
<div class="scene-pipe"></div>
<div class="scene-coin"></div>
<div class="scene-ground"></div>
</div>
</div>
</section>
<!-- TICKER -->
<div class="ticker">
<div class="ticker-track" id="tickerTrack">
<span class="r">&#9733; HOT</span><span data-i18n="ticker_1"></span>
<span class="g">&#9679; NEW</span><span data-i18n="ticker_2"></span>
<span class="r">&#9733; EVENT</span><span data-i18n="ticker_3"></span>
<span class="g">&#9679; TIP</span><span data-i18n="ticker_4"></span>
<span class="r">&#9733; UPDATE</span><span data-i18n="ticker_5"></span>
</div>
</div>
<!-- CATEGORIES -->
<section style="padding:0">
<div class="container">
<div class="pbox clip-corner" style="margin-top:30px">
<div class="cats" id="catsWrap">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</div>
</section>
<!-- HOT -->
<section id="hot">
<div class="container">
<div class="sec-head">
<h2 class="sec-title" data-i18n="section_hot"></h2>
<span class="sec-sub">HIGH SCORE TABLE</span>
<span class="sec-line"></span>
</div>
<div class="hot-grid">
<div class="scoreboard" id="scoreboard">
<div class="score-head">
<span>RANK</span><span data-i18n="score_name"></span><span class="h-rate" data-i18n="score_rating"></span><span data-i18n="score_plays"></span><span class="h-go"></span>
</div>
<div id="scoreRows">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
<div class="hot-side" id="hotSide">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</div>
</section>
<!-- NEW -->
<section id="new" style="background:var(--bg-4);border-top:4px solid var(--ink);border-bottom:4px solid var(--ink)">
<div class="container">
<div class="sec-head">
<h2 class="sec-title" data-i18n="section_new"></h2>
<span class="sec-sub">NEW RELEASES</span>
<span class="sec-line"></span>
</div>
<div class="new-grid" id="newGrid">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</section>
<!-- LIBRARY -->
<section id="library">
<div class="container">
<div class="sec-head">
<h2 class="sec-title" data-i18n="section_library"></h2>
<span class="sec-sub">GAME LIBRARY</span>
<span class="sec-line"></span>
</div>
<div class="lib-bar">
<div class="lib-search">
<input type="text" id="libSearch" data-i18n-placeholder="lib_search_placeholder" aria-label="">
<button onclick="onLibSearch()">Q</button>
</div>
<div class="lib-select">
<select id="libSort" onchange="onLibSort()">
<option value="" data-i18n="sort_default"></option>
<option value="rating" data-i18n="sort_rating"></option>
<option value="plays" data-i18n="sort_plays"></option>
<option value="newest" data-i18n="sort_newest"></option>
</select>
</div>
<div class="lib-select">
<select id="libCat" onchange="onLibCat()">
<option value="" data-i18n="cat_all"></option>
</select>
</div>
<div class="lib-count"><span data-i18n="lib_total_prefix"></span> <b id="libTotal">0</b> <span data-i18n="lib_total_suffix"></span></div>
</div>
<div class="game-grid" id="gameGrid">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
<div class="pagination" id="pagination"></div>
</div>
</section>
<!-- FOOTER -->
<footer class="footer">
<div class="container">
<div class="foot-grid">
<div class="foot-brand">
<div class="logo-txt">PIXEL GAMES</div>
<p data-i18n="footer_brand_desc"></p>
</div>
<div class="foot-col">
<h5 data-i18n="footer_game_center"></h5>
<a href="#library" data-i18n="footer_lib"></a>
<a href="#hot" data-i18n="footer_hot"></a>
<a href="#new" data-i18n="footer_new"></a>
<a href="#" onclick="return false" data-i18n="footer_browse_cat"></a>
</div>
<div class="foot-col">
<h5 data-i18n="footer_community"></h5>
<a href="#" onclick="return false" data-i18n="footer_forum"></a>
<a href="#" onclick="return false" data-i18n="footer_guide"></a>
<a href="#" onclick="return false" data-i18n="footer_rating"></a>
<a href="#" onclick="return false" data-i18n="footer_workshop"></a>
</div>
<div class="foot-col">
<h5 data-i18n="footer_about"></h5>
<a href="#" onclick="return false" data-i18n="footer_about_us"></a>
<a href="#" onclick="return false" data-i18n="footer_contact"></a>
<a href="#" onclick="return false" data-i18n="footer_privacy"></a>
<a href="#" onclick="return false" data-i18n="footer_terms"></a>
</div>
</div>
<div class="foot-bottom">
<p data-i18n="footer_copyright"></p>
<p class="foot-press blink" data-i18n="footer_press_start"></p>
</div>
</div>
</footer>
<!-- MODAL -->
<div class="modal-mask" id="modalMask" onclick="if(event.target===this)closeModal()">
<div class="modal" role="dialog" aria-modal="true">
<div class="modal-titlebar">
<span class="dots"><i></i><i></i><i></i></span>
<span id="modalTitle">LOADING...</span>
<button class="modal-close" onclick="closeModal()" aria-label="X">X</button>
</div>
<div class="modal-body" id="modalBody">
<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>
</div>
</div>
</div>
<!-- TO TOP -->
<button class="to-top" id="toTop" onclick="scrollTop2()" aria-label="Top">▲</button>
<script>
/* ============================================================
PIXEL GAMES — i18n System
============================================================ */
const I18N_LOCALES = {
'zh-CN': {
logo_title: 'PIXEL GAMES',
logo_sub: '像素游戏厅',
nav_home: '首页',
nav_library: '游戏库',
nav_hot: '排行榜',
nav_new: '新游',
search_placeholder: '搜索游戏...',
hero_sub: '\u25B8 INSERT COIN TO PLAY \u25C2',
hero_title_line1: '像素',
hero_title_line2: '世界',
hero_press_start: 'PRESS START',
hero_tag_1: '\u25B8 8-BIT 复古',
hero_tag_2: '\u25B8 海量游戏',
hero_tag_3: '\u25B8 极速下载',
hero_tag_4: '\u25B8 玩家社区',
hero_cta_play: '开始游戏 \u25B6',
hero_cta_rank: '查看排行',
hero_daily_coin: '每日登录 +100 币',
ticker_1: '《龙之传说》已登顶本周排行榜',
ticker_2: '《星际远征》正式上线,立即下载体验',
ticker_3: '周末双倍经验活动开启中',
ticker_4: '每日签到领取免费游戏币',
ticker_5: '《暗影刺客》v2.1.0 更新发布',
section_hot: '热门排行榜',
section_new: '新游速报',
section_library: '游戏仓库',
score_name: '游戏名称',
score_rating: '评分',
score_plays: '游玩次数',
lib_search_placeholder: '输入游戏名称或关键词...',
sort_default: '默认排序',
sort_rating: '评分最高',
sort_plays: '最多游玩',
sort_newest: '最新发布',
cat_all: '全部分类',
lib_total_prefix: '共',
lib_total_suffix: '款游戏',
empty_hot: '暂无热门游戏数据',
empty_new: '暂无新游数据',
empty_no_match: '未找到符合条件的游戏',
modal_category: '分类',
modal_version: '版本',
modal_size: '大小',
modal_platform: '平台',
modal_release_date: '发售日期',
modal_downloads: '下载量',
modal_desc_title: '\u25B8 游戏简介',
modal_desc_empty: '暂无简介',
modal_features_title: '\u2605 特色玩法',
modal_shots_title: '\u25A3 游戏截图',
modal_shots_empty: '暂无截图',
modal_download_btn: '立即下载 \u25B6',
modal_fav_btn: '加入收藏',
modal_close_btn: '关闭',
toast_downloading: '开始下载',
toast_fav_added: '已加入收藏',
footer_brand_desc: '复古像素风游戏平台 \u00B7 汇集海量经典与独立游戏 \u00B7 为玩家打造纯粹的 8-BIT 游戏体验。INSERT COIN, PRESS START, 享受游戏本来的乐趣。',
footer_game_center: '游戏中心',
footer_lib: '游戏仓库',
footer_hot: '热门排行',
footer_new: '新游速报',
footer_browse_cat: '分类浏览',
footer_community: '玩家社区',
footer_forum: '玩家论坛',
footer_guide: '攻略中心',
footer_rating: '评分评价',
footer_workshop: '创作工坊',
footer_about: '关于我们',
footer_about_us: '关于平台',
footer_contact: '联系我们',
footer_privacy: '隐私政策',
footer_terms: '服务条款',
footer_copyright: '\u00A9 2026 PIXEL GAMES \u00B7 像素游戏厅 \u00B7 保留所有权利',
footer_press_start: '\u25B2 PRESS START \u25B2',
cat_action: '动作',
cat_rpg: '角色扮演',
cat_shooter: '射击',
cat_adventure: '冒险',
cat_strategy: '策略',
cat_simulation: '模拟',
cat_sports: '体育',
cat_puzzle: '益智',
mock_desc_1: '这是一款充满复古魅力的像素风游戏,融合了经典玩法与现代设计。玩家将踏上史诗般的冒险旅程,探索广袤的世界,挑战强大的敌人,揭开隐藏的秘密。精美的像素画面与动感的8-bit音效带您重温黄金游戏时代。',
mock_desc_2: '在像素构筑的奇幻大陆上,你将扮演一位被命运选中的勇者,与忠诚的伙伴并肩作战,对抗邪恶势力。经典的回合制战斗、丰富的技能树与多样的装备系统,带你体验最纯粹的RPG乐趣。',
mock_desc_3: '激烈的像素射击对战,支持多人在线竞技。多种武器选择、多变的地图场景、快节奏的战斗体验,让你在复古画风中感受肾上腺素飙升的快感。',
mock_desc_4: '建造、探索、生存——在无限大的像素沙盒世界中,你的想象力是唯一的限制。收集资源、打造工具、建造庇护所,与好友一起开启无尽冒险。',
mock_feat_1: '无缝开放世界',
mock_feat_2: '实时战斗系统',
mock_feat_3: '动态天气系统',
mock_feat_4: '多结局剧情',
mock_feat_5: '合作多人模式',
mock_feat_6: '物理引擎破坏',
mock_feat_7: '技能树系统',
mock_feat_8: '昼夜循环',
mock_feat_9: '随机地下城',
mock_feat_10: '宠物养成'
},
'en': {
logo_title: 'PIXEL GAMES',
logo_sub: 'Pixel Arcade',
nav_home: 'Home',
nav_library: 'Library',
nav_hot: 'Rankings',
nav_new: 'New',
search_placeholder: 'Search games...',
hero_sub: '\u25B8 INSERT COIN TO PLAY \u25C2',
hero_title_line1: 'Pixel',
hero_title_line2: 'World',
hero_press_start: 'PRESS START',
hero_tag_1: '\u25B8 8-BIT Retro',
hero_tag_2: '\u25B8 Huge Library',
hero_tag_3: '\u25B8 Fast Download',
hero_tag_4: '\u25B8 Community',
hero_cta_play: 'Play Now \u25B6',
hero_cta_rank: 'View Rankings',
hero_daily_coin: 'Daily login +100 coins',
ticker_1: 'Dragon Legend tops this week\'s chart',
ticker_2: 'Star Expedition officially launched, download now',
ticker_3: 'Weekend double XP event now active',
ticker_4: 'Daily sign-in for free game coins',
ticker_5: 'Shadow Assassin v2.1.0 update released',
section_hot: 'Top Rankings',
section_new: 'New Releases',
section_library: 'Game Library',
score_name: 'Game Title',
score_rating: 'Rating',
score_plays: 'Plays',
lib_search_placeholder: 'Enter game name or keyword...',
sort_default: 'Default Sort',
sort_rating: 'Top Rated',
sort_plays: 'Most Played',
sort_newest: 'Newest',
cat_all: 'All Categories',
lib_total_prefix: 'Total',
lib_total_suffix: 'games',
empty_hot: 'No ranking data yet',
empty_new: 'No new games yet',
empty_no_match: 'No matching games found',
modal_category: 'Category',
modal_version: 'Version',
modal_size: 'Size',
modal_platform: 'Platform',
modal_release_date: 'Release Date',
modal_downloads: 'Downloads',
modal_desc_title: '\u25B8 Description',
modal_desc_empty: 'No description available',
modal_features_title: '\u2605 Features',
modal_shots_title: '\u25A3 Screenshots',
modal_shots_empty: 'No screenshots',
modal_download_btn: 'Download \u25B6',
modal_fav_btn: 'Add to Favorites',
modal_close_btn: 'Close',
toast_downloading: 'Downloading',
toast_fav_added: 'Added to favorites',
footer_brand_desc: 'A retro pixel-style game platform featuring a massive collection of classic and indie games. Built for pure 8-BIT gaming experience. INSERT COIN, PRESS START, enjoy gaming at its finest.',
footer_game_center: 'Game Center',
footer_lib: 'Game Library',
footer_hot: 'Top Charts',
footer_new: 'New Releases',
footer_browse_cat: 'Browse Categories',
footer_community: 'Community',
footer_forum: 'Forum',
footer_guide: 'Guides',
footer_rating: 'Reviews',
footer_workshop: 'Workshop',
footer_about: 'About',
footer_about_us: 'About Us',
footer_contact: 'Contact',
footer_privacy: 'Privacy Policy',
footer_terms: 'Terms of Service',
footer_copyright: '\u00A9 2026 PIXEL GAMES. All rights reserved.',
footer_press_start: '\u25B2 PRESS START \u25B2',
cat_action: 'Action',
cat_rpg: 'RPG',
cat_shooter: 'Shooter',
cat_adventure: 'Adventure',
cat_strategy: 'Strategy',
cat_simulation: 'Simulation',
cat_sports: 'Sports',
cat_puzzle: 'Puzzle',
mock_desc_1: 'A charming pixel-art game blending classic gameplay with modern design. Embark on an epic adventure, explore vast worlds, defeat powerful enemies, and uncover hidden secrets. Beautiful pixel graphics and dynamic 8-bit sound transport you to the golden age of gaming.',
mock_desc_2: 'On a pixel-crafted fantasy continent, play as a chosen hero fighting alongside loyal companions against evil forces. Classic turn-based combat, rich skill trees, and diverse equipment systems deliver the purest RPG experience.',
mock_desc_3: 'Intense pixel shooting combat with multiplayer online battles. Multiple weapon choices, dynamic maps, and fast-paced action bring adrenaline-pumping thrills in retro style.',
mock_desc_4: 'Build, explore, survive — in an infinite pixel sandbox world where your imagination is the only limit. Collect resources, craft tools, build shelters, and embark on endless adventures with friends.',
mock_feat_1: 'Seamless Open World',
mock_feat_2: 'Real-Time Combat',
mock_feat_3: 'Dynamic Weather',
mock_feat_4: 'Multiple Endings',
mock_feat_5: 'Co-op Multiplayer',
mock_feat_6: 'Physics Destruction',
mock_feat_7: 'Skill Tree System',
mock_feat_8: 'Day/Night Cycle',
mock_feat_9: 'Random Dungeons',
mock_feat_10: 'Pet Companion'
},
'zh-TW': {
logo_title: 'PIXEL GAMES',
logo_sub: '像素遊戲廳',
nav_home: '首頁',
nav_library: '遊戲庫',
nav_hot: '排行榜',
nav_new: '新遊',
search_placeholder: '搜尋遊戲...',
hero_sub: '\u25B8 INSERT COIN TO PLAY \u25C2',
hero_title_line1: '像素',
hero_title_line2: '世界',
hero_press_start: 'PRESS START',
hero_tag_1: '\u25B8 8-BIT 復古',
hero_tag_2: '\u25B8 海量遊戲',
hero_tag_3: '\u25B8 極速下載',
hero_tag_4: '\u25B8 玩家社群',
hero_cta_play: '開始遊戲 \u25B6',
hero_cta_rank: '查看排行',
hero_daily_coin: '每日登入 +100 幣',
ticker_1: '《龍之傳說》已登頂本週排行榜',
ticker_2: '《星際遠征》正式上線,立即下載體驗',
ticker_3: '週末雙倍經驗活動開啟中',
ticker_4: '每日簽到領取免費遊戲幣',
ticker_5: '《暗影刺客》v2.1.0 更新發布',
section_hot: '熱門排行榜',
section_new: '新遊速報',
section_library: '遊戲倉庫',
score_name: '遊戲名稱',
score_rating: '評分',
score_plays: '遊玩次數',
lib_search_placeholder: '輸入遊戲名稱或關鍵詞...',
sort_default: '預設排序',
sort_rating: '評分最高',
sort_plays: '最多遊玩',
sort_newest: '最新發布',
cat_all: '全部分類',
lib_total_prefix: '共',
lib_total_suffix: '款遊戲',
empty_hot: '暫無熱門遊戲資料',
empty_new: '暫無新遊資料',
empty_no_match: '未找到符合條件的遊戲',
modal_category: '分類',
modal_version: '版本',
modal_size: '大小',
modal_platform: '平台',
modal_release_date: '發售日期',
modal_downloads: '下載量',
modal_desc_title: '\u25B8 遊戲簡介',
modal_desc_empty: '暫無簡介',
modal_features_title: '\u2605 特色玩法',
modal_shots_title: '\u25A3 遊戲截圖',
modal_shots_empty: '暫無截圖',
modal_download_btn: '立即下載 \u25B6',
modal_fav_btn: '加入收藏',
modal_close_btn: '關閉',
toast_downloading: '開始下載',
toast_fav_added: '已加入收藏',
footer_brand_desc: '復古像素風遊戲平台 \u00B7 匯集海量經典與獨立遊戲 \u00B7 為玩家打造純粹的 8-BIT 遊戲體驗。INSERT COIN, PRESS START, 享受遊戲本來的樂趣。',
footer_game_center: '遊戲中心',
footer_lib: '遊戲倉庫',
footer_hot: '熱門排行',
footer_new: '新遊速報',
footer_browse_cat: '分類瀏覽',
footer_community: '玩家社群',
footer_forum: '玩家論壇',
footer_guide: '攻略中心',
footer_rating: '評分評價',
footer_workshop: '創作工坊',
footer_about: '關於我們',
footer_about_us: '關於平台',
footer_contact: '聯絡我們',
footer_privacy: '隱私政策',
footer_terms: '服務條款',
footer_copyright: '\u00A9 2026 PIXEL GAMES \u00B7 像素遊戲廳 \u00B7 保留所有權利',
footer_press_start: '\u25B2 PRESS START \u25B2',
cat_action: '動作',
cat_rpg: '角色扮演',
cat_shooter: '射擊',
cat_adventure: '冒險',
cat_strategy: '策略',
cat_simulation: '模擬',
cat_sports: '體育',
cat_puzzle: '益智',
mock_desc_1: '這是一款充滿復古魅力的像素風遊戲,融合了經典玩法與現代設計。玩家將踏上史詩般的冒險旅程,探索廣袤的世界,挑戰強大的敵人,揭開隱藏的秘密。精美的像素畫面與動感的8-bit音效帶您重溫黃金遊戲時代。',
mock_desc_2: '在像素構築的奇幻大陸上,你將扮演一位被命運選中的勇者,與忠誠的夥伴並肩作戰,對抗邪惡勢力。經典的回合制戰鬥、豐富的技能樹與多樣的裝備系統,帶你體驗最純粹的RPG樂趣。',
mock_desc_3: '激烈的像素射擊對戰,支援多人線上競技。多種武器選擇、多變的地圖場景、快節奏的戰鬥體驗,讓你在復古畫風中感受腎上腺素飆升的快感。',
mock_desc_4: '建造、探索、生存——在無限大的像素沙盒世界中,你的想像力是唯一的限制。收集資源、打造工具、建造庇護所,與好友一起開啟無盡冒險。',
mock_feat_1: '無縫開放世界',
mock_feat_2: '即時戰鬥系統',
mock_feat_3: '動態天氣系統',
mock_feat_4: '多結局劇情',
mock_feat_5: '合作多人模式',
mock_feat_6: '物理引擎破壞',
mock_feat_7: '技能樹系統',
mock_feat_8: '晝夜循環',
mock_feat_9: '隨機地下城',
mock_feat_10: '寵物養成'
}
};
const LANG_LABELS = {'zh-CN':'中文','en':'EN','zh-TW':'繁中'};
let currentLang = (function(){
try{
var u=new URLSearchParams(location.search).get('lang');
if(u&&I18N_LOCALES[u]) return u;
}catch(e){}
try{var s=localStorage.getItem('lang');if(s&&I18N_LOCALES[s]) return s;}catch(e){}
var b=navigator.language||navigator.userLanguage||'';
if(b.startsWith('zh-TW')||b.startsWith('zh-Hant')) return 'zh-TW';
if(b.startsWith('zh')) return 'zh-CN';
return 'en';
})();
function t(key){return (I18N_LOCALES[currentLang]&&I18N_LOCALES[currentLang][key])||(I18N_LOCALES['zh-CN'][key])||key;}
function applyI18n(){
document.querySelectorAll('[data-i18n]').forEach(function(el){
var key=el.getAttribute('data-i18n');
el.textContent=t(key);
});
document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){
var key=el.getAttribute('data-i18n-placeholder');
el.placeholder=t(key);
});
document.documentElement.lang=currentLang==='zh-TW'?'zh-TW':currentLang==='zh-CN'?'zh-CN':'en';
var lbl=document.getElementById('langLabel');
if(lbl) lbl.textContent=LANG_LABELS[currentLang]||currentLang;
document.querySelectorAll('.lang-option').forEach(function(opt){
opt.classList.toggle('active',opt.getAttribute('data-lang')===currentLang);
});
document.title='PIXEL GAMES \u00B7 '+t('logo_sub');
}
function switchLang(lang){
if(!I18N_LOCALES[lang]) return;
currentLang=lang;
try{localStorage.setItem('lang',lang);}catch(e){}
applyI18n();
if(typeof onLangChange==='function') onLangChange(lang);
document.getElementById('langDropdown').classList.remove('open');
}
window.switchLang = switchLang;
function toggleLangDropdown(){
document.getElementById('langDropdown').classList.toggle('open');
}
window.toggleLangDropdown = toggleLangDropdown;
document.addEventListener('click',function(e){
var sw=document.getElementById('langSwitcher');
if(sw&&!sw.contains(e.target)) document.getElementById('langDropdown').classList.remove('open');
});
let onLangChange = null;
/* ============================================================
PIXEL GAMES — 前端逻辑
============================================================ */
var API = '/api/v1/game';
/* ---------- 工具函数 ---------- */
function formatNum(n){
n = Number(n)||0;
if(n>=100000000) return (n/100000000).toFixed(1).replace(/\.0$/,'')+'\u4EBF';
if(n>=10000) return (n/10000).toFixed(1).replace(/\.0$/,'')+'\u4E07';
return String(n);
}
function parseJSON(str, fallback){
try{ return JSON.parse(str); }catch(e){ return fallback; }
}
function escapeHTML(s){
if(s==null) return '';
return String(s).replace(/[&<>"']/g, function(c){
return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];
});
}
function ratingBlocks(rating){
var r = Number(rating)||0;
var filled = Math.round(r*2);
var s = '';
for(var i=0;i<10;i++){ s += i<filled ? '\u2588' : '\u2591'; }
return '<span class="rb-fill">'+s.slice(0,filled)+'</span><span class="rb-empty">'+s.slice(filled)+'</span>';
}
function coverHTML(g, cls){
var c = g && g.cover ? g.cover : '';
if(c){
return '<img src="'+escapeHTML(c)+'" alt="'+escapeHTML(g.name||'')+'" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'grid\'"><div class="ph" style="display:none">'+escapeHTML((g.name||'PIXEL').slice(0,4))+'</div>';
}
return '<div class="ph">'+escapeHTML((g&&g.name||'PIXEL').slice(0,6))+'</div>';
}
/* ---------- API ---------- */
function apiGet(path){
return fetch(path, {headers:{'Accept':'application/json'}})
.then(function(r){ if(!r.ok) throw new Error('HTTP '+r.status); return r.json(); })
.then(function(res){
if(res && Object.prototype.hasOwnProperty.call(res,'code')){
if(res.code!==0) throw new Error(res.message||'API error');
return res.data;
}
return res;
});
}
/* ---------- 状态 ---------- */
var state = {
cats: [],
lib: { list:[], total:0, page:1, page_size:12, keyword:'', sort:'', category_id:'' },
homeLoaded:false
};
/* ============================================================
模拟数据
============================================================ */
var NAMES = {
'zh-CN': ['龙之传说','星际远征','暗影刺客','钢铁战线','幻境迷宫','魔法纪元','赛车狂飙','机甲风暴','荒野求生','深海探险','天空之城','末日堡垒','剑与魔法','像素英雄','霓虹狙击','炼金术士','勇者斗恶龙','银河漂流','地下王国','雷霆战机','忍者之道','王者棋盘','农场物语','太空殖民','蒸汽朋克','光明使者','暗夜猎手','极速漂移','神秘海域','传奇战场','永恒之歌','混沌之心','机械迷城','风暴英雄','烈焰之刃'],
'en': ['Dragon Legend','Star Expedition','Shadow Assassin','Steel Frontline','Fantasy Maze','Magic Era','Racing Fury','Mech Storm','Wilderness','Deep Sea','Sky Fortress','Doomsday Base','Swords & Magic','Pixel Hero','Neon Sniper','Alchemist','Dragon Quest','Galaxy Drift','Underground Kingdom','Thunder Fighter','Ninja Way','King\'s Board','Farm Story','Space Colony','Steam Punk','Light Bringer','Night Hunter','Speed Drift','Mysterious Sea','Legend Battlefield','Eternal Song','Heart of Chaos','Mech City','Storm Hero','Flame Blade'],
'zh-TW': ['龍之傳說','星際遠征','暗影刺客','鋼鐵戰線','幻境迷宮','魔法紀元','賽車狂飆','機甲風暴','荒野求生','深海探險','天空之城','末日堡壘','劍與魔法','像素英雄','霓虹狙擊','煉金術士','勇者鬥惡龍','銀河漂流','地下王國','雷霆戰機','忍者之道','王者棋盤','農場物語','太空殖民','蒸汽朋克','光明使者','暗夜獵手','極速漂移','神秘海域','傳奇戰場','永恆之歌','混沌之心','機械迷城','風暴英雄','烈焰之刃']
};
var CATS = {
'zh-CN': [['动作','⚔'],['角色扮演','★'],['射击','◉'],['冒险','▲'],['策略','♟'],['模拟','◆'],['体育','●'],['益智','♦']],
'en': [['Action','⚔'],['RPG','★'],['Shooter','◉'],['Adventure','▲'],['Strategy','♟'],['Simulation','◆'],['Sports','●'],['Puzzle','♦']],
'zh-TW': [['動作','⚔'],['角色扮演','★'],['射擊','◉'],['冒險','▲'],['策略','♟'],['模擬','◆'],['體育','●'],['益智','♦']]
};
var CAT_KEYS = ['cat_action','cat_rpg','cat_shooter','cat_adventure','cat_strategy','cat_simulation','cat_sports','cat_puzzle'];
var TAGS = {
'zh-CN': ['开放世界','巨龙','史诗','多人联机','硬核','剧情','生存','沙盒','潜行','竞技','合作','Roguelike','解谜','建造','魔法','科幻','末日','机甲','赛车','像素'],
'en': ['Open World','Dragons','Epic','Multiplayer','Hardcore','Story','Survival','Sandbox','Stealth','Competitive','Co-op','Roguelike','Puzzle','Building','Magic','Sci-Fi','Post-Apocalyptic','Mecha','Racing','Pixel'],
'zh-TW': ['開放世界','巨龍','史詩','多人連線','硬核','劇情','生存','沙盒','潛行','競技','合作','Roguelike','解謎','建造','魔法','科幻','末日','機甲','賽車','像素']
};
var FEATS_POOL = {
'zh-CN': ['无缝开放世界','实时战斗系统','动态天气系统','多结局剧情','合作多人模式','物理引擎破坏','技能树系统','宠物养成','昼夜循环','随机地下城'],
'en': ['Seamless Open World','Real-Time Combat','Dynamic Weather','Multiple Endings','Co-op Multiplayer','Physics Destruction','Skill Tree System','Pet Companion','Day/Night Cycle','Random Dungeons'],
'zh-TW': ['無縫開放世界','即時戰鬥系統','動態天氣系統','多結局劇情','合作多人模式','物理引擎破壞','技能樹系統','寵物養成','晝夜循環','隨機地下城']
};
var PUB = {
'zh-CN': ['像素工作室','银河游戏','雷霆互动','幻影科技','复古发行','独立工坊'],
'en': ['Pixel Studio','Galaxy Games','Thunder Interactive','Phantom Tech','Retro Publish','Indie Workshop'],
'zh-TW': ['像素工作室','銀河遊戲','雷霆互動','幻影科技','復古發行','獨立工坊']
};
function rand(a){ return a[Math.floor(Math.random()*a.length)]; }
function randInt(a,b){ return Math.floor(Math.random()*(b-a+1))+a; }
function mockGame(id){
var lang = currentLang;
var names = NAMES[lang] || NAMES['zh-CN'];
var cats = CATS[lang] || CATS['zh-CN'];
var tags = TAGS[lang] || TAGS['zh-CN'];
var feats = FEATS_POOL[lang] || FEATS_POOL['zh-CN'];
var pubs = PUB[lang] || PUB['zh-CN'];
var descs = [
t('mock_desc_1'), t('mock_desc_2'), t('mock_desc_3'), t('mock_desc_4')
];
var name = names[(id-1)%names.length] + (id>names.length?(' '+Math.floor((id-1)/names.length)):'');
var ci = (id-1)%cats.length;
var tagN = randInt(2,4), tagArr=[]; for(var i=0;i<tagN;i++){ var tg=rand(tags); if(tagArr.indexOf(tg)<0)tagArr.push(tg);}
var feN = randInt(2,3), featArr=[]; for(var j=0;j<feN;j++){ var f=rand(feats); if(featArr.indexOf(f)<0)featArr.push(f);}
return {
id:id, name:name, description: rand(descs),
category_id: ci+1, category_name: t(CAT_KEYS[ci]),
cover:'', developer:rand(pubs), publisher:rand(pubs),
rating: Math.round((3.5+Math.random()*1.5)*10)/10,
plays: randInt(1000,9800000), downloads: randInt(500,5000000),
size: randInt(20,120)+'.'+randInt(0,9)+' GB', version:'v'+randInt(1,3)+'.'+randInt(0,9)+'.'+randInt(0,9),
release_date:'2026-'+String(randInt(1,7)).padStart(2,'0')+'-'+String(randInt(1,28)).padStart(2,'0'),
platform:'PC / PS5 / Switch', tags:JSON.stringify(tagArr), screenshots:'[]', features:JSON.stringify(featArr),
is_hot: Math.random()>0.5, is_new: Math.random()>0.5, sort:id, status:1
};
}
function mockGames(n, hotOnly, newOnly){
var arr=[];
for(var i=0;i<n;i++){
var g = mockGame(randInt(1,35));
if(hotOnly) g.is_hot=true;
if(newOnly){ g.is_new=true; }
arr.push(g);
}
return arr;
}
/* ============================================================
分类
============================================================ */
function loadCategories(){
return apiGet(API+'/categories').then(function(list){
state.cats = (list||[]).slice().sort(function(a,b){return (a.sort||0)-(b.sort||0);});
renderCats(state.cats);
renderLibCatSelect(state.cats);
}).catch(function(){
state.cats = MOCK_CATS();
renderCats(state.cats);
renderLibCatSelect(state.cats);
});
}
function MOCK_CATS(){
var cats = CATS[currentLang] || CATS['zh-CN'];
return cats.map(function(c,i){
return {id:i+1,name:c[0],icon:c[1],sort:i+1};
});
}
function renderCats(cats){
var html = '<button class="cat-btn active" data-cid="" onclick="onCatClick(this,\'\')"><span class="ci">\u2605</span>'+escapeHTML(t('cat_all'))+'</button>';
cats.forEach(function(c){
html += '<button class="cat-btn" data-cid="'+c.id+'" onclick="onCatClick(this,'+c.id+')"><span class="ci">'+escapeHTML(c.icon||'\u25C6')+'</span>'+escapeHTML(c.name)+'</button>';
});
document.getElementById('catsWrap').innerHTML = html;
}
function renderLibCatSelect(cats){
var sel = document.getElementById('libCat');
sel.innerHTML = '<option value="">'+escapeHTML(t('cat_all'))+'</option>';
cats.forEach(function(c){
var o = document.createElement('option');
o.value = c.id; o.textContent = c.name;
sel.appendChild(o);
});
}
function onCatClick(btn, cid){
var btns = document.querySelectorAll('.cat-btn');
for(var i=0;i<btns.length;i++) btns[i].classList.remove('active');
btn.classList.add('active');
state.lib.category_id = cid;
var sel = document.getElementById('libCat');
sel.value = cid;
state.lib.page = 1;
loadLibrary();
scrollTo2('library');
}
/* ============================================================
首页数据
============================================================ */
function loadHome(){
return apiGet(API+'/home').then(function(d){
d = d||{};
renderCats((d.categories&&d.categories.length)?d.categories:state.cats);
renderHot(d.hot||[]);
renderNew(d.new||[]);
}).catch(function(){
Promise.all([
apiGet(API+'/hot?limit=8').catch(function(){return mockGames(8,true);}),
apiGet(API+'/new?limit=4').catch(function(){return mockGames(4,false,true);})
]).then(function(res){
renderHot(res[0]||[]);
renderNew(res[1]||[]);
});
});
}
/* ============================================================
热门排行榜
============================================================ */
function renderHot(list){
list = (list||[]).slice(0,8);
if(!list.length){
document.getElementById('scoreRows').innerHTML = emptyHTML(t('empty_hot'));
document.getElementById('hotSide').innerHTML = '';
return;
}
var rows = '';
list.forEach(function(g,i){
var rk = i+1;
var rkCls = rk===1?'rank-1':rk===2?'rank-2':rk===3?'rank-3':'rank-oth';
var rkStr = rk<10?'0'+rk:''+rk;
rows += '<div class="score-row" onclick="openModal('+g.id+')">'
+ '<span class="score-rank '+rkCls+'">'+rkStr+'</span>'
+ '<span class="score-name">'+(rk<=3?'<span class="pix pix-star"></span> ':'')+escapeHTML(g.name||'')+(g.is_hot?'<span class="tag-hot">HOT</span>':'')+'</span>'
+ '<span class="score-rate"><span class="rate-val-mini">'+(Number(g.rating)||0).toFixed(1)+'</span> '+ratingBlocks(g.rating)+'</span>'
+ '<span class="score-plays">'+formatNum(g.plays)+' \u6B21</span>'
+ '<span class="score-go">\u25B6 '+t('score_name')+'</span>'
+ '</div>';
});
document.getElementById('scoreRows').innerHTML = rows;
var top = list[0];
var side = '<div class="featured-card" onclick="openModal('+top.id+')">'
+ '<div class="featured-cover">'+coverHTML(top)+'</div>'
+ '<div class="featured-badge">NO.1</div>'
+ '<div class="featured-body">'
+ '<h3>'+escapeHTML(top.name||'')+'</h3>'
+ '<p>'+escapeHTML((top.description||'').slice(0,70))+'</p>'
+ '<div class="featured-meta"><span>\u2605 '+(Number(top.rating)||0).toFixed(1)+'</span><span>\u25B6 '+formatNum(top.plays)+'</span><span>\u25C6 '+(top.category_name||'')+'</span></div>'
+ '</div></div>';
side += '<div class="featured-card"><div class="featured-body"><div style="font-size:12px;color:var(--purple);letter-spacing:2px;margin-bottom:10px">TOP 3</div>';
list.slice(0,3).forEach(function(g,i){
side += '<div onclick="openModal('+g.id+')" style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:2px solid var(--bg-4);cursor:pointer">'
+ '<span style="font-size:12px;color:var(--green)"><b style="color:'+(i===0?'var(--yellow)':i===1?'#c0c0d0':'var(--red)')+'">0'+(i+1)+'</b> '+escapeHTML(g.name||'')+'</span>'
+ '<span style="font-size:11px;color:var(--yellow)">'+(Number(g.rating)||0).toFixed(1)+'\u2605</span></div>';
});
side += '</div></div>';
document.getElementById('hotSide').innerHTML = side;
}
/* ============================================================
新游速报
============================================================ */
function renderNew(list){
list = (list||[]).slice(0,4);
if(!list.length){
document.getElementById('newGrid').innerHTML = emptyHTML(t('empty_new'));
return;
}
var html = '';
list.forEach(function(g){
var tags = parseJSON(g.tags,[]);
var tagHTML = tags.slice(0,3).map(function(tg){return '<span>'+escapeHTML(tg)+'</span>';}).join('');
html += '<div class="new-card" onclick="openModal('+g.id+')">'
+ '<div class="new-cover">'+coverHTML(g)+'<div class="new-badge">NEW!</div></div>'
+ '<div class="new-body">'
+ '<h4>'+escapeHTML(g.name||'')+'</h4>'
+ '<div class="dev">'+escapeHTML(g.developer||'')+' \u00B7 '+(g.release_date||'')+'</div>'
+ '<div class="new-tags">'+tagHTML+'</div>'
+ '</div></div>';
});
document.getElementById('newGrid').innerHTML = html;
}
/* ============================================================
游戏仓库
============================================================ */
function loadLibrary(){
var q = state.lib;
var url = API+'/games?page='+q.page+'&page_size='+q.page_size;
if(q.category_id) url += '&category_id='+encodeURIComponent(q.category_id);
if(q.keyword) url += '&keyword='+encodeURIComponent(q.keyword);
if(q.sort) url += '&sort='+encodeURIComponent(q.sort);
showLoading('gameGrid');
apiGet(url).then(function(res){
res = res||{};
q.list = res.list||[];
q.total = res.total||0;
renderLibrary();
}).catch(function(){
var all = mockGames(36);
var f = all;
if(q.category_id) f = f.filter(function(g){return String(g.category_id)===String(q.category_id);});
if(q.keyword) f = f.filter(function(g){return (g.name||'').indexOf(q.keyword)>=0 || (g.description||'').indexOf(q.keyword)>=0;});
if(q.sort==='rating') f.sort(function(a,b){return (b.rating||0)-(a.rating||0);});
else if(q.sort==='plays') f.sort(function(a,b){return (b.plays||0)-(a.plays||0);});
else if(q.sort==='newest') f.sort(function(a,b){return (b.release_date||'').localeCompare(a.release_date||'');});
var start = (q.page-1)*q.page_size;
q.list = f.slice(start, start+q.page_size);
q.total = f.length;
renderLibrary();
});
}
function renderLibrary(){
document.getElementById('libTotal').textContent = state.lib.total;
var grid = document.getElementById('gameGrid');
if(!state.lib.list.length){
grid.innerHTML = emptyHTML(t('empty_no_match'));
document.getElementById('pagination').innerHTML = '';
return;
}
var html = '';
state.lib.list.forEach(function(g){
var tags = parseJSON(g.tags,[]);
var tagHTML = tags.slice(0,3).map(function(tg){return '<span>'+escapeHTML(tg)+'</span>';}).join('');
var badges = '';
if(g.is_hot) badges += '<span class="gbadge gbadge-hot">HOT</span>';
if(g.is_new) badges += '<span class="gbadge gbadge-new">NEW</span>';
if(g.category_name) badges += '<span class="gbadge gbadge-cat">'+escapeHTML(g.category_name)+'</span>';
html += '<div class="game-card" onclick="openModal('+g.id+')">'
+ '<div class="gcard-cover">'+coverHTML(g)+'<div class="gcard-badges">'+badges+'</div><div class="gcard-rating">'+(Number(g.rating)||0).toFixed(1)+'\u2605</div></div>'
+ '<div class="gcard-body">'
+ '<div class="gcard-name">'+escapeHTML(g.name||'')+'</div>'
+ '<div class="gcard-dev">'+escapeHTML(g.developer||'')+'</div>'
+ '<div class="gcard-tags">'+tagHTML+'</div>'
+ '<div class="gcard-foot"><span class="gcard-plays">\u25B6 '+formatNum(g.plays)+'</span><span class="gcard-size">'+escapeHTML(g.size||'')+'</span></div>'
+ '</div></div>';
});
grid.innerHTML = html;
renderPagination();
}
function renderPagination(){
var q = state.lib;
var pages = Math.ceil(q.total/q.page_size)||1;
var cur = q.page;
var el = document.getElementById('pagination');
if(pages<=1){ el.innerHTML=''; return; }
var html = '';
html += '<button class="page-btn" '+(cur<=1?'disabled':'')+' onclick="goPage('+(cur-1)+')">\u25C0</button>';
var from = Math.max(1, cur-2), to = Math.min(pages, cur+2);
if(from>1){ html += '<button class="page-btn" onclick="goPage(1)">1</button>'; if(from>2) html+='<span class="page-dots">\u00B7\u00B7\u00B7</span>'; }
for(var p=from;p<=to;p++){
html += '<button class="page-btn '+(p===cur?'active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
}
if(to<pages){ if(to<pages-1) html+='<span class="page-dots">\u00B7\u00B7\u00B7</span>'; html += '<button class="page-btn" onclick="goPage('+pages+')">'+pages+'</button>'; }
html += '<button class="page-btn" '+(cur>=pages?'disabled':'')+' onclick="goPage('+(cur+1)+')">\u25B6</button>';
el.innerHTML = html;
}
function goPage(p){
state.lib.page = p;
loadLibrary();
scrollTo2('library');
}
function onLibSearch(){ state.lib.keyword = document.getElementById('libSearch').value.trim(); state.lib.page=1; loadLibrary(); }
function onLibSort(){ state.lib.sort = document.getElementById('libSort').value; state.lib.page=1; loadLibrary(); }
function onLibCat(){ state.lib.category_id = document.getElementById('libCat').value; syncCatBtn(state.lib.category_id); state.lib.page=1; loadLibrary(); }
function syncCatBtn(cid){
var btns = document.querySelectorAll('.cat-btn');
for(var i=0;i<btns.length;i++){
if(btns[i].getAttribute('data-cid')===String(cid)) btns[i].classList.add('active');
else btns[i].classList.remove('active');
}
}
var navDebounce, libDebounce;
document.getElementById('libSearch').addEventListener('input', function(){
clearTimeout(libDebounce);
var v = this.value;
libDebounce = setTimeout(function(){ state.lib.keyword=v.trim(); state.lib.page=1; loadLibrary(); }, 360);
});
document.getElementById('navSearchInput').addEventListener('input', function(){
clearTimeout(navDebounce);
var v = this.value;
navDebounce = setTimeout(function(){
document.getElementById('libSearch').value = v;
state.lib.keyword = v.trim(); state.lib.page=1; loadLibrary();
if(v.trim()) scrollTo2('library');
}, 360);
});
/* ============================================================
Modal
============================================================ */
function openModal(id){
var mask = document.getElementById('modalMask');
mask.classList.add('open');
document.body.style.overflow='hidden';
document.getElementById('modalTitle').textContent = 'LOADING...';
document.getElementById('modalBody').innerHTML = '<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>';
apiGet(API+'/games/'+id).then(function(g){
renderModal(g);
}).catch(function(){
var g = mockGame(id);
renderModal(g);
});
}
function renderModal(g){
if(!g){ document.getElementById('modalBody').innerHTML = emptyHTML(t('empty_no_match')); return; }
document.getElementById('modalTitle').textContent = g.name||'';
var tags = parseJSON(g.tags,[]);
var features = parseJSON(g.features,[]);
var shots = parseJSON(g.screenshots,[]);
var tagHTML = tags.map(function(tg){return '<span>'+escapeHTML(tg)+'</span>';}).join('');
var featHTML = features.map(function(f){return '<li>'+escapeHTML(f)+'</li>';}).join('');
var shotHTML = shots.length ? shots.map(function(s){return '<img src="'+escapeHTML(s)+'" alt="screenshot" onerror="this.style.display=\'none\'">';}).join('') : '<div class="ph" style="width:200px;height:120px;display:grid;place-items:center;color:var(--text-dimer);font-size:11px;border:3px solid var(--ink)">'+escapeHTML(t('modal_shots_empty'))+'</div>';
var html = '<div class="modal-top">'
+ '<div class="modal-cover">'+coverHTML(g)+'</div>'
+ '<div class="modal-info">'
+ '<h2>'+escapeHTML(g.name||'')+'</h2>'
+ '<div class="m-dev">'+escapeHTML(g.developer||'')+(g.publisher?(' / '+escapeHTML(g.publisher)):'')+'</div>'
+ '<div class="modal-rate"><span class="rate-val">'+(Number(g.rating)||0).toFixed(1)+'</span>'+ratingBlocks(g.rating)+'<span style="font-size:11px;color:var(--text-dim)">/ 5.0</span></div>'
+ '<div class="m-tags">'+tagHTML+'</div>'
+ '<div class="m-meta">'
+ metaItem(t('modal_category'), g.category_name||'')
+ metaItem(t('modal_version'), g.version||'-')
+ metaItem(t('modal_size'), g.size||'-')
+ metaItem(t('modal_platform'), g.platform||'-')
+ metaItem(t('modal_release_date'), g.release_date||'-')
+ metaItem(t('modal_downloads'), formatNum(g.downloads||g.plays||0))
+ '</div>'
+ '</div></div>'
+ '<div class="modal-desc"><span class="lbl">'+escapeHTML(t('modal_desc_title'))+'</span>'+escapeHTML(g.description||t('modal_desc_empty'))+'</div>'
+ (features.length?'<div class="modal-features"><div class="lbl">'+escapeHTML(t('modal_features_title'))+'</div><ul>'+featHTML+'</ul></div>':'')
+ '<div class="modal-shots"><div class="lbl">'+escapeHTML(t('modal_shots_title'))+'</div><div class="shots-row">'+shotHTML+'</div></div>'
+ '<div class="modal-actions">'
+ '<button class="pbtn pbtn-yellow" onclick="alert(\''+escapeHTML(t('toast_downloading'))+':'+escapeHTML(g.name||'')+'\')">'+escapeHTML(t('modal_download_btn'))+'</button>'
+ '<button class="pbtn pbtn-ghost" onclick="alert(\''+escapeHTML(t('toast_fav_added'))+'\')">'+escapeHTML(t('modal_fav_btn'))+'</button>'
+ '<button class="pbtn pbtn-red" onclick="closeModal()">'+escapeHTML(t('modal_close_btn'))+'</button>'
+ '</div>';
document.getElementById('modalBody').innerHTML = html;
}
function metaItem(label, val){
return '<div class="m-meta-item"><label>'+escapeHTML(label)+'</label><span>'+escapeHTML(val)+'</span></div>';
}
function closeModal(){
document.getElementById('modalMask').classList.remove('open');
document.body.style.overflow='';
}
document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeModal(); });
/* ============================================================
辅助
============================================================ */
function showLoading(id){ document.getElementById(id).innerHTML = '<div class="loading-px"><span class="ld"></span><span class="ld"></span><span class="ld"></span><span class="ld"></span></div>'; }
function emptyHTML(msg){ return '<div class="empty-state" style="grid-column:1/-1"><div class="es-icon">\u25A3</div><p>'+escapeHTML(msg)+'</p></div>'; }
function scrollTo2(id){ var el=document.getElementById(id); if(el){ var y=el.getBoundingClientRect().top+window.pageYOffset-80; window.scrollTo({top:y,behavior:'smooth'}); } }
function scrollTop2(){ window.scrollTo({top:0,behavior:'smooth'}); }
window.addEventListener('scroll', function(){
var t = document.getElementById('toTop');
if(window.pageYOffset>400) t.classList.add('show'); else t.classList.remove('show');
var secs = ['home','hot','new','library'];
var cur = 'home';
for(var i=0;i<secs.length;i++){ var el=document.getElementById(secs[i]); if(el && el.getBoundingClientRect().top<160) cur=secs[i]; }
});
(function(){
var s = document.createElement('style');
s.textContent = '.rb-fill{color:var(--yellow);letter-spacing:0}.rb-empty{color:var(--bg-3);letter-spacing:0}.rate-val-mini{color:var(--yellow);font-weight:bold;margin-right:2px}';
document.head.appendChild(s);
})();
(function(){
var el = document.getElementById('heroChar');
if(!el) return;
var p = 'var(--ink)';
var g = 'var(--green)';
var r = 'var(--red)';
var s = 'var(--yellow)';
var br = 'var(--bg-4)';
var px = 8;
function row(y, str){
var shadows = [];
for(var x=0;x<str.length;x++){
var ch = str[x];
var c;
if(ch==='G') c=g; else if(ch==='R') c=r; else if(ch==='S') c=s; else if(ch==='B') c=br; else if(ch==='K') c=p; else continue;
shadows.push((x*px)+'px '+(y*px)+'px 0 0 '+c);
}
return shadows.join(',');
}
var map = [
'..GGGG..',
'.GGGGGG.',
'SSKSSKSS',
'SSSSSSSS',
'SSKKKKSS',
'RRRRRRRR',
'RRRRRRRR',
'RRRRRRRR',
'.R.RR.R.',
'.S.SS.S.',
'.B.BB.B.'
];
var all=[];
for(var i=0;i<map.length;i++){ all.push(row(i, map[i])); }
el.style.cssText = 'width:'+px+'px;height:'+px+'px;position:relative;background:transparent;box-shadow:'+all.join(',')+';animation:float-up 2.2s ease-in-out infinite';
})();
/* ============================================================
语言切换回调
============================================================ */
onLangChange = function(lang){
state.lib = { list:[], total:0, page:1, page_size:12, keyword:'', sort:'', category_id:'' };
document.getElementById('libSearch').value = '';
document.getElementById('navSearchInput').value = '';
loadCategories().then(function(){
loadHome();
loadLibrary();
});
};
/* ============================================================
初始化
============================================================ */
function init(){
applyI18n();
loadCategories().then(function(){
loadHome();
loadLibrary();
});
setInterval(function(){
var el = document.getElementById('coinCount');
var v = parseInt(el.textContent,10)||0;
if(Math.random()>0.7){ el.textContent = v+1; }
}, 2000);
}
init();
</script>
</body>
</html>