基地静音开关:新增 b-mute 像素图标按钮、AU.applyMute 音乐闸门、mute/music 音频状态云端同步(server DEFAULT_STATE/vState 扩展,含 mute-btn.png 参考图)
This commit is contained in:
parent
d328c02366
commit
adc4fb36d3
BIN
mute-btn.png
Normal file
BIN
mute-btn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
@ -102,6 +102,12 @@ h1.logo{font-size:clamp(20px,5.4vw,44px);color:#fff;letter-spacing:4px;line-heig
|
|||||||
#tb-pause{position:absolute;top:6px;right:6px;z-index:46;font:inherit;font-size:11px;width:40px;height:40px;
|
#tb-pause{position:absolute;top:6px;right:6px;z-index:46;font:inherit;font-size:11px;width:40px;height:40px;
|
||||||
background:rgba(16,26,51,.7);border:3px solid var(--line);color:var(--ink);letter-spacing:1px}
|
background:rgba(16,26,51,.7);border:3px solid var(--line);color:var(--ink);letter-spacing:1px}
|
||||||
#tb-pause:active{background:var(--cyan);color:#000}
|
#tb-pause:active{background:var(--cyan);color:#000}
|
||||||
|
#b-mute{position:absolute;top:10px;right:10px;z-index:5;width:42px;height:42px;
|
||||||
|
display:flex;align-items:center;justify-content:center;
|
||||||
|
background:rgba(16,26,51,.85);border:3px solid var(--line);color:var(--ink);cursor:pointer;padding:0;line-height:1}
|
||||||
|
#b-mute canvas{image-rendering:pixelated;pointer-events:none}
|
||||||
|
#b-mute:hover{border-color:var(--cyan);color:var(--cyan)}
|
||||||
|
#b-mute:active{background:var(--cyan);color:#000}
|
||||||
.blink{animation:blk 1s steps(2) infinite}
|
.blink{animation:blk 1s steps(2) infinite}
|
||||||
@keyframes blk{50%{opacity:.25}}
|
@keyframes blk{50%{opacity:.25}}
|
||||||
.help-list{font-size:clamp(7px,1.5vw,10px);color:var(--ink);line-height:2.2;letter-spacing:2px;text-align:left}
|
.help-list{font-size:clamp(7px,1.5vw,10px);color:var(--ink);line-height:2.2;letter-spacing:2px;text-align:left}
|
||||||
|
|||||||
@ -63,6 +63,7 @@
|
|||||||
|
|
||||||
<!-- 标题 -->
|
<!-- 标题 -->
|
||||||
<div class="ovl hide" id="o-title">
|
<div class="ovl hide" id="o-title">
|
||||||
|
<button id="b-mute" title="静音开关(跟随账号云端保存)"><canvas id="b-mute-icon" width="36" height="26"></canvas></button>
|
||||||
<div class="tag">PIXEL ROGUELIKE SHMUP</div>
|
<div class="tag">PIXEL ROGUELIKE SHMUP</div>
|
||||||
<h1 class="logo">STAR<br>RAID</h1>
|
<h1 class="logo">STAR<br>RAID</h1>
|
||||||
<div class="logo-cn">星尘突击队</div>
|
<div class="logo-cn">星尘突击队</div>
|
||||||
|
|||||||
@ -4,9 +4,11 @@ const AU={ctx:null,master:null,mgain:null,simMute:false,
|
|||||||
init(){ if(this.ctx) return;
|
init(){ if(this.ctx) return;
|
||||||
try{ this.ctx=new (window.AudioContext||window.webkitAudioContext)();
|
try{ this.ctx=new (window.AudioContext||window.webkitAudioContext)();
|
||||||
this.master=this.ctx.createGain(); this.master.gain.value=0.5; this.master.connect(this.ctx.destination);
|
this.master=this.ctx.createGain(); this.master.gain.value=0.5; this.master.connect(this.ctx.destination);
|
||||||
this.mgain=this.ctx.createGain(); this.mgain.gain.value=SAVE.music?0.16:0; this.mgain.connect(this.master);
|
this.mgain=this.ctx.createGain(); this.mgain.gain.value=(SAVE.mute||!SAVE.music)?0:0.16; this.mgain.connect(this.master);
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
},
|
},
|
||||||
|
/* 音乐音量统一闸门:静音 或 音乐关 都归零(SAVE.mute 管 SFX 由 tone/noise 自查) */
|
||||||
|
applyMute(){ if(this.mgain) this.mgain.gain.value=(SAVE.mute||!SAVE.music)?0:0.16; },
|
||||||
now(){return this.ctx?this.ctx.currentTime:0;},
|
now(){return this.ctx?this.ctx.currentTime:0;},
|
||||||
env(g,t,a,d,v){g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(v,t+a);g.gain.exponentialRampToValueAtTime(0.001,t+a+d);},
|
env(g,t,a,d,v){g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(v,t+a);g.gain.exponentialRampToValueAtTime(0.001,t+a+d);},
|
||||||
tone(f,type,dur,vol=0.2,slide=0){ if(!this.ctx||SAVE.mute||this.simMute)return;
|
tone(f,type,dur,vol=0.2,slide=0){ if(!this.ctx||SAVE.mute||this.simMute)return;
|
||||||
|
|||||||
@ -53,7 +53,7 @@ addEventListener("keydown",e=>{
|
|||||||
if(GAMEKEYS.includes(e.code))e.preventDefault();
|
if(GAMEKEYS.includes(e.code))e.preventDefault();
|
||||||
KEYS[e.code]=true;
|
KEYS[e.code]=true;
|
||||||
AU.init(); if(AU.ctx&&AU.ctx.state==="suspended")AU.ctx.resume();
|
AU.init(); if(AU.ctx&&AU.ctx.state==="suspended")AU.ctx.resume();
|
||||||
if(e.code==="KeyM"){SAVE.music=!SAVE.music;if(AU.mgain)AU.mgain.gain.value=SAVE.music?0.16:0;persist();}
|
if(e.code==="KeyM"){SAVE.music=!SAVE.music;AU.applyMute();persist();}
|
||||||
if(e.code==="Escape"&&UI==="demoSim"){exitDemoSim();return;}
|
if(e.code==="Escape"&&UI==="demoSim"){exitDemoSim();return;}
|
||||||
if(e.code==="KeyP"||e.code==="Escape")togglePause();
|
if(e.code==="KeyP"||e.code==="Escape")togglePause();
|
||||||
if(UI==="cards"&&["Digit1","Digit2","Digit3"].includes(e.code))pickCard(+e.code.slice(-1)-1);
|
if(UI==="cards"&&["Digit1","Digit2","Digit3"].includes(e.code))pickCard(+e.code.slice(-1)-1);
|
||||||
|
|||||||
@ -51,8 +51,11 @@ const NET = {
|
|||||||
SAVE.codex = {};
|
SAVE.codex = {};
|
||||||
SAVE.coins = 0; SAVE.ships = ["falcon"]; SAVE.m = {};
|
SAVE.coins = 0; SAVE.ships = ["falcon"]; SAVE.m = {};
|
||||||
SAVE.best = { s0: 0, s1: 0, e0: 0, e1: 0, stage: 0 }; SAVE.rush = {};
|
SAVE.best = { s0: 0, s1: 0, e0: 0, e1: 0, stage: 0 }; SAVE.rush = {};
|
||||||
|
SAVE.mute = false; SAVE.music = true; // 音频开关也防串号,恢复默认
|
||||||
if (typeof SETUP !== "undefined") SETUP.ship = "falcon";
|
if (typeof SETUP !== "undefined") SETUP.ship = "falcon";
|
||||||
persist();
|
persist();
|
||||||
|
if (typeof updateMuteBtn === "function") updateMuteBtn();
|
||||||
|
if (typeof AU !== "undefined") AU.applyMute();
|
||||||
},
|
},
|
||||||
|
|
||||||
/* 应用服务端返回的用户数据:图鉴以服务端为准【替换】本地
|
/* 应用服务端返回的用户数据:图鉴以服务端为准【替换】本地
|
||||||
@ -72,10 +75,14 @@ const NET = {
|
|||||||
SAVE.m = Object.assign({}, d.state.m);
|
SAVE.m = Object.assign({}, d.state.m);
|
||||||
SAVE.best = Object.assign({ s0: 0, s1: 0, e0: 0, e1: 0, stage: 0 }, d.state.best);
|
SAVE.best = Object.assign({ s0: 0, s1: 0, e0: 0, e1: 0, stage: 0 }, d.state.best);
|
||||||
SAVE.rush = Object.assign({}, d.state.rush);
|
SAVE.rush = Object.assign({}, d.state.rush);
|
||||||
|
SAVE.mute = d.state.mute === true; // 音频开关跟随账号(缺省不静音)
|
||||||
|
SAVE.music = d.state.music !== false; // 缺省音乐开
|
||||||
if (d.state.ship && SAVE.ships.includes(d.state.ship) && typeof SETUP !== "undefined") SETUP.ship = d.state.ship;
|
if (d.state.ship && SAVE.ships.includes(d.state.ship) && typeof SETUP !== "undefined") SETUP.ship = d.state.ship;
|
||||||
}
|
}
|
||||||
persist();
|
persist();
|
||||||
this._mutePush = false;
|
this._mutePush = false;
|
||||||
|
if (typeof updateMuteBtn === "function") updateMuteBtn(); // 刷新基地静音按钮图标
|
||||||
|
if (typeof AU !== "undefined") AU.applyMute();
|
||||||
if (!d.state) this.pushState(); // 服务端无存档(老账号首登):把本地存档上传初始化
|
if (!d.state) this.pushState(); // 服务端无存档(老账号首登):把本地存档上传初始化
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -90,6 +97,7 @@ const NET = {
|
|||||||
coins: SAVE.coins, ships: SAVE.ships,
|
coins: SAVE.coins, ships: SAVE.ships,
|
||||||
ship: typeof SETUP !== "undefined" ? SETUP.ship : "falcon",
|
ship: typeof SETUP !== "undefined" ? SETUP.ship : "falcon",
|
||||||
m: SAVE.m, best: SAVE.best, rush: SAVE.rush || {},
|
m: SAVE.m, best: SAVE.best, rush: SAVE.rush || {},
|
||||||
|
mute: !!SAVE.mute, music: SAVE.music !== false,
|
||||||
} }).catch(() => { /* 网络失败静默:本地存档仍在 */ });
|
} }).catch(() => { /* 网络失败静默:本地存档仍在 */ });
|
||||||
}, 600);
|
}, 600);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -14,6 +14,49 @@ $("b-hangar").onclick=()=>{AU.init();buildShop();show("o-hangar");};
|
|||||||
$("b-codex").onclick=()=>{AU.init();buildCodex();show("o-codex");};
|
$("b-codex").onclick=()=>{AU.init();buildCodex();show("o-codex");};
|
||||||
$("b-rush").onclick=()=>{AU.init();buildRush();show("o-rush");};
|
$("b-rush").onclick=()=>{AU.init();buildRush();show("o-rush");};
|
||||||
$("b-back6").onclick=()=>{UI="title";show("o-title");};
|
$("b-back6").onclick=()=>{UI="title";show("o-title");};
|
||||||
|
/* 基地右上角:静音开关(SFX tone/noise 自查 SAVE.mute;音乐走 AU.applyMute 闸门;persist 触发云同步)
|
||||||
|
图标为手绘像素画:开声=青喇叭+绿音波 / 静音=灰喇叭+红叉 */
|
||||||
|
const SPK_ON=[ // 音波三根竖线:从左到右由短到长
|
||||||
|
".....SS.........",
|
||||||
|
"....SSS.........",
|
||||||
|
"..SSSSS........W",
|
||||||
|
"SSSSSSS......W.W",
|
||||||
|
"SSSSSSSS...W.W.W",
|
||||||
|
"SSSSSSSSS..W.W.W",
|
||||||
|
"SSSSSSSS...W.W.W",
|
||||||
|
"SSSSSSS......W.W",
|
||||||
|
"..SSSSS........W",
|
||||||
|
"....SSS.........",
|
||||||
|
".....SS........."];
|
||||||
|
const SPK_OFF=[
|
||||||
|
".....SS.........",
|
||||||
|
"....SSS.........",
|
||||||
|
"..SSSSS.........",
|
||||||
|
"SSSSSSS...X...X.",
|
||||||
|
"SSSSSSSS...X.X..",
|
||||||
|
"SSSSSSSSS...X...",
|
||||||
|
"SSSSSSSS...X.X..",
|
||||||
|
"SSSSSSS...X...X.",
|
||||||
|
"..SSSSS.........",
|
||||||
|
"....SSS.........",
|
||||||
|
".....SS........."];
|
||||||
|
function updateMuteBtn(){
|
||||||
|
const c=$("b-mute-icon");if(!c)return;
|
||||||
|
const g=c.getContext("2d");g.clearRect(0,0,c.width,c.height);
|
||||||
|
const art=SAVE.mute?SPK_OFF:SPK_ON;
|
||||||
|
for(let y=0;y<art.length;y++)for(let x=0;x<art[y].length;x++){
|
||||||
|
const ch=art[y][x];if(ch===".")continue;
|
||||||
|
g.fillStyle=SAVE.mute?(ch==="X"?"#ff5566":"#7f8cb8"):(ch==="W"?"#5cff9d":"#41e6ff");
|
||||||
|
g.fillRect(x*2+2,y*2+2,2,2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("b-mute").onclick=()=>{
|
||||||
|
SAVE.mute=!SAVE.mute;persist();
|
||||||
|
AU.init();if(AU.ctx&&AU.ctx.state==="suspended")AU.ctx.resume();
|
||||||
|
AU.applyMute();updateMuteBtn();
|
||||||
|
if(!SAVE.mute)SFX.pick(); // 解除静音给个反馈音(静音时 tone 自查抑制)
|
||||||
|
};
|
||||||
|
updateMuteBtn();
|
||||||
$("b-back4").onclick=()=>{UI="title";bestLine();show("o-title");};
|
$("b-back4").onclick=()=>{UI="title";bestLine();show("o-title");};
|
||||||
$("b-back5").onclick=exitDemoSim;
|
$("b-back5").onclick=exitDemoSim;
|
||||||
/* 图鉴测试模式(隐藏入口):连点图鉴标题“军”字10次开关。
|
/* 图鉴测试模式(隐藏入口):连点图鉴标题“军”字10次开关。
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const RUN_MODES = new Set(["stage", "endless", "rush"]);
|
|||||||
const SHIP_KEYS = new Set(["falcon", "ghost", "bastion", "valkyrie", "asura"]);
|
const SHIP_KEYS = new Set(["falcon", "ghost", "bastion", "valkyrie", "asura"]);
|
||||||
const PASSIVE_KEYS = new Set(["fire", "crit", "shield", "nano", "mag", "bounty", "odcore", "ghost"]);
|
const PASSIVE_KEYS = new Set(["fire", "crit", "shield", "nano", "mag", "bounty", "odcore", "ghost"]);
|
||||||
const META_MAX = { weapon: 2, armor: 2, hpup: 2, luck: 2, wallet: 3, mag2: 2, odstart: 1 };
|
const META_MAX = { weapon: 2, armor: 2, hpup: 2, luck: 2, wallet: 3, mag2: 2, odstart: 1 };
|
||||||
const DEFAULT_STATE = { coins: 0, ships: ["falcon"], ship: "falcon", m: {}, best: { s0: 0, s1: 0, e0: 0, e1: 0, stage: 0 }, rush: {} };
|
const DEFAULT_STATE = { coins: 0, ships: ["falcon"], ship: "falcon", m: {}, best: { s0: 0, s1: 0, e0: 0, e1: 0, stage: 0 }, rush: {}, mute: false, music: true };
|
||||||
|
|
||||||
const qInsUser = db.prepare("INSERT INTO users(username,email,pass_hash,created_at) VALUES(?,?,?,?)");
|
const qInsUser = db.prepare("INSERT INTO users(username,email,pass_hash,created_at) VALUES(?,?,?,?)");
|
||||||
const qInsStats = db.prepare("INSERT OR IGNORE INTO user_stats(user_id) VALUES(?)");
|
const qInsStats = db.prepare("INSERT OR IGNORE INTO user_stats(user_id) VALUES(?)");
|
||||||
@ -76,7 +76,10 @@ function vState(s) {
|
|||||||
const rush = {};
|
const rush = {};
|
||||||
if (s.rush && typeof s.rush === "object") for (const k of Object.keys(s.rush).slice(0, 32))
|
if (s.rush && typeof s.rush === "object") for (const k of Object.keys(s.rush).slice(0, 32))
|
||||||
if (/^\d{1,2}$/.test(k) && vInt(s.rush[k], 0, 8640000)) rush[k] = s.rush[k];
|
if (/^\d{1,2}$/.test(k) && vInt(s.rush[k], 0, 8640000)) rush[k] = s.rush[k];
|
||||||
return { coins: s.coins, ships, ship, m, best, rush };
|
/* 音频开关:缺省=不静音/音乐开(老存档无此字段) */
|
||||||
|
const mute = s.mute === true;
|
||||||
|
const music = s.music !== false;
|
||||||
|
return { coins: s.coins, ships, ship, m, best, rush, mute, music };
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 无尽模式断点存档校验(波次检查点快照;装备 key 复用图鉴白名单) */
|
/* 无尽模式断点存档校验(波次检查点快照;装备 key 复用图鉴白名单) */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user