danoniplus 50.4.0 → 50.5.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 +479 -16549
- package/js/lib/danoni_constants.js +2 -1
- package/js/lib/dataLoader.js +2837 -0
- package/js/lib/dosConverter.js +3093 -0
- package/js/lib/keyconfig.js +1932 -0
- package/js/lib/mainWindow.js +2626 -0
- package/js/lib/result.js +979 -0
- package/js/lib/settings.js +3426 -0
- package/js/lib/title.js +1274 -0
- package/package.json +1 -1
|
@@ -0,0 +1,3426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dancing☆Onigiri (CW Edition)
|
|
3
|
+
* Settings, Display, Ex-Settings画面
|
|
4
|
+
* - ページ: option, difSelector, scoreDetail, settingsDisplay, displayPreview, exSetting
|
|
5
|
+
*
|
|
6
|
+
* Source by tickle
|
|
7
|
+
* Created : 2026/09/13
|
|
8
|
+
* Revised :
|
|
9
|
+
*
|
|
10
|
+
* https://github.com/cwtickle/danoniplus
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/*-----------------------------------------------------------*/
|
|
14
|
+
/* Scene : SETTINGS [lime] */
|
|
15
|
+
/*-----------------------------------------------------------*/
|
|
16
|
+
|
|
17
|
+
const commonSettingBtn = _labelName => {
|
|
18
|
+
|
|
19
|
+
const switchSave = evt => {
|
|
20
|
+
const from = boolToSwitch(g_stateObj.dataSaveFlg);
|
|
21
|
+
g_stateObj.dataSaveFlg = !g_stateObj.dataSaveFlg;
|
|
22
|
+
updateSettingSummary();
|
|
23
|
+
|
|
24
|
+
const to = boolToSwitch(g_stateObj.dataSaveFlg);
|
|
25
|
+
evt.target.classList.replace(g_cssObj[`button_${from}`], g_cssObj[`button_${to}`]);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
multiAppend(divRoot,
|
|
29
|
+
|
|
30
|
+
// タイトル画面へ戻る
|
|
31
|
+
createCss2Button(`btnBack`, g_lblNameObj.b_back, () => true, {
|
|
32
|
+
...g_lblPosObj.btnBack,
|
|
33
|
+
animationName: (g_initialFlg ? `` : `smallToNormalY`), resetFunc: () => titleInit(),
|
|
34
|
+
}, g_cssObj.button_Back),
|
|
35
|
+
|
|
36
|
+
// キーコンフィグ画面へ移動
|
|
37
|
+
createCss2Button(`btnKeyConfig`, g_lblNameObj.b_keyConfig, () => true, {
|
|
38
|
+
...g_lblPosObj.btnKeyConfig,
|
|
39
|
+
animationName: (g_initialFlg ? `` : `smallToNormalY`), resetFunc: () => keyConfigInit(`Main`, true),
|
|
40
|
+
}, g_cssObj.button_Setting),
|
|
41
|
+
|
|
42
|
+
// プレイ開始
|
|
43
|
+
makePlayButton(() => loadMusic()),
|
|
44
|
+
|
|
45
|
+
// Display設定へ移動
|
|
46
|
+
createCss2Button(`btn${_labelName}`, `>`, () => true, {
|
|
47
|
+
...g_lblPosObj.btnSwitchSetting,
|
|
48
|
+
title: g_msgObj[`to${_labelName}`], resetFunc: () => g_moveSettingWindow(),
|
|
49
|
+
cxtFunc: () => g_moveSettingWindow(true, -1),
|
|
50
|
+
}, g_cssObj.button_Mini),
|
|
51
|
+
|
|
52
|
+
// データセーブフラグの切替
|
|
53
|
+
createCss2Button(`btnSave`, g_lblNameObj.dataSave, evt => switchSave(evt), {
|
|
54
|
+
...g_lblPosObj.btnSave,
|
|
55
|
+
cxtFunc: evt => switchSave(evt),
|
|
56
|
+
visibility: g_langStorage.safeMode === C_FLG_OFF ? C_DIS_INHERIT : `hidden`,
|
|
57
|
+
}, g_cssObj.button_Default, (g_stateObj.dataSaveFlg ? g_cssObj.button_ON : g_cssObj.button_OFF)),
|
|
58
|
+
|
|
59
|
+
// データ管理画面へ移動
|
|
60
|
+
createCss2Button(`btnReset`, g_lblNameObj.dataReset, () => {
|
|
61
|
+
dataMgtInit();
|
|
62
|
+
}, g_lblPosObj.btnReset, g_cssObj.button_Reset),
|
|
63
|
+
|
|
64
|
+
// 前提条件表示用画面へ移動(debug=trueの場合のみ)
|
|
65
|
+
createCss2Button(`btnPrecond`, g_lblNameObj.b_precond, () => true, {
|
|
66
|
+
...g_lblPosObj.btnPrecond,
|
|
67
|
+
resetFunc: () => preconditionInit(),
|
|
68
|
+
}, g_cssObj.button_Setting),
|
|
69
|
+
|
|
70
|
+
// 設定内容サマリを表示
|
|
71
|
+
createCss2Button(`btnSettingSummary`, `>`, () => true, {
|
|
72
|
+
...g_lblPosObj.btnSettingSummary,
|
|
73
|
+
resetFunc: () => {
|
|
74
|
+
g_stateObj.settingSummaryVisible = !g_stateObj.settingSummaryVisible;
|
|
75
|
+
visibleSettingSummary(g_stateObj.settingSummaryVisible);
|
|
76
|
+
},
|
|
77
|
+
}, g_cssObj.button_Mini),
|
|
78
|
+
);
|
|
79
|
+
makeSettingSummary();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const makeSettingSummary = () => {
|
|
83
|
+
const tmpDiv = createEmptySprite(divRoot, `settingSumSprite`, g_windowObj.settingSumSprite);
|
|
84
|
+
tmpDiv.style.background = g_headerObj.baseBrightFlg ? `#ffffffee` : `#000000cc`;
|
|
85
|
+
|
|
86
|
+
multiAppend(tmpDiv,
|
|
87
|
+
createDivCss2Label(`lblSummaryHeader`, g_lblNameObj.settingSummary, g_lblPosObj.lblSummaryHeader),
|
|
88
|
+
createDivCss2Label(`lblSummaryEnvironment`, ``, g_lblPosObj.lblSummaryEnvironment),
|
|
89
|
+
createDivCss2Label(`lblSummaryDifHeader`, g_lblNameObj.rt_Difficulty, g_lblPosObj.lblSummaryDifHeader),
|
|
90
|
+
createDivCss2Label(`lblSummaryDifInfo`, ``, g_lblPosObj.lblSummaryDifInfo),
|
|
91
|
+
createDivCss2Label(`lblSummaryPlaystyleHeader`, g_lblNameObj.rt_Style, g_lblPosObj.lblSummaryPlaystyleHeader),
|
|
92
|
+
createDivCss2Label(`lblSummaryPlaystyleInfo`, ``, g_lblPosObj.lblSummaryPlaystyleInfo),
|
|
93
|
+
createDivCss2Label(`lblSummaryDisplayHeader`, g_lblNameObj.rt_Display, g_lblPosObj.lblSummaryDisplayHeader),
|
|
94
|
+
createDivCss2Label(`lblSummaryDisplayInfo`, ``, g_lblPosObj.lblSummaryDisplayInfo),
|
|
95
|
+
createDivCss2Label(`lblSummaryDisplay2Info`, ``, g_lblPosObj.lblSummaryDisplay2Info),
|
|
96
|
+
);
|
|
97
|
+
tmpDiv.style.visibility = g_stateObj.settingSummaryVisible ? `visible` : `hidden`;
|
|
98
|
+
dragDiv(`settingSumSprite`, {
|
|
99
|
+
minX: g_btnX() + 25, maxX: g_btnX() + 25, minY: 10, maxY: g_sHeight - 10,
|
|
100
|
+
});
|
|
101
|
+
if (g_posObj.settingSumSprite?.y !== undefined) {
|
|
102
|
+
document.getElementById(`settingSumSprite`).style.top = wUnit(g_posObj.settingSumSprite.y);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const visibleSettingSummary = _visible => {
|
|
107
|
+
const summaryDiv = document.getElementById(`settingSumSprite`);
|
|
108
|
+
if (summaryDiv) {
|
|
109
|
+
summaryDiv.style.visibility = _visible ? `visible` : `hidden`;
|
|
110
|
+
updateSettingSummary();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const updateSettingSummary = () => {
|
|
115
|
+
if (document.getElementById(`settingSumSprite`) === null) return;
|
|
116
|
+
const orgShuffleFlg = getOrgShuffleFlg();
|
|
117
|
+
const settingData = getSelectedSettingList(orgShuffleFlg);
|
|
118
|
+
const estimatedHighscoreCondition = g_stateObj.dataSaveFlg && (g_stateObj.autoPlay !== C_FLG_ALL && g_headerObj.playbackRate === 1 && g_stateObj.fadein < 10 &&
|
|
119
|
+
(g_stateObj.shuffle === C_FLG_OFF || (g_stateObj.shuffle.endsWith(`Mirror`) && orgShuffleFlg)));
|
|
120
|
+
|
|
121
|
+
document.getElementById(`lblSummaryDifInfo`).innerHTML = settingData.difData + `${estimatedHighscoreCondition ? '' : ` | <span class="common_auto common_bold">No Records</span>`}`;
|
|
122
|
+
document.getElementById(`lblSummaryPlaystyleInfo`).textContent = settingData.playStyleData + `${g_stateObj.excessive === C_FLG_ON ? ' | Excessive' : ''}`;
|
|
123
|
+
document.getElementById(`lblSummaryDisplayInfo`).textContent = settingData.displayData;
|
|
124
|
+
document.getElementById(`lblSummaryDisplay2Info`).textContent = settingData.display2Data;
|
|
125
|
+
document.getElementById(`lblSummaryEnvironment`).textContent =
|
|
126
|
+
`(Adj: ${g_stateObj.adjustment} f, Volume: ${g_stateObj.volume}%, ` +
|
|
127
|
+
`ColorType: ${g_colorType}, KeyPattern: ${g_keyObj.currentPtn === -1 ? 'Self' : g_keyObj.currentPtn + 1})`;
|
|
128
|
+
|
|
129
|
+
safeExecuteCustomHooks(`g_customJsObj.settingSummary`, g_customJsObj.settingSummary);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* PLAYボタンの作成
|
|
134
|
+
* @param {Function} _func
|
|
135
|
+
* @returns {HTMLDivElement}
|
|
136
|
+
*/
|
|
137
|
+
const makePlayButton = _func => createCss2Button(`btnPlay`, g_lblNameObj.b_play, () => true, {
|
|
138
|
+
...g_lblPosObj.btnPlay, animationName: (g_initialFlg ? `` : `smallToNormalY`), resetFunc: _func,
|
|
139
|
+
}, g_cssObj.button_Next);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 設定・オプション画面初期化
|
|
143
|
+
*/
|
|
144
|
+
const optionInit = () => {
|
|
145
|
+
|
|
146
|
+
clearWindow();
|
|
147
|
+
pauseBGM();
|
|
148
|
+
const divRoot = document.getElementById(`divRoot`);
|
|
149
|
+
g_currentPage = `option`;
|
|
150
|
+
g_stateObj.filterKeys = ``;
|
|
151
|
+
|
|
152
|
+
// 楽曲データの表示
|
|
153
|
+
const text = getMusicInfoView();
|
|
154
|
+
divRoot.appendChild(createDivCss2Label(`lblMusicInfo`, text,
|
|
155
|
+
{ ...g_lblPosObj.lblMusicInfo, siz: getFontSize2(text, g_btnWidth(3 / 4), { maxSiz: 12 }) }));
|
|
156
|
+
|
|
157
|
+
// タイトル文字描画
|
|
158
|
+
divRoot.appendChild(getTitleDivLabel(`lblTitle`, g_lblNameObj.settings, 0, 15, `settings_Title`));
|
|
159
|
+
|
|
160
|
+
// オプションボタン用の設置
|
|
161
|
+
createOptionWindow(divRoot);
|
|
162
|
+
|
|
163
|
+
// ユーザカスタムイベント(初期)
|
|
164
|
+
safeExecuteCustomHooks(`g_customJsObj.option`, g_customJsObj.option);
|
|
165
|
+
|
|
166
|
+
// ボタン描画
|
|
167
|
+
commonSettingBtn(`Display`);
|
|
168
|
+
|
|
169
|
+
// キー操作イベント(デフォルト)
|
|
170
|
+
setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
|
|
171
|
+
document.oncontextmenu = () => true;
|
|
172
|
+
g_initialFlg = true;
|
|
173
|
+
|
|
174
|
+
safeExecuteCustomHooks(`g_skinJsObj.option`, g_skinJsObj.option);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 設定画面に表示する楽曲・BPM情報の取得
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
const getMusicInfoView = () => {
|
|
182
|
+
const idx = g_headerObj.musicNos[g_stateObj.scoreId] || 0;
|
|
183
|
+
let text = `♪` + (g_headerObj.musicSelectUse ? `${unEscapeHtml(g_headerObj.musicTitles[idx])} / ` : ``) +
|
|
184
|
+
`BPM: ${g_headerObj.bpms[idx]}`;
|
|
185
|
+
if (!g_headerObj.musicSelectUse && g_headerObj.bpms[idx] === `----`) {
|
|
186
|
+
text = ``;
|
|
187
|
+
}
|
|
188
|
+
return text;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 設定画面用スプライトリストの作成
|
|
193
|
+
* @param {object} _settingList (設定名、縦位置、縦位置差分、幅差分、高さ差分)を設定別にリスト化
|
|
194
|
+
* @returns
|
|
195
|
+
*/
|
|
196
|
+
const setSpriteList = _settingList => {
|
|
197
|
+
const optionWidth = (g_sWidth - 450) / 2;
|
|
198
|
+
const spriteList = [];
|
|
199
|
+
Object.keys(_settingList).forEach(setting =>
|
|
200
|
+
spriteList[setting] = createEmptySprite(optionsprite, `${setting}Sprite`, {
|
|
201
|
+
x: 25, y: _settingList[setting].heightPos * g_limitObj.setLblHeight + (_settingList[setting].y || 0) + 20,
|
|
202
|
+
w: optionWidth + (_settingList[setting].dw || 0), h: g_limitObj.setLblHeight + (_settingList[setting].dh || 0),
|
|
203
|
+
}));
|
|
204
|
+
return spriteList;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* スライダー共通処理 (Fadein, Appearance)
|
|
209
|
+
* @param {HTMLInputElement} _slider
|
|
210
|
+
* @param {HTMLDivElement} _link
|
|
211
|
+
* @param {string} _type
|
|
212
|
+
* @returns {string}
|
|
213
|
+
*/
|
|
214
|
+
const inputSlider = (_slider, _link, _type) => {
|
|
215
|
+
const value = parseInt(_slider.value);
|
|
216
|
+
_link.textContent = g_sliderView.get(_type)(value);
|
|
217
|
+
return value;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 譜面変更セレクターの削除
|
|
222
|
+
*/
|
|
223
|
+
const resetDifWindow = () => {
|
|
224
|
+
if (document.getElementById(`difList`) !== null) {
|
|
225
|
+
deleteChildspriteAll(`difList`);
|
|
226
|
+
[`difList`, `difCover`, `btnDifU`, `btnDifD`].forEach(obj => document.getElementById(obj).remove());
|
|
227
|
+
g_currentPage = `option`;
|
|
228
|
+
setShortcutEvent(g_currentPage, () => true, { displayFlg: false, dfEvtFlg: true });
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 次の譜面番号を取得
|
|
234
|
+
* @param {number} _scoreId
|
|
235
|
+
* @param {number} _scrollNum
|
|
236
|
+
* @returns {number}
|
|
237
|
+
*/
|
|
238
|
+
const getNextDifficulty = (_scoreId, _scrollNum) => {
|
|
239
|
+
const currentPosIdx = g_headerObj.viewLists.findIndex(val => val === _scoreId);
|
|
240
|
+
const nextPosIdx = (currentPosIdx === -1 ? 0 : nextPos(currentPosIdx, _scrollNum, g_headerObj.viewLists.length));
|
|
241
|
+
return g_headerObj.viewLists[nextPosIdx];
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 譜面選択処理
|
|
246
|
+
* @param {number} _scrollNum
|
|
247
|
+
*/
|
|
248
|
+
const nextDifficulty = (_scrollNum = 1) => {
|
|
249
|
+
g_keyObj.prevKey = g_headerObj.keyLabels[g_stateObj.scoreId];
|
|
250
|
+
g_stateObj.scoreId = getNextDifficulty(g_stateObj.scoreId, _scrollNum);
|
|
251
|
+
setDifficulty(true);
|
|
252
|
+
resetDifWindow();
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 譜面リストの作成
|
|
257
|
+
* @param {HTMLDivElement} _difList
|
|
258
|
+
* @param {string} _targetKey
|
|
259
|
+
*/
|
|
260
|
+
const makeDifList = (_difList, _targetKey = ``) => {
|
|
261
|
+
let k = 0, pos = 0, curk = -1;
|
|
262
|
+
g_headerObj.viewLists.forEach(j => {
|
|
263
|
+
const keyLabel = g_headerObj.keyLabels[j];
|
|
264
|
+
if (_targetKey === `` || keyLabel === _targetKey) {
|
|
265
|
+
|
|
266
|
+
// 譜面名の表示
|
|
267
|
+
const prefix = `${getKeyName(keyLabel)} / `;
|
|
268
|
+
let text = `${g_headerObj.difLabels[j]}`;
|
|
269
|
+
if (g_headerObj.makerView) {
|
|
270
|
+
text += ` (${g_headerObj.creatorNames[j]})`;
|
|
271
|
+
}
|
|
272
|
+
// キー種と譜面名に分割し、譜面名が長すぎる場合は二段に分割して表示
|
|
273
|
+
const [difText, difSiz] = getFontSizeMulti(text, g_limitObj.difSelectorWidth, {
|
|
274
|
+
maxSiz: g_limitObj.difSelectorSiz, maxSizMulti: 9, prefix, len: 30,
|
|
275
|
+
})
|
|
276
|
+
_difList.appendChild(makeDifLblCssButton(`dif${k}`, difText, k, () => nextDifficulty(j - g_stateObj.scoreId), {
|
|
277
|
+
btnStyle: (j === g_stateObj.scoreId ? `Setting` : `Default`), siz: difSiz,
|
|
278
|
+
}));
|
|
279
|
+
document.getElementById(`dif${k}`).style.lineHeight = `9px`;
|
|
280
|
+
if (j === g_stateObj.scoreId) {
|
|
281
|
+
pos = k + 6.5 * (g_sHeight - 239) / 261;
|
|
282
|
+
curk = k;
|
|
283
|
+
}
|
|
284
|
+
k++;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (document.getElementById(`lblDifCnt`) === null) {
|
|
288
|
+
difCover.appendChild(createDivCss2Label(`lblDifCnt`, ``, {
|
|
289
|
+
x: 0, y: 27, w: g_limitObj.difCoverWidth, h: 16, siz: 12, fontWeight: `bold`,
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
// フィルタなし> ALL: 1/ 5, フィルタあり> 7k: 1/ 1 のように現在位置と(フィルタされた)譜面数を表示
|
|
293
|
+
// 現在位置が不確定の場合は、7k: -/ 1 のように現在位置を「-」で表示
|
|
294
|
+
const keyUnitAbbName = getStgDetailName(getKeyUnitName(_targetKey)).slice(0, 1) || ``;
|
|
295
|
+
lblDifCnt.innerHTML = `${_targetKey === '' ? 'ALL' : getKeyName(_targetKey) + keyUnitAbbName}: ${curk === -1 ? '-' : curk + 1} / ${k}`;
|
|
296
|
+
_difList.scrollTop = Math.max(pos * g_limitObj.setLblHeight - parseInt(_difList.style.height), 0);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* 譜面セレクター位置の変更ボタン
|
|
301
|
+
* @param {number} _scrollNum
|
|
302
|
+
*/
|
|
303
|
+
const makeDifBtn = (_scrollNum = 1) => {
|
|
304
|
+
const dir = _scrollNum === 1 ? `D` : `U`;
|
|
305
|
+
return createCss2Button(`btnDif${dir}`, g_settingBtnObj.chara[dir], () => {
|
|
306
|
+
do {
|
|
307
|
+
g_stateObj.scoreId = getNextDifficulty(g_stateObj.scoreId, _scrollNum);
|
|
308
|
+
} while (g_stateObj.filterKeys !== `` && g_stateObj.filterKeys !== g_headerObj.keyLabels[g_stateObj.scoreId]);
|
|
309
|
+
setDifficulty(true);
|
|
310
|
+
deleteChildspriteAll(`difList`);
|
|
311
|
+
makeDifList(difList, g_stateObj.filterKeys);
|
|
312
|
+
g_keyObj.prevKey = g_keyObj.currentKey;
|
|
313
|
+
}, {
|
|
314
|
+
x: 430 + _scrollNum * 10, y: 40, w: 20, h: 20, siz: g_limitObj.jdgCntsSiz,
|
|
315
|
+
}, g_cssObj.button_Mini);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 譜面変更セレクターの作成・再作成
|
|
320
|
+
* @param {string} [_key=''] 絞り込みするキー名(無指定で絞り込みしない)
|
|
321
|
+
*/
|
|
322
|
+
const createDifWindow = (_key = ``) => {
|
|
323
|
+
g_currentPage = `difSelector`;
|
|
324
|
+
setShortcutEvent(g_currentPage);
|
|
325
|
+
const difList = createEmptySprite(optionsprite, `difList`, g_windowObj.difList, g_cssObj.settings_DifSelector);
|
|
326
|
+
const difCover = createEmptySprite(optionsprite, `difCover`, g_windowObj.difCover, g_cssObj.settings_DifSelector);
|
|
327
|
+
const difFilter = createEmptySprite(difCover, `difFilter`, g_windowObj.difFilter, g_cssObj.settings_DifSelector)
|
|
328
|
+
|
|
329
|
+
// リスト再作成
|
|
330
|
+
makeDifList(difList, _key);
|
|
331
|
+
|
|
332
|
+
// ランダム選択
|
|
333
|
+
difCover.appendChild(
|
|
334
|
+
makeDifLblCssButton(`difRandom`, `RANDOM`, 0, () => {
|
|
335
|
+
nextDifficulty(Math.floor(Math.random() * g_headerObj.keyLabels.length));
|
|
336
|
+
}, { w: g_limitObj.difCoverWidth })
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// 全リスト
|
|
340
|
+
difCover.appendChild(
|
|
341
|
+
makeDifLblCssButton(`keyFilter`, `ALL`, 1.9, () => {
|
|
342
|
+
resetDifWindow();
|
|
343
|
+
g_stateObj.filterKeys = ``;
|
|
344
|
+
createDifWindow();
|
|
345
|
+
}, { w: g_limitObj.difCoverWidth, btnStyle: (g_stateObj.filterKeys === `` ? `Setting` : `Default`) })
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// キー別フィルタボタン作成
|
|
349
|
+
let pos = 0;
|
|
350
|
+
g_headerObj.viewKeyLists.forEach((targetKey, m) => {
|
|
351
|
+
difFilter.appendChild(
|
|
352
|
+
makeDifLblCssButton(`keyFilter${m}`, `${getKeyName(targetKey)} ${getStgDetailName(getKeyUnitName(targetKey))}`, m, () => {
|
|
353
|
+
resetDifWindow();
|
|
354
|
+
g_stateObj.filterKeys = targetKey;
|
|
355
|
+
createDifWindow(targetKey);
|
|
356
|
+
}, { w: g_limitObj.difCoverWidth, btnStyle: (g_stateObj.filterKeys === targetKey ? `Setting` : `Default`) })
|
|
357
|
+
);
|
|
358
|
+
if (g_stateObj.filterKeys === targetKey) {
|
|
359
|
+
pos = m + 5 * (g_sHeight - 300) / 200;
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
difFilter.scrollTop = Math.max(pos * g_limitObj.setLblHeight - parseInt(difFilter.style.height), 0);
|
|
363
|
+
|
|
364
|
+
multiAppend(optionsprite, makeDifBtn(-1), makeDifBtn());
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* 譜面変更ボタンを押したときの処理
|
|
369
|
+
* @param {number} [_num=1]
|
|
370
|
+
*/
|
|
371
|
+
const changeDifficulty = (_num = 1) => {
|
|
372
|
+
if (g_headerObj.difSelectorUse) {
|
|
373
|
+
g_stateObj.filterKeys = ``;
|
|
374
|
+
if (document.getElementById(`difList`) === null) {
|
|
375
|
+
g_keyObj.prevKey = g_keyObj.currentKey;
|
|
376
|
+
createDifWindow();
|
|
377
|
+
} else {
|
|
378
|
+
resetDifWindow();
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
nextDifficulty(_num);
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* 譜面基礎データの取得
|
|
387
|
+
* @param {number} _scoreId
|
|
388
|
+
* @returns {{ arrowCnts: number, frzCnts: number, apm: number, playingTime: string }}
|
|
389
|
+
*/
|
|
390
|
+
const getScoreBaseData = _scoreId => {
|
|
391
|
+
const arrowCnts = sumData(g_detailObj.arrowCnt[_scoreId]);
|
|
392
|
+
const frzCnts = sumData(g_detailObj.frzCnt[_scoreId]);
|
|
393
|
+
return {
|
|
394
|
+
arrowCnts: arrowCnts,
|
|
395
|
+
frzCnts: frzCnts,
|
|
396
|
+
apm: Math.round((arrowCnts + frzCnts) / (g_detailObj.playingFrame[_scoreId] / g_fps / 60)),
|
|
397
|
+
playingTime: transFrameToTimer(g_detailObj.playingFrame[_scoreId]),
|
|
398
|
+
};
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* 速度変化グラフの描画
|
|
403
|
+
* @param {number} _scoreId
|
|
404
|
+
*/
|
|
405
|
+
const drawSpeedGraph = _scoreId => {
|
|
406
|
+
const startFrame = g_detailObj.startFrame[_scoreId];
|
|
407
|
+
const playingFrame = g_detailObj.playingFrameWithBlank[_scoreId];
|
|
408
|
+
const speedObj = {
|
|
409
|
+
speed: { frame: [0], speed: [1], cnt: 0, strokeColor: g_graphColorObj.speed },
|
|
410
|
+
boost: { frame: [0], speed: [1], cnt: 0, strokeColor: g_graphColorObj.boost }
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const tmpSpeedPoint = [0];
|
|
414
|
+
Object.keys(speedObj).forEach(speedType => {
|
|
415
|
+
const frame = speedObj[speedType].frame;
|
|
416
|
+
const speed = speedObj[speedType].speed;
|
|
417
|
+
const speedData = g_detailObj[`${speedType}Data`][_scoreId];
|
|
418
|
+
|
|
419
|
+
for (let i = 0; i < speedData?.length; i += 2) {
|
|
420
|
+
if (speedData[i] >= startFrame) {
|
|
421
|
+
frame.push(speedData[i] - startFrame);
|
|
422
|
+
speed.push(getSpeedFactor(speedData[i + 1]));
|
|
423
|
+
tmpSpeedPoint.push(speedData[i] - startFrame);
|
|
424
|
+
}
|
|
425
|
+
speedObj[speedType].cnt++;
|
|
426
|
+
}
|
|
427
|
+
frame.push(playingFrame);
|
|
428
|
+
speed.push(speed.at(-1));
|
|
429
|
+
tmpSpeedPoint.push(playingFrame);
|
|
430
|
+
});
|
|
431
|
+
const speedPoints = makeDedupliArray(tmpSpeedPoint).sort((a, b) => a - b);
|
|
432
|
+
let speedPointIdx = 0;
|
|
433
|
+
|
|
434
|
+
const canvas = document.getElementById(`graphSpeed`);
|
|
435
|
+
const context = canvas.getContext(`2d`);
|
|
436
|
+
const [_a, _b] = [-75, 100];
|
|
437
|
+
const [_min, _max] = [-0.2, 2.2];
|
|
438
|
+
const lineX = [0, 150], lineY = 208;
|
|
439
|
+
|
|
440
|
+
const avgX = [0, 0];
|
|
441
|
+
const avgSubX = [0, 0];
|
|
442
|
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
443
|
+
drawBaseLine(context, { _fixed: 1, _mark: `x`, _a, _b, _min, _max });
|
|
444
|
+
|
|
445
|
+
Object.keys(speedObj).forEach((speedType, j) => {
|
|
446
|
+
const frame = speedObj[speedType].frame;
|
|
447
|
+
const speed = speedObj[speedType].speed;
|
|
448
|
+
|
|
449
|
+
context.beginPath();
|
|
450
|
+
let preY;
|
|
451
|
+
let avgSubFrame = 0;
|
|
452
|
+
|
|
453
|
+
for (let i = 0; i < frame.length; i++) {
|
|
454
|
+
const x = frame[i] * (g_limitObj.graphWidth - 30) / playingFrame + 30;
|
|
455
|
+
const y = (Math.min(Math.max(speed[i], _min - 0.05), _max + 0.05) - 1) * _a + _b;
|
|
456
|
+
|
|
457
|
+
context.lineTo(x, preY);
|
|
458
|
+
context.lineTo(x, y);
|
|
459
|
+
preY = y;
|
|
460
|
+
|
|
461
|
+
const prevFrame = (i > 0 ? frame[i - 1] : 0); // frame[] は startFrame 相対。初回は 0 起点
|
|
462
|
+
const deltaFrame = frame[i] - prevFrame;
|
|
463
|
+
const prevSpeed = (i > 0 ? speed[i - 1] : 1);
|
|
464
|
+
avgX[j] += deltaFrame * prevSpeed;
|
|
465
|
+
if (prevSpeed !== 1) {
|
|
466
|
+
avgSubFrame += deltaFrame;
|
|
467
|
+
avgSubX[j] += deltaFrame * prevSpeed;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
avgX[j] /= playingFrame;
|
|
471
|
+
avgSubX[j] /= Math.max(avgSubFrame, 1);
|
|
472
|
+
|
|
473
|
+
context.lineWidth = 2;
|
|
474
|
+
context.strokeStyle = speedObj[speedType].strokeColor;
|
|
475
|
+
context.stroke();
|
|
476
|
+
|
|
477
|
+
context.beginPath();
|
|
478
|
+
context.moveTo(lineX[j], lineY);
|
|
479
|
+
context.lineTo(lineX[j] + 25, lineY);
|
|
480
|
+
context.stroke();
|
|
481
|
+
context.font = `${wUnit(g_limitObj.mainSiz)} ${getBasicFont()}`;
|
|
482
|
+
context.fillText(g_lblNameObj[`s_${speedType}`], lineX[j] + 30, lineY + 3);
|
|
483
|
+
|
|
484
|
+
const maxSpeed = Math.max(...speed);
|
|
485
|
+
const minSpeed = Math.min(...speed);
|
|
486
|
+
context.font = `${wUnit(g_limitObj.graphMiniSiz)} ${getBasicFont()}`;
|
|
487
|
+
context.fillText(`(${minSpeed.toFixed(2)}x` + (minSpeed === maxSpeed ? `` : ` -- ${Math.max(...speed).toFixed(2)}x`) + `)`, lineX[j] + 30, lineY + 16);
|
|
488
|
+
context.fillText(`Avg. ` + (avgX[j] === 1 ? `----` : `${(avgSubX[j]).toFixed(2)}x`), lineX[j] + 30, lineY + 29);
|
|
489
|
+
updateScoreDetailLabel(`Speed`, `${speedType}S`, speedObj[speedType].cnt, j, g_lblNameObj[`s_${speedType}`]);
|
|
490
|
+
});
|
|
491
|
+
updateScoreDetailLabel(`Speed`, `avgS`, `${(avgX[0] * avgX[1]).toFixed(2)}x`, 2, g_lblNameObj.s_avg);
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* 速度ポインタ位置の変更
|
|
495
|
+
* @param {number} _num
|
|
496
|
+
*/
|
|
497
|
+
const changeSpdCursor = (_num = 1) => {
|
|
498
|
+
speedPointIdx = nextPos(speedPointIdx, _num, speedPoints.length);
|
|
499
|
+
movePointer(speedPoints[speedPointIdx]);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* 速度ポインタの移動
|
|
504
|
+
* @param {number} _frame
|
|
505
|
+
*/
|
|
506
|
+
const movePointer = _frame => {
|
|
507
|
+
const canvasP = document.getElementById(`graphSpeed2`);
|
|
508
|
+
const contextP = canvasP.getContext(`2d`);
|
|
509
|
+
contextP.clearRect(0, 0, canvas.width, canvas.height);
|
|
510
|
+
|
|
511
|
+
const offsetX = _frame * (g_limitObj.graphWidth - 30) / playingFrame + 30;
|
|
512
|
+
const speed = { speed: 0, boost: 0 };
|
|
513
|
+
|
|
514
|
+
Object.keys(speedObj).forEach(speedType => {
|
|
515
|
+
const speedFrames = speedObj[speedType].frame.concat(Infinity);
|
|
516
|
+
const speedIndex = speedObj[speedType].frame.findIndex((frame, i) => frame <= _frame && _frame < speedFrames[i + 1]);
|
|
517
|
+
speed[speedType] = speedObj[speedType].speed[speedIndex];
|
|
518
|
+
const y = (Math.min(Math.max(speed[speedType], _min - 0.05), _max + 0.05) - 1) * _a + _b;
|
|
519
|
+
|
|
520
|
+
contextP.beginPath();
|
|
521
|
+
contextP.fillStyle = g_graphColorObj[speedType];
|
|
522
|
+
contextP.arc(offsetX, y, 5, 0, 360);
|
|
523
|
+
contextP.closePath();
|
|
524
|
+
contextP.fill();
|
|
525
|
+
});
|
|
526
|
+
calculateTotalSpeed(speed.speed, speed.boost, _frame);
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// 速度計算用ラベルの再作成
|
|
530
|
+
deleteDiv(detailSpeed, `btnSpdCursorL`);
|
|
531
|
+
deleteDiv(detailSpeed, `btnSpdCursorR`);
|
|
532
|
+
if (document.getElementById(`lblSpdHeader`) === null) {
|
|
533
|
+
multiAppend(detailSpeed,
|
|
534
|
+
createDivCss2Label(`lblSpdHeader`, `TotalSpeed`, g_lblPosObj.lblSpdHeader),
|
|
535
|
+
createDivCss2Label(`lblSpdBase`, ``, g_lblPosObj.lblSpdBase),
|
|
536
|
+
createDivCss2Label(`lblSpdOverall`, ``, g_lblPosObj.lblSpdOverall),
|
|
537
|
+
createDivCss2Label(`lblSpdBoost`, ``, g_lblPosObj.lblSpdBoost),
|
|
538
|
+
createDivCss2Label(`lblSpdTotal`, ``, g_lblPosObj.lblSpdTotal),
|
|
539
|
+
createDivCss2Label(`lblSpdFrame`, ``, g_lblPosObj.lblSpdFrame),
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
multiAppend(detailSpeed,
|
|
543
|
+
createCss2Button(`btnSpdCursorL`, `<`, () => changeSpdCursor(-1),
|
|
544
|
+
g_lblPosObj.btnSpdCursorL, g_cssObj.button_Mini),
|
|
545
|
+
createCss2Button(`btnSpdCursorR`, `>`, () => changeSpdCursor(),
|
|
546
|
+
g_lblPosObj.btnSpdCursorR, g_cssObj.button_Mini),
|
|
547
|
+
);
|
|
548
|
+
movePointer(0);
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* 合計速度の表示更新
|
|
553
|
+
* @param {number} _speed
|
|
554
|
+
* @param {number} _boost
|
|
555
|
+
* @param {number} _frame
|
|
556
|
+
*/
|
|
557
|
+
const calculateTotalSpeed = (_speed = null, _boost = null, _frame = 0) => {
|
|
558
|
+
if (document.getElementById(`lblSpdHeader`) === null) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
let speed, boost;
|
|
562
|
+
if (_speed !== null && _boost !== null) {
|
|
563
|
+
speed = _speed;
|
|
564
|
+
boost = _boost;
|
|
565
|
+
lblSpdOverall.textContent = `x${_speed.toFixed(2)}`;
|
|
566
|
+
lblSpdBoost.textContent = `x${_boost.toFixed(2)}`;
|
|
567
|
+
} else {
|
|
568
|
+
speed = Number(lblSpdOverall.textContent.slice(1));
|
|
569
|
+
boost = Number(lblSpdBoost.textContent.slice(1));
|
|
570
|
+
}
|
|
571
|
+
lblSpdBase.textContent = `${g_stateObj.speed.toFixed(2)}`;
|
|
572
|
+
lblSpdTotal.textContent = `=${(g_stateObj.speed * speed * boost).toFixed(2)}`;
|
|
573
|
+
lblSpdFrame.textContent = `[${transFrameToTimer(_frame + g_detailObj.startFrame[g_stateObj.scoreId])}]`;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* 譜面密度グラフの描画
|
|
578
|
+
* @param {number} _scoreId
|
|
579
|
+
*/
|
|
580
|
+
const drawDensityGraph = _scoreId => {
|
|
581
|
+
|
|
582
|
+
const canvas = document.getElementById(`graphDensity`);
|
|
583
|
+
const context = canvas.getContext(`2d`);
|
|
584
|
+
drawBaseLine(context);
|
|
585
|
+
for (let j = 0; j < g_limitObj.densityDivision; j++) {
|
|
586
|
+
context.beginPath();
|
|
587
|
+
[``, `2Push`, `3Push`].forEach(val => {
|
|
588
|
+
context.fillStyle = (g_detailObj.maxDensity[_scoreId].includes(j) ? g_graphColorObj[`max${val}`] : g_graphColorObj[`default${val}`]);
|
|
589
|
+
context.fillRect(16 * j * 16 / g_limitObj.densityDivision + 30, 195 - 9 * g_detailObj[`density${val}Data`][_scoreId][j] / 10,
|
|
590
|
+
15.5 * 16 / g_limitObj.densityDivision, 9 * g_detailObj[`density${val}Diff`][_scoreId][j] / 10
|
|
591
|
+
);
|
|
592
|
+
});
|
|
593
|
+
context.stroke();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const lineNames = [`Single`, `Chord`, `Triad+`];
|
|
597
|
+
Object.keys(g_graphColorObj).filter(val => val.indexOf(`max`) !== -1).forEach((val, j) => {
|
|
598
|
+
const lineX = 70 + j * 70;
|
|
599
|
+
|
|
600
|
+
context.beginPath();
|
|
601
|
+
context.lineWidth = 3;
|
|
602
|
+
context.fillStyle = g_rankObj.rankColorAllPerfect;
|
|
603
|
+
context.strokeStyle = g_graphColorObj[val];
|
|
604
|
+
context.moveTo(lineX, 215);
|
|
605
|
+
context.lineTo(lineX + 20, 215);
|
|
606
|
+
context.stroke();
|
|
607
|
+
context.font = `${wUnit(g_limitObj.difSelectorSiz)} ${getBasicFont()}`;
|
|
608
|
+
context.fillText(lineNames[j], lineX + 20, 218);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
const obj = getScoreBaseData(_scoreId);
|
|
612
|
+
updateScoreDetailLabel(`Density`, `APM`, obj.apm, 0, g_lblNameObj.s_apm);
|
|
613
|
+
updateScoreDetailLabel(`Density`, `Time`, obj.playingTime, 1, g_lblNameObj.s_time);
|
|
614
|
+
updateScoreDetailLabel(`Density`, `Arrow`, obj.arrowCnts, 3, g_lblNameObj.s_arrow);
|
|
615
|
+
updateScoreDetailLabel(`Density`, `Frz`, obj.frzCnts, 4, `${g_lblNameObj.s_frz}${g_headerObj.frzStartjdgUse ? ' <span class="common_bold">(2x)</span>' : ''}`);
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* 譜面明細内の補足情報の登録・更新
|
|
620
|
+
* @param {string} _name 表示する譜面明細のラベル
|
|
621
|
+
* @param {string} _label
|
|
622
|
+
* @param {string} _value
|
|
623
|
+
* @param {number} [_pos=0] 表示位置
|
|
624
|
+
* @param {string} [_labelname=_label]
|
|
625
|
+
*/
|
|
626
|
+
const updateScoreDetailLabel = (_name, _label, _value, _pos = 0, _labelname = _label) => {
|
|
627
|
+
const baseLabel = (_bLabel, _bLabelname, _bAlign) =>
|
|
628
|
+
document.getElementById(`detail${_name}`).appendChild(
|
|
629
|
+
createDivCss2Label(_bLabel, _bLabelname, {
|
|
630
|
+
x: 10, y: 130 + _pos * 16, w: 100, h: 16, siz: g_limitObj.difSelectorSiz, align: _bAlign,
|
|
631
|
+
})
|
|
632
|
+
);
|
|
633
|
+
if (document.getElementById(`data${_label}`) === null) {
|
|
634
|
+
baseLabel(`lbl${_label}`, `${_labelname}`, C_ALIGN_LEFT);
|
|
635
|
+
baseLabel(`data${_label}`, `${_value}`, C_ALIGN_RIGHT);
|
|
636
|
+
} else {
|
|
637
|
+
document.getElementById(`data${_label}`).textContent = `${_value}`;
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* グラフの縦軸を描画
|
|
643
|
+
* @param {CanvasRenderingContext2D} _context
|
|
644
|
+
* @param {number} [object._fixed=2] y座標)目盛表記する小数桁数
|
|
645
|
+
* @param {string} [object._mark=''] y座標)目盛の単位
|
|
646
|
+
* @param {number} [object._resolution=10] y座標)明細分割数
|
|
647
|
+
* @param {number} [object._a=-90]
|
|
648
|
+
* @param {number} [object._b=105]
|
|
649
|
+
* @param {number} [object._min=0] y座標)目盛の下限値
|
|
650
|
+
* @param {number} [object._max=2] y座標)目盛の上限値
|
|
651
|
+
*/
|
|
652
|
+
const drawBaseLine = (_context, { _fixed = 2, _mark = ``, _resolution = 10, _a = -90, _b = 105, _min = 0, _max = 2 } = {}) => {
|
|
653
|
+
_context.clearRect(0, 0, g_limitObj.graphWidth, g_limitObj.graphHeight);
|
|
654
|
+
|
|
655
|
+
for (let j = _min * _resolution; j <= _max * _resolution; j += 5) {
|
|
656
|
+
for (let k = 0; k < 5; k++) {
|
|
657
|
+
if ((j + k) % 5 === 0) {
|
|
658
|
+
drawLine(_context, (j + k) / _resolution, `main`, { _fixed, _mark, _a, _b });
|
|
659
|
+
} else {
|
|
660
|
+
drawLine(_context, (j + k) / _resolution, `sub`, { _fixed, _mark, _a, _b });
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* グラフ上に目盛を表示
|
|
668
|
+
* @param {CanvasRenderingContext2D} _context
|
|
669
|
+
* @param {number} _y
|
|
670
|
+
* @param {string} _lineType
|
|
671
|
+
* @param {number} [object._fixed] y座標)目盛表記する小数桁数
|
|
672
|
+
* @param {string} [object._mark] y座標)目盛の単位
|
|
673
|
+
* @param {number} [object._a]
|
|
674
|
+
* @param {number} [object._b]
|
|
675
|
+
*/
|
|
676
|
+
const drawLine = (_context, _y, _lineType, { _fixed, _mark, _a, _b } = {}) => {
|
|
677
|
+
const lineY = (_y - 1) * _a + _b;
|
|
678
|
+
_context.beginPath();
|
|
679
|
+
_context.moveTo(30, lineY);
|
|
680
|
+
_context.lineTo(g_limitObj.graphWidth, lineY);
|
|
681
|
+
_context.lineWidth = 1;
|
|
682
|
+
|
|
683
|
+
if (_lineType === `main`) {
|
|
684
|
+
const textBaseObj = document.getElementById(`lnkDifficulty`);
|
|
685
|
+
const textColor = window.getComputedStyle(textBaseObj, ``).color;
|
|
686
|
+
_context.strokeStyle = textColor;
|
|
687
|
+
_context.font = `${wUnit(12)} ${getBasicFont()}`;
|
|
688
|
+
_context.fillStyle = textColor;
|
|
689
|
+
_context.fillText(_y.toFixed(_fixed) + _mark, 2, lineY + 4);
|
|
690
|
+
} else {
|
|
691
|
+
_context.strokeStyle = `#646464`;
|
|
692
|
+
}
|
|
693
|
+
_context.stroke();
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* 譜面の難易度情報用ラベル作成
|
|
698
|
+
* @param {number} _scoreId
|
|
699
|
+
*/
|
|
700
|
+
const makeDifInfoLabels = _scoreId => {
|
|
701
|
+
|
|
702
|
+
// ツール難易度
|
|
703
|
+
const detailToolDif = document.getElementById(`detailToolDif`);
|
|
704
|
+
/**
|
|
705
|
+
* 譜面の難易度情報ラベルの作成
|
|
706
|
+
* @param {string} _lbl
|
|
707
|
+
* @param {string} _data
|
|
708
|
+
* @param {object} _obj
|
|
709
|
+
* @returns {HTMLDivElement}
|
|
710
|
+
*/
|
|
711
|
+
const makeDifInfoLabel = (_lbl, _data, { x = 130, y = 25, w = 125, h = 35, siz = g_limitObj.difSelectorSiz, ...rest } = {}) =>
|
|
712
|
+
createDivCss2Label(_lbl, _data, { x, y, w, h, siz, align: C_ALIGN_LEFT, ...rest });
|
|
713
|
+
|
|
714
|
+
let printData = ``;
|
|
715
|
+
for (let j = 0; j < g_detailObj.arrowCnt.length; j++) {
|
|
716
|
+
const obj = getScoreBaseData(j);
|
|
717
|
+
printData +=
|
|
718
|
+
// 譜面番号
|
|
719
|
+
`[${j + 1}]\t` +
|
|
720
|
+
// ツール値
|
|
721
|
+
`${g_detailObj.toolDif[j].tool}\t` +
|
|
722
|
+
// 同時
|
|
723
|
+
`${g_detailObj.toolDif[j].douji}\t` +
|
|
724
|
+
// 縦連
|
|
725
|
+
`${g_detailObj.toolDif[j].tate}\t` +
|
|
726
|
+
// 総矢印数
|
|
727
|
+
`${(obj.arrowCnts + obj.frzCnts)}\t` +
|
|
728
|
+
// 矢印
|
|
729
|
+
`${obj.arrowCnts}\t` +
|
|
730
|
+
// フリーズアロー
|
|
731
|
+
`${obj.frzCnts}\t` +
|
|
732
|
+
// APM
|
|
733
|
+
`${obj.apm}\t` +
|
|
734
|
+
// 時間(分秒)
|
|
735
|
+
`${obj.playingTime}\r\n`;
|
|
736
|
+
}
|
|
737
|
+
multiAppend(detailToolDif,
|
|
738
|
+
makeDifInfoLabel(`lblTooldif`, g_lblNameObj.s_level, g_lblPosObj.lblTooldif),
|
|
739
|
+
makeDifInfoLabel(`dataTooldif`, ``, g_lblPosObj.dataTooldif),
|
|
740
|
+
makeDifInfoLabel(`lblDouji`, g_lblNameObj.s_douji, g_lblPosObj.lblDouji),
|
|
741
|
+
makeDifInfoLabel(`lblTate`, g_lblNameObj.s_tate, g_lblPosObj.lblTate),
|
|
742
|
+
makeDifInfoLabel(`dataDouji`, ``, g_lblPosObj.dataDouji),
|
|
743
|
+
makeDifInfoLabel(`dataTate`, ``, g_lblPosObj.dataTate),
|
|
744
|
+
makeDifInfoLabel(`lblArrowInfo`, g_lblNameObj.s_cnts, g_lblPosObj.lblArrowInfo),
|
|
745
|
+
makeDifInfoLabel(`dataArrowInfo`, ``, g_lblPosObj.dataArrowInfo),
|
|
746
|
+
makeDifInfoLabel(`lblArrowInfo2`, ``, g_lblPosObj.lblArrowInfo2),
|
|
747
|
+
makeDifInfoLabel(`dataArrowInfo2`, ``, g_lblPosObj.dataArrowInfo2),
|
|
748
|
+
makeDifLblCssButton(`lnkDifInfo`, g_lblNameObj.s_print, 8, () => {
|
|
749
|
+
copyTextToClipboard(
|
|
750
|
+
`****** ${g_lblNameObj.s_printTitle} [${g_version}] ******\r\n\r\n`
|
|
751
|
+
+ `\t${g_lblNameObj.s_printHeader}\r\n\r\n${printData}`, g_msgInfoObj.I_0003
|
|
752
|
+
);
|
|
753
|
+
}, g_lblPosObj.lnkDifInfo),
|
|
754
|
+
);
|
|
755
|
+
createScText(lnkDifInfo, `DifInfo`, { targetLabel: `lnkDifInfo`, x: -10 });
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* 譜面の難易度情報更新
|
|
760
|
+
* @param {number} _scoreId
|
|
761
|
+
*/
|
|
762
|
+
const makeDifInfo = _scoreId => {
|
|
763
|
+
|
|
764
|
+
const arrowCnts = sumData(g_detailObj.arrowCnt[_scoreId]);
|
|
765
|
+
const frzCnts = sumData(g_detailObj.frzCnt[_scoreId].map(val => Math.floor(val)));
|
|
766
|
+
const push3CntStr = (g_detailObj.toolDif[_scoreId].push3.length === 0 ? `None` : `(${g_detailObj.toolDif[_scoreId].push3.join(', ')})`);
|
|
767
|
+
|
|
768
|
+
if (document.getElementById(`lblTooldif`) === null) {
|
|
769
|
+
makeDifInfoLabels(_scoreId);
|
|
770
|
+
}
|
|
771
|
+
dataTooldif.textContent = g_detailObj.toolDif[_scoreId].tool;
|
|
772
|
+
dataDouji.textContent = g_detailObj.toolDif[_scoreId].douji;
|
|
773
|
+
dataTate.textContent = g_detailObj.toolDif[_scoreId].tate;
|
|
774
|
+
lblArrowInfo2.innerHTML = g_lblNameObj.s_linecnts.split(`{0}`)
|
|
775
|
+
.join(`${makeDedupliArray(g_detailObj.toolDif[_scoreId].push3).length} /cnt:${g_detailObj.toolDif[_scoreId].push3cnt}`);
|
|
776
|
+
dataArrowInfo.innerHTML = `${arrowCnts + frzCnts * (g_headerObj.frzStartjdgUse ? 2 : 1)}
|
|
777
|
+
<span style="font-size:${wUnit(g_limitObj.difSelectorSiz)};">(${arrowCnts} + ${frzCnts}${g_headerObj.frzStartjdgUse ? ' <span class="common_bold">x 2</span>' : ''})</span>`;
|
|
778
|
+
|
|
779
|
+
const makeArrowCntsView = (_cntlist) => {
|
|
780
|
+
const targetKey = g_headerObj.keyLabels[_scoreId];
|
|
781
|
+
const cntlist = [
|
|
782
|
+
_cntlist.filter((val, j) =>
|
|
783
|
+
g_keyObj[`pos${targetKey}_0`][j] < g_keyObj[`div${targetKey}_0`]),
|
|
784
|
+
_cntlist.filter((val, j) =>
|
|
785
|
+
g_keyObj[`pos${targetKey}_0`][j] >= g_keyObj[`div${targetKey}_0`])
|
|
786
|
+
];
|
|
787
|
+
|
|
788
|
+
let cntlistStr = ``;
|
|
789
|
+
cntlist.filter(array => array.length > 0).forEach(array => {
|
|
790
|
+
const maxVal = array.reduce((a, b) => Math.max(a, b));
|
|
791
|
+
const minVal = array.reduce((a, b) => Math.min(a, b));
|
|
792
|
+
|
|
793
|
+
cntlistStr += `[ `;
|
|
794
|
+
array.forEach((val, j) => {
|
|
795
|
+
if (maxVal !== minVal) {
|
|
796
|
+
array[j] = (val === minVal ? `<span class="settings_minArrowCnts">${val}</span>` :
|
|
797
|
+
(val === maxVal ? `<span class="settings_maxArrowCnts common_bold">${val}</span>` : val));
|
|
798
|
+
}
|
|
799
|
+
if (val - Math.floor(val) > 0) {
|
|
800
|
+
array[j] = `<span class="keyconfig_warning">${val}</span>`;
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
cntlistStr += array.join(`, `) + ` ]`;
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
return cntlistStr;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
dataArrowInfo2.innerHTML = `<br>${makeArrowCntsView(g_detailObj.arrowCnt[_scoreId])}<br><br>
|
|
810
|
+
${makeArrowCntsView(g_detailObj.frzCnt[_scoreId])}<br><br>${push3CntStr}`;
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* ハイスコア表示
|
|
815
|
+
* @param {number} _scoreId
|
|
816
|
+
*/
|
|
817
|
+
const makeHighScore = _scoreId => {
|
|
818
|
+
const detailHighScore = document.getElementById(`detailHighScore`);
|
|
819
|
+
|
|
820
|
+
// 再描画のため一度クリア
|
|
821
|
+
deleteChildspriteAll(`detailHighScore`);
|
|
822
|
+
|
|
823
|
+
const keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`;
|
|
824
|
+
const assistFlg = (g_autoPlaysBase.includes(g_stateObj.autoPlay) ? `` : `-${getStgDetailName(g_stateObj.autoPlay)}${getStgDetailName('less')}`);
|
|
825
|
+
const mirrorName = (g_stateObj.shuffle === C_FLG_OFF ? `` : `-${g_stateObj.shuffle}`);
|
|
826
|
+
const transKeyName = getTransKeyName();
|
|
827
|
+
let scoreName = getStorageKeyName(g_headerObj.keyLabels[_scoreId], transKeyName, assistFlg, mirrorName, _scoreId);
|
|
828
|
+
|
|
829
|
+
if (!hasVal(g_localStorage.highscores?.[scoreName])) {
|
|
830
|
+
|
|
831
|
+
// 古いキー定義の情報を検索
|
|
832
|
+
const relatedKeys = Object.entries(g_keyObj.keyTransPattern)
|
|
833
|
+
.filter(([key, value]) => value === g_headerObj.keyLabels[_scoreId])
|
|
834
|
+
.map(([key]) => key);
|
|
835
|
+
|
|
836
|
+
// 古いキー定義のハイスコアがいる場合は、現行キー定義として表示
|
|
837
|
+
for (const legacyKey of relatedKeys) {
|
|
838
|
+
let tmpScoreName = getStorageKeyName(legacyKey, transKeyName, assistFlg, mirrorName, _scoreId);
|
|
839
|
+
const src = g_localStorage.highscores?.[tmpScoreName];
|
|
840
|
+
if (hasVal(src)) {
|
|
841
|
+
g_localStorage.highscores[scoreName] = structuredClone(src);
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const createScoreLabel = (_id, _text, { xPos = 0, yPos = 0, dx = 0, w = 150, h = 17, colorName = _id, align = C_ALIGN_LEFT, overflow = `visible` } = {}) =>
|
|
848
|
+
createDivCss2Label(`lblH${toCapitalize(_id)}`, _text, {
|
|
849
|
+
x: xPos * 150 + 130 + dx, y: yPos * 16 + 5, w, h, siz: 14, align, overflow,
|
|
850
|
+
}, g_cssObj[`common_${colorName}`]);
|
|
851
|
+
|
|
852
|
+
const charas = [
|
|
853
|
+
`ii`, `shakin`, `matari`, `shobon`, `uwan`, `kita`, `iknai`, `maxCombo`, `fmaxCombo`, ``, `score`,
|
|
854
|
+
];
|
|
855
|
+
const extData = {
|
|
856
|
+
fast: `diffFast`, slow: `diffSlow`, adj: `estAdj`, excessive: `excessive`,
|
|
857
|
+
};
|
|
858
|
+
// 各判定 (FreezeComboとScoreの間に1行の空白を入れる)
|
|
859
|
+
charas.forEach((chara, j) => {
|
|
860
|
+
if (chara === ``) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
multiAppend(detailHighScore,
|
|
864
|
+
createScoreLabel(chara, g_lblNameObj[`j_${chara}`], { yPos: j }),
|
|
865
|
+
createScoreLabel(`${chara}S`, g_localStorage.highscores?.[scoreName]?.[chara] ?? C_FLG_HYPHEN,
|
|
866
|
+
{ xPos: 0, yPos: j, align: C_ALIGN_RIGHT }),
|
|
867
|
+
);
|
|
868
|
+
});
|
|
869
|
+
// Fast, Slow, 推定Adj, Excessive (値が無ければスキップ)
|
|
870
|
+
Object.keys(extData).forEach((chara, j) => {
|
|
871
|
+
if (!hasVal(g_localStorage.highscores?.[scoreName]?.[chara], C_FLG_HYPHEN)) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
multiAppend(detailHighScore,
|
|
875
|
+
createScoreLabel(chara, g_lblNameObj[`j_${chara}`], { xPos: 1, yPos: j, dx: 20, colorName: extData[chara] }),
|
|
876
|
+
createScoreLabel(`${chara}S`, g_localStorage.highscores?.[scoreName]?.[chara],
|
|
877
|
+
{ xPos: 1, yPos: j, dx: -25, align: C_ALIGN_RIGHT }),
|
|
878
|
+
);
|
|
879
|
+
});
|
|
880
|
+
if (hasVal(g_localStorage.highscores?.[scoreName]?.adj)) {
|
|
881
|
+
multiAppend(detailHighScore, createScoreLabel(`adjF`, `f`, { xPos: 2, yPos: 2, dx: -23 }));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// カスタム表示 (resultValsViewに指定した表示のみ)
|
|
885
|
+
g_headerObj.resultValsView
|
|
886
|
+
.filter(key => hasVal(g_localStorage.highscores?.[scoreName]?.[g_presetObj.resultVals?.[key]]))
|
|
887
|
+
.forEach((key, j) => {
|
|
888
|
+
multiAppend(detailHighScore,
|
|
889
|
+
createScoreLabel(key, g_presetObj.resultVals[key], { xPos: 1, yPos: j + 5, dx: 20 }),
|
|
890
|
+
createScoreLabel(`${key}S`, g_localStorage.highscores?.[scoreName]?.[g_presetObj.resultVals[key]],
|
|
891
|
+
{ xPos: 1, yPos: j + 5, dx: -25, align: C_ALIGN_RIGHT }),
|
|
892
|
+
);
|
|
893
|
+
});
|
|
894
|
+
// ランク、クリアランプ、特殊設定条件
|
|
895
|
+
multiAppend(detailHighScore,
|
|
896
|
+
createDivCss2Label(`lblHRank`, g_localStorage.highscores?.[scoreName]?.rankMark ?? `--`, {
|
|
897
|
+
...g_lblPosObj.lblHRank,
|
|
898
|
+
color: g_localStorage.highscores?.[scoreName]?.rankColor ?? `#666666`,
|
|
899
|
+
fontFamily: getBasicFont(`"Bookman Old Style"`),
|
|
900
|
+
}),
|
|
901
|
+
createScoreLabel(`lblHDateTime`, g_localStorage.highscores?.[scoreName]?.dateTime ?? `----/--/-- --:--`, { yPos: 12 }),
|
|
902
|
+
createScoreLabel(`lblHMarks`,
|
|
903
|
+
`${g_localStorage.highscores?.[scoreName]?.fullCombo ?? '' ? '<span class="result_FullCombo">◆</span>' : ''}` +
|
|
904
|
+
`${g_localStorage.highscores?.[scoreName]?.perfect ?? '' ? '<span class="result_Perfect">◆</span>' : ''}` +
|
|
905
|
+
`${g_localStorage.highscores?.[scoreName]?.allPerfect ?? '' ? '<span class="result_AllPerfect">◆</span>' : ''}`, { xPos: 1, dx: 20, yPos: 12, w: 100, align: C_ALIGN_CENTER }),
|
|
906
|
+
createScoreLabel(`lblHClearLamps`, `Cleared: ` + (g_localStorage.highscores?.[scoreName]?.clearLamps?.join(', ') ?? C_FLG_HYPHEN), { yPos: 13, overflow: C_DIS_AUTO, w: g_sWidth / 2 + 40, h: 37 }),
|
|
907
|
+
|
|
908
|
+
createScoreLabel(`lblHShuffle`, g_stateObj.shuffle.indexOf(`Mirror`) < 0 ? `` : `Shuffle: <span class="common_shuffle">${g_stateObj.shuffle}</span>`, { yPos: 11.5, dx: -130 }),
|
|
909
|
+
createScoreLabel(`lblHAssist`, g_autoPlaysBase.includes(g_stateObj.autoPlay) ? `` : `Assist: <span class="common_assist">${g_stateObj.autoPlay}</span>`, { yPos: 12.5, dx: -130 }),
|
|
910
|
+
createScoreLabel(`lblHAnother`, !hasVal(g_keyObj[`transKey${keyCtrlPtn}`]) ? `` : `A.Keymode: <span class="common_another">${g_keyObj[`transKey${keyCtrlPtn}`]}</span>`, { yPos: 13.5, dx: -130 }),
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
// 結果をクリップボードへコピー (ハイスコア保存分)
|
|
914
|
+
if (g_localStorage.highscores?.[scoreName] !== undefined) {
|
|
915
|
+
const twiturl = new URL(g_localStorageUrlOrg);
|
|
916
|
+
twiturl.searchParams.append(`scoreId`, _scoreId);
|
|
917
|
+
const baseTwitUrl = g_isLocal ? `` : `${twiturl.toString()}`.replace(/[\t\n]/g, ``);
|
|
918
|
+
|
|
919
|
+
let tweetFrzJdg = ``;
|
|
920
|
+
let tweetMaxCombo = `${g_localStorage.highscores?.[scoreName]?.maxCombo}`;
|
|
921
|
+
if (sumData(g_detailObj.frzCnt[_scoreId]) > 0) {
|
|
922
|
+
tweetFrzJdg = `${g_localStorage.highscores?.[scoreName]?.kita}-${g_localStorage.highscores?.[scoreName]?.iknai}`;
|
|
923
|
+
tweetMaxCombo += `-${g_localStorage.highscores?.[scoreName]?.fmaxCombo}`;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
const musicTitle = g_headerObj.musicTitles[g_headerObj.musicNos[_scoreId]] || g_headerObj.musicTitle;
|
|
927
|
+
let tweetDifData = `${getKeyName(g_headerObj.keyLabels[_scoreId])}${transKeyName}${getStgDetailName('k-')}${g_headerObj.difLabels[_scoreId]}${assistFlg}`;
|
|
928
|
+
if (g_stateObj.shuffle !== `OFF`) {
|
|
929
|
+
tweetDifData += `:${getStgDetailName(g_stateObj.shuffle)}`;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
const storedExcessive = g_localStorage.highscores[scoreName]?.excessive;
|
|
933
|
+
const resultParams = {
|
|
934
|
+
hashTag: (hasVal(g_headerObj.hashTag) ? ` ${g_headerObj.hashTag}` : ``),
|
|
935
|
+
tuning: g_headerObj.creatorNames[_scoreId],
|
|
936
|
+
rankMark: g_localStorage.highscores?.[scoreName]?.rankMark || `--`,
|
|
937
|
+
playStyleData: g_localStorage.highscores[scoreName]?.playStyle || `--`,
|
|
938
|
+
highscore: g_localStorage.highscores[scoreName],
|
|
939
|
+
tweetExcessive: hasVal(storedExcessive, C_FLG_HYPHEN) ? `(+${storedExcessive})` : ``,
|
|
940
|
+
musicTitle, tweetDifData, tweetFrzJdg, tweetMaxCombo, baseTwitUrl,
|
|
941
|
+
};
|
|
942
|
+
const resultCommon = unEscapeHtml(makeResultText(g_templateObj.resultFormatDf, resultParams));
|
|
943
|
+
let tweetResultTmp = makeResultText(g_headerObj.resultFormat, resultParams);
|
|
944
|
+
if (g_presetObj.resultVals !== undefined) {
|
|
945
|
+
Object.keys(g_presetObj.resultVals).forEach(key =>
|
|
946
|
+
tweetResultTmp = tweetResultTmp.split(`[${key}]`).join(g_localStorage.highscores[scoreName][g_presetObj.resultVals[key]] || ``));
|
|
947
|
+
}
|
|
948
|
+
const resultText = `${unEscapeHtml(tweetResultTmp)}`;
|
|
949
|
+
multiAppend(detailHighScore,
|
|
950
|
+
makeDifLblCssButton(`lnkResetHighScore`, g_lblNameObj.s_resetResult, 7, () => {
|
|
951
|
+
if (window.confirm(g_msgObj.highscResetConfirm)) {
|
|
952
|
+
delete g_localStorage.highscores[scoreName];
|
|
953
|
+
makeHighScore(_scoreId);
|
|
954
|
+
}
|
|
955
|
+
}, { ...g_lblPosObj.lnkHighScore, btnStyle: `Reset` }),
|
|
956
|
+
makeDifLblCssButton(`lnkHighScore`, g_lblNameObj.s_result, 8, () => {
|
|
957
|
+
copyTextToClipboard(keyIsShift() ? resultCommon : resultText, g_msgInfoObj.I_0001);
|
|
958
|
+
}, g_lblPosObj.lnkHighScore),
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* 譜面ミニマップを描画し、適切なスクロール位置へ調整する。
|
|
965
|
+
* デフォルトはスクロール反転で、現在位置を保つように上下反転する。
|
|
966
|
+
* @param {string} _scoreId - 描画対象の譜面ID
|
|
967
|
+
* @param {object} [_options={}] - オプションパラメータ
|
|
968
|
+
* @param {boolean} [_options._initFlg=false] - 譜面切替モード
|
|
969
|
+
* - 前の譜面での演奏進行度(時間軸の比率)を算出し、新しい譜面でも同じ進行位置を表示する。
|
|
970
|
+
* 位置記憶がない場合はフェードイン設定に基づく初期位置を表示。
|
|
971
|
+
* @param {boolean} [_options._fadeinFlg=false] - フェードインモード。フェードイン設定値を優先して移動する。
|
|
972
|
+
*/
|
|
973
|
+
const drawMinimap = (_scoreId, { _initFlg = false, _fadeinFlg = false } = {}) => {
|
|
974
|
+
const detailMiniMap = document.getElementById(`detailMiniMap`);
|
|
975
|
+
if (detailMiniMap === null) return; // scoreDetailUse=false 等で未生成の場合は何もしない
|
|
976
|
+
|
|
977
|
+
const isRev = g_stateObj.miniMapRevFlg;
|
|
978
|
+
const subEl = document.getElementById(`detailMiniMapSub`);
|
|
979
|
+
const currentScrollTop = subEl ? subEl.scrollTop : 0;
|
|
980
|
+
|
|
981
|
+
// 前の譜面でスクロール可能な状態だったかを判定
|
|
982
|
+
const hasPreviousScrollRange = subEl && subEl.scrollHeight > subEl.clientHeight;
|
|
983
|
+
|
|
984
|
+
// 前の譜面での「スクロール位置の比率」を計算
|
|
985
|
+
// 完全に一番上のときは 0、一番下のときは 1 となる比率
|
|
986
|
+
let progressRatio = 0;
|
|
987
|
+
if (hasPreviousScrollRange) {
|
|
988
|
+
const rawRatio = currentScrollTop / (subEl.scrollHeight - subEl.clientHeight);
|
|
989
|
+
// リバース時は「上が終点」なので、進行度としては反転させる
|
|
990
|
+
progressRatio = isRev ? (1.0 - rawRatio) : rawRatio;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// 再描画のため一度クリア
|
|
994
|
+
deleteChildspriteAll(`detailMiniMap`);
|
|
995
|
+
|
|
996
|
+
// --- ミニマップ生成/取得 (Lazy Generation) ---
|
|
997
|
+
let savedCanvases = isRev
|
|
998
|
+
? g_detailObj.scoreMinimapReverse[_scoreId]
|
|
999
|
+
: g_detailObj.scoreMinimap[_scoreId];
|
|
1000
|
+
|
|
1001
|
+
const params = g_detailObj.miniMapParams[_scoreId];
|
|
1002
|
+
const kPtn = params._keyCtrlPtn;
|
|
1003
|
+
if (!g_detailObj.scoreMinimapHeader[kPtn]) {
|
|
1004
|
+
// ヘッダーはキー種ごとに共通なので、未作成の場合のみ生成してキャッシュ
|
|
1005
|
+
g_detailObj.scoreMinimapHeader[kPtn] = createMinimapHeader(params.config, kPtn, params._keyNum);
|
|
1006
|
+
}
|
|
1007
|
+
if (!savedCanvases) {
|
|
1008
|
+
// 未作成の場合のみミニマップを生成(Lazy Generation)
|
|
1009
|
+
savedCanvases = generateMinimapData(params, isRev);
|
|
1010
|
+
|
|
1011
|
+
// 生成したものをキャッシュに保存
|
|
1012
|
+
if (isRev) {
|
|
1013
|
+
g_detailObj.scoreMinimapReverse[_scoreId] = savedCanvases;
|
|
1014
|
+
} else {
|
|
1015
|
+
g_detailObj.scoreMinimap[_scoreId] = savedCanvases;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// --- ヘッダー部分 ---
|
|
1020
|
+
const detailMiniMapHeader = createEmptySprite(detailMiniMap, `detailMiniMapHeader`, g_windowObj.detailMiniMapHeader);
|
|
1021
|
+
$id(`detailMiniMapHeader`).top = wUnit(g_stateObj.miniMapRevFlg ? 230 + g_sHeight - 500 : 0);
|
|
1022
|
+
detailMiniMapHeader.appendChild(g_detailObj.scoreMinimapHeader[kPtn]);
|
|
1023
|
+
|
|
1024
|
+
// --- メイン(譜面)部分 ---
|
|
1025
|
+
const detailMiniMapSub = createEmptySprite(detailMiniMap, `detailMiniMapSub`, g_windowObj.detailMiniMapSub);
|
|
1026
|
+
$id(`detailMiniMapSub`).top = wUnit(g_stateObj.miniMapRevFlg ? 0 : 15);
|
|
1027
|
+
|
|
1028
|
+
Object.assign(detailMiniMapSub.style, {
|
|
1029
|
+
overflowX: 'hidden',
|
|
1030
|
+
overflowY: 'auto',
|
|
1031
|
+
pointerEvents: 'auto',
|
|
1032
|
+
display: 'block',
|
|
1033
|
+
textAlign: 'left',
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
if (savedCanvases && Array.isArray(savedCanvases)) {
|
|
1037
|
+
// 退避したCanvasそのものをDOMに追加(再描画不要で高速)
|
|
1038
|
+
savedCanvases.forEach(canvas => {
|
|
1039
|
+
Object.assign(canvas.style, { position: 'static', display: 'block', height: 'auto' });
|
|
1040
|
+
detailMiniMapSub.appendChild(canvas);
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
const scrollHeight = Math.max(detailMiniMapSub.scrollHeight - detailMiniMapSub.clientHeight, 0);
|
|
1044
|
+
const playingFrame = Math.max(g_detailObj.playingFrame[_scoreId], 1);
|
|
1045
|
+
const lastFrame = g_detailObj.startFrame[_scoreId] + g_detailObj.playingFrameWithBlank[_scoreId];
|
|
1046
|
+
const firstArrowFrame = lastFrame - g_detailObj.playingFrame[_scoreId];
|
|
1047
|
+
const fadeinFrameOffset = Math.max(0, Math.min(
|
|
1048
|
+
playingFrame,
|
|
1049
|
+
getStartFrame(lastFrame, g_stateObj.fadein, _scoreId) - firstArrowFrame
|
|
1050
|
+
));
|
|
1051
|
+
const fadeinScrollTop = scrollHeight * fadeinFrameOffset / playingFrame;
|
|
1052
|
+
const visualFadeinPos = isRev ? scrollHeight - fadeinScrollTop : fadeinScrollTop;
|
|
1053
|
+
|
|
1054
|
+
// --- スクロール位置の決定ロジック
|
|
1055
|
+
let targetScrollTop = 0;
|
|
1056
|
+
|
|
1057
|
+
if (_fadeinFlg) {
|
|
1058
|
+
// 【最優先】フェードイン操作時:設定値を強制適用
|
|
1059
|
+
targetScrollTop = visualFadeinPos;
|
|
1060
|
+
} else if (_initFlg) {
|
|
1061
|
+
// 【譜面切替時】
|
|
1062
|
+
if (hasPreviousScrollRange) {
|
|
1063
|
+
// 以前の進行度を継承
|
|
1064
|
+
const visualRatio = isRev ? (1.0 - progressRatio) : progressRatio;
|
|
1065
|
+
targetScrollTop = scrollHeight * visualRatio;
|
|
1066
|
+
} else {
|
|
1067
|
+
// 初回はフェードイン位置
|
|
1068
|
+
targetScrollTop = visualFadeinPos;
|
|
1069
|
+
}
|
|
1070
|
+
} else {
|
|
1071
|
+
// 【リバース切替時】物理反転
|
|
1072
|
+
targetScrollTop = scrollHeight - currentScrollTop;
|
|
1073
|
+
}
|
|
1074
|
+
detailMiniMapSub.scrollTop = targetScrollTop;
|
|
1075
|
+
|
|
1076
|
+
if (document.getElementById(`lnkMiniMapRev`) === null) {
|
|
1077
|
+
scoreDetail.appendChild(
|
|
1078
|
+
makeDifLblCssButton(`lnkMiniMapRev`, g_lblNameObj.s_rev + `${g_stateObj.miniMapRevFlg ? `↑` : `↓`}`, 8, () => {
|
|
1079
|
+
g_stateObj.miniMapRevFlg = !g_stateObj.miniMapRevFlg;
|
|
1080
|
+
lnkMiniMapRev.textContent = g_lblNameObj.s_rev + `${g_stateObj.miniMapRevFlg ? `↑` : `↓`}`;
|
|
1081
|
+
drawMinimap(g_stateObj.scoreId);
|
|
1082
|
+
createScText(lnkMiniMapRev, `MiniMapRev`, { targetLabel: `lnkMiniMapRev`, x: -12 });
|
|
1083
|
+
}, g_lblPosObj.lnkMiniMapRev)
|
|
1084
|
+
);
|
|
1085
|
+
createScText(lnkMiniMapRev, `MiniMapRev`, { targetLabel: `lnkMiniMapRev`, x: -12 });
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* 譜面ミニマップ:キー名を表示するヘッダーキャンバスを作成する
|
|
1091
|
+
* @param {object} _config ミニマップの基本設定
|
|
1092
|
+
* @param {number} _config.timeMargin 時間軸のマージン
|
|
1093
|
+
* @param {number} _config.laneWidth レーンの幅
|
|
1094
|
+
* @param {number} _config.logicalWidth キャンバスの論理幅
|
|
1095
|
+
* @param {string} _keyCtrlPtn キーコントロールパターン
|
|
1096
|
+
* @param {number} _keyNum キー数
|
|
1097
|
+
* @return {HTMLCanvasElement} ヘッダー用のキャンバス要素
|
|
1098
|
+
*/
|
|
1099
|
+
const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
1100
|
+
const { timeMargin, laneWidth, logicalWidth } = _config;
|
|
1101
|
+
const headerHeight = 15; // ヘッダーの固定高
|
|
1102
|
+
|
|
1103
|
+
const canvas = document.createElement('canvas');
|
|
1104
|
+
const ctx = canvas.getContext('2d');
|
|
1105
|
+
|
|
1106
|
+
// 解像度と表示サイズの設定
|
|
1107
|
+
canvas.width = logicalWidth * g_dpr;
|
|
1108
|
+
canvas.height = headerHeight * g_dpr;
|
|
1109
|
+
canvas.style.width = wUnit(logicalWidth);
|
|
1110
|
+
canvas.style.height = wUnit(headerHeight);
|
|
1111
|
+
canvas.style.display = 'block';
|
|
1112
|
+
|
|
1113
|
+
ctx.scale(g_dpr, g_dpr);
|
|
1114
|
+
|
|
1115
|
+
// テキストのスタイル設定
|
|
1116
|
+
ctx.fillStyle = '#999';
|
|
1117
|
+
ctx.font = `10px ${getBasicFont()}`;
|
|
1118
|
+
ctx.textAlign = 'center';
|
|
1119
|
+
ctx.textBaseline = 'middle';
|
|
1120
|
+
|
|
1121
|
+
// 各レーンのキー名を描画
|
|
1122
|
+
for (let j = 0; j < _keyNum; j++) {
|
|
1123
|
+
// config.laneWidth を使って中央座標を計算
|
|
1124
|
+
const x = timeMargin + j * laneWidth + laneWidth / 2;
|
|
1125
|
+
const keyText = g_kCd[g_keyObj[`keyCtrl${_keyCtrlPtn}`][j][0]].split(` `).join(``);
|
|
1126
|
+
|
|
1127
|
+
ctx.fillText(keyText, x, headerHeight / 2 + 2); // 視覚的な中央調整で +2px
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
return canvas;
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* 譜面ミニマップ:譜面ミニマップ本体生成
|
|
1135
|
+
* @param {object} _params ミニマップ生成のためのパラメータオブジェクト
|
|
1136
|
+
* @param {object} _params._scoreObj 譜面データオブジェクト
|
|
1137
|
+
* @param {number} _params._keyNum キー数
|
|
1138
|
+
* @param {number} _params._playingFrame 演奏時間(フレーム数)
|
|
1139
|
+
* @param {number} _params._firstArrowFrame 最初の矢印のフレーム位置
|
|
1140
|
+
* @param {string} _params._keyCtrlPtn キーコントロールパターン
|
|
1141
|
+
* @param {object} _params.config ミニマップの基本設定
|
|
1142
|
+
* @param {number} _params.config.scale ミニマップの時間軸のスケール
|
|
1143
|
+
* @param {number} _params.config.timeMargin 時間軸のマージン
|
|
1144
|
+
* @param {number} _params.config.laneWidth レーンの幅
|
|
1145
|
+
* @param {number} _params.config.logicalWidth キャンバスの論理幅
|
|
1146
|
+
* @param {number} _params.config.mmMarginY ミニマップの上下マージン
|
|
1147
|
+
* @param {boolean} _isReverse ミニマップのリバース表示フラグ
|
|
1148
|
+
* @returns {HTMLCanvasElement[]} ミニマップ用のキャンバス要素の配列
|
|
1149
|
+
*/
|
|
1150
|
+
const generateMinimapData = (_params, _isReverse) => {
|
|
1151
|
+
const { _scoreObj, _keyNum, _playingFrame, _firstArrowFrame, _keyCtrlPtn, config } = _params;
|
|
1152
|
+
const { scale, timeMargin, laneWidth, logicalWidth, mmMarginY } = config;
|
|
1153
|
+
|
|
1154
|
+
const mmHeightTotal = _playingFrame * scale + mmMarginY * 2;
|
|
1155
|
+
const canvases = createSplitCanvases(logicalWidth, mmHeightTotal);
|
|
1156
|
+
|
|
1157
|
+
const getY = (frame) => {
|
|
1158
|
+
const relativeFrame = frame - _firstArrowFrame;
|
|
1159
|
+
const rawY = relativeFrame * scale;
|
|
1160
|
+
// mmHeightTotalから引くのではなく、中身の演奏時間部分(_playingFrame * scale)を基準にリバース
|
|
1161
|
+
return _isReverse
|
|
1162
|
+
? (_playingFrame * scale - rawY + mmMarginY)
|
|
1163
|
+
: (rawY + mmMarginY);
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
// 1. 時間軸描画
|
|
1167
|
+
const interval = g_fps;
|
|
1168
|
+
for (let f = Math.ceil(_firstArrowFrame / interval) * interval; f <= _firstArrowFrame + _playingFrame; f += interval) {
|
|
1169
|
+
const y = getY(f);
|
|
1170
|
+
distributeDrawing(canvases, y - 5, 10, g_dpr, (ctx) => {
|
|
1171
|
+
ctx.strokeStyle = '#444';
|
|
1172
|
+
ctx.fillStyle = '#999';
|
|
1173
|
+
ctx.font = `10px ${getBasicFont()}`;
|
|
1174
|
+
ctx.textAlign = 'right';
|
|
1175
|
+
ctx.textBaseline = 'middle';
|
|
1176
|
+
ctx.beginPath(); ctx.moveTo(timeMargin, y); ctx.lineTo(timeMargin + laneWidth * _keyNum, y); ctx.stroke();
|
|
1177
|
+
const [m, s] = transFrameToTimer(f).split(':');
|
|
1178
|
+
ctx.fillText(`${m.padStart(2, '0')}:${s}`, timeMargin, y);
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// 2. フリーズノート
|
|
1183
|
+
for (let j = 0; j < _keyNum; j++) {
|
|
1184
|
+
const frz = _scoreObj.frzData[j];
|
|
1185
|
+
for (let k = 0; k < frz.length; k += 2) {
|
|
1186
|
+
const start = frz[k];
|
|
1187
|
+
const end = frz[k + 1];
|
|
1188
|
+
|
|
1189
|
+
// 終了地点がない、またはどちらかが数値でない場合はスキップ
|
|
1190
|
+
if (end === undefined || isNaN(start) || isNaN(end)) {
|
|
1191
|
+
console.warn(`Minimap: Incomplete freeze note pair at lane ${j}, index ${k}`);
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
const y1 = getY(start);
|
|
1195
|
+
const y2 = getY(end);
|
|
1196
|
+
const top = Math.min(y1, y2);
|
|
1197
|
+
const h = Math.abs(y2 - y1);
|
|
1198
|
+
const x = timeMargin + j * laneWidth;
|
|
1199
|
+
distributeDrawing(canvases, top, h, g_dpr, (ctx) => {
|
|
1200
|
+
ctx.fillStyle = 'rgba(0, 200, 255, 0.4)';
|
|
1201
|
+
ctx.fillRect(x + 2, top, laneWidth - 3, h);
|
|
1202
|
+
ctx.strokeStyle = 'rgba(0, 200, 255, 0.8)';
|
|
1203
|
+
ctx.strokeRect(x + 2, top, laneWidth - 3, h);
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// 3. 通常ノート
|
|
1209
|
+
for (let j = 0; j < _keyNum; j++) {
|
|
1210
|
+
const color = g_dfColorObj.setColorType2[g_keyObj[`color${_keyCtrlPtn}_0`][j]] || '#ffffff';
|
|
1211
|
+
_scoreObj.arrowData[j].forEach(note => {
|
|
1212
|
+
const y = getY(parseFloat(note));
|
|
1213
|
+
distributeDrawing(canvases, y - 1.5, 3, g_dpr, (ctx) => {
|
|
1214
|
+
ctx.fillStyle = color;
|
|
1215
|
+
ctx.fillRect(timeMargin + j * laneWidth + 1, y - 1.5, laneWidth - 1, 3);
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
return canvases.map(item => item.canvas);
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* 描画対象のCanvasを判定して描画を実行する
|
|
1225
|
+
* @param {object[]} _canvases
|
|
1226
|
+
* @param {HTMLCanvasElement} _canvases[].canvas 分割されたCanvas要素
|
|
1227
|
+
* @param {CanvasRenderingContext2D} _canvases[].ctx Canvasの描画コンテキスト
|
|
1228
|
+
* @param {number} _canvases[].offsetTop Canvasの論理上のオフセット位置
|
|
1229
|
+
* @param {number} _canvases[].logicalHeight Canvasの論理上の高さ
|
|
1230
|
+
* @param {number} _y
|
|
1231
|
+
* @param {number} _h
|
|
1232
|
+
* @param {number} _dpr
|
|
1233
|
+
* @param {Function} _drawFunc
|
|
1234
|
+
*/
|
|
1235
|
+
const distributeDrawing = (_canvases, _y, _h, _dpr, _drawFunc) => {
|
|
1236
|
+
_canvases.forEach(item => {
|
|
1237
|
+
const canvasHeight = item.logicalHeight;
|
|
1238
|
+
if (_y + _h >= item.offsetTop && _y <= item.offsetTop + canvasHeight) {
|
|
1239
|
+
item.ctx.save();
|
|
1240
|
+
item.ctx.translate(0, -item.offsetTop);
|
|
1241
|
+
_drawFunc(item.ctx);
|
|
1242
|
+
item.ctx.restore();
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* 指定したキー名のキー別ストレージオブジェクトを取得
|
|
1249
|
+
* @param {string} _keyName
|
|
1250
|
+
* @returns {[Object, string]} [storageObj, addKey]
|
|
1251
|
+
*/
|
|
1252
|
+
const getKeyStorageObjByName = (_keyName) => {
|
|
1253
|
+
if (g_headerObj.keyExtraList.includes(_keyName)) {
|
|
1254
|
+
return [g_localStorage, _keyName];
|
|
1255
|
+
}
|
|
1256
|
+
return [parseStorageData(`danonicw-${_keyName}k`, {
|
|
1257
|
+
reverse: C_FLG_OFF, keyCtrl: [[]], keyCtrlPtn: 0, setColor: [],
|
|
1258
|
+
}), ``];
|
|
1259
|
+
};
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* 別キーモード時、移行先キーが保持するSelf保存パターンの取得可否チェック
|
|
1263
|
+
* @param {string} _keyCtrlPtn 例: "7_2"
|
|
1264
|
+
* @returns {number[][]|undefined} 適用可能な場合はSelf保存済みkeyCtrl配列、不可ならundefined
|
|
1265
|
+
*/
|
|
1266
|
+
const getTransKeySelfCtrl = (_keyCtrlPtn) => {
|
|
1267
|
+
const transKeyName = g_keyObj[`transKey${_keyCtrlPtn}`];
|
|
1268
|
+
const transKeyPtn = g_keyObj[`transKeyPtn${_keyCtrlPtn}`];
|
|
1269
|
+
if (!hasVal(transKeyName) || transKeyPtn === undefined) {
|
|
1270
|
+
return undefined;
|
|
1271
|
+
}
|
|
1272
|
+
const [storageObj, addKey] = getKeyStorageObjByName(transKeyName);
|
|
1273
|
+
const savedCtrl = storageObj[`keyCtrl${addKey}`];
|
|
1274
|
+
const savedPtn = storageObj[`keyCtrlPtn${addKey}`];
|
|
1275
|
+
|
|
1276
|
+
if (savedPtn !== transKeyPtn) {
|
|
1277
|
+
return undefined;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// 現在のキー配置(基準形状)とレーン数・各レーン長が完全一致することを確認
|
|
1281
|
+
const baseCtrl = g_keyObj[`keyCtrl${_keyCtrlPtn}`];
|
|
1282
|
+
const isValidShape = hasArrayList(savedCtrl, baseCtrl.length) &&
|
|
1283
|
+
savedCtrl.length === baseCtrl.length &&
|
|
1284
|
+
baseCtrl.every((lane, j) => hasArrayList(savedCtrl[j], lane.length) && savedCtrl[j].length === lane.length);
|
|
1285
|
+
|
|
1286
|
+
return isValidShape ? savedCtrl : undefined;
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* 別キーモードの移行先Selfパターンを現在のキー配置へ反映(保存はしない)
|
|
1291
|
+
*/
|
|
1292
|
+
const applyTransKeySelfPattern = () => {
|
|
1293
|
+
const keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`;
|
|
1294
|
+
const savedCtrl = getTransKeySelfCtrl(keyCtrlPtn);
|
|
1295
|
+
if (savedCtrl === undefined) {
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
const baseKeyNum = g_keyObj[`${g_keyObj.defaultProp}${keyCtrlPtn}`].length;
|
|
1299
|
+
for (let j = 0; j < baseKeyNum; j++) {
|
|
1300
|
+
for (let k = 0; k < (savedCtrl[j]?.length ?? 0); k++) {
|
|
1301
|
+
g_keyObj[`keyCtrl${keyCtrlPtn}`][j][k] = setIntVal(savedCtrl[j][k], 0);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
keyConfigInit();
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* 譜面初期化処理
|
|
1309
|
+
* - 譜面の基本設定(キー数、初期速度、リバース、ゲージ設定)をここで行う
|
|
1310
|
+
* - g_canLoadDifInfoFlg は譜面初期化フラグで、初期化したくない場合は対象画面にて false にしておく
|
|
1311
|
+
* (Display設定画面、キーコンフィグ画面では通常OFF)
|
|
1312
|
+
* この関数を実行後、このフラグはONに戻るようになっている
|
|
1313
|
+
* - [キーコン]->[初期化]->[名称設定]の順に配置する。
|
|
1314
|
+
* 初期化処理にてキー数関連の設定を行っているため、この順序で無いとデータが正しく格納されない
|
|
1315
|
+
*
|
|
1316
|
+
* @param {boolean} _initFlg
|
|
1317
|
+
*/
|
|
1318
|
+
const setDifficulty = (_initFlg) => {
|
|
1319
|
+
|
|
1320
|
+
// ---------------------------------------------------
|
|
1321
|
+
// 1. キーコンフィグ設定 (KeyConfig)
|
|
1322
|
+
g_keyObj.currentKey = g_headerObj.keyLabels[g_stateObj.scoreId];
|
|
1323
|
+
const isNotSameKey = (g_keyObj.prevKey !== g_keyObj.currentKey);
|
|
1324
|
+
|
|
1325
|
+
if (g_headerObj.dummyScoreNos !== undefined) {
|
|
1326
|
+
g_stateObj.dummyId = setIntVal(g_headerObj.dummyScoreNos[g_stateObj.scoreId], ``);
|
|
1327
|
+
}
|
|
1328
|
+
// 特殊キーフラグ
|
|
1329
|
+
g_stateObj.extraKeyFlg = g_headerObj.keyExtraList.includes(g_keyObj.currentKey);
|
|
1330
|
+
|
|
1331
|
+
// ---------------------------------------------------
|
|
1332
|
+
// 2. 初期化設定
|
|
1333
|
+
|
|
1334
|
+
// 保存した設定の再読込条件(設定画面切り替え時はスキップ)
|
|
1335
|
+
// ローカルストレージで保存した設定を呼び出し
|
|
1336
|
+
if ((g_canLoadDifInfoFlg && (isNotSameKey && g_stateObj.dataSaveFlg)) || _initFlg) {
|
|
1337
|
+
|
|
1338
|
+
if (isNotSameKey && g_keyObj.prevKey !== `Dummy`) {
|
|
1339
|
+
// キーパターン初期化
|
|
1340
|
+
g_keyObj.currentPtn = 0;
|
|
1341
|
+
g_keycons.keySwitchNum = 0;
|
|
1342
|
+
}
|
|
1343
|
+
const [storageObj, addKey] = getKeyStorageObjByName(g_keyObj.currentKey);
|
|
1344
|
+
if (isNotSameKey) {
|
|
1345
|
+
if (!g_stateObj.extraKeyFlg) {
|
|
1346
|
+
g_localKeyStorage = storageObj;
|
|
1347
|
+
}
|
|
1348
|
+
getKeyReverse(storageObj, addKey);
|
|
1349
|
+
|
|
1350
|
+
// キーコンフィグ初期値設定
|
|
1351
|
+
if (storageObj[`keyCtrlPtn${addKey}`] === undefined) {
|
|
1352
|
+
storageObj[`keyCtrlPtn${addKey}`] = 0;
|
|
1353
|
+
}
|
|
1354
|
+
getKeyCtrl(storageObj, addKey);
|
|
1355
|
+
|
|
1356
|
+
// カラーセット初期値設定
|
|
1357
|
+
if (storageObj[`setColor${addKey}`] === undefined) {
|
|
1358
|
+
storageObj[`setColor${addKey}`] = [];
|
|
1359
|
+
}
|
|
1360
|
+
if (storageObj[`setColor${addKey}`].length > 0) {
|
|
1361
|
+
g_keycons.colorTypes = addValtoArray(g_keycons.colorTypes, g_keycons.colorSelf);
|
|
1362
|
+
resetColorType({ _fromObj: storageObj, _from: addKey, _to: g_keycons.colorSelf });
|
|
1363
|
+
resetColorType({ _fromObj: storageObj, _from: addKey, _toObj: g_dfColorObj, _to: g_keycons.colorSelf });
|
|
1364
|
+
|
|
1365
|
+
} else {
|
|
1366
|
+
if (g_localStorage.colorType === g_keycons.colorSelf) {
|
|
1367
|
+
g_colorType = `Default`;
|
|
1368
|
+
}
|
|
1369
|
+
g_keycons.colorTypes = g_keycons.colorTypes.filter(val => val !== g_keycons.colorSelf);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`;
|
|
1373
|
+
|
|
1374
|
+
// カラーグループ、シャッフルグループの設定
|
|
1375
|
+
g_keycons.groups.forEach(type => {
|
|
1376
|
+
resetGroupList(type, keyCtrlPtn);
|
|
1377
|
+
if (g_keyObj.currentPtn === -1) {
|
|
1378
|
+
const storageKeyName = storageObj[`${type}${addKey}`] || storageObj[`${type}${g_keyObj.currentKey}_-1_-1`];
|
|
1379
|
+
if (storageKeyName !== undefined) {
|
|
1380
|
+
g_keyObj[`${type}${g_keyObj.currentKey}_-1`] = structuredClone(storageKeyName);
|
|
1381
|
+
}
|
|
1382
|
+
g_keyObj[`${type}${g_keyObj.currentKey}_-1_-1`] = structuredClone(g_keyObj[`${type}${g_keyObj.currentKey}_-1`]);
|
|
1383
|
+
} else {
|
|
1384
|
+
g_keyObj[`${type}${keyCtrlPtn}`] = structuredClone(g_keyObj[`${type}${keyCtrlPtn}_0`]);
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
}
|
|
1389
|
+
// 曲中ショートカットキーの切り替え
|
|
1390
|
+
setPlayingShortcut();
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// スクロール設定用の配列を入れ替え
|
|
1394
|
+
g_settings.scrolls = structuredClone(
|
|
1395
|
+
typeof g_keyObj[`scrollDir${g_keyObj.currentKey}_${g_keyObj.currentPtn}`] === C_TYP_OBJECT ?
|
|
1396
|
+
Object.keys(g_keyObj[`scrollDir${g_keyObj.currentKey}_${g_keyObj.currentPtn}`]) : g_keyObj.scrollName_def
|
|
1397
|
+
);
|
|
1398
|
+
|
|
1399
|
+
// アシスト設定の配列を入れ替え
|
|
1400
|
+
g_settings.autoPlays = (typeof g_keyObj[`assistPos${g_keyObj.currentKey}_${g_keyObj.currentPtn}`] === C_TYP_OBJECT ?
|
|
1401
|
+
g_autoPlaysBase.concat(Object.keys(g_keyObj[`assistPos${g_keyObj.currentKey}_${g_keyObj.currentPtn}`])) :
|
|
1402
|
+
g_autoPlaysBase.concat());
|
|
1403
|
+
|
|
1404
|
+
// ゲージ設定及びカーソル位置調整
|
|
1405
|
+
setGauge(0, true);
|
|
1406
|
+
|
|
1407
|
+
// 速度、スクロール、アシスト設定のカーソル位置調整
|
|
1408
|
+
if (_initFlg) {
|
|
1409
|
+
g_stateObj.speed = g_headerObj.initSpeeds[g_stateObj.scoreId];
|
|
1410
|
+
g_settings.speedNum = getCurrentNo(g_settings.speeds, g_stateObj.speed);
|
|
1411
|
+
}
|
|
1412
|
+
g_settings.scrollNum = getCurrentNo(g_settings.scrolls, g_stateObj.scroll);
|
|
1413
|
+
g_settings.autoPlayNum = getCurrentNo(g_settings.autoPlays, g_stateObj.autoPlay);
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
// 選択中のキーのヘルプ表示
|
|
1417
|
+
const targetKeymode = hasVal(g_keyObj[`transKey${g_keyObj.currentKey}_${g_keyObj.currentPtn}`])
|
|
1418
|
+
? g_keyObj[`transKey${g_keyObj.currentKey}_${g_keyObj.currentPtn}`] : g_keyObj.currentKey;
|
|
1419
|
+
btnKeymodeHelp.classList.remove(g_cssObj.button_Setting, g_cssObj.button_Tweet);
|
|
1420
|
+
btnKeymodeHelp.classList.add(
|
|
1421
|
+
g_cssObj[`button_${targetKeymode !== g_keyObj.currentKey
|
|
1422
|
+
&& (g_keyObj.defaultKeyList.includes(targetKeymode) || g_lblNameObj[`keyHelp${targetKeymode}`]) ? `Tweet` : `Setting`}`]
|
|
1423
|
+
);
|
|
1424
|
+
btnKeymodeHelp.style.display = (
|
|
1425
|
+
g_keyObj.defaultKeyList.includes(targetKeymode) || g_lblNameObj[`keyHelp${targetKeymode}`]
|
|
1426
|
+
? `` : C_DIS_NONE
|
|
1427
|
+
);
|
|
1428
|
+
|
|
1429
|
+
// ---------------------------------------------------
|
|
1430
|
+
// 3. 名称の設定
|
|
1431
|
+
|
|
1432
|
+
// 譜面名設定 (Difficulty)
|
|
1433
|
+
const difWidth = parseFloat(lnkDifficulty.style.width) - 20;
|
|
1434
|
+
const transKeyName = getTransKeyName();
|
|
1435
|
+
const keyUnitName = getStgDetailName(getKeyUnitName(g_keyObj.currentKey));
|
|
1436
|
+
|
|
1437
|
+
const prefix = `${getKeyName(g_keyObj.currentKey)}${transKeyName} ${keyUnitName} / `;
|
|
1438
|
+
let difLabel = `${g_headerObj.difLabels[g_stateObj.scoreId]}`;
|
|
1439
|
+
|
|
1440
|
+
const [difName, difSiz] = getFontSizeMulti(difLabel, difWidth, { maxSiz: g_limitObj.setLblSiz, prefix });
|
|
1441
|
+
lnkDifficulty.style.fontSize = wUnit(difSiz);
|
|
1442
|
+
|
|
1443
|
+
const difNames = [difName];
|
|
1444
|
+
if (g_headerObj.makerView) {
|
|
1445
|
+
difNames.push(`(${g_headerObj.creatorNames[g_stateObj.scoreId]})`);
|
|
1446
|
+
difNames.forEach((difName, j) => {
|
|
1447
|
+
const tmpSize = getFontSize2(difName, difWidth);
|
|
1448
|
+
difNames[j] = `<span style="font-size:${wUnit(tmpSize)}">${difName}</span>`;
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
lnkDifficulty.innerHTML = difNames.join(``);
|
|
1452
|
+
|
|
1453
|
+
// 速度設定 (Speed)
|
|
1454
|
+
setSetting(0, `speed`, { unitName: ` ${g_lblNameObj.multi}` });
|
|
1455
|
+
|
|
1456
|
+
// リバース設定 (Reverse, Scroll)
|
|
1457
|
+
if (g_headerObj.scrollUse) {
|
|
1458
|
+
g_stateObj.scroll = g_settings.scrolls[g_settings.scrollNum];
|
|
1459
|
+
const [visibleScr, hiddenScr] = (g_settings.scrolls.length > 1 ? [`scroll`, `reverse`] : [`reverse`, `scroll`]);
|
|
1460
|
+
document.getElementById(`${visibleScr}Sprite`).style.display = C_DIS_INHERIT;
|
|
1461
|
+
document.getElementById(`${hiddenScr}Sprite`).style.display = C_DIS_NONE;
|
|
1462
|
+
setSetting(0, visibleScr);
|
|
1463
|
+
|
|
1464
|
+
g_shortcutObj.option.KeyR.id = g_settings.scrolls.includes(C_FLG_REVERSE) ?
|
|
1465
|
+
g_shortcutObj.option.KeyR.exId : g_shortcutObj.option.KeyR.dfId;
|
|
1466
|
+
|
|
1467
|
+
if (g_settings.scrolls.length > 1) {
|
|
1468
|
+
setReverseView(document.getElementById(`btnReverse`));
|
|
1469
|
+
}
|
|
1470
|
+
} else {
|
|
1471
|
+
g_settings.scrolls = structuredClone(g_keyObj.scrollName_def);
|
|
1472
|
+
setSetting(0, `reverse`);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// オート・アシスト設定 (AutoPlay)
|
|
1476
|
+
g_stateObj.autoPlay = g_settings.autoPlays[g_settings.autoPlayNum];
|
|
1477
|
+
lnkAutoPlay.textContent = getStgDetailName(g_stateObj.autoPlay);
|
|
1478
|
+
|
|
1479
|
+
// 譜面毎のExcessive再設定(意図的に変更した場合のみ元に戻さない)
|
|
1480
|
+
if (g_canLoadDifInfoFlg && (g_stateObj.excessiveScoreId !== g_stateObj.scoreId)) {
|
|
1481
|
+
g_headerObj.excessiveJdgUse = g_headerObj.excessiveJdgUses[g_stateObj.scoreId];
|
|
1482
|
+
g_stateObj.excessive = boolToSwitch(g_headerObj.excessiveJdgUse);
|
|
1483
|
+
}
|
|
1484
|
+
g_headerObj.excessiveUse = g_headerObj.excessiveUses[g_stateObj.scoreId];
|
|
1485
|
+
if (g_headerObj.excessiveUse) {
|
|
1486
|
+
setExcessive(document.getElementById(`lnkExcessive`), g_stateObj.excessive === C_FLG_ON);
|
|
1487
|
+
lblExcessive.style.display = C_DIS_NONE;
|
|
1488
|
+
lnkExcessive.style.display = C_DIS_INHERIT;
|
|
1489
|
+
} else {
|
|
1490
|
+
g_stateObj.excessiveChgFlg = false;
|
|
1491
|
+
lblExcessive.style.display = (g_headerObj.excessiveJdgUses[g_stateObj.scoreId] ? C_DIS_INHERIT : C_DIS_NONE);
|
|
1492
|
+
lnkExcessive.style.display = C_DIS_NONE;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// 譜面明細画面の再描画
|
|
1496
|
+
if (g_settings.scoreDetails.length > 0) {
|
|
1497
|
+
drawSpeedGraph(g_stateObj.scoreId);
|
|
1498
|
+
drawDensityGraph(g_stateObj.scoreId);
|
|
1499
|
+
makeDifInfo(g_stateObj.scoreId);
|
|
1500
|
+
makeHighScore(g_stateObj.scoreId);
|
|
1501
|
+
drawMinimap(g_stateObj.scoreId, { _initFlg: true });
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// 楽曲データの表示
|
|
1505
|
+
lblMusicInfo.textContent = getMusicInfoView();
|
|
1506
|
+
lblMusicInfo.style.fontSize = wUnit(getFontSize2(lblMusicInfo.textContent, g_btnWidth(3 / 4), { maxSiz: 12 }));
|
|
1507
|
+
|
|
1508
|
+
// ユーザカスタムイベント(初期)
|
|
1509
|
+
safeExecuteCustomHooks(`g_customJsObj.difficulty`, g_customJsObj.difficulty, _initFlg, g_canLoadDifInfoFlg);
|
|
1510
|
+
resolveKeyFamily();
|
|
1511
|
+
|
|
1512
|
+
// 設定サマリー表示の更新
|
|
1513
|
+
updateSettingSummary();
|
|
1514
|
+
|
|
1515
|
+
// ---------------------------------------------------
|
|
1516
|
+
// 4. 譜面初期情報ロード許可フラグの設定
|
|
1517
|
+
g_canLoadDifInfoFlg = true;
|
|
1518
|
+
};
|
|
1519
|
+
|
|
1520
|
+
/**
|
|
1521
|
+
* keyLabelの系統ごとに、選択時に強制したい設定値・選択/離脱時の個別処理を登録する
|
|
1522
|
+
* @param {string} _name 系統名
|
|
1523
|
+
* @param {(key: string, context: { scoreId: number, ptn: string, prevKey: string }) => boolean} _match
|
|
1524
|
+
* この系統に属するkeyLabelかどうかの判定関数。第二引数のcontextは任意で利用可能
|
|
1525
|
+
* @param {object} [_fields] path -> 値 or 値を返す関数(単純代入で済むもの)
|
|
1526
|
+
* @param {object} [_hooks] { onAcquire, onRelease } 選択/離脱の瞬間に1回だけ呼ばれる(複合処理用)
|
|
1527
|
+
* @param {object} [_appliers] path -> 個別の適用関数(単純代入で済まないfields用)
|
|
1528
|
+
*/
|
|
1529
|
+
const registerKeyFamily = (_name, _match, _fields = {}, _hooks = {}, _appliers = {}) => {
|
|
1530
|
+
g_familyObj.families.push({ name: _name, match: _match, fields: _fields, appliers: _appliers, ..._hooks });
|
|
1531
|
+
};
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
* 現在のキー状態に応じて適用すべき設定群を判定し、
|
|
1535
|
+
* 各パラメータの値・スナップショット・フックを管理・適用する
|
|
1536
|
+
*/
|
|
1537
|
+
const resolveKeyFamily = () => {
|
|
1538
|
+
// match() へ渡す追加情報。第一引数(keyLabel)だけで判定できる場合は無視してよい
|
|
1539
|
+
const context = {
|
|
1540
|
+
scoreId: g_stateObj.scoreId,
|
|
1541
|
+
ptn: g_keyObj.currentPtn,
|
|
1542
|
+
prevKey: g_keyObj.prevKey,
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
// 1. 現在のキーに一致するファミリーを検索(見つからなければ null = 標準状態)
|
|
1546
|
+
const newFamily = g_familyObj.families.find(f => f.match(g_keyObj.currentKey, context)) ?? null;
|
|
1547
|
+
|
|
1548
|
+
// 2. ファミリーが切り替わった瞬間(入場・離脱)にフック(固有の副作用)を実行
|
|
1549
|
+
if (newFamily !== g_familyObj.currentFamily) {
|
|
1550
|
+
g_familyObj.currentFamily?.onRelease?.();
|
|
1551
|
+
newFamily?.onAcquire?.();
|
|
1552
|
+
g_familyObj.currentFamily = newFamily;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// 3. 登録されているすべてのファミリーが持つパス(プロパティ)の全リストを取得
|
|
1556
|
+
const allPaths = new Set(g_familyObj.families.flatMap(f => Object.keys(f.fields)));
|
|
1557
|
+
allPaths.forEach(path => {
|
|
1558
|
+
const spec = newFamily?.fields[path];
|
|
1559
|
+
|
|
1560
|
+
if (spec !== undefined) {
|
|
1561
|
+
// 【変更値の適用】現在のファミリーにそのプロパティの設定がある場合
|
|
1562
|
+
// 初めて書き換えるプロパティの場合のみ、元の値をスナップショット(初期値)として退避
|
|
1563
|
+
if (!g_familyObj.ownership[path]) {
|
|
1564
|
+
g_familyObj.snapshot[path] = getPathVal(path);
|
|
1565
|
+
}
|
|
1566
|
+
// 関数なら評価し、値ならそのまま適用値とする
|
|
1567
|
+
g_familyObj.ownership[path] = newFamily;
|
|
1568
|
+
const applier = newFamily.appliers[path] ?? (v => setPathVal(path, v));
|
|
1569
|
+
applyIfChanged(path, typeof spec === `function` ? spec() : spec, applier);
|
|
1570
|
+
|
|
1571
|
+
} else if (g_familyObj.ownership[path]) {
|
|
1572
|
+
// 【標準への復元】別のファミリーに移行し、かつ元々自分が書き換えていたプロパティの場合
|
|
1573
|
+
// スナップショットから元の値(初期値)を復元し、オーナーシップを解放
|
|
1574
|
+
const applier = g_familyObj.ownership[path].appliers[path] ?? (v => setPathVal(path, v));
|
|
1575
|
+
applyIfChanged(path, g_familyObj.snapshot[path], applier);
|
|
1576
|
+
g_familyObj.ownership[path] = null;
|
|
1577
|
+
}
|
|
1578
|
+
// ownership[path]がnullのままなら、標準同士の切り替えでも一切触らない
|
|
1579
|
+
});
|
|
1580
|
+
};
|
|
1581
|
+
|
|
1582
|
+
/**
|
|
1583
|
+
* 値に変更がある場合のみ、代入またはカスタム処理を実行する
|
|
1584
|
+
* - 「エンジンが前回適用した値」ではなく「実際に今その場所にある値」と比較することで、
|
|
1585
|
+
* ユーザーの手動操作など、本エンジンを介さずに値が変更されたケースでも
|
|
1586
|
+
* 正しく差分を検知できるようにしている
|
|
1587
|
+
* @param {string} _path 設定のパス
|
|
1588
|
+
* @param {string|number} _value 適用する値
|
|
1589
|
+
* @param {Function} _applier 実際の適用処理を行う関数
|
|
1590
|
+
*/
|
|
1591
|
+
const applyIfChanged = (_path, _value, _applier) => {
|
|
1592
|
+
// 実際の現在値と適用したい値が一致していれば、無駄な再描画や再代入を防ぐためスキップ
|
|
1593
|
+
if (getPathVal(_path) === _value) return;
|
|
1594
|
+
|
|
1595
|
+
// 実際の反映処理(値の代入、または画像再描画などの副作用)を実行
|
|
1596
|
+
_applier(_value);
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1599
|
+
/**
|
|
1600
|
+
* 設定・オプション画面のラベル・ボタン処理の描画
|
|
1601
|
+
* @param {Object} _sprite 基準とするスプライト(ここで指定する座標は、そのスプライトからの相対位置)
|
|
1602
|
+
*/
|
|
1603
|
+
const createOptionWindow = _sprite => {
|
|
1604
|
+
|
|
1605
|
+
// 各ボタン用のスプライトを作成
|
|
1606
|
+
const optionsprite = createEmptySprite(_sprite, `optionsprite`, g_windowObj.optionSprite);
|
|
1607
|
+
|
|
1608
|
+
// 設定毎に個別のスプライトを作成し、その中にラベル・ボタン類を配置
|
|
1609
|
+
const spriteList = setSpriteList(g_settingPos.option);
|
|
1610
|
+
|
|
1611
|
+
// ---------------------------------------------------
|
|
1612
|
+
// 難易度 (Difficulty)
|
|
1613
|
+
// 縦位置: 0
|
|
1614
|
+
spriteList.difficulty.appendChild(createLblSetting(`Difficulty`, -5));
|
|
1615
|
+
|
|
1616
|
+
const lnkDifficulty = makeSettingLblCssButton(`lnkDifficulty`, ``, 0, () => changeDifficulty(), {
|
|
1617
|
+
y: -10, h: g_limitObj.setLblHeight + 10, cxtFunc: () => changeDifficulty(-1),
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1620
|
+
// 譜面選択ボタン(メイン、右回し、左回し)
|
|
1621
|
+
multiAppend(spriteList.difficulty,
|
|
1622
|
+
lnkDifficulty,
|
|
1623
|
+
makeMiniCssButton(`lnkDifficulty`, `R`, 0, () => nextDifficulty(), { dy: -10, dh: 10 }),
|
|
1624
|
+
makeMiniCssButton(`lnkDifficulty`, `L`, 0, () => nextDifficulty(-1), { dy: -10, dh: 10 }),
|
|
1625
|
+
);
|
|
1626
|
+
createScText(spriteList.difficulty, `Difficulty`);
|
|
1627
|
+
if (g_headerObj.difSelectorUse) {
|
|
1628
|
+
createScText(spriteList.difficulty, `DifficultyList`, { x: 154, y: -10, targetLabel: `lnkDifficulty` });
|
|
1629
|
+
}
|
|
1630
|
+
multiAppend(difficultySprite,
|
|
1631
|
+
createCss2Button(`btnKeymodeHelp`, `?`, () => {
|
|
1632
|
+
const targetKeymode = hasVal(g_keyObj[`transKey${g_keyObj.currentKey}_${g_keyObj.currentPtn}`])
|
|
1633
|
+
? g_keyObj[`transKey${g_keyObj.currentKey}_${g_keyObj.currentPtn}`] : g_keyObj.currentKey;
|
|
1634
|
+
openLink(
|
|
1635
|
+
g_keyObj.defaultKeyList.includes(targetKeymode)
|
|
1636
|
+
? g_lblNameObj.keymodeUrl + targetKeymode
|
|
1637
|
+
: g_lblNameObj[`keyHelp${targetKeymode}`]);
|
|
1638
|
+
}, g_lblPosObj.btnKeymodeHelp, g_cssObj.button_Setting),
|
|
1639
|
+
)
|
|
1640
|
+
|
|
1641
|
+
// ---------------------------------------------------
|
|
1642
|
+
// ハイスコア機能実装時に使用予定のスペース
|
|
1643
|
+
// 縦位置: 1
|
|
1644
|
+
|
|
1645
|
+
// ---------------------------------------------------
|
|
1646
|
+
// 速度(Speed)
|
|
1647
|
+
// 縦位置: 2 短縮ショートカットあり
|
|
1648
|
+
createGeneralSetting(spriteList.speed, `speed`, {
|
|
1649
|
+
skipTerms: g_settings.speedTerms, hiddenBtn: true, scLabel: g_lblNameObj.sc_speed, roundNum: 5,
|
|
1650
|
+
unitName: ` ${g_lblNameObj.multi}`, addRFunc: () => calculateTotalSpeed(),
|
|
1651
|
+
});
|
|
1652
|
+
if (g_headerObj.baseSpeed !== 1) {
|
|
1653
|
+
divRoot.appendChild(
|
|
1654
|
+
createDivCss2Label(`lblBaseSpd`, `Δv: ${Math.round(g_headerObj.baseSpeed * 100) / 100}x`, g_lblPosObj.lblBaseSpd)
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* 譜面明細子画面・グラフの作成
|
|
1660
|
+
* @param {string} _name
|
|
1661
|
+
* @param {boolean} _graphUseFlg
|
|
1662
|
+
* @returns {HTMLDivElement}
|
|
1663
|
+
*/
|
|
1664
|
+
const createScoreDetail = (_name, _graphUseFlg = true, _graphNum = 1) => {
|
|
1665
|
+
const detailObj = createEmptySprite(scoreDetail, `detail${_name}`, g_windowObj.detailObj);
|
|
1666
|
+
|
|
1667
|
+
if (_graphUseFlg) {
|
|
1668
|
+
for (let j = 0; j < _graphNum; j++) {
|
|
1669
|
+
const graphObj = document.createElement(`canvas`);
|
|
1670
|
+
const textBaseObj = document.getElementById(`lnkDifficulty`);
|
|
1671
|
+
const bkColor = window.getComputedStyle(textBaseObj, ``).backgroundColor;
|
|
1672
|
+
|
|
1673
|
+
graphObj.id = `graph${_name}${j > 0 ? j + 1 : ``}`;
|
|
1674
|
+
graphObj.width = g_limitObj.graphWidth * g_dpr;
|
|
1675
|
+
graphObj.height = g_limitObj.graphHeight * g_dpr;
|
|
1676
|
+
graphObj.style.width = wUnit(g_limitObj.graphWidth);
|
|
1677
|
+
graphObj.style.height = wUnit(g_limitObj.graphHeight);
|
|
1678
|
+
graphObj.style.left = wUnit(125);
|
|
1679
|
+
graphObj.style.top = wUnit(0);
|
|
1680
|
+
graphObj.style.position = `absolute`;
|
|
1681
|
+
graphObj.style.background = j === 0 ? bkColor : `#ffffff00`;
|
|
1682
|
+
const ctx = graphObj.getContext(`2d`);
|
|
1683
|
+
ctx.scale(g_dpr, g_dpr);
|
|
1684
|
+
|
|
1685
|
+
detailObj.appendChild(graphObj);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
return detailObj;
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
if (g_settings.scoreDetails.length > 0) {
|
|
1693
|
+
multiAppend(spriteList.speed,
|
|
1694
|
+
createCss2Button(`btnGraphB`, ``, () => true, {
|
|
1695
|
+
x: -25, y: -60, w: 0, h: 0, opacity: 0, resetFunc: () => setScoreDetail(true),
|
|
1696
|
+
}, g_cssObj.button_Mini),
|
|
1697
|
+
createCss2Button(`btnGraph`, `i`, () => true, {
|
|
1698
|
+
x: -25, y: -60, w: 30, h: 30, siz: g_limitObj.jdgCharaSiz, title: g_msgObj.graph,
|
|
1699
|
+
resetFunc: () => setScoreDetail(), cxtFunc: () => setScoreDetail(),
|
|
1700
|
+
}, g_cssObj.button_Mini)
|
|
1701
|
+
);
|
|
1702
|
+
g_stateObj.scoreDetailViewFlg = false;
|
|
1703
|
+
const scoreDetail = createEmptySprite(optionsprite, `scoreDetail`, g_windowObj.scoreDetail, g_cssObj.settings_DifSelector);
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* 譜面明細表示の切替
|
|
1707
|
+
* @param {number} _val
|
|
1708
|
+
*/
|
|
1709
|
+
const changeScoreDetail = (_val = 0) => {
|
|
1710
|
+
if (g_currentPage === `difSelector`) {
|
|
1711
|
+
resetDifWindow();
|
|
1712
|
+
}
|
|
1713
|
+
g_currentPage = `scoreDetail`;
|
|
1714
|
+
setShortcutEvent(g_currentPage, () => true, { displayFlg: false });
|
|
1715
|
+
g_stateObj.scoreDetailViewFlg = true;
|
|
1716
|
+
scoreDetail.style.visibility = `visible`;
|
|
1717
|
+
visibleSettingSummary(false);
|
|
1718
|
+
|
|
1719
|
+
// 表示内容を非表示化、ボタン色をデフォルトに戻す
|
|
1720
|
+
$id(`detail${g_stateObj.scoreDetail}`).visibility = `hidden`;
|
|
1721
|
+
document.getElementById(`lnk${g_stateObj.scoreDetail}G`).classList.replace(g_cssObj.button_Setting, g_cssObj.button_Default);
|
|
1722
|
+
|
|
1723
|
+
// 選択先を表示、ボタン色を選択中に変更
|
|
1724
|
+
// Qキーを押したときのリンク先を次の明細へ変更
|
|
1725
|
+
g_stateObj.scoreDetail = g_settings.scoreDetails[_val];
|
|
1726
|
+
[`option`, `difSelector`, `scoreDetail`].forEach(page => g_shortcutObj[page].KeyQ.id = g_settings.scoreDetailCursors[nextPos(_val, 1, g_settings.scoreDetailCursors.length)]);
|
|
1727
|
+
g_shortcutObj.scoreDetail.ArrowDown.id = g_settings.scoreDetailCursorsOrg[nextPos(_val, 1, g_settings.scoreDetailCursorsOrg.length)];
|
|
1728
|
+
g_shortcutObj.scoreDetail.ArrowUp.id = g_settings.scoreDetailCursorsOrg[nextPos(_val, -1, g_settings.scoreDetailCursorsOrg.length)];
|
|
1729
|
+
|
|
1730
|
+
$id(`detail${g_stateObj.scoreDetail}`).visibility = `visible`;
|
|
1731
|
+
document.getElementById(`lnk${g_stateObj.scoreDetail}G`).classList.replace(g_cssObj.button_Default, g_cssObj.button_Setting);
|
|
1732
|
+
|
|
1733
|
+
document.getElementById(`lnkMiniMapRev`).style.display =
|
|
1734
|
+
g_stateObj.scoreDetail === `MiniMap` && g_stateObj.scoreDetailViewFlg ? C_DIS_INHERIT : C_DIS_NONE;
|
|
1735
|
+
};
|
|
1736
|
+
|
|
1737
|
+
multiAppend(scoreDetail,
|
|
1738
|
+
createScoreDetail(`Speed`, true, 2),
|
|
1739
|
+
createScoreDetail(`Density`),
|
|
1740
|
+
createScoreDetail(`ToolDif`, false),
|
|
1741
|
+
createScoreDetail(`HighScore`, false),
|
|
1742
|
+
createScoreDetail(`MiniMap`, false),
|
|
1743
|
+
);
|
|
1744
|
+
g_settings.scoreDetails.forEach((sd, j) => {
|
|
1745
|
+
scoreDetail.appendChild(
|
|
1746
|
+
makeDifLblCssButton(`lnk${sd}G`, getStgDetailName(sd), j, () => changeScoreDetail(j), {
|
|
1747
|
+
w: g_limitObj.difCoverWidth, h: 20, title: g_msgObj[`s_${sd}`],
|
|
1748
|
+
btnStyle: (g_stateObj.scoreDetail === sd ? `Setting` : `Default`),
|
|
1749
|
+
})
|
|
1750
|
+
);
|
|
1751
|
+
createScText(document.getElementById(`lnk${sd}G`), `${sd}G`, { targetLabel: `lnk${sd}G`, x: -5 });
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/**
|
|
1756
|
+
* 譜面明細表示/非表示ボタンの処理
|
|
1757
|
+
* @param {boolean} _resetFlg
|
|
1758
|
+
*/
|
|
1759
|
+
const setScoreDetail = (_resetFlg = false) => {
|
|
1760
|
+
if (g_currentPage === `difSelector`) {
|
|
1761
|
+
resetDifWindow();
|
|
1762
|
+
g_stateObj.scoreDetailViewFlg = false;
|
|
1763
|
+
g_shortcutObj.difSelector.KeyQ.id = g_settings.scoreDetailCursors[0];
|
|
1764
|
+
}
|
|
1765
|
+
const scoreDetail = document.getElementById(`scoreDetail`);
|
|
1766
|
+
const detailObj = document.getElementById(`detail${g_stateObj.scoreDetail}`);
|
|
1767
|
+
const visibles = [`hidden`, `visible`];
|
|
1768
|
+
|
|
1769
|
+
g_stateObj.scoreDetailViewFlg = !g_stateObj.scoreDetailViewFlg;
|
|
1770
|
+
scoreDetail.style.visibility = visibles[Number(g_stateObj.scoreDetailViewFlg)];
|
|
1771
|
+
detailObj.style.visibility = visibles[Number(g_stateObj.scoreDetailViewFlg)];
|
|
1772
|
+
visibleSettingSummary(g_stateObj.scoreDetailViewFlg ? false : g_stateObj.settingSummaryVisible);
|
|
1773
|
+
|
|
1774
|
+
g_currentPage = g_stateObj.scoreDetailViewFlg ? `scoreDetail` : `option`;
|
|
1775
|
+
setShortcutEvent(g_currentPage, () => true, { displayFlg: false });
|
|
1776
|
+
|
|
1777
|
+
// Qキーのカーソル位置は、ハードリセットまたはスコア明細を閉じるときに先頭へ戻す
|
|
1778
|
+
if (_resetFlg || !g_stateObj.scoreDetailViewFlg) {
|
|
1779
|
+
[`option`, `difSelector`, `scoreDetail`].forEach(page => g_shortcutObj[page].KeyQ.id = g_settings.scoreDetailCursors[0]);
|
|
1780
|
+
}
|
|
1781
|
+
document.getElementById(`lnkMiniMapRev`).style.display =
|
|
1782
|
+
g_stateObj.scoreDetail === `MiniMap` && g_stateObj.scoreDetailViewFlg ? C_DIS_INHERIT : C_DIS_NONE;
|
|
1783
|
+
};
|
|
1784
|
+
|
|
1785
|
+
// ---------------------------------------------------
|
|
1786
|
+
// 速度モーション (Motion)
|
|
1787
|
+
// 縦位置: 3
|
|
1788
|
+
createGeneralSetting(spriteList.motion, `motion`);
|
|
1789
|
+
|
|
1790
|
+
// ---------------------------------------------------
|
|
1791
|
+
// リバース (Reverse) / スクロール (Scroll)
|
|
1792
|
+
// 縦位置: 4
|
|
1793
|
+
createGeneralSetting(spriteList.reverse, `reverse`, {
|
|
1794
|
+
addRFunc: () => {
|
|
1795
|
+
if (g_headerObj.scrollUse && g_settings.scrolls.length > 1) {
|
|
1796
|
+
setReverseView(document.getElementById(`btnReverse`));
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
if (g_headerObj.scrollUse) {
|
|
1801
|
+
createGeneralSetting(spriteList.scroll, `scroll`, { scLabel: g_lblNameObj.sc_scroll });
|
|
1802
|
+
[$id(`lnkScroll`).left, $id(`lnkScroll`).width] = [
|
|
1803
|
+
wUnit(parseFloat($id(`lnkScroll`).left) + 90), wUnit(parseFloat($id(`lnkScroll`).width) - 90)
|
|
1804
|
+
];
|
|
1805
|
+
|
|
1806
|
+
spriteList.scroll.appendChild(
|
|
1807
|
+
createCss2Button(`btnReverse`, `${g_lblNameObj.Reverse}:${getStgDetailName(g_stateObj.reverse)}`, evt => setReverse(evt.target), {
|
|
1808
|
+
...g_lblPosObj.btnReverse,
|
|
1809
|
+
cxtFunc: evt => setReverse(evt.target),
|
|
1810
|
+
}, g_cssObj.button_Default, g_cssObj[`button_Rev${g_stateObj.reverse}`])
|
|
1811
|
+
);
|
|
1812
|
+
spriteList[g_settings.scrolls.length > 1 ? `reverse` : `scroll`].style.display = C_DIS_NONE;
|
|
1813
|
+
} else {
|
|
1814
|
+
spriteList.scroll.style.pointerEvents = C_DIS_NONE;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// ---------------------------------------------------
|
|
1818
|
+
// ミラー・ランダム (Shuffle)
|
|
1819
|
+
// 縦位置: 5.5
|
|
1820
|
+
createGeneralSetting(spriteList.shuffle, `shuffle`, g_settings.scoreDetails.length > 0 ? {
|
|
1821
|
+
addRFunc: () => makeHighScore(g_stateObj.scoreId),
|
|
1822
|
+
} : {});
|
|
1823
|
+
|
|
1824
|
+
// ---------------------------------------------------
|
|
1825
|
+
// 鑑賞モード設定 (AutoPlay)
|
|
1826
|
+
// 縦位置: 6.5
|
|
1827
|
+
createGeneralSetting(spriteList.autoPlay, `autoPlay`, g_settings.scoreDetails.length > 0 ? {
|
|
1828
|
+
addRFunc: () => makeHighScore(g_stateObj.scoreId),
|
|
1829
|
+
} : {});
|
|
1830
|
+
|
|
1831
|
+
// ---------------------------------------------------
|
|
1832
|
+
// ゲージ設定 (Gauge)
|
|
1833
|
+
// 縦位置: 7.5
|
|
1834
|
+
spriteList.gauge.appendChild(createLblSetting(`Gauge`));
|
|
1835
|
+
|
|
1836
|
+
// ゲージ設定詳細 縦位置: ゲージ設定+1
|
|
1837
|
+
spriteList.gauge.appendChild(createDivCss2Label(`lblGauge2`, ``, g_lblPosObj.lblGauge2));
|
|
1838
|
+
|
|
1839
|
+
if (g_headerObj.gaugeUse) {
|
|
1840
|
+
multiAppend(spriteList.gauge,
|
|
1841
|
+
makeSettingLblCssButton(`lnkGauge`, ``, 0, () => setGauge(1), { cxtFunc: () => setGauge(-1) }),
|
|
1842
|
+
makeMiniCssButton(`lnkGauge`, `R`, 0, () => setGauge(1)),
|
|
1843
|
+
makeMiniCssButton(`lnkGauge`, `L`, 0, () => setGauge(-1)),
|
|
1844
|
+
);
|
|
1845
|
+
createScText(spriteList.gauge, `Gauge`);
|
|
1846
|
+
} else {
|
|
1847
|
+
lblGauge.classList.add(g_cssObj.settings_Disabled);
|
|
1848
|
+
spriteList.gauge.appendChild(makeDisabledLabel(`lnkGauge`, 0, getStgDetailName(g_stateObj.gauge)));
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// 空押し判定設定 (Excessive)
|
|
1852
|
+
spriteList.gauge.appendChild(
|
|
1853
|
+
createDivCss2Label(`lblExcessive`, `${g_lblNameObj.Excessive}:${C_FLG_ON}`,
|
|
1854
|
+
g_lblPosObj.lblExcessive, g_cssObj[`button_Disabled${C_FLG_ON}`]
|
|
1855
|
+
)
|
|
1856
|
+
);
|
|
1857
|
+
spriteList.gauge.appendChild(
|
|
1858
|
+
createCss2Button(`lnkExcessive`, g_lblNameObj.Excessive, evt => setExcessive(evt.target), {
|
|
1859
|
+
...g_lblPosObj.btnExcessive,
|
|
1860
|
+
title: g_msgObj.excessive, cxtFunc: evt => setExcessive(evt.target),
|
|
1861
|
+
}, g_cssObj.button_Default, g_cssObj[`button_Rev${g_stateObj.excessive}`])
|
|
1862
|
+
);
|
|
1863
|
+
createScText(lnkExcessive, `Excessive`, { x: -13, targetLabel: `lnkExcessive` });
|
|
1864
|
+
|
|
1865
|
+
// ---------------------------------------------------
|
|
1866
|
+
// タイミング調整 (Adjustment)
|
|
1867
|
+
// 縦位置: 10.5 短縮ショートカットあり
|
|
1868
|
+
createGeneralSetting(spriteList.adjustment, `adjustment`, {
|
|
1869
|
+
skipTerms: g_settings.adjustmentTerms, hiddenBtn: true, scLabel: g_lblNameObj.sc_adjustment, roundNum: 5,
|
|
1870
|
+
unitName: g_lblNameObj.frame, addRFunc: () => viewAdjustment(),
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
const viewAdjustment = () => {
|
|
1874
|
+
if (g_headerObj.playbackRate !== 1) {
|
|
1875
|
+
const adjustmentVal = isLocalMusicFile(g_stateObj.scoreId)
|
|
1876
|
+
? Math.round(g_stateObj.adjustment / g_headerObj.playbackRate)
|
|
1877
|
+
: (g_stateObj.adjustment / g_headerObj.playbackRate).toFixed(1);
|
|
1878
|
+
document.getElementById(`lnkAdjustment`).textContent = ``;
|
|
1879
|
+
if (document.getElementById(`lnkAdjustment1`) === null) {
|
|
1880
|
+
multiAppend(
|
|
1881
|
+
adjustmentSprite,
|
|
1882
|
+
createDivCss2Label(`lnkAdjustment1`, ``, g_lblPosObj.lnkAdjustment1),
|
|
1883
|
+
createDivCss2Label(`lnkAdjustment2`, ``, g_lblPosObj.lnkAdjustment2),
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
document.getElementById(`lnkAdjustment1`).textContent = `${adjustmentVal}${g_lblNameObj.frame}`;
|
|
1887
|
+
document.getElementById(`lnkAdjustment2`).textContent = `(${g_stateObj.adjustment.toFixed(1)}${g_localStorage.adjustment === g_stateObj.adjustment ? '*' : ''})`;
|
|
1888
|
+
}
|
|
1889
|
+
};
|
|
1890
|
+
viewAdjustment();
|
|
1891
|
+
|
|
1892
|
+
// ---------------------------------------------------
|
|
1893
|
+
// フェードイン (Fadein)
|
|
1894
|
+
// 縦位置: 11.5 スライダーあり
|
|
1895
|
+
spriteList.fadein.appendChild(createLblSetting(`Fadein`));
|
|
1896
|
+
|
|
1897
|
+
const lnkFadein = createDivCss2Label(`lnkFadein`, `${g_stateObj.fadein}${g_lblNameObj.percent}`,
|
|
1898
|
+
g_lblPosObj.lnkFadein, g_cssObj.settings_FadeinBar);
|
|
1899
|
+
spriteList.fadein.appendChild(lnkFadein);
|
|
1900
|
+
|
|
1901
|
+
const setFadein = _sign => {
|
|
1902
|
+
g_stateObj.fadein = nextPos(g_stateObj.fadein, _sign, 100);
|
|
1903
|
+
fadeinSlider.value = g_stateObj.fadein;
|
|
1904
|
+
lnkFadein.textContent = `${g_stateObj.fadein}${g_lblNameObj.percent}`;
|
|
1905
|
+
updateSettingSummary();
|
|
1906
|
+
drawMinimap(g_stateObj.scoreId, { _fadeinFlg: true });
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1909
|
+
multiAppend(spriteList.fadein,
|
|
1910
|
+
|
|
1911
|
+
// 右回し・左回しボタン
|
|
1912
|
+
makeMiniCssButton(`lnkFadein`, `R`, 0, () => setFadein(1)),
|
|
1913
|
+
makeMiniCssButton(`lnkFadein`, `L`, 0, () => setFadein(-1)),
|
|
1914
|
+
|
|
1915
|
+
// フェードインのスライダー処理
|
|
1916
|
+
createDivCss2Label(`lblFadeinBar`, `<input id="fadeinSlider" type="range" value="${g_stateObj.fadein}" min="0" max="99" step="1">`,
|
|
1917
|
+
g_lblPosObj.lblFadeinBar),
|
|
1918
|
+
|
|
1919
|
+
);
|
|
1920
|
+
|
|
1921
|
+
const fadeinSlider = document.getElementById(`fadeinSlider`);
|
|
1922
|
+
g_handler.addListener(fadeinSlider, `input`, () => {
|
|
1923
|
+
g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein, `fadein`);
|
|
1924
|
+
updateSettingSummary();
|
|
1925
|
+
drawMinimap(g_stateObj.scoreId, { _fadeinFlg: true });
|
|
1926
|
+
}, false);
|
|
1927
|
+
|
|
1928
|
+
// ---------------------------------------------------
|
|
1929
|
+
// ボリューム (Volume)
|
|
1930
|
+
// 縦位置: 12.5
|
|
1931
|
+
createGeneralSetting(spriteList.volume, `volume`, { unitName: g_lblNameObj.percent });
|
|
1932
|
+
|
|
1933
|
+
// 譜面番号の再取得
|
|
1934
|
+
g_stateObj.scoreId = getNextDifficulty(g_stateObj.scoreId, 0);
|
|
1935
|
+
const keyLists = makeDedupliArray(g_headerObj.viewLists.map(j => g_headerObj.keyLabels[j]));
|
|
1936
|
+
g_headerObj.viewKeyLists = keyLists.sort((a, b) => parseInt(a) - parseInt(b));
|
|
1937
|
+
g_headerObj.difSelectorUse = getDifSelectorUse(g_rootObj.difSelectorUse);
|
|
1938
|
+
|
|
1939
|
+
// 設定画面の一通りのオブジェクトを作成後に譜面・速度・ゲージ設定をまとめて行う
|
|
1940
|
+
setDifficulty(false);
|
|
1941
|
+
optionsprite.oncontextmenu = () => false;
|
|
1942
|
+
};
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* 汎用設定
|
|
1946
|
+
* @param {HTMLDivElement} _obj
|
|
1947
|
+
* @param {string} _settingName
|
|
1948
|
+
* @param {string} [object.unitName=''] 設定名の単位
|
|
1949
|
+
* @param {number[]} [object.skipTerms] ボタンの設定スキップ間隔(デフォルト:[1(外側), 1(内側), 1(最内側)])
|
|
1950
|
+
* @param {boolean} [object.hiddenBtn=false] 隠しボタン(ショートカットキーのみ)の利用有無
|
|
1951
|
+
* @param {Function} [object.addRFunc] 右側のボタンを押したときの追加処理
|
|
1952
|
+
* @param {Function} [object.addLFunc] 左側のボタンを押したときの追加処理
|
|
1953
|
+
* @param {string} [object.settingLabel=_settingName] 設定名
|
|
1954
|
+
* @param {string} [object.displayName] 画面名
|
|
1955
|
+
* @param {string} [object.scLabel=''] ショートカットキーの表示名
|
|
1956
|
+
* @param {number} [object.roundNum=0] 設定スキップ間隔の丸め基準数
|
|
1957
|
+
* @param {number} [object.adjY=0] 設定ボタンのY座標位置
|
|
1958
|
+
*/
|
|
1959
|
+
const createGeneralSetting = (_obj, _settingName, { unitName = ``,
|
|
1960
|
+
skipTerms = fillArray(3, 1), hiddenBtn = false, addRFunc = () => { }, addLFunc = addRFunc,
|
|
1961
|
+
settingLabel = _settingName, displayName = g_currentPage, scLabel = ``, roundNum = 0, adjY = 0 } = {}) => {
|
|
1962
|
+
|
|
1963
|
+
const settingUpper = toCapitalize(_settingName);
|
|
1964
|
+
const linkId = `lnk${settingUpper}`;
|
|
1965
|
+
const initName = `${getStgDetailName(g_stateObj[_settingName])}${unitName}`;
|
|
1966
|
+
_obj.appendChild(createLblSetting(settingUpper, adjY, toCapitalize(settingLabel)));
|
|
1967
|
+
|
|
1968
|
+
if (g_headerObj[`${_settingName}Use`] === undefined || g_headerObj[`${_settingName}Use`]) {
|
|
1969
|
+
|
|
1970
|
+
multiAppend(_obj,
|
|
1971
|
+
makeSettingLblCssButton(linkId, `${initName}${g_localStorage[_settingName] === g_stateObj[_settingName] ? ' *' : ''}`, 0,
|
|
1972
|
+
() => {
|
|
1973
|
+
setSetting(skipTerms[1], _settingName, { func: () => addRFunc(), unitName, roundNum });
|
|
1974
|
+
}, {
|
|
1975
|
+
cxtFunc: () => {
|
|
1976
|
+
setSetting(skipTerms[1] * (-1), _settingName, { func: () => addLFunc(), unitName, roundNum });
|
|
1977
|
+
}
|
|
1978
|
+
}),
|
|
1979
|
+
|
|
1980
|
+
// 右回し・左回しボタン(外側)
|
|
1981
|
+
makeMiniCssButton(linkId, `R`, 0, () =>
|
|
1982
|
+
setSetting(skipTerms[0], _settingName, { func: () => addRFunc(), unitName, roundNum })),
|
|
1983
|
+
makeMiniCssButton(linkId, `L`, 0, () =>
|
|
1984
|
+
setSetting(skipTerms[0] * (-1), _settingName, { func: () => addLFunc(), unitName, roundNum })),
|
|
1985
|
+
);
|
|
1986
|
+
|
|
1987
|
+
// 右回し・左回しボタン(内側)
|
|
1988
|
+
if (skipTerms[1] > 1) {
|
|
1989
|
+
multiAppend(_obj,
|
|
1990
|
+
makeMiniCssButton(linkId, `RR`, 0, () =>
|
|
1991
|
+
setSetting(skipTerms[1], _settingName, { func: () => addRFunc(), unitName, roundNum })),
|
|
1992
|
+
makeMiniCssButton(linkId, `LL`, 0, () =>
|
|
1993
|
+
setSetting(skipTerms[1] * (-1), _settingName, { func: () => addLFunc(), unitName, roundNum })),
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// 右回し・左回しボタン(最内側)
|
|
1998
|
+
if (skipTerms[2] > 1) {
|
|
1999
|
+
multiAppend(_obj,
|
|
2000
|
+
makeMiniCssButton(linkId, `RRR`, 0, () =>
|
|
2001
|
+
setSetting(skipTerms[2], _settingName, { func: () => addRFunc(), unitName, roundNum })
|
|
2002
|
+
, { dw: -g_limitObj.setMiniWidth / 2 }),
|
|
2003
|
+
makeMiniCssButton(linkId, `LLL`, 0, () =>
|
|
2004
|
+
setSetting(skipTerms[2] * (-1), _settingName, { func: () => addLFunc(), unitName, roundNum })
|
|
2005
|
+
, { dw: -g_limitObj.setMiniWidth / 2 }),
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// 右回し・左回しボタン(不可視)
|
|
2010
|
+
if (hiddenBtn) {
|
|
2011
|
+
multiAppend(_obj,
|
|
2012
|
+
makeMiniCssButton(linkId, `HR`, 0, () => setSetting(1, _settingName, { func: () => addRFunc(), unitName, roundNum }), { visibility: `hidden` }),
|
|
2013
|
+
makeMiniCssButton(linkId, `HL`, 0, () => setSetting(-1, _settingName, { func: () => addLFunc(), unitName, roundNum }), { visibility: `hidden` }),
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// ショートカットキー表示
|
|
2018
|
+
createScText(_obj, settingUpper, { displayName: displayName, dfLabel: scLabel });
|
|
2019
|
+
|
|
2020
|
+
} else {
|
|
2021
|
+
document.getElementById(`lbl${settingUpper}`).classList.add(g_cssObj.settings_Disabled);
|
|
2022
|
+
_obj.appendChild(makeDisabledLabel(linkId, 0, initName));
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
|
|
2026
|
+
/**
|
|
2027
|
+
* 設定画面用ラベルの作成
|
|
2028
|
+
* @param {string} _settingName
|
|
2029
|
+
* @param {number} _adjY
|
|
2030
|
+
* @param {string} _settingLabel
|
|
2031
|
+
* @returns {HTMLDivElement}
|
|
2032
|
+
*/
|
|
2033
|
+
const createLblSetting = (_settingName, _adjY = 0, _settingLabel = _settingName) => {
|
|
2034
|
+
const lbl = createDivCss2Label(`lbl${_settingName}`, g_lblNameObj[_settingLabel], {
|
|
2035
|
+
x: -5, y: _adjY, w: 110, pointerEvents: C_DIS_AUTO,
|
|
2036
|
+
}, `settings_${_settingName}`);
|
|
2037
|
+
lbl.title = g_msgObj[`${_settingName.charAt(0).toLowerCase()}${_settingName.slice(1)}`];
|
|
2038
|
+
return lbl;
|
|
2039
|
+
};
|
|
2040
|
+
|
|
2041
|
+
/**
|
|
2042
|
+
* 設定名の置き換え処理(未指定時は原則g_lblNameObjの値を使用、明示的な指定がある場合のみ元の名前を返却)
|
|
2043
|
+
* @param {string} _name
|
|
2044
|
+
* @returns {string}
|
|
2045
|
+
*/
|
|
2046
|
+
const getStgDetailName = _name => {
|
|
2047
|
+
return g_lblNameObj[`u_${_name}`] !== undefined && (
|
|
2048
|
+
g_presetObj.lblRenames === undefined ||
|
|
2049
|
+
g_presetObj.lblRenames[g_currentPage] === undefined ||
|
|
2050
|
+
g_presetObj.lblRenames[g_currentPage]
|
|
2051
|
+
) ? g_lblNameObj[`u_${_name}`] : _name;
|
|
2052
|
+
};
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* 設定メイン・汎用
|
|
2056
|
+
* @param {number} _scrollNum
|
|
2057
|
+
* @param {string} _settingName
|
|
2058
|
+
* @param {Function} [func=()=>true] 設定ボタンを押した後の追加処理
|
|
2059
|
+
* @param {string} [unitName=''] 設定の単位名
|
|
2060
|
+
* @param {number} [roundNum=0] 設定スキップ間隔の丸め基準数
|
|
2061
|
+
*/
|
|
2062
|
+
const setSetting = (_scrollNum, _settingName, { func = () => true, unitName = ``, roundNum = 0, maxSiz = g_limitObj.setLblSiz } = {}) => {
|
|
2063
|
+
let settingNum = g_settings[`${_settingName}Num`];
|
|
2064
|
+
const settingList = g_settings[`${_settingName}s`];
|
|
2065
|
+
const settingMax = settingList.length - 1;
|
|
2066
|
+
|
|
2067
|
+
// _roundNum単位で丸める
|
|
2068
|
+
if (roundNum > 0 && _scrollNum >= roundNum) {
|
|
2069
|
+
settingNum = Math.floor(settingNum / roundNum) * roundNum;
|
|
2070
|
+
} else if (roundNum > 0 && -_scrollNum >= roundNum) {
|
|
2071
|
+
settingNum = Math.ceil(settingNum / roundNum) * roundNum;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
if (_scrollNum > 0) {
|
|
2075
|
+
settingNum = (settingNum === settingMax ? 0 : Math.min(settingNum + _scrollNum, settingMax));
|
|
2076
|
+
} else if (_scrollNum < 0) {
|
|
2077
|
+
settingNum = (settingNum === 0 ? settingMax : Math.max(settingNum + _scrollNum, 0));
|
|
2078
|
+
}
|
|
2079
|
+
g_stateObj[_settingName] = settingList[settingNum];
|
|
2080
|
+
g_settings[`${_settingName}Num`] = settingNum;
|
|
2081
|
+
|
|
2082
|
+
const settingName = `${getStgDetailName(g_stateObj[_settingName])}${unitName}${g_localStorage[_settingName] === g_stateObj[_settingName] ? ' *' : ''}`;
|
|
2083
|
+
const lnkName = `lnk${toCapitalize(_settingName)}`;
|
|
2084
|
+
document.getElementById(lnkName).textContent = settingName;
|
|
2085
|
+
$id(lnkName).fontSize = wUnit(getFontSize2(settingName, parseFloat($id(lnkName).width), { maxSiz }));
|
|
2086
|
+
|
|
2087
|
+
func();
|
|
2088
|
+
|
|
2089
|
+
if (document.getElementById(`settingSumSprite`) !== null) {
|
|
2090
|
+
updateSettingSummary();
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
/**
|
|
2095
|
+
* 無効化用ラベル作成
|
|
2096
|
+
* @param {string} _id
|
|
2097
|
+
* @param {number} _heightPos
|
|
2098
|
+
* @param {string} _defaultStr
|
|
2099
|
+
* @returns {HTMLDivElement}
|
|
2100
|
+
*/
|
|
2101
|
+
const makeDisabledLabel = (_id, _heightPos, _defaultStr) =>
|
|
2102
|
+
createDivCss2Label(_id, _defaultStr, {
|
|
2103
|
+
x: g_limitObj.setLblLeft, y: g_limitObj.setLblHeight * _heightPos,
|
|
2104
|
+
}, g_cssObj.settings_Disabled);
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* 保存済みリバース取得処理
|
|
2108
|
+
* @param {object} _localStorage 保存先のローカルストレージ名
|
|
2109
|
+
* @param {string} _extraKeyName 特殊キー名(通常キーは省略)
|
|
2110
|
+
*/
|
|
2111
|
+
const getKeyReverse = (_localStorage, _extraKeyName = ``) => {
|
|
2112
|
+
if (_localStorage[`reverse${_extraKeyName}`] !== undefined && g_headerObj.reverseUse) {
|
|
2113
|
+
g_stateObj.reverse = _localStorage[`reverse${_extraKeyName}`] ?? C_FLG_OFF;
|
|
2114
|
+
g_settings.reverseNum = roundZero(g_settings.reverses.findIndex(reverse => reverse === g_stateObj.reverse));
|
|
2115
|
+
} else {
|
|
2116
|
+
setReverseDefault();
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* リバースのデフォルト化処理
|
|
2122
|
+
*/
|
|
2123
|
+
const setReverseDefault = () => {
|
|
2124
|
+
g_stateObj.reverse = C_FLG_OFF;
|
|
2125
|
+
g_settings.reverseNum = 0;
|
|
2126
|
+
};
|
|
2127
|
+
|
|
2128
|
+
const setReverse = _btn => {
|
|
2129
|
+
if (!g_settings.scrolls.includes(C_FLG_REVERSE) && g_headerObj.reverseUse) {
|
|
2130
|
+
g_settings.reverseNum = (g_settings.reverseNum + 1) % 2;
|
|
2131
|
+
g_stateObj.reverse = g_settings.reverses[g_settings.reverseNum];
|
|
2132
|
+
setReverseView(_btn);
|
|
2133
|
+
updateSettingSummary();
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
|
|
2137
|
+
const setReverseView = _btn => {
|
|
2138
|
+
_btn.classList.replace(g_cssObj[`button_Rev${g_settings.reverses[(g_settings.reverseNum + 1) % 2]}`],
|
|
2139
|
+
g_cssObj[`button_Rev${g_settings.reverses[g_settings.reverseNum]}`]);
|
|
2140
|
+
if (!g_settings.scrolls.includes(C_FLG_REVERSE) && g_headerObj.reverseUse) {
|
|
2141
|
+
_btn.textContent = `${g_lblNameObj.Reverse}:${getStgDetailName(g_stateObj.reverse)}`;
|
|
2142
|
+
} else {
|
|
2143
|
+
_btn.textContent = `X`;
|
|
2144
|
+
setReverseDefault();
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
|
|
2148
|
+
/**
|
|
2149
|
+
* ゲージ設定メイン
|
|
2150
|
+
* @param {number} _scrollNum
|
|
2151
|
+
* @param {boolean} _gaugeInitFlg
|
|
2152
|
+
*/
|
|
2153
|
+
const setGauge = (_scrollNum, _gaugeInitFlg = false) => {
|
|
2154
|
+
|
|
2155
|
+
/**
|
|
2156
|
+
* 数式からゲージ値に変換
|
|
2157
|
+
* arrow[] -> 矢印数, frz[] -> フリーズアロー数, all[] -> 矢印+フリーズアロー数に置換する
|
|
2158
|
+
* @param {string} _val
|
|
2159
|
+
* @param {string} _defaultVal
|
|
2160
|
+
* @returns {number}
|
|
2161
|
+
*/
|
|
2162
|
+
const getGaugeCalc = (_val, _defaultVal) => {
|
|
2163
|
+
return setVal(convertStrToVal(
|
|
2164
|
+
replaceStr(_val, g_escapeStr.gaugeParamName)?.split(`{0}`).join(g_stateObj.scoreId)
|
|
2165
|
+
), _defaultVal, C_TYP_CALC);
|
|
2166
|
+
};
|
|
2167
|
+
/**
|
|
2168
|
+
* ゲージ詳細一括変更
|
|
2169
|
+
* @param {object} _baseObj
|
|
2170
|
+
* @param {number} object.magInit
|
|
2171
|
+
* @param {number} object.magRcv
|
|
2172
|
+
* @param {number} object.magDmg
|
|
2173
|
+
*/
|
|
2174
|
+
const setLifeCategory = (_baseObj, { _magInit = 1, _magRcv = 1, _magDmg = 1 } = {}) => {
|
|
2175
|
+
g_stateObj.lifeInit = getGaugeCalc(_baseObj.lifeInits[g_stateObj.scoreId], g_stateObj.lifeInit) * _magInit;
|
|
2176
|
+
g_stateObj.lifeRcv = getGaugeCalc(_baseObj.lifeRecoverys[g_stateObj.scoreId], g_stateObj.lifeRcv) * _magRcv;
|
|
2177
|
+
g_stateObj.lifeDmg = getGaugeCalc(_baseObj.lifeDamages[g_stateObj.scoreId], g_stateObj.lifeDmg) * _magDmg;
|
|
2178
|
+
};
|
|
2179
|
+
|
|
2180
|
+
/**
|
|
2181
|
+
* ライフモード切替
|
|
2182
|
+
* @param {object} _baseObj
|
|
2183
|
+
*/
|
|
2184
|
+
const changeLifeMode = (_baseObj) => {
|
|
2185
|
+
if (_baseObj.lifeBorders[g_stateObj.scoreId] === `x`) {
|
|
2186
|
+
g_stateObj.lifeBorder = 0;
|
|
2187
|
+
g_stateObj.lifeMode = C_LFE_SURVIVAL;
|
|
2188
|
+
} else {
|
|
2189
|
+
g_stateObj.lifeBorder = getGaugeCalc(_baseObj.lifeBorders[g_stateObj.scoreId], g_stateObj.lifeBorder);
|
|
2190
|
+
g_stateObj.lifeMode = C_LFE_BORDER;
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
|
|
2194
|
+
// ゲージ初期化
|
|
2195
|
+
|
|
2196
|
+
// カスタムゲージの設定取得
|
|
2197
|
+
const defaultCustomGauge = g_gaugeOptionObj.custom0 || g_gaugeOptionObj.customDefault;
|
|
2198
|
+
if (hasVal(defaultCustomGauge)) {
|
|
2199
|
+
g_gaugeOptionObj.custom = (g_gaugeOptionObj[`custom${g_stateObj.scoreId}`] || defaultCustomGauge).concat();
|
|
2200
|
+
g_gaugeOptionObj.varCustom = (g_gaugeOptionObj[`varCustom${g_stateObj.scoreId}`] || g_gaugeOptionObj.varCustom0 || g_gaugeOptionObj.varCustomDefault).concat();
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
// ゲージタイプの設定
|
|
2204
|
+
changeLifeMode(g_headerObj);
|
|
2205
|
+
g_gaugeType = (g_gaugeOptionObj.custom.length > 0 ? C_LFE_CUSTOM : g_stateObj.lifeMode);
|
|
2206
|
+
|
|
2207
|
+
// ゲージ配列を入れ替え
|
|
2208
|
+
g_settings.gauges = structuredClone(g_gaugeOptionObj[g_gaugeType.toLowerCase()]);
|
|
2209
|
+
g_settings.gaugeNum = getCurrentNo(g_settings.gauges, g_stateObj.gauge);
|
|
2210
|
+
g_stateObj.gauge = g_settings.gauges[g_settings.gaugeNum];
|
|
2211
|
+
|
|
2212
|
+
setSetting(_scrollNum, `gauge`);
|
|
2213
|
+
g_stateObj.lifeVariable = g_gaugeOptionObj[`var${g_gaugeType}`][g_settings.gaugeNum];
|
|
2214
|
+
|
|
2215
|
+
// デフォルトゲージの設定を適用(g_gaugeOptionObjから取得)
|
|
2216
|
+
if (g_settings.gaugeNum !== 0 &&
|
|
2217
|
+
(g_gaugeOptionObj.custom.length === 0 ||
|
|
2218
|
+
g_gaugeOptionObj.defaultList.includes(g_gaugeOptionObj[`defaultGauge${g_stateObj.scoreId}`]))) {
|
|
2219
|
+
|
|
2220
|
+
const gType = (g_gaugeType === C_LFE_CUSTOM ?
|
|
2221
|
+
toCapitalize(g_gaugeOptionObj[`defaultGauge${g_stateObj.scoreId}`]) : g_gaugeType);
|
|
2222
|
+
const getGaugeVal = _type => g_gaugeOptionObj[`${_type}${gType}`][g_settings.gaugeNum];
|
|
2223
|
+
g_stateObj.lifeMode = getGaugeVal(`type`);
|
|
2224
|
+
g_stateObj.lifeBorder = getGaugeVal(`clear`);
|
|
2225
|
+
g_stateObj.lifeInit = getGaugeVal(`init`);
|
|
2226
|
+
g_stateObj.lifeRcv = getGaugeVal(`rcv`);
|
|
2227
|
+
g_stateObj.lifeDmg = getGaugeVal(`dmg`);
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
// デフォルトゲージの初期設定(Light, Easyでは回復量を2倍にする)
|
|
2231
|
+
if ([`Original`, `Light`, `Normal`, `Easy`].includes(g_stateObj.gauge)) {
|
|
2232
|
+
setLifeCategory(g_headerObj, { _magRcv: [`Light`, `Easy`].includes(g_stateObj.gauge) ? 2 : 1 });
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
// ゲージ設定別に個別設定した場合はここで設定を上書き
|
|
2236
|
+
// 譜面ヘッダー:gaugeXXX で設定した値がここで適用される
|
|
2237
|
+
if (hasVal(g_gaugeOptionObj[`gauge${g_stateObj.gauge}s`])) {
|
|
2238
|
+
const tmpGaugeObj = g_gaugeOptionObj[`gauge${g_stateObj.gauge}s`];
|
|
2239
|
+
if (hasVal(tmpGaugeObj.lifeBorders[g_stateObj.scoreId])) {
|
|
2240
|
+
changeLifeMode(tmpGaugeObj);
|
|
2241
|
+
}
|
|
2242
|
+
setLifeCategory(tmpGaugeObj);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
// ゲージ詳細情報を表示
|
|
2246
|
+
lblGauge2.innerHTML = gaugeFormat(g_stateObj.lifeMode,
|
|
2247
|
+
g_stateObj.lifeBorder, g_stateObj.lifeRcv, g_stateObj.lifeDmg, g_stateObj.lifeInit, g_stateObj.lifeVariable);
|
|
2248
|
+
};
|
|
2249
|
+
|
|
2250
|
+
/**
|
|
2251
|
+
* ゲージ設定の詳細表示を整形
|
|
2252
|
+
* @param {string} _mode
|
|
2253
|
+
* @param {number} _border
|
|
2254
|
+
* @param {number} _rcv
|
|
2255
|
+
* @param {number} _dmg
|
|
2256
|
+
* @param {number} _init
|
|
2257
|
+
* @param {string} _lifeValFlg
|
|
2258
|
+
* @returns {string}
|
|
2259
|
+
*/
|
|
2260
|
+
const gaugeFormat = (_mode, _border, _rcv, _dmg, _init, _lifeValFlg) => {
|
|
2261
|
+
const initVal = g_headerObj.maxLifeVal * _init / 100;
|
|
2262
|
+
const borderVal = g_headerObj.maxLifeVal * _border / 100;
|
|
2263
|
+
|
|
2264
|
+
// 整形用にライフ初期値を整数、回復・ダメージ量を小数第2位に丸める
|
|
2265
|
+
const init = Math.round(initVal);
|
|
2266
|
+
const borderText = (_mode === C_LFE_BORDER && _border !== 0 ? Math.round(borderVal) : `-`);
|
|
2267
|
+
const round2 = _val => Math.round(_val * 100) / 100;
|
|
2268
|
+
|
|
2269
|
+
let rcvText = round2(_rcv), dmgText = round2(_dmg);
|
|
2270
|
+
let realRcv = _rcv, realDmg = _dmg;
|
|
2271
|
+
const allCnt = sumData(g_detailObj.arrowCnt[g_stateObj.scoreId]) +
|
|
2272
|
+
(g_headerObj.frzStartjdgUse ? 2 : 1) * sumData(g_detailObj.frzCnt[g_stateObj.scoreId]);
|
|
2273
|
+
|
|
2274
|
+
// ゲージ設定が矢印数依存の場合、実際の値に変換して表示する
|
|
2275
|
+
// 表示上、計算した値は小数第二位までの表示とする(それ以外はそのまま)
|
|
2276
|
+
if (_lifeValFlg === C_FLG_ON) {
|
|
2277
|
+
rcvText = ``, dmgText = ``;
|
|
2278
|
+
if (allCnt > 0) {
|
|
2279
|
+
realRcv = Math.min(calcLifeVal(_rcv, allCnt), g_headerObj.maxLifeVal);
|
|
2280
|
+
realDmg = Math.min(calcLifeVal(_dmg, allCnt), g_headerObj.maxLifeVal);
|
|
2281
|
+
rcvText = `${realRcv.toFixed(2)}<br>`;
|
|
2282
|
+
dmgText = `${realDmg.toFixed(2)}<br>`;
|
|
2283
|
+
}
|
|
2284
|
+
rcvText += `<span class="settings_lifeVal">(${round2(_rcv)})</span>`;
|
|
2285
|
+
dmgText += `<span class="settings_lifeVal">(${round2(_dmg)})</span>`;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
// 達成率(Accuracy)・許容ミス数の計算
|
|
2289
|
+
const [rateText, allowableCntsText] = getAccuracy(borderVal, realRcv, realDmg, initVal, allCnt);
|
|
2290
|
+
g_workObj.requiredAccuracy = rateText;
|
|
2291
|
+
|
|
2292
|
+
// このテーブルのみpointer-eventsを有効にする(オンマウス許可)
|
|
2293
|
+
return `<div id="gaugeDivCover" class="settings_gaugeDivCover" style="pointer-events: auto;">
|
|
2294
|
+
<div id="lblGaugeDivTable" class="settings_gaugeDivTable">
|
|
2295
|
+
<div id="lblGaugeStart" class="settings_gaugeDivTableCol settings_gaugeStart">
|
|
2296
|
+
${g_lblNameObj.g_start}
|
|
2297
|
+
</div>
|
|
2298
|
+
<div id="lblGaugeBorder" class="settings_gaugeDivTableCol settings_gaugeEtc">
|
|
2299
|
+
${g_lblNameObj.g_border}
|
|
2300
|
+
</div>
|
|
2301
|
+
<div id="lblGaugeRecovery" class="settings_gaugeDivTableCol settings_gaugeEtc">
|
|
2302
|
+
${g_lblNameObj.g_recovery}
|
|
2303
|
+
</div>
|
|
2304
|
+
<div id="lblGaugeDamage" class="settings_gaugeDivTableCol settings_gaugeEtc">
|
|
2305
|
+
${g_lblNameObj.g_damage}
|
|
2306
|
+
</div>
|
|
2307
|
+
<div id="lblGaugeRate" class="settings_gaugeDivTableCol settings_gaugeEtc">
|
|
2308
|
+
${g_lblNameObj.g_rate}
|
|
2309
|
+
</div>
|
|
2310
|
+
</div>
|
|
2311
|
+
<div id="dataGaugeDivTable" class="settings_gaugeDivTable">
|
|
2312
|
+
<div id="dataGaugeStart" class="settings_gaugeDivTableCol settings_gaugeVal settings_gaugeStart">
|
|
2313
|
+
${init}/${g_headerObj.maxLifeVal}
|
|
2314
|
+
</div>
|
|
2315
|
+
<div id="dataGaugeBorder" class="settings_gaugeDivTableCol settings_gaugeVal settings_gaugeEtc">
|
|
2316
|
+
${borderText}
|
|
2317
|
+
</div>
|
|
2318
|
+
<div id="dataGaugeRecovery" class="settings_gaugeDivTableCol settings_gaugeVal settings_gaugeEtc">
|
|
2319
|
+
${rcvText}
|
|
2320
|
+
</div>
|
|
2321
|
+
<div id="dataGaugeDamage" class="settings_gaugeDivTableCol settings_gaugeVal settings_gaugeEtc">
|
|
2322
|
+
${dmgText}
|
|
2323
|
+
</div>
|
|
2324
|
+
<div id="dataGaugeRate" class="settings_gaugeDivTableCol settings_gaugeVal settings_gaugeEtc" style="line-height: 12px;">
|
|
2325
|
+
${rateText}<br><span style="font-size: 10px;">${allowableCntsText}</span>
|
|
2326
|
+
</div>
|
|
2327
|
+
</div>
|
|
2328
|
+
</div>
|
|
2329
|
+
`;
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2332
|
+
/**
|
|
2333
|
+
* 達成率、許容ミス数の取得
|
|
2334
|
+
* @param {number} _border
|
|
2335
|
+
* @param {number} _rcv
|
|
2336
|
+
* @param {number} _dmg
|
|
2337
|
+
* @param {number} _init
|
|
2338
|
+
* @param {number} _allCnt
|
|
2339
|
+
* @returns {string[]}
|
|
2340
|
+
*/
|
|
2341
|
+
const getAccuracy = (_border, _rcv, _dmg, _init, _allCnt) => {
|
|
2342
|
+
const justPoint = _rcv + _dmg > 0 ? Math.max(_border - _init + _dmg * _allCnt, 0) / (_rcv + _dmg) : 0;
|
|
2343
|
+
const minRecovery = (_border === 0 ? Math.floor(justPoint + 1) : Math.ceil(justPoint));
|
|
2344
|
+
const rate = Math.max(minRecovery / _allCnt * 100, 0);
|
|
2345
|
+
let rateText = _allCnt > 0 ? (rate <= 100 ? `${rate.toFixed(2)}%` : `<span class="settings_lifeVal">${rate.toFixed(2)}%</span>`) : `----`;
|
|
2346
|
+
|
|
2347
|
+
// 許容ミス数の計算
|
|
2348
|
+
const allowableCnts = Math.min(_allCnt - minRecovery, _allCnt);
|
|
2349
|
+
let allowableCntsText = _allCnt > 0 && allowableCnts !== 0 ? (allowableCnts > 0 ? `${allowableCnts}miss↓` : `(${allowableCnts}miss)`) : ``;
|
|
2350
|
+
|
|
2351
|
+
if ((_rcv === 0 && _dmg === 0) || _rcv < 0 || _dmg < 0) {
|
|
2352
|
+
rateText = `----`;
|
|
2353
|
+
allowableCntsText = ``;
|
|
2354
|
+
}
|
|
2355
|
+
return [rateText, allowableCntsText];
|
|
2356
|
+
};
|
|
2357
|
+
|
|
2358
|
+
/**
|
|
2359
|
+
* 空押し判定の設定
|
|
2360
|
+
* @param {HTMLDivElement} _btn
|
|
2361
|
+
*/
|
|
2362
|
+
const setExcessive = (_btn, _val) => {
|
|
2363
|
+
const curExcessive = Number(g_settings.excessiveNum);
|
|
2364
|
+
g_settings.excessiveNum = _val !== undefined ? Number(_val) : (curExcessive + 1) % 2;
|
|
2365
|
+
g_stateObj.excessiveChgFlg = _val === undefined;
|
|
2366
|
+
if (g_stateObj.excessiveChgFlg) {
|
|
2367
|
+
g_stateObj.excessiveScoreId = g_stateObj.scoreId;
|
|
2368
|
+
}
|
|
2369
|
+
g_stateObj.excessive = g_settings.excessives[g_settings.excessiveNum];
|
|
2370
|
+
updateSettingSummary();
|
|
2371
|
+
_btn.classList.replace(g_cssObj[`button_Rev${g_settings.excessives[curExcessive]}`],
|
|
2372
|
+
g_cssObj[`button_Rev${g_settings.excessives[g_settings.excessiveNum]}`]);
|
|
2373
|
+
};
|
|
2374
|
+
|
|
2375
|
+
/**
|
|
2376
|
+
* 保存済みキーコンフィグ取得処理
|
|
2377
|
+
* @param {object} _localStorage 保存先のローカルストレージ名
|
|
2378
|
+
* @param {string} _extraKeyName 特殊キー名(通常キーは省略)
|
|
2379
|
+
*/
|
|
2380
|
+
const getKeyCtrl = (_localStorage, _extraKeyName = ``) => {
|
|
2381
|
+
g_keyObj.storagePtn = _localStorage[`keyCtrlPtn${_extraKeyName}`];
|
|
2382
|
+
const basePtn = `${g_keyObj.currentKey}_${g_keyObj.storagePtn}`;
|
|
2383
|
+
if (g_keyObj[`keyCtrl${basePtn}`] === undefined || hasVal(g_keyObj[`transKey${basePtn}`])) {
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
const baseKeyNum = g_keyObj[`${g_keyObj.defaultProp}${basePtn}`].length;
|
|
2387
|
+
|
|
2388
|
+
if (_localStorage[`keyCtrl${_extraKeyName}`]?.[0].length > 0) {
|
|
2389
|
+
const prevPtn = g_keyObj.currentPtn;
|
|
2390
|
+
g_keyObj.currentPtn = -1;
|
|
2391
|
+
const copyPtn = `${g_keyObj.currentKey}_-1`;
|
|
2392
|
+
g_keyObj[`keyCtrl${copyPtn}`] = [...Array(baseKeyNum)].map(() => []);
|
|
2393
|
+
g_keyObj[`keyCtrl${copyPtn}d`] = [...Array(baseKeyNum)].map(() => []);
|
|
2394
|
+
|
|
2395
|
+
for (let j = 0; j < baseKeyNum; j++) {
|
|
2396
|
+
for (let k = 0; k < g_keyObj[`keyCtrl${basePtn}`][j].length; k++) {
|
|
2397
|
+
g_keyObj[`keyCtrl${copyPtn}d`][j][k] = g_keyObj[`keyCtrl${copyPtn}`][j][k] = _localStorage[`keyCtrl${_extraKeyName}`][j][k];
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
const isUpdate = prevPtn !== -1 && g_keyObj.prevKey !== g_keyObj.currentKey;
|
|
2402
|
+
g_keyCopyLists.multiple.filter(header => g_keyObj[`${header}${basePtn}`] !== undefined && isUpdate)
|
|
2403
|
+
.forEach(header => g_keyObj[`${header}${copyPtn}`] = structuredClone(g_keyObj[`${header}${basePtn}`]));
|
|
2404
|
+
g_keyCopyLists.simple.filter(header => g_keyObj[`${header}${basePtn}`] !== undefined && isUpdate)
|
|
2405
|
+
.forEach(header => g_keyObj[`${header}${copyPtn}`] = g_keyObj[`${header}${basePtn}`]);
|
|
2406
|
+
|
|
2407
|
+
g_keycons.groups.forEach(type => {
|
|
2408
|
+
let maxPtn = 0;
|
|
2409
|
+
while (g_keyObj[`${type}${basePtn}_${maxPtn}`] !== undefined) {
|
|
2410
|
+
maxPtn++;
|
|
2411
|
+
}
|
|
2412
|
+
for (let j = 0; j < maxPtn; j++) {
|
|
2413
|
+
g_keyObj[`${type}${copyPtn}_${j}`] = structuredClone(g_keyObj[`${type}${basePtn}_${j}`]);
|
|
2414
|
+
}
|
|
2415
|
+
g_keyObj[`${type}${copyPtn}_0d`] = structuredClone(g_keyObj[`${type}${copyPtn}_0`]);
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
};
|
|
2419
|
+
|
|
2420
|
+
/**
|
|
2421
|
+
* 設定・オプション表示用ボタン
|
|
2422
|
+
* @param {string} _id
|
|
2423
|
+
* @param {string} _name 初期設定文字
|
|
2424
|
+
* @param {number} _heightPos 上からの配置順
|
|
2425
|
+
* @param {Function} _func 通常ボタン処理
|
|
2426
|
+
* @param {number} [object.x]
|
|
2427
|
+
* @param {number} [object.y]
|
|
2428
|
+
* @param {number} [object.w]
|
|
2429
|
+
* @param {number} [object.h]
|
|
2430
|
+
* @param {number} [object.siz]
|
|
2431
|
+
* @param {Function} [object.cxtFunc] 右クリック時の処理
|
|
2432
|
+
* @param {...any} [object.rest]
|
|
2433
|
+
* @param {...any} _classes 追加するクラス
|
|
2434
|
+
* @returns {HTMLDivElement}
|
|
2435
|
+
*/
|
|
2436
|
+
const makeSettingLblCssButton = (_id, _name, _heightPos, _func, {
|
|
2437
|
+
x = g_limitObj.setLblLeft, y = g_limitObj.setLblHeight * _heightPos,
|
|
2438
|
+
w = g_limitObj.setLblWidth, h = g_limitObj.setLblHeight, siz = g_limitObj.setLblSiz,
|
|
2439
|
+
cxtFunc = () => true, ...rest } = {}, ..._classes) =>
|
|
2440
|
+
createCss2Button(_id, _name, _func, { x, y, w, h, siz: getFontSize2(_name, w, { maxSiz: siz }), cxtFunc, ...rest }, g_cssObj.button_Default, ..._classes);
|
|
2441
|
+
|
|
2442
|
+
/**
|
|
2443
|
+
* 譜面変更セレクター用ボタン
|
|
2444
|
+
* @param {string} _id
|
|
2445
|
+
* @param {string} _name 初期設定文字
|
|
2446
|
+
* @param {number} _heightPos 上からの配置順
|
|
2447
|
+
* @param {Function} _func
|
|
2448
|
+
* @param {number} [object.x]
|
|
2449
|
+
* @param {number} [object.h]
|
|
2450
|
+
* @param {number} [object.y=h*_heightPos]
|
|
2451
|
+
* @param {number} [object.w]
|
|
2452
|
+
* @param {number} [object.siz]
|
|
2453
|
+
* @param {string} [object.btnStyle='Default']
|
|
2454
|
+
* @returns {HTMLDivElement}
|
|
2455
|
+
*/
|
|
2456
|
+
const makeDifLblCssButton = (_id, _name, _heightPos, _func, {
|
|
2457
|
+
x = 0, h = g_limitObj.setLblHeight, y = h * _heightPos,
|
|
2458
|
+
w = g_limitObj.difSelectorWidth, siz = g_limitObj.difSelectorSiz, btnStyle = `Default` } = {}) =>
|
|
2459
|
+
createCss2Button(_id, _name, _func, {
|
|
2460
|
+
x, y, w, h, siz, borderStyle: `solid`, title: g_msgObj[_id] ?? ``,
|
|
2461
|
+
}, g_cssObj[`button_${btnStyle}`], g_cssObj.button_ON);
|
|
2462
|
+
|
|
2463
|
+
/**
|
|
2464
|
+
* 設定・オプション用の設定変更ミニボタン
|
|
2465
|
+
* @param {string} _id
|
|
2466
|
+
* @param {string} _directionFlg 表示用ボタンのどちら側に置くかを設定。(R, RR:右、L, LL:左)
|
|
2467
|
+
* @param {number} _heightPos 上からの配置順
|
|
2468
|
+
* @param {Function} _func
|
|
2469
|
+
* @param {number} [object.dx=0]
|
|
2470
|
+
* @param {number} [object.dy=0]
|
|
2471
|
+
* @param {number} [object.dw=0]
|
|
2472
|
+
* @param {number} [object.dh=0]
|
|
2473
|
+
* @param {number} [object.dsiz=0]
|
|
2474
|
+
* @param {string} [object.visibility='visible']
|
|
2475
|
+
* @returns {HTMLDivElement}
|
|
2476
|
+
*/
|
|
2477
|
+
const makeMiniCssButton = (_id, _directionFlg, _heightPos, _func, { dx = 0, dy = 0, dw = 0, dh = 0, dsiz = 0, visibility = `visible` } = {}) =>
|
|
2478
|
+
createCss2Button(`${_id}${_directionFlg}`, g_settingBtnObj.chara[_directionFlg], _func, {
|
|
2479
|
+
x: g_settingBtnObj.pos[_directionFlg] + dx, y: g_limitObj.setLblHeight * _heightPos + dy,
|
|
2480
|
+
w: g_limitObj.setMiniWidth + dw, h: g_limitObj.setLblHeight + dh, siz: g_limitObj.setLblSiz + dsiz, visibility,
|
|
2481
|
+
}, g_cssObj.button_Mini);
|
|
2482
|
+
|
|
2483
|
+
/*-----------------------------------------------------------*/
|
|
2484
|
+
/* Scene : SETTINGS-DISPLAY [lemon] */
|
|
2485
|
+
/*-----------------------------------------------------------*/
|
|
2486
|
+
|
|
2487
|
+
const settingsDisplayInit = () => {
|
|
2488
|
+
|
|
2489
|
+
clearWindow();
|
|
2490
|
+
const divRoot = document.getElementById(`divRoot`);
|
|
2491
|
+
g_currentPage = `settingsDisplay`;
|
|
2492
|
+
|
|
2493
|
+
// 譜面初期情報ロード許可フラグ
|
|
2494
|
+
g_canLoadDifInfoFlg = false;
|
|
2495
|
+
|
|
2496
|
+
// タイトル文字描画
|
|
2497
|
+
divRoot.appendChild(getTitleDivLabel(`lblTitle`, g_lblNameObj.display, 0, 15, `settings_Display`));
|
|
2498
|
+
|
|
2499
|
+
// オプションボタン用の設置
|
|
2500
|
+
createSettingsDisplayWindow(divRoot);
|
|
2501
|
+
|
|
2502
|
+
// ショートカットキーメッセージ
|
|
2503
|
+
multiAppend(divRoot,
|
|
2504
|
+
createDescDiv(`scMsg`, g_lblNameObj.sdShortcutDesc),
|
|
2505
|
+
createCss2Button(`btnDisplayPreview`, `↓ Preview`, _evt => {
|
|
2506
|
+
toggleDisplayPreview();
|
|
2507
|
+
}, g_lblPosObj.btnDisplayPreview, g_cssObj.button_Setting),
|
|
2508
|
+
);
|
|
2509
|
+
createScText(btnDisplayPreview, `DisplayPreview`, { displayName: `settingsDisplay`, targetLabel: `btnDisplayPreview`, x: -25 });
|
|
2510
|
+
|
|
2511
|
+
// ユーザカスタムイベント(初期)
|
|
2512
|
+
safeExecuteCustomHooks(`g_customJsObj.settingsDisplay`, g_customJsObj.settingsDisplay);
|
|
2513
|
+
|
|
2514
|
+
// ボタン描画
|
|
2515
|
+
commonSettingBtn(`Settings`);
|
|
2516
|
+
|
|
2517
|
+
// キー操作イベント(デフォルト)
|
|
2518
|
+
setShortcutEvent(g_currentPage);
|
|
2519
|
+
document.oncontextmenu = () => true;
|
|
2520
|
+
|
|
2521
|
+
safeExecuteCustomHooks(`g_skinJsObj.settingsDisplay`, g_skinJsObj.settingsDisplay);
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2524
|
+
/** プレビューウィンドウのルートdiv */
|
|
2525
|
+
let g_previewRoot = null;
|
|
2526
|
+
|
|
2527
|
+
/** プレビュー内の各UIオブジェクトの現在座標 */
|
|
2528
|
+
const g_previewPos = {
|
|
2529
|
+
arrowJdg: { x: null, y: null }, // 通常判定キャラクタ・コンボ
|
|
2530
|
+
frzJdg: { x: null, y: null }, // フリーズ判定キャラクタ・コンボ
|
|
2531
|
+
shortcut: { x: null, y: null },
|
|
2532
|
+
};
|
|
2533
|
+
|
|
2534
|
+
/**
|
|
2535
|
+
* プレビューのトグル(表示 / 非表示)
|
|
2536
|
+
*/
|
|
2537
|
+
const toggleDisplayPreview = () => {
|
|
2538
|
+
if (g_previewRoot && document.getElementById(`displayPreviewOverlay`)) {
|
|
2539
|
+
closeDisplayPreview();
|
|
2540
|
+
g_currentPage = `settingsDisplay`;
|
|
2541
|
+
} else {
|
|
2542
|
+
openDisplayPreview();
|
|
2543
|
+
g_currentPage = `displayPreview`;
|
|
2544
|
+
}
|
|
2545
|
+
setShortcutEvent(g_currentPage, () => true, { displayFlg: false });
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
/**
|
|
2549
|
+
* プレビューオーバーレイを開く
|
|
2550
|
+
*/
|
|
2551
|
+
const openDisplayPreview = () => {
|
|
2552
|
+
const divRoot = document.getElementById(`divRoot`);
|
|
2553
|
+
if (!divRoot) return;
|
|
2554
|
+
|
|
2555
|
+
// 既存があれば削除
|
|
2556
|
+
closeDisplayPreview();
|
|
2557
|
+
|
|
2558
|
+
// ============================================================
|
|
2559
|
+
// オーバーレイ本体
|
|
2560
|
+
// ============================================================
|
|
2561
|
+
const overlay = createEmptySprite(divRoot, `displayPreviewOverlay`, {
|
|
2562
|
+
w: g_sWidth, h: g_sHeight, background: g_headerObj.baseBrightFlg ? `#eeeeeeee` : `#111111dd`, pointerEvents: C_DIS_AUTO,
|
|
2563
|
+
});
|
|
2564
|
+
g_previewRoot = overlay;
|
|
2565
|
+
multiAppend(overlay,
|
|
2566
|
+
createDivCss2Label(`lblDisplayPreview`, `Display Preview`,
|
|
2567
|
+
g_lblPosObj.lblDisplayPreview, g_cssObj.settings_Display),
|
|
2568
|
+
createCss2Button(`btnDisplayPreview2`, `↑ Preview`, _evt => {
|
|
2569
|
+
toggleDisplayPreview();
|
|
2570
|
+
}, g_lblPosObj.btnDisplayPreview, g_cssObj.button_Setting),
|
|
2571
|
+
createCss2Button(`btnDisplayReset`, `Reset`, _evt => {
|
|
2572
|
+
if (window.confirm(g_msgObj.displayPreviewResetConfirm)) {
|
|
2573
|
+
Object.assign(g_diffObj, g_diffInitObj);
|
|
2574
|
+
Object.keys(g_diffInitObj).forEach(key => {
|
|
2575
|
+
g_localStorage[key] = g_diffInitObj[key];
|
|
2576
|
+
});
|
|
2577
|
+
Object.keys(g_previewPos).forEach(key => g_previewPos[key] = { x: null, y: null });
|
|
2578
|
+
closeDisplayPreview();
|
|
2579
|
+
openDisplayPreview();
|
|
2580
|
+
}
|
|
2581
|
+
}, g_lblPosObj.btnDisplayReset, g_cssObj.button_Reset),
|
|
2582
|
+
createDescDiv(`lblDisplayPreviewMsg`, g_lblNameObj.displayPreviewDesc),
|
|
2583
|
+
);
|
|
2584
|
+
if (g_headerObj.scAreaWidth > 0) {
|
|
2585
|
+
overlay.appendChild(createDescDiv(`lblDisplayPreviewMsg2`, g_lblNameObj.displayPreviewDesc2));
|
|
2586
|
+
}
|
|
2587
|
+
createScText(btnDisplayReset, `DisplayPreviewReset`, { displayName: `displayPreview`, targetLabel: `btnDisplayReset`, x: -15 });
|
|
2588
|
+
|
|
2589
|
+
// ============================================================
|
|
2590
|
+
// プレイ画面フレーム(白枠)
|
|
2591
|
+
// ============================================================
|
|
2592
|
+
const rate = 0.8;
|
|
2593
|
+
const playW = g_headerObj.playingWidth || g_sWidth;
|
|
2594
|
+
const playH = g_headerObj.playingHeight || g_sHeight;
|
|
2595
|
+
const frameX = Math.round((g_sWidth - playW) / 2);
|
|
2596
|
+
const frameY = Math.round((g_sHeight - playH) / 2);
|
|
2597
|
+
|
|
2598
|
+
const frame = createEmptySprite(overlay, `previewFrame`, {
|
|
2599
|
+
x: frameX, y: frameY, w: playW, h: playH,
|
|
2600
|
+
background: g_headerObj.baseBrightFlg ? `#eeeeee` : `#111111`,
|
|
2601
|
+
border: `1px solid #444444`,
|
|
2602
|
+
boxSizing: `border-box`,
|
|
2603
|
+
transform: `scale(${rate})`,
|
|
2604
|
+
});
|
|
2605
|
+
|
|
2606
|
+
// ============================================================
|
|
2607
|
+
// Display設定に応じたUIを描画
|
|
2608
|
+
// ============================================================
|
|
2609
|
+
buildPreviewUI(frame, playW, playH);
|
|
2610
|
+
};
|
|
2611
|
+
|
|
2612
|
+
/**
|
|
2613
|
+
* プレビューオーバーレイを閉じる
|
|
2614
|
+
*/
|
|
2615
|
+
const closeDisplayPreview = () => {
|
|
2616
|
+
const overlay = document.getElementById(`displayPreviewOverlay`);
|
|
2617
|
+
if (overlay) {
|
|
2618
|
+
deleteChildspriteAll(`displayPreviewOverlay`);
|
|
2619
|
+
overlay.remove();
|
|
2620
|
+
}
|
|
2621
|
+
g_previewRoot = null;
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
/**
|
|
2625
|
+
* プレビュー内のUI要素を構築する
|
|
2626
|
+
* @param {HTMLElement} _frame プレイ画面フレーム要素
|
|
2627
|
+
* @param {number} _playW プレイ幅(px)
|
|
2628
|
+
* @param {number} _playH プレイ高さ(px)
|
|
2629
|
+
*/
|
|
2630
|
+
const buildPreviewUI = (_frame, _playW, _playH) => {
|
|
2631
|
+
|
|
2632
|
+
// --- Display設定の現在値を取得 ---
|
|
2633
|
+
const d = {
|
|
2634
|
+
stepzone: g_stateObj.d_stepzone,
|
|
2635
|
+
judgment: g_stateObj.d_judgment,
|
|
2636
|
+
lifegauge: g_stateObj.d_lifegauge,
|
|
2637
|
+
score: g_stateObj.d_score,
|
|
2638
|
+
musicinfo: g_stateObj.d_musicinfo,
|
|
2639
|
+
};
|
|
2640
|
+
|
|
2641
|
+
const disableBox = (_name, { x, y, w, h } = {}) =>
|
|
2642
|
+
createDivCss2Label(`previewDisableBox_${_name}`, `${g_emojiObj.crossMark} ${_name}`, {
|
|
2643
|
+
x, y, w, h, background: `rgba(0,0,0,0.5)`, border: `1px dashed #555555`,
|
|
2644
|
+
size: 32, color: `#cccccc`, align: C_ALIGN_LEFT,
|
|
2645
|
+
});
|
|
2646
|
+
|
|
2647
|
+
// ============================================================
|
|
2648
|
+
// ステップゾーン(中央横帯)
|
|
2649
|
+
// ============================================================
|
|
2650
|
+
const stepY = g_posObj.stepY ?? C_STEP_Y;
|
|
2651
|
+
const revStepY = g_posObj.reverseStepY;
|
|
2652
|
+
const hitPos = g_stateObj.hitPosition ?? 0;
|
|
2653
|
+
|
|
2654
|
+
// 簡易ステップゾーン(7レーン分)
|
|
2655
|
+
const laneCount = 7;
|
|
2656
|
+
const laneW = 50;
|
|
2657
|
+
const totalW = laneCount * laneW;
|
|
2658
|
+
const startX = Math.round((_playW - totalW) / 2);
|
|
2659
|
+
|
|
2660
|
+
if (d.stepzone === C_FLG_OFF) {
|
|
2661
|
+
multiAppend(_frame,
|
|
2662
|
+
disableBox(`StepZone`, { x: Math.round(_playW / 2 - 200), y: stepY, w: 400, h: 50 }),
|
|
2663
|
+
disableBox(`StepZone_Rev`, { x: Math.round(_playW / 2 - 200), y: C_STEP_Y + revStepY, w: 400, h: 50 }),
|
|
2664
|
+
);
|
|
2665
|
+
} else {
|
|
2666
|
+
for (let j = 0; j < laneCount; j++) {
|
|
2667
|
+
createEmptySprite(_frame, `previewStep${j}`, {
|
|
2668
|
+
x: startX + j * laneW + 2, y: stepY + 2, w: laneW - 4, h: laneW - 4,
|
|
2669
|
+
background: `rgba(100,100,200,0.25)`,
|
|
2670
|
+
border: `1px solid rgba(150,150,255,0.5)`,
|
|
2671
|
+
});
|
|
2672
|
+
createEmptySprite(_frame, `previewStepR${j}`, {
|
|
2673
|
+
x: startX + j * laneW + 2, y: C_STEP_Y + revStepY + 2, w: laneW - 4, h: laneW - 4,
|
|
2674
|
+
background: `rgba(200,100,100,0.20)`,
|
|
2675
|
+
border: `1px solid rgba(255,150,150,0.4)`,
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// ============================================================
|
|
2681
|
+
// HitPosition を視覚化する判定基準ライン
|
|
2682
|
+
// ============================================================
|
|
2683
|
+
// 通常譜面用の判定ライン(赤または目立つ色で、レーン幅全体をカバー)
|
|
2684
|
+
// 上から下に流れる場合、hitPosがプラスなら「ステップゾーンより下」にラインが来る
|
|
2685
|
+
const lineNormal = createEmptySprite(_frame, `previewHitPosLine`, {
|
|
2686
|
+
x: startX,
|
|
2687
|
+
y: stepY + Math.round(laneW / 2) + hitPos, // ステップゾーンの中心 + hitPos
|
|
2688
|
+
w: totalW,
|
|
2689
|
+
h: 2, // 2pxの横線
|
|
2690
|
+
background: `#33aaff`,
|
|
2691
|
+
boxShadow: `0 0 4px #33aaff`, // ネオンっぽく光らせて目立たせる
|
|
2692
|
+
});
|
|
2693
|
+
|
|
2694
|
+
// 青いラインの右端(totalW から10pxほど外側)に数値を表示
|
|
2695
|
+
multiAppend(
|
|
2696
|
+
lineNormal,
|
|
2697
|
+
createDivCss2Label(`previewHitPosTitle`, `Hit`, {
|
|
2698
|
+
...g_lblPosObj.previewHitPosText, x: -60, y: -14, align: C_ALIGN_RIGHT,
|
|
2699
|
+
}),
|
|
2700
|
+
createDivCss2Label(`previewHitPosTitle2`, `Position`, {
|
|
2701
|
+
...g_lblPosObj.previewHitPosText, x: -60, y: -2, align: C_ALIGN_RIGHT,
|
|
2702
|
+
}),
|
|
2703
|
+
createDivCss2Label(`previewHitPosText`, `${hitPos > 0 ? '+' : ''}${hitPos}px↑↓`, {
|
|
2704
|
+
...g_lblPosObj.previewHitPosText, x: totalW + 5, y: -8, align: C_ALIGN_LEFT,
|
|
2705
|
+
}),
|
|
2706
|
+
);
|
|
2707
|
+
|
|
2708
|
+
// リバース譜面用の判定ライン
|
|
2709
|
+
// 下から上に流れる場合、hitPosがプラスなら「ステップゾーンより上(座標としてはマイナス)」に来る
|
|
2710
|
+
createEmptySprite(_frame, `previewHitPosLineRev`, {
|
|
2711
|
+
x: startX,
|
|
2712
|
+
y: (C_STEP_Y + revStepY) + Math.round(laneW / 2) - hitPos, // ステップゾーンの中心 - hitPos
|
|
2713
|
+
w: totalW,
|
|
2714
|
+
h: 2,
|
|
2715
|
+
background: `#ffaa00`,
|
|
2716
|
+
boxShadow: `0 0 4px #ffaa00`,
|
|
2717
|
+
});
|
|
2718
|
+
|
|
2719
|
+
// ============================================================
|
|
2720
|
+
// 判定エリア(ドラッグ可能)
|
|
2721
|
+
// ============================================================
|
|
2722
|
+
if (d.judgment === C_FLG_OFF) {
|
|
2723
|
+
_frame.appendChild(disableBox(`Judgment`, {
|
|
2724
|
+
x: Math.round(_playW / 2 - 220), y: Math.round((_playH + (g_posObj?.stepYR ?? 0)) / 2 - 60), w: 440, h: 120,
|
|
2725
|
+
}));
|
|
2726
|
+
} else {
|
|
2727
|
+
const opacity = g_stateObj.opacity / 100;
|
|
2728
|
+
const jdgCenterY = Math.round((_playH + (g_posObj?.stepYR ?? 0)) / 2);
|
|
2729
|
+
const groupW = 370;
|
|
2730
|
+
const groupH = 51;
|
|
2731
|
+
|
|
2732
|
+
const jdgSettings = [
|
|
2733
|
+
{
|
|
2734
|
+
key: `arrowJdg`,
|
|
2735
|
+
stdYOffset: -60,
|
|
2736
|
+
offsetX: g_diffObj.arrowJdgX ?? 0,
|
|
2737
|
+
offsetY: g_diffObj.arrowJdgY ?? 0,
|
|
2738
|
+
chara: d.judgment === C_FLG_ON ? g_lblNameObj.j_ii : ``,
|
|
2739
|
+
css: [g_cssObj.common_ii, g_cssObj.common_kita], // [chara, combo]
|
|
2740
|
+
toast: g_lblNameObj.arrowJdgUpdate,
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
key: `frzJdg`,
|
|
2744
|
+
stdYOffset: 10,
|
|
2745
|
+
offsetX: g_diffObj.frzJdgX ?? 0,
|
|
2746
|
+
offsetY: g_diffObj.frzJdgY ?? 0,
|
|
2747
|
+
chara: d.judgment === C_FLG_ON ? g_lblNameObj.j_kita : ``,
|
|
2748
|
+
css: [g_cssObj.common_kita, g_cssObj.common_ii],
|
|
2749
|
+
toast: g_lblNameObj.frzJdgUpdate,
|
|
2750
|
+
}
|
|
2751
|
+
];
|
|
2752
|
+
|
|
2753
|
+
jdgSettings.forEach(item => {
|
|
2754
|
+
|
|
2755
|
+
const stdX = Math.round(_playW / 2 - (item.key === `arrowJdg` ? 220 : 120));
|
|
2756
|
+
const initX = g_previewPos[item.key].x ?? (stdX + item.offsetX);
|
|
2757
|
+
const initY = g_previewPos[item.key].y ?? (jdgCenterY + item.offsetY + item.stdYOffset);
|
|
2758
|
+
|
|
2759
|
+
// グループコンテナの生成
|
|
2760
|
+
const group = createEmptySprite(_frame, `previewGrp_${item.key}`, {
|
|
2761
|
+
x: initX, y: initY, w: groupW, h: groupH, pointerEvents: C_DIS_AUTO,
|
|
2762
|
+
});
|
|
2763
|
+
|
|
2764
|
+
// ラベル類の配置
|
|
2765
|
+
multiAppend(group,
|
|
2766
|
+
createDivCss2Label(`previewChara_${item.key}`, item.chara, {
|
|
2767
|
+
x: 0, y: 0, w: g_limitObj.jdgCharaWidth, h: g_limitObj.jdgCharaHeight,
|
|
2768
|
+
siz: g_limitObj.jdgCharaSiz, opacity,
|
|
2769
|
+
}, item.css[0]),
|
|
2770
|
+
createDivCss2Label(`previewCombo_${item.key}`, d.judgment === C_FLG_ON ? `5 Combo!!` : ``, {
|
|
2771
|
+
x: 170, y: 0, w: g_limitObj.jdgCharaWidth, h: g_limitObj.jdgCharaHeight,
|
|
2772
|
+
siz: g_limitObj.jdgCharaSiz, opacity,
|
|
2773
|
+
}, item.css[1]),
|
|
2774
|
+
createDivCss2Label(`previewDiff_${item.key}`, `Fast 3 Frames`, {
|
|
2775
|
+
x: 170, y: 25, w: g_limitObj.jdgCharaWidth, h: g_limitObj.jdgCharaHeight,
|
|
2776
|
+
siz: g_limitObj.mainSiz, color: `#ff9966`, opacity,
|
|
2777
|
+
}, g_cssObj.common_fast),
|
|
2778
|
+
);
|
|
2779
|
+
|
|
2780
|
+
// ドラッグ機能を付与
|
|
2781
|
+
makeElementDraggable(group, item.key, _playW, _playH, { w: groupW, h: groupH }, {
|
|
2782
|
+
toastTitle: item.toast,
|
|
2783
|
+
getStdPos: () => ({
|
|
2784
|
+
x: stdX,
|
|
2785
|
+
y: Math.round(jdgCenterY + item.stdYOffset),
|
|
2786
|
+
}),
|
|
2787
|
+
});
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
// ============================================================
|
|
2792
|
+
// ライフゲージ(左縦帯)
|
|
2793
|
+
// ============================================================
|
|
2794
|
+
if (d.lifegauge === C_FLG_OFF) {
|
|
2795
|
+
_frame.appendChild(disableBox(`LifeGauge`, g_lblPosObj.previewLifeDisabled));
|
|
2796
|
+
} else {
|
|
2797
|
+
multiAppend(_frame,
|
|
2798
|
+
createDivCss2Label(`previewLifeBack`, ``, g_lblPosObj.previewLifeBack, g_cssObj.life_Background),
|
|
2799
|
+
createDivCss2Label(`previewLifeBar`, ``, g_lblPosObj.previewLifeBar, g_cssObj.life_Cleared),
|
|
2800
|
+
createDivCss2Label(`previewLifeNum`, `700`, g_lblPosObj.previewLifeNum, g_cssObj.life_Cleared),
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
// ============================================================
|
|
2805
|
+
// スコア・判定カウンタ(右端縦列)
|
|
2806
|
+
// ============================================================
|
|
2807
|
+
if (d.score === C_FLG_OFF) {
|
|
2808
|
+
_frame.appendChild(disableBox(`Score`, g_lblPosObj.previewScoreDisabled));
|
|
2809
|
+
|
|
2810
|
+
if (g_stateObj.frzReturn !== C_FLG_OFF) {
|
|
2811
|
+
_frame.appendChild(disableBox(`FrzReturnBar`, g_lblPosObj.previewLifeFrzDisabled));
|
|
2812
|
+
}
|
|
2813
|
+
} else {
|
|
2814
|
+
const scoreItems = [
|
|
2815
|
+
{ name: `ii`, cnt: `5` },
|
|
2816
|
+
{ name: `shakin`, cnt: `0` },
|
|
2817
|
+
{ name: `matari`, cnt: `0` },
|
|
2818
|
+
{ name: `shobon`, cnt: `0` },
|
|
2819
|
+
{ name: `uwan`, cnt: `0` },
|
|
2820
|
+
{ name: `combo`, cnt: `5` },
|
|
2821
|
+
{},
|
|
2822
|
+
{ name: `kita`, cnt: `5` },
|
|
2823
|
+
{ name: `iknai`, cnt: `0` },
|
|
2824
|
+
{ name: `combo`, cnt: `5` },
|
|
2825
|
+
];
|
|
2826
|
+
const sx = _playW - 110 + g_headerObj.scAreaWidth;
|
|
2827
|
+
scoreItems.forEach((item, i) => {
|
|
2828
|
+
_frame.appendChild(
|
|
2829
|
+
createDivCss2Label(`previewScore${i}`, item.cnt || ``, {
|
|
2830
|
+
x: sx + 50, y: 20 * (i + 1), w: 50, h: 20,
|
|
2831
|
+
siz: 16, align: `right`,
|
|
2832
|
+
}, g_cssObj[`common_${item.name}`]),
|
|
2833
|
+
);
|
|
2834
|
+
});
|
|
2835
|
+
|
|
2836
|
+
// FrzReturn用ゲージ
|
|
2837
|
+
if (g_stateObj.frzReturn !== C_FLG_OFF) {
|
|
2838
|
+
multiAppend(_frame,
|
|
2839
|
+
createDivCss2Label(`previewFrzLifeBack`, ``, g_lblPosObj.previewFrzLifeBack, g_cssObj.life_Background),
|
|
2840
|
+
createDivCss2Label(`previewFrzLifeBar`, ``, g_lblPosObj.previewFrzLifeBar, g_cssObj.life_frzNormal),
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
// ============================================================
|
|
2846
|
+
// 曲名・制作者(左下)
|
|
2847
|
+
// ============================================================
|
|
2848
|
+
if (d.musicinfo === C_FLG_OFF) {
|
|
2849
|
+
_frame.appendChild(disableBox(`MusicInfo`, g_lblPosObj.previewMusicInfoDisabled));
|
|
2850
|
+
_frame.appendChild(disableBox(`GaugeName`, g_lblPosObj.previewGaugeNameDisabled));
|
|
2851
|
+
} else {
|
|
2852
|
+
const creditName = `Sample Music / Artist Name`;
|
|
2853
|
+
const difName = `[7key / Normal]`;
|
|
2854
|
+
const checkMusicSiz = (_text, _siz) => getFontSize2(_text, g_headerObj.playingWidth - g_headerObj.customViewWidth - 125, { maxSiz: _siz });
|
|
2855
|
+
|
|
2856
|
+
multiAppend(_frame,
|
|
2857
|
+
createDivCss2Label(`previewCredit`, creditName, {
|
|
2858
|
+
...g_lblPosObj.lblCredit, siz: checkMusicSiz(creditName, g_limitObj.musicTitleSiz)
|
|
2859
|
+
}),
|
|
2860
|
+
createDivCss2Label(`previewDifName`, difName, {
|
|
2861
|
+
...g_lblPosObj.lblDifName, siz: checkMusicSiz(difName, 12)
|
|
2862
|
+
}),
|
|
2863
|
+
createDivCss2Label(`previewTime1`, `0:04 /`, {
|
|
2864
|
+
...g_lblPosObj.lblTime1,
|
|
2865
|
+
}),
|
|
2866
|
+
createDivCss2Label(`previewTime2`, `2:54`, g_lblPosObj.lblTime2),
|
|
2867
|
+
|
|
2868
|
+
// ゲージ設定名
|
|
2869
|
+
createDivCss2Label(`previewGauge`, `Original`, g_lblPosObj.lblGaugeMode),
|
|
2870
|
+
)
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
// ============================================================
|
|
2874
|
+
// ショートカット表示
|
|
2875
|
+
// ============================================================
|
|
2876
|
+
const scGroup = createEmptySprite(_frame, `previewScGroup`, {
|
|
2877
|
+
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
2878
|
+
y: _playH - 100 + g_diffObj.shortcutY, w: 80, h: 100, pointerEvents: C_DIS_AUTO,
|
|
2879
|
+
});
|
|
2880
|
+
multiAppend(scGroup,
|
|
2881
|
+
createDivCss2Label(`lblRetry`, `[${g_lblNameObj.l_retry}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 0 }),
|
|
2882
|
+
createDivCss2Label(`lblRetrySc`, g_kCd[g_headerObj.keyRetry],
|
|
2883
|
+
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 15, fontWeight: g_headerObj.keyRetry === C_KEY_RETRY ? `normal` : `bold` }),
|
|
2884
|
+
createDivCss2Label(`lblTitleBack`, `[${g_lblNameObj.l_titleBack}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 35 }),
|
|
2885
|
+
createDivCss2Label(`lblTitleBackSc`, g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack],
|
|
2886
|
+
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 50, fontWeight: g_headerObj.keyTitleBack === C_KEY_TITLEBACK ? `normal` : `bold` }),
|
|
2887
|
+
createDivCss2Label(`lblPause`, `[${g_lblNameObj.l_pause}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 70 }),
|
|
2888
|
+
createDivCss2Label(`lblPauseSc`, g_kCd[g_headerObj.keyPause],
|
|
2889
|
+
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 85, fontWeight: g_headerObj.keyPause === C_KEY_PAUSE ? `normal` : `bold` }),
|
|
2890
|
+
);
|
|
2891
|
+
const scConfig = {
|
|
2892
|
+
toastTitle: g_lblNameObj.shortcutUpdate,
|
|
2893
|
+
getStdPos: () => ({
|
|
2894
|
+
x: g_sWidth + g_headerObj.scAreaWidth - 85,
|
|
2895
|
+
y: _playH - 100
|
|
2896
|
+
}),
|
|
2897
|
+
};
|
|
2898
|
+
makeElementDraggable(scGroup, `shortcut`, _playW, _playH, { w: 80, h: 100 }, scConfig);
|
|
2899
|
+
|
|
2900
|
+
// ユーザカスタムイベント(プレビュー表示用)
|
|
2901
|
+
safeExecuteCustomHooks(`g_customJsObj.displayPreview`, g_customJsObj.displayPreview, _frame, _playW, _playH);
|
|
2902
|
+
};
|
|
2903
|
+
|
|
2904
|
+
/**
|
|
2905
|
+
* プレビューが表示されたまま、HitPositionのラインだけを動かす
|
|
2906
|
+
* @param {number} _newHitPos 新しい g_stateObj.hitPosition の値
|
|
2907
|
+
*/
|
|
2908
|
+
const updatePreviewHitPositionLine = (_newHitPos) => {
|
|
2909
|
+
|
|
2910
|
+
// 表示用の文字列を作成(例: "+15px", "-8px", "0px")
|
|
2911
|
+
const sign = _newHitPos > 0 ? `+` : ``;
|
|
2912
|
+
const textValue = `${sign}${_newHitPos}px↑↓`;
|
|
2913
|
+
|
|
2914
|
+
// 1. 各種基準座標を再取得(buildPreviewUI 内の計算ロジックと同期)
|
|
2915
|
+
const stepY = g_posObj.stepY ?? C_STEP_Y;
|
|
2916
|
+
const revStepY = g_posObj.reverseStepY;
|
|
2917
|
+
const laneW = 50;
|
|
2918
|
+
|
|
2919
|
+
// 2. DOM要素を直接取得
|
|
2920
|
+
const lineNormal = document.getElementById(`previewHitPosLine`);
|
|
2921
|
+
const lineReverse = document.getElementById(`previewHitPosLineRev`);
|
|
2922
|
+
const textNormal = document.getElementById(`previewHitPosText`);
|
|
2923
|
+
|
|
2924
|
+
// 3. プレビューが表示されている場合のみ、style.top を直接書き換える
|
|
2925
|
+
if (lineNormal) {
|
|
2926
|
+
const newY = stepY + Math.round(laneW / 2) + _newHitPos;
|
|
2927
|
+
lineNormal.style.top = wUnit(newY);
|
|
2928
|
+
}
|
|
2929
|
+
if (textNormal) {
|
|
2930
|
+
textNormal.textContent = textValue;
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
if (lineReverse) {
|
|
2934
|
+
const newY = (C_STEP_Y + revStepY) + Math.round(laneW / 2) - _newHitPos;
|
|
2935
|
+
lineReverse.style.top = wUnit(newY);
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
|
|
2939
|
+
/**
|
|
2940
|
+
* 要素をドラッグ可能にする(汎用ユーティリティ)
|
|
2941
|
+
* @param {HTMLElement} _target ドラッグ対象の要素
|
|
2942
|
+
* @param {string} _key 座標保存用のキー(g_previewPosオブジェクトのプロパティ名)
|
|
2943
|
+
* @param {number} _playW 制限範囲の幅
|
|
2944
|
+
* @param {number} _playH 制限範囲の高さ
|
|
2945
|
+
* @param {object} _bounds 要素自体のサイズ { w, h, scale } (はみ出し防止用)
|
|
2946
|
+
* @param {object} _config 座標反映ルールオブジェクト
|
|
2947
|
+
*/
|
|
2948
|
+
const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) => {
|
|
2949
|
+
let dragging = false;
|
|
2950
|
+
let dragStartX = 0, dragStartY = 0;
|
|
2951
|
+
let elemStartX = 0, elemStartY = 0;
|
|
2952
|
+
|
|
2953
|
+
const boundsW = _bounds?.w ?? _target.offsetWidth ?? 0;
|
|
2954
|
+
const boundsH = _bounds?.h ?? _target.offsetHeight ?? 0;
|
|
2955
|
+
const scale = _bounds?.scale ?? 0.8;
|
|
2956
|
+
|
|
2957
|
+
// ドラッグハンドル(薄い枠)を作成
|
|
2958
|
+
const handleId = _target.id ? `handle_${_target.id}` : `dragHandle_${Math.random().toString(36).slice(2, 9)}`;
|
|
2959
|
+
_target.style.cursor = `grab`;
|
|
2960
|
+
|
|
2961
|
+
const bgColor = g_headerObj.baseBrightFlg ? `0,0,0` : `255,255,255`;
|
|
2962
|
+
createEmptySprite(_target, handleId, {
|
|
2963
|
+
x: 0, y: 0, w: boundsW, h: boundsH,
|
|
2964
|
+
border: `1px dashed rgba(${bgColor},0.3)`,
|
|
2965
|
+
boxSizing: `border-box`, borderRadius: `2px`,
|
|
2966
|
+
background: `rgba(${bgColor},0.04)`,
|
|
2967
|
+
});
|
|
2968
|
+
|
|
2969
|
+
g_handler.addListener(_target, `pointerdown`, _evt => {
|
|
2970
|
+
dragging = true;
|
|
2971
|
+
dragStartX = _evt.clientX;
|
|
2972
|
+
dragStartY = _evt.clientY;
|
|
2973
|
+
elemStartX = parseInt(_target.style.left, 10) || 0;
|
|
2974
|
+
elemStartY = parseInt(_target.style.top, 10) || 0;
|
|
2975
|
+
_target.style.cursor = `grabbing`;
|
|
2976
|
+
_target.setPointerCapture(_evt.pointerId);
|
|
2977
|
+
_evt.stopPropagation();
|
|
2978
|
+
});
|
|
2979
|
+
|
|
2980
|
+
g_handler.addListener(_target, `pointermove`, _evt => {
|
|
2981
|
+
if (!dragging) return;
|
|
2982
|
+
|
|
2983
|
+
// 1. マウスの実際の移動量を計算
|
|
2984
|
+
const mouseDx = _evt.clientX - dragStartX;
|
|
2985
|
+
const mouseDy = _evt.clientY - dragStartY;
|
|
2986
|
+
|
|
2987
|
+
// 2. 【最重要】スケール逆算して、縮小空間内の移動量に変換
|
|
2988
|
+
const dx = mouseDx / scale;
|
|
2989
|
+
const dy = mouseDy / scale;
|
|
2990
|
+
|
|
2991
|
+
// 3. 境界値制限
|
|
2992
|
+
const minX = g_headerObj.playingLayout ? -g_headerObj.scAreaWidth : 0;
|
|
2993
|
+
const newX = Math.max(minX, Math.min(_playW + g_headerObj.scAreaWidth - boundsW, elemStartX + dx));
|
|
2994
|
+
const newY = Math.max(0, Math.min(_playH - boundsH, elemStartY + dy));
|
|
2995
|
+
|
|
2996
|
+
_target.style.left = wUnit(newX);
|
|
2997
|
+
_target.style.top = wUnit(newY);
|
|
2998
|
+
_evt.stopPropagation();
|
|
2999
|
+
});
|
|
3000
|
+
|
|
3001
|
+
g_handler.addListener(_target, `pointerup`, _evt => {
|
|
3002
|
+
if (!dragging) return;
|
|
3003
|
+
dragging = false;
|
|
3004
|
+
_target.style.cursor = `grab`;
|
|
3005
|
+
|
|
3006
|
+
const finalX = parseInt(_target.style.left, 10) || 0;
|
|
3007
|
+
const finalY = parseInt(_target.style.top, 10) || 0;
|
|
3008
|
+
|
|
3009
|
+
if (g_previewPos[_key]) {
|
|
3010
|
+
g_previewPos[_key].x = finalX;
|
|
3011
|
+
g_previewPos[_key].y = finalY;
|
|
3012
|
+
}
|
|
3013
|
+
if (_config) {
|
|
3014
|
+
applyElementPositionToGame(finalX, finalY, _config, _key);
|
|
3015
|
+
}
|
|
3016
|
+
_evt.stopPropagation();
|
|
3017
|
+
});
|
|
3018
|
+
|
|
3019
|
+
g_handler.addListener(_target, `pointercancel`, _evt => {
|
|
3020
|
+
dragging = false;
|
|
3021
|
+
_target.style.cursor = `grab`;
|
|
3022
|
+
});
|
|
3023
|
+
};
|
|
3024
|
+
|
|
3025
|
+
/**
|
|
3026
|
+
* ドラッグ結果の座標をゲーム本体の設定に汎用的に反映する
|
|
3027
|
+
* @param {number} _x 確定したframe相対X
|
|
3028
|
+
* @param {number} _y 確定したframe相対Y
|
|
3029
|
+
* @param {object} _config 反映用の設定オブジェクト
|
|
3030
|
+
* @param {string} _key 保存用キー(g_diffObjのプロパティ名の接頭辞)
|
|
3031
|
+
*/
|
|
3032
|
+
const applyElementPositionToGame = (_x, _y, _config, _key) => {
|
|
3033
|
+
|
|
3034
|
+
// 1. 各要素固有の「標準座標(基準点)」を計算
|
|
3035
|
+
const std = _config.getStdPos();
|
|
3036
|
+
|
|
3037
|
+
// 2. オフセット(差分)を計算
|
|
3038
|
+
const diffX = _x - std.x;
|
|
3039
|
+
const diffY = _y - std.y;
|
|
3040
|
+
|
|
3041
|
+
// 3. 指定された保存先にオフセットを格納
|
|
3042
|
+
g_diffObj[`${_key}X`] = diffX;
|
|
3043
|
+
g_diffObj[`${_key}Y`] = diffY;
|
|
3044
|
+
g_localStorage[`${_key}X`] = diffX;
|
|
3045
|
+
g_localStorage[`${_key}Y`] = diffY;
|
|
3046
|
+
|
|
3047
|
+
// 4. トースト表示 (通知が不要な要素なら省略可能にする)
|
|
3048
|
+
if (_config.toastTitle) {
|
|
3049
|
+
showToast(`${_config.toastTitle}: dX=${diffX}, dY=${diffY}`);
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
3052
|
+
|
|
3053
|
+
/**
|
|
3054
|
+
* 画面上部に一時的なトーストメッセージを表示する
|
|
3055
|
+
* @param {string} _msg
|
|
3056
|
+
*/
|
|
3057
|
+
const showToast = _msg => {
|
|
3058
|
+
const existing = document.getElementById(`previewToast`);
|
|
3059
|
+
if (existing) existing.remove();
|
|
3060
|
+
|
|
3061
|
+
const toast = createDivCss2Label(`previewToast`, _msg, {
|
|
3062
|
+
x: g_btnX() + g_btnWidth() / 2, y: 50, w: g_btnWidth() / 2, h: 10, siz: 12,
|
|
3063
|
+
transform: `translateX(-50%)`,
|
|
3064
|
+
background: `rgba(0,40,80,0.92)`,
|
|
3065
|
+
color: `#aaddff`,
|
|
3066
|
+
border: `1px solid #3366aa`,
|
|
3067
|
+
borderRadius: `6px`,
|
|
3068
|
+
padding: `6px 16px`,
|
|
3069
|
+
fontFamily: `monospace`,
|
|
3070
|
+
whiteSpace: `nowrap`,
|
|
3071
|
+
pointerEvents: C_DIS_NONE,
|
|
3072
|
+
transition: `opacity 0.4s`,
|
|
3073
|
+
opacity: `1`,
|
|
3074
|
+
});
|
|
3075
|
+
divRoot.appendChild(toast);
|
|
3076
|
+
g_timerHandler.setTimeout(() => { toast.style.opacity = `0`; }, 2200);
|
|
3077
|
+
g_timerHandler.setTimeout(() => { if (toast.parentNode) toast.remove(); }, 2700);
|
|
3078
|
+
};
|
|
3079
|
+
|
|
3080
|
+
/**
|
|
3081
|
+
* 設定・オプション画面のラベル・ボタン処理の描画
|
|
3082
|
+
* @param {Object} _sprite 基準とするスプライト(ここで指定する座標は、そのスプライトからの相対位置)
|
|
3083
|
+
*/
|
|
3084
|
+
const createSettingsDisplayWindow = _sprite => {
|
|
3085
|
+
|
|
3086
|
+
/**
|
|
3087
|
+
* Display表示/非表示ボタン
|
|
3088
|
+
* @param {string} _name
|
|
3089
|
+
* @param {number} _heightPos 縦位置
|
|
3090
|
+
* @param {number} _widthPos 横位置
|
|
3091
|
+
*/
|
|
3092
|
+
const makeDisplayButton = (_name, _heightPos, _widthPos) => {
|
|
3093
|
+
|
|
3094
|
+
const flg = g_stateObj[`d_${_name.toLowerCase()}`];
|
|
3095
|
+
const linkId = `lnk${_name}`;
|
|
3096
|
+
|
|
3097
|
+
if (g_headerObj[`${_name}Use`]) {
|
|
3098
|
+
|
|
3099
|
+
// 設定名、CSS名(2種)、表示名
|
|
3100
|
+
const list = [C_FLG_OFF, C_FLG_ON].concat(g_settings[`d_${_name}s`] || []);
|
|
3101
|
+
const cssBarList = [C_FLG_OFF, C_FLG_ON].concat(Array(g_settings[`d_${_name}s`]?.length).fill(g_settings.d_cssBarExName) || []);
|
|
3102
|
+
const cssBgList = [g_settings.d_cssBgName, g_settings.d_cssBgName].concat(Array(g_settings[`d_${_name}s`]?.length).fill(g_settings.d_cssBgExName) || []);
|
|
3103
|
+
const lbls = [toCapitalize(_name), toCapitalize(_name)].concat(g_settings[`d_${_name}s`] || []);
|
|
3104
|
+
|
|
3105
|
+
const dispView = () => [C_FLG_OFF, C_FLG_ON].includes(g_stateObj[`d_${_name.toLowerCase()}`]) ?
|
|
3106
|
+
g_lblNameObj[`d_${toCapitalize(_name)}`] : getStgDetailName(lbls[g_settings.displayNum[_name]]);
|
|
3107
|
+
|
|
3108
|
+
const withShortCutDesc = () => createScText(document.getElementById(linkId), `${toCapitalize(_name)}`,
|
|
3109
|
+
{ displayName: g_currentPage, targetLabel: linkId, x: -5 });
|
|
3110
|
+
|
|
3111
|
+
/**
|
|
3112
|
+
* Displayボタン処理
|
|
3113
|
+
* @param {number} _scrollNum
|
|
3114
|
+
* @param {boolean} _filterFlg
|
|
3115
|
+
*/
|
|
3116
|
+
const switchDisplay = (_scrollNum = 1, _filterFlg = true) => {
|
|
3117
|
+
const prevDisp = g_settings.displayNum[_name];
|
|
3118
|
+
const [prevBarColor, prevBgColor] = [cssBarList[prevDisp], cssBgList[prevDisp]];
|
|
3119
|
+
|
|
3120
|
+
g_settings.displayNum[_name] = nextPos(prevDisp, _scrollNum, _filterFlg ? 2 : list.length);
|
|
3121
|
+
const nextDisp = g_settings.displayNum[_name];
|
|
3122
|
+
const [nextBarColor, nextBgColor] = [cssBarList[nextDisp], cssBgList[nextDisp]];
|
|
3123
|
+
|
|
3124
|
+
g_stateObj[`d_${_name.toLowerCase()}`] = list[g_settings.displayNum[_name]];
|
|
3125
|
+
document.getElementById(linkId).innerHTML = dispView();
|
|
3126
|
+
document.getElementById(linkId).classList.replace(g_cssObj[`button_${prevBarColor}`], g_cssObj[`button_${nextBarColor}`]);
|
|
3127
|
+
document.getElementById(linkId).classList.replace(g_cssObj[`button_${prevBgColor}`], g_cssObj[`button_${nextBgColor}`]);
|
|
3128
|
+
|
|
3129
|
+
withShortCutDesc();
|
|
3130
|
+
interlockingButton(g_headerObj, _name, nextBarColor, prevBarColor, true);
|
|
3131
|
+
updateSettingSummary();
|
|
3132
|
+
};
|
|
3133
|
+
|
|
3134
|
+
// Displayボタン初期化
|
|
3135
|
+
g_settings.displayNum[_name] = list.findIndex(flg => flg === g_stateObj[`d_${_name.toLowerCase()}`]);
|
|
3136
|
+
displaySprite.appendChild(
|
|
3137
|
+
makeSettingLblCssButton(linkId, dispView(), _heightPos, () => switchDisplay(), {
|
|
3138
|
+
x: 30 + 180 * _widthPos, y: 20 * _heightPos, w: 170, h: 18,
|
|
3139
|
+
title: g_msgObj[`d_${_name.toLowerCase()}`], borderStyle: `solid`,
|
|
3140
|
+
cxtFunc: () => switchDisplay(-1),
|
|
3141
|
+
}, `button_${cssBgList[g_settings.displayNum[_name]]}`, `button_${cssBarList[g_settings.displayNum[_name]]}`)
|
|
3142
|
+
);
|
|
3143
|
+
withShortCutDesc();
|
|
3144
|
+
|
|
3145
|
+
// Display切替ボタン(ON/OFF以外用)
|
|
3146
|
+
if (g_settings[`d_${_name}s`] !== undefined) {
|
|
3147
|
+
displaySprite.appendChild(
|
|
3148
|
+
makeSettingLblCssButton(`${linkId}R`, `>`, _heightPos, () => switchDisplay(1, false), {
|
|
3149
|
+
x: 175 + 180 * _widthPos, w: 25, y: 2 + 20 * _heightPos, h: 18, cxtFunc: () => switchDisplay(-1, false),
|
|
3150
|
+
}, g_cssObj.button_Mini)
|
|
3151
|
+
);
|
|
3152
|
+
}
|
|
3153
|
+
} else {
|
|
3154
|
+
displaySprite.appendChild(
|
|
3155
|
+
createDivCss2Label(linkId, g_lblNameObj[`d_${toCapitalize(_name)}`] + `:${g_headerObj[`${_name}Set`]}`, {
|
|
3156
|
+
x: 30 + 180 * _widthPos, y: 3 + 20 * _heightPos,
|
|
3157
|
+
w: 170, siz: g_limitObj.difSelectorSiz,
|
|
3158
|
+
}, g_cssObj[`button_Disabled${flg}`])
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
|
|
3163
|
+
// 各ボタン用のスプライトを作成
|
|
3164
|
+
const optionsprite = createEmptySprite(_sprite, `optionsprite`, g_windowObj.optionSprite);
|
|
3165
|
+
|
|
3166
|
+
// 設定毎に個別のスプライトを作成し、その中にラベル・ボタン類を配置
|
|
3167
|
+
const displaySprite = createEmptySprite(optionsprite, `displaySprite`, g_windowObj.displaySprite);
|
|
3168
|
+
const spriteList = setSpriteList(g_settingPos.settingsDisplay);
|
|
3169
|
+
|
|
3170
|
+
_sprite.appendChild(createDivCss2Label(`sdDesc`, g_lblNameObj.sdDesc, g_lblPosObj.sdDesc));
|
|
3171
|
+
g_displays.forEach((name, j) => makeDisplayButton(name, j % 6, Math.floor(j / 6)));
|
|
3172
|
+
|
|
3173
|
+
// ---------------------------------------------------
|
|
3174
|
+
// 矢印の見え方 (Appearance)
|
|
3175
|
+
// 縦位置: 5.8
|
|
3176
|
+
createGeneralSetting(spriteList.appearance, `appearance`, {
|
|
3177
|
+
addRFunc: () => dispAppearanceSlider(),
|
|
3178
|
+
});
|
|
3179
|
+
|
|
3180
|
+
// Hidden+/Sudden+初期値用スライダー、ロックボタン
|
|
3181
|
+
multiAppend(spriteList.appearance,
|
|
3182
|
+
createDivCss2Label(`lblAppearancePos`, `${g_hidSudObj.distH[g_stateObj.appearance](g_hidSudObj.filterPos)}`, g_lblPosObj.lblAppearancePos),
|
|
3183
|
+
createDivCss2Label(`lblAppearanceBar`, `<input id="appearanceSlider" type="range" value="${g_hidSudObj.filterPos}" min="0" max="100" step="1">`,
|
|
3184
|
+
g_lblPosObj.lblAppearanceBar),
|
|
3185
|
+
createCss2Button(`lnkLockBtn`, g_lblNameObj.filterLock, evt => setLockView(evt.target), {
|
|
3186
|
+
...g_lblPosObj.lnkLockBtn, cxtFunc: evt => setLockView(evt.target),
|
|
3187
|
+
}, g_cssObj.button_Default, g_cssObj[`button_Rev${g_stateObj.filterLock}`]),
|
|
3188
|
+
);
|
|
3189
|
+
|
|
3190
|
+
const setLockView = (_btn) => {
|
|
3191
|
+
const prevLock = g_stateObj.filterLock;
|
|
3192
|
+
g_settings.filterLockNum = (g_settings.filterLockNum + 1) % 2;
|
|
3193
|
+
g_stateObj.filterLock = g_settings.filterLocks[g_settings.filterLockNum];
|
|
3194
|
+
|
|
3195
|
+
_btn.classList.replace(g_cssObj[`button_Rev${prevLock}`],
|
|
3196
|
+
g_cssObj[`button_Rev${g_stateObj.filterLock}`]);
|
|
3197
|
+
updateSettingSummary();
|
|
3198
|
+
};
|
|
3199
|
+
|
|
3200
|
+
const appearanceSlider = document.getElementById(`appearanceSlider`);
|
|
3201
|
+
g_handler.addListener(appearanceSlider, `input`, () => {
|
|
3202
|
+
g_hidSudObj.filterPos = inputSlider(appearanceSlider, lblAppearancePos, `appearance`);
|
|
3203
|
+
updateSettingSummary();
|
|
3204
|
+
}, false);
|
|
3205
|
+
|
|
3206
|
+
const dispAppearanceSlider = () => {
|
|
3207
|
+
[`lblAppearanceBar`, `lnkLockBtn`, `lnkfilterLine`].forEach(obj =>
|
|
3208
|
+
$id(obj).visibility = g_appearanceRanges.includes(g_stateObj.appearance) ? `Visible` : `Hidden`);
|
|
3209
|
+
inputSlider(appearanceSlider, lblAppearancePos, `appearance`);
|
|
3210
|
+
};
|
|
3211
|
+
dispAppearanceSlider();
|
|
3212
|
+
|
|
3213
|
+
// ---------------------------------------------------
|
|
3214
|
+
// 判定表示系の不透明度 (Opacity)
|
|
3215
|
+
// 縦位置: 7.4
|
|
3216
|
+
g_headerObj.opacityUse = g_headerObj.judgmentUse || g_headerObj.judgmentSet === C_FLG_ON;
|
|
3217
|
+
|
|
3218
|
+
createGeneralSetting(spriteList.opacity, `opacity`, { unitName: g_lblNameObj.percent });
|
|
3219
|
+
|
|
3220
|
+
// ---------------------------------------------------
|
|
3221
|
+
// タイミング調整 (HitPosition)
|
|
3222
|
+
// 縦位置: 8.4
|
|
3223
|
+
createGeneralSetting(spriteList.hitPosition, `hitPosition`, {
|
|
3224
|
+
skipTerms: g_settings.hitPositionTerms, scLabel: g_lblNameObj.sc_hitPosition, roundNum: 5,
|
|
3225
|
+
unitName: g_lblNameObj.pixel,
|
|
3226
|
+
addRFunc: () => updatePreviewHitPositionLine(g_stateObj.hitPosition),
|
|
3227
|
+
});
|
|
3228
|
+
};
|
|
3229
|
+
|
|
3230
|
+
/**
|
|
3231
|
+
* Displayボタンを切り替えたときに連動して切り替えるボタンの設定
|
|
3232
|
+
* @param {object} _headerObj
|
|
3233
|
+
* @param {string} _name
|
|
3234
|
+
* @param {string} _current 変更元
|
|
3235
|
+
* @param {string} _next 変更先
|
|
3236
|
+
* @param {boolean} _buttonFlg ボタンフラグ (false: 初期, true: ボタン)
|
|
3237
|
+
*/
|
|
3238
|
+
const interlockingButton = (_headerObj, _name, _current, _next, _buttonFlg = false) => {
|
|
3239
|
+
let includeDefaults = [];
|
|
3240
|
+
if (g_stateObj[`d_${_name.toLowerCase()}`] === C_FLG_OFF) {
|
|
3241
|
+
g_displays.forEach(option => {
|
|
3242
|
+
if (option === _name) {
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
if (g_stateObj[`d_${option.toLowerCase()}`] === C_FLG_ON && _headerObj[`${option}Default`] !== undefined) {
|
|
3246
|
+
includeDefaults = includeDefaults.concat(_headerObj[`${option}ChainOFF`]);
|
|
3247
|
+
}
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
if (_headerObj[`${_name}ChainOFF`].length !== 0) {
|
|
3252
|
+
|
|
3253
|
+
// 連動してOFFにするボタンの設定
|
|
3254
|
+
_headerObj[`${_name}ChainOFF`].filter(defaultOption => !includeDefaults.includes(defaultOption))
|
|
3255
|
+
.forEach(defaultOption => {
|
|
3256
|
+
g_stateObj[`d_${defaultOption.toLowerCase()}`] = _next;
|
|
3257
|
+
if (_buttonFlg) {
|
|
3258
|
+
let txtDisabled = ``;
|
|
3259
|
+
if (!g_headerObj[`${defaultOption}Use`]) {
|
|
3260
|
+
txtDisabled = `Disabled`;
|
|
3261
|
+
document.getElementById(`lnk${defaultOption}`).textContent = `${toCapitalize(defaultOption)}:${_next}`;
|
|
3262
|
+
}
|
|
3263
|
+
document.getElementById(`lnk${defaultOption}`).classList.replace(g_cssObj[`button_${txtDisabled}${_current}`], g_cssObj[`button_Disabled${_next}`]);
|
|
3264
|
+
}
|
|
3265
|
+
// さらに連動する場合は設定を反転
|
|
3266
|
+
interlockingButton(_headerObj, defaultOption, _next, _current, _buttonFlg);
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
};
|
|
3270
|
+
|
|
3271
|
+
/*-----------------------------------------------------------*/
|
|
3272
|
+
/* Scene : EX-SETTINGS [apple] */
|
|
3273
|
+
/*-----------------------------------------------------------*/
|
|
3274
|
+
|
|
3275
|
+
const exSettingInit = () => {
|
|
3276
|
+
clearWindow();
|
|
3277
|
+
g_currentPage = `exSetting`;
|
|
3278
|
+
|
|
3279
|
+
multiAppend(divRoot,
|
|
3280
|
+
|
|
3281
|
+
// 画面タイトル
|
|
3282
|
+
getTitleDivLabel(`lblTitle`,
|
|
3283
|
+
`<div class="settings_Title">EX-</div><div class="settings_Title2">SETTINGS</div>`
|
|
3284
|
+
.replace(/[\t\n]/g, ``), 0, 15, g_cssObj.flex_centering),
|
|
3285
|
+
|
|
3286
|
+
);
|
|
3287
|
+
|
|
3288
|
+
// 各ボタン用のスプライトを作成
|
|
3289
|
+
createEmptySprite(divRoot, `optionsprite`, g_windowObj.optionSprite);
|
|
3290
|
+
const spriteList = setSpriteList(g_settingPos.exSetting);
|
|
3291
|
+
|
|
3292
|
+
createGeneralSettingEx(spriteList, `playWindow`, { defaultList: [`Default`] });
|
|
3293
|
+
createGeneralSetting(spriteList.stepArea, `stepArea`);
|
|
3294
|
+
createGeneralSettingEx(spriteList, `frzReturn`);
|
|
3295
|
+
createGeneralSetting(spriteList.shaking, `shaking`);
|
|
3296
|
+
createGeneralSetting(spriteList.effect, `effect`, {
|
|
3297
|
+
addRFunc: () => {
|
|
3298
|
+
g_stateObj.d_arroweffect = boolToSwitch(g_stateObj.effect !== C_FLG_OFF || g_headerObj.arrowEffectSetFlg);
|
|
3299
|
+
g_headerObj.arrowEffectUse = g_stateObj.effect === C_FLG_OFF && g_headerObj.arrowEffectUseOrg;
|
|
3300
|
+
g_headerObj.arrowEffectSet = g_stateObj.d_arroweffect;
|
|
3301
|
+
},
|
|
3302
|
+
});
|
|
3303
|
+
createGeneralSettingEx(spriteList, `camoufrage`, { defaultList: [] });
|
|
3304
|
+
createGeneralSetting(spriteList.swapping, `swapping`);
|
|
3305
|
+
createGeneralSetting(spriteList.judgRange, `judgRange`, {
|
|
3306
|
+
addRFunc: () => {
|
|
3307
|
+
[g_judgObj.arrowJ, g_judgObj.frzJ] = g_judgRanges[g_stateObj.judgRange];
|
|
3308
|
+
lblJudgRangeView.innerHTML = getJudgRangeView();
|
|
3309
|
+
}
|
|
3310
|
+
});
|
|
3311
|
+
createGeneralSetting(spriteList.autoRetry, `autoRetry`);
|
|
3312
|
+
|
|
3313
|
+
// 判定範囲の設定を表示
|
|
3314
|
+
const getJudgRangeView = () => `| ` +
|
|
3315
|
+
`<span class="common_ii">${g_lblNameObj.j_ii}</span>: ≦ <b>±${g_judgObj.arrowJ[0]} f</b> | ` +
|
|
3316
|
+
`<span class="common_shakin">${g_lblNameObj.j_shakin}</span>: ≦ <b>±${g_judgObj.arrowJ[1]} f</b> | ` +
|
|
3317
|
+
`<span class="common_matari">${g_lblNameObj.j_matari}</span>: ≦ <b>±${g_judgObj.arrowJ[2]} f</b> | <br>| ` +
|
|
3318
|
+
`<span class="common_shobon">${g_lblNameObj.j_shobon}</span>: ≦ <b>±${g_judgObj.arrowJ[3]} f</b> | ` +
|
|
3319
|
+
`<span class="common_uwan">${g_lblNameObj.j_uwan}</span>: > <b>±${g_judgObj.arrowJ[3]} f</b> | <br>| ` +
|
|
3320
|
+
`<span class="common_kita">${g_lblNameObj.j_kita}</span>: ≦ <b>±${g_judgObj.frzJ[1]} f</b> | ` +
|
|
3321
|
+
`<span class="common_iknai">${g_lblNameObj.j_iknai}</span>: > <b>±${g_judgObj.frzJ[1]} f</b> |`;
|
|
3322
|
+
|
|
3323
|
+
multiAppend(judgRangeSprite,
|
|
3324
|
+
createDivCss2Label(`lblJudgRangeView`, getJudgRangeView(), {
|
|
3325
|
+
x: parseFloat($id(`lblJudgRange`).left) + 10, y: parseFloat($id(`lblJudgRange`).top) + 25, w: 300, h: 30, siz: 13,
|
|
3326
|
+
align: C_ALIGN_LEFT,
|
|
3327
|
+
}),
|
|
3328
|
+
);
|
|
3329
|
+
|
|
3330
|
+
// ユーザカスタムイベント(初期)
|
|
3331
|
+
safeExecuteCustomHooks(`g_customJsObj.exSetting`, g_customJsObj.exSetting);
|
|
3332
|
+
|
|
3333
|
+
// 設定系のボタン群をまとめて作成(Data Save, Display切替, Back, KeyConfig, Playボタン)
|
|
3334
|
+
commonSettingBtn(g_currentPage);
|
|
3335
|
+
|
|
3336
|
+
// キー操作イベント(デフォルト)
|
|
3337
|
+
setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
|
|
3338
|
+
document.oncontextmenu = () => true;
|
|
3339
|
+
|
|
3340
|
+
safeExecuteCustomHooks(`g_skinJsObj.exSetting`, g_skinJsObj.exSetting);
|
|
3341
|
+
};
|
|
3342
|
+
|
|
3343
|
+
/**
|
|
3344
|
+
* 拡張設定込みの標準設定
|
|
3345
|
+
* @param {any[]} _spriteList
|
|
3346
|
+
* @param {string} _name
|
|
3347
|
+
* @param {{ defaultList?: string[], displayName?: string, func?: Function, funcEx?: Function }} [options={}]
|
|
3348
|
+
* @param {string[]} [options.defaultList=[C_FLG_OFF]] 拡張設定未使用の設定リスト
|
|
3349
|
+
* @param {string} [options.displayName='exSetting']
|
|
3350
|
+
* @param {Function} [options.func=()=>true] 通常ボタン用追加関数
|
|
3351
|
+
* @param {Function} [options.funcEx=()=>true] 拡張ボタン用追加関数
|
|
3352
|
+
*/
|
|
3353
|
+
const createGeneralSettingEx = (_spriteList, _name, { defaultList = [C_FLG_OFF], displayName = `exSetting`,
|
|
3354
|
+
func = () => true, funcEx = () => true } = {}) => {
|
|
3355
|
+
if (_spriteList?.[_name] === undefined) return;
|
|
3356
|
+
|
|
3357
|
+
/**
|
|
3358
|
+
* 拡張ボタンの表示・非表示と通常ボタンの幅変更
|
|
3359
|
+
*/
|
|
3360
|
+
const setExpandedBtnSiz = () => {
|
|
3361
|
+
const camelH = toCapitalize(_name);
|
|
3362
|
+
const isDefault = defaultList.includes(g_stateObj[_name]);
|
|
3363
|
+
if (isDefault) {
|
|
3364
|
+
$id(`lnk${camelH}Type`).display = C_DIS_NONE;
|
|
3365
|
+
$id(`lnk${camelH}`).left = wUnit(g_limitObj.setLblLeft);
|
|
3366
|
+
$id(`lnk${camelH}`).width = wUnit(g_limitObj.setLblWidth);
|
|
3367
|
+
} else {
|
|
3368
|
+
$id(`lnk${camelH}Type`).display = C_DIS_INHERIT;
|
|
3369
|
+
$id(`lnk${camelH}`).left = wUnit(g_limitObj.setLblLeftShort);
|
|
3370
|
+
$id(`lnk${camelH}`).width = wUnit(g_limitObj.setLblWidthShort);
|
|
3371
|
+
}
|
|
3372
|
+
const labelWidth = isDefault ? g_limitObj.setLblWidth : g_limitObj.setLblWidthShort - 10;
|
|
3373
|
+
$id(`lnk${camelH}`).fontSize = wUnit(getFontSize2(getStgDetailName(g_stateObj[_name]), labelWidth, { maxSiz: g_limitObj.setLblSiz }));
|
|
3374
|
+
};
|
|
3375
|
+
|
|
3376
|
+
/**
|
|
3377
|
+
* 拡張ボタンの作成
|
|
3378
|
+
* @returns {HTMLDivElement}
|
|
3379
|
+
*/
|
|
3380
|
+
const createExpandedBtn = () =>
|
|
3381
|
+
createCss2Button(`lnk${toCapitalize(_name)}Type`, getStgDetailName(g_stateObj[`${_name}Type`]),
|
|
3382
|
+
() => {
|
|
3383
|
+
setSetting(1, `${_name}Type`, { maxSiz: g_limitObj.difSelectorSiz });
|
|
3384
|
+
funcEx();
|
|
3385
|
+
createExpandedScView(_name);
|
|
3386
|
+
},
|
|
3387
|
+
{
|
|
3388
|
+
...g_lblPosObj.btnReverse,
|
|
3389
|
+
cxtFunc: () => {
|
|
3390
|
+
setSetting(-1, `${_name}Type`, { maxSiz: g_limitObj.difSelectorSiz });
|
|
3391
|
+
funcEx();
|
|
3392
|
+
createExpandedScView(_name);
|
|
3393
|
+
},
|
|
3394
|
+
title: g_msgObj[`${_name}Type`] ?? ``,
|
|
3395
|
+
}, g_cssObj.button_Default, g_cssObj.button_RevON);
|
|
3396
|
+
|
|
3397
|
+
/**
|
|
3398
|
+
* 拡張ボタンのショートカット表示、拡張ボタンのCSS切り替え
|
|
3399
|
+
*/
|
|
3400
|
+
const createExpandedScView = () => {
|
|
3401
|
+
const settingLabel = `${toCapitalize(_name)}Type`;
|
|
3402
|
+
if (document.getElementById(`sc${settingLabel}`) === null) {
|
|
3403
|
+
createScText(document.getElementById(`lnk${settingLabel}`), settingLabel, {
|
|
3404
|
+
displayName, targetLabel: `lnk${settingLabel}`, x: -13
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
document.getElementById(`lnk${settingLabel}`).classList.remove(g_cssObj.button_RevON, g_cssObj.button_RevOFF);
|
|
3408
|
+
document.getElementById(`lnk${settingLabel}`).classList.add(g_cssObj[`button_Rev${boolToSwitch(g_stateObj[`${_name}Type`] !== C_FLG_HYPHEN)}`]);
|
|
3409
|
+
};
|
|
3410
|
+
|
|
3411
|
+
// TypeUse 未定義時は true 扱いにする
|
|
3412
|
+
const typeEnabled = setBoolVal(g_headerObj[`${_name}TypeUse`], true);
|
|
3413
|
+
createGeneralSetting(_spriteList[_name], _name, {
|
|
3414
|
+
addRFunc: () => {
|
|
3415
|
+
if (typeEnabled) {
|
|
3416
|
+
setExpandedBtnSiz();
|
|
3417
|
+
}
|
|
3418
|
+
func();
|
|
3419
|
+
},
|
|
3420
|
+
});
|
|
3421
|
+
if (typeEnabled) {
|
|
3422
|
+
_spriteList[_name].appendChild(createExpandedBtn());
|
|
3423
|
+
setExpandedBtnSiz();
|
|
3424
|
+
createExpandedScView();
|
|
3425
|
+
}
|
|
3426
|
+
};
|