danoniplus 48.5.2 → 49.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
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 48.5.2`;
11
+ const g_version = `Ver 49.0.0`;
12
12
  const g_revisedDate = `2026/06/23`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
@@ -1492,6 +1492,53 @@ const getEmojiForCanvas = _str => {
1492
1492
  return result;
1493
1493
  };
1494
1494
 
1495
+ /**
1496
+ * 複数行に跨る可能性のある文字列を、改行タグ付きの文字列とフォントサイズに変換
1497
+ * @param {string} _targetStr
1498
+ * @param {number} _maxWidth
1499
+ * @param {object} object
1500
+ * @param {string} [object.font=getBasicFont()]
1501
+ * @param {number} [object.maxSiz=14]
1502
+ * @param {number} [object.minSiz=5]
1503
+ * @param {number} [object.maxSizMulti=maxSiz]
1504
+ * @param {string} [object.prefix='']
1505
+ * @param {number} [object.len=30]
1506
+ * @param {string} [object.delim=' ']
1507
+ * @returns {[string, number]}
1508
+ */
1509
+ const getFontSizeMulti = (_targetStr, _maxWidth, { font = getBasicFont(),
1510
+ maxSiz = 14, minSiz = 5, maxSizMulti = maxSiz, prefix = ``, len = 20, delim = ` ` } = {}) => {
1511
+
1512
+ // _targetStr が長い場合のみ、その中身を 2つ に分解する
1513
+ if (_targetStr.length > len) {
1514
+ let breakNum = -1;
1515
+ const halfIndex = Math.floor(_targetStr.length / 2);
1516
+
1517
+ // 文字列の中央から前に向かってスペースを探す
1518
+ for (let j = halfIndex; j > 0; j--) {
1519
+ if (_targetStr[j] === delim) {
1520
+ breakNum = j;
1521
+ break;
1522
+ }
1523
+ }
1524
+ // スペースがなければ、中央(よりやや左)で強制分割
1525
+ if (breakNum === -1) {
1526
+ breakNum = halfIndex;
1527
+ }
1528
+ const isSpace = _targetStr[breakNum] === delim;
1529
+ const firstPart = _targetStr.slice(0, breakNum);
1530
+ const secondPart = _targetStr.slice(isSpace ? breakNum + delim.length : breakNum);
1531
+
1532
+ // 難易度名の中に <br> を仕込む
1533
+ _targetStr = `${firstPart}<br>${secondPart}`;
1534
+ }
1535
+
1536
+ // 3. 最終的な文字列を結合(prefix と難易度名の1つ目の塊が1行目になる)
1537
+ const fullStr = `${prefix}${_targetStr}`;
1538
+
1539
+ return [fullStr, getFontSize2(fullStr, _maxWidth, { font, maxSiz: _targetStr.includes(`<br>`) ? maxSizMulti : maxSiz, minSiz })];
1540
+ };
1541
+
1495
1542
  /**
1496
1543
  * 指定した横幅に合ったフォントサイズを取得
1497
1544
  * @param {string} _str
@@ -1502,8 +1549,16 @@ const getEmojiForCanvas = _str => {
1502
1549
  * @returns {number}
1503
1550
  */
1504
1551
  const getFontSize2 = (_str, _maxWidth, { font = getBasicFont(), maxSiz = 14, minSiz = 5 } = {}) => {
1552
+ // 文字列を改行で分割(null/undefined 対策も含む)
1553
+ const lines = _str ? _str.split('<br>') : [];
1554
+ if (lines.length === 0) return maxSiz;
1555
+
1556
+ // 大きいサイズから順に試す
1505
1557
  for (let siz = maxSiz; siz >= minSiz; siz--) {
1506
- if (_maxWidth >= getStrWidth(getLongestStr(_str?.split(`<br>`)), siz, font)) {
1558
+ // すべての行が _maxWidth 以内に収まるかチェック
1559
+ const isFitAllLines = lines.every(line => _maxWidth >= getStrWidth(line, siz, font));
1560
+
1561
+ if (isFitAllLines) {
1507
1562
  return siz;
1508
1563
  }
1509
1564
  }
@@ -7179,12 +7234,21 @@ const makeDifList = (_difList, _targetKey = ``) => {
7179
7234
  g_headerObj.viewLists.forEach(j => {
7180
7235
  const keyLabel = g_headerObj.keyLabels[j];
7181
7236
  if (_targetKey === `` || keyLabel === _targetKey) {
7182
- let text = `${getKeyName(keyLabel)} / ${g_headerObj.difLabels[j]}`;
7237
+
7238
+ // 譜面名の表示
7239
+ const prefix = `${getKeyName(keyLabel)} / `;
7240
+ let text = `${g_headerObj.difLabels[j]}`;
7183
7241
  if (g_headerObj.makerView) {
7184
7242
  text += ` (${g_headerObj.creatorNames[j]})`;
7185
7243
  }
7186
- _difList.appendChild(makeDifLblCssButton(`dif${k}`, text, k, () => nextDifficulty(j - g_stateObj.scoreId),
7187
- { btnStyle: (j === g_stateObj.scoreId ? `Setting` : `Default`) }));
7244
+ // キー種と譜面名に分割し、譜面名が長すぎる場合は二段に分割して表示
7245
+ const [difText, difSiz] = getFontSizeMulti(text, g_limitObj.difSelectorWidth, {
7246
+ maxSiz: g_limitObj.difSelectorSiz, maxSizMulti: 9, prefix, len: 30,
7247
+ })
7248
+ _difList.appendChild(makeDifLblCssButton(`dif${k}`, difText, k, () => nextDifficulty(j - g_stateObj.scoreId), {
7249
+ btnStyle: (j === g_stateObj.scoreId ? `Setting` : `Default`), siz: difSiz,
7250
+ }));
7251
+ document.getElementById(`dif${k}`).style.lineHeight = `9px`;
7188
7252
  if (j === g_stateObj.scoreId) {
7189
7253
  pos = k + 6.5 * (g_sHeight - 239) / 261;
7190
7254
  curk = k;
@@ -8136,9 +8200,14 @@ const setDifficulty = (_initFlg) => {
8136
8200
  const difWidth = parseFloat(lnkDifficulty.style.width) - 20;
8137
8201
  const transKeyName = getTransKeyName();
8138
8202
  const keyUnitName = getStgDetailName(getKeyUnitName(g_keyObj.currentKey));
8139
- const difNames = [`${getKeyName(g_keyObj.currentKey)}${transKeyName} ${keyUnitName} / ${g_headerObj.difLabels[g_stateObj.scoreId]}`];
8140
- lnkDifficulty.style.fontSize = wUnit(getFontSize2(difNames[0], difWidth, { maxSiz: g_limitObj.setLblSiz }));
8141
8203
 
8204
+ const prefix = `${getKeyName(g_keyObj.currentKey)}${transKeyName} ${keyUnitName} / `;
8205
+ let difLabel = `${g_headerObj.difLabels[g_stateObj.scoreId]}`;
8206
+
8207
+ const [difName, difSiz] = getFontSizeMulti(difLabel, difWidth, { maxSiz: g_limitObj.setLblSiz, prefix });
8208
+ lnkDifficulty.style.fontSize = wUnit(difSiz);
8209
+
8210
+ const difNames = [difName];
8142
8211
  if (g_headerObj.makerView) {
8143
8212
  difNames.push(`(${g_headerObj.creatorNames[g_stateObj.scoreId]})`);
8144
8213
  difNames.forEach((difName, j) => {
@@ -8998,6 +9067,9 @@ const setExcessive = (_btn, _val) => {
8998
9067
  const getKeyCtrl = (_localStorage, _extraKeyName = ``) => {
8999
9068
  g_keyObj.storagePtn = _localStorage[`keyCtrlPtn${_extraKeyName}`];
9000
9069
  const basePtn = `${g_keyObj.currentKey}_${g_keyObj.storagePtn}`;
9070
+ if (g_keyObj[`keyCtrl${basePtn}`] === undefined || hasVal(g_keyObj[`transKey${basePtn}`])) {
9071
+ return;
9072
+ }
9001
9073
  const baseKeyNum = g_keyObj[`${g_keyObj.defaultProp}${basePtn}`].length;
9002
9074
 
9003
9075
  if (_localStorage[`keyCtrl${_extraKeyName}`]?.[0].length > 0) {
@@ -10098,6 +10170,11 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10098
10170
  // 譜面初期情報ロード許可フラグ
10099
10171
  g_canLoadDifInfoFlg = false;
10100
10172
 
10173
+ if (_initFlg) {
10174
+ g_stateObj.keyLockFlg = false;
10175
+ g_stateObj.kbPreviewFlg = false;
10176
+ }
10177
+
10101
10178
  multiAppend(divRoot,
10102
10179
 
10103
10180
  // キーコンフィグ画面タイトル
@@ -10105,8 +10182,7 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10105
10182
  `<div class="settings_Title">${g_lblNameObj.key}</div><div class="settings_Title2">${g_lblNameObj.config}</div>`
10106
10183
  .replace(/[\t\n]/g, ``), 0, 15, g_cssObj.flex_centering),
10107
10184
 
10108
- createDescDiv(`kcDesc`, g_lblNameObj.kcDesc.split(`{0}`).join(g_kCd[C_KEY_RETRY])
10109
- .split(`{1}:`).join(g_isMac ? `` : `Delete:`)),
10185
+ createDescDiv(`kcDesc`, getKcDescMsg()),
10110
10186
 
10111
10187
  createDescDiv(`kcShuffleDesc`,
10112
10188
  g_headerObj.shuffleUse && g_settings.shuffles.filter(val => val.endsWith(`+`)).length > 0
@@ -10236,11 +10312,24 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10236
10312
  * @param {number} _scrollNum
10237
10313
  */
10238
10314
  const changeTmpColor = (_j, _scrollNum = 1) => {
10239
- changeTmpData(`color`, g_headerObj.setColor.length, _j, _scrollNum);
10240
- const arrowColor = getKeyConfigColor(_j, g_keyObj[`color${keyCtrlPtn}`][_j]);
10241
- $id(`arrow${_j}`).background = arrowColor;
10242
- $id(`arrowShadow${_j}`).background = getShadowColor(g_keyObj[`color${keyCtrlPtn}`][_j], arrowColor);
10315
+ const changeTmpOneColor = _idx => {
10316
+ changeTmpData(`color`, g_headerObj.setColor.length, _idx, _scrollNum);
10317
+ const arrowColor = getKeyConfigColor(_idx, g_keyObj[`color${keyCtrlPtn}`][_idx]);
10318
+ $id(`arrow${_idx}`).background = arrowColor;
10319
+ $id(`arrowShadow${_idx}`).background = getShadowColor(g_keyObj[`color${keyCtrlPtn}`][_idx], arrowColor);
10320
+ };
10243
10321
 
10322
+ if (g_stateObj.keyLockFlg && keyIsShift()) {
10323
+ const tmpList = [];
10324
+ g_keyObj[`color${keyCtrlPtn}`].forEach((val, idx) => {
10325
+ if (val === g_keyObj[`color${keyCtrlPtn}`][_j]) {
10326
+ tmpList.push(idx);
10327
+ }
10328
+ });
10329
+ tmpList.forEach(idx => changeTmpOneColor(idx));
10330
+ } else {
10331
+ changeTmpOneColor(_j);
10332
+ }
10244
10333
  adjustScrollPoint(parseFloat($id(`arrow${_j}`).left));
10245
10334
  };
10246
10335
 
@@ -10250,10 +10339,23 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10250
10339
  * @param {number} _scrollNum
10251
10340
  */
10252
10341
  const changeTmpShuffleNum = (_j, _scrollNum = 1) => {
10253
- const tmpShuffle = changeTmpData(`shuffle`, g_keyObj[`keyCtrl${keyCtrlPtn}`].length - 1, _j, _scrollNum);
10254
- document.getElementById(`sArrow${_j}`).textContent = tmpShuffle + 1;
10342
+ const changeTmpOneShuffle = _idx => {
10343
+ const tmpShuffle = changeTmpData(`shuffle`, g_keyObj[`keyCtrl${keyCtrlPtn}`].length - 1, _idx, _scrollNum);
10344
+ document.getElementById(`sArrow${_idx}`).textContent = tmpShuffle + 1;
10345
+ changeShuffleConfigColor(keyCtrlPtn, g_keyObj[`shuffle${keyCtrlPtn}_${g_keycons.shuffleGroupNum}`][_idx], _idx);
10346
+ };
10255
10347
 
10256
- changeShuffleConfigColor(keyCtrlPtn, g_keyObj[`shuffle${keyCtrlPtn}_${g_keycons.shuffleGroupNum}`][_j], _j);
10348
+ if (g_stateObj.keyLockFlg && keyIsShift()) {
10349
+ const tmpList = [];
10350
+ g_keyObj[`shuffle${keyCtrlPtn}`].forEach((val, idx) => {
10351
+ if (val === g_keyObj[`shuffle${keyCtrlPtn}`][_j]) {
10352
+ tmpList.push(idx);
10353
+ }
10354
+ });
10355
+ tmpList.forEach(idx => changeTmpOneShuffle(idx));
10356
+ } else {
10357
+ changeTmpOneShuffle(_j);
10358
+ }
10257
10359
  adjustScrollPoint(parseFloat($id(`arrow${_j}`).left));
10258
10360
  };
10259
10361
 
@@ -10941,17 +11043,30 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10941
11043
  }
10942
11044
  }, g_lblPosObj.btnKcReset, g_cssObj.button_Reset),
10943
11045
 
11046
+ createCss2Button(`btnKeyLock`, getKeyLockName(), () => {
11047
+ g_stateObj.keyLockFlg = !g_stateObj.keyLockFlg;
11048
+ makeInfoWindow(g_msgInfoObj.I_0012.split(`{0}`).join(boolToSwitch(!g_stateObj.keyLockFlg)), `leftToRightFade`);
11049
+ toggleKcDesc();
11050
+ }, g_lblPosObj.btnKcKeyLock, g_cssObj.button_Mini),
11051
+
10944
11052
  // プレイ開始
10945
11053
  makePlayButton(() => loadMusic())
10946
11054
  );
11055
+ toggleKcDesc();
10947
11056
 
10948
11057
  // キーボード押下時処理
10949
11058
  setShortcutEvent(g_currentPage, (kbCode) => {
11059
+ const C_KEY_ESCAPE = 27;
11060
+ const C_KEY_IME = 229;
10950
11061
  const keyCdObj = document.getElementById(`keycon${g_currentj}_${g_currentk}`);
10951
11062
  let setKey = g_kCdN.findIndex(kCd => kCd === kbCode);
10952
11063
 
10953
- const C_KEY_ESCAPE = 27;
10954
- const C_KEY_IME = 229;
11064
+ if (g_stateObj.keyLockFlg) {
11065
+ if (setKey === C_KEY_ESCAPE) {
11066
+ btnBack.click();
11067
+ }
11068
+ return;
11069
+ }
10955
11070
 
10956
11071
  // 全角切替、BackSpace、Deleteキー、Escキーは割り当て禁止
10957
11072
  // また、直前と同じキーを押した場合(BackSpaceを除く)はキー操作を無効にする
@@ -11032,11 +11147,33 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
11032
11147
  })
11033
11148
  );
11034
11149
 
11150
+ if (g_stateObj.kbPreviewFlg) {
11151
+ btnKbPreview.click();
11152
+ }
11153
+
11035
11154
  safeExecuteCustomHooks(`g_skinJsObj.keyconfig`, g_skinJsObj.keyconfig);
11036
11155
  document.onkeyup = evt => commonKeyUp(evt);
11037
11156
  document.oncontextmenu = () => false;
11038
11157
  };
11039
11158
 
11159
+ const toggleKcDesc = () => {
11160
+ if (document.getElementById(`kcDesc`) !== null) {
11161
+ kcDesc.textContent = getKcDescMsg();
11162
+ kcDesc.style.fontSize = wUnit(getFontSize2(kcDesc.textContent, g_lblPosObj.kcDesc.w, { maxSiz: g_limitObj.mainSiz }));
11163
+ kcDesc.classList.remove(g_cssObj.title_base, g_cssObj.keyconfig_Defaultkey);
11164
+ kcDesc.classList.add(g_stateObj.keyLockFlg ? g_cssObj.keyconfig_Defaultkey : g_cssObj.title_base);
11165
+ btnKeyLock.innerHTML = getKeyLockName();
11166
+ }
11167
+ };
11168
+
11169
+ const getKcDescMsg = () =>
11170
+ g_stateObj.keyLockFlg
11171
+ ? g_lblNameObj.kcNonDesc
11172
+ : g_lblNameObj.kcDesc.split(`{0}`).join(g_kCd[C_KEY_RETRY]).split(`{1}:`).join(g_isMac ? `` : `Delete:`);
11173
+
11174
+ const getKeyLockName = () =>
11175
+ `${g_lblNameObj.b_keyLock}${g_stateObj.keyLockFlg ? g_emojiObj.locked : g_emojiObj.unlocked}`;
11176
+
11040
11177
  /**
11041
11178
  * キーボードレイアウトプレビュー(Canvas版)
11042
11179
  *
@@ -11612,7 +11749,9 @@ const keyconfigKeyboardPreview = (() => {
11612
11749
 
11613
11750
  _state.visible = !_state.visible;
11614
11751
  area.style.display = _state.visible ? `block` : `none`;
11615
-
11752
+ g_stateObj.kbPreviewFlg = _state.visible;
11753
+ g_stateObj.keyLockFlg = _state.visible;
11754
+ toggleKcDesc();
11616
11755
  if (_state.visible) refresh();
11617
11756
  };
11618
11757
 
@@ -16725,7 +16864,8 @@ const resultInit = () => {
16725
16864
  makeCssResultPlayData(`lblMusicData`, dataRX, g_cssObj.result_style, 0, mTitleForView[0]),
16726
16865
  makeCssResultPlayData(`lblMusicData2`, dataRX, g_cssObj.result_style, 1, mTitleForView[1]),
16727
16866
  makeCssResultPlayData(`lblDifficulty`, lblRX, g_cssObj.result_lbl, 2, g_lblNameObj.rt_Difficulty, C_ALIGN_LEFT),
16728
- makeCssResultPlayData(`lblDifData`, dataRX, g_cssObj.result_style, 2, settingData.difData),
16867
+ makeCssResultPlayData(`lblDifData`, dataRX, g_cssObj.result_style, 2, settingData.difData, C_ALIGN_CENTER,
16868
+ { siz: getFontSize2(settingData.difData, 350) }),
16729
16869
  makeCssResultPlayData(`lblStyle`, lblRX, g_cssObj.result_lbl, 3, g_lblNameObj.rt_Style, C_ALIGN_LEFT),
16730
16870
  makeCssResultPlayData(`lblStyleData`, dataRX, g_cssObj.result_style, 3, settingData.playStyleData),
16731
16871
  makeCssResultPlayData(`lblDisplay`, lblRX, g_cssObj.result_lbl, 4, g_lblNameObj.rt_Display, C_ALIGN_LEFT),
@@ -16734,21 +16874,9 @@ const resultInit = () => {
16734
16874
  );
16735
16875
 
16736
16876
  // 設定項目が多い場合に2行に分解して表示する処理
16737
- let playStyleBreakNum = lblStyleData.textContent.length;
16738
- if (lblStyleData.textContent.length > 60) {
16739
- for (let j = Math.floor(lblStyleData.textContent.length / 2); j > 0; j--) {
16740
- if (lblStyleData.textContent[j] === `,`) {
16741
- playStyleBreakNum = j + 2;
16742
- break;
16743
- }
16744
- }
16745
- lblStyleData.style.top = `${parseFloat(lblStyleData.style.top) - 3}px`;
16746
- lblStyleData.innerHTML = `${lblStyleData.textContent.slice(0, playStyleBreakNum)}<br>` +
16747
- `${lblStyleData.textContent.slice(playStyleBreakNum)}`;
16748
- lblStyleData.style.fontSize = `${getFontSize2(lblStyleData.textContent.slice(0, playStyleBreakNum), 350, { maxSiz: 10 })}px`;
16749
- } else {
16750
- lblStyleData.style.fontSize = `${getFontSize2(lblStyleData.textContent, 350)}px`;
16751
- }
16877
+ const [styleStr, styleSiz] = getFontSizeMulti(settingData.playStyleData, 350, { maxSizMulti: 10, len: 60 });
16878
+ lblStyleData.innerHTML = styleStr;
16879
+ lblStyleData.style.fontSize = wUnit(styleSiz);
16752
16880
 
16753
16881
  /**
16754
16882
  * キャラクタ、スコア描画のID共通部、色CSS名、スコア変数名
@@ -17182,6 +17310,7 @@ const resultInit = () => {
17182
17310
  const artistName = g_headerObj.artistNames[g_headerObj.musicNos[g_stateObj.scoreId]] || g_headerObj.artistName;
17183
17311
  const logicalWidth = 400;
17184
17312
  const logicalHeight = g_sHeight - 90;
17313
+ const flapWidth = 370;
17185
17314
 
17186
17315
  canvas.id = `resultImage`;
17187
17316
  canvas.width = logicalWidth * g_dpr;
@@ -17210,13 +17339,14 @@ const resultInit = () => {
17210
17339
  drawText(unEscapeHtml(mTitleForView[1]), { hy: 2 });
17211
17340
  drawText(`${getEmojiForCanvas(g_emojiObj.memo)} ${unEscapeHtml(g_headerObj.tuning)} / ${getEmojiForCanvas(g_emojiObj.musical)} ${unEscapeHtml(artistName)}`,
17212
17341
  { hy: mTitleForView[1] !== `` ? 3 : 2, siz: 12 });
17213
- drawText(unEscapeHtml(settingData.difDataForImage), { hy: 4 });
17342
+ drawText(unEscapeHtml(settingData.difDataForImage), { hy: 4, siz: getFontSize2(settingData.difDataForImage, flapWidth) });
17214
17343
 
17215
17344
  if (settingData.playStyleData.length > 60) {
17216
- drawText(settingData.playStyleData.slice(0, playStyleBreakNum), { hy: 5, siz: getFontSize2(settingData.playStyleData.slice(0, playStyleBreakNum), 370) });
17217
- drawText(settingData.playStyleData.slice(playStyleBreakNum), { hy: 6, siz: getFontSize2(settingData.playStyleData.slice(playStyleBreakNum), 370) });
17345
+ const strs = styleStr.split(`<br>`);
17346
+ drawText(strs[0], { hy: 5, siz: getFontSize2(strs[0], flapWidth) });
17347
+ drawText(strs[1], { hy: 6, siz: getFontSize2(strs[1], flapWidth) });
17218
17348
  } else {
17219
- drawText(settingData.playStyleData, { hy: 5, siz: getFontSize2(settingData.playStyleData, 370, { maxSiz: 15 }) });
17349
+ drawText(settingData.playStyleData, { hy: 5, siz: getFontSize2(settingData.playStyleData, flapWidth, { maxSiz: 15 }) });
17220
17350
  }
17221
17351
  Object.keys(jdgScoreObj).forEach(score => {
17222
17352
  drawText(g_lblNameObj[`j_${score}`], { hy: 7 + jdgScoreObj[score].pos, color: jdgScoreObj[score].dfColor });
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/06/14 (v48.5.0)
8
+ * Revised : 2026/06/23 (v49.0.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -640,6 +640,10 @@ const updateWindowSiz = () => {
640
640
  x: g_btnX() + Math.floor((g_btnWidth() - 80) / 2), y: 3, w: 80, h: 18, siz: 11,
641
641
  title: g_msgObj.displayPreview,
642
642
  },
643
+ btnKcKeyLock: {
644
+ x: g_btnX() + Math.floor((g_btnWidth() - 80) / 2) + 90, y: 3, w: 80, h: 18, siz: 11,
645
+ title: g_msgObj.keyLock,
646
+ },
643
647
 
644
648
  btnKcBack: {
645
649
  x: g_btnX(1 / 3), y: g_sHeight - 75,
@@ -1062,6 +1066,8 @@ const g_emojiObj = {
1062
1066
  memo: `&#x1f4dd;`, // メモ (memo)
1063
1067
  musical: `&#x1f3b5;`, // 音符 (musical note)
1064
1068
  camera: `&#x1f4f7;`, // カメラ (camera)
1069
+ locked: `&#x1f512;`, // 鍵 (locked)
1070
+ unlocked: `&#x1f513;`, // 鍵開 (unlocked)
1065
1071
  };
1066
1072
 
1067
1073
  /** 設定・オプション画面用共通 */
@@ -1284,6 +1290,9 @@ const g_stateObj = {
1284
1290
  rotateEnabled: true,
1285
1291
  flatStepHeight: C_ARW_WIDTH,
1286
1292
 
1293
+ keyLockFlg: false,
1294
+ kbPreviewFlg: false,
1295
+
1287
1296
  dm_environment: C_FLG_OFF,
1288
1297
  dm_highscores: C_FLG_OFF,
1289
1298
  dm_customKey: C_FLG_OFF,
@@ -3597,7 +3606,7 @@ const g_keyObj = {
3597
3606
  // - _0 の数字部分をカウントアップすることで実現できる。
3598
3607
  keyCtrl5_1: [[`Space`], [`Left`], [`Down`], [`Up`], [`Right`]],
3599
3608
  keyCtrl7_1: [[`S`], [`E`], [`F`], [`Space`, `G`, `H`], [`J`], [`I`], [`L`]],
3600
- keyCtrl8_1: [[`Enter`], [`S`], [`D`], [`F`], [`Space`], [`J`], [`K`], [`L`]],
3609
+ keyCtrl8_1: [[`Tab`], [`S`], [`D`], [`F`], [`Space`], [`J`], [`K`], [`L`]],
3601
3610
  keyCtrl9A_1: [[`S`], [`D`], [`E`, `R`], [`F`], [`Space`], [`Left`], [`Down`], [`Up`], [`Right`]],
3602
3611
  keyCtrl9i_1: [[`A`], [`S`], [`D`], [`F`], [`Space`], [`Left`], [`Down`], [`Up`], [`Right`]],
3603
3612
  keyCtrl11_1: [[`S`], [`D`], [`F`], [`Space`], [`J`], [`K`], [`L`], [`Left`], [`Down`], [`Up`], [`Right`]],
@@ -3658,7 +3667,7 @@ const g_keyObj = {
3658
3667
  // ショートカットキーコード
3659
3668
  keyRetry: 8, // 8: Backspace
3660
3669
  keyRetry8_0: 9, // 9: Tab
3661
- keyRetry8_1: 9,
3670
+ keyRetry8_1: 8, // 8: Backspace
3662
3671
  keyRetry11j_0: 123, // 123: F12
3663
3672
 
3664
3673
  keyTitleBack: 46, // 46: Delete
@@ -3666,9 +3675,8 @@ const g_keyObj = {
3666
3675
 
3667
3676
  // 別キー
3668
3677
  transKey8_2: '12',
3669
- transKey15A_1: '',
3670
- transKey15B_0: '',
3671
- transKey15B_1: '',
3678
+ transKey15A_1: '15B', // 15A/15Bはキーパターンをコピーして生成するため、transKeyを明示的に指定
3679
+ transKey15B_0: '', // 15A/15Bはキーパターンをコピーして生成するため、transKeyを明示的に指定
3672
3680
 
3673
3681
  // キー置換用(ParaFla版との互換)
3674
3682
  keyTransPattern: {
@@ -4461,6 +4469,7 @@ const g_lang_msgInfoObj = {
4461
4469
  I_0006: `ローカルストレージ情報をクリップボードにコピーしました!`,
4462
4470
  I_0007: `オブジェクト情報をクリップボードにコピーしました!`,
4463
4471
  I_0011: `指定した部分キーが未定義のため、画面表示できません。設定を見直してください。`,
4472
+ I_0012: `キー割り当てモードを{0}に変更しました。`,
4464
4473
  },
4465
4474
  En: {
4466
4475
  W_0001: `Your browser is not guaranteed to work.<br>
@@ -4511,6 +4520,7 @@ const g_lang_msgInfoObj = {
4511
4520
  I_0006: `Local storage information copied to clipboard!`,
4512
4521
  I_0007: `Object information copied to clipboard!`,
4513
4522
  I_0011: `The specified partial key is undefined and cannot be displayed on the screen. Please review your settings.`,
4523
+ I_0012: `Key assignment mode changed to {0}.`,
4514
4524
  },
4515
4525
  };
4516
4526
 
@@ -4566,6 +4576,7 @@ const g_lblNameObj = {
4566
4576
  b_close: `Close`,
4567
4577
  b_cReset: `Reset`,
4568
4578
  b_precond: `Precondition`,
4579
+ b_keyLock: `KeyLock`,
4569
4580
 
4570
4581
  Difficulty: `Difficulty`,
4571
4582
  Speed: `Speed`,
@@ -4818,6 +4829,7 @@ const g_lang_lblNameObj = {
4818
4829
  dataDeleteONDesc: `セーフモード適用中はデータ消去は行えません。変更するにはセーフモードを解除してください`,
4819
4830
 
4820
4831
  kcDesc: `[{0}:スキップ / {1}:(代替キーのみ)キー無効化]`,
4832
+ kcNonDesc: `キー割り当て無効化中です。解除するには「KeyLock」を押してください`,
4821
4833
  kcShuffleDesc: `番号をクリックでシャッフルグループ、矢印をクリックでカラーグループを変更`,
4822
4834
  kcNoShuffleDesc: `矢印をクリックでカラーグループを変更`,
4823
4835
  sdDesc: `[クリックでON/OFFを切替、灰色でOFF]`,
@@ -4881,6 +4893,7 @@ const g_lang_lblNameObj = {
4881
4893
  dataDeleteONDesc: `Data erasure cannot be performed while safe mode is applied. <br>Please deactivate the safe mode to change the data.`,
4882
4894
 
4883
4895
  kcDesc: `[{0}:Skip / {1}:Key invalidation (Alternate keys only)]`,
4896
+ kcNonDesc: `Key assignments are currently disabled. Press "KeyLock" to disable this setting.`,
4884
4897
  kcShuffleDesc: `Click the number to change the shuffle group, and click the arrow to change the color.`,
4885
4898
  kcNoShuffleDesc: `Click the arrow to change the color group.`,
4886
4899
  sdDesc: `[Click to switch, gray to OFF]`,
@@ -5052,6 +5065,7 @@ const g_lang_msgObj = {
5052
5065
  stepRtnGroup: `矢印などノーツの種類、回転に関するパターンを切り替えます。\nあらかじめ設定されている場合のみ変更可能です。`,
5053
5066
  kcReset: `対応するキーの割り当てを元に戻します。`,
5054
5067
  kcPreview: `キーボードレイアウトのプレビューを表示/非表示します。`,
5068
+ keyLock: `キー割り当ての有効/無効を切り替えます。\n無効化時はシフトキー+番号/矢印クリックで同一グループの矢印群をまとめてグループ変更できます。`,
5055
5069
 
5056
5070
  pickArrow: `色番号ごとの矢印色(枠、塗りつぶし)、通常時のフリーズアロー色(枠、帯)を\nカラーピッカーから選んで変更できます。`,
5057
5071
  pickColorR: `設定する矢印色の種類を切り替えます。`,
@@ -5159,6 +5173,7 @@ const g_lang_msgObj = {
5159
5173
  stepRtnGroup: `Switches the type of notes, such as arrows, and the pattern regarding rotation.\nThis can only be changed if it has been set in advance.`,
5160
5174
  kcReset: `Restores the corresponding key assignments.`,
5161
5175
  kcPreview: `Show/hide the preview of the keyboard layout.`,
5176
+ keyLock: `Toggles key assignments on or off. \nWhen disabled, you can use the Shift key plus a number or arrow key \nto change the group of all arrows in the same group at once.`,
5162
5177
 
5163
5178
  pickArrow: `Change the frame or fill of arrow color and the frame or bar of normal freeze-arrow color\nfor each color number from the color picker.`,
5164
5179
  pickColorR: `Switches the arrow color type to be set.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "48.5.2",
3
+ "version": "49.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",