public/dev_tools.js
分割 6 / 10 · 元ファイル 964〜1104行付近
最後に描けてから: (C&&C.lastDraw)? +((performance.now()-C.lastDraw)/1000).toFixed(1) : null,
画面に板がある: !!cv,
板の大きさ: cv? cv.width+'x'+cv.height : null,
GPU設定: (typeof SETTINGS!=='undefined') ? {全体:!!SETTINGS.gl, 教室:!!SETTINGS.glClass} : null,
教室の印: document.body.classList.contains('glcOn'),
止まった関所: (C&&C.why)||null, // 2026-09-01 K1指示: 描いていない時、どの関所で戻っているか
};
}catch(e){ return {読めず:String(e)}; } })(),
/* ロード画面の失敗記録(2026-08-29 Codex納品キットの依頼)。
準備が間に合わなかった/例外で落ちた時だけ中身が入る。空なら順調 */
loadLog:(()=>{ try{ return (window.__HZG_LOADLOG||[]).slice(-12); }catch(e){ return null; } })(),
/* 音の記録(2026-08-29 K1依頼)。タイトルの曲の頭が壊れる件の断定用。
時刻・曲の位置・読み込み具合(0〜4)・音量を、下ごしらえから鳴らし終わりまで並べてある。
読み方: 「鳴らし始めて+○ms」の行で位置が滑らかに進んでいなければ、そこが壊れた瞬間。
曲の取得の「控えから:true」はキャッシュから来たという意味(タスクキル直後の起動で出る) */
bgmLog:(()=>{ try{ return (window.__HZG_BGMLOG||[]).slice(-90); }catch(e){ return null; } })(),
/* プロローグADVの文字送りの記録(2026-09-02 K1指示)。実機で「2文字目から1秒詰まる」件の断定用。
行ごとに、何文字目が何msに「置かれ」何msに「出た」かが並ぶ。
読み方:
・置いた が26msずつ進んでいない → 本体(JS)がそこで止まっている
・置いた は26msずつなのに 出た が飛んでいる → 絵が追いつけていない(描画側)
・前のコマから が大きい所 = 画面が止まっていた長さ
幕が「開きながら」の行は、プロローグの1行目(700msかけて幕を開けている最中) */
advLog:(()=>{ try{ return (window.__HZG_ADVLOG||[]).slice(-6); }catch(e){ return null; } })(),
/* 指の操作が何msかかったか(2026-09-02)。10ms以上かかった物だけ載せる。
文字送りが止まった時刻と重なっていれば、その出来事の処理が犯人 */
evLog:(()=>{ try{ return (window.__HZG_EVLOG||[]).filter(e=>e.かかった>=10).slice(-24); }catch(e){ return null; } })(),
/* 浮き文字・吹き出しの位置(2026-09-02 K1報告「一瞬上に行ってまた元の位置に戻る」)。
直近3秒ぶんを、コマごと・物ごとに並べてある。
読み方: 同じidの「上」を縦に追う。1コマだけ大きく減って次で戻っていれば、そいつが跳んだ物。
跳び幅が46px前後なら、盤面の縦のずらし幅と同じ=座標の取り違え */
/* 押下音の計器(2026-09-03 K1許可)。
「呼ばれた」に対して「鳴った」が足りない時、その差がどの理由で消えたかが分かる。
状態別に interrupted や suspended が積まれていれば、音の口が寝ていたのが原因 */
kmLog:(()=>{ try{ return window.__HZG_KMLOG||null; }catch(e){ return null; } })(),
/* 曲の出入り(2026-09-03 K1指示)。直近80件。
「かけようとした」に対して「鳴らし始めた」が無ければ、その次の行に理由が出ている */
bgmEv:(()=>{ try{ return (window.__HZG_BGMEV||[]).slice(-40); }catch(e){ return []; } })(),
/* 曲の位置の物差し(2026-09-04 K1指示「計器をたせ」)。0.5秒ごと・直近60秒ぶん。
読み方: 「位置」を縦に追う。0付近を行ったり来たり=頭が繰り返している /
同じ数字が続く=止まっている / 1回だけ大きく戻る=ふつうのループの継ぎ目 */
bgmPos:(()=>{ try{ return (window.__HZG_BGMPOS||[]).slice(-120); }catch(e){ return []; } })(),
/* 曲の要素自身の出来事。ゲームが頼んでいない再生や頭出しが混ざっていればここに出る */
bgmElEv:(()=>{ if(!HZG_METERS.on('曲')) return []; try{ return (window.__HZG_BGMEL_EV||[]).slice(-60); }catch(e){ return []; } })(),
/* 画面が隠れた・戻った時刻(2026-09-04)。今までは合計秒数しか無かった */
visLog:(()=>{ if(!HZG_METERS.on('画面')) return []; try{ return (window.__HZG_VISLOG||[]).slice(-40); }catch(e){ return []; } })(),
/* 効果音の計器(2026-09-04 K1指示「どの効果音がどのタイミングで鳴ったか」)。
直近160件。「幕が画面に」がtrueの行は、K1 STUDIOのロゴが出ている間に鳴った音 */
seLog:(()=>{ if(!HZG_METERS.on('効果音')) return []; try{ return (window.__HZG_SELOG||[]).slice(-160); }catch(e){ return []; } })(),
/* 音の口(AudioContext)の顛末(2026-09-05 K1指示)。
いつ作り、いつresumeを頼み、それが成功したか失敗したか、0.5秒後にどうなっていたか */
acLog:(()=>{ if(!HZG_METERS.on('音の口')) return []; try{ return (window.__HZG_ACLOG||[]).slice(-120); }catch(e){ return []; } })(),
/* 物理の重さ(2026-09-06 K1指示「計器足しとけよ」)。発熱の疑いの切り分け用。
1コマぶんのミリ秒(直近240コマ)・平均・最悪・回したコマ数・
そのうち何も動いていなかったコマ数・床の物の数・机や壁の数。
「コマ仕事」(perf.raid)と並べれば、物理が占める割合が出る */
/* ★スポット計器(2026-09-06 K1指示)。大群予告の曲が鳴らない件**専用**。直ったら消す */
spotBgm:(()=>{ if(!HZG_METERS.on('スポット')) return []; try{ return (window.__HZG_SPOTBGM||[]).slice(-40); }catch(e){ return []; } })(),
/* ★スポット計器(2026-09-06 K1指示「スポット計器つけとけ」)。
床のバスケットボールがつかめない件**専用**。直ったら消す。
入切の札に関わらず必ず載せる——この件は「記録が残らないこと」が問題なので、
K1が計器をオフにしていても取りこぼさない */
spotBall:(()=>{ try{ return (window.__HZG_SPOTBALL||[]).slice(-40); }catch(e){ return []; } })(),
/* ★【スポット計器 20260912 掴まれたまま】K1報告 gctjwu「佐藤がゾンビに掴まれたまま離されなくなった」。直ったら消す。
読み方: 「放した」の行=掴んでいない敵に掴まれたままだった(前ならそのまま固まっていた)瞬間。敵の状態と狙いで、どの道で外れたかが分かる */
spotGrab:(()=>{ try{ return (window.__HZG_SPOTGRAB||[]).slice(-40); }catch(e){ return []; } })(),
physPerf:(()=>{ if(!HZG_METERS.on('物理')) return null; try{
const P=window.__HZG_PHYSPERF; if(!P) return null;
const a=P.ms||[]; const n=a.length||1;
const sum=a.reduce((x,y)=>x+y,0);
return {平均ms:+(sum/n).toFixed(2), 最悪ms:+Math.max.apply(null,a.concat([0])).toFixed(2),
回したコマ数:P.n, 何も動いていないコマ数:P.idle,
無駄の割合:P.n?(+(P.idle/P.n*100).toFixed(1)+'%'):'-',
台数:P.台数, 動いている:P.動いている, 机:P.机, 壁:P.壁, バリケード:P.バリケード,
進めた回数:P.進めた回数, 直近のms:a.slice(-40)};
}catch(e){ return null; } })(),
/* 床の物(武器・ボール)の動き(2026-09-06 K1指示「バグ報告に計器足すなりしろ」)。
0.1秒ごとに、教室か襲撃か・種類・位置・高さ・平面の速さ・回っている速さ・
材質(空気抵抗/摩擦/跳ね返り)・世界を進めた刻み。K1報告「襲撃で床を滑ってる」の切り分け用。
両方の場面が同じ一覧に入るので、そのまま並べて見比べられる */
dropLog:(()=>{ if(!HZG_METERS.on('床の物')) return []; try{ return (window.__HZG_DROPLOG||[]).slice(-260); }catch(e){ return []; } })(),
/* 起動の一部始終(2026-09-05 K1指示)。触った回数・幕が開いた時刻・判断材料が揃った時刻 */
bootLog:(()=>{ if(!HZG_METERS.on('起動')) return []; try{ return (window.__HZG_BOOTLOG||[]).slice(-150); }catch(e){ return []; } })(),
/* 絵が異常に大きくなった記録(2026-09-05 K1報告)。空なら一度も起きていない。
「誰」がキャラの番号なら席の札の変形が原因、「写した枠」なら測り方(K)が原因 */
bigLog:(()=>{ if(!HZG_METERS.on('絵の大きさ')) return []; try{ return (window.__HZG_BIG||[]).slice(-12); }catch(e){ return []; } })(),
/* 今のKと盤面の実幅。上の記録と見比べる基準 */
glK:(()=>{ try{ const sc=document.getElementById('scene'); if(!sc) return null;
const r=sc.getBoundingClientRect();
return {盤面の実幅:Math.round(r.width), K:+(r.width/390).toFixed(3)}; }catch(e){ return null; } })(),
/* 必殺技の計器(2026-09-03 K1許可)。技の最中と直後3秒の様子。
「進んだ」がほぼ0のまま続き、「道」が0なら、道が引けずに踏み込んでは戻される痙攣 */
spLog:(()=>{ try{ const L=(window.__HZG_SPLOG||[]);
return L.length ? L.slice(-160) : []; }catch(e){ return []; } })(),
floatLog:(()=>{ try{ const L=(window.__HZG_FLOATLOG||[]);
/* 跳んだ物だけに絞って載せる(全部載せると報告が太る) */
const byId={};
for(const f of L) for(const o of (f.物||[])){ (byId[o.id]=byId[o.id]||[]).push({t:f.t, 上:o.上, 字:o.字}); }
const out={};
for(const id in byId){ const a=byId[id]; let mx=0;
for(let i=1;i<a.length;i++) mx=Math.max(mx, Math.abs(a[i].上-a[i-1].上));
if(mx>=12) out[id+'('+a[0].字+') 最大'+Math.round(mx)+'px']=a.map(x=>x.上); }
/* 盤面のずらしが動いていないか(跳びの本命)。値が変わったコマだけ並べる */
const cam=[]; let pv=null;
for(const f of L){
const k=f.盤面の位置+'|'+f.親たち+'/'+f.送り+'/'+f.見える窓のずれ+'/'+f.ずらし+'/'+f.倍率+'/'+f.拡大+'/'+f.縦ずれ+'/'+f.箱の高さ;
if(k!==pv){ cam.push({t:f.t, 盤面の位置:f.盤面の位置, 親たち:f.親たち, 送り:f.送り,
見える窓のずれ:f.見える窓のずれ, ずらし:f.ずらし, 倍率:f.倍率,
拡大:f.拡大, 縦ずれ:f.縦ずれ, 箱の高さ:f.箱の高さ}); pv=k; }
}
if(cam.length>1) out['★土台が動いた']=cam.slice(0,24);
else if(cam.length===1) out['★土台は動いていない']=cam[0];
return Object.keys(out).length? out : {跳んだ物:'なし(直近3秒)'};
}catch(e){ return null; } })(),
/* コマ仕事の内訳(2026-08-21): 襲撃ループ/教室ループの自己時間。19ms級の犯人をここで切り分ける */
perf:(()=>{ try{ const q=a=>{ if(!a||!a.length) return null; const b=a.slice().sort((x,y)=>x-y);
return {中央:+b[Math.floor(b.length/2)].toFixed(1), 遅い方1割:+b[Math.floor(b.length*0.9)].toFixed(1), 最悪:+b[b.length-1].toFixed(1)}; };
const P=window.__hzgPerf||{}; return {襲撃ループ:q(P.raid), 教室ループ:q(P.cls)}; }catch(e){ return null; } })(),
/* 切り替え工程の時刻印(全撤去TODO段0・2026-08-23): 実機のカクつきがどの工程間で起きたかを読む */
switchLog:(()=>{ try{ return ((window.__hzgPerf||{}).sw||[]).slice(-24); }catch(e){ return null; } })(),
/* 発熱計(2026-08-23 K1報告): 描画回数/秒と隠れている間の活動。隠れて動いた秒数>0なら異常 */
heat:(()=>{ try{ const R=window.__heatRing||[]; if(!R.length) return null;
const last=R.slice(-60);
const sum=(a,k)=>a.reduce((x,y)=>x+y[k],0);
const hid=R.filter(x=>x.h);
return { 直近60秒:{ 教室描画_平均毎秒:+(sum(last,'p')/last.length).toFixed(1),
教室描画_最大毎秒:Math.max.apply(null,last.map(x=>x.p)),
戦闘描画_平均毎秒:+(sum(last,'d')/last.length).toFixed(1) },
隠れていた秒数:hid.length,
隠れて動いた秒数:hid.filter(x=>x.p>0||x.d>0).length };
}catch(e){ return null; } })(),
events:BB_EV.slice(-100) };
try{
const js=document.querySelector('script[src*="raid_battle.js"]');
if(js) rep.jsver=String(js.getAttribute('src')||'').split('?')[1]||'';
}catch(e){}
try{ if(typeof game!=='undefined' && game){
rep.screen={ phase:game.phase, day:game.day, slot:game.slot,
alive:(game.students||[]).filter(function(s){ return ['healthy','injured','suspect'].indexOf(s.state)>=0; }).length };
rep.daily=slimDaily(game);
} }catch(e){ rep.screenErr=String(e).slice(0,120); }