danoniplus 50.3.1 → 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.
@@ -0,0 +1,979 @@
1
+ /**
2
+ * Dancing☆Onigiri (CW Edition)
3
+ * 結果画面
4
+ * - ページ: result
5
+ *
6
+ * Source by tickle
7
+ * Created : 2026/09/13
8
+ * Revised :
9
+ *
10
+ * https://github.com/cwtickle/danoniplus
11
+ */
12
+
13
+ /*-----------------------------------------------------------*/
14
+ /* Scene : RESULT [grape] */
15
+ /*-----------------------------------------------------------*/
16
+
17
+ /**
18
+ * リザルト画面初期化
19
+ */
20
+ const resultInit = () => {
21
+
22
+ clearWindow();
23
+ g_currentPage = `result`;
24
+
25
+ // 結果画面用フレーム初期化
26
+ g_scoreObj.resultFrameNum = 0;
27
+
28
+ // リザルトアニメーション用フレーム初期化、ループカウンター設定
29
+ g_animationData.forEach(sprite => {
30
+ g_scoreObj[`${sprite}ResultFrameNum`] = 0;
31
+ g_scoreObj[`${sprite}ResultLoopCount`] = 0;
32
+ });
33
+
34
+ const divRoot = document.getElementById(`divRoot`);
35
+
36
+ // 曲時間制御変数
37
+ let thisTime;
38
+ let buffTime;
39
+ let resultStartTime = g_workObj.mainEndTime > 0 ? g_workObj.mainEndTime : performance.now();
40
+
41
+ if (g_stateObj.d_background === C_FLG_OFF && g_headerObj.resultMotionSet) {
42
+ } else {
43
+ // ゲームオーバー時は失敗時のリザルトモーションを適用
44
+ if (!g_finishFlg) {
45
+ const scoreIdHeader = setScoreIdHeader(g_stateObj.scoreId, g_stateObj.scoreLockFlg, false);
46
+
47
+ g_animationData.forEach(sprite => {
48
+ const failedData = g_rootObj[`${sprite}failedS${scoreIdHeader}_data`] ?? g_rootObj[`${sprite}failedS_data`];
49
+ if (failedData !== undefined) {
50
+ [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] = g_animationFunc.make[sprite](failedData);
51
+ }
52
+ });
53
+ } else if (g_gameOverFlg) {
54
+ g_animationData.forEach(sprite => {
55
+ g_headerObj[`${sprite}ResultData`] = g_headerObj[`${sprite}FailedData`].concat();
56
+ g_headerObj[`${sprite}ResultMaxDepth`] = g_headerObj[`${sprite}FailedMaxDepth`];
57
+ });
58
+ }
59
+ }
60
+
61
+ // diffListから適正Adjを算出(20個以下の場合は算出しない)
62
+ const getSign = _val => (_val > 0 ? `+` : ``);
63
+ const getDiffFrame = _val => `${getSign(_val)}${_val}${g_lblNameObj.frame}`;
64
+ const diffLength = g_workObj.diffList.length;
65
+ const bayesFunc = (_offset, _length) => {
66
+ let result = 0;
67
+ for (let j = _offset; j < _length; j++) {
68
+ result += (_length - j) * (j + 1) * g_workObj.diffList[j];
69
+ }
70
+ return result;
71
+ };
72
+ const bayesExVal = 6 * bayesFunc(0, diffLength) / (diffLength * (diffLength + 1) * (diffLength + 2));
73
+ const estimatedAdj = (diffLength <= 20 ? `` : Math.round((g_stateObj.adjustment / g_headerObj.playbackRate - bayesExVal) * 10) / 10);
74
+
75
+ // 背景スプライトを作成
76
+ createMultipleSprite(`backResultSprite`, g_headerObj.backResultMaxDepth);
77
+
78
+ // タイトル文字描画
79
+ divRoot.appendChild(getTitleDivLabel(`lblTitle`, g_lblNameObj.result, 0, 15, `settings_Title`));
80
+
81
+ const playDataWindow = createEmptySprite(divRoot, `playDataWindow`, g_windowObj.playDataWindow, g_cssObj.result_PlayDataWindow);
82
+ const resultWindow = createEmptySprite(divRoot, `resultWindow`, g_windowObj.resultWindow);
83
+
84
+ const playingArrows = g_resultObj.ii + g_resultObj.shakin +
85
+ g_resultObj.matari + g_resultObj.shobon + g_resultObj.uwan +
86
+ g_resultObj.kita + g_resultObj.iknai;
87
+
88
+ // スコア計算(一括)
89
+ const scoreTmp = Object.keys(g_pointAllocation).reduce(
90
+ (score, name) => score + g_resultObj[name] * g_pointAllocation[name], 0);
91
+
92
+ const allScore = g_fullArrows * 10;
93
+ const resultScore = Math.round(scoreTmp / allScore * g_maxScore) || 0;
94
+ g_resultObj.score = resultScore;
95
+ const allArrowsPlayed = playingArrows === g_fullArrows;
96
+
97
+ // ランク計算
98
+ let rankMark = g_rankObj.rankMarkX;
99
+ let rankColor = g_rankObj.rankColorX;
100
+ if (g_gameOverFlg) {
101
+ rankMark = g_rankObj.rankMarkF;
102
+ rankColor = g_rankObj.rankColorF;
103
+ g_resultObj.spState = `failed`;
104
+ } else if (allArrowsPlayed && g_stateObj.autoAll === C_FLG_OFF && !(g_headerObj.excessiveJdgUse && g_stateObj.excessive === C_FLG_OFF)) {
105
+ if (g_resultObj.spState === ``) {
106
+ g_resultObj.spState = `cleared`;
107
+ }
108
+ if (g_resultObj.spState === `perfect` || g_resultObj.spState === `allPerfect`) {
109
+ rankMark = g_rankObj[`rankMark${toCapitalize(g_resultObj.spState)}`];
110
+ rankColor = g_rankObj[`rankColor${toCapitalize(g_resultObj.spState)}`];
111
+ } else {
112
+ const rPos = g_rankObj.rankRate.findIndex(rate => resultScore * 100 / g_maxScore >= rate);
113
+ rankMark = g_rankObj.rankMarks[rPos];
114
+ rankColor = g_rankObj.rankColor[rPos];
115
+ }
116
+ }
117
+
118
+ // 曲名・オプション描画
119
+ const playbackView = (g_headerObj.playbackRate === 1 ? `` : ` [Rate:${g_headerObj.playbackRate}]`);
120
+ const musicTitle = (g_headerObj.musicTitles[g_headerObj.musicNos[g_stateObj.scoreId]] || g_headerObj.musicTitle) + playbackView;
121
+
122
+ const mTitleForView = [g_headerObj.musicTitleForView[0], (g_headerObj.musicTitleForView[1] || ``) + playbackView];
123
+ if (g_headerObj.musicTitlesForView[g_headerObj.musicNos[g_stateObj.scoreId]] !== undefined) {
124
+ mTitleForView.forEach((mTitle, j) =>
125
+ mTitleForView[j] = g_headerObj.musicTitlesForView[g_headerObj.musicNos[g_stateObj.scoreId]][j] + (j === 1 ? playbackView : ``));
126
+ }
127
+
128
+ const transKeyName = getTransKeyName();
129
+ const orgShuffleFlg = getOrgShuffleFlg();
130
+ const shuffleName = getShuffleName();
131
+ const settingData = getSelectedSettingList(orgShuffleFlg);
132
+
133
+ const [lblRX, dataRX] = [20, 60];
134
+ multiAppend(playDataWindow,
135
+ makeCssResultPlayData(`lblMusic`, lblRX, g_cssObj.result_lbl, 0, g_lblNameObj.rt_Music, C_ALIGN_LEFT),
136
+ makeCssResultPlayData(`lblMusicData`, dataRX, g_cssObj.result_style, 0, mTitleForView[0]),
137
+ makeCssResultPlayData(`lblMusicData2`, dataRX, g_cssObj.result_style, 1, mTitleForView[1]),
138
+ makeCssResultPlayData(`lblDifficulty`, lblRX, g_cssObj.result_lbl, 2, g_lblNameObj.rt_Difficulty, C_ALIGN_LEFT),
139
+ makeCssResultPlayData(`lblDifData`, dataRX, g_cssObj.result_style, 2, settingData.difData, C_ALIGN_CENTER,
140
+ { siz: getFontSize2(settingData.difData, 350) }),
141
+ makeCssResultPlayData(`lblStyle`, lblRX, g_cssObj.result_lbl, 3, g_lblNameObj.rt_Style, C_ALIGN_LEFT),
142
+ makeCssResultPlayData(`lblStyleData`, dataRX, g_cssObj.result_style, 3, settingData.playStyleData),
143
+ makeCssResultPlayData(`lblDisplay`, lblRX, g_cssObj.result_lbl, 4, g_lblNameObj.rt_Display, C_ALIGN_LEFT),
144
+ makeCssResultPlayData(`lblDisplayData`, dataRX, g_cssObj.result_style, 4, settingData.displayData),
145
+ makeCssResultPlayData(`lblDisplay2Data`, dataRX, g_cssObj.result_style, 5, settingData.display2Data),
146
+ );
147
+
148
+ // 設定項目が多い場合に2行に分解して表示する処理
149
+ const [styleStr, styleSiz] = getFontSizeMulti(settingData.playStyleData, 350, { maxSizMulti: 10, len: 60 });
150
+ lblStyleData.innerHTML = styleStr;
151
+ lblStyleData.style.fontSize = wUnit(styleSiz);
152
+
153
+ /**
154
+ * キャラクタ、スコア描画のID共通部、色CSS名、スコア変数名
155
+ * @property {number} pos 表示位置(縦)
156
+ * @property {string} id 表示用ラベルフッター
157
+ * @property {string} color CSS用ラベルフッター
158
+ * @property {string} label 表示名
159
+ * @property {string} dfColor 表示する文字のカラーコード (リザルト画像で使用)
160
+ */
161
+ const jdgScoreObj = {
162
+ ii: { pos: 0, id: `Ii`, color: `ii`, label: g_lblNameObj.j_ii, dfColor: `#66ffff`, },
163
+ shakin: { pos: 1, id: `Shakin`, color: `shakin`, label: g_lblNameObj.j_shakin, dfColor: `#99ff99`, },
164
+ matari: { pos: 2, id: `Matari`, color: `matari`, label: g_lblNameObj.j_matari, dfColor: `#ff9966`, },
165
+ shobon: { pos: 3, id: `Shobon`, color: `shobon`, label: g_lblNameObj.j_shobon, dfColor: `#ccccff`, },
166
+ uwan: { pos: 4, id: `Uwan`, color: `uwan`, label: g_lblNameObj.j_uwan, dfColor: `#ff9999`, },
167
+ kita: { pos: 5, id: `Kita`, color: `kita`, label: g_lblNameObj.j_kita, dfColor: `#ffff99`, },
168
+ iknai: { pos: 6, id: `Iknai`, color: `iknai`, label: g_lblNameObj.j_iknai, dfColor: `#99ff66`, },
169
+ maxCombo: { pos: 7, id: `MCombo`, color: `combo`, label: g_lblNameObj.j_maxCombo, dfColor: `#ffffff`, },
170
+ fmaxCombo: { pos: 8, id: `FCombo`, color: `combo`, label: g_lblNameObj.j_fmaxCombo, dfColor: `#ffffff`, },
171
+ score: { pos: 10, id: `Score`, color: `score`, label: g_lblNameObj.j_score, dfColor: `#ffffff`, },
172
+ };
173
+
174
+ // キャラクタ、スコア描画
175
+ Object.keys(jdgScoreObj).forEach(score =>
176
+ multiAppend(resultWindow,
177
+ makeCssResultSymbol(`lbl${jdgScoreObj[score].id}`, 0, g_cssObj[`common_${jdgScoreObj[score].color}`], jdgScoreObj[score].pos, jdgScoreObj[score].label),
178
+ makeCssResultSymbol(`lbl${jdgScoreObj[score].id}S`, 50, g_cssObj.common_score, jdgScoreObj[score].pos, g_resultObj[score], C_ALIGN_RIGHT),
179
+ ));
180
+ if (g_stateObj.autoAll === C_FLG_OFF) {
181
+ const [lblPosX, dataPosX] = [350, 260];
182
+ multiAppend(resultWindow,
183
+ makeCssResultSymbol(`lblFast`, lblPosX, g_cssObj.common_diffFast, 0, g_lblNameObj.j_fast),
184
+ makeCssResultSymbol(`lblSlow`, lblPosX, g_cssObj.common_diffSlow, 2, g_lblNameObj.j_slow),
185
+ makeCssResultSymbol(`lblFastS`, dataPosX, g_cssObj.score, 1, g_resultObj.fast, C_ALIGN_RIGHT),
186
+ makeCssResultSymbol(`lblSlowS`, dataPosX, g_cssObj.score, 3, g_resultObj.slow, C_ALIGN_RIGHT),
187
+ );
188
+ if (estimatedAdj !== ``) {
189
+ multiAppend(resultWindow,
190
+ makeCssResultSymbol(`lblAdj`, lblPosX, g_cssObj.common_estAdj, 4, g_lblNameObj.j_adj),
191
+ makeCssResultSymbol(`lblAdjS`, dataPosX, g_cssObj.score, 5, `${getDiffFrame(estimatedAdj)}`, C_ALIGN_RIGHT),
192
+ );
193
+ }
194
+ if (g_stateObj.excessive === C_FLG_ON) {
195
+ multiAppend(resultWindow,
196
+ makeCssResultSymbol(`lblExcessive`, lblPosX, g_cssObj.common_excessive, 6, g_lblNameObj.j_excessive),
197
+ makeCssResultSymbol(`lblExcessiveS`, dataPosX, g_cssObj.score, 7, g_resultObj.excessive, C_ALIGN_RIGHT),
198
+ );
199
+ }
200
+ }
201
+
202
+ // ランク描画
203
+ resultWindow.appendChild(
204
+ createDivCss2Label(`lblRank`, rankMark, {
205
+ ...g_lblPosObj.lblRank, color: rankColor, fontFamily: getBasicFont(`"Bookman Old Style"`),
206
+ })
207
+ );
208
+
209
+ // Cleared & Failed表示
210
+ const lblResultPre = createDivCss2Label(
211
+ `lblResultPre`,
212
+ resultViewText(g_gameOverFlg ? `failed` : `cleared`),
213
+ {
214
+ ...g_lblPosObj.lblResultPre,
215
+ animationDuration: (g_gameOverFlg ? `3s` : `2.5s`),
216
+ animationName: (g_gameOverFlg ? `upToDownFade` : `leftToRightFade`)
217
+ }, g_cssObj.result_Cleared, g_cssObj.result_Window
218
+ );
219
+ divRoot.appendChild(lblResultPre);
220
+
221
+ divRoot.appendChild(createDivCss2Label(`lblResultPre2`,
222
+ resultViewText(g_gameOverFlg ? `failed` : (allArrowsPlayed ? g_resultObj.spState : ``)),
223
+ g_lblPosObj.lblResultPre2, g_cssObj.result_Cleared));
224
+
225
+ // プレイデータは Cleared & Failed に合わせて表示
226
+ playDataWindow.style.animationDuration = `3s`;
227
+ playDataWindow.style.animationName = `slowlyAppearing`;
228
+
229
+ if (g_finishFlg && g_headerObj.resultDelayFrame > 0) {
230
+ lblResultPre.style.animationDelay = `${g_headerObj.resultDelayFrame / g_fps}s`;
231
+ playDataWindow.style.animationDelay = `${g_headerObj.resultDelayFrame / g_fps}s`;
232
+ }
233
+
234
+ // ハイスコア差分計算
235
+ const assistFlg = (g_autoPlaysBase.includes(g_stateObj.autoPlay) ? `` : `-${g_stateObj.autoPlay}less`);
236
+ const mirrorName = (g_stateObj.shuffle.indexOf(`Mirror`) !== -1 ? `-${g_stateObj.shuffle}` : ``);
237
+ let scoreName = getStorageKeyName(g_headerObj.keyLabels[g_stateObj.scoreId], transKeyName, assistFlg, mirrorName, g_stateObj.scoreId);
238
+
239
+ const highscoreDfObj = {
240
+ ii: 0, shakin: 0, matari: 0, shobon: 0, uwan: 0,
241
+ kita: 0, iknai: 0,
242
+ maxCombo: 0, fmaxCombo: 0, score: 0,
243
+ };
244
+
245
+ const highscorePreCondition = (g_stateObj.autoAll === C_FLG_OFF && g_headerObj.playbackRate === 1 &&
246
+ (g_stateObj.shuffle === C_FLG_OFF || (g_stateObj.shuffle.endsWith(`Mirror`) && orgShuffleFlg)));
247
+ if (highscorePreCondition) {
248
+
249
+ // ハイスコア差分描画
250
+ Object.keys(jdgScoreObj).filter(score => score !== `score`).forEach(score =>
251
+ multiAppend(resultWindow,
252
+ makeCssResultSymbol(`lbl${jdgScoreObj[score].id}L1`, C_RLT_BRACKET_L, g_cssObj.result_scoreHiBlanket, jdgScoreObj[score].pos, `(+`),
253
+ makeCssResultSymbol(`lbl${jdgScoreObj[score].id}LS`, C_RLT_HIDIF_X, g_cssObj.result_scoreHi, jdgScoreObj[score].pos, 0, C_ALIGN_RIGHT),
254
+ makeCssResultSymbol(`lbl${jdgScoreObj[score].id}L2`, C_RLT_BRACKET_R, g_cssObj.result_scoreHiBlanket, jdgScoreObj[score].pos, `)`),
255
+ ));
256
+
257
+ } else {
258
+ resultWindow.appendChild(makeCssResultSymbol(`lblAutoView`, 215, g_cssObj.result_noRecord, 4, `(No Record)`));
259
+ const lblAutoView = document.getElementById(`lblAutoView`);
260
+ lblAutoView.style.fontSize = wUnit(20);
261
+ }
262
+
263
+ // ゲージ推移グラフの描画
264
+ const gaugeTransitionWindow = createEmptySprite(divRoot, `gaugeTransitionWindow`, g_windowObj.gaugeTransition, g_cssObj.result_PlayDataWindow);
265
+ for (let j = 0; j < 2; j++) {
266
+ const canvas = document.createElement(`canvas`);
267
+ canvas.id = `graphGaugeTransition${j > 0 ? j + 1 : ``}`;
268
+ canvas.width = g_limitObj.gaugeTransitionWidth * g_dpr;
269
+ canvas.height = g_limitObj.gaugeTransitionHeight * g_dpr;
270
+ canvas.style.width = wUnit(g_limitObj.gaugeTransitionWidth);
271
+ canvas.style.height = wUnit(g_limitObj.gaugeTransitionHeight);
272
+ canvas.getContext(`2d`).scale(g_dpr, g_dpr);
273
+ canvas.style.left = wUnit(0);
274
+ canvas.style.top = wUnit(0);
275
+ canvas.style.position = `absolute`;
276
+ if (j > 0) {
277
+ canvas.style.pointerEvents = C_DIS_NONE;
278
+ }
279
+ gaugeTransitionWindow.appendChild(canvas);
280
+ }
281
+
282
+ multiAppend(divRoot,
283
+ createCss2Button(`btnGaugeTransition`, `i`, () => true, {
284
+ x: g_sWidth / 2 - 250, y: 185, w: 30, h: 30, siz: g_limitObj.jdgCharaSiz,
285
+ resetFunc: () => changeGaugeTransition(), cxtFunc: () => changeGaugeTransition(),
286
+ }, g_cssObj.button_Mini),
287
+ );
288
+ multiAppend(gaugeTransitionWindow,
289
+ createCss2Button(`btnGaugeTrL`, `<`, () => true, {
290
+ x: -45, y: 35, w: 20, h: 30, siz: g_limitObj.jdgCharaSiz,
291
+ resetFunc: () => moveCursor(keyIsShift() ? -10 : -1),
292
+ }, g_cssObj.button_Setting),
293
+ createCss2Button(`btnGaugeTrR`, `>`, () => true, {
294
+ x: -25, y: 35, w: 20, h: 30, siz: g_limitObj.jdgCharaSiz,
295
+ resetFunc: () => moveCursor(keyIsShift() ? 10 : 1),
296
+ }, g_cssObj.button_Setting),
297
+ );
298
+ g_stateObj.gaugeTransitionViewFlg = false;
299
+
300
+ const changeGaugeTransition = () => {
301
+ if (g_stateObj.gaugeTransitionViewFlg) {
302
+ resultWindow.style.opacity = `1`;
303
+ gaugeTransitionWindow.style.visibility = `hidden`;
304
+ g_stateObj.gaugeTransitionViewFlg = false;
305
+ } else {
306
+ resultWindow.style.opacity = `0.3`;
307
+ gaugeTransitionWindow.style.visibility = `visible`;
308
+ g_stateObj.gaugeTransitionViewFlg = true;
309
+ }
310
+ };
311
+
312
+ const startFrame = g_detailObj.startFrame[g_stateObj.scoreId];
313
+ let playingFrame = g_detailObj.playingFrameWithBlank[g_stateObj.scoreId];
314
+ if (playingFrame <= 0) {
315
+ playingFrame = 1;
316
+ }
317
+ const transitionObj = { frame: [0], life: [g_workObj.lifeInit] };
318
+
319
+ const frame = transitionObj.frame;
320
+ const life = transitionObj.life;
321
+ const transitionData = g_resultObj.gaugeTransition;
322
+
323
+ for (let i = 0; i < transitionData?.length; i++) {
324
+ if (i === 0 || transitionData[i - 1][1] !== transitionData[i][1]) {
325
+ frame.push(transitionData[i][0] - startFrame);
326
+ life.push(transitionData[i][1]);
327
+ }
328
+ }
329
+
330
+ frame.push(playingFrame);
331
+ life.push(life.at(-1));
332
+
333
+ // グラフ本体の描画
334
+ const context = document.getElementById(`graphGaugeTransition`).getContext(`2d`);
335
+ context.lineWidth = 2;
336
+
337
+ let preX, preY;
338
+ const borderY = (g_limitObj.gaugeTransitionHeight - 2) - g_workObj.lifeBorder * (g_limitObj.gaugeTransitionHeight - 2) / g_headerObj.maxLifeVal + 1;
339
+
340
+ for (let i = 0; i < frame.length; i++) {
341
+ const x = frame[i] * g_limitObj.gaugeTransitionWidth / playingFrame;
342
+ const y = (g_limitObj.gaugeTransitionHeight - 2) - life[i] * (g_limitObj.gaugeTransitionHeight - 2) / g_headerObj.maxLifeVal + 1;
343
+
344
+ if (i === 0) {
345
+ context.beginPath();
346
+ context.moveTo(x, y);
347
+ } else {
348
+ context.moveTo(preX, preY);
349
+ context.lineTo(x, preY);
350
+
351
+ if (life[i - 1] === 0 && life[i] === 0) {
352
+ context.strokeStyle = g_graphColorObj.failed;
353
+
354
+ } else if (life[i - 1] >= g_workObj.lifeBorder && life[i] >= g_workObj.lifeBorder) {
355
+ context.lineTo(x, y);
356
+ context.strokeStyle = g_graphColorObj.clear;
357
+
358
+ } else if (life[i - 1] < g_workObj.lifeBorder && life[i] >= g_workObj.lifeBorder) {
359
+ context.lineTo(x, borderY);
360
+ context.strokeStyle = g_graphColorObj.failed;
361
+ context.stroke();
362
+ context.beginPath();
363
+ context.moveTo(x, borderY);
364
+ context.lineTo(x, y);
365
+ context.strokeStyle = g_graphColorObj.clear;
366
+
367
+ } else if (life[i - 1] >= g_workObj.lifeBorder && life[i] < g_workObj.lifeBorder) {
368
+ context.lineTo(x, borderY);
369
+ context.strokeStyle = g_graphColorObj.clear;
370
+ context.stroke();
371
+ context.beginPath();
372
+ context.moveTo(x, borderY);
373
+ context.lineTo(x, y);
374
+ context.strokeStyle = g_graphColorObj.failed;
375
+
376
+ } else {
377
+ context.lineTo(x, y);
378
+ context.strokeStyle = g_graphColorObj.failed;
379
+ }
380
+
381
+ context.stroke();
382
+ context.beginPath();
383
+ }
384
+ preX = x;
385
+ preY = y;
386
+ }
387
+
388
+ let cursorFrame = 0; // 現在のカーソル位置(frame)
389
+ const moveCursor = (sec = 1) => {
390
+ cursorFrame = Math.max(0, Math.min(playingFrame, cursorFrame + sec * g_fps));
391
+ drawOverlay();
392
+ };
393
+
394
+ // 既存のグラフの上から縦線と時間を重ねる
395
+ const drawOverlay = () => {
396
+
397
+ const canvas = document.getElementById(`graphGaugeTransition2`);
398
+ const ctx = canvas.getContext(`2d`);
399
+ const [w, h] = [parseInt(canvas.style.width), parseInt(canvas.style.height)];
400
+ const x = cursorFrame / playingFrame * w;
401
+ ctx.clearRect(0, 0, w, h);
402
+
403
+ // 縦線
404
+ ctx.beginPath();
405
+ ctx.moveTo(x, 0);
406
+ ctx.lineTo(x, h);
407
+ ctx.strokeStyle = "#009999";
408
+ ctx.lineWidth = 1.5;
409
+ ctx.stroke();
410
+
411
+ // 時間表示
412
+ const timer = transFrameToTimer(cursorFrame + startFrame);
413
+ ctx.font = `14px ${getBasicFont()}`;
414
+ ctx.fillStyle = "#009999";
415
+ ctx.textAlign = x > w * 0.8 ? C_ALIGN_RIGHT : C_ALIGN_LEFT;
416
+ ctx.fillText(
417
+ `${timer}`,
418
+ x > w * 0.8 ? x - 5 : x + 5,
419
+ g_limitObj.gaugeTransitionHeight - 35
420
+ );
421
+ };
422
+ drawOverlay();
423
+
424
+ // ユーザカスタムイベント(初期)
425
+ const currentDateTime = new Date().toLocaleString();
426
+ safeExecuteCustomHooks(`g_customJsObj.result`, g_customJsObj.result);
427
+
428
+ if (highscorePreCondition) {
429
+
430
+ // 古いキー定義の情報を検索
431
+ const relatedKeys = Object.entries(g_keyObj.keyTransPattern)
432
+ .filter(([key, value]) => value === g_headerObj.keyLabels[g_stateObj.scoreId])
433
+ .map(([key]) => key);
434
+
435
+ // 古いキー定義のスコアデータを現行キー定義に移行
436
+ for (const legacyKey of relatedKeys) {
437
+ let tmpScoreName = getStorageKeyName(
438
+ legacyKey, transKeyName, assistFlg, mirrorName, g_stateObj.scoreId
439
+ );
440
+ const src = g_localStorage.highscores?.[tmpScoreName];
441
+ if (!hasVal(src)) {
442
+ continue;
443
+ }
444
+
445
+ // 現行キー定義にスコアデータが存在しない場合、移行元のスコアデータをコピー
446
+ if (!hasVal(g_localStorage.highscores?.[scoreName])) {
447
+ g_localStorage.highscores[scoreName] = structuredClone(src);
448
+ }
449
+
450
+ // 古いキー定義は見つかった最初の1件のみ移行し、以降は削除
451
+ delete g_localStorage.highscores[tmpScoreName];
452
+ }
453
+
454
+ Object.keys(jdgScoreObj).filter(judge => judge !== ``)
455
+ .forEach(judge => highscoreDfObj[judge] = g_resultObj[judge] -
456
+ (scoreName in g_localStorage.highscores ? g_localStorage.highscores[scoreName][judge] : 0));
457
+
458
+ if (g_stateObj.dataSaveFlg) {
459
+
460
+ const setScoreData = () => {
461
+ g_localStorage.highscores[scoreName].dateTime = currentDateTime;
462
+ g_localStorage.highscores[scoreName].rankMark = rankMark;
463
+ g_localStorage.highscores[scoreName].rankColor = rankColor;
464
+ g_localStorage.highscores[scoreName].playStyle = settingData.playStyleData;
465
+
466
+ g_localStorage.highscores[scoreName].fast = g_resultObj.fast;
467
+ g_localStorage.highscores[scoreName].slow = g_resultObj.slow;
468
+ g_localStorage.highscores[scoreName].adj = estimatedAdj;
469
+ g_localStorage.highscores[scoreName].excessive = g_stateObj.excessive === C_FLG_ON ?
470
+ g_resultObj.excessive : C_FLG_HYPHEN;
471
+
472
+ if (g_presetObj.resultVals !== undefined) {
473
+ Object.keys(g_presetObj.resultVals).forEach(key =>
474
+ g_localStorage.highscores[scoreName][g_presetObj.resultVals[key]] = g_resultObj[g_presetObj.resultVals[key]]);
475
+ }
476
+ };
477
+
478
+ // All Perfect時(かつスコアが同一時)はFast+Slowが最小のときに更新処理を行う
479
+ if (rankMark === g_rankObj.rankMarkAllPerfect &&
480
+ g_localStorage.highscores[scoreName]?.score === g_resultObj.score) {
481
+ if (g_localStorage.highscores[scoreName].fast === undefined ||
482
+ g_localStorage.highscores[scoreName].fast + g_localStorage.highscores[scoreName].slow >
483
+ g_resultObj.fast + g_resultObj.slow) {
484
+ setScoreData();
485
+ g_localStorage.highscores[scoreName].score = g_resultObj.score;
486
+ }
487
+ }
488
+
489
+ // ハイスコア更新時処理
490
+ if (highscoreDfObj.score > 0) {
491
+ if (g_localStorage.highscores[scoreName] === undefined) {
492
+ g_localStorage.highscores[scoreName] = {};
493
+ }
494
+ Object.keys(jdgScoreObj).filter(judge => judge !== ``)
495
+ .forEach(judge => g_localStorage.highscores[scoreName][judge] = g_resultObj[judge]);
496
+ setScoreData();
497
+ }
498
+
499
+ // クリアランプ点灯処理
500
+ if (![``, `failed`, `cleared`].includes(g_resultObj.spState)) {
501
+ g_localStorage.highscores[scoreName][g_resultObj.spState] = true;
502
+ }
503
+ const isGameCompleted = !g_gameOverFlg && g_finishFlg;
504
+ const hasValidAccuracy = g_workObj.requiredAccuracy !== `----`;
505
+ if (isGameCompleted && hasValidAccuracy && allArrowsPlayed) {
506
+ if (g_localStorage.highscores[scoreName].clearLamps === undefined) {
507
+ g_localStorage.highscores[scoreName].clearLamps = [];
508
+ }
509
+ g_localStorage.highscores[scoreName].clearLamps =
510
+ makeDedupliArray(g_localStorage.highscores[scoreName].clearLamps, [g_stateObj.gauge]);
511
+ }
512
+ localStorage.setItem(g_localStorageUrl, JSON.stringify(g_localStorage));
513
+ }
514
+
515
+ // ハイスコア差分値適用、ハイスコア部分作成
516
+ Object.keys(jdgScoreObj).forEach(score => {
517
+ const jdgScore = jdgScoreObj[score];
518
+ if (score === `score`) {
519
+ multiAppend(resultWindow,
520
+ makeCssResultSymbol(`lbl${jdgScore.id}L1`, C_RLT_BRACKET_L, `${highscoreDfObj.score > 0 ? g_cssObj.result_scoreHiPlus : g_cssObj.result_scoreHiBlanket}`,
521
+ jdgScore.pos, `(${highscoreDfObj[score] >= 0 ? "+" : "-"}`),
522
+ makeCssResultSymbol(`lbl${jdgScore.id}LS`, C_RLT_HIDIF_X, `${highscoreDfObj.score > 0 ? g_cssObj.result_scoreHiPlus : g_cssObj.result_scoreHi}`,
523
+ jdgScore.pos, Math.abs(highscoreDfObj[score]), C_ALIGN_RIGHT),
524
+ makeCssResultSymbol(`lbl${jdgScore.id}L2`, C_RLT_BRACKET_R, `${highscoreDfObj.score > 0 ? g_cssObj.result_scoreHiPlus : g_cssObj.result_scoreHiBlanket}`,
525
+ jdgScore.pos, `)`),
526
+ );
527
+ } else {
528
+ document.getElementById(`lbl${jdgScore.id}L1`).textContent = `(${highscoreDfObj[score] >= 0 ? "+" : "-"}`;
529
+ document.getElementById(`lbl${jdgScore.id}LS`).textContent = Math.abs(highscoreDfObj[score]);
530
+ }
531
+ });
532
+
533
+ }
534
+
535
+ // X (Twitter)用リザルト
536
+ // スコアを上塗りする可能性があるため、カスタムイベント後に配置
537
+ const hashTag = (hasVal(g_headerObj.hashTag) ? ` ${g_headerObj.hashTag}` : ``);
538
+ const keyUnitName = getStgDetailName(getKeyUnitName(g_keyObj.currentKey));
539
+ const keyUnitAbbName = keyUnitName.slice(0, 1) || ``;
540
+ let tweetDifData = `${getKeyName(g_headerObj.keyLabels[g_stateObj.scoreId])}${transKeyName}${getStgDetailName(keyUnitAbbName + '-')}${g_headerObj.difLabels[g_stateObj.scoreId]}${assistFlg}`;
541
+ if (g_stateObj.shuffle !== `OFF`) {
542
+ tweetDifData += `:${shuffleName}`;
543
+ }
544
+ const twiturl = new URL(g_localStorageUrl);
545
+ twiturl.searchParams.append(`scoreId`, g_stateObj.scoreId);
546
+ const baseTwitUrl = g_isLocal ? `` : `${twiturl.toString()}`.replace(/[\t\n]/g, ``);
547
+
548
+ const tweetExcessive = (g_stateObj.excessive === C_FLG_ON) ? `(+${g_resultObj.excessive})` : ``;
549
+
550
+ let tweetFrzJdg = ``;
551
+ let tweetMaxCombo = `${g_resultObj.maxCombo}`;
552
+ if (g_allFrz > 0) {
553
+ tweetFrzJdg = `${g_resultObj.kita}-${g_resultObj.iknai}`;
554
+ tweetMaxCombo += `-${g_resultObj.fmaxCombo}`;
555
+ }
556
+
557
+ const resultParams = {
558
+ tuning: g_headerObj.tuning,
559
+ highscore: g_resultObj,
560
+ playStyleData: settingData.playStyleData,
561
+ hashTag, musicTitle, tweetDifData, rankMark,
562
+ tweetExcessive, tweetFrzJdg, tweetMaxCombo, baseTwitUrl
563
+ };
564
+ let tweetResultTmp = makeResultText(g_headerObj.resultFormat, resultParams);
565
+ let resultCommonTmp = makeResultText(g_templateObj.resultFormatDf, resultParams);
566
+
567
+ if (g_presetObj.resultVals !== undefined) {
568
+ Object.keys(g_presetObj.resultVals).forEach(key =>
569
+ tweetResultTmp = tweetResultTmp.split(`[${key}]`).join(g_resultObj[g_presetObj.resultVals[key]]));
570
+ }
571
+ const resultText = `${unEscapeHtml(tweetResultTmp)}`;
572
+ const tweetResult = `${g_linkObj.x}?text=${encodeURIComponent(resultText)}`;
573
+
574
+ /**
575
+ * リザルト画像をCanvasで作成しクリップボードへコピー
576
+ * @param {string} _msg
577
+ */
578
+ const copyResultImageData = _msg => {
579
+ const tmpDiv = createEmptySprite(divRoot, `tmpDiv`, { x: 0, y: 0, w: g_sWidth, h: g_sHeight, pointerEvents: C_DIS_AUTO });
580
+ tmpDiv.style.background = `#000000cc`;
581
+ const canvas = document.createElement(`canvas`);
582
+ const artistName = g_headerObj.artistNames[g_headerObj.musicNos[g_stateObj.scoreId]] || g_headerObj.artistName;
583
+ const logicalWidth = 400;
584
+ const logicalHeight = g_sHeight - 90;
585
+ const flapWidth = 370;
586
+
587
+ canvas.id = `resultImage`;
588
+ canvas.width = logicalWidth * g_dpr;
589
+ canvas.height = logicalHeight * g_dpr;
590
+ canvas.style.width = wUnit(logicalWidth);
591
+ canvas.style.height = wUnit(logicalHeight);
592
+ canvas.style.left = wUnit((g_sWidth - parseFloat(canvas.style.width)) / 2);
593
+ canvas.style.top = wUnit(20);
594
+ canvas.style.position = `absolute`;
595
+
596
+ const context = canvas.getContext(`2d`);
597
+ context.scale(g_dpr, g_dpr);
598
+ const drawText = (_text, { x = 30, dy = 0, hy, siz = 15, color = `#cccccc`, align = C_ALIGN_LEFT, font } = {}) => {
599
+ context.font = `${wUnit(siz)} ${getBasicFont(font)}`;
600
+ context.fillStyle = color;
601
+ context.textAlign = align;
602
+ context.fillText(_text, x, 35 + hy * 18 + dy);
603
+ };
604
+ makeBgCanvas(context, { w: logicalWidth, h: logicalHeight });
605
+
606
+ drawText(`R`, { dy: -5, hy: 0, siz: 40, color: `#9999ff` });
607
+ drawText(`ESULT`, { x: 57, dy: -5, hy: 0, siz: 25 });
608
+ drawText(`${g_lblNameObj.dancing}${g_lblNameObj.star}${g_lblNameObj.onigiri}`,
609
+ { x: 280, dy: -15, hy: 0, siz: 20, color: `#999999`, align: C_ALIGN_CENTER });
610
+ drawText(unEscapeHtml(mTitleForView[0]), { hy: 1 });
611
+ drawText(unEscapeHtml(mTitleForView[1]), { hy: 2 });
612
+ drawText(`${getEmojiForCanvas(g_emojiObj.memo)} ${unEscapeHtml(g_headerObj.tuning)} / ${getEmojiForCanvas(g_emojiObj.musical)} ${unEscapeHtml(artistName)}`,
613
+ { hy: mTitleForView[1] !== `` ? 3 : 2, siz: 12 });
614
+ drawText(unEscapeHtml(settingData.difDataForImage), { hy: 4, siz: getFontSize2(settingData.difDataForImage, flapWidth) });
615
+
616
+ if (settingData.playStyleData.length > 60) {
617
+ const strs = styleStr.split(`<br>`);
618
+ drawText(strs[0], { hy: 5, siz: getFontSize2(strs[0], flapWidth) });
619
+ drawText(strs[1], { hy: 6, siz: getFontSize2(strs[1], flapWidth) });
620
+ } else {
621
+ drawText(settingData.playStyleData, { hy: 5, siz: getFontSize2(settingData.playStyleData, flapWidth, { maxSiz: 15 }) });
622
+ }
623
+ Object.keys(jdgScoreObj).forEach(score => {
624
+ drawText(g_lblNameObj[`j_${score}`], { hy: 7 + jdgScoreObj[score].pos, color: jdgScoreObj[score].dfColor });
625
+ drawText(g_resultObj[score], { x: 200, hy: 7 + jdgScoreObj[score].pos, align: C_ALIGN_RIGHT });
626
+ });
627
+
628
+ if (highscorePreCondition) {
629
+ drawText(`(${highscoreDfObj.score >= 0 ? '+' : '-'} ${Math.abs(highscoreDfObj.score)})`,
630
+ { x: 206, hy: 18, color: highscoreDfObj.score > 0 ? `#ffff99` : `#cccccc`, align: C_ALIGN_RIGHT });
631
+ }
632
+
633
+ if (g_stateObj.autoAll === C_FLG_OFF) {
634
+ drawText(g_lblNameObj.j_fast, { x: 240, hy: 7, color: `#ff9966` });
635
+ drawText(g_resultObj.fast, { x: 360, hy: 7, align: C_ALIGN_RIGHT });
636
+ drawText(g_lblNameObj.j_slow, { x: 240, hy: 8, color: `#ccccff` });
637
+ drawText(g_resultObj.slow, { x: 360, hy: 8, align: C_ALIGN_RIGHT });
638
+ if (estimatedAdj !== ``) {
639
+ drawText(g_lblNameObj.j_adj, { x: 240, hy: 9, color: `#99ff99` });
640
+ drawText(getDiffFrame(estimatedAdj), { x: 360, hy: 9, align: C_ALIGN_RIGHT });
641
+ }
642
+ if (g_stateObj.excessive === C_FLG_ON) {
643
+ drawText(g_lblNameObj.j_excessive, { x: 240, hy: 10, color: `#ffff99` });
644
+ drawText(g_resultObj.excessive, { x: 360, hy: 10, align: C_ALIGN_RIGHT });
645
+ }
646
+ g_headerObj.resultValsView
647
+ .filter(key => hasVal(g_resultObj[g_presetObj.resultVals[key]]))
648
+ .forEach((key, j) => {
649
+ drawText(g_presetObj.resultVals[key], { x: 240, hy: j + 12, color: `#ffffff` });
650
+ drawText(g_resultObj[g_presetObj.resultVals[key]], { x: 360, hy: j + 12, align: C_ALIGN_RIGHT });
651
+ });
652
+ }
653
+ drawText(rankMark, { x: 240, hy: 18, siz: 50, color: rankColor, font: `"Bookman Old Style"` });
654
+ drawText(baseTwitUrl, { hy: 19, siz: 8 });
655
+ drawText(currentDateTime, { hy: 20 });
656
+
657
+ tmpDiv.appendChild(canvas);
658
+
659
+ const viewResultImage = () => {
660
+ if (document.getElementById(`tmpClose`) === null) {
661
+ divRoot.oncontextmenu = () => true;
662
+ makeLinkButton(tmpDiv, `Tmp`);
663
+ tmpDiv.appendChild(createCss2Button(`tmpClose`, g_lblNameObj.b_close, () => true, {
664
+ ...g_lblPosObj.btnRsCopyClose,
665
+ resetFunc: () => {
666
+ tmpDiv.removeChild(canvas);
667
+ divRoot.removeChild(tmpDiv);
668
+ divRoot.oncontextmenu = () => false;
669
+ },
670
+ }, g_cssObj.button_Back));
671
+ tmpDiv.appendChild(createDescDiv(`resultImageDesc`, g_lblNameObj.resultImageDesc));
672
+ }
673
+ };
674
+
675
+ try {
676
+ if (ClipboardItem === undefined) {
677
+ throw new Error(`error`);
678
+ }
679
+ if (keyIsShift()) {
680
+ viewResultImage();
681
+ } else {
682
+ // Canvas の内容を PNG 画像として取得
683
+ canvas.toBlob(async blob => {
684
+ try {
685
+ if (blob === null) {
686
+ throw new Error(`Failed to create result image blob.`);
687
+ }
688
+ await navigator.clipboard.write([
689
+ new ClipboardItem({ 'image/png': blob })
690
+ ]);
691
+ tmpDiv.removeChild(canvas);
692
+ divRoot.removeChild(tmpDiv);
693
+ makeInfoWindow(_msg, `leftToRightFade`);
694
+ } catch {
695
+ viewResultImage();
696
+ }
697
+ });
698
+ }
699
+
700
+ } catch (err) {
701
+ // 画像をクリップボードへコピーできないときは代替で画像保存可能な画面を表示
702
+ viewResultImage();
703
+ }
704
+ };
705
+
706
+ /**
707
+ * 音源、ループ処理の停止
708
+ * @param {string} _id
709
+ * @param {string} _name
710
+ * @param {object} _posObj
711
+ * @param {Function} _func
712
+ * @param {...any} _cssClass
713
+ * @returns {HTMLDivElement}
714
+ */
715
+ const resetCommonBtn = (_id, _name, _posObj, _func, _cssClass) =>
716
+ createCss2Button(_id, _name, () => {
717
+ if (g_finishFlg) {
718
+ g_audio.pause();
719
+ }
720
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
721
+ g_timerHandler.clearTimeout(g_timeoutEvtResultId);
722
+ }, { ..._posObj, resetFunc: () => _func() }, _cssClass);
723
+
724
+ /**
725
+ * 外部リンクボタンを作成
726
+ * @param {object} _div
727
+ * @param {string} _param
728
+ */
729
+ const makeLinkButton = (_div = divRoot, _param = ``) => {
730
+ multiAppend(_div,
731
+ // リザルトデータをX (Twitter)へ転送
732
+ createCss2Button(`btnTweet${_param}`, g_lblNameObj.b_tweet, () => true, {
733
+ ...g_lblPosObj.btnRsTweet, resetFunc: () => openLink(tweetResult),
734
+ }, g_cssObj.button_Tweet),
735
+
736
+ // Discordへのリンク
737
+ createCss2Button(`btnGitter${_param}`, g_lblNameObj.b_gitter, () => true, {
738
+ ...g_lblPosObj.btnRsGitter, resetFunc: () => openLink(g_linkObj.discord),
739
+ }, g_cssObj.button_Discord),
740
+ );
741
+ };
742
+
743
+ // ボタン描画
744
+ multiAppend(divRoot,
745
+
746
+ // タイトル画面へ戻る
747
+ resetCommonBtn(`btnBack`, g_lblNameObj.b_back, g_lblPosObj.btnRsBack, titleInit, g_cssObj.button_Back),
748
+
749
+ // リザルトデータをクリップボードへコピー
750
+ createCss2Button(`btnCopy`, g_lblNameObj.b_copy, () =>
751
+ copyTextToClipboard(keyIsShift() ?
752
+ unEscapeHtml(resultCommonTmp) : resultText, g_msgInfoObj.I_0001),
753
+ g_lblPosObj.btnRsCopy, g_cssObj.button_Setting),
754
+ );
755
+ makeLinkButton();
756
+ multiAppend(divRoot,
757
+ // リトライ
758
+ resetCommonBtn(`btnRetry`, g_lblNameObj.b_retry, g_lblPosObj.btnRsRetry, loadMusic, g_cssObj.button_Reset),
759
+
760
+ createCss2Button(`btnCopyImage`, g_emojiObj.camera, () => true, {
761
+ ...g_lblPosObj.btnRsCopyImage, resetFunc: () => copyResultImageData(g_msgInfoObj.I_0001),
762
+ }, g_cssObj.button_Default_NoColor),
763
+ );
764
+
765
+ // マスクスプライトを作成
766
+ const makeResultSprite = createMultipleSprite(`maskResultSprite`, g_headerObj.maskResultMaxDepth);
767
+ makeResultSprite.style.pointerEvents = g_headerObj.maskresultButton ? C_DIS_AUTO : C_DIS_NONE;
768
+
769
+ // リザルトモーションの0フレーム対応
770
+ g_animationData.filter(sprite => g_scoreObj[`${sprite}ResultFrameNum`] === 0 && g_headerObj[`${sprite}ResultData`]?.[0] !== undefined)
771
+ .forEach(sprite => {
772
+ g_scoreObj[`${sprite}ResultFrameNum`] = g_animationFunc.draw[sprite](0, `result`, sprite);
773
+ g_headerObj[`${sprite}ResultData`][0] = undefined;
774
+ });
775
+
776
+ /**
777
+ * タイトルのモーション設定
778
+ */
779
+ const flowResultTimeline = () => {
780
+
781
+ // ユーザカスタムイベント(フレーム毎)
782
+ safeExecuteCustomHooks(`g_customJsObj.resultEnterFrame`, g_customJsObj.resultEnterFrame);
783
+
784
+ // 背景・マスクモーション、スキン変更
785
+ drawTitleResultMotion(g_currentPage);
786
+
787
+ // リザルト画面移行後のフェードアウト処理
788
+ if (g_scoreObj.fadeOutFrame >= g_scoreObj.frameNum) {
789
+ if (g_scoreObj.frameNum >= g_scoreObj.fullFrame) {
790
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
791
+ }
792
+ g_scoreObj.frameNum++;
793
+ } else {
794
+ const tmpVolume = (g_audio.volume - (3 * g_stateObj.volume / 100 * C_FRM_AFTERFADE / g_scoreObj.fadeOutTerm) / 1000);
795
+ if (tmpVolume < 0) {
796
+ g_audio.volume = 0;
797
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
798
+ } else {
799
+ g_audio.volume = tmpVolume;
800
+ }
801
+ }
802
+
803
+ thisTime = performance.now();
804
+ buffTime = thisTime - resultStartTime - g_scoreObj.resultFrameNum * 1000 / g_fps;
805
+
806
+ g_scoreObj.resultFrameNum++;
807
+ g_animationData.forEach(sprite => g_scoreObj[`${sprite}ResultFrameNum`]++);
808
+ g_timeoutEvtResultId = g_timerHandler.setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
809
+ };
810
+ flowResultTimeline();
811
+
812
+ // キー操作イベント(デフォルト)
813
+ setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
814
+ document.oncontextmenu = () => true;
815
+
816
+ safeExecuteCustomHooks(`g_skinJsObj.result`, g_skinJsObj.result);
817
+ };
818
+
819
+ /**
820
+ * 選択した設定の情報を取得
821
+ * @param {boolean} _orgShuffleFlg
822
+ * @returns {object}
823
+ */
824
+ const getSelectedSettingList = (_orgShuffleFlg) => {
825
+
826
+ const transKeyName = getTransKeyName();
827
+ /**
828
+ * プレイスタイルのカスタム有無
829
+ * @param {string} _flg
830
+ * @param {string|boolean} _defaultSet デフォルト値
831
+ * @param {string} _displayText
832
+ * @returns {string}
833
+ */
834
+ const withOptions = (_flg, _defaultSet, _displayText = _flg) =>
835
+ (_flg !== _defaultSet ? getStgDetailName(_displayText) : ``);
836
+
837
+ const withDisplays = (_flg, _defaultSet, _displayText = _flg) =>
838
+ (_flg !== _defaultSet
839
+ ? getStgDetailName(_displayText) + (_flg === C_FLG_OFF ? `` : ` : ${getStgDetailName(_flg)}`) : ``);
840
+
841
+ // 譜面名の組み立て処理 (Ex: 9Akey / Normal-Leftless (maker) [X-Mirror])
842
+ const keyUnitName = getStgDetailName(getKeyUnitName(g_keyObj.currentKey));
843
+ const difDatas = [
844
+ `${getKeyName(g_headerObj.keyLabels[g_stateObj.scoreId])}${transKeyName} ${keyUnitName} / ${g_headerObj.difLabels[g_stateObj.scoreId]}`,
845
+ `${withOptions(g_autoPlaysBase.includes(g_stateObj.autoPlay), true, `-${getStgDetailName(g_stateObj.autoPlay)}${getStgDetailName('less')}`)}`,
846
+ `${withOptions(g_headerObj.makerView, false, `(${g_headerObj.creatorNames[g_stateObj.scoreId]})`)}`,
847
+ `${withOptions(g_stateObj.shuffle, C_FLG_OFF, `[${getShuffleName()}]`)}`
848
+ ];
849
+ let difData = difDatas.filter(value => value !== ``).join(` `);
850
+ const difDataForImage = difDatas.filter((value, j) => value !== `` && j !== 2).join(` `);
851
+
852
+ // 設定の組み立て処理 (Ex: 4x, Brake, Reverse, Sudden+, NoRecovery)
853
+ let playStyleData = [
854
+ `${g_stateObj.speed}${g_lblNameObj.multi}`,
855
+ withOptions(g_stateObj.motion, C_FLG_OFF),
856
+ `${withOptions(g_stateObj.reverse, C_FLG_OFF,
857
+ getStgDetailName(g_stateObj.scroll !== C_FLG_HYPHEN ? 'R-' : C_FLG_REVERSE))}${withOptions(g_stateObj.scroll, C_FLG_HYPHEN)}`,
858
+ withOptions(g_stateObj.appearance, `Visible`) +
859
+ ((g_appearanceRanges.includes(g_stateObj.appearance) && g_stateObj.filterLock === C_FLG_ON) ? `(${g_hidSudObj.filterPos}%)` : ``),
860
+ withOptions(g_stateObj.gauge, g_settings.gauges[0]),
861
+ withOptions(g_stateObj.playWindow, `Default`,
862
+ `${getStgDetailName(g_stateObj.playWindowType === C_FLG_REVERSE2 ? `R-` : ``)}${getStgDetailName(g_stateObj.playWindow)}`),
863
+ withOptions(g_stateObj.stepArea, `Default`),
864
+ withOptions(g_stateObj.frzReturn, C_FLG_OFF,
865
+ `FR:${getStgDetailName(g_stateObj.frzReturn)}(${getStgDetailName(g_stateObj.frzReturnType)})`),
866
+ withOptions(g_stateObj.shaking, C_FLG_OFF),
867
+ withOptions(g_stateObj.effect, C_FLG_OFF),
868
+ [
869
+ withOptions(g_stateObj.camoufrage, C_FLG_OFF, `Cmf:${getStgDetailName(g_stateObj.camoufrage)}`),
870
+ withOptions(g_stateObj.camoufrageType, C_FLG_HYPHEN,
871
+ `${g_stateObj.camoufrage !== C_FLG_OFF ? '' : 'Cmf:'}${getStgDetailName(g_stateObj.camoufrageType)}`)
872
+ ].filter(value => value !== ``).join(`+`),
873
+ withOptions(g_stateObj.swapping, C_FLG_OFF,
874
+ `Swap:${getStgDetailName(g_stateObj.swapping)}${!_orgShuffleFlg && !g_stateObj.swapping.endsWith(`+`) ? getStgDetailName(`(S)`) : ``}`),
875
+ withOptions(g_stateObj.judgRange, `Normal`, `Judg:${getStgDetailName(g_stateObj.judgRange)}`),
876
+ ].filter(value => value !== ``).join(`, `);
877
+
878
+ // Display設定の組み立て処理 (Ex: Step : FlatBar, Judge, Life : OFF)
879
+ let displayData = [
880
+ withDisplays(g_stateObj.d_stepzone, C_FLG_ON, g_lblNameObj.rd_StepZone),
881
+ withDisplays(g_stateObj.d_judgment, C_FLG_ON, g_lblNameObj.rd_Judgment),
882
+ withDisplays(g_stateObj.d_lifegauge, C_FLG_ON, g_lblNameObj.rd_LifeGauge),
883
+ withDisplays(g_stateObj.d_score, C_FLG_ON, g_lblNameObj.rd_Score),
884
+ withDisplays(g_stateObj.d_musicinfo, C_FLG_ON, g_lblNameObj.rd_MusicInfo),
885
+ withDisplays(g_stateObj.d_filterline, C_FLG_ON, g_lblNameObj.rd_FilterLine),
886
+ ].filter(value => value !== ``).join(`, `);
887
+ if (displayData === ``) {
888
+ displayData = getStgDetailName(`All Visible`);
889
+ } else {
890
+ // 表示設定のOFF項目を末尾にまとめる
891
+ const displayList = displayData.split(`, `).sort((a, b) => b.includes(`:`) - a.includes(`:`));
892
+ displayData = displayList.join(`, `);
893
+ if (!displayList.at(-1).includes(`:`)) {
894
+ displayData += ` : ${getStgDetailName(C_FLG_OFF)}`;
895
+ }
896
+ }
897
+
898
+ let display2Data = [
899
+ withDisplays(g_stateObj.d_velocity, C_FLG_ON, g_lblNameObj.rd_Velocity),
900
+ withDisplays(g_stateObj.d_color, C_FLG_ON, g_lblNameObj.rd_Color),
901
+ withDisplays(g_stateObj.d_background, C_FLG_ON, g_lblNameObj.rd_Background),
902
+ withDisplays(g_stateObj.d_arroweffect, C_FLG_ON, g_lblNameObj.rd_ArrowEffect),
903
+ withDisplays(g_stateObj.d_special, C_FLG_ON, g_lblNameObj.rd_Special),
904
+ ].filter(value => value !== ``).join(`, `);
905
+ if (display2Data !== ``) {
906
+ // 表示設定のOFF項目を末尾にまとめる
907
+ const display2List = display2Data.split(`, `).sort((a, b) => b.includes(`:`) - a.includes(`:`));
908
+ display2Data = display2List.join(`, `);
909
+ if (!display2List.at(-1).includes(`:`)) {
910
+ display2Data += ` : ${getStgDetailName(C_FLG_OFF)}`;
911
+ }
912
+ }
913
+
914
+ return { difData, difDataForImage, playStyleData, displayData, display2Data };
915
+ };
916
+
917
+ /**
918
+ * リザルトフォーマットの整形処理
919
+ * @param {string} _format
920
+ * @param {object} object フォーマット置き換え変数群
921
+ * @param {string} object.hashTag ハッシュタグ
922
+ * @param {string} object.musicTitle 曲名
923
+ * @param {string} object.tweetDifData 譜面名
924
+ * @param {string} object.tuning 製作者名
925
+ * @param {string} object.rankMark ランク
926
+ * @param {string} object.playStyleData プレイ設定
927
+ * @param {object} object.highscore ハイスコア(判定別)
928
+ * @param {string} object.tweetExcessive 空押し判定状況
929
+ * @param {string} object.tweetFrzJdg フリーズアロー判定状況
930
+ * @param {string} object.tweetMaxCombo コンボ数状況
931
+ * @param {string} object.baseTwitUrl X投稿用URL
932
+ * @returns {string}
933
+ */
934
+ const makeResultText = (_format, {
935
+ hashTag, musicTitle, tweetDifData, tuning, rankMark, playStyleData,
936
+ highscore, tweetExcessive, tweetFrzJdg, tweetMaxCombo, baseTwitUrl } = {}) =>
937
+ replaceStr(_format, [
938
+ [`[hashTag]`, hashTag],
939
+ [`[musicTitle]`, musicTitle],
940
+ [`[keyLabel]`, tweetDifData],
941
+ [`[maker]`, tuning],
942
+ [`[rank]`, rankMark],
943
+ [`[score]`, highscore?.score],
944
+ [`[playStyle]`, playStyleData],
945
+ [`[arrowJdg]`, `${highscore?.ii}-${highscore?.shakin}-${highscore?.matari}-${highscore?.shobon}-${highscore?.uwan}${tweetExcessive}`],
946
+ [`[frzJdg]`, tweetFrzJdg],
947
+ [`[maxCombo]`, tweetMaxCombo],
948
+ [`[url]`, baseTwitUrl]
949
+ ]);
950
+
951
+ /**
952
+ * 結果表示作成(曲名、オプション)
953
+ * @param {string} _id
954
+ * @param {number} _x
955
+ * @param {string} _class
956
+ * @param {number} _heightPos
957
+ * @param {string} _text
958
+ * @param {string} _align
959
+ * @param {number} [object.w=400]
960
+ * @param {number} [object.siz=g_limitObj.mainSiz]
961
+ * @returns {HTMLDivElement}
962
+ */
963
+ const makeCssResultPlayData = (_id, _x, _class, _heightPos, _text, _align = C_ALIGN_CENTER, { w = 400, siz = g_limitObj.mainSiz } = {}) =>
964
+ createDivCss2Label(_id, _text, {
965
+ x: _x, y: g_limitObj.setMiniSiz * _heightPos, w, h: g_limitObj.setMiniSiz, siz, align: _align,
966
+ }, _class);
967
+
968
+ /**
969
+ * 結果表示作成(キャラクタ)
970
+ * @param {string} _id
971
+ * @param {number} _x
972
+ * @param {string} _class
973
+ * @param {number} _heightPos
974
+ * @param {string} _text
975
+ * @param {string} _align
976
+ * @returns {HTMLDivElement}
977
+ */
978
+ const makeCssResultSymbol = (_id, _x, _class, _heightPos, _text, _align = C_ALIGN_LEFT) =>
979
+ makeCssResultPlayData(_id, _x, _class, _heightPos, _text, _align, { w: 150, siz: g_limitObj.jdgCntsSiz });