danoniplus 49.6.1 → 50.1.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/css/danoni_main.css +6 -1
- package/js/danoni_main.js +304 -34
- package/js/lib/danoni_constants.js +39 -4
- package/package.json +1 -1
package/css/danoni_main.css
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
本体cssファイル
|
|
5
5
|
|
|
6
6
|
Created : 2018/10/08
|
|
7
|
-
Revised : 2026/
|
|
7
|
+
Revised : 2026/08/29 (v50.1.0)
|
|
8
8
|
|
|
9
9
|
https://github.com/cwtickle/danoniplus
|
|
10
10
|
------------------------------------------ */
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
overflow: hidden;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/* タブのバックグラウンド化に伴う一時停止(danoni_main.js側でdivRootにgamePausedクラスを付け外し) */
|
|
28
|
+
#divRoot.gamePaused * {
|
|
29
|
+
animation-play-state: paused !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
input[type="range"] {
|
|
28
33
|
appearance: none;
|
|
29
34
|
background: transparent;
|
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/08/
|
|
7
|
+
* Revised : 2026/08/29
|
|
8
8
|
*
|
|
9
9
|
* https://github.com/cwtickle/danoniplus
|
|
10
10
|
*/
|
|
11
|
-
const g_version = `Ver
|
|
12
|
-
const g_revisedDate = `2026/08/
|
|
11
|
+
const g_version = `Ver 50.1.0`;
|
|
12
|
+
const g_revisedDate = `2026/08/29`;
|
|
13
13
|
|
|
14
14
|
// カスタム用バージョン (danoni_custom.js 等で指定可)
|
|
15
15
|
let g_localVersion = ``;
|
|
@@ -123,6 +123,12 @@ const waitUntilLoaded = () => {
|
|
|
123
123
|
await loadScript2(`${g_rootPath}../js/lib/danoni_constants.js?${g_versionForUrl}`);
|
|
124
124
|
await loadScript2(`${g_rootPath}../js/lib/legacy_functions.js?${g_versionForUrl.split(`.`)[0]}`, false);
|
|
125
125
|
initialControl();
|
|
126
|
+
|
|
127
|
+
// プレイ画面(g_currentPage === `main`)でのみ有効化される、タブ非表示検知(常時1個だけ登録)
|
|
128
|
+
g_handler.addListener(document, `visibilitychange`, () => {
|
|
129
|
+
if (g_currentPage !== `main`) return;
|
|
130
|
+
document.hidden ? g_timelineHooks.pause() : g_timelineHooks.resume();
|
|
131
|
+
});
|
|
126
132
|
})();
|
|
127
133
|
|
|
128
134
|
/*-----------------------------------------------------------*/
|
|
@@ -269,6 +275,11 @@ let g_audioForMS = null;
|
|
|
269
275
|
let g_timeoutEvtId = 0;
|
|
270
276
|
let g_timeoutEvtTitleId = 0;
|
|
271
277
|
let g_timeoutEvtResultId = 0;
|
|
278
|
+
|
|
279
|
+
// タブのバックグラウンド化に伴う一時停止/再開のフック
|
|
280
|
+
let g_timelineHooks = { pause: () => { }, resume: () => { } };
|
|
281
|
+
let g_timelineSessionId = 0; // mainInit()呼び出しごとに採番。古いセッションのfinishResume無効化に使用
|
|
282
|
+
|
|
272
283
|
let g_inputKeyBuffer = {};
|
|
273
284
|
|
|
274
285
|
// 音楽ファイル エンコードフラグ
|
|
@@ -606,9 +617,9 @@ const formatObject = (_obj, _indent = 0, { colorFmt = true, rootKey = `` } = {})
|
|
|
606
617
|
// scrollDirXのスクロール方向表示処理
|
|
607
618
|
return _value === 1 ? `1|<span style="color:#ff9999">↑</span>` : `-1|<span style="color:#66ff66">↓</span>`;
|
|
608
619
|
|
|
609
|
-
} else if (listMatching(_rootKey, [`keyCtrl`, `keyRetry`, `keyTitleBack`], { prefix: `^` })
|
|
620
|
+
} else if (listMatching(_rootKey, [`keyCtrl`, `keyRetry`, `keyTitleBack`, `keyPause`], { prefix: `^` })
|
|
610
621
|
&& !_rootKey.startsWith(`keyCtrlPtn`)) {
|
|
611
|
-
// keyCtrlX, keyRetryX, keyTitleBackX の対応キー表示処理
|
|
622
|
+
// keyCtrlX, keyRetryX, keyTitleBackX, keyPauseX の対応キー表示処理
|
|
612
623
|
return (g_kCd[_value] && _value !== 0) ? `${_value}|<span style="color:#ffff66">${g_kCd[_value]}</span>` : `----`;
|
|
613
624
|
}
|
|
614
625
|
} else if (isObj(_value)) {
|
|
@@ -2640,7 +2651,7 @@ class AudioPlayer {
|
|
|
2640
2651
|
}
|
|
2641
2652
|
|
|
2642
2653
|
get elapsedTime() {
|
|
2643
|
-
return this._context.currentTime - this.
|
|
2654
|
+
return (this._context.currentTime - this._scheduledTime) * this.playbackRate + g_scheduleLead + this._fadeinPosition;
|
|
2644
2655
|
}
|
|
2645
2656
|
|
|
2646
2657
|
/** AudioContextの現在時刻(秒) */
|
|
@@ -4232,6 +4243,9 @@ const headerConvert = _dosObj => {
|
|
|
4232
4243
|
obj.keyTitleBack = setIntVal(getKeyCtrlVal(_dosObj.keyTitleBack), C_KEY_TITLEBACK);
|
|
4233
4244
|
obj.keyTitleBackDef = obj.keyTitleBack;
|
|
4234
4245
|
obj.keyTitleBackDef2 = obj.keyTitleBack;
|
|
4246
|
+
obj.keyPause = setIntVal(getKeyCtrlVal(_dosObj.keyPause), C_KEY_PAUSE);
|
|
4247
|
+
obj.keyPauseDef = obj.keyPause;
|
|
4248
|
+
obj.keyPauseDef2 = obj.keyPause;
|
|
4235
4249
|
|
|
4236
4250
|
// フリーズアローの許容フレーム数設定
|
|
4237
4251
|
obj.frzAttempt = setIntVal(_dosObj.frzAttempt, C_FRM_FRZATTEMPT);
|
|
@@ -5702,6 +5716,9 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
|
|
|
5702
5716
|
// プレイ中ショートカット:タイトルバック (keyTitleBackX_Y)
|
|
5703
5717
|
newKeySingleParam(newKey, `keyTitleBack`, C_TYP_STRING, C_KEY_TITLEBACK);
|
|
5704
5718
|
|
|
5719
|
+
// プレイ中ショートカット:タイトルバック (keyPauseX_Y)
|
|
5720
|
+
newKeySingleParam(newKey, `keyPause`, C_TYP_STRING, C_KEY_PAUSE);
|
|
5721
|
+
|
|
5705
5722
|
// 別キーフラグ (transKeyX_Y)
|
|
5706
5723
|
newKeySingleParam(newKey, `transKey`, C_TYP_STRING, ``);
|
|
5707
5724
|
|
|
@@ -5740,8 +5757,9 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
|
|
|
5740
5757
|
// カスタムキーで定義されたtransKeyPtnを補完
|
|
5741
5758
|
completeTransKeyPtn([newKey]);
|
|
5742
5759
|
|
|
5743
|
-
// keyRetry, keyTitleBackのキー名をキーコードに変換
|
|
5744
|
-
const keyTypePatterns = Object.keys(g_keyObj).filter(val =>
|
|
5760
|
+
// keyRetry, keyTitleBack, keyPauseのキー名をキーコードに変換
|
|
5761
|
+
const keyTypePatterns = Object.keys(g_keyObj).filter(val =>
|
|
5762
|
+
val.startsWith(`keyRetry${newKey}`) || val.startsWith(`keyTitleBack${newKey}`) || val.startsWith(`keyPause${newKey}`));
|
|
5745
5763
|
keyTypePatterns.forEach(name => g_keyObj[name] = getKeyCtrlVal(g_keyObj[name]));
|
|
5746
5764
|
} catch (e) {
|
|
5747
5765
|
g_headerObj.undefinedKeyListFinal.push(newKey);
|
|
@@ -8461,6 +8479,7 @@ const setDifficulty = (_initFlg) => {
|
|
|
8461
8479
|
|
|
8462
8480
|
// ユーザカスタムイベント(初期)
|
|
8463
8481
|
safeExecuteCustomHooks(`g_customJsObj.difficulty`, g_customJsObj.difficulty, _initFlg, g_canLoadDifInfoFlg);
|
|
8482
|
+
resolveKeyFamily();
|
|
8464
8483
|
|
|
8465
8484
|
// 設定サマリー表示の更新
|
|
8466
8485
|
updateSettingSummary();
|
|
@@ -8470,6 +8489,77 @@ const setDifficulty = (_initFlg) => {
|
|
|
8470
8489
|
g_canLoadDifInfoFlg = true;
|
|
8471
8490
|
};
|
|
8472
8491
|
|
|
8492
|
+
/**
|
|
8493
|
+
* keyLabelの系統ごとに、選択時に強制したい設定値・選択/離脱時の個別処理を登録する
|
|
8494
|
+
* @param {string} _name 系統名
|
|
8495
|
+
* @param {(key: string) => boolean} _match この系統に属するkeyLabelかどうか
|
|
8496
|
+
* @param {object} [_fields] path -> 値 or 値を返す関数(単純代入で済むもの)
|
|
8497
|
+
* @param {object} [_hooks] { onAcquire, onRelease } 選択/離脱の瞬間に1回だけ呼ばれる(複合処理用)
|
|
8498
|
+
* @param {object} [_appliers] path -> 個別の適用関数(単純代入で済まないfields用)
|
|
8499
|
+
*/
|
|
8500
|
+
const registerKeyFamily = (_name, _match, _fields = {}, _hooks = {}, _appliers = {}) => {
|
|
8501
|
+
g_familyObj.families.push({ name: _name, match: _match, fields: _fields, appliers: _appliers, ..._hooks });
|
|
8502
|
+
};
|
|
8503
|
+
|
|
8504
|
+
/**
|
|
8505
|
+
* 現在のキー状態に応じて適用すべき設定群を判定し、
|
|
8506
|
+
* 各パラメータの値・スナップショット・フックを管理・適用する
|
|
8507
|
+
*/
|
|
8508
|
+
const resolveKeyFamily = () => {
|
|
8509
|
+
// 1. 現在のキーに一致するファミリーを検索(見つからなければ null = 標準状態)
|
|
8510
|
+
const newFamily = g_familyObj.families.find(f => f.match(g_keyObj.currentKey)) ?? null;
|
|
8511
|
+
|
|
8512
|
+
// 2. ファミリーが切り替わった瞬間(入場・離脱)にフック(固有の副作用)を実行
|
|
8513
|
+
if (newFamily !== g_familyObj.currentFamily) {
|
|
8514
|
+
g_familyObj.currentFamily?.onRelease?.();
|
|
8515
|
+
newFamily?.onAcquire?.();
|
|
8516
|
+
g_familyObj.currentFamily = newFamily;
|
|
8517
|
+
}
|
|
8518
|
+
|
|
8519
|
+
// 3. 登録されているすべてのファミリーが持つパス(プロパティ)の全リストを取得
|
|
8520
|
+
const allPaths = new Set(g_familyObj.families.flatMap(f => Object.keys(f.fields)));
|
|
8521
|
+
allPaths.forEach(path => {
|
|
8522
|
+
const spec = newFamily?.fields[path];
|
|
8523
|
+
// 該当プロパティのカスタム処理があれば優先し、なければ通常のプロパティ代入を使用
|
|
8524
|
+
const applier = g_familyObj.families.map(f => f.appliers[path]).find(Boolean) ?? (v => setPathVal(path, v));
|
|
8525
|
+
|
|
8526
|
+
if (spec !== undefined) {
|
|
8527
|
+
// 【変更値の適用】現在のファミリーにそのプロパティの設定がある場合
|
|
8528
|
+
// 初めて書き換えるプロパティの場合のみ、元の値をスナップショット(初期値)として退避
|
|
8529
|
+
if (!g_familyObj.ownership[path]) {
|
|
8530
|
+
g_familyObj.snapshot[path] = getPathVal(path);
|
|
8531
|
+
g_familyObj.ownership[path] = true;
|
|
8532
|
+
}
|
|
8533
|
+
// 関数なら評価し、値ならそのまま適用値とする
|
|
8534
|
+
applyIfChanged(path, typeof spec === `function` ? spec() : spec, applier);
|
|
8535
|
+
} else if (g_familyObj.ownership[path]) {
|
|
8536
|
+
// 【標準への復元】別のファミリーに移行し、かつ元々自分が書き換えていたプロパティの場合
|
|
8537
|
+
// スナップショットから元の値(初期値)を復元し、オーナーシップを解放
|
|
8538
|
+
applyIfChanged(path, g_familyObj.snapshot[path], applier);
|
|
8539
|
+
g_familyObj.ownership[path] = false;
|
|
8540
|
+
}
|
|
8541
|
+
// ownership[path]がfalseのままなら、標準同士の切り替えでも一切触らない
|
|
8542
|
+
});
|
|
8543
|
+
};
|
|
8544
|
+
|
|
8545
|
+
/**
|
|
8546
|
+
* 値に変更がある場合のみ、代入またはカスタム処理を実行してキャッシュを更新する
|
|
8547
|
+
* @param {string} _path 設定のパス
|
|
8548
|
+
* @param {string|number} _value 適用する値
|
|
8549
|
+
* @param {Function} _applier 実際の適用処理を行う関数
|
|
8550
|
+
*/
|
|
8551
|
+
const applyIfChanged = (_path, _value, _applier) => {
|
|
8552
|
+
// すでに同じ値が適用済みの場合は、無駄な再描画や再代入を防ぐためスキップ
|
|
8553
|
+
if (g_familyObj.appliedPaths.has(_path) && g_familyObj.applied[_path] === _value) return;
|
|
8554
|
+
|
|
8555
|
+
// 適用値をキャッシュに保存
|
|
8556
|
+
g_familyObj.applied[_path] = _value;
|
|
8557
|
+
g_familyObj.appliedPaths.add(_path);
|
|
8558
|
+
|
|
8559
|
+
// 実際の反映処理(値の代入、または画像再描画などの副作用)を実行
|
|
8560
|
+
_applier(_value);
|
|
8561
|
+
};
|
|
8562
|
+
|
|
8473
8563
|
/**
|
|
8474
8564
|
* 設定・オプション画面のラベル・ボタン処理の描画
|
|
8475
8565
|
* @param {Object} _sprite 基準とするスプライト(ここで指定する座標は、そのスプライトからの相対位置)
|
|
@@ -9786,7 +9876,7 @@ const buildPreviewUI = (_frame, _playW, _playH) => {
|
|
|
9786
9876
|
// ============================================================
|
|
9787
9877
|
const scGroup = createEmptySprite(_frame, `previewScGroup`, {
|
|
9788
9878
|
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
9789
|
-
y: _playH -
|
|
9879
|
+
y: _playH - 100 + g_diffObj.shortcutY, w: 80, h: 100, pointerEvents: C_DIS_AUTO,
|
|
9790
9880
|
});
|
|
9791
9881
|
multiAppend(scGroup,
|
|
9792
9882
|
createDivCss2Label(`lblRetry`, `[${g_lblNameObj.l_retry}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 0 }),
|
|
@@ -9795,15 +9885,18 @@ const buildPreviewUI = (_frame, _playW, _playH) => {
|
|
|
9795
9885
|
createDivCss2Label(`lblTitleBack`, `[${g_lblNameObj.l_titleBack}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 35 }),
|
|
9796
9886
|
createDivCss2Label(`lblTitleBackSc`, g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack],
|
|
9797
9887
|
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 50, fontWeight: g_headerObj.keyTitleBack === C_KEY_TITLEBACK ? `normal` : `bold` }),
|
|
9888
|
+
createDivCss2Label(`lblPause`, `[${g_lblNameObj.l_pause}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 70 }),
|
|
9889
|
+
createDivCss2Label(`lblPauseSc`, g_kCd[g_headerObj.keyPause],
|
|
9890
|
+
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 85, fontWeight: g_headerObj.keyPause === C_KEY_PAUSE ? `normal` : `bold` }),
|
|
9798
9891
|
);
|
|
9799
9892
|
const scConfig = {
|
|
9800
9893
|
toastTitle: g_lblNameObj.shortcutUpdate,
|
|
9801
9894
|
getStdPos: () => ({
|
|
9802
9895
|
x: g_sWidth + g_headerObj.scAreaWidth - 85,
|
|
9803
|
-
y: _playH -
|
|
9896
|
+
y: _playH - 100
|
|
9804
9897
|
}),
|
|
9805
9898
|
};
|
|
9806
|
-
makeElementDraggable(scGroup, `shortcut`, _playW, _playH, { w: 80, h:
|
|
9899
|
+
makeElementDraggable(scGroup, `shortcut`, _playW, _playH, { w: 80, h: 100 }, scConfig);
|
|
9807
9900
|
|
|
9808
9901
|
// ユーザカスタムイベント(プレビュー表示用)
|
|
9809
9902
|
safeExecuteCustomHooks(`g_customJsObj.displayPreview`, g_customJsObj.displayPreview, _frame, _playW, _playH);
|
|
@@ -10828,13 +10921,22 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
|
|
|
10828
10921
|
|
|
10829
10922
|
// リトライのショートカットキー変更
|
|
10830
10923
|
createCss2Button(`scRetry`, getScMsg.Retry(), () => {
|
|
10831
|
-
cursor.style.left = wUnit(g_btnX(
|
|
10924
|
+
cursor.style.left = wUnit(g_btnX(1 / 2) + kcSubX);
|
|
10832
10925
|
cursor.style.top = wUnit(g_sHeight - 160 + kcSubY);
|
|
10833
10926
|
selectedKc = `Retry`;
|
|
10834
10927
|
}, g_lblPosObj.scRetry, g_cssObj.button_Default_NoColor,
|
|
10835
10928
|
g_headerObj.keyRetry === g_headerObj.keyRetryDef2 ?
|
|
10836
10929
|
g_cssObj.title_base : g_cssObj.keyconfig_Changekey),
|
|
10837
10930
|
|
|
10931
|
+
// ポーズのショートカットキー変更
|
|
10932
|
+
createCss2Button(`scPause`, getScMsg.Pause(), () => {
|
|
10933
|
+
cursor.style.left = wUnit(g_btnX(3 / 4) + kcSubX);
|
|
10934
|
+
cursor.style.top = wUnit(g_sHeight - 160 + kcSubY);
|
|
10935
|
+
selectedKc = `Pause`;
|
|
10936
|
+
}, g_lblPosObj.scPause, g_cssObj.button_Default_NoColor,
|
|
10937
|
+
g_headerObj.keyPause === g_headerObj.keyPauseDef2 ?
|
|
10938
|
+
g_cssObj.title_base : g_cssObj.keyconfig_Changekey),
|
|
10939
|
+
|
|
10838
10940
|
// 別キーモード警告メッセージ
|
|
10839
10941
|
createDivCss2Label(
|
|
10840
10942
|
`kcMsg`,
|
|
@@ -11342,8 +11444,8 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
|
|
|
11342
11444
|
} else if ((keyIsDown(g_kCdNameObj.metaLKey) || keyIsDown(g_kCdNameObj.metaRKey)) && keyIsShift()) {
|
|
11343
11445
|
return;
|
|
11344
11446
|
}
|
|
11345
|
-
if (selectedKc === `TitleBack` || selectedKc === `Retry`) {
|
|
11346
|
-
//
|
|
11447
|
+
if (selectedKc === `TitleBack` || selectedKc === `Retry` || selectedKc === `Pause`) {
|
|
11448
|
+
// タイトルバックキー、リトライキー、ポーズキーはプレイ中の操作キーと重複しないようにする(簡易的)
|
|
11347
11449
|
if (g_keyObj[`keyCtrl${keyCtrlPtn}`].flat().includes(setKey)) {
|
|
11348
11450
|
makeInfoWindow(g_msgInfoObj.I_0002, `fadeOut0`);
|
|
11349
11451
|
return;
|
|
@@ -11353,10 +11455,10 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
|
|
|
11353
11455
|
g_headerObj[`key${selectedKc}Def`] = setKey;
|
|
11354
11456
|
document.getElementById(`sc${selectedKc}`).textContent = getScMsg[selectedKc]();
|
|
11355
11457
|
document.getElementById(`sc${selectedKc}`).style.fontSize =
|
|
11356
|
-
wUnit(getFontSize2(getScMsg[selectedKc](), g_btnWidth(
|
|
11458
|
+
wUnit(getFontSize2(getScMsg[selectedKc](), g_btnWidth(1 / 4) - 40, { maxSiz: g_limitObj.mainSiz }));
|
|
11357
11459
|
if (g_isMac) {
|
|
11358
11460
|
scTitleBack.textContent = getScMsg.TitleBack();
|
|
11359
|
-
scTitleBack.style.fontSize = wUnit(getFontSize2(getScMsg.TitleBack(), g_btnWidth(
|
|
11461
|
+
scTitleBack.style.fontSize = wUnit(getFontSize2(getScMsg.TitleBack(), g_btnWidth(1 / 4) - 40, { maxSiz: g_limitObj.mainSiz }));
|
|
11360
11462
|
}
|
|
11361
11463
|
changeConfigColor(document.getElementById(`sc${selectedKc}`),
|
|
11362
11464
|
g_headerObj[`key${selectedKc}`] === g_headerObj[`key${selectedKc}Def2`] ?
|
|
@@ -12087,9 +12189,9 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
12087
12189
|
_state.mappedSet = new Set(ctrl.map(arr => toCodeStr(arr[0])).filter(v => v !== ``));
|
|
12088
12190
|
_state.altSet = new Set(ctrl.flatMap(arr => arr.slice(1)).map(toCodeStr).filter(v => v !== ``));
|
|
12089
12191
|
|
|
12090
|
-
// プレイ中ショートカット: keyRetry / keyTitleBack は g_headerObj から取得、PgDn(34) / PgUp(33) は固定
|
|
12192
|
+
// プレイ中ショートカット: keyRetry / keyTitleBack / keyPause は g_headerObj から取得、PgDn(34) / PgUp(33) は固定
|
|
12091
12193
|
_state.shortcutSet = new Set(
|
|
12092
|
-
[g_headerObj.keyRetry, g_headerObj.keyTitleBack, 34, 33].map(toCodeStr).filter(v => v !== ``)
|
|
12194
|
+
[g_headerObj.keyRetry, g_headerObj.keyTitleBack, g_headerObj.keyPause, 34, 33].map(toCodeStr).filter(v => v !== ``)
|
|
12093
12195
|
);
|
|
12094
12196
|
|
|
12095
12197
|
if (_state.visible) drawMap();
|
|
@@ -12171,6 +12273,9 @@ const setPlayingShortcut = () => {
|
|
|
12171
12273
|
if (g_headerObj.keyTitleBackDef === C_KEY_TITLEBACK) {
|
|
12172
12274
|
g_headerObj.keyTitleBack = setIntVal(getKeyCtrlVal(g_keyObj[`keyTitleBack${keyCtrlPtn}`]), g_headerObj.keyTitleBackDef);
|
|
12173
12275
|
}
|
|
12276
|
+
if (g_headerObj.keyPauseDef === C_KEY_PAUSE) {
|
|
12277
|
+
g_headerObj.keyPause = setIntVal(getKeyCtrlVal(g_keyObj[`keyPause${keyCtrlPtn}`]), g_headerObj.keyPauseDef);
|
|
12278
|
+
}
|
|
12174
12279
|
};
|
|
12175
12280
|
|
|
12176
12281
|
/**
|
|
@@ -14684,8 +14789,12 @@ const getArrowSettings = () => {
|
|
|
14684
14789
|
g_workObj.arrowReturnVal = 0;
|
|
14685
14790
|
g_gameOverFlg = false;
|
|
14686
14791
|
g_finishFlg = true;
|
|
14687
|
-
g_workObj.nonDefaultSc = g_headerObj.keyRetry !== C_KEY_RETRY || g_headerObj.keyTitleBack !== C_KEY_TITLEBACK;
|
|
14688
|
-
if (g_headerObj.scAreaWidth === 0 && (
|
|
14792
|
+
g_workObj.nonDefaultSc = g_headerObj.keyRetry !== C_KEY_RETRY || g_headerObj.keyTitleBack !== C_KEY_TITLEBACK || g_headerObj.keyPause !== C_KEY_PAUSE;
|
|
14793
|
+
if (g_headerObj.scAreaWidth === 0 && (
|
|
14794
|
+
g_headerObj.keyRetry !== g_headerObj.keyRetryDef2 ||
|
|
14795
|
+
g_headerObj.keyTitleBack !== g_headerObj.keyTitleBackDef2 ||
|
|
14796
|
+
g_headerObj.keyPause !== g_headerObj.keyPauseDef2
|
|
14797
|
+
)) {
|
|
14689
14798
|
g_workObj.nonDefaultSc = false;
|
|
14690
14799
|
}
|
|
14691
14800
|
if (g_diffObj.shortcutX !== 0 || g_diffObj.shortcutY !== 0) {
|
|
@@ -15032,7 +15141,7 @@ const mainInit = () => {
|
|
|
15032
15141
|
|
|
15033
15142
|
// 開始位置、楽曲再生位置の設定
|
|
15034
15143
|
const firstFrame = g_scoreObj.frameNum;
|
|
15035
|
-
|
|
15144
|
+
let musicStartFrame = firstFrame + g_headerObj.blankFrame;
|
|
15036
15145
|
const fadeFlgs = { fadein: [`In`, `Out`], fadeout: [`Out`, `In`] };
|
|
15037
15146
|
g_audio.volume = (firstFrame === 0 ? g_stateObj.volume / 100 : 0);
|
|
15038
15147
|
|
|
@@ -15042,6 +15151,12 @@ const mainInit = () => {
|
|
|
15042
15151
|
let musicStartTime;
|
|
15043
15152
|
let musicStartCtxTime;
|
|
15044
15153
|
let musicStartFlg = false;
|
|
15154
|
+
let isPaused = false; // 一時停止中かどうか(経路によらず共通のフラグ)
|
|
15155
|
+
let manualPauseFlg = false; // 手動ポーズによる一時停止かどうか(true時はvisibilitychangeでの自動再開をスキップ)
|
|
15156
|
+
let pausedElapsedTime = null; // [AudioPlayer/開始後のみ] 一時停止時点の再生位置(秒)
|
|
15157
|
+
let pausedStartAdjustment = null; // [AudioPlayer/開始前のみ] 開始予定までの残り時間(秒)
|
|
15158
|
+
let countdownTimeoutId = null; // 再開前カウントダウンのタイマーID。null=カウントダウン中でない
|
|
15159
|
+
const mySessionId = ++g_timelineSessionId; // このmainInit()実行を識別するID
|
|
15045
15160
|
|
|
15046
15161
|
g_inputKeyBuffer = {};
|
|
15047
15162
|
|
|
@@ -15163,29 +15278,30 @@ const mainInit = () => {
|
|
|
15163
15278
|
}
|
|
15164
15279
|
|
|
15165
15280
|
if (g_workObj.nonDefaultSc) {
|
|
15281
|
+
const diffX = g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX;
|
|
15282
|
+
const diffY = g_headerObj.playingHeight + g_diffObj.shortcutY;
|
|
15166
15283
|
multiAppend(infoSprite,
|
|
15167
15284
|
createDivCss2Label(`lblRetry`, `[${g_lblNameObj.l_retry}]`, {
|
|
15168
|
-
...g_lblPosObj.lblMainScHeader,
|
|
15169
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15170
|
-
y: g_headerObj.playingHeight - 65 + g_diffObj.shortcutY,
|
|
15285
|
+
...g_lblPosObj.lblMainScHeader, x: diffX, y: diffY - 95,
|
|
15171
15286
|
}),
|
|
15172
15287
|
createDivCss2Label(`lblRetrySc`, g_kCd[g_headerObj.keyRetry], {
|
|
15173
|
-
...g_lblPosObj.lblMainScKey,
|
|
15174
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15175
|
-
y: g_headerObj.playingHeight - 50 + g_diffObj.shortcutY,
|
|
15288
|
+
...g_lblPosObj.lblMainScKey, x: diffX, y: diffY - 80,
|
|
15176
15289
|
fontWeight: g_headerObj.keyRetry === C_KEY_RETRY ? `normal` : `bold`,
|
|
15177
15290
|
}),
|
|
15178
15291
|
createDivCss2Label(`lblTitleBack`, `[${g_lblNameObj.l_titleBack}]`, {
|
|
15179
|
-
...g_lblPosObj.lblMainScHeader,
|
|
15180
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15181
|
-
y: g_headerObj.playingHeight - 35 + g_diffObj.shortcutY,
|
|
15292
|
+
...g_lblPosObj.lblMainScHeader, x: diffX, y: diffY - 65,
|
|
15182
15293
|
}),
|
|
15183
15294
|
createDivCss2Label(`lblTitleBackSc`, g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack], {
|
|
15184
|
-
...g_lblPosObj.lblMainScKey,
|
|
15185
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15186
|
-
y: g_headerObj.playingHeight - 20 + g_diffObj.shortcutY,
|
|
15295
|
+
...g_lblPosObj.lblMainScKey, x: diffX, y: diffY - 50,
|
|
15187
15296
|
fontWeight: g_headerObj.keyTitleBack === C_KEY_TITLEBACK ? `normal` : `bold`,
|
|
15188
15297
|
}),
|
|
15298
|
+
createDivCss2Label(`lblPause`, `[${g_lblNameObj.l_pause}]`, {
|
|
15299
|
+
...g_lblPosObj.lblMainScHeader, x: diffX, y: diffY - 35,
|
|
15300
|
+
}),
|
|
15301
|
+
createDivCss2Label(`lblPauseSc`, g_kCd[g_headerObj.keyPause], {
|
|
15302
|
+
...g_lblPosObj.lblMainScKey, x: diffX, y: diffY - 20,
|
|
15303
|
+
fontWeight: g_headerObj.keyPause === C_KEY_PAUSE ? `normal` : `bold`,
|
|
15304
|
+
}),
|
|
15189
15305
|
);
|
|
15190
15306
|
}
|
|
15191
15307
|
|
|
@@ -15340,6 +15456,13 @@ const mainInit = () => {
|
|
|
15340
15456
|
if (evt.repeat && !g_mainRepeatObj.key.includes(setCode)) {
|
|
15341
15457
|
return blockCode(setCode);
|
|
15342
15458
|
}
|
|
15459
|
+
if (setCode === g_kCdN[g_headerObj.keyPause]) {
|
|
15460
|
+
isPaused ? resumeTimeline(true) : pauseTimeline(true);
|
|
15461
|
+
return blockCode(setCode);
|
|
15462
|
+
}
|
|
15463
|
+
if (isPaused) {
|
|
15464
|
+
return blockCode(setCode);
|
|
15465
|
+
}
|
|
15343
15466
|
g_inputKeyBuffer[setCode] = true;
|
|
15344
15467
|
mainKeyDownActFunc[g_stateObj.autoAll](setCode);
|
|
15345
15468
|
|
|
@@ -16302,6 +16425,149 @@ const mainInit = () => {
|
|
|
16302
16425
|
Math.min(Math.max(1000 / g_fps - buffTime, 0), g_maxFrameWait));
|
|
16303
16426
|
}
|
|
16304
16427
|
};
|
|
16428
|
+
|
|
16429
|
+
/*
|
|
16430
|
+
* タブのバックグラウンド化に伴う一時停止/再開
|
|
16431
|
+
* - 【既知の制限】movLock/initManual設定(movArrowYがCSS animation任せになるケース)では、
|
|
16432
|
+
* 矢印移動そのものがCSS animation駆動になるため、この一時停止機能はバックグラウンド中の
|
|
16433
|
+
* 一時停止を保証できない(演出効果と異なり判定位置に直結するため実プレイには不適)。
|
|
16434
|
+
* 対処は行わず、既知の制限として明記するに留める。
|
|
16435
|
+
*/
|
|
16436
|
+
const cancelResumeCountdown = () => {
|
|
16437
|
+
if (countdownTimeoutId !== null) {
|
|
16438
|
+
clearTimeout(countdownTimeoutId);
|
|
16439
|
+
countdownTimeoutId = null;
|
|
16440
|
+
}
|
|
16441
|
+
document.getElementById(`lblResumeCountdown`)?.remove();
|
|
16442
|
+
};
|
|
16443
|
+
|
|
16444
|
+
let nativeAudioWasPlaying = false;
|
|
16445
|
+
|
|
16446
|
+
const pauseTimeline = (_manual = false) => {
|
|
16447
|
+
// カウントダウン中に再度バックグラウンド化した場合は、カウントダウンのみ中断する
|
|
16448
|
+
// (音源は既に停止済み・gamePausedも付与済みのため、以降の処理は不要)
|
|
16449
|
+
cancelResumeCountdown();
|
|
16450
|
+
if (isPaused) {
|
|
16451
|
+
if (_manual) {
|
|
16452
|
+
// カウントダウン中に手動ポーズキーが押された場合、手動扱いに切り替える
|
|
16453
|
+
manualPauseFlg = true;
|
|
16454
|
+
}
|
|
16455
|
+
return;
|
|
16456
|
+
}
|
|
16457
|
+
isPaused = true;
|
|
16458
|
+
manualPauseFlg = _manual;
|
|
16459
|
+
pausedElapsedTime = null;
|
|
16460
|
+
pausedStartAdjustment = null;
|
|
16461
|
+
if (document.getElementById(`lblPauseMark`) == null) {
|
|
16462
|
+
const countdownLabel = createDivCss2Label(`lblPauseMark`, `Pause`, {
|
|
16463
|
+
...g_lblPosObj.lblPauseMark, x: g_workObj.playingX,
|
|
16464
|
+
});
|
|
16465
|
+
divRoot.appendChild(countdownLabel);
|
|
16466
|
+
}
|
|
16467
|
+
|
|
16468
|
+
if (g_audio instanceof AudioPlayer) {
|
|
16469
|
+
if (g_audio.contextTime < g_audio.scheduledTime) {
|
|
16470
|
+
// 音源開始前は、再生位置ではなく開始予定時刻までの残り時間を保持する。
|
|
16471
|
+
// g_audio.play() は g_scheduleLead を加算するため、その分を除く。
|
|
16472
|
+
pausedStartAdjustment = g_audio.scheduledTime
|
|
16473
|
+
- g_audio.contextTime - g_scheduleLead;
|
|
16474
|
+
} else {
|
|
16475
|
+
// 音源開始後: 再生位置を保存する。
|
|
16476
|
+
pausedElapsedTime = g_audio.elapsedTime - g_scheduleLead;
|
|
16477
|
+
}
|
|
16478
|
+
} else {
|
|
16479
|
+
// `paused` が false の場合だけ、再開時に play() を呼ぶ
|
|
16480
|
+
nativeAudioWasPlaying = !g_audio.paused;
|
|
16481
|
+
}
|
|
16482
|
+
clearTimeout(g_timeoutEvtId);
|
|
16483
|
+
g_audio.pause();
|
|
16484
|
+
|
|
16485
|
+
// フォーカスを失うとkeyupが届かなくなり、押しっぱなし判定・表示が残り得るため、
|
|
16486
|
+
// ここで強制的に全キー「離した」状態に戻す
|
|
16487
|
+
g_inputKeyBuffer = {};
|
|
16488
|
+
g_workObj.keyHitFlg.forEach(lane => lane.fill(false));
|
|
16489
|
+
mainKeyUpActFunc[g_stateObj.autoAll]();
|
|
16490
|
+
divRoot.classList.add(`gamePaused`);
|
|
16491
|
+
};
|
|
16492
|
+
|
|
16493
|
+
const resumeTimeline = (_manual = false) => {
|
|
16494
|
+
if (!isPaused || countdownTimeoutId !== null) {
|
|
16495
|
+
return;
|
|
16496
|
+
}
|
|
16497
|
+
if (!_manual && manualPauseFlg) {
|
|
16498
|
+
// 手動ポーズ中は、タブ復帰による自動再開をスキップする
|
|
16499
|
+
if (document.getElementById(`lblPauseMark`) == null) {
|
|
16500
|
+
const countdownLabel = createDivCss2Label(`lblPauseMark`, `Pause`, {
|
|
16501
|
+
...g_lblPosObj.lblPauseMark, x: g_workObj.playingX,
|
|
16502
|
+
});
|
|
16503
|
+
divRoot.appendChild(countdownLabel);
|
|
16504
|
+
}
|
|
16505
|
+
return;
|
|
16506
|
+
}
|
|
16507
|
+
deleteDiv(divRoot, `lblPauseMark`);
|
|
16508
|
+
const countdownLabel = createDivCss2Label(`lblResumeCountdown`, ``, {
|
|
16509
|
+
...g_lblPosObj.lblPauseMark, x: g_workObj.playingX,
|
|
16510
|
+
});
|
|
16511
|
+
divRoot.appendChild(countdownLabel);
|
|
16512
|
+
|
|
16513
|
+
const finishResume = () => {
|
|
16514
|
+
countdownTimeoutId = null;
|
|
16515
|
+
countdownLabel.remove();
|
|
16516
|
+
|
|
16517
|
+
// カウントダウン中に曲中リトライ等でmainInit()が再実行されたりプレイ画面外に移動した場合、
|
|
16518
|
+
// 古いセッションのfinishResumeが後から発火して二重再生・二重ループを起こすのを防ぐ
|
|
16519
|
+
if (mySessionId !== g_timelineSessionId || g_currentPage !== `main`) {
|
|
16520
|
+
return;
|
|
16521
|
+
}
|
|
16522
|
+
|
|
16523
|
+
if (g_audio instanceof AudioPlayer) {
|
|
16524
|
+
// バックグラウンド中にsuspendされていた場合に備え、先に明示的にresumeを試みる
|
|
16525
|
+
getSharedAudioContext();
|
|
16526
|
+
|
|
16527
|
+
if (pausedStartAdjustment !== null) {
|
|
16528
|
+
// 開始前: 音源と譜面の残り開始時間を両方維持する。
|
|
16529
|
+
g_audio.play(pausedStartAdjustment);
|
|
16530
|
+
musicStartCtxTime = g_audio.scheduledTime
|
|
16531
|
+
+ (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
|
|
16532
|
+
// musicStartFrame は変更しない。
|
|
16533
|
+
} else {
|
|
16534
|
+
// 開始後: 再生位置から再開し、現在フレームを基準に再アンカーする。
|
|
16535
|
+
g_audio.currentTime = pausedElapsedTime;
|
|
16536
|
+
g_audio.play();
|
|
16537
|
+
musicStartCtxTime = g_audio.scheduledTime
|
|
16538
|
+
+ (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
|
|
16539
|
+
musicStartFrame = g_scoreObj.frameNum;
|
|
16540
|
+
}
|
|
16541
|
+
|
|
16542
|
+
} else {
|
|
16543
|
+
if (nativeAudioWasPlaying) {
|
|
16544
|
+
g_audio.play();
|
|
16545
|
+
}
|
|
16546
|
+
}
|
|
16547
|
+
// バックグラウンド経過時間をフレーム計算から除外する
|
|
16548
|
+
musicStartTime = performance.now() - (g_scoreObj.frameNum - musicStartFrame) * 1000 / g_fps;
|
|
16549
|
+
|
|
16550
|
+
divRoot.classList.remove(`gamePaused`);
|
|
16551
|
+
isPaused = false;
|
|
16552
|
+
manualPauseFlg = false;
|
|
16553
|
+
pausedElapsedTime = null;
|
|
16554
|
+
pausedStartAdjustment = null;
|
|
16555
|
+
g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
|
|
16556
|
+
};
|
|
16557
|
+
|
|
16558
|
+
const tick = _remaining => {
|
|
16559
|
+
if (_remaining <= 0) {
|
|
16560
|
+
finishResume();
|
|
16561
|
+
return;
|
|
16562
|
+
}
|
|
16563
|
+
countdownLabel.innerHTML = String(_remaining);
|
|
16564
|
+
countdownTimeoutId = setTimeout(() => tick(_remaining - 1), 1000);
|
|
16565
|
+
};
|
|
16566
|
+
tick(3);
|
|
16567
|
+
};
|
|
16568
|
+
|
|
16569
|
+
g_timelineHooks.pause = pauseTimeline;
|
|
16570
|
+
g_timelineHooks.resume = resumeTimeline;
|
|
16305
16571
|
safeExecuteCustomHooks(`g_skinJsObj.main`, g_skinJsObj.main);
|
|
16306
16572
|
|
|
16307
16573
|
g_audio.currentTime = firstFrame / g_fps * g_headerObj.playbackRate;
|
|
@@ -16319,7 +16585,11 @@ const mainInit = () => {
|
|
|
16319
16585
|
+ (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
|
|
16320
16586
|
}
|
|
16321
16587
|
|
|
16322
|
-
|
|
16588
|
+
if (document.hidden) {
|
|
16589
|
+
pauseTimeline();
|
|
16590
|
+
} else {
|
|
16591
|
+
g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
|
|
16592
|
+
}
|
|
16323
16593
|
};
|
|
16324
16594
|
|
|
16325
16595
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Source by tickle
|
|
7
7
|
* Created : 2019/11/19
|
|
8
|
-
* Revised : 2026/08/
|
|
8
|
+
* Revised : 2026/08/29 (v50.1.0)
|
|
9
9
|
*
|
|
10
10
|
* https://github.com/cwtickle/danoniplus
|
|
11
11
|
*/
|
|
@@ -186,6 +186,7 @@ const g_lblPosObj = {};
|
|
|
186
186
|
const getScMsg = {
|
|
187
187
|
TitleBack: () => g_lblNameObj.kcShortcutDesc1.split(`{0}`).join(g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack]),
|
|
188
188
|
Retry: () => g_lblNameObj.kcShortcutDesc2.split(`{1}`).join(g_kCd[g_headerObj.keyRetry]),
|
|
189
|
+
Pause: () => g_lblNameObj.kcShortcutDesc3.split(`{2}`).join(g_kCd[g_headerObj.keyPause]),
|
|
189
190
|
};
|
|
190
191
|
|
|
191
192
|
/**
|
|
@@ -676,11 +677,15 @@ const updateWindowSiz = () => {
|
|
|
676
677
|
},
|
|
677
678
|
scTitleBack: {
|
|
678
679
|
x: g_btnX(1 / 4) + 20, y: g_sHeight - 50, align: C_ALIGN_LEFT,
|
|
679
|
-
w: g_btnWidth(
|
|
680
|
+
w: g_btnWidth(1 / 4) - 40, h: C_KYC_REPHEIGHT, siz: getFontSize2(getScMsg.TitleBack(), g_btnWidth(1 / 4) - 40, { maxSiz: 13 }),
|
|
680
681
|
},
|
|
681
682
|
scRetry: {
|
|
682
|
-
x: g_btnX(
|
|
683
|
-
w: g_btnWidth(
|
|
683
|
+
x: g_btnX(1 / 2) + 20, y: g_sHeight - 50, align: C_ALIGN_LEFT,
|
|
684
|
+
w: g_btnWidth(1 / 4) - 40, h: C_KYC_REPHEIGHT, siz: getFontSize2(getScMsg.Retry(), g_btnWidth(1 / 4) - 40, { maxSiz: 13 }),
|
|
685
|
+
},
|
|
686
|
+
scPause: {
|
|
687
|
+
x: g_btnX(3 / 4) + 20, y: g_sHeight - 50, align: C_ALIGN_LEFT,
|
|
688
|
+
w: g_btnWidth(1 / 4) - 40, h: C_KYC_REPHEIGHT, siz: getFontSize2(getScMsg.Pause(), g_btnWidth(1 / 4) - 40, { maxSiz: 13 }),
|
|
684
689
|
},
|
|
685
690
|
|
|
686
691
|
/** メイン画面 */
|
|
@@ -739,6 +744,10 @@ const updateWindowSiz = () => {
|
|
|
739
744
|
lblMainScKey: {
|
|
740
745
|
x: g_sWidth + g_headerObj.scAreaWidth - 85, w: 80, h: 20, siz: 12, align: C_ALIGN_RIGHT,
|
|
741
746
|
},
|
|
747
|
+
lblPauseMark: {
|
|
748
|
+
y: g_headerObj.playingY + (g_headerObj.playingHeight + g_posObj.stepYR) / 2 - 75,
|
|
749
|
+
w: g_sWidth, h: 50, siz: 40, background: `#000000cc`, align: C_ALIGN_CENTER,
|
|
750
|
+
},
|
|
742
751
|
|
|
743
752
|
/** 結果画面 */
|
|
744
753
|
lblRank: {
|
|
@@ -1126,6 +1135,7 @@ const C_FRM_FRZATTEMPT = 5;
|
|
|
1126
1135
|
/** ショートカットキー */
|
|
1127
1136
|
const C_KEY_RETRY = 8;
|
|
1128
1137
|
const C_KEY_TITLEBACK = 46;
|
|
1138
|
+
const C_KEY_PAUSE = 19;
|
|
1129
1139
|
|
|
1130
1140
|
/** 判定系共通オブジェクト */
|
|
1131
1141
|
const g_judgObj = {
|
|
@@ -4841,6 +4851,7 @@ const g_lang_lblNameObj = {
|
|
|
4841
4851
|
kcShortcutDesc: `プレイ中ショートカット:`,
|
|
4842
4852
|
kcShortcutDesc1: `タイトルバック: {0}`,
|
|
4843
4853
|
kcShortcutDesc2: `リトライ: {1}`,
|
|
4854
|
+
kcShortcutDesc3: `ポーズ: {2}`,
|
|
4844
4855
|
transKeyDesc: `別キーモードではキーコンフィグ、ColorType等は保存されません`,
|
|
4845
4856
|
colorTypeDesc: `現在のColorTypeの設定では、色変化(Display:Color)は自動的にOFFになります`,
|
|
4846
4857
|
sdShortcutDesc: `Hid+/Sud+時ショートカット:「pageUp」カバーを上へ / 「pageDown」下へ`,
|
|
@@ -4888,6 +4899,7 @@ const g_lang_lblNameObj = {
|
|
|
4888
4899
|
|
|
4889
4900
|
l_retry: `リトライ`,
|
|
4890
4901
|
l_titleBack: `タイトルバック`,
|
|
4902
|
+
l_pause: `ポーズ`,
|
|
4891
4903
|
|
|
4892
4904
|
helpUrl: `https://github.com/cwtickle/danoniplus/wiki/AboutGameSystem`,
|
|
4893
4905
|
securityUrl: `https://github.com/cwtickle/danoniplus/security/policy`,
|
|
@@ -4905,6 +4917,7 @@ const g_lang_lblNameObj = {
|
|
|
4905
4917
|
kcShortcutDesc: `Shortcut during play:`,
|
|
4906
4918
|
kcShortcutDesc1: `Return to title: {0}`,
|
|
4907
4919
|
kcShortcutDesc2: `Retry the game: {1}`,
|
|
4920
|
+
kcShortcutDesc3: `Pause: {2}`,
|
|
4908
4921
|
transKeyDesc: `Key config, Color type, etc. are not saved for alternate keymode`,
|
|
4909
4922
|
colorTypeDesc: `With the current ColorType setting, color change (Display:Color) will be automatically turned OFF.`,
|
|
4910
4923
|
sdShortcutDesc: `When "Hidden+" or "Sudden+" select, "pageUp" cover up / "pageDown" cover down`,
|
|
@@ -4952,6 +4965,7 @@ const g_lang_lblNameObj = {
|
|
|
4952
4965
|
|
|
4953
4966
|
l_retry: `Retry`,
|
|
4954
4967
|
l_titleBack: `Go to title`,
|
|
4968
|
+
l_pause: `Pause`,
|
|
4955
4969
|
|
|
4956
4970
|
helpUrl: `https://github.com/cwtickle/danoniplus-docs/wiki/AboutGameSystem`,
|
|
4957
4971
|
securityUrl: `https://github.com/cwtickle/danoniplus-docs/wiki/SecurityPolicy`,
|
|
@@ -5273,6 +5287,27 @@ const g_skinJsObj = {
|
|
|
5273
5287
|
result: [],
|
|
5274
5288
|
};
|
|
5275
5289
|
|
|
5290
|
+
const g_root = {
|
|
5291
|
+
get g_diffObj() { return g_diffObj; },
|
|
5292
|
+
get g_posObj() { return g_posObj; },
|
|
5293
|
+
get g_headerObj() { return g_headerObj; },
|
|
5294
|
+
get g_stateObj() { return g_stateObj; },
|
|
5295
|
+
get g_keycons() { return g_keycons; },
|
|
5296
|
+
};
|
|
5297
|
+
const getPathVal = _path => _path.split(`.`).reduce((o, k) => o?.[k], g_root);
|
|
5298
|
+
const setPathVal = (_path, _value) => {
|
|
5299
|
+
const keys = _path.split(`.`);
|
|
5300
|
+
const last = keys.pop();
|
|
5301
|
+
const target = keys.reduce((o, k) => o?.[k], g_root);
|
|
5302
|
+
if (target == null || !Object.hasOwn(target, last)) {
|
|
5303
|
+
console.warn(`setPathVal: path not found for "${_path}"`);
|
|
5304
|
+
return;
|
|
5305
|
+
}
|
|
5306
|
+
target[last] = _value;
|
|
5307
|
+
};
|
|
5308
|
+
|
|
5309
|
+
const g_familyObj = { families: [], currentFamily: null, ownership: {}, snapshot: {}, applied: {}, appliedPaths: new Set() };
|
|
5310
|
+
|
|
5276
5311
|
const g_errorCache = {
|
|
5277
5312
|
'g_customJsObj.titleEnterFrame': [],
|
|
5278
5313
|
'g_customJsObj.mainEnterFrame': [],
|