public/index.html
分割 5 / 6 · 元ファイル 822〜1018行付近
uiBtn('shop',bx,y+62,bw,44,'ショップ','スクラップ '+SAVE.scrap,'on');
uiBtn('avatar',bx,y+114,bw,44,'アバター変更','','on');
g2.textAlign='center';
g2.font='11px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='rgba(244,236,214,.4)';
g2.fillText('さいこう '+SAVE.best+'体 '+VER,W/2,y+186);
g2.textAlign='left';
}
function drawResult(){
g2.fillStyle='rgba(18,8,8,.74)'; g2.fillRect(0,0,W,H);
var r=result||{base:0,mult:1,total:0,kills:0,t:0};
g2.textAlign='center';
g2.font='bold 28px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='#f0d2c4'; g2.fillText('やられた',W/2,H*0.20);
g2.font='13px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='rgba(244,236,214,.72)';
g2.fillText('倒した '+r.kills+'体 / 生き残り '+Math.floor(r.t)+'秒',W/2,H*0.20+26);
/* ★増えた理由を並べて見せる(K1「基礎→生存ボーナス×1.5→最終獲得、のように明示する」) */
var y=H*0.33, bw=Math.min(284,W-44), bx=(W-bw)/2;
g2.textAlign='left';
roundRect(bx,y,bw,118,12); g2.fillStyle='rgba(12,18,24,.92)'; g2.fill();
g2.strokeStyle='rgba(92,120,144,.6)'; g2.lineWidth=1.5; g2.stroke();
g2.font='13px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='rgba(244,236,214,.8)'; g2.fillText('基礎スクラップ',bx+16,y+30);
g2.textAlign='right'; g2.fillStyle='#f4ecd6';
g2.font='bold 15px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillText(''+r.base,bx+bw-16,y+30);
g2.textAlign='left';
g2.font='13px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='rgba(159,216,255,.9)'; g2.fillText('生存ボーナス',bx+16,y+58);
g2.textAlign='right'; g2.font='bold 15px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillText('×'+r.mult.toFixed(1),bx+bw-16,y+58);
g2.textAlign='left';
g2.strokeStyle='rgba(92,120,144,.45)'; g2.lineWidth=1;
g2.beginPath(); g2.moveTo(bx+14,y+72); g2.lineTo(bx+bw-14,y+72); g2.stroke();
g2.font='bold 14px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='#ffce6a'; g2.fillText('もらった',bx+16,y+100);
g2.textAlign='right'; g2.font='bold 22px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillText(''+r.total,bx+bw-16,y+102);
g2.textAlign='center';
g2.font='11px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle='rgba(244,236,214,.5)';
g2.fillText('ぜんぶで '+SAVE.scrap+' スクラップ',W/2,y+140);
g2.textAlign='left';
if(overT>0.5){
var by=y+156;
uiBtn('again',bx,by,bw*0.56-4,46,'もう一度','','hot');
uiBtn('top',bx+bw*0.56+4,by,bw*0.44-4,46,'トップへ','','on');
}
}
function draw(){
BTN.length=0;
g2.save();
if(shake>0) g2.translate((Math.random()*2-1)*shake*5,(Math.random()*2-1)*shake*5);
drawBG();
var i;
for(i=0;i<world.zs.length;i++) if(world.zs[i].dead) drawZombie(world.zs[i]);
for(i=0;i<world.ps.length;i++) if(!world.ps[i].alive) drawPerson(world.ps[i],world.ps[i].id===meId);
for(i=0;i<world.zs.length;i++) if(!world.zs[i].dead) drawZombie(world.zs[i]);
for(i=0;i<world.ps.length;i++) if(world.ps[i].alive) drawPerson(world.ps[i],world.ps[i].id===meId);
for(i=0;i<world.cs.length;i++) drawCrow(world.cs[i]);
for(i=0;i<S.length;i++) drawStoneAt(S[i].b.position.x,S[i].b.position.y,S[i].b.angle,S[i].r,S[i].rock);
for(i=0;i<P.length;i++){
var q=P[i];
g2.globalAlpha=Math.max(0,1-q.t/q.life);
g2.fillStyle=q.col; g2.beginPath(); g2.arc(q.x,q.y,q.r,0,7); g2.fill();
}
g2.globalAlpha=1;
guide();
g2.restore();
g2.textAlign='center';
for(i=0;i<FL.length;i++){
var f=FL[i];
g2.globalAlpha=Math.max(0,1-f.t);
g2.font='bold 15px "Hiragino Kaku Gothic ProN",sans-serif';
g2.fillStyle=f.col; g2.fillText(f.s,f.x,f.y);
}
g2.globalAlpha=1; g2.textAlign='left';
if(scr==='play') drawHUD();
else if(scr==='title') drawTitle();
else if(scr==='result'){ drawHUD(); drawResult(); }
}
/* ===== 着せ替えとお店(ここだけDOM。縦に流してよい) ===== */
var shAv=document.getElementById('shAv'), shShop=document.getElementById('shShop');
function miniCanvas(w,h,fn){
var c=document.createElement('canvas');
var dpr=Math.min(3,window.devicePixelRatio||1);
c.width=w*dpr; c.height=h*dpr; c.style.width=w+'px'; c.style.height=h+'px';
var g=c.getContext('2d'); g.setTransform(dpr,0,0,dpr,0,0);
fn(g,w,h);
return c;
}
function drawMiniPerson(g,w,h,av0,cIdx,htIdx){
var av=SHURE.fix(av0);
var r=h*0.135, x=w/2, hy=h*0.30, shY=hy+1.30*r, bot=shY+2.40*r;
g.strokeStyle='#33323a'; g.lineWidth=r*0.28; g.lineCap='round';
g.beginPath(); g.moveTo(x,bot-r*0.2); g.lineTo(x-r*0.34,bot+r*1.3); g.stroke();
g.beginPath(); g.moveTo(x,bot-r*0.2); g.lineTo(x+r*0.42,bot+r*1.3); g.stroke();
SHURE.draw(g,x,hy,r,1,av,{},bot);
var cst=wrGet(WR.COSTUMES,cIdx); if(cst&&cst.draw) cst.draw(g,x,shY,bot,r,SHURE.WEAR_COL[av.w],1);
var hat=wrGet(WR.HATS,htIdx); if(hat&&hat.draw) hat.draw(g,x,hy,r,1,SHURE.HAIR_COL[av.hc]);
var skin=SHURE.SKIN_COL[av.sk];
g.strokeStyle=skin; g.lineWidth=r*0.28;
g.beginPath(); g.moveTo(x,shY+r*0.12); g.lineTo(x+1.5*r,shY+1.5*r); g.stroke();
g.fillStyle=skin; g.beginPath(); g.arc(x+1.5*r,shY+1.5*r,r*0.20,0,7); g.fill();
}
function openAvatar(){ shAv.classList.add('on'); renderAvatar(); }
function openShop(){ shShop.classList.add('on'); renderShop(); }
document.getElementById('avX').onclick=function(){ shAv.classList.remove('on'); };
document.getElementById('spX').onclick=function(){ shShop.classList.remove('on'); };
function renderAvatar(){
document.getElementById('avSc').textContent='スクラップ '+SAVE.scrap;
var b=document.getElementById('avBody');
b.innerHTML='';
var prev=document.createElement('div');
prev.style.cssText='display:flex;justify-content:center;padding:6px 0 10px';
prev.appendChild(miniCanvas(150,190,function(g,w,h){
g.fillStyle='#0a1016'; g.fillRect(0,0,w,h);
drawMiniPerson(g,w,h,SAVE.av,SAVE.eq.c,SAVE.eq.ht);
}));
b.appendChild(prev);
var parts=[
['髪型','h',SHURE.HAIR_N,function(i){ return SHURE.HAIR_NM[i]; },null],
['髪の色','hc',8,null,SHURE.HAIR_COL],
['肌','sk',4,null,SHURE.SKIN_COL],
['服の色','w',8,null,SHURE.WEAR_COL]
];
parts.forEach(function(pt){
var lab=document.createElement('div'); lab.className='lab';
lab.textContent=pt[0]+'(ぜんぶ無料)';
b.appendChild(lab);
var gd=document.createElement('div'); gd.className='grid';
for(var i=0;i<pt[2];i++) (function(i){
var bt=document.createElement('button');
if(SAVE.av[pt[1]]===i) bt.className='on';
if(pt[4]){ bt.style.background=pt[4][i]; bt.style.height='34px'; bt.textContent=''; }
else bt.textContent=pt[3]?pt[3](i):(''+(i+1));
bt.onclick=function(){ SAVE.av[pt[1]]=i; putSave(); renderAvatar(); sendLook(); };
gd.appendChild(bt);
})(i);
b.appendChild(gd);
});
}
var SPTAB='c';
function renderShop(){
document.getElementById('spSc').textContent='スクラップ '+SAVE.scrap;
var tabs=document.getElementById('spTabs');
tabs.innerHTML='';
[['c','衣装'],['ht','帽子'],['st','石']].forEach(function(t){
var b=document.createElement('button'); b.textContent=t[1];
if(SPTAB===t[0]) b.className='on';
b.onclick=function(){ SPTAB=t[0]; renderShop(); };
tabs.appendChild(b);
});
var list=(SPTAB==='c')?WR.COSTUMES:(SPTAB==='ht'?WR.HATS:WR.STONES);
var body=document.getElementById('spBody');
body.innerHTML='';
list.forEach(function(it,i){
var row=document.createElement('div'); row.className='row';
row.appendChild(miniCanvas(54,60,function(g,w,h){
g.fillStyle='#0a1016'; g.fillRect(0,0,w,h);
if(SPTAB==='st'){ g.save(); g.translate(w/2,h/2); it.draw(g,16,0); g.restore(); }
else drawMiniPerson(g,w,h,SAVE.av,SPTAB==='c'?i:SAVE.eq.c,SPTAB==='ht'?i:SAVE.eq.ht);
}));
var tx=document.createElement('div');
var nm=document.createElement('div'); nm.className='nm'; nm.textContent=it.nm;
var pr=document.createElement('div'); pr.className='pr';
var own=SAVE.own[SPTAB].indexOf(i)>=0;
pr.textContent=own?'持っている':(it.price+' スクラップ');
tx.appendChild(nm); tx.appendChild(pr); row.appendChild(tx);
var sp=document.createElement('div'); sp.className='sp'; row.appendChild(sp);
var bt=document.createElement('button');
if(SAVE.eq[SPTAB]===i){ bt.className='on'; bt.textContent=(SPTAB==='st')?'使用中':'着ている'; }
else if(own){ bt.textContent=(SPTAB==='st')?'使う':'着る';
bt.onclick=function(){ SAVE.eq[SPTAB]=i; putSave(); renderShop(); sendLook(); }; }
else if(SAVE.scrap>=it.price){ bt.className='buy'; bt.textContent='買う';
bt.onclick=function(){ SAVE.scrap-=it.price; SAVE.own[SPTAB].push(i); SAVE.eq[SPTAB]=i;
putSave(); renderShop(); sendLook(); }; }
else{ bt.className='ng'; bt.innerHTML='買う<i>たりない</i>'; }
row.appendChild(bt);
body.appendChild(row);
});
}
function sendLook(){
if(NET.ws&&NET.ws.readyState===1) netSend({y:'me',h:SAVE.av.h,hc:SAVE.av.hc,sk:SAVE.av.sk,w:SAVE.av.w,
c:SAVE.eq.c,ht:SAVE.eq.ht,st:SAVE.eq.st});
var p=myP(); if(p&&!NET.on){ p.av=SAVE.av; p.look=SAVE.eq; }
}
/* ===== 毎コマ ===== */
function loop(fn){
var last=performance.now(), on=true;
function tick(now){
if(!on) return;
var dt=Math.min(0.05,(now-last)/1000); last=now;