danoniplus 46.6.2 → 47.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/danoni_main.js CHANGED
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2026/04/12
7
+ * Revised : 2026/04/19
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 46.6.2`;
12
- const g_revisedDate = `2026/04/12`;
11
+ const g_version = `Ver 47.0.0`;
12
+ const g_revisedDate = `2026/04/19`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -213,6 +213,9 @@ const g_detailObj = {
213
213
  speedData: [],
214
214
  boostData: [],
215
215
  toolDif: [],
216
+ scoreMinimap: {},
217
+ scoreMinimapReverse: {},
218
+ scoreMinimapHeader: {},
216
219
  };
217
220
 
218
221
  const g_workObj = {
@@ -1202,6 +1205,7 @@ const safeExecuteCustomHooks = (_hookName, _funcArray, ...args) => {
1202
1205
  console.group(`${unEscapeEmoji(g_emojiObj.crossMark)} Custom Function Error: [${_hookName}] (Index: ${index}${func.name ? `, Func: ${func.name}` : ``})`);
1203
1206
  console.error(e);
1204
1207
  console.groupEnd();
1208
+ makeInfoWindow(g_msgInfoObj.W_0051, `leftToRightFade`);
1205
1209
 
1206
1210
  } else if (errorCache && !errorCache[index]?.has(func)) {
1207
1211
  // ループがある場合のエラー処理(初回のみ)
@@ -1210,6 +1214,7 @@ const safeExecuteCustomHooks = (_hookName, _funcArray, ...args) => {
1210
1214
  console.groupEnd();
1211
1215
  errorCache[index] = new Set();
1212
1216
  errorCache[index].add(func);
1217
+ makeInfoWindow(g_msgInfoObj.W_0051, `leftToRightFade`);
1213
1218
  }
1214
1219
  }
1215
1220
  }
@@ -3350,6 +3355,154 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
3350
3355
  g_detailObj.startFrame[_scoreId] = startFrame;
3351
3356
  g_detailObj.playingFrame[_scoreId] = playingFrame;
3352
3357
  g_detailObj.playingFrameWithBlank[_scoreId] = lastFrame - startFrame;
3358
+
3359
+ const generateMinimap = (_scoreId, _scoreObj, _keyNum, _playingFrame, _firstArrowFrame, _isReverse = false) => {
3360
+ // 高さを演奏時間に比例させる (例: 1フレーム = 0.5px)
3361
+ const scale = 1.5;
3362
+ const dpr = window.devicePixelRatio || 1; // デバイスのピクセル比を取得(通常 2〜3)
3363
+
3364
+ const timeMargin = 35; // 時間表示用の左マージン
3365
+ const mmWidth = (g_sWidth - 500) / 2 + 290; // 基準となるコマの横幅(親divより少し短くする)
3366
+ const mmHeightTotal = _playingFrame * scale;
3367
+ const mmMarginY = 2;
3368
+ const laneWidth = Math.min(Math.floor((mmWidth - timeMargin) / _keyNum), 40);
3369
+ const logicalWidth = timeMargin + (laneWidth * _keyNum);
3370
+
3371
+ // ヘッダー用キャンバスの作成
3372
+ const canvasHeader = document.createElement(`canvas`);
3373
+ const ctxHeader = canvasHeader.getContext(`2d`);
3374
+ canvasHeader.width = logicalWidth * dpr;
3375
+ canvasHeader.height = 15 * dpr;
3376
+ canvasHeader.style.width = `${logicalWidth}px`;
3377
+ canvasHeader.style.height = `15px`;
3378
+ ctxHeader.scale(dpr, dpr);
3379
+ ctxHeader.fillStyle = '#999';
3380
+ ctxHeader.font = `10px ${getBasicFont()}`;
3381
+ ctxHeader.textAlign = 'center';
3382
+
3383
+ for (let j = 0; j < _keyNum; j++) {
3384
+ const x = timeMargin + j * laneWidth + laneWidth / 2;
3385
+ ctxHeader.fillText(g_kCd[g_keyObj[`keyCtrl${_keyCtrlPtn}`][j][0]], x, 10);
3386
+ ctxHeader.strokeStyle = '#444';
3387
+ }
3388
+
3389
+ g_detailObj.scoreMinimapHeader[_scoreId] = canvasHeader;
3390
+
3391
+ // 譜面全体のキャンバスの作成
3392
+ const MAX_CANVAS_HEIGHT = 10000; // 1枚あたりの高さ制限(安全圏)
3393
+ const canvasCount = Math.ceil((mmHeightTotal + mmMarginY * 2) / MAX_CANVAS_HEIGHT);
3394
+ const canvases = [];
3395
+
3396
+ for (let i = 0; i < canvasCount; i++) {
3397
+ const cvs = document.createElement('canvas');
3398
+ const h = (i === canvasCount - 1)
3399
+ ? (mmHeightTotal + mmMarginY * 2) - (MAX_CANVAS_HEIGHT * i)
3400
+ : MAX_CANVAS_HEIGHT;
3401
+
3402
+ cvs.width = logicalWidth * dpr;
3403
+ cvs.height = h * dpr;
3404
+ cvs.style.width = `${logicalWidth}px`;
3405
+ cvs.style.height = `${h}px`;
3406
+ cvs.style.display = 'block'; // 隙間防止
3407
+ const ctx = cvs.getContext('2d');
3408
+ ctx.scale(dpr, dpr);
3409
+ canvases.push({ canvas: cvs, ctx: ctx, offsetTop: i * MAX_CANVAS_HEIGHT });
3410
+ }
3411
+
3412
+ // --- 描画先を振り分けるヘルパー関数 ---
3413
+ const drawOnTarget = (y, height, drawFunc) => {
3414
+ canvases.forEach(item => {
3415
+ // 描画要素がCanvasの範囲内に入っているか判定
3416
+ if (y + height >= item.offsetTop && y <= item.offsetTop + (item.canvas.height / dpr)) {
3417
+ item.ctx.save();
3418
+ item.ctx.translate(0, -item.offsetTop);
3419
+ drawFunc(item.ctx);
3420
+ item.ctx.restore();
3421
+ }
3422
+ });
3423
+ };
3424
+
3425
+ // --- Y座標計算用の関数 ---
3426
+ // 通常:上(0)から下へ増える
3427
+ // リバース:最大値(mmHeight)から上へ向かって減る
3428
+ const getY = (frame) => {
3429
+ const relativeFrame = frame - _firstArrowFrame;
3430
+ const rawY = relativeFrame * scale;
3431
+ return _isReverse ? (mmHeightTotal - rawY + mmMarginY) : (rawY + mmMarginY);
3432
+ };
3433
+
3434
+ // 時間表記用のフォーマット関数
3435
+ const formatTime = (frame) => {
3436
+ const [m, s] = transFrameToTimer(frame).split(`:`);
3437
+ return `${m.padStart(2, `0`)}:${s}`;
3438
+ };
3439
+
3440
+ // --- 1. 時間軸・ガイドライン ---
3441
+ const interval = g_fps;
3442
+ let startPoint = Math.ceil(_firstArrowFrame / interval) * interval;
3443
+ for (let currentFrame = startPoint; currentFrame <= _firstArrowFrame + _playingFrame; currentFrame += interval) {
3444
+ const y = getY(currentFrame);
3445
+ drawOnTarget(y - 5, 10, (ctx) => {
3446
+ ctx.strokeStyle = '#444';
3447
+ ctx.fillStyle = '#999';
3448
+ ctx.font = `10px ${getBasicFont()}`;
3449
+ ctx.textAlign = 'right';
3450
+ ctx.textBaseline = 'middle';
3451
+ ctx.beginPath();
3452
+ ctx.moveTo(timeMargin, y);
3453
+ ctx.lineTo(timeMargin + laneWidth * _keyNum, y);
3454
+ ctx.stroke();
3455
+ ctx.fillText(formatTime(currentFrame), timeMargin, y);
3456
+ });
3457
+ }
3458
+
3459
+ // --- 2. フリーズノート ---
3460
+ for (let j = 0; j < _keyNum; j++) {
3461
+ const frz = _scoreObj.frzData[j];
3462
+ for (let k = 0; k < frz.length; k += 2) {
3463
+ const start = frz[k], end = frz[k + 1];
3464
+ if (isNaN(start) || isNaN(end)) continue;
3465
+ const y1 = getY(start), y2 = getY(end);
3466
+ const x = timeMargin + j * laneWidth;
3467
+ const top = Math.min(y1, y2), h = Math.abs(y2 - y1);
3468
+
3469
+ drawOnTarget(top, h, (ctx) => {
3470
+ ctx.fillStyle = 'rgba(0, 200, 255, 0.4)';
3471
+ ctx.fillRect(x + 2, top, laneWidth - 3, h);
3472
+ ctx.strokeStyle = 'rgba(0, 200, 255, 0.8)';
3473
+ ctx.strokeRect(x + 2, top, laneWidth - 3, h);
3474
+ });
3475
+ }
3476
+ }
3477
+
3478
+ // --- 3. 通常ノート ---
3479
+ for (let j = 0; j < _keyNum; j++) {
3480
+ const color = g_dfColorObj.setColorType2[g_keyObj[`color${_keyCtrlPtn}_0`][j]] || '#ffffff';
3481
+ _scoreObj.arrowData[j].forEach(note => {
3482
+ const val = parseFloat(note);
3483
+ if (isNaN(val)) return;
3484
+ const y = getY(val), x = timeMargin + j * laneWidth;
3485
+ drawOnTarget(y - 1.5, 3, (ctx) => {
3486
+ ctx.fillStyle = color;
3487
+ ctx.fillRect(x + 1, y - 1.5, laneWidth - 1, 3);
3488
+ });
3489
+ });
3490
+ }
3491
+
3492
+ // 保存(Canvasの配列を保存する)
3493
+ const result = canvases.map(item => item.canvas);
3494
+ if (_isReverse) {
3495
+ g_detailObj.scoreMinimapReverse[_scoreId] = result;
3496
+ } else {
3497
+ g_detailObj.scoreMinimap[_scoreId] = result;
3498
+ }
3499
+ };
3500
+
3501
+ // storeBaseData の中で呼び出す
3502
+ // 通常版を生成
3503
+ generateMinimap(_scoreId, _scoreObj, keyNum, playingFrame, firstArrowFrame, false);
3504
+ // リバース版を生成
3505
+ generateMinimap(_scoreId, _scoreObj, keyNum, playingFrame, firstArrowFrame, true);
3353
3506
  };
3354
3507
 
3355
3508
  /**
@@ -4391,8 +4544,8 @@ const headerConvert = _dosObj => {
4391
4544
  obj.playingLayout = g_presetObj.playingLayout ?? true;
4392
4545
  }
4393
4546
 
4394
- // ジャストフレームの設定 (ローカル: 0フレーム, リモートサーバ上: 1フレーム以内)
4395
- obj.justFrames = (g_isLocal) ? 0 : 1;
4547
+ // ジャストフレームの設定 (ローカル/デバッグ時: 0フレーム, 通常時: 1フレーム以内)
4548
+ obj.justFrames = g_isDebug ? 0 : 1;
4396
4549
 
4397
4550
  // リザルトデータのカスタマイズ
4398
4551
  obj.resultFormat = escapeHtmlForEnabledTag(_dosObj.resultFormat ?? g_presetObj.resultFormat ?? g_templateObj.resultFormatDf);
@@ -7578,6 +7731,71 @@ const makeHighScore = _scoreId => {
7578
7731
  }
7579
7732
  };
7580
7733
 
7734
+ const drawMinimap = (_scoreId, _initFlg = false) => {
7735
+ const detailMiniMap = document.getElementById(`detailMiniMap`);
7736
+ if (detailMiniMap === null) return; // scoreDetailUse=false 等で未生成の場合は何もしない
7737
+
7738
+ const currentScrollTop = document.getElementById(`detailMiniMapSub`)
7739
+ ? document.getElementById(`detailMiniMapSub`).scrollTop : 0;
7740
+
7741
+ // 再描画のため一度クリア
7742
+ deleteChildspriteAll(`detailMiniMap`);
7743
+
7744
+ // drawMinimap 内の Canvas 追加部分
7745
+ const savedCanvases = g_stateObj.miniMapRevFlg
7746
+ ? g_detailObj.scoreMinimapReverse[_scoreId]
7747
+ : g_detailObj.scoreMinimap[_scoreId];
7748
+
7749
+ // --- ヘッダー部分 ---
7750
+ const detailMiniMapHeader = createEmptySprite(detailMiniMap, `detailMiniMapHeader`, g_windowObj.detailMiniMapHeader);
7751
+ $id(`detailMiniMapHeader`).top = (g_stateObj.miniMapRevFlg ? 230 : 0) + `px`;
7752
+ detailMiniMapHeader.appendChild(g_detailObj.scoreMinimapHeader[_scoreId]);
7753
+
7754
+ // --- メイン(譜面)部分 ---
7755
+ const detailMiniMapSub = createEmptySprite(detailMiniMap, `detailMiniMapSub`, g_windowObj.detailMiniMapSub);
7756
+ $id(`detailMiniMapSub`).top = (g_stateObj.miniMapRevFlg ? 0 : 15) + `px`;
7757
+
7758
+ detailMiniMapSub.style.overflowX = 'hidden';
7759
+ detailMiniMapSub.style.overflowY = 'auto';
7760
+ detailMiniMapSub.style.pointerEvents = 'auto';
7761
+
7762
+ if (savedCanvases && Array.isArray(savedCanvases)) {
7763
+ // 退避したCanvasそのものをDOMに追加(再描画不要で高速)
7764
+ detailMiniMapSub.style.overflow = C_DIS_AUTO;
7765
+ detailMiniMapSub.style.pointerEvents = C_DIS_AUTO;
7766
+ savedCanvases.forEach(canvas => {
7767
+ canvas.style.position = 'static';
7768
+ canvas.style.display = 'block';
7769
+ canvas.style.height = 'auto';
7770
+ detailMiniMapSub.appendChild(canvas);
7771
+ });
7772
+ }
7773
+ const scrollHeight = Math.max(detailMiniMapSub.scrollHeight - detailMiniMapSub.clientHeight, 0);
7774
+ const playingFrame = Math.max(g_detailObj.playingFrame[_scoreId], 1);
7775
+ const lastFrame = g_detailObj.startFrame[_scoreId] + g_detailObj.playingFrameWithBlank[_scoreId];
7776
+ const firstArrowFrame = lastFrame - g_detailObj.playingFrame[_scoreId];
7777
+ const fadeinFrameOffset = Math.max(0, Math.min(
7778
+ playingFrame,
7779
+ getStartFrame(lastFrame, g_stateObj.fadein, _scoreId) - firstArrowFrame
7780
+ ));
7781
+ const fadeinScrollTop = scrollHeight * fadeinFrameOffset / playingFrame;
7782
+ detailMiniMapSub.scrollTop = g_stateObj.miniMapRevFlg
7783
+ ? (_initFlg ? scrollHeight - currentScrollTop : scrollHeight - fadeinScrollTop)
7784
+ : (_initFlg ? scrollHeight - currentScrollTop : fadeinScrollTop);
7785
+
7786
+ if (document.getElementById(`lnkMiniMapRev`) === null) {
7787
+ scoreDetail.appendChild(
7788
+ makeDifLblCssButton(`lnkMiniMapRev`, g_lblNameObj.s_rev + `${g_stateObj.miniMapRevFlg ? `↑` : `↓`}`, 8, () => {
7789
+ g_stateObj.miniMapRevFlg = !g_stateObj.miniMapRevFlg;
7790
+ lnkMiniMapRev.textContent = g_lblNameObj.s_rev + `${g_stateObj.miniMapRevFlg ? `↑` : `↓`}`;
7791
+ drawMinimap(g_stateObj.scoreId, true);
7792
+ createScText(lnkMiniMapRev, `MiniMapRev`, { targetLabel: `lnkMiniMapRev`, x: -12 });
7793
+ }, g_lblPosObj.lnkMiniMapRev)
7794
+ );
7795
+ createScText(lnkMiniMapRev, `MiniMapRev`, { targetLabel: `lnkMiniMapRev`, x: -12 });
7796
+ }
7797
+ };
7798
+
7581
7799
  /**
7582
7800
  * 譜面初期化処理
7583
7801
  * - 譜面の基本設定(キー数、初期速度、リバース、ゲージ設定)をここで行う
@@ -7766,6 +7984,7 @@ const setDifficulty = (_initFlg) => {
7766
7984
  drawDensityGraph(g_stateObj.scoreId);
7767
7985
  makeDifInfo(g_stateObj.scoreId);
7768
7986
  makeHighScore(g_stateObj.scoreId);
7987
+ drawMinimap(g_stateObj.scoreId);
7769
7988
  }
7770
7989
 
7771
7990
  // 楽曲データの表示
@@ -7899,6 +8118,9 @@ const createOptionWindow = _sprite => {
7899
8118
 
7900
8119
  $id(`detail${g_stateObj.scoreDetail}`).visibility = `visible`;
7901
8120
  document.getElementById(`lnk${g_stateObj.scoreDetail}G`).classList.replace(g_cssObj.button_Default, g_cssObj.button_Setting);
8121
+
8122
+ document.getElementById(`lnkMiniMapRev`).style.display =
8123
+ g_stateObj.scoreDetail === `MiniMap` && g_stateObj.scoreDetailViewFlg ? C_DIS_INHERIT : C_DIS_NONE;
7902
8124
  };
7903
8125
 
7904
8126
  multiAppend(scoreDetail,
@@ -7906,6 +8128,7 @@ const createOptionWindow = _sprite => {
7906
8128
  createScoreDetail(`Density`),
7907
8129
  createScoreDetail(`ToolDif`, false),
7908
8130
  createScoreDetail(`HighScore`, false),
8131
+ createScoreDetail(`MiniMap`, false),
7909
8132
  );
7910
8133
  g_settings.scoreDetails.forEach((sd, j) => {
7911
8134
  scoreDetail.appendChild(
@@ -7941,6 +8164,8 @@ const createOptionWindow = _sprite => {
7941
8164
  if (_resetFlg) {
7942
8165
  g_shortcutObj.option.KeyQ.id = g_settings.scoreDetailCursors[0];
7943
8166
  }
8167
+ document.getElementById(`lnkMiniMapRev`).style.display =
8168
+ g_stateObj.scoreDetail === `MiniMap` && g_stateObj.scoreDetailViewFlg ? C_DIS_INHERIT : C_DIS_NONE;
7944
8169
  };
7945
8170
 
7946
8171
  // ---------------------------------------------------
@@ -8057,6 +8282,7 @@ const createOptionWindow = _sprite => {
8057
8282
  fadeinSlider.value = g_stateObj.fadein;
8058
8283
  lnkFadein.textContent = `${g_stateObj.fadein}${g_lblNameObj.percent}`;
8059
8284
  updateSettingSummary();
8285
+ drawMinimap(g_stateObj.scoreId);
8060
8286
  };
8061
8287
 
8062
8288
  multiAppend(spriteList.fadein,
@@ -8075,6 +8301,7 @@ const createOptionWindow = _sprite => {
8075
8301
  fadeinSlider.addEventListener(`input`, () => {
8076
8302
  g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein, `fadein`);
8077
8303
  updateSettingSummary();
8304
+ drawMinimap(g_stateObj.scoreId);
8078
8305
  }, false);
8079
8306
 
8080
8307
  // ---------------------------------------------------
@@ -12014,6 +12241,7 @@ const getArrowSettings = () => {
12014
12241
  g_workObj.diffList = [];
12015
12242
  g_workObj.mainEndTime = 0;
12016
12243
  g_workObj.currentLifeState = ``;
12244
+ g_errorCache['g_customJsObj.mainEnterFrame'] = [];
12017
12245
 
12018
12246
  const rotateBy = (val, delta) => {
12019
12247
  // numeric
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/04/12 (v46.6.2)
8
+ * Revised : 2026/04/19 (v47.0.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -213,6 +213,8 @@ const updateWindowSiz = () => {
213
213
  difFilter: { x: 0, y: 66, w: 140, h: 204 + g_sHeight - 500, overflow: C_DIS_AUTO, pointerEvents: C_DIS_AUTO },
214
214
  displaySprite: { x: 25, y: 30, w: (g_sWidth - 450) / 2, h: g_limitObj.setLblHeight * 5 },
215
215
  scoreDetail: { x: 20, y: 85, w: (g_sWidth - 500) / 2 + 420, h: 245, visibility: `hidden`, pointerEvents: C_DIS_AUTO },
216
+ detailMiniMapHeader: { x: 110, y: 0, w: (g_sWidth - 500) / 2 + 310, h: 15 },
217
+ detailMiniMapSub: { x: 110, y: 15, w: (g_sWidth - 500) / 2 + 310, h: 230, overflow: C_DIS_AUTO, pointerEvents: C_DIS_AUTO },
216
218
  detailObj: { w: (g_sWidth - 500) / 2 + 420, h: 230, visibility: `hidden` },
217
219
  keyconSprite: { y: 105, h: g_sHeight - 105, overflow: C_DIS_AUTO },
218
220
  loader: { y: g_sHeight - 10, h: 10, backgroundColor: `#333333` },
@@ -513,6 +515,9 @@ const updateWindowSiz = () => {
513
515
  btnSpdCursorR: {
514
516
  x: 100, y: 180, w: 15, h: 20, siz: 12,
515
517
  },
518
+ lnkMiniMapRev: {
519
+ w: g_limitObj.difCoverWidth, h: 20, borderStyle: `solid`,
520
+ },
516
521
 
517
522
  /** ディスプレイ画面 */
518
523
  scMsg: {
@@ -1170,6 +1175,7 @@ const g_stateObj = {
1170
1175
  scoreDetailViewFlg: false,
1171
1176
  scoreDetail: `Speed`,
1172
1177
  settingSummaryVisible: false,
1178
+ miniMapRevFlg: false,
1173
1179
 
1174
1180
  d_stepzone: C_FLG_ON,
1175
1181
  d_judgment: C_FLG_ON,
@@ -1329,7 +1335,7 @@ const g_settings = {
1329
1335
  opacitys: [10, 25, 50, 75, 100],
1330
1336
  opacityNum: 0,
1331
1337
 
1332
- scoreDetailDefs: [`Density`, `Speed`, `ToolDif`, `HighScore`],
1338
+ scoreDetailDefs: [`Density`, `Speed`, `ToolDif`, `HighScore`, `MiniMap`],
1333
1339
  scoreDetails: [],
1334
1340
  scoreDetailCursors: [],
1335
1341
 
@@ -2709,12 +2715,15 @@ const g_shortcutObj = {
2709
2715
  Digit2: { id: `lnkSpeedG` },
2710
2716
  Digit3: { id: `lnkToolDifG` },
2711
2717
  Digit4: { id: `lnkHighScoreG` },
2718
+ Digit5: { id: `lnkMiniMapG` },
2712
2719
  Numpad1: { id: `lnkDensityG` },
2713
2720
  Numpad2: { id: `lnkSpeedG` },
2714
2721
  Numpad3: { id: `lnkToolDifG` },
2715
2722
  Numpad4: { id: `lnkHighScoreG` },
2723
+ Numpad5: { id: `lnkMiniMapG` },
2716
2724
  KeyQ: { id: `lnkDensityG` },
2717
2725
  KeyP: { id: `lnkDifInfo` },
2726
+ KeyX: { id: `lnkMiniMapRev` },
2718
2727
  KeyZ: { id: `btnSave` },
2719
2728
  ControlLeft_KeyC: { id: `` },
2720
2729
  ControlRight_KeyC: { id: `` },
@@ -2745,12 +2754,15 @@ const g_shortcutObj = {
2745
2754
  Digit2: { id: `lnkSpeedG` },
2746
2755
  Digit3: { id: `lnkToolDifG` },
2747
2756
  Digit4: { id: `lnkHighScoreG` },
2757
+ Digit5: { id: `lnkMiniMapG` },
2748
2758
  Numpad1: { id: `lnkDensityG` },
2749
2759
  Numpad2: { id: `lnkSpeedG` },
2750
2760
  Numpad3: { id: `lnkToolDifG` },
2751
2761
  Numpad4: { id: `lnkHighScoreG` },
2762
+ Numpad5: { id: `lnkMiniMapG` },
2752
2763
  KeyQ: { id: `lnkDensityG` },
2753
2764
  KeyP: { id: `lnkDifInfo` },
2765
+ KeyX: { id: `lnkMiniMapRev` },
2754
2766
  ControlLeft_KeyC: { id: `` },
2755
2767
  ControlRight_KeyC: { id: `` },
2756
2768
  KeyC: { id: `lnkHighScore`, reset: true },
@@ -4254,6 +4266,7 @@ const g_lang_msgInfoObj = {
4254
4266
  W_0031: `セーフモード適用中です。ローカルストレージ情報を使わない設定になっています。<br>
4255
4267
  「Data Management」から解除が可能です。(W-0031)`,
4256
4268
  W_0041: `選曲単品モードが有効になっています。<br><a href="{0}">[ 選曲画面へ戻る ]</a>`,
4269
+ W_0051: `カスタムJS実行中にエラーが発生しました。処理は続行します。<br>繰り返し発生する場合はサイト管理者へ連絡してください。`,
4257
4270
 
4258
4271
  E_0011: `アーティスト名が未入力です。(E-0011)`,
4259
4272
  E_0012: `曲名情報が未設定です。(E-0012)<br>
@@ -4301,6 +4314,7 @@ const g_lang_msgInfoObj = {
4301
4314
  The setting is set to not use local storage information <br>
4302
4315
  and can be removed from Data Management. (W-0031)`,
4303
4316
  W_0041: `The single music selection mode is enabled.<br><a href="{0}">[ Return to the original page ]</a>`,
4317
+ W_0051: `An error occurred while executing custom JavaScript. Processing will continue.<br>If this error persists, please contact the site owner.`,
4304
4318
 
4305
4319
  E_0011: `The artist name is not set. (E-0011)`,
4306
4320
  E_0012: `The song title information is not set. (E-0012)<br>
@@ -4431,6 +4445,8 @@ const g_lblNameObj = {
4431
4445
  s_arrow: `Arrow`,
4432
4446
  s_frz: `Frz`,
4433
4447
 
4448
+ s_rev: `MapScroll`,
4449
+
4434
4450
  d_StepZone: `StepZone`,
4435
4451
  d_Judgment: `Judgment`,
4436
4452
  d_FastSlow: `FastSlow`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "46.6.2",
3
+ "version": "47.0.0",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "jsdelivr": "./js/danoni_main.js",