逐光女足:无名热爱
by GoldenOtter46645 lines27.7 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>逐光女足:无名热爱 · Demo</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
html,body { width:100%; height:100%; overflow:hidden; background:#1a1626; font-family:"PingFang SC","Microsoft YaHei",sans-serif; color:#fff; }
#game { position:relative; width:100%; height:100%; max-width:480px; margin:0 auto; overflow:hidden; display:flex; flex-direction:column; transition:background .6s; }
/* 三套色调主题 */
body.theme-daily #game { background:linear-gradient(180deg,#d8cfc4 0%,#c4b8a8 50%,#a89e90 100%); color:#3a3228; }
body.theme-heart #game { background:linear-gradient(180deg,#2b2340 0%,#3b2f55 55%,#241d36 100%); color:#fff; }
body.theme-awaken #game { background:linear-gradient(180deg,#f5e6c8 0%,#e8c88a 50%,#d4a85a 100%); color:#4a3820; }
/* 通用 UI */
.topbar { display:flex; justify-content:space-between; align-items:center; padding:10px 12px; font-size:12px; z-index:20; }
body.theme-daily .pill { background:rgba(255,255,255,.4); border:1px solid rgba(255,255,255,.5); }
body.theme-heart .pill { background:rgba(0,0,0,.35); border:1px solid rgba(255,255,255,.15); }
body.theme-awaken .pill { background:rgba(255,255,255,.5); border:1px solid rgba(255,255,255,.6); }
.pill { border-radius:20px; padding:5px 10px; display:flex; align-items:center; gap:6px; backdrop-filter:blur(4px); }
.btn { border:none; outline:none; cursor:pointer; font-family:inherit; font-weight:700; color:#fff; border-radius:24px; padding:12px 28px; font-size:14px; transition:transform .1s; }
.btn:active { transform:scale(.95); }
.btn-main { background:linear-gradient(180deg,#ffb35c,#ff7a3d); box-shadow:0 6px 18px rgba(255,122,61,.4); }
.btn-ghost { background:rgba(255,255,255,.18); color:inherit; }
.btn-gold { background:linear-gradient(180deg,#ffe9a8,#e8a83c); color:#4a3010; box-shadow:0 6px 18px rgba(232,168,60,.4); }
/* 主界面 */
#screenHome { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px; text-align:center; padding:20px; overflow-y:auto; }
#logo { font-size:26px; font-weight:800; letter-spacing:2px; line-height:1.4; }
body.theme-daily #logo { color:#8a6a3a; }
body.theme-heart #logo { background:linear-gradient(180deg,#ffe9a8,#ff9d5c); -webkit-background-clip:text; background-clip:text; color:transparent; }
body.theme-awaken #logo { color:#8a5a20; }
#slogan { font-size:12px; line-height:1.9; max-width:280px; opacity:.85; font-style:italic; }
/* 底部导航 */
#navbar { display:flex; justify-content:space-around; padding:8px 0 12px; background:inherit; z-index:20; border-top:1px solid rgba(255,255,255,.1); }
.navBtn { background:none; border:none; color:inherit; font-size:11px; display:flex; flex-direction:column; align-items:center; gap:2px; cursor:pointer; opacity:.7; }
.navBtn.active { opacity:1; font-weight:700; }
.navBtn .ico { font-size:20px; }
/* 章节地图 */
#screenMap { flex:1; display:none; flex-direction:column; padding:16px; overflow-y:auto; }
#screenMap h2 { font-size:16px; margin-bottom:12px; }
.chapter { background:rgba(255,255,255,.12); border-radius:14px; padding:14px; margin-bottom:10px; border:1px solid rgba(255,255,255,.15); }
.chapter.locked { opacity:.4; }
.chapter.done { border-color:rgba(126,231,224,.5); }
.chapter .chTitle { font-size:14px; font-weight:700; margin-bottom:4px; }
.chapter .chDesc { font-size:11px; opacity:.75; line-height:1.6; }
.chapter .chBtn { margin-top:8px; font-size:12px; padding:8px 18px; }
/* 角色页 */
#screenChars { flex:1; display:none; flex-direction:column; padding:16px; overflow-y:auto; }
#screenChars h2 { font-size:16px; margin-bottom:12px; }
.charCard { background:rgba(255,255,255,.12); border-radius:14px; padding:14px; margin-bottom:10px; display:flex; gap:12px; border:1px solid rgba(255,255,255,.15); }
.charAvatar { width:56px; height:56px; border-radius:14px; display:flex; align-items:center; justify-content:center; font-size:28px; flex-shrink:0; }
.charInfo { flex:1; }
.charName { font-size:14px; font-weight:700; }
.charRole { font-size:10px; opacity:.7; margin-bottom:4px; }
.charDesc { font-size:11px; line-height:1.6; opacity:.8; }
.charSkill { font-size:10px; margin-top:4px; opacity:.75; }
.breakBadge { font-size:10px; background:rgba(126,231,224,.25); padding:2px 8px; border-radius:10px; margin-left:6px; }
/* 羁绊页 */
#screenBond { flex:1; display:none; flex-direction:column; padding:16px; overflow-y:auto; }
.bondItem { background:rgba(255,255,255,.12); border-radius:14px; padding:14px; margin-bottom:10px; border:1px solid rgba(255,255,255,.15); }
.bondName { font-size:14px; font-weight:700; margin-bottom:4px; }
.bondDesc { font-size:11px; opacity:.8; line-height:1.6; }
/* 挂机训练场 */
#screenTrain { flex:1; display:none; flex-direction:column; align-items:center; justify-content:center; padding:20px; text-align:center; gap:14px; }
#trainTimer { font-size:32px; font-weight:800; }
#trainReward { font-size:13px; opacity:.8; line-height:1.8; }
/* 跑酷关卡 */
#screenRun { flex:1; display:none; position:relative; }
#runCanvas { width:100%; height:100%; display:block; }
#runHud { position:absolute; top:8px; left:12px; right:12px; display:flex; justify-content:space-between; font-size:12px; z-index:10; }
#moodWrap { background:rgba(0,0,0,.4); border-radius:20px; padding:5px 10px; display:flex; align-items:center; gap:6px; }
#moodBar2 { width:110px; height:8px; background:rgba(255,255,255,.2); border-radius:4px; overflow:hidden; }
#moodFill { height:100%; width:100%; background:linear-gradient(90deg,#7fe7e0,#4ecdc4); }
#skillBtn { position:absolute; right:16px; bottom:26px; width:60px; height:60px; border-radius:50%; border:2px solid rgba(255,255,255,.4); background:radial-gradient(circle at 35% 30%,#8fe9e2,#2fa8a0); font-size:22px; color:#fff; box-shadow:0 0 18px rgba(78,205,196,.6); z-index:10; }
#skillBtn:disabled { filter:grayscale(.8); opacity:.5; }
/* 弹窗通用 */
.overlay { position:absolute; inset:0; background:rgba(15,10,30,.85); display:none; align-items:center; justify-content:center; z-index:30; backdrop-filter:blur(6px); color:#fff; }
.panel { width:80%; background:linear-gradient(180deg,#3a2f5c,#241d3d); border:1px solid rgba(255,255,255,.15); border-radius:20px; padding:22px; text-align:center; }
.panel h2 { font-size:18px; margin-bottom:8px; }
.panel p { font-size:13px; line-height:1.9; margin-bottom:14px; opacity:.9; }
.stat { display:flex; justify-content:space-between; font-size:13px; padding:6px 4px; border-bottom:1px dashed rgba(255,255,255,.12); }
.panel .btn { margin-top:14px; }
/* 剧情对话 */
#dialogue { position:absolute; left:10px; right:10px; bottom:14px; background:rgba(20,14,40,.92); border:1px solid rgba(255,255,255,.15); border-radius:14px; padding:14px 16px; display:none; z-index:40; color:#fff; }
#dialogue .who { font-size:12px; color:#ffb35c; margin-bottom:6px; }
#dialogue .say { font-size:14px; line-height:1.8; }
#dialogue .next { font-size:11px; color:#9b8fc4; text-align:right; margin-top:8px; }
#toast { position:absolute; top:60px; left:50%; transform:translateX(-50%); background:rgba(0,0,0,.6); padding:8px 16px; border-radius:20px; font-size:12px; display:none; z-index:50; color:#fff; }
</style>
</head>
<body class="theme-heart">
<div id="game">
<div class="topbar">
<div class="pill">⚪ <b id="coinTxt">0</b></div>
<div class="pill">💎 心境 <b id="moodTxt">100</b></div>
<div class="pill">📅 第 <b id="dayTxt">1</b> 天</div>
</div>
<!-- 主界面 -->
<div id="screenHome">
<div id="logo">逐光女足<br>无名热爱</div>
<div id="slogan">「热爱从不需要冠军证明。<br>真正的输赢,永远在心里。」</div>
<button class="btn btn-main" onclick="goMap()">🗺 章节地图</button>
<button class="btn btn-ghost" onclick="goChars()">👥 队员</button>
</div>
<!-- 章节地图 -->
<div id="screenMap">
<h2>🗺 章节地图</h2>
<div id="chapterList"></div>
</div>
<!-- 角色页 -->
<div id="screenChars">
<h2>👥 娥眉女足 · 队员</h2>
<div id="charList"></div>
</div>
<!-- 羁绊页 -->
<div id="screenBond">
<h2>💞 羁绊</h2>
<div id="bondList"></div>
</div>
<!-- 挂机训练场 -->
<div id="screenTrain">
<h2>🏟 日常训练场</h2>
<div id="trainTimer">00:30</div>
<div id="trainReward">挂机收益进行中…<br>每 30 秒获得 10 金币 + 5 心境</div>
<button class="btn btn-main" onclick="claimTrain()">领取奖励</button>
</div>
<!-- 跑酷关卡 -->
<div id="screenRun">
<canvas id="runCanvas"></canvas>
<div id="runHud">
<div id="moodWrap">心境值
<div id="moodBar2"><div id="moodFill"></div></div>
</div>
<div style="background:rgba(0,0,0,.4);border-radius:20px;padding:5px 10px">距离 <b id="distTxt">0</b> m</div>
</div>
<button id="skillBtn" onclick="useSkill()">🌟</button>
</div>
<!-- 结算 -->
<div class="overlay" id="screenResult">
<div class="panel">
<h2 id="resultTitle"></h2>
<p id="resultDesc"></p>
<div class="stat"><span>奔跑距离</span><b id="rDist"></b></div>
<div class="stat"><span>收集心境光球</span><b id="rOrb"></b></div>
<div class="stat"><span>击退心魔</span><b id="rDefeated"></b></div>
<div class="stat"><span>获得金币</span><b id="rCoin"></b></div>
<button class="btn btn-main" onclick="closeResult()">收下奖励</button>
</div>
</div>
<!-- 结局 -->
<div class="overlay" id="screenEnding">
<div class="panel">
<h2 id="endingTitle"></h2>
<p id="endingDesc"></p>
<p style="font-size:12px;opacity:.6;font-style:italic" id="endingQuote"></p>
<button class="btn btn-gold" onclick="closeEnding()">回到主页</button>
</div>
</div>
<!-- 剧情 -->
<div id="dialogue">
<div class="who" id="dlgWho"></div>
<div class="say" id="dlgSay"></div>
<div class="next">▼ 点击继续</div>
</div>
<div id="toast"></div>
<!-- 底部导航 -->
<div id="navbar">
<button class="navBtn active" onclick="goHome()"><span class="ico">🏠</span>主页</button>
<button class="navBtn" onclick="goMap()"><span class="ico">🗺</span>章节</button>
<button class="navBtn" onclick="goChars()"><span class="ico">👥</span>队员</button>
<button class="navBtn" onclick="goBond()"><span class="ico">💞</span>羁绊</button>
<button class="navBtn" onclick="goTrain()"><span class="ico">🏟</span>训练</button>
</div>
</div>
<script>
/* ==================== 数据 ==================== */
const CHAPTERS = [
{ id:0, title:'序章 · 尘埃里的球队', desc:'无人看好的娥眉女足,双双咬牙带队。', unlock:()=>true,
story:[['双双','我们没钱、没人看,但只要还在场上,就不算输。'],['教练','你们是我见过最倔的一群女孩。']] },
{ id:1, title:'第一章 · 紧绷的锋芒', desc:'双双苛刻带队,与天才钰珑不合。', unlock:(s)=>s.ch>=0,
story:[['双双','不许失误!一个球都不能丢!'],['钰珑','踢球这么累,你到底在跟谁较劲?'],['双双','……我必须赢。']] },
{ id:2, title:'第二章 · 心魔幻域', desc:'每一个队员的内心关卡,焦虑化作敌人。', unlock:(s)=>s.ch>=1,
story:[['双双','我们输了……全是我的错。'],['雪野','队长,我先走了。'],['钰珑','你把所有人都推开了!']] },
{ id:3, title:'第三章 · 温柔的觉醒', desc:'师太登场,心法治愈全队。', unlock:(s)=>s.ch>=2,
story:[['师太','踢球不是为了奖杯,是为了脚下有风、心中有光。'],['双双','原来我不是爱足球,我是太想靠胜利救赎自己。']] },
{ id:4, title:'第四章 · 流水破局', desc:'全员和解,功夫足球全开。', unlock:(s)=>s.ch>=3,
story:[['钰珑','这次,我来承担。'],['双双','信你。']] },
{ id:5, title:'第五章 · 不必夺冠的巅峰', desc:'决赛,双结局。', unlock:(s)=>s.ch>=4,
story:[['双双','不管结果如何,我们都已经赢了。']] }
];
const CHARS = [
{ id:'shuangshuang', name:'双双', role:'队长 · 执念蜕变型', avatar:'🧢', color:'linear-gradient(135deg,#2c2c2c,#c0392b)',
desc:'前期冷硬紧绷,后期温柔坚定。配色从黑红走向暖红+金。',
skill:'坚守防线 · 团队护盾 · 绝境爆发', broken:false },
{ id:'yulong', name:'钰珑', role:'天才前锋 · 自由热烈型', avatar:'🌟', color:'linear-gradient(135deg,#ff9a3d,#ffd93d)',
desc:'高马尾碎发,笑容明媚。橙暖黄配色。',
skill:'高速突破 · 花式射门 · 灵动闪避', broken:false },
{ id:'xueye', name:'雪野', role:'治愈替补 · 灵魂导师', avatar:'❄️', color:'linear-gradient(135deg,#e8f4f8,#7fe7e0)',
desc:'温柔短发,纯白浅青。全队buff与心境治愈。',
skill:'全队buff · 心境治愈 · 团队辅助', broken:false },
{ id:'shita', name:'师太', role:'隐藏导师', avatar:'🀄', color:'linear-gradient(135deg,#d4a85a,#8a5a20)',
desc:'慵懒旗袍,麻将折扇。看似摆烂,实则通透。',
skill:'功夫心法加持 · 全队属性突破', broken:false },
{ id:'xufeng', name:'徐风', role:'亦正亦邪战术师', avatar:'🍃', color:'linear-gradient(135deg,#7f8fa6,#4a5568)',
desc:'清冷国风少年,理智毒舌,后期温柔护队。',
skill:'战术破解 · 敌方减益', broken:false }
];
const BONDS = [
{ name:'刚柔并济', chars:'双双 × 钰珑', desc:'紧绷与自由的和解,刚柔并济,全队核心。' },
{ name:'无名热爱', chars:'娥眉女足 全员', desc:'没有天才开挂,都是普通人靠热爱翻盘。' }
];
/* ==================== 存档 ==================== */
const SAVE_KEY = 'zuguang_v2';
let S = JSON.parse(localStorage.getItem(SAVE_KEY) || 'null') || {
coin: 50, moodMax: 100, day: 1,
ch: 0,
breaks: {},
breaksCount: 0,
trainTime: 0
};
function save(){ localStorage.setItem(SAVE_KEY, JSON.stringify(S)); }
function refreshHome(){
document.getElementById('coinTxt').textContent = S.coin;
document.getElementById('moodTxt').textContent = S.moodMax;
document.getElementById('dayTxt').textContent = S.day;
}
function toast(t){
const el = document.getElementById('toast');
el.textContent = t; el.style.display='block';
clearTimeout(el._t); el._t = setTimeout(()=>el.style.display='none',1800);
}
/* ==================== 页面切换 ==================== */
function show(id){
['screenHome','screenMap','screenChars','screenBond','screenTrain','screenRun'].forEach(s=>{
document.getElementById(s).style.display = s===id ? (s==='screenRun'?'block':'flex') : 'none';
});
}
function goHome(){ show('screenHome'); document.body.className='theme-heart'; refreshHome(); }
function goMap(){ show('screenMap'); renderChapters(); }
function goChars(){ show('screenChars'); renderChars(); }
function goBond(){ show('screenBond'); renderBonds(); }
function goTrain(){ show('screenTrain'); startTrain(); }
/* ==================== 章节 ==================== */
function renderChapters(){
const list = document.getElementById('chapterList');
list.innerHTML = '';
CHAPTERS.forEach((c,i)=>{
const unlocked = c.unlock(S);
const done = S.ch > i;
const div = document.createElement('div');
div.className = 'chapter' + (!unlocked ? ' locked':'') + (done?' done':'');
div.innerHTML = `
<div class="chTitle">${done?'✅ ':''}${c.title}</div>
<div class="chDesc">${c.desc}</div>
<button class="btn btn-main chBtn" ${unlocked?'':'disabled'} onclick="enterChapter(${i})">
${done?'重播': unlocked?'开始':'未解锁'}
</button>`;
list.appendChild(div);
});
}
function enterChapter(i){
const c = CHAPTERS[i];
startDialogue(c.story, ()=>{
if(i===2) document.body.className='theme-heart';
else if(i===3) document.body.className='theme-awaken';
else document.body.className='theme-daily';
startRun(i);
});
}
/* ==================== 剧情对话 ==================== */
let dlgQueue = [], dlgCb = null;
function startDialogue(queue, cb){
dlgQueue = [...queue]; dlgCb = cb;
showDlg();
}
function showDlg(){
if(dlgQueue.length===0){
document.getElementById('dialogue').style.display='none';
dlgCb && dlgCb(); dlgCb=null;
return;
}
const [who,say] = dlgQueue.shift();
document.getElementById('dlgWho').textContent = who;
document.getElementById('dlgSay').textContent = say;
document.getElementById('dialogue').style.display='block';
}
document.getElementById('dialogue').addEventListener('click', showDlg);
/* ==================== 角色 ==================== */
function renderChars(){
const list = document.getElementById('charList');
list.innerHTML = '';
CHARS.forEach(c=>{
const broken = S.breaks[c.id];
const div = document.createElement('div');
div.className='charCard';
div.innerHTML = `
<div class="charAvatar" style="background:${c.color}">${c.avatar}</div>
<div class="charInfo">
<div class="charName">${c.name}${broken?'<span class="breakBadge">已突破</span>':''}</div>
<div class="charRole">${c.role}</div>
<div class="charDesc">${c.desc}</div>
<div class="charSkill">🎯 ${c.skill}</div>
${broken?'':'<button class="btn btn-ghost" style="margin-top:8px;padding:6px 14px;font-size:11px" onclick="breakthrough(\''+c.id+'\')">心境突破(30金币)</button>'}
</div>`;
list.appendChild(div);
});
}
function breakthrough(id){
if(S.coin < 30){ toast('金币不足,去训练场挂机吧~'); return; }
S.coin -= 30;
S.breaks[id] = true;
S.breaksCount++;
S.moodMax += 10;
save(); refreshHome(); renderChars();
toast('✨ 心境突破!角色蜕变立绘已解锁');
}
/* ==================== 羁绊 ==================== */
function renderBonds(){
const list = document.getElementById('bondList');
list.innerHTML = '';
BONDS.forEach(b=>{
const div = document.createElement('div');
div.className='bondItem';
div.innerHTML = `<div class="bondName">💞 ${b.name}</div>
<div class="charRole">${b.chars}</div>
<div class="bondDesc">${b.desc}</div>`;
list.appendChild(div);
});
}
/* ==================== 挂机训练场 ==================== */
let trainInt = null, trainSec = 0;
function startTrain(){
if(trainInt) return;
trainSec = 0;
trainInt = setInterval(()=>{
trainSec++;
if(trainSec>=30){
S.coin += 10;
S.moodMax = Math.min(S.moodMax, 200);
save(); refreshHome();
trainSec = 0;
toast('+10 金币');
}
const m = String(Math.floor(trainSec/60)).padStart(2,'0');
const s = String(trainSec%60).padStart(2,'0');
document.getElementById('trainTimer').textContent = m+':'+s;
}, 1000);
}
function claimTrain(){
clearInterval(trainInt); trainInt=null;
toast('已领取挂机收益');
goHome();
}
/* ==================== 跑酷关卡 ==================== */
const canvas = document.getElementById('runCanvas');
const ctx = canvas.getContext('2d');
let W, H, groundY;
function resize(){
const r = canvas.getBoundingClientRect();
W = canvas.width = r.width * devicePixelRatio;
H = canvas.height = r.height * devicePixelRatio;
groundY = H * 0.78;
}
let running=false, raf=null;
let player, obstacles, orbs, bubbles, speed, dist, mood, skillCD, defeated, curChapter;
let bgTheme = 'heart';
function startRun(chIdx){
curChapter = chIdx;
document.getElementById('screenRun').style.display='block';
resize();
running = true;
mood = S.moodMax;
dist = 0; skillCD = 0; defeated = 0;
speed = 4 + S.breaksCount * 0.5;
bgTheme = chIdx===2 ? 'heart' : chIdx===3 ? 'awaken' : 'daily';
player = { x: W*0.18, y: groundY, vy:0, w:34*devicePixelRatio, h:52*devicePixelRatio, onGround:true };
obstacles = []; orbs = []; bubbles = [];
spawnTimer = 0;
canvas.onpointerdown = jump;
loop();
}
function jump(){
if(!running || !player.onGround) return;
player.vy = -11 * devicePixelRatio;
player.onGround = false;
}
function useSkill(){
if(!running || skillCD>0) return;
skillCD = 300;
obstacles.forEach(o=>{ if(o.type==='shadow') o.dead=true; });
defeated += obstacles.filter(o=>o.dead).length;
toast('🌟 心境爆发!');
}
let spawnTimer=0;
function loop(){
if(!running) return;
update(); draw();
raf = requestAnimationFrame(loop);
}
function update(){
player.vy += 0.55 * devicePixelRatio;
player.y += player.vy;
if(player.y >= groundY){ player.y=groundY; player.vy=0; player.onGround=true; }
dist += speed * 0.1;
skillCD = Math.max(0, skillCD-1);
document.getElementById('skillBtn').disabled = skillCD>0;
mood -= 0.06;
spawnTimer--;
if(spawnTimer<=0){
spawnTimer = Math.max(35, 80 - S.breaksCount*5);
const r = Math.random();
if(r<0.4) obstacles.push(mkObstacle('shadow'));
else if(r<0.65) obstacles.push(mkObstacle('fog'));
else orbs.push(mkOrb());
if(Math.random()<0.3) bubbles.push(mkBubble());
}
obstacles.forEach(o=> o.x -= speed*devicePixelRatio);
orbs.forEach(o=> o.x -= speed*devicePixelRatio);
bubbles.forEach(o=> o.x -= speed*devicePixelRatio*0.6);
obstacles.forEach(o=>{
if(o.dead) return;
if(hit(player,o)){ o.dead=true; mood -= o.type==='shadow'?18:10; }
});
orbs.forEach(o=>{
if(o.dead) return;
if(hit(player,o)){ o.dead=true; mood=Math.min(S.moodMax,mood+8); }
});
obstacles = obstacles.filter(o=>!o.dead && o.x>-60);
orbs = orbs.filter(o=>!o.dead && o.x>-60);
bubbles = bubbles.filter(o=>o.x>-100);
document.getElementById('moodFill').style.width = Math.max(0,mood/S.moodMax*100)+'%';
document.getElementById('distTxt').textContent = Math.floor(dist);
if(mood<=0) endRun(false);
if(dist>=300) endRun(true);
}
function mkObstacle(type){
const dpr=devicePixelRatio;
if(type==='shadow') return { type, x:W+50*dpr, y:groundY-46*dpr, w:40*dpr, h:46*dpr, dead:false, wob:Math.random()*6 };
return { type:'fog', x:W+50*dpr, y:groundY-14*dpr, w:46*dpr, h:18*dpr, dead:false };
}
function mkOrb(){
const dpr=devicePixelRatio;
return { x:W+30*dpr, y:groundY-(60+Math.random()*70)*dpr, r:9*dpr, dead:false };
}
function mkBubble(){
const words=['放弃','害怕','我不行','跑不动了'];
return { x:W+60, y:H*0.25+Math.random()*H*0.2, text:words[Math.floor(Math.random()*words.length)] };
}
function hit(a,b){
return Math.abs(a.x-b.x)<(a.w/2+(b.w||b.r)) && Math.abs(a.y-b.y)<(a.h/2+(b.h||b.r));
}
function draw(){
const dpr=devicePixelRatio;
let g;
if(bgTheme==='heart'){
g = ctx.createLinearGradient(0,0,0,H);
g.addColorStop(0,'#3a2f5c'); g.addColorStop(1,'#241d36');
} else if(bgTheme==='awaken'){
g = ctx.createLinearGradient(0,0,0,H);
g.addColorStop(0,'#f5e6c8'); g.addColorStop(1,'#d4a85a');
} else {
g = ctx.createLinearGradient(0,0,0,H);
g.addColorStop(0,'#c4b8a8'); g.addColorStop(1,'#a89e90');
}
ctx.fillStyle=g; ctx.fillRect(0,0,W,H);
ctx.fillStyle = bgTheme==='awaken' ? '#c49a5a' : bgTheme==='heart' ? '#32284e' : '#b8a890';
ctx.fillRect(0,groundY,W,H-groundY);
if(bgTheme==='heart'){
ctx.save(); ctx.globalAlpha=.35;
ctx.strokeStyle='rgba(200,220,255,0.5)'; ctx.lineWidth=1.5*dpr;
const off=(dist*2)%(W*0.6);
for(let i=0;i<5;i++){
const x=((i*W*0.25-off)%(W+200));
ctx.beginPath(); ctx.moveTo(x,H*0.2); ctx.lineTo(x+30*dpr,H*0.35); ctx.lineTo(x-10*dpr,H*0.5); ctx.stroke();
}
ctx.restore();
}
obstacles.forEach(o=>{
if(o.type==='shadow'){
ctx.save(); ctx.fillStyle='rgba(10,8,20,0.85)';
const bob=Math.sin(o.wob)*4*dpr;
ctx.beginPath(); ctx.ellipse(o.x,o.y+bob,o.w/2,o.h/2,0,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#ff5c7a';
ctx.beginPath(); ctx.arc(o.x-6*dpr,o.y-6*dpr+bob,2*dpr,0,Math.PI*2); ctx.arc(o.x+6*dpr,o.y-6*dpr+bob,2*dpr,0,Math.PI*2); ctx.fill();
ctx.restore();
} else {
ctx.save(); ctx.fillStyle='rgba(0,0,0,0.45)';
ctx.beginPath(); ctx.ellipse(o.x,groundY,o.w/2,o.h/2,0,0,Math.PI*2); ctx.fill();
ctx.restore();
}
});
orbs.forEach(o=>{
ctx.save();
const grd=ctx.createRadialGradient(o.x,o.y,0,o.x,o.y,o.r*2);
grd.addColorStop(0,'rgba(180,255,245,1)'); grd.addColorStop(1,'rgba(78,205,196,0)');
ctx.fillStyle=grd; ctx.beginPath(); ctx.arc(o.x,o.y,o.r*2,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#eafffb'; ctx.beginPath(); ctx.arc(o.x,o.y,o.r,0,Math.PI*2); ctx.fill();
ctx.restore();
});
bubbles.forEach(b=>{
ctx.save(); ctx.font=`${12*dpr} sans-serif`;
const tw=ctx.measureText(b.text).width;
ctx.fillStyle='rgba(180,150,220,0.25)'; ctx.strokeStyle='rgba(200,170,240,0.5)';
roundRect(ctx,b.x-tw/2-10*dpr,b.y-12*dpr,tw+20*dpr,24*dpr,10*dpr);
ctx.fill(); ctx.stroke();
ctx.fillStyle='#e8d8ff'; ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.fillText(b.text,b.x,b.y);
ctx.restore();
});
ctx.save();
const px=player.x, py=player.y-player.h/2;
ctx.fillStyle='#e74c3c';
roundRect(ctx,px-12*dpr,py,24*dpr,30*dpr,6*dpr); ctx.fill();
const leg=Math.sin(dist*0.3)*6*dpr;
ctx.strokeStyle='#ffe0b8'; ctx.lineWidth=5*dpr; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(px,py+30*dpr); ctx.lineTo(px-6*dpr+leg,py+50*dpr); ctx.stroke();
ctx.beginPath(); ctx.moveTo(px,py+30*dpr); ctx.lineTo(px+6*dpr-leg,py+50*dpr); ctx.stroke();
ctx.fillStyle='#ffd9b3';
ctx.beginPath(); ctx.arc(px,py-8*dpr,9*dpr,0,Math.PI*2); ctx.fill();
ctx.strokeStyle='#3a2a1a'; ctx.lineWidth=4*dpr;
ctx.beginPath(); ctx.moveTo(px-8*dpr,py-10*dpr); ctx.lineTo(px-16*dpr,py-2*dpr); ctx.stroke();
ctx.restore();
}
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();
}
function endRun(win){
running=false; cancelAnimationFrame(raf);
canvas.onpointerdown=null;
const coinGain = win ? 40 : Math.floor(dist/10);
S.coin += coinGain;
if(win && curChapter >= S.ch) S.ch = curChapter+1;
if(win) S.day += 1;
save();
document.getElementById('rDist').textContent = Math.floor(dist)+' m';
document.getElementById('rOrb').textContent = orbs.filter(o=>o.dead).length;
document.getElementById('rDefeated').textContent = defeated;
document.getElementById('rCoin').textContent = '+'+coinGain;
if(curChapter===5 && win){
const isTrue = S.breaksCount >= 3;
document.getElementById('endingTitle').textContent = isTrue ? '🏆 巅峰夺冠结局' : '🌟 全力以赴结局';
document.getElementById('endingDesc').textContent = isTrue
? '我们站上了最高领奖台。致敬每一个不被看好的无名之辈。'
: '我们拼尽了全力。输赢已不再重要,因为我们已经成为了更好的人。';
document.getElementById('endingQuote').textContent = isTrue
? '「我们是无名之辈,亦有万丈光芒。」'
: '「不是所有热爱都要奖杯收尾,坚持本身就是最好的加冕。」';
document.getElementById('screenResult').style.display='none';
document.getElementById('screenEnding').style.display='flex';
} else {
document.getElementById('resultTitle').textContent = win ? '🌟 你跑到了光里' : '💔 心境耗尽';
document.getElementById('resultDesc').textContent = win
? '教练说:看到没?这就是你坚持的样子。'
: '没关系,明天再跑一圈。热爱不怕输。';
document.getElementById('screenResult').style.display='flex';
}
}
function closeResult(){
document.getElementById('screenResult').style.display='none';
document.getElementById('screenRun').style.display='none';
goHome();
}
function closeEnding(){
document.getElementById('screenEnding').style.display='none';
document.getElementById('screenRun').style.display='none';
goHome();
}
refreshHome();
</script>
</body>
</html>Game Source: 逐光女足:无名热爱
Creator: GoldenOtter46
Libraries: none
Complexity: complex (645 lines, 27.7 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-goldenotter46" to link back to the original. Then publish at arcadelab.ai/publish.