danoniplus 51.3.0 → 51.4.1

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/10/10
7
+ * Revised : 2026/10/20
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 51.3.0`;
12
- const g_revisedDate = `2026/10/10`;
11
+ const g_version = `Ver 51.4.1`;
12
+ const g_revisedDate = `2026/10/20`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/10/10 (v51.3.0)
8
+ * Revised : 2026/10/20 (v51.4.1)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -207,7 +207,7 @@ const updateWindowSiz = () => {
207
207
  keyTitleSprite: { x: g_btnX(1 / 4), y: g_sHeight / 2 - 5, w: g_btnWidth(1 / 2), h: 16 },
208
208
  mSelectTitleSprite: { x: g_btnX(), y: 0, w: g_btnWidth(), h: g_sHeight, clipPath: `inset(12% 0 8% 0)` },
209
209
  settingSumSprite: { x: g_btnX() + 25, y: g_sHeight - 200, w: g_btnWidth() - 50, h: 100, pointerEvents: C_DIS_AUTO, overflow: C_DIS_AUTO },
210
- optionSprite: { x: (g_sWidth - 450) / 2, y: 65, w: 450, h: 325 },
210
+ optionSprite: { x: (g_sWidth - 450) / 2, y: 65, w: g_sWidth / 2 + 225, h: 325 },
211
211
  dataSprite: { x: g_btnX() + (g_sWidth - Math.max(g_sWidth - 100, 450)) / 2, y: 65, w: Math.max(g_sWidth - 100, 450), h: 325 },
212
212
  keyListSprite: { x: 0, y: g_limitObj.setLblHeight * 7.5 + 40, w: 150, h: g_sHeight - 380, overflow: C_DIS_AUTO },
213
213
  difList: { x: 165, y: 60, w: 280, h: 270 + g_sHeight - 500, overflow: C_DIS_AUTO, pointerEvents: C_DIS_AUTO },
@@ -1406,7 +1406,7 @@ const g_settings = {
1406
1406
  },
1407
1407
  environments: [`adjustment`, `volume`, `colorType`, `appearance`, `opacity`, `hitPosition`,
1408
1408
  `arrowJdgX`, `arrowJdgY`, `frzJdgX`, `frzJdgY`, `shortcutX`, `shortcutY`],
1409
- keyStorages: [`reverse`, `keyCtrl`, `keyCtrlPtn`, `shuffle`, `color`, `stepRtn`],
1409
+ keyStorages: [`reverse`, `keyCtrl`, `keyCtrlPtn`, `shuffle`, `color`, `stepRtn`, `shortcut`],
1410
1410
  colorStorages: [`setColor`, `setShadowColor`, `frzColor`, `frzShadowColor`],
1411
1411
 
1412
1412
  mSelectableTerms: 3,
@@ -1551,6 +1551,12 @@ g_settings.volumeNum = g_settings.volumes.length - 1;
1551
1551
  g_settings.bgmVolumeNum = roundZero(g_settings.volumes.findIndex(v => v === g_stateObj.bgmVolume));
1552
1552
  g_settings.opacityNum = g_settings.opacitys.length - 1;
1553
1553
 
1554
+ const g_settingWindowTitleKeys = new Map([
1555
+ [optionInit, `Option`],
1556
+ [settingsDisplayInit, `Display`],
1557
+ [exSettingInit, `Settings`],
1558
+ ]);
1559
+
1554
1560
  /**
1555
1561
  * 設定画面間移動
1556
1562
  */
@@ -2916,7 +2922,7 @@ const g_shortcutObj = {
2916
2922
  NumpadEnter: { id: `btnPlay` },
2917
2923
  ShiftLeft_Tab: { id: `btnBack` },
2918
2924
  ShiftRight_Tab: { id: `btnBack` },
2919
- Tab: { id: `btnDisplay` },
2925
+ Tab: { id: `btnNextSetting` },
2920
2926
  KeyU: { id: `btnSettingSummary` },
2921
2927
  },
2922
2928
  difSelector: {
@@ -2951,7 +2957,7 @@ const g_shortcutObj = {
2951
2957
  NumpadEnter: { id: `lnkDifficulty` },
2952
2958
  ShiftLeft_Tab: { id: `btnBack` },
2953
2959
  ShiftRight_Tab: { id: `btnBack` },
2954
- Tab: { id: `btnDisplay` },
2960
+ Tab: { id: `btnNextSetting` },
2955
2961
  KeyU: { id: `btnSettingSummary` },
2956
2962
  },
2957
2963
  scoreDetail: {
@@ -3000,7 +3006,7 @@ const g_shortcutObj = {
3000
3006
  NumpadEnter: { id: `btnPlay` },
3001
3007
  ShiftLeft_Tab: { id: `btnBack` },
3002
3008
  ShiftRight_Tab: { id: `btnBack` },
3003
- Tab: { id: `btnDisplay` },
3009
+ Tab: { id: `btnNextSetting` },
3004
3010
  KeyU: { id: `btnSettingSummary` },
3005
3011
  },
3006
3012
  settingsDisplay: {
@@ -3097,7 +3103,7 @@ const g_shortcutObj = {
3097
3103
  NumpadEnter: { id: `btnPlay` },
3098
3104
  ShiftLeft_Tab: { id: `btnBack` },
3099
3105
  ShiftRight_Tab: { id: `btnBack` },
3100
- Tab: { id: `btnSettings` },
3106
+ Tab: { id: `btnNextSetting` },
3101
3107
  KeyU: { id: `btnSettingSummary` },
3102
3108
  },
3103
3109
  displayPreview: {
@@ -3116,7 +3122,7 @@ const g_shortcutObj = {
3116
3122
  NumpadEnter: { id: `btnPlay` },
3117
3123
  ShiftLeft_Tab: { id: `btnBack` },
3118
3124
  ShiftRight_Tab: { id: `btnBack` },
3119
- Tab: { id: `btnSettings` },
3125
+ Tab: { id: `btnNextSetting` },
3120
3126
  },
3121
3127
  exSetting: {
3122
3128
  ShiftLeft_KeyP: { id: `lnkPlayWindowL` },
@@ -3157,7 +3163,7 @@ const g_shortcutObj = {
3157
3163
  Enter: { id: `btnPlay` },
3158
3164
  ShiftLeft_Tab: { id: `btnBack` },
3159
3165
  ShiftRight_Tab: { id: `btnBack` },
3160
- Tab: { id: `btnexSetting` },
3166
+ Tab: { id: `btnNextSetting` },
3161
3167
  KeyU: { id: `btnSettingSummary` },
3162
3168
  },
3163
3169
  keyConfig: {
@@ -3220,11 +3226,11 @@ const g_btnPatterns = {
3220
3226
  title: { Start: 0, Comment: -10, MusicSelectRandom: -10 },
3221
3227
  dataMgt: { Back: 0, Environment: -35, Highscores: -35, CustomKey: -35, Others: -35 },
3222
3228
  precondition: { Back: 0 },
3223
- option: { Back: 0, KeyConfig: 0, Play: 0, Display: -5, Save: -10, Graph: -25, SettingSummary: -10 },
3229
+ option: { Back: 0, KeyConfig: 0, Play: 0, NextSetting: -5, Save: -10, Graph: -25, SettingSummary: -10 },
3224
3230
  difSelector: {},
3225
3231
  scoreDetail: {},
3226
- settingsDisplay: { Back: 0, KeyConfig: 0, Play: 0, Save: -10, Settings: -5, SettingSummary: -10 },
3227
- exSetting: { Back: 0, KeyConfig: 0, Play: 0, exSetting: -5, Save: -10, SettingSummary: -10 },
3232
+ settingsDisplay: { Back: 0, KeyConfig: 0, Play: 0, Save: -10, NextSetting: -5, SettingSummary: -10 },
3233
+ exSetting: { Back: 0, KeyConfig: 0, Play: 0, NextSetting: -5, Save: -10, SettingSummary: -10 },
3228
3234
  loadingIos: { Play: 0 },
3229
3235
  keyConfig: { Back: -3, Play: 0 },
3230
3236
  result: { Back: -5, Copy: -5, Tweet: -5, Gitter: -5, Retry: 0, GaugeTransition: -15 },
@@ -4658,6 +4664,7 @@ const g_lblNameObj = {
4658
4664
  b_cReset: `Reset`,
4659
4665
  b_precond: `Precondition`,
4660
4666
  b_keyLock: `KeyLock`,
4667
+ b_kbLane: `Group Color`,
4661
4668
 
4662
4669
  Difficulty: `Difficulty`,
4663
4670
  Speed: `Speed`,
@@ -5090,6 +5097,7 @@ const g_lang_msgObj = {
5090
5097
 
5091
5098
  graph: `譜面密度や速度変化状況、\n譜面の難易度などの情報を表示します。`,
5092
5099
  dataSave: `ハイスコア、リバース設定、\nキーコンフィグの保存の有無を設定します。`,
5100
+ toOption: `譜面、速度、ゲージなどの基本設定を設定します。`,
5093
5101
  toDisplay: `プレイ画面上のオブジェクトの\n表示・非表示(一部透明度)を設定します。`,
5094
5102
  toSettings: `その他の拡張設定を設定します。`,
5095
5103
 
@@ -5200,6 +5208,7 @@ const g_lang_msgObj = {
5200
5208
 
5201
5209
  graph: `Displays detailed information about the chart, such as chart's density status, sequences' velocity changes, and chart's difficulty.`,
5202
5210
  dataSave: `Set whether to save the high score, reverse setting, and key config.`,
5211
+ toOption: `Set basic settings such as chart, speed, and gauge.`,
5203
5212
  toDisplay: `Set the display or non-display (partial transparency) of objects on the play screen.`,
5204
5213
  toSettings: `Set other extended settings.`,
5205
5214
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2026/09/13
8
- * Revised : 2026/10/10 (v51.3.0)
8
+ * Revised : 2026/10/20 (v51.4.1)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -2466,6 +2466,7 @@ const getArrowSettings = (_retryFlg = false) => {
2466
2466
 
2467
2467
  // FrzReturnの初期化
2468
2468
  g_workObj.frzReturnFlg = false;
2469
+ g_workObj.frzReturnPausedFlg = false;
2469
2470
  g_workObj.frzReturnSeq = g_frzReturnSeqFunc.get(g_stateObj.frzReturnType)();
2470
2471
  if (g_workObj.frzReturnTimerId) {
2471
2472
  g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2026/09/13
8
- * Revised : 2026/10/10 (v51.3.0)
8
+ * Revised : 2026/10/12 (v51.4.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -1665,6 +1665,14 @@ const headerConvert = _dosObj => {
1665
1665
  g_canDisabledSettings.forEach(option =>
1666
1666
  obj[`${option}Use`] = setBoolVal(_dosObj[`${option}Use`] ?? g_presetObj.settingUse?.[option], true));
1667
1667
 
1668
+ // 拡張機能の無効設定
1669
+ if (!setBoolVal(_dosObj.displayWindowUse, true)) {
1670
+ g_settings.settingWindows = g_settings.settingWindows.filter(option => option !== settingsDisplayInit);
1671
+ }
1672
+ if (!setBoolVal(_dosObj.exSettingWindowUse, true)) {
1673
+ g_settings.settingWindows = g_settings.settingWindows.filter(option => option !== exSettingInit);
1674
+ }
1675
+
1668
1676
  let interlockingErrorFlg = false;
1669
1677
  g_displays.forEach((option, j) => {
1670
1678
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2026/09/13
8
- * Revised : 2026/10/10 (v51.3.0)
8
+ * Revised : 2026/10/12 (v51.4.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -33,6 +33,7 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
33
33
  if (_initFlg) {
34
34
  g_stateObj.keyLockFlg = false;
35
35
  g_stateObj.kbPreviewFlg = false;
36
+ g_stateObj.kbLaneFlg = false;
36
37
  }
37
38
 
38
39
  multiAppend(divRoot,
@@ -400,6 +401,9 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
400
401
  viewGroup(_type);
401
402
  if (_type === `stepRtn`) {
402
403
  keyConfigInit(g_kcType);
404
+ } else if (_type === `color`) {
405
+ // カラーグループ変更時はレーン色分けプレビューへ反映
406
+ keyconfigKeyboardPreview.refresh();
403
407
  }
404
408
  };
405
409
 
@@ -580,6 +584,11 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
580
584
 
581
585
  // 次の位置が見えなくなったらkeyconSpriteの位置を調整する
582
586
  adjustScrollPoint(nextLeft);
587
+
588
+ // レーン色分けプレビュー中はカーソルレーンの強調を更新
589
+ if (g_stateObj.kbLaneFlg) {
590
+ keyconfigKeyboardPreview.refresh();
591
+ }
583
592
  };
584
593
 
585
594
  /**
@@ -1311,11 +1320,22 @@ const keyconfigKeyboardPreview = (() => {
1311
1320
  },
1312
1321
  bgFill: `#0d0d1a`, // Canvas 背景
1313
1322
  legendText: `#cccccc`, // 凡例テキスト
1323
+
1324
+ // レーン色分けモード用(凡例と文字色。レーン色自体は g_dfColorObj.setColorType2 を使用)
1325
+ // 枠線は未割当キーと同色に統一し、メイン/代替は塗りのアルファのみで区別する
1326
+ laneMain: { fill: `#cccccc`, stroke: `#555577` },
1327
+ laneAlt: { fill: `rgba(204,204,204,0.35)`, stroke: `#555577` },
1328
+ laneAltBright: { fill: `rgba(204,204,204,0.6)`, stroke: `#555577` },
1329
+ laneStroke: `#555577`,
1330
+ laneText: `#111111`,
1314
1331
  };
1315
1332
 
1316
1333
  // 凡例エリアの高さ
1317
1334
  const LEGEND_H = 25;
1318
1335
 
1336
+ // レーン色分けモードで、同一キーに割り当てられた色を分割描画する上限数
1337
+ const C_LANE_SPLIT_MAX = 3;
1338
+
1319
1339
  // -------------------------------------------------------------------------
1320
1340
  // キーレイアウト定義
1321
1341
  //
@@ -1475,6 +1495,7 @@ const keyconfigKeyboardPreview = (() => {
1475
1495
  mappedSet: new Set(), // メインキー(code文字列)
1476
1496
  altSet: new Set(), // 代替キー(code文字列)
1477
1497
  shortcutSet: new Set(), // ショートカットキー(code文字列)
1498
+ laneMap: new Map(), // code文字列 → { entries: [{ colorPos, alt }], cursor: boolean }
1478
1499
  canvasBase: null,
1479
1500
  canvasMap: null,
1480
1501
  keyDataList: [], // { code, x, y, w, h, label } — drawMap で照合するキャッシュ
@@ -1620,13 +1641,16 @@ const keyconfigKeyboardPreview = (() => {
1620
1641
  * @param {Object} keyData - 位置・サイズ・キーコードを含むキー情報
1621
1642
  * @param {Object} style - fill, stroke, text 等の色セット
1622
1643
  * @param {number} lw - 枠線の太さ(lineWidth)
1644
+ * @param {boolean} shapeFlg - 枠線・背景を描画するかどうか(false の場合はテキストのみ描画)
1623
1645
  */
1624
- const drawOneKey = (ctx, { keyData, style, lw = 1 }) => {
1646
+ const drawOneKey = (ctx, { keyData, style, lw = 1, shapeFlg = true }) => {
1625
1647
  const { x, y, w: keyW, h: keyH, code, label } = keyData;
1626
1648
 
1627
1649
  // 1. キーの枠線・背景を描画
1628
- roundRect(ctx, x + 0.5, y + 0.5, keyW - 1, keyH - 1, kr());
1629
- fillStrokeShape(ctx, { fill: style.fill, stroke: style.stroke, lineWidth: lw });
1650
+ if (shapeFlg) {
1651
+ roundRect(ctx, x + 0.5, y + 0.5, keyW - 1, keyH - 1, kr());
1652
+ fillStrokeShape(ctx, { fill: style.fill, stroke: style.stroke, lineWidth: lw });
1653
+ }
1630
1654
 
1631
1655
  // 2. キー内部のテキスト(メイン・サブ)を描画
1632
1656
  const [primary, sub] = getKeyLabels(code, label);
@@ -1744,9 +1768,13 @@ const keyconfigKeyboardPreview = (() => {
1744
1768
 
1745
1769
  const legends = [
1746
1770
  { style: C_COLOR.normal, label: g_lblNameObj.unallocated },
1747
- { style: C_COLOR.mapped, label: g_lblNameObj.allocated },
1748
- { style: C_COLOR.alt, label: g_lblNameObj.altAllocated },
1749
- { style: C_COLOR.shortcut, label: g_lblNameObj.shortcutKey }
1771
+ { style: g_stateObj.kbLaneFlg ? C_COLOR.laneMain : C_COLOR.mapped, label: g_lblNameObj.allocated },
1772
+ {
1773
+ style: g_stateObj.kbLaneFlg
1774
+ ? (g_headerObj.baseBrightFlg ? C_COLOR.laneAltBright : C_COLOR.laneAlt) : C_COLOR.alt,
1775
+ label: g_lblNameObj.altAllocated,
1776
+ },
1777
+ { style: C_COLOR.shortcut, label: g_lblNameObj.shortcutKey },
1750
1778
  ];
1751
1779
 
1752
1780
  let lx = 8;
@@ -1761,6 +1789,111 @@ const keyconfigKeyboardPreview = (() => {
1761
1789
  });
1762
1790
  };
1763
1791
 
1792
+ /**
1793
+ * 明度が低い色を、未割当キーと区別できる下限まで持ち上げる
1794
+ * @param {string} _hex #rrggbb 形式の色
1795
+ * @param {number} _minLum 下限値(RGBの最大成分で判定)
1796
+ * @returns {string}
1797
+ */
1798
+ const liftDarkColor = (_hex, _minLum = 0x66) => {
1799
+ const n = parseInt(_hex.slice(1, 7), 16);
1800
+ const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
1801
+ const lum = Math.max(r, g, b);
1802
+ if (lum >= _minLum) {
1803
+ return _hex;
1804
+ }
1805
+ const d = _minLum - lum;
1806
+ return `#${[r, g, b].map(v => (v + d).toString(16).padStart(2, `0`)).join(``)}`;
1807
+ };
1808
+
1809
+ /**
1810
+ * #rrggbb 形式の色にアルファ値を付与する
1811
+ * @param {string} _hex
1812
+ * @param {number} _alpha
1813
+ * @returns {string}
1814
+ */
1815
+ const hexToRgba = (_hex, _alpha) => {
1816
+ const n = parseInt(_hex.slice(1, 7), 16);
1817
+ return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${_alpha})`;
1818
+ };
1819
+
1820
+ /**
1821
+ * レーン色分けモードのスタイル取得(色は譜面ミニマップと同じ setColorType2 固定)
1822
+ * @param {number} _colorPos カラーグループ番号
1823
+ * @param {boolean} _altFlg 代替キーか
1824
+ */
1825
+ const getLaneStyle = (_colorPos, _altFlg) => {
1826
+ const brightFlg = g_headerObj.baseBrightFlg;
1827
+ const baseColor = g_dfColorObj.setColorType2[_colorPos] ?? `#ffffff`;
1828
+
1829
+ // 明背景のみ、黒に近い色を未割当キーと区別できる明るさまで持ち上げる
1830
+ const color = brightFlg ? liftDarkColor(baseColor) : baseColor;
1831
+ // 明背景では暗色セットが使われるため、文字は白にする
1832
+ const mainText = brightFlg ? `#ffffff` : C_COLOR.laneText;
1833
+ if (!_altFlg) {
1834
+ return { fill: color, text: mainText };
1835
+ }
1836
+ return brightFlg
1837
+ ? { fill: hexToRgba(color, 0.6), text: `#ffffff` }
1838
+ : { fill: hexToRgba(color, 0.35), text: color };
1839
+ };
1840
+
1841
+ /**
1842
+ * レーン色分けモードでの単一キー描画
1843
+ * - 複数色の場合は縦方向に等分割して塗り分ける
1844
+ * @param {CanvasRenderingContext2D} ctx
1845
+ * @param {Object} keyData
1846
+ * @param {{colorPos: number, alt: boolean}[]} entries
1847
+ * @param {boolean} cursorFlg カーソル中のレーンか
1848
+ */
1849
+ const drawLaneKey = (ctx, keyData, entries, cursorFlg) => {
1850
+ const { x, y, w: keyW, h: keyH } = keyData;
1851
+ const styles = entries.map(val => getLaneStyle(val.colorPos, val.alt));
1852
+ const segW = keyW / styles.length;
1853
+
1854
+ styles.forEach((style, i) => {
1855
+ ctx.save();
1856
+ ctx.beginPath();
1857
+ ctx.rect(x + segW * i, y, segW, keyH);
1858
+ ctx.clip();
1859
+ roundRect(ctx, x + 0.5, y + 0.5, keyW - 1, keyH - 1, kr());
1860
+ fillStrokeShape(ctx, { fill: style.fill });
1861
+ ctx.restore();
1862
+ });
1863
+
1864
+ // 枠線はキー全体に対して1回だけ、全キー共通の色で描画
1865
+ roundRect(ctx, x + 0.5, y + 0.5, keyW - 1, keyH - 1, kr());
1866
+ fillStrokeShape(ctx, { stroke: C_COLOR.laneStroke, lineWidth: 1.5 });
1867
+
1868
+ // 文字は全体に1回だけ描画(メインが含まれる場合はメインの文字色)
1869
+ const mainIdx = entries.findIndex(val => !val.alt);
1870
+ const textColor = styles[mainIdx >= 0 ? mainIdx : 0].text;
1871
+ drawOneKey(ctx, { keyData, style: { text: textColor }, shapeFlg: false });
1872
+
1873
+ // カーソル中のレーンは白枠で強調
1874
+ if (cursorFlg) {
1875
+ roundRect(ctx, x + 0.5, y + 0.5, keyW - 1, keyH - 1, kr());
1876
+ fillStrokeShape(ctx, { stroke: `#ffffff`, lineWidth: 2.5 });
1877
+ }
1878
+ };
1879
+
1880
+ /**
1881
+ * レーン色分けモードのマッピング描画(ショートカットキーは専用色で最優先)
1882
+ * @param {CanvasRenderingContext2D} ctx
1883
+ */
1884
+ const drawLaneMap = ctx => {
1885
+ _state.keyDataList.forEach(keyData => {
1886
+ if (_state.shortcutSet.has(keyData.code)) {
1887
+ drawOneKey(ctx, { keyData, style: C_COLOR.shortcut, lw: 1.5 });
1888
+ return;
1889
+ }
1890
+ const info = _state.laneMap.get(keyData.code);
1891
+ if (info !== undefined) {
1892
+ drawLaneKey(ctx, keyData, info.entries, info.cursor);
1893
+ }
1894
+ });
1895
+ };
1896
+
1764
1897
  /**
1765
1898
  * マッピング強調レイヤーを再描画する。
1766
1899
  * メインキー(mappedSet)を青系、代替キー(altSet)を黄系で色分けする。
@@ -1771,6 +1904,11 @@ const keyconfigKeyboardPreview = (() => {
1771
1904
 
1772
1905
  ctx.clearRect(0, 0, _state.cvsW, _state.cvsH);
1773
1906
 
1907
+ if (g_stateObj.kbLaneFlg) {
1908
+ drawLaneMap(ctx);
1909
+ return;
1910
+ }
1911
+
1774
1912
  // キー状態に応じた色取得ロジック(優先度: ショートカット > メイン > 代替)
1775
1913
  const getKeyStyle = (code) => {
1776
1914
  if (_state.shortcutSet.has(code)) return C_COLOR.shortcut;
@@ -1815,6 +1953,16 @@ const keyconfigKeyboardPreview = (() => {
1815
1953
  pointerEvents: C_DIS_AUTO, background: `#00000080`, display: C_DIS_NONE, overflow: `hidden`,
1816
1954
  });
1817
1955
 
1956
+ // レーン色分け表示の切替ボタン(プレビューエリア内の上部余白に配置)
1957
+ const getLaneBtnName = () => `${g_lblNameObj.b_kbLane ?? `Lane Color`}: ${boolToSwitch(!!g_stateObj.kbLaneFlg)}`;
1958
+ const btnLane = createCss2Button(`btnKbLane`, getLaneBtnName(), () => {
1959
+ g_stateObj.kbLaneFlg = !g_stateObj.kbLaneFlg;
1960
+ btnLane.textContent = getLaneBtnName();
1961
+ drawBase(); // 凡例の切替
1962
+ refresh();
1963
+ }, { x: _state.cvsW - 130, y: 10, w: 120, h: 22, siz: 12 }, g_cssObj.button_Mini);
1964
+ areaDiv.appendChild(btnLane);
1965
+
1818
1966
  /**
1819
1967
  * プレビュー用canvasを生成しareaDivに追加、_stateに登録する
1820
1968
  * @param {string} _id
@@ -1873,6 +2021,33 @@ const keyconfigKeyboardPreview = (() => {
1873
2021
  [g_headerObj.keyRetry, g_headerObj.keyTitleBack, g_headerObj.keyPause, 34, 33].map(toCodeStr).filter(v => v !== ``)
1874
2022
  );
1875
2023
 
2024
+ // レーン色分け用: キーコード → 割り当て先レーンのカラーグループ情報
2025
+ const colorList = g_keyObj[`color${tkObj.keyCtrlPtn}`];
2026
+ _state.laneMap = new Map();
2027
+ g_keyObj[`keyCtrl${tkObj.keyCtrlPtn}`].forEach((arr, j) => {
2028
+ if (!tkObj.keyGroupMaps[j].includes(configKeyGroupList[g_keycons.keySwitchNum])) {
2029
+ return;
2030
+ }
2031
+ arr.forEach((num, k) => {
2032
+ const code = toCodeStr(num);
2033
+ if (code === ``) {
2034
+ return;
2035
+ }
2036
+ const info = _state.laneMap.get(code) ?? { entries: [], cursor: false };
2037
+ const [colorPos, alt] = [colorList[j], k > 0];
2038
+ if (!info.entries.some(val => val.colorPos === colorPos && val.alt === alt)) {
2039
+ info.entries.push({ colorPos, alt });
2040
+ }
2041
+ info.cursor ||= (j === g_currentj);
2042
+ _state.laneMap.set(code, info);
2043
+ });
2044
+ });
2045
+ // メイン優先で並べ、分割数は上限まで
2046
+ _state.laneMap.forEach(info => {
2047
+ info.entries.sort((a, b) => Number(a.alt) - Number(b.alt));
2048
+ info.entries = info.entries.slice(0, C_LANE_SPLIT_MAX);
2049
+ });
2050
+
1876
2051
  if (_state.visible) drawMap();
1877
2052
  };
1878
2053
 
@@ -1885,6 +2060,7 @@ const keyconfigKeyboardPreview = (() => {
1885
2060
  _state.mappedSet = new Set();
1886
2061
  _state.altSet = new Set();
1887
2062
  _state.shortcutSet = new Set();
2063
+ _state.laneMap = new Map();
1888
2064
  _state.keyDataList = [];
1889
2065
  _state.canvasBase = null;
1890
2066
  _state.canvasMap = null;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2026/09/13
8
- * Revised : 2026/10/07 (v51.2.2)
8
+ * Revised : 2026/10/20 (v51.4.1)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -405,10 +405,12 @@ const mainInit = () => {
405
405
  };
406
406
  Object.keys(jdgMainScoreObj).forEach(jdgScore =>
407
407
  infoSprite.appendChild(
408
- makeCounterSymbol(`lbl${jdgScore}`,
409
- g_headerObj.playingWidth - 110 + (g_workObj.nonDefaultSc ? g_headerObj.scAreaWidth : 0),
410
- g_cssObj[`common_${jdgMainScoreObj[jdgScore][0]}`], jdgMainScoreObj[jdgScore][1] + 1, 0, g_workObj.scoreDisp
411
- )
408
+ createDivCss2Label(`lbl${jdgScore}`, 0, {
409
+ x: g_headerObj.playingWidth - 110 + (g_workObj.nonDefaultSc ? g_headerObj.scAreaWidth : 0),
410
+ y: g_limitObj.jdgCntsHeight * (jdgMainScoreObj[jdgScore][1] + 1),
411
+ w: g_limitObj.jdgCntsWidth, h: g_limitObj.jdgCntsHeight, siz: g_limitObj.jdgCntsSiz, align: C_ALIGN_RIGHT,
412
+ display: g_workObj.scoreDisp,
413
+ }, g_cssObj[`common_${jdgMainScoreObj[jdgScore][0]}`])
412
414
  )
413
415
  );
414
416
 
@@ -1116,8 +1118,9 @@ const mainInit = () => {
1116
1118
 
1117
1119
  // 移動
1118
1120
  updateArrowPos(arrowName, currentArrow);
1121
+ currentArrow.cnt--;
1119
1122
 
1120
- funcs.judgeMotion(_j, arrowName, --currentArrow.cnt);
1123
+ funcs.judgeMotion(_j, arrowName, currentArrow.cnt);
1121
1124
  funcs.judgeNext(_j, _k, currentArrow.cnt);
1122
1125
  };
1123
1126
 
@@ -1273,7 +1276,7 @@ const mainInit = () => {
1273
1276
  * @param {string} _frzName
1274
1277
  * @param {object} _frz g_attrObj[_frzName]
1275
1278
  */
1276
- const movFrzMoving = (_j, _k, _name, _frzName, _frz) => {
1279
+ const movFrz = (_j, _k, _name, _frzName, _frz) => {
1277
1280
 
1278
1281
  // 全体色変化 (通常時)
1279
1282
  changeColorOnFrame(_j, _k, _name, `Normal`);
@@ -1295,7 +1298,7 @@ const mainInit = () => {
1295
1298
  * @param {object} _frz g_attrObj[_frzName]
1296
1299
  * @param {number} _movY 1フレームあたりの移動量
1297
1300
  */
1298
- const movFrzHolding = (_j, _k, _name, _frzName, _frz, _movY) => {
1301
+ const holdFrz = (_j, _k, _name, _frzName, _frz, _movY) => {
1299
1302
  const funcs = objFuncs[_name];
1300
1303
 
1301
1304
  // 全体色変化 (ヒット時)
@@ -1332,7 +1335,7 @@ const mainInit = () => {
1332
1335
  * @param {object} _frz g_attrObj[_frzName]
1333
1336
  * @param {number} _movY 1フレームあたりの移動量
1334
1337
  */
1335
- const movFrzEnded = (_j, _name, _frzName, _frz, _movY) => {
1338
+ const endFrz = (_j, _name, _frzName, _frz, _movY) => {
1336
1339
  _frz.frzBarLength -= _movY * _frz.dir;
1337
1340
  if (_frz.frzBarLength > 0) {
1338
1341
  _frz.y -= _movY;
@@ -1354,14 +1357,14 @@ const mainInit = () => {
1354
1357
  const movY = g_workObj.currentSpeed * currentFrz.boostSpd * currentFrz.dir;
1355
1358
 
1356
1359
  if (currentFrz.judgEndFlg) {
1357
- movFrzEnded(_j, _name, frzName, currentFrz, movY);
1360
+ endFrz(_j, _name, frzName, currentFrz, movY);
1358
1361
  return;
1359
1362
  }
1360
1363
 
1361
1364
  if (currentFrz.isMoving) {
1362
- movFrzMoving(_j, _k, _name, frzName, currentFrz);
1365
+ movFrz(_j, _k, _name, frzName, currentFrz);
1363
1366
  } else {
1364
- movFrzHolding(_j, _k, _name, frzName, currentFrz, movY);
1367
+ holdFrz(_j, _k, _name, frzName, currentFrz, movY);
1365
1368
  }
1366
1369
 
1367
1370
  // フリーズアローが枠外に出たときの処理
@@ -1426,8 +1429,7 @@ const mainInit = () => {
1426
1429
  } else if (/\[fontSize=\d+\]/.test(wordDat)) {
1427
1430
 
1428
1431
  // フォントサイズ変更
1429
- const fontSize = setIntVal(wordDat.match(/\d+/)[0], g_limitObj.mainSiz);
1430
- styWord.fontSize = wUnit(fontSize);
1432
+ styWord.fontSize = wUnit(setIntVal(wordDat.match(/\d+/)[0], g_limitObj.mainSiz));
1431
1433
 
1432
1434
  } else {
1433
1435
 
@@ -1435,9 +1437,10 @@ const mainInit = () => {
1435
1437
  const fadeFinished = _currentFrame - g_workObj.lastFadeFrame[wordDepth] >= g_workObj.wordFadeFrame[wordDepth];
1436
1438
  if (fadeFinished) {
1437
1439
  [`Out`, `In`].forEach(type => {
1438
- if (g_wordObj[`fade${type}Flg${wordDepth}`]) {
1440
+ const key = `fade${type}Flg${wordDepth}`;
1441
+ if (g_wordObj[key]) {
1439
1442
  styWord.animationName = `none`;
1440
- g_wordObj[`fade${type}Flg${wordDepth}`] = false;
1443
+ g_wordObj[key] = false;
1441
1444
  }
1442
1445
  });
1443
1446
  }
@@ -1729,6 +1732,7 @@ const mainInit = () => {
1729
1732
  // FrzReturn演出はflowTimelineと独立したタイマーで動いているため、
1730
1733
  // 一時停止時点で早期終了させる(位置復元はせず、通常終了時と同じ後片付けを行う)
1731
1734
  resetFrzReturn();
1735
+ g_workObj.frzReturnPausedFlg = true;
1732
1736
  }
1733
1737
  g_timerHandler.clearTimeout(g_timeoutEvtId);
1734
1738
  g_audio.pause();
@@ -1805,6 +1809,11 @@ const mainInit = () => {
1805
1809
  pausedElapsedTime = null;
1806
1810
  pausedStartAdjustment = null;
1807
1811
  g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, 1000 / g_fps);
1812
+
1813
+ if (g_workObj.frzReturnPausedFlg) {
1814
+ startFrzReturn();
1815
+ g_workObj.frzReturnPausedFlg = false;
1816
+ }
1808
1817
  };
1809
1818
 
1810
1819
  const tick = _remaining => {
@@ -2059,24 +2068,6 @@ const changeAppearanceFilter = (_num = 10) => {
2059
2068
  return doubleFilterFlg;
2060
2069
  };
2061
2070
 
2062
- /**
2063
- * 判定カウンタ表示作成
2064
- * @param {string} _id
2065
- * @param {number} _x
2066
- * @param {string} _class
2067
- * @param {number} _heightPos
2068
- * @param {string|number} _text
2069
- * @param {string} [_display='inherit'] 表示有無 (inherit: 表示 / none: 非表示)
2070
- * @returns {HTMLDivElement}
2071
- */
2072
- const makeCounterSymbol = (_id, _x, _class, _heightPos, _text, _display = C_DIS_INHERIT) => {
2073
- return createDivCss2Label(_id, _text, {
2074
- x: _x, y: g_limitObj.jdgCntsHeight * _heightPos,
2075
- w: g_limitObj.jdgCntsWidth, h: g_limitObj.jdgCntsHeight, siz: g_limitObj.jdgCntsSiz, align: C_ALIGN_RIGHT,
2076
- display: _display,
2077
- }, _class);
2078
- };
2079
-
2080
2071
  /**
2081
2072
  * ステップゾーンの表示・非表示切替
2082
2073
  * @param {number} _j
@@ -2156,6 +2147,7 @@ const executeFrzReturn = (_seq, _idx, _axis) => {
2156
2147
  // 画面がプレイ画面から移動した場合
2157
2148
  g_workObj.frzReturnFlg = false;
2158
2149
  g_workObj.frzReturnTimerId = null;
2150
+ g_workObj.frzReturnPausedFlg = false;
2159
2151
  return;
2160
2152
  }
2161
2153
 
@@ -2600,11 +2592,10 @@ const changeLifeColor = (_state = ``) => {
2600
2592
 
2601
2593
  if (_state !== `` && _state !== g_workObj.currentLifeState) {
2602
2594
  const lifeCss = g_cssObj[`life_${_state}`];
2603
- lblLife.classList.remove(g_cssObj.life_Max, g_cssObj.life_Cleared, g_cssObj.life_Failed);
2604
- lifeBar.classList.remove(g_cssObj.life_Max, g_cssObj.life_Cleared, g_cssObj.life_Failed);
2605
- lblLife.classList.add(lifeCss);
2606
- lifeBar.classList.add(lifeCss);
2607
-
2595
+ [lblLife, lifeBar].forEach(elm => {
2596
+ elm.classList.remove(g_cssObj.life_Max, g_cssObj.life_Cleared, g_cssObj.life_Failed);
2597
+ elm.classList.add(lifeCss);
2598
+ });
2608
2599
  g_workObj.currentLifeState = _state;
2609
2600
  }
2610
2601
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2026/09/13
8
- * Revised : 2026/10/10 (v51.3.0)
8
+ * Revised : 2026/10/12 (v51.4.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -14,7 +14,7 @@
14
14
  /* Scene : SETTINGS [lime] */
15
15
  /*-----------------------------------------------------------*/
16
16
 
17
- const commonSettingBtn = _labelName => {
17
+ const commonSettingBtn = () => {
18
18
 
19
19
  const switchSave = evt => {
20
20
  const from = boolToSwitch(g_stateObj.dataSaveFlg);
@@ -24,6 +24,8 @@ const commonSettingBtn = _labelName => {
24
24
  const to = boolToSwitch(g_stateObj.dataSaveFlg);
25
25
  evt.target.classList.replace(g_cssObj[`button_${from}`], g_cssObj[`button_${to}`]);
26
26
  };
27
+ const nextPageNum = nextPos(g_settings.settingWindowNum, 1, g_settings.settingWindows.length);
28
+ const labelName = g_settingWindowTitleKeys.get(g_settings.settingWindows[nextPageNum]);
27
29
 
28
30
  multiAppend(divRoot,
29
31
 
@@ -42,18 +44,19 @@ const commonSettingBtn = _labelName => {
42
44
  // プレイ開始
43
45
  makePlayButton(() => loadMusic()),
44
46
 
45
- // Display設定へ移動
46
- createCss2Button(`btn${_labelName}`, `>`, () => true, {
47
+ // 次の設定へ移動
48
+ createCss2Button(`btnNextSetting`, `>`, () => true, {
47
49
  ...g_lblPosObj.btnSwitchSetting,
48
- title: g_msgObj[`to${_labelName}`], resetFunc: () => g_moveSettingWindow(),
50
+ title: g_msgObj[`to${labelName}`], resetFunc: () => g_moveSettingWindow(),
49
51
  cxtFunc: () => g_moveSettingWindow(true, -1),
52
+ display: g_settings.settingWindows.length > 1 ? C_DIS_INHERIT : C_DIS_NONE,
50
53
  }, g_cssObj.button_Mini),
51
54
 
52
55
  // データセーブフラグの切替
53
56
  createCss2Button(`btnSave`, g_lblNameObj.dataSave, evt => switchSave(evt), {
54
57
  ...g_lblPosObj.btnSave,
55
58
  cxtFunc: evt => switchSave(evt),
56
- visibility: g_langStorage.safeMode === C_FLG_OFF ? C_DIS_INHERIT : `hidden`,
59
+ display: g_langStorage.safeMode === C_FLG_OFF ? C_DIS_INHERIT : C_DIS_NONE,
57
60
  }, g_cssObj.button_Default, (g_stateObj.dataSaveFlg ? g_cssObj.button_ON : g_cssObj.button_OFF)),
58
61
 
59
62
  // データ管理画面へ移動
@@ -170,7 +173,7 @@ const optionInit = () => {
170
173
  safeExecuteCustomHooks(`g_customJsObj.option`, g_customJsObj.option);
171
174
 
172
175
  // ボタン描画
173
- commonSettingBtn(`Display`);
176
+ commonSettingBtn();
174
177
 
175
178
  // キー操作イベント(デフォルト)
176
179
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
@@ -2403,7 +2406,7 @@ const settingsDisplayInit = () => {
2403
2406
  safeExecuteCustomHooks(`g_customJsObj.settingsDisplay`, g_customJsObj.settingsDisplay);
2404
2407
 
2405
2408
  // ボタン描画
2406
- commonSettingBtn(`Settings`);
2409
+ commonSettingBtn();
2407
2410
 
2408
2411
  // キー操作イベント(デフォルト)
2409
2412
  setShortcutEvent(g_currentPage);
@@ -3242,7 +3245,7 @@ const exSettingInit = () => {
3242
3245
  safeExecuteCustomHooks(`g_customJsObj.exSetting`, g_customJsObj.exSetting);
3243
3246
 
3244
3247
  // 設定系のボタン群をまとめて作成(Data Save, Display切替, Back, KeyConfig, Playボタン)
3245
- commonSettingBtn(g_currentPage);
3248
+ commonSettingBtn();
3246
3249
 
3247
3250
  // キー操作イベント(デフォルト)
3248
3251
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "51.3.0",
3
+ "version": "51.4.1",
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",