danoniplus 50.4.0 → 50.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,961 @@
1
+ /**
2
+ * Dancing☆Onigiri (CW Edition)
3
+ * 結果画面
4
+ * - ページ: result
5
+ *
6
+ * Source by tickle
7
+ * Created : 2026/09/13
8
+ * Revised : 2026/09/15 (v50.5.1)
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
+ gaugeTransitionWindow.appendChild(
267
+ createCanvas(`graphGaugeTransition${j > 0 ? j + 1 : ``}`, {
268
+ w: g_limitObj.gaugeTransitionWidth, h: g_limitObj.gaugeTransitionHeight,
269
+ pointerEvents: j > 0 ? C_DIS_NONE : C_DIS_AUTO,
270
+ })
271
+ );
272
+ }
273
+
274
+ multiAppend(divRoot,
275
+ createCss2Button(`btnGaugeTransition`, `i`, () => true, {
276
+ x: g_sWidth / 2 - 250, y: 185, w: 30, h: 30, siz: g_limitObj.jdgCharaSiz,
277
+ resetFunc: () => changeGaugeTransition(), cxtFunc: () => changeGaugeTransition(),
278
+ }, g_cssObj.button_Mini),
279
+ );
280
+ multiAppend(gaugeTransitionWindow,
281
+ createCss2Button(`btnGaugeTrL`, `<`, () => true, {
282
+ x: -45, y: 35, w: 20, h: 30, siz: g_limitObj.jdgCharaSiz,
283
+ resetFunc: () => moveCursor(keyIsShift() ? -10 : -1),
284
+ }, g_cssObj.button_Setting),
285
+ createCss2Button(`btnGaugeTrR`, `>`, () => true, {
286
+ x: -25, y: 35, w: 20, h: 30, siz: g_limitObj.jdgCharaSiz,
287
+ resetFunc: () => moveCursor(keyIsShift() ? 10 : 1),
288
+ }, g_cssObj.button_Setting),
289
+ );
290
+ g_stateObj.gaugeTransitionViewFlg = false;
291
+
292
+ const changeGaugeTransition = () => {
293
+ if (g_stateObj.gaugeTransitionViewFlg) {
294
+ resultWindow.style.opacity = `1`;
295
+ gaugeTransitionWindow.style.visibility = `hidden`;
296
+ g_stateObj.gaugeTransitionViewFlg = false;
297
+ } else {
298
+ resultWindow.style.opacity = `0.3`;
299
+ gaugeTransitionWindow.style.visibility = `visible`;
300
+ g_stateObj.gaugeTransitionViewFlg = true;
301
+ }
302
+ };
303
+
304
+ const startFrame = g_detailObj.startFrame[g_stateObj.scoreId];
305
+ let playingFrame = g_detailObj.playingFrameWithBlank[g_stateObj.scoreId];
306
+ if (playingFrame <= 0) {
307
+ playingFrame = 1;
308
+ }
309
+ const transitionObj = { frame: [0], life: [g_workObj.lifeInit] };
310
+
311
+ const frame = transitionObj.frame;
312
+ const life = transitionObj.life;
313
+ const transitionData = g_resultObj.gaugeTransition;
314
+
315
+ for (let i = 0; i < transitionData?.length; i++) {
316
+ if (i === 0 || transitionData[i - 1][1] !== transitionData[i][1]) {
317
+ frame.push(transitionData[i][0] - startFrame);
318
+ life.push(transitionData[i][1]);
319
+ }
320
+ }
321
+
322
+ frame.push(playingFrame);
323
+ life.push(life.at(-1));
324
+
325
+ // グラフ本体の描画
326
+ const context = document.getElementById(`graphGaugeTransition`).getContext(`2d`);
327
+ context.lineWidth = 2;
328
+
329
+ let preX, preY;
330
+ const borderY = (g_limitObj.gaugeTransitionHeight - 2) - g_workObj.lifeBorder * (g_limitObj.gaugeTransitionHeight - 2) / g_headerObj.maxLifeVal + 1;
331
+
332
+ for (let i = 0; i < frame.length; i++) {
333
+ const x = frame[i] * g_limitObj.gaugeTransitionWidth / playingFrame;
334
+ const y = (g_limitObj.gaugeTransitionHeight - 2) - life[i] * (g_limitObj.gaugeTransitionHeight - 2) / g_headerObj.maxLifeVal + 1;
335
+
336
+ if (i === 0) {
337
+ context.beginPath();
338
+ context.moveTo(x, y);
339
+ } else {
340
+ context.moveTo(preX, preY);
341
+ context.lineTo(x, preY);
342
+
343
+ if (life[i - 1] === 0 && life[i] === 0) {
344
+ context.strokeStyle = g_graphColorObj.failed;
345
+
346
+ } else if (life[i - 1] >= g_workObj.lifeBorder && life[i] >= g_workObj.lifeBorder) {
347
+ context.lineTo(x, y);
348
+ context.strokeStyle = g_graphColorObj.clear;
349
+
350
+ } else if (life[i - 1] < g_workObj.lifeBorder && life[i] >= g_workObj.lifeBorder) {
351
+ context.lineTo(x, borderY);
352
+ context.strokeStyle = g_graphColorObj.failed;
353
+ context.stroke();
354
+ context.beginPath();
355
+ context.moveTo(x, borderY);
356
+ context.lineTo(x, y);
357
+ context.strokeStyle = g_graphColorObj.clear;
358
+
359
+ } else if (life[i - 1] >= g_workObj.lifeBorder && life[i] < g_workObj.lifeBorder) {
360
+ context.lineTo(x, borderY);
361
+ context.strokeStyle = g_graphColorObj.clear;
362
+ context.stroke();
363
+ context.beginPath();
364
+ context.moveTo(x, borderY);
365
+ context.lineTo(x, y);
366
+ context.strokeStyle = g_graphColorObj.failed;
367
+
368
+ } else {
369
+ context.lineTo(x, y);
370
+ context.strokeStyle = g_graphColorObj.failed;
371
+ }
372
+
373
+ context.stroke();
374
+ context.beginPath();
375
+ }
376
+ preX = x;
377
+ preY = y;
378
+ }
379
+
380
+ let cursorFrame = 0; // 現在のカーソル位置(frame)
381
+ const moveCursor = (sec = 1) => {
382
+ cursorFrame = Math.max(0, Math.min(playingFrame, cursorFrame + sec * g_fps));
383
+ drawOverlay();
384
+ };
385
+
386
+ // 既存のグラフの上から縦線と時間を重ねる
387
+ const drawOverlay = () => {
388
+
389
+ const canvas = document.getElementById(`graphGaugeTransition2`);
390
+ const ctx = canvas.getContext(`2d`);
391
+ const [w, h] = [parseInt(canvas.style.width), parseInt(canvas.style.height)];
392
+ const x = cursorFrame / playingFrame * w;
393
+ ctx.clearRect(0, 0, w, h);
394
+
395
+ // 縦線
396
+ ctx.beginPath();
397
+ ctx.moveTo(x, 0);
398
+ ctx.lineTo(x, h);
399
+ ctx.strokeStyle = "#009999";
400
+ ctx.lineWidth = 1.5;
401
+ ctx.stroke();
402
+
403
+ // 時間表示
404
+ const timer = transFrameToTimer(cursorFrame + startFrame);
405
+ ctx.font = `14px ${getBasicFont()}`;
406
+ ctx.fillStyle = "#009999";
407
+ ctx.textAlign = x > w * 0.8 ? C_ALIGN_RIGHT : C_ALIGN_LEFT;
408
+ ctx.fillText(
409
+ `${timer}`,
410
+ x > w * 0.8 ? x - 5 : x + 5,
411
+ g_limitObj.gaugeTransitionHeight - 35
412
+ );
413
+ };
414
+ drawOverlay();
415
+
416
+ // ユーザカスタムイベント(初期)
417
+ const currentDateTime = new Date().toLocaleString();
418
+ safeExecuteCustomHooks(`g_customJsObj.result`, g_customJsObj.result);
419
+
420
+ if (highscorePreCondition) {
421
+
422
+ // 古いキー定義の情報を検索
423
+ const relatedKeys = Object.entries(g_keyObj.keyTransPattern)
424
+ .filter(([key, value]) => value === g_headerObj.keyLabels[g_stateObj.scoreId])
425
+ .map(([key]) => key);
426
+
427
+ // 古いキー定義のスコアデータを現行キー定義に移行
428
+ for (const legacyKey of relatedKeys) {
429
+ let tmpScoreName = getStorageKeyName(
430
+ legacyKey, transKeyName, assistFlg, mirrorName, g_stateObj.scoreId
431
+ );
432
+ const src = g_localStorage.highscores?.[tmpScoreName];
433
+ if (!hasVal(src)) {
434
+ continue;
435
+ }
436
+
437
+ // 現行キー定義にスコアデータが存在しない場合、移行元のスコアデータをコピー
438
+ if (!hasVal(g_localStorage.highscores?.[scoreName])) {
439
+ g_localStorage.highscores[scoreName] = structuredClone(src);
440
+ }
441
+
442
+ // 古いキー定義は見つかった最初の1件のみ移行し、以降は削除
443
+ delete g_localStorage.highscores[tmpScoreName];
444
+ }
445
+
446
+ Object.keys(jdgScoreObj).filter(judge => judge !== ``)
447
+ .forEach(judge => highscoreDfObj[judge] = g_resultObj[judge] -
448
+ (scoreName in g_localStorage.highscores ? g_localStorage.highscores[scoreName][judge] : 0));
449
+
450
+ if (g_stateObj.dataSaveFlg) {
451
+
452
+ const setScoreData = () => {
453
+ g_localStorage.highscores[scoreName].dateTime = currentDateTime;
454
+ g_localStorage.highscores[scoreName].rankMark = rankMark;
455
+ g_localStorage.highscores[scoreName].rankColor = rankColor;
456
+ g_localStorage.highscores[scoreName].playStyle = settingData.playStyleData;
457
+
458
+ g_localStorage.highscores[scoreName].fast = g_resultObj.fast;
459
+ g_localStorage.highscores[scoreName].slow = g_resultObj.slow;
460
+ g_localStorage.highscores[scoreName].adj = estimatedAdj;
461
+ g_localStorage.highscores[scoreName].excessive = g_stateObj.excessive === C_FLG_ON ?
462
+ g_resultObj.excessive : C_FLG_HYPHEN;
463
+
464
+ if (g_presetObj.resultVals !== undefined) {
465
+ Object.keys(g_presetObj.resultVals).forEach(key =>
466
+ g_localStorage.highscores[scoreName][g_presetObj.resultVals[key]] = g_resultObj[g_presetObj.resultVals[key]]);
467
+ }
468
+ };
469
+
470
+ // All Perfect時(かつスコアが同一時)はFast+Slowが最小のときに更新処理を行う
471
+ if (rankMark === g_rankObj.rankMarkAllPerfect &&
472
+ g_localStorage.highscores[scoreName]?.score === g_resultObj.score) {
473
+ if (g_localStorage.highscores[scoreName].fast === undefined ||
474
+ g_localStorage.highscores[scoreName].fast + g_localStorage.highscores[scoreName].slow >
475
+ g_resultObj.fast + g_resultObj.slow) {
476
+ setScoreData();
477
+ g_localStorage.highscores[scoreName].score = g_resultObj.score;
478
+ }
479
+ }
480
+
481
+ // ハイスコア更新時処理
482
+ if (highscoreDfObj.score > 0) {
483
+ if (g_localStorage.highscores[scoreName] === undefined) {
484
+ g_localStorage.highscores[scoreName] = {};
485
+ }
486
+ Object.keys(jdgScoreObj).filter(judge => judge !== ``)
487
+ .forEach(judge => g_localStorage.highscores[scoreName][judge] = g_resultObj[judge]);
488
+ setScoreData();
489
+ }
490
+
491
+ // クリアランプ点灯処理
492
+ if (![``, `failed`, `cleared`].includes(g_resultObj.spState)) {
493
+ g_localStorage.highscores[scoreName][g_resultObj.spState] = true;
494
+ }
495
+ const isGameCompleted = !g_gameOverFlg && g_finishFlg;
496
+ const hasValidAccuracy = g_workObj.requiredAccuracy !== `----`;
497
+ if (isGameCompleted && hasValidAccuracy && allArrowsPlayed) {
498
+ if (g_localStorage.highscores[scoreName].clearLamps === undefined) {
499
+ g_localStorage.highscores[scoreName].clearLamps = [];
500
+ }
501
+ g_localStorage.highscores[scoreName].clearLamps =
502
+ makeDedupliArray(g_localStorage.highscores[scoreName].clearLamps, [g_stateObj.gauge]);
503
+ }
504
+ localStorage.setItem(g_localStorageUrl, JSON.stringify(g_localStorage));
505
+ }
506
+
507
+ // ハイスコア差分値適用、ハイスコア部分作成
508
+ Object.keys(jdgScoreObj).forEach(score => {
509
+ const jdgScore = jdgScoreObj[score];
510
+ if (score === `score`) {
511
+ multiAppend(resultWindow,
512
+ makeCssResultSymbol(`lbl${jdgScore.id}L1`, C_RLT_BRACKET_L, `${highscoreDfObj.score > 0 ? g_cssObj.result_scoreHiPlus : g_cssObj.result_scoreHiBlanket}`,
513
+ jdgScore.pos, `(${highscoreDfObj[score] >= 0 ? "+" : "-"}`),
514
+ makeCssResultSymbol(`lbl${jdgScore.id}LS`, C_RLT_HIDIF_X, `${highscoreDfObj.score > 0 ? g_cssObj.result_scoreHiPlus : g_cssObj.result_scoreHi}`,
515
+ jdgScore.pos, Math.abs(highscoreDfObj[score]), C_ALIGN_RIGHT),
516
+ makeCssResultSymbol(`lbl${jdgScore.id}L2`, C_RLT_BRACKET_R, `${highscoreDfObj.score > 0 ? g_cssObj.result_scoreHiPlus : g_cssObj.result_scoreHiBlanket}`,
517
+ jdgScore.pos, `)`),
518
+ );
519
+ } else {
520
+ document.getElementById(`lbl${jdgScore.id}L1`).textContent = `(${highscoreDfObj[score] >= 0 ? "+" : "-"}`;
521
+ document.getElementById(`lbl${jdgScore.id}LS`).textContent = Math.abs(highscoreDfObj[score]);
522
+ }
523
+ });
524
+
525
+ }
526
+
527
+ // X (Twitter)用リザルト
528
+ // スコアを上塗りする可能性があるため、カスタムイベント後に配置
529
+ const hashTag = (hasVal(g_headerObj.hashTag) ? ` ${g_headerObj.hashTag}` : ``);
530
+ const keyUnitName = getStgDetailName(getKeyUnitName(g_keyObj.currentKey));
531
+ const keyUnitAbbName = keyUnitName.slice(0, 1) || ``;
532
+ let tweetDifData = `${getKeyName(g_headerObj.keyLabels[g_stateObj.scoreId])}${transKeyName}${getStgDetailName(keyUnitAbbName + '-')}${g_headerObj.difLabels[g_stateObj.scoreId]}${assistFlg}`;
533
+ if (g_stateObj.shuffle !== `OFF`) {
534
+ tweetDifData += `:${shuffleName}`;
535
+ }
536
+ const twiturl = new URL(g_localStorageUrl);
537
+ twiturl.searchParams.append(`scoreId`, g_stateObj.scoreId);
538
+ const baseTwitUrl = g_isLocal ? `` : `${twiturl.toString()}`.replace(/[\t\n]/g, ``);
539
+
540
+ const tweetExcessive = (g_stateObj.excessive === C_FLG_ON) ? `(+${g_resultObj.excessive})` : ``;
541
+
542
+ let tweetFrzJdg = ``;
543
+ let tweetMaxCombo = `${g_resultObj.maxCombo}`;
544
+ if (g_allFrz > 0) {
545
+ tweetFrzJdg = `${g_resultObj.kita}-${g_resultObj.iknai}`;
546
+ tweetMaxCombo += `-${g_resultObj.fmaxCombo}`;
547
+ }
548
+
549
+ const resultParams = {
550
+ tuning: g_headerObj.tuning,
551
+ highscore: g_resultObj,
552
+ playStyleData: settingData.playStyleData,
553
+ hashTag, musicTitle, tweetDifData, rankMark,
554
+ tweetExcessive, tweetFrzJdg, tweetMaxCombo, baseTwitUrl
555
+ };
556
+ let tweetResultTmp = makeResultText(g_headerObj.resultFormat, resultParams);
557
+ let resultCommonTmp = makeResultText(g_templateObj.resultFormatDf, resultParams);
558
+
559
+ if (g_presetObj.resultVals !== undefined) {
560
+ Object.keys(g_presetObj.resultVals).forEach(key =>
561
+ tweetResultTmp = tweetResultTmp.split(`[${key}]`).join(g_resultObj[g_presetObj.resultVals[key]]));
562
+ }
563
+ const resultText = `${unEscapeHtml(tweetResultTmp)}`;
564
+ const tweetResult = `${g_linkObj.x}?text=${encodeURIComponent(resultText)}`;
565
+
566
+ /**
567
+ * リザルト画像をCanvasで作成しクリップボードへコピー
568
+ * @param {string} _msg
569
+ */
570
+ const copyResultImageData = _msg => {
571
+ const tmpDiv = createEmptySprite(divRoot, `tmpDiv`, { x: 0, y: 0, w: g_sWidth, h: g_sHeight, pointerEvents: C_DIS_AUTO });
572
+ tmpDiv.style.background = `#000000cc`;
573
+
574
+ const artistName = g_headerObj.artistNames[g_headerObj.musicNos[g_stateObj.scoreId]] || g_headerObj.artistName;
575
+ const logicalWidth = 400;
576
+ const logicalHeight = g_sHeight - 90;
577
+ const flapWidth = 370;
578
+
579
+ const canvas = createCanvas(`resultImage`, {
580
+ x: (g_sWidth - logicalWidth) / 2, y: 20, w: logicalWidth, h: logicalHeight
581
+ });
582
+ const context = canvas.getContext(`2d`);
583
+ const drawText = (_text, { x = 30, dy = 0, hy, siz = 15, color = `#cccccc`, align = C_ALIGN_LEFT, font } = {}) => {
584
+ fillCanvasText(context, _text, x, 35 + hy * 18 + dy, { siz, font, color, align });
585
+ };
586
+ makeBgCanvas(context, { w: logicalWidth, h: logicalHeight });
587
+
588
+ drawText(`R`, { dy: -5, hy: 0, siz: 40, color: `#9999ff` });
589
+ drawText(`ESULT`, { x: 57, dy: -5, hy: 0, siz: 25 });
590
+ drawText(`${g_lblNameObj.dancing}${g_lblNameObj.star}${g_lblNameObj.onigiri}`,
591
+ { x: 280, dy: -15, hy: 0, siz: 20, color: `#999999`, align: C_ALIGN_CENTER });
592
+ drawText(unEscapeHtml(mTitleForView[0]), { hy: 1 });
593
+ drawText(unEscapeHtml(mTitleForView[1]), { hy: 2 });
594
+ drawText(`${getEmojiForCanvas(g_emojiObj.memo)} ${unEscapeHtml(g_headerObj.tuning)} / ${getEmojiForCanvas(g_emojiObj.musical)} ${unEscapeHtml(artistName)}`,
595
+ { hy: mTitleForView[1] !== `` ? 3 : 2, siz: 12 });
596
+ drawText(unEscapeHtml(settingData.difDataForImage), { hy: 4, siz: getFontSize2(settingData.difDataForImage, flapWidth) });
597
+
598
+ if (settingData.playStyleData.length > 60) {
599
+ const strs = styleStr.split(`<br>`);
600
+ drawText(strs[0], { hy: 5, siz: getFontSize2(strs[0], flapWidth) });
601
+ drawText(strs[1], { hy: 6, siz: getFontSize2(strs[1], flapWidth) });
602
+ } else {
603
+ drawText(settingData.playStyleData, { hy: 5, siz: getFontSize2(settingData.playStyleData, flapWidth, { maxSiz: 15 }) });
604
+ }
605
+ Object.keys(jdgScoreObj).forEach(score => {
606
+ drawText(g_lblNameObj[`j_${score}`], { hy: 7 + jdgScoreObj[score].pos, color: jdgScoreObj[score].dfColor });
607
+ drawText(g_resultObj[score], { x: 200, hy: 7 + jdgScoreObj[score].pos, align: C_ALIGN_RIGHT });
608
+ });
609
+
610
+ if (highscorePreCondition) {
611
+ drawText(`(${highscoreDfObj.score >= 0 ? '+' : '-'} ${Math.abs(highscoreDfObj.score)})`,
612
+ { x: 206, hy: 18, color: highscoreDfObj.score > 0 ? `#ffff99` : `#cccccc`, align: C_ALIGN_RIGHT });
613
+ }
614
+
615
+ if (g_stateObj.autoAll === C_FLG_OFF) {
616
+ drawText(g_lblNameObj.j_fast, { x: 240, hy: 7, color: `#ff9966` });
617
+ drawText(g_resultObj.fast, { x: 360, hy: 7, align: C_ALIGN_RIGHT });
618
+ drawText(g_lblNameObj.j_slow, { x: 240, hy: 8, color: `#ccccff` });
619
+ drawText(g_resultObj.slow, { x: 360, hy: 8, align: C_ALIGN_RIGHT });
620
+ if (estimatedAdj !== ``) {
621
+ drawText(g_lblNameObj.j_adj, { x: 240, hy: 9, color: `#99ff99` });
622
+ drawText(getDiffFrame(estimatedAdj), { x: 360, hy: 9, align: C_ALIGN_RIGHT });
623
+ }
624
+ if (g_stateObj.excessive === C_FLG_ON) {
625
+ drawText(g_lblNameObj.j_excessive, { x: 240, hy: 10, color: `#ffff99` });
626
+ drawText(g_resultObj.excessive, { x: 360, hy: 10, align: C_ALIGN_RIGHT });
627
+ }
628
+ g_headerObj.resultValsView
629
+ .filter(key => hasVal(g_resultObj[g_presetObj.resultVals[key]]))
630
+ .forEach((key, j) => {
631
+ drawText(g_presetObj.resultVals[key], { x: 240, hy: j + 12, color: `#ffffff` });
632
+ drawText(g_resultObj[g_presetObj.resultVals[key]], { x: 360, hy: j + 12, align: C_ALIGN_RIGHT });
633
+ });
634
+ }
635
+ drawText(rankMark, { x: 240, hy: 18, siz: 50, color: rankColor, font: `"Bookman Old Style"` });
636
+ drawText(baseTwitUrl, { hy: 19, siz: 8 });
637
+ drawText(currentDateTime, { hy: 20 });
638
+
639
+ tmpDiv.appendChild(canvas);
640
+
641
+ const viewResultImage = () => {
642
+ if (document.getElementById(`tmpClose`) === null) {
643
+ divRoot.oncontextmenu = () => true;
644
+ makeLinkButton(tmpDiv, `Tmp`);
645
+ tmpDiv.appendChild(createCss2Button(`tmpClose`, g_lblNameObj.b_close, () => true, {
646
+ ...g_lblPosObj.btnRsCopyClose,
647
+ resetFunc: () => {
648
+ tmpDiv.removeChild(canvas);
649
+ divRoot.removeChild(tmpDiv);
650
+ divRoot.oncontextmenu = () => false;
651
+ },
652
+ }, g_cssObj.button_Back));
653
+ tmpDiv.appendChild(createDescDiv(`resultImageDesc`, g_lblNameObj.resultImageDesc));
654
+ }
655
+ };
656
+
657
+ try {
658
+ if (ClipboardItem === undefined) {
659
+ throw new Error(`error`);
660
+ }
661
+ if (keyIsShift()) {
662
+ viewResultImage();
663
+ } else {
664
+ // Canvas の内容を PNG 画像として取得
665
+ canvas.toBlob(async blob => {
666
+ try {
667
+ if (blob === null) {
668
+ throw new Error(`Failed to create result image blob.`);
669
+ }
670
+ await navigator.clipboard.write([
671
+ new ClipboardItem({ 'image/png': blob })
672
+ ]);
673
+ tmpDiv.removeChild(canvas);
674
+ divRoot.removeChild(tmpDiv);
675
+ makeInfoWindow(_msg, `leftToRightFade`);
676
+ } catch {
677
+ viewResultImage();
678
+ }
679
+ });
680
+ }
681
+
682
+ } catch (err) {
683
+ // 画像をクリップボードへコピーできないときは代替で画像保存可能な画面を表示
684
+ viewResultImage();
685
+ }
686
+ };
687
+
688
+ /**
689
+ * 音源、ループ処理の停止
690
+ * @param {string} _id
691
+ * @param {string} _name
692
+ * @param {object} _posObj
693
+ * @param {Function} _func
694
+ * @param {...any} _cssClass
695
+ * @returns {HTMLDivElement}
696
+ */
697
+ const resetCommonBtn = (_id, _name, _posObj, _func, _cssClass) =>
698
+ createCss2Button(_id, _name, () => {
699
+ if (g_finishFlg) {
700
+ g_audio.pause();
701
+ }
702
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
703
+ g_timerHandler.clearTimeout(g_timeoutEvtResultId);
704
+ }, { ..._posObj, resetFunc: () => _func() }, _cssClass);
705
+
706
+ /**
707
+ * 外部リンクボタンを作成
708
+ * @param {object} _div
709
+ * @param {string} _param
710
+ */
711
+ const makeLinkButton = (_div = divRoot, _param = ``) => {
712
+ multiAppend(_div,
713
+ // リザルトデータをX (Twitter)へ転送
714
+ createCss2Button(`btnTweet${_param}`, g_lblNameObj.b_tweet, () => true, {
715
+ ...g_lblPosObj.btnRsTweet, resetFunc: () => openLink(tweetResult),
716
+ }, g_cssObj.button_Tweet),
717
+
718
+ // Discordへのリンク
719
+ createCss2Button(`btnGitter${_param}`, g_lblNameObj.b_gitter, () => true, {
720
+ ...g_lblPosObj.btnRsGitter, resetFunc: () => openLink(g_linkObj.discord),
721
+ }, g_cssObj.button_Discord),
722
+ );
723
+ };
724
+
725
+ // ボタン描画
726
+ multiAppend(divRoot,
727
+
728
+ // タイトル画面へ戻る
729
+ resetCommonBtn(`btnBack`, g_lblNameObj.b_back, g_lblPosObj.btnRsBack, titleInit, g_cssObj.button_Back),
730
+
731
+ // リザルトデータをクリップボードへコピー
732
+ createCss2Button(`btnCopy`, g_lblNameObj.b_copy, () =>
733
+ copyTextToClipboard(keyIsShift() ?
734
+ unEscapeHtml(resultCommonTmp) : resultText, g_msgInfoObj.I_0001),
735
+ g_lblPosObj.btnRsCopy, g_cssObj.button_Setting),
736
+ );
737
+ makeLinkButton();
738
+ multiAppend(divRoot,
739
+ // リトライ
740
+ resetCommonBtn(`btnRetry`, g_lblNameObj.b_retry, g_lblPosObj.btnRsRetry, loadMusic, g_cssObj.button_Reset),
741
+
742
+ createCss2Button(`btnCopyImage`, g_emojiObj.camera, () => true, {
743
+ ...g_lblPosObj.btnRsCopyImage, resetFunc: () => copyResultImageData(g_msgInfoObj.I_0001),
744
+ }, g_cssObj.button_Default_NoColor),
745
+ );
746
+
747
+ // マスクスプライトを作成
748
+ const makeResultSprite = createMultipleSprite(`maskResultSprite`, g_headerObj.maskResultMaxDepth);
749
+ makeResultSprite.style.pointerEvents = g_headerObj.maskresultButton ? C_DIS_AUTO : C_DIS_NONE;
750
+
751
+ // リザルトモーションの0フレーム対応
752
+ g_animationData.filter(sprite => g_scoreObj[`${sprite}ResultFrameNum`] === 0 && g_headerObj[`${sprite}ResultData`]?.[0] !== undefined)
753
+ .forEach(sprite => {
754
+ g_scoreObj[`${sprite}ResultFrameNum`] = g_animationFunc.draw[sprite](0, `result`, sprite);
755
+ g_headerObj[`${sprite}ResultData`][0] = undefined;
756
+ });
757
+
758
+ /**
759
+ * タイトルのモーション設定
760
+ */
761
+ const flowResultTimeline = () => {
762
+
763
+ // ユーザカスタムイベント(フレーム毎)
764
+ safeExecuteCustomHooks(`g_customJsObj.resultEnterFrame`, g_customJsObj.resultEnterFrame);
765
+
766
+ // 背景・マスクモーション、スキン変更
767
+ drawTitleResultMotion(g_currentPage);
768
+
769
+ // リザルト画面移行後のフェードアウト処理
770
+ if (g_scoreObj.fadeOutFrame >= g_scoreObj.frameNum) {
771
+ if (g_scoreObj.frameNum >= g_scoreObj.fullFrame) {
772
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
773
+ }
774
+ g_scoreObj.frameNum++;
775
+ } else {
776
+ const tmpVolume = (g_audio.volume - (3 * g_stateObj.volume / 100 * C_FRM_AFTERFADE / g_scoreObj.fadeOutTerm) / 1000);
777
+ if (tmpVolume < 0) {
778
+ g_audio.volume = 0;
779
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
780
+ } else {
781
+ g_audio.volume = tmpVolume;
782
+ }
783
+ }
784
+
785
+ thisTime = performance.now();
786
+ buffTime = thisTime - resultStartTime - g_scoreObj.resultFrameNum * 1000 / g_fps;
787
+
788
+ g_scoreObj.resultFrameNum++;
789
+ g_animationData.forEach(sprite => g_scoreObj[`${sprite}ResultFrameNum`]++);
790
+ g_timeoutEvtResultId = g_timerHandler.setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
791
+ };
792
+ flowResultTimeline();
793
+
794
+ // キー操作イベント(デフォルト)
795
+ setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
796
+ document.oncontextmenu = () => true;
797
+
798
+ safeExecuteCustomHooks(`g_skinJsObj.result`, g_skinJsObj.result);
799
+ };
800
+
801
+ /**
802
+ * 選択した設定の情報を取得
803
+ * @param {boolean} _orgShuffleFlg
804
+ * @returns {object}
805
+ */
806
+ const getSelectedSettingList = (_orgShuffleFlg) => {
807
+
808
+ const transKeyName = getTransKeyName();
809
+ /**
810
+ * プレイスタイルのカスタム有無
811
+ * @param {string} _flg
812
+ * @param {string|boolean} _defaultSet デフォルト値
813
+ * @param {string} _displayText
814
+ * @returns {string}
815
+ */
816
+ const withOptions = (_flg, _defaultSet, _displayText = _flg) =>
817
+ (_flg !== _defaultSet ? getStgDetailName(_displayText) : ``);
818
+
819
+ const withDisplays = (_flg, _defaultSet, _displayText = _flg) =>
820
+ (_flg !== _defaultSet
821
+ ? getStgDetailName(_displayText) + (_flg === C_FLG_OFF ? `` : ` : ${getStgDetailName(_flg)}`) : ``);
822
+
823
+ // 譜面名の組み立て処理 (Ex: 9Akey / Normal-Leftless (maker) [X-Mirror])
824
+ const keyUnitName = getStgDetailName(getKeyUnitName(g_keyObj.currentKey));
825
+ const difDatas = [
826
+ `${getKeyName(g_headerObj.keyLabels[g_stateObj.scoreId])}${transKeyName} ${keyUnitName} / ${g_headerObj.difLabels[g_stateObj.scoreId]}`,
827
+ `${withOptions(g_autoPlaysBase.includes(g_stateObj.autoPlay), true, `-${getStgDetailName(g_stateObj.autoPlay)}${getStgDetailName('less')}`)}`,
828
+ `${withOptions(g_headerObj.makerView, false, `(${g_headerObj.creatorNames[g_stateObj.scoreId]})`)}`,
829
+ `${withOptions(g_stateObj.shuffle, C_FLG_OFF, `[${getShuffleName()}]`)}`
830
+ ];
831
+ let difData = difDatas.filter(value => value !== ``).join(` `);
832
+ const difDataForImage = difDatas.filter((value, j) => value !== `` && j !== 2).join(` `);
833
+
834
+ // 設定の組み立て処理 (Ex: 4x, Brake, Reverse, Sudden+, NoRecovery)
835
+ let playStyleData = [
836
+ `${g_stateObj.speed}${g_lblNameObj.multi}`,
837
+ withOptions(g_stateObj.motion, C_FLG_OFF),
838
+ `${withOptions(g_stateObj.reverse, C_FLG_OFF,
839
+ getStgDetailName(g_stateObj.scroll !== C_FLG_HYPHEN ? 'R-' : C_FLG_REVERSE))}${withOptions(g_stateObj.scroll, C_FLG_HYPHEN)}`,
840
+ withOptions(g_stateObj.appearance, `Visible`) +
841
+ ((g_appearanceRanges.includes(g_stateObj.appearance) && g_stateObj.filterLock === C_FLG_ON) ? `(${g_hidSudObj.filterPos}%)` : ``),
842
+ withOptions(g_stateObj.gauge, g_settings.gauges[0]),
843
+ withOptions(g_stateObj.playWindow, `Default`,
844
+ `${getStgDetailName(g_stateObj.playWindowType === C_FLG_REVERSE2 ? `R-` : ``)}${getStgDetailName(g_stateObj.playWindow)}`),
845
+ withOptions(g_stateObj.stepArea, `Default`),
846
+ withOptions(g_stateObj.frzReturn, C_FLG_OFF,
847
+ `FR:${getStgDetailName(g_stateObj.frzReturn)}(${getStgDetailName(g_stateObj.frzReturnType)})`),
848
+ withOptions(g_stateObj.shaking, C_FLG_OFF),
849
+ withOptions(g_stateObj.effect, C_FLG_OFF),
850
+ [
851
+ withOptions(g_stateObj.camoufrage, C_FLG_OFF, `Cmf:${getStgDetailName(g_stateObj.camoufrage)}`),
852
+ withOptions(g_stateObj.camoufrageType, C_FLG_HYPHEN,
853
+ `${g_stateObj.camoufrage !== C_FLG_OFF ? '' : 'Cmf:'}${getStgDetailName(g_stateObj.camoufrageType)}`)
854
+ ].filter(value => value !== ``).join(`+`),
855
+ withOptions(g_stateObj.swapping, C_FLG_OFF,
856
+ `Swap:${getStgDetailName(g_stateObj.swapping)}${!_orgShuffleFlg && !g_stateObj.swapping.endsWith(`+`) ? getStgDetailName(`(S)`) : ``}`),
857
+ withOptions(g_stateObj.judgRange, `Normal`, `Judg:${getStgDetailName(g_stateObj.judgRange)}`),
858
+ ].filter(value => value !== ``).join(`, `);
859
+
860
+ // Display設定の組み立て処理 (Ex: Step : FlatBar, Judge, Life : OFF)
861
+ let displayData = [
862
+ withDisplays(g_stateObj.d_stepzone, C_FLG_ON, g_lblNameObj.rd_StepZone),
863
+ withDisplays(g_stateObj.d_judgment, C_FLG_ON, g_lblNameObj.rd_Judgment),
864
+ withDisplays(g_stateObj.d_lifegauge, C_FLG_ON, g_lblNameObj.rd_LifeGauge),
865
+ withDisplays(g_stateObj.d_score, C_FLG_ON, g_lblNameObj.rd_Score),
866
+ withDisplays(g_stateObj.d_musicinfo, C_FLG_ON, g_lblNameObj.rd_MusicInfo),
867
+ withDisplays(g_stateObj.d_filterline, C_FLG_ON, g_lblNameObj.rd_FilterLine),
868
+ ].filter(value => value !== ``).join(`, `);
869
+ if (displayData === ``) {
870
+ displayData = getStgDetailName(`All Visible`);
871
+ } else {
872
+ // 表示設定のOFF項目を末尾にまとめる
873
+ const displayList = displayData.split(`, `).sort((a, b) => b.includes(`:`) - a.includes(`:`));
874
+ displayData = displayList.join(`, `);
875
+ if (!displayList.at(-1).includes(`:`)) {
876
+ displayData += ` : ${getStgDetailName(C_FLG_OFF)}`;
877
+ }
878
+ }
879
+
880
+ let display2Data = [
881
+ withDisplays(g_stateObj.d_velocity, C_FLG_ON, g_lblNameObj.rd_Velocity),
882
+ withDisplays(g_stateObj.d_color, C_FLG_ON, g_lblNameObj.rd_Color),
883
+ withDisplays(g_stateObj.d_background, C_FLG_ON, g_lblNameObj.rd_Background),
884
+ withDisplays(g_stateObj.d_arroweffect, C_FLG_ON, g_lblNameObj.rd_ArrowEffect),
885
+ withDisplays(g_stateObj.d_special, C_FLG_ON, g_lblNameObj.rd_Special),
886
+ ].filter(value => value !== ``).join(`, `);
887
+ if (display2Data !== ``) {
888
+ // 表示設定のOFF項目を末尾にまとめる
889
+ const display2List = display2Data.split(`, `).sort((a, b) => b.includes(`:`) - a.includes(`:`));
890
+ display2Data = display2List.join(`, `);
891
+ if (!display2List.at(-1).includes(`:`)) {
892
+ display2Data += ` : ${getStgDetailName(C_FLG_OFF)}`;
893
+ }
894
+ }
895
+
896
+ return { difData, difDataForImage, playStyleData, displayData, display2Data };
897
+ };
898
+
899
+ /**
900
+ * リザルトフォーマットの整形処理
901
+ * @param {string} _format
902
+ * @param {object} object フォーマット置き換え変数群
903
+ * @param {string} object.hashTag ハッシュタグ
904
+ * @param {string} object.musicTitle 曲名
905
+ * @param {string} object.tweetDifData 譜面名
906
+ * @param {string} object.tuning 製作者名
907
+ * @param {string} object.rankMark ランク
908
+ * @param {string} object.playStyleData プレイ設定
909
+ * @param {object} object.highscore ハイスコア(判定別)
910
+ * @param {string} object.tweetExcessive 空押し判定状況
911
+ * @param {string} object.tweetFrzJdg フリーズアロー判定状況
912
+ * @param {string} object.tweetMaxCombo コンボ数状況
913
+ * @param {string} object.baseTwitUrl X投稿用URL
914
+ * @returns {string}
915
+ */
916
+ const makeResultText = (_format, {
917
+ hashTag, musicTitle, tweetDifData, tuning, rankMark, playStyleData,
918
+ highscore, tweetExcessive, tweetFrzJdg, tweetMaxCombo, baseTwitUrl } = {}) =>
919
+ replaceStr(_format, [
920
+ [`[hashTag]`, hashTag],
921
+ [`[musicTitle]`, musicTitle],
922
+ [`[keyLabel]`, tweetDifData],
923
+ [`[maker]`, tuning],
924
+ [`[rank]`, rankMark],
925
+ [`[score]`, highscore?.score],
926
+ [`[playStyle]`, playStyleData],
927
+ [`[arrowJdg]`, `${highscore?.ii}-${highscore?.shakin}-${highscore?.matari}-${highscore?.shobon}-${highscore?.uwan}${tweetExcessive}`],
928
+ [`[frzJdg]`, tweetFrzJdg],
929
+ [`[maxCombo]`, tweetMaxCombo],
930
+ [`[url]`, baseTwitUrl]
931
+ ]);
932
+
933
+ /**
934
+ * 結果表示作成(曲名、オプション)
935
+ * @param {string} _id
936
+ * @param {number} _x
937
+ * @param {string} _class
938
+ * @param {number} _heightPos
939
+ * @param {string} _text
940
+ * @param {string} _align
941
+ * @param {number} [object.w=400]
942
+ * @param {number} [object.siz=g_limitObj.mainSiz]
943
+ * @returns {HTMLDivElement}
944
+ */
945
+ const makeCssResultPlayData = (_id, _x, _class, _heightPos, _text, _align = C_ALIGN_CENTER, { w = 400, siz = g_limitObj.mainSiz } = {}) =>
946
+ createDivCss2Label(_id, _text, {
947
+ x: _x, y: g_limitObj.setMiniSiz * _heightPos, w, h: g_limitObj.setMiniSiz, siz, align: _align,
948
+ }, _class);
949
+
950
+ /**
951
+ * 結果表示作成(キャラクタ)
952
+ * @param {string} _id
953
+ * @param {number} _x
954
+ * @param {string} _class
955
+ * @param {number} _heightPos
956
+ * @param {string} _text
957
+ * @param {string} _align
958
+ * @returns {HTMLDivElement}
959
+ */
960
+ const makeCssResultSymbol = (_id, _x, _class, _heightPos, _text, _align = C_ALIGN_LEFT) =>
961
+ makeCssResultPlayData(_id, _x, _class, _heightPos, _text, _align, { w: 150, siz: g_limitObj.jdgCntsSiz });