diff --git a/index.html b/index.html index e9e175f..4f176a2 100644 --- a/index.html +++ b/index.html @@ -193,10 +193,13 @@ const LANES=[ {label:"Blue", color:"#3182ce",key:"h", type:"hihat"}, {label:"Green", color:"#38a169",key:"j", type:"tom2"}, {label:"Kick", color:"#805ad5",key:" ", type:"kick"}, + {label:"Crash", color:"#ed8936",key:"d", type:"crash"}, + {label:"Ride", color:"#4fd1c5",key:"k", type:"ride"}, + {label:"Tom 3", color:"#d53f8c",key:"l", type:"tom3"}, ]; // which pads are active (show notes) — all on by default -let activePads=[true,true,true,true,true]; +let activePads=[true,true,true,true,true,false,false,false]; // Simple mode — thins out notes, keeping only strong beats let simpleMode=false; @@ -4289,9 +4292,9 @@ let gameAc=null,mp3Buffer=null,mp3Src=null; let notes=[],gameStartTime=null,gameEndTime=null,playing=false; let gamePlayers=[],activePIdx=0; let cScore=0,cStreak=0,cTotal=0,cGood=0,cMaxStreak=0; -let laneFlash=[0,0,0,0,0],msgTmr=null,prevBtns={},gameSynthNodes=[],classicalHitMode='orchestra'; +let laneFlash=[0,0,0,0,0,0,0,0],msgTmr=null,prevBtns={},gameSynthNodes=[],classicalHitMode='orchestra'; let paused=false,pauseTime=0; -var gpMap=JSON.parse(localStorage.getItem("drgameGpMap")||"null")||{0:2,1:3,2:0,3:1,4:4,8:4}; +var gpMap=JSON.parse(localStorage.getItem("drgameGpMap")||"null")||{0:2,1:3,2:0,3:1,4:4,5:5,6:6,7:7,8:4}; var remapping=false,remapStep=0,remapResult={}; var _savedSettings=JSON.parse(localStorage.getItem("drgameSettings")||"{}"); var gameSpeed=_savedSettings.speed||1.0,masterVolume=_savedSettings.volume||1.0; @@ -4324,7 +4327,7 @@ function buildPadToggles(){ const b=document.createElement("button"); b.className="ptog"+(activePads[i]?" on":""); b.style.borderColor=lane.color;b.style.color=lane.color; - b.textContent=lane.label+(i===4?" (Kick)":""); + b.textContent=lane.label; b.onclick=()=>{activePads[i]=!activePads[i];buildPadToggles();}; el.appendChild(b); }); @@ -4645,34 +4648,35 @@ function draw(){ if(!W||!H)return; cx.clearRect(0,0,W,H); - // Active pad lanes (only those toggled on, excluding kick) - const activeLanes=LANES.slice(0,4).map((l,i)=>i).filter(i=>activePads[i]); + // Pad lanes (everything except kick) in columns; kick centered at bottom + const padLanes=LANES.map((l,i)=>i).filter(i=>i!==4&&activePads[i]); const hasKick=activePads[4]; const PAD_H=hasKick?Math.floor(H*0.78):H; const KICK_H=H-PAD_H; - const nLanes=activeLanes.length; - const PAD_W=nLanes>0?W/nLanes:W; + const nLanes=padLanes.length; + const LANE_W=nLanes>0?W/nLanes:W; const HIT_Y=Math.floor(PAD_H*0.76); const KICK_HIT_Y=PAD_H+Math.floor(KICK_H*0.55); const now=(playing&&gameStartTime!=null)?getAc().currentTime-gameStartTime:-9999; // Lane positions map: lane index -> x center const laneX={}; - activeLanes.forEach((li,col)=>{laneX[li]=col*PAD_W+PAD_W/2;}); + padLanes.forEach((li,col)=>{laneX[li]=col*LANE_W+LANE_W/2;}); // Draw pad lanes - activeLanes.forEach((li,col)=>{ - const x=col*PAD_W; + padLanes.forEach((li,col)=>{ + const x=col*LANE_W; cx.fillStyle=laneFlash[li]?LANES[li].color+"33":"#0d0d16"; - cx.fillRect(x,0,PAD_W,PAD_H); + cx.fillRect(x,0,LANE_W,PAD_H); if(col>0){cx.strokeStyle="#1e1e2e";cx.lineWidth=1;cx.beginPath();cx.moveTo(x,0);cx.lineTo(x,PAD_H);cx.stroke();} cx.fillStyle=LANES[li].color;cx.font="bold 12px sans-serif";cx.textAlign="center"; - cx.fillText(LANES[li].label,x+PAD_W/2,PAD_H-20); + cx.fillText(LANES[li].label,x+LANE_W/2,PAD_H-20); cx.fillStyle="#999";cx.font="10px sans-serif"; - cx.fillText(LANES[li].key.toUpperCase(),x+PAD_W/2,PAD_H-7); + var keyLabel=LANES[li].key===" "?"SPACE":LANES[li].key.toUpperCase(); + cx.fillText(keyLabel,x+LANE_W/2,PAD_H-7); }); - // Kick zone + // Kick zone — centered at bottom, notes fall from top of screen if(hasKick){ cx.fillStyle=laneFlash[4]?LANES[4].color+"33":"#0a0a12"; cx.fillRect(0,PAD_H,W,KICK_H); @@ -4685,7 +4689,7 @@ function draw(){ if(nLanes===0&&!hasKick)return; - // Hit line + // Pad hit line if(nLanes>0){ cx.strokeStyle="rgba(255,255,255,0.5)";cx.lineWidth=2; cx.beginPath();cx.moveTo(0,HIT_Y);cx.lineTo(W,HIT_Y);cx.stroke(); @@ -4699,9 +4703,9 @@ function draw(){ cx.beginPath();cx.moveTo(W*0.15,KICK_HIT_Y);cx.lineTo(W*0.85,KICK_HIT_Y);cx.stroke(); } - // Target rings — pads - activeLanes.forEach(li=>{ - const x=laneX[li],r=Math.min(PAD_W*0.33,26); + // Target rings — pad lanes + padLanes.forEach(li=>{ + const x=laneX[li],r=Math.min(LANE_W*0.33,26); cx.beginPath();cx.arc(x,HIT_Y,r,0,Math.PI*2); cx.strokeStyle=laneFlash[li]?LANES[li].color:LANES[li].color+"55"; cx.lineWidth=laneFlash[li]?3:2;cx.stroke(); @@ -4721,30 +4725,16 @@ function draw(){ if(notes.length===0||notes.every(n=>n.hit||n.missed))endTurn(); } - // Notes + // Notes — pad lanes fall in columns, kick falls centered from top if(mode==="builtin"&&now>-9000){ for(const n of notes){ if(n.hit)continue; const dt=n.time-now; - if(n.lane<4){ - if(!laneX.hasOwnProperty(n.lane))continue; - const ny=HIT_Y-dt*SPEED; - if(ny<-40||ny>PAD_H+40){ - if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}} - continue; - } - const x=laneX[n.lane],r=Math.min(PAD_W*0.33,26); - cx.globalAlpha=n.missed?0.2:1; - const g=cx.createRadialGradient(x,ny,0,x,ny,r*2); - g.addColorStop(0,LANES[n.lane].color+"aa");g.addColorStop(1,LANES[n.lane].color+"00"); - cx.fillStyle=g;cx.beginPath();cx.arc(x,ny,r*2,0,Math.PI*2);cx.fill(); - cx.fillStyle=LANES[n.lane].color;cx.beginPath();cx.arc(x,ny,r,0,Math.PI*2);cx.fill(); - cx.strokeStyle="#fff";cx.lineWidth=2;cx.beginPath();cx.arc(x,ny,r,0,Math.PI*2);cx.stroke(); - cx.globalAlpha=1; - if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}} - } else if(hasKick){ - const ky=KICK_HIT_Y-dt*SPEED*0.5; - if(kyH+30){ + if(n.lane===4){ + // Kick — falls from top of screen, centered, landing on KICK_HIT_Y + if(!hasKick)continue; + const ky=KICK_HIT_Y-dt*SPEED; + if(ky<-40||ky>H+40){ if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}} continue; } @@ -4754,6 +4744,23 @@ function draw(){ cx.strokeStyle="#fff";cx.lineWidth=2;cx.beginPath();cx.arc(W/2,ky,kr,0,Math.PI*2);cx.stroke(); cx.globalAlpha=1; if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}} + } else { + // Pad lanes — fall in columns + if(!laneX.hasOwnProperty(n.lane))continue; + const ny=HIT_Y-dt*SPEED; + if(ny<-40||ny>PAD_H+40){ + if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}} + continue; + } + const x=laneX[n.lane],r=Math.min(LANE_W*0.33,26); + cx.globalAlpha=n.missed?0.2:1; + const g=cx.createRadialGradient(x,ny,0,x,ny,r*2); + g.addColorStop(0,LANES[n.lane].color+"aa");g.addColorStop(1,LANES[n.lane].color+"00"); + cx.fillStyle=g;cx.beginPath();cx.arc(x,ny,r*2,0,Math.PI*2);cx.fill(); + cx.fillStyle=LANES[n.lane].color;cx.beginPath();cx.arc(x,ny,r,0,Math.PI*2);cx.fill(); + cx.strokeStyle="#fff";cx.lineWidth=2;cx.beginPath();cx.arc(x,ny,r,0,Math.PI*2);cx.stroke(); + cx.globalAlpha=1; + if(!n.missed&&dt<-HIT_WIN){n.missed=true;if(cStreak>0){cStreak=0;updateHud();}} } } } @@ -4789,8 +4796,29 @@ function playDrumAt(ac,lane,t,vol,arr){ f.type="highpass";f.frequency.value=7000;s.buffer=buf;s.connect(f);f.connect(g);g.connect(ac.destination); g.gain.setValueAtTime(vol*.5,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.05); s.start(t);s.stop(t+0.06);mk(s); - } else { // tom / tom2 - const freq=type==="tom"?160:120; + } else if(type==="crash"){ + // Crash cymbal — long noise burst with resonance + const sz=ac.sampleRate*0.4,buf=ac.createBuffer(1,sz,ac.sampleRate),d=buf.getChannelData(0); + for(let i=0;i