danoniplus 52.0.0 → 52.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/danoni_main.css +16 -1
- package/danoni/ringtest.html +98 -0
- package/js/danoni_main.js +14 -3
- package/js/lib/danoni_constants.js +126 -16
- package/js/lib/dataLoader.js +333 -9
- package/js/lib/dosConverter.js +32 -5
- package/js/lib/mainWindow.js +382 -26
- package/js/lib/result.js +3 -1
- package/js/lib/settings.js +451 -59
- package/package.json +1 -1
- package/skin/danoni_skin_default.css +3 -1
- package/skin/danoni_skin_light.css +3 -1
- package/skin/danoni_skin_skyblue.css +3 -1
- package/sound/nosound.mp3 +0 -0
package/js/lib/settings.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Source by tickle
|
|
7
7
|
* Created : 2026/09/13
|
|
8
|
-
* Revised : 2026/11/
|
|
8
|
+
* Revised : 2026/11/08 (v52.2.0)
|
|
9
9
|
*
|
|
10
10
|
* https://github.com/cwtickle/danoniplus
|
|
11
11
|
*/
|
|
@@ -625,6 +625,7 @@ const drawDensityGraph = _scoreId => {
|
|
|
625
625
|
updateScoreDetailLabel(`Density`, `Arrow`, obj.arrowCnts, 3, g_lblNameObj.s_arrow);
|
|
626
626
|
updateScoreDetailLabel(`Density`, `Frz`, obj.frzCnts, 4,
|
|
627
627
|
`${g_lblNameObj.s_frz}${g_headerObj.frzStartjdgUse ? ' <span class="common_bold">(2x)</span>' : ''}`);
|
|
628
|
+
updateScoreDetailLabel(`Density`, `Ring`, g_detailObj.ringCnt[_scoreId], 5, g_lblNameObj.s_ring);
|
|
628
629
|
};
|
|
629
630
|
|
|
630
631
|
/**
|
|
@@ -734,11 +735,13 @@ const makeDifInfoLabels = _scoreId => {
|
|
|
734
735
|
// 縦連
|
|
735
736
|
`${g_detailObj.toolDif[j].tate}\t` +
|
|
736
737
|
// 総矢印数
|
|
737
|
-
`${(obj.arrowCnts + obj.frzCnts)}\t` +
|
|
738
|
+
`${(obj.arrowCnts + obj.frzCnts + g_detailObj.ringCnt[j])}\t` +
|
|
738
739
|
// 矢印
|
|
739
740
|
`${obj.arrowCnts}\t` +
|
|
740
741
|
// フリーズアロー
|
|
741
742
|
`${obj.frzCnts}\t` +
|
|
743
|
+
// リングノート
|
|
744
|
+
`${g_detailObj.ringCnt[j]}\t` +
|
|
742
745
|
// APM
|
|
743
746
|
`${obj.apm}\t` +
|
|
744
747
|
// 時間(分秒)
|
|
@@ -787,9 +790,10 @@ const makeDifInfo = _scoreId => {
|
|
|
787
790
|
lblArrowInfo2.innerHTML = g_lblNameObj.s_linecnts.split(`{0}`)
|
|
788
791
|
.join(`${makeDedupliArray(g_detailObj.toolDif[_scoreId].push3).length} /cnt:${g_detailObj.toolDif[_scoreId].push3cnt}`);
|
|
789
792
|
dataArrowInfo.innerHTML =
|
|
790
|
-
`${arrowCnts + frzCnts * (g_headerObj.frzStartjdgUse ? 2 : 1)} ` +
|
|
793
|
+
`${arrowCnts + frzCnts * (g_headerObj.frzStartjdgUse ? 2 : 1) + g_detailObj.ringCnt[_scoreId]} ` +
|
|
791
794
|
`<span style="font-size:${wUnit(g_limitObj.difSelectorSiz)};">` +
|
|
792
|
-
`(${arrowCnts} + ${frzCnts}${g_headerObj.frzStartjdgUse ? ' <span class="common_bold">x 2</span>' : ''}
|
|
795
|
+
`(${arrowCnts} + ${frzCnts}${g_headerObj.frzStartjdgUse ? ' <span class="common_bold">x 2</span>' : ''}` +
|
|
796
|
+
`${hasRingNotes() ? ` + ${g_detailObj.ringCnt[_scoreId]}` : ''})` +
|
|
793
797
|
`</span>`;
|
|
794
798
|
|
|
795
799
|
const makeArrowCntsView = (_cntlist) => {
|
|
@@ -917,25 +921,25 @@ const makeHighScore = _scoreId => {
|
|
|
917
921
|
color: g_localStorage.highscores?.[scoreName]?.rankColor ?? `#666666`,
|
|
918
922
|
fontFamily: getBasicFont(`"Bookman Old Style"`),
|
|
919
923
|
}),
|
|
920
|
-
createScoreLabel(`
|
|
921
|
-
createScoreLabel(`
|
|
924
|
+
createScoreLabel(`dateTime`, g_localStorage.highscores?.[scoreName]?.dateTime ?? `----/--/-- --:--`, { yPos: 12 }),
|
|
925
|
+
createScoreLabel(`marks`,
|
|
922
926
|
`${g_localStorage.highscores?.[scoreName]?.fullCombo ?? '' ? '<span class="result_FullCombo">◆</span>' : ''}` +
|
|
923
927
|
`${g_localStorage.highscores?.[scoreName]?.perfect ?? '' ? '<span class="result_Perfect">◆</span>' : ''}` +
|
|
924
928
|
`${g_localStorage.highscores?.[scoreName]?.allPerfect ?? '' ? '<span class="result_AllPerfect">◆</span>' : ''}`,
|
|
925
929
|
{ xPos: 1, dx: 20, yPos: 12, w: 100, align: C_ALIGN_CENTER }),
|
|
926
|
-
createScoreLabel(`
|
|
930
|
+
createScoreLabel(`clearLamps`,
|
|
927
931
|
`Cleared: ` + (g_localStorage.highscores?.[scoreName]?.clearLamps?.join(', ') ?? C_FLG_HYPHEN),
|
|
928
932
|
{ yPos: 13, overflow: C_DIS_AUTO, w: g_sWidth / 2 + 40, h: 37 }),
|
|
929
933
|
|
|
930
|
-
createScoreLabel(`
|
|
934
|
+
createScoreLabel(`shuffleS`,
|
|
931
935
|
g_stateObj.shuffle.indexOf(`Mirror`) < 0
|
|
932
936
|
? `` : `Shuffle: <span class="common_shuffle">${g_stateObj.shuffle}</span>`,
|
|
933
937
|
{ yPos: 11.5, dx: -130 }),
|
|
934
|
-
createScoreLabel(`
|
|
938
|
+
createScoreLabel(`assistS`,
|
|
935
939
|
g_autoPlaysBase.includes(g_stateObj.autoPlay)
|
|
936
940
|
? `` : `Assist: <span class="common_assist">${g_stateObj.autoPlay}</span>`,
|
|
937
941
|
{ yPos: 12.5, dx: -130 }),
|
|
938
|
-
createScoreLabel(`
|
|
942
|
+
createScoreLabel(`anotherS`,
|
|
939
943
|
!hasVal(g_keyObj[`transKey${keyCtrlPtn}`])
|
|
940
944
|
? `` : `A.Keymode: <span class="common_another">${g_keyObj[`transKey${keyCtrlPtn}`]}</span>`,
|
|
941
945
|
{ yPos: 13.5, dx: -130 }),
|
|
@@ -1031,9 +1035,11 @@ const drawMinimap = (_scoreId, { _initFlg = false, _fadeinFlg = false } = {}) =>
|
|
|
1031
1035
|
|
|
1032
1036
|
const params = g_detailObj.miniMapParams[_scoreId];
|
|
1033
1037
|
const kPtn = params._keyCtrlPtn;
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1038
|
+
// リング帯の有無で幅が変わるため、キー種 + リング帯有無でキャッシュする
|
|
1039
|
+
const headerKey = params.config.ringBandWidth > 0 ? `${kPtn}_ring` : kPtn;
|
|
1040
|
+
if (!g_detailObj.scoreMinimapHeader[headerKey]) {
|
|
1041
|
+
// ヘッダーはキー種(+リング帯有無)ごとに共通なので、未作成の場合のみ生成してキャッシュ
|
|
1042
|
+
g_detailObj.scoreMinimapHeader[headerKey] = createMinimapHeader(params.config, kPtn, params._keyNum);
|
|
1037
1043
|
}
|
|
1038
1044
|
if (!savedCanvases) {
|
|
1039
1045
|
// 未作成の場合のみミニマップを生成(Lazy Generation)
|
|
@@ -1050,7 +1056,7 @@ const drawMinimap = (_scoreId, { _initFlg = false, _fadeinFlg = false } = {}) =>
|
|
|
1050
1056
|
// --- ヘッダー部分 ---
|
|
1051
1057
|
const detailMiniMapHeader = createEmptySprite(detailMiniMap, `detailMiniMapHeader`, g_windowObj.detailMiniMapHeader);
|
|
1052
1058
|
$id(`detailMiniMapHeader`).top = wUnit(g_stateObj.miniMapRevFlg ? 230 + g_sHeight - 500 : 0);
|
|
1053
|
-
detailMiniMapHeader.appendChild(g_detailObj.scoreMinimapHeader[
|
|
1059
|
+
detailMiniMapHeader.appendChild(g_detailObj.scoreMinimapHeader[headerKey]);
|
|
1054
1060
|
|
|
1055
1061
|
// --- メイン(譜面)部分 ---
|
|
1056
1062
|
const detailMiniMapSub = createEmptySprite(detailMiniMap, `detailMiniMapSub`, g_windowObj.detailMiniMapSub);
|
|
@@ -1128,7 +1134,7 @@ const drawMinimap = (_scoreId, { _initFlg = false, _fadeinFlg = false } = {}) =>
|
|
|
1128
1134
|
* @return {HTMLCanvasElement} ヘッダー用のキャンバス要素
|
|
1129
1135
|
*/
|
|
1130
1136
|
const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
1131
|
-
const { timeMargin, laneWidth, logicalWidth } = _config;
|
|
1137
|
+
const { timeMargin, laneWidth, logicalWidth, ringBandWidth = 0 } = _config;
|
|
1132
1138
|
const headerHeight = 15; // ヘッダーの固定高
|
|
1133
1139
|
|
|
1134
1140
|
const canvas = createCanvas(null, { w: logicalWidth, h: headerHeight, position: null });
|
|
@@ -1143,6 +1149,12 @@ const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
|
1143
1149
|
// 視覚的な中央調整で +2px
|
|
1144
1150
|
fillCanvasText(ctx, keyText, x, headerHeight / 2 + 2, { siz: 10, color: '#999', align: C_ALIGN_CENTER, baseline: `middle` });
|
|
1145
1151
|
}
|
|
1152
|
+
// リングノート帯の見出し
|
|
1153
|
+
if (ringBandWidth > 0) {
|
|
1154
|
+
const bandX = timeMargin + laneWidth * _keyNum + 4;
|
|
1155
|
+
fillCanvasText(ctx, `Ring`, bandX + (ringBandWidth - 4) / 2, headerHeight / 2 + 2,
|
|
1156
|
+
{ siz: 10, color: '#999', align: C_ALIGN_CENTER, baseline: `middle` });
|
|
1157
|
+
}
|
|
1146
1158
|
|
|
1147
1159
|
return canvas;
|
|
1148
1160
|
};
|
|
@@ -1166,7 +1178,7 @@ const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
|
1166
1178
|
*/
|
|
1167
1179
|
const generateMinimapData = (_params, _isReverse) => {
|
|
1168
1180
|
const { _scoreObj, _keyNum, _playingFrame, _firstArrowFrame, _keyCtrlPtn, config } = _params;
|
|
1169
|
-
const { scale, timeMargin, laneWidth, logicalWidth, mmMarginY } = config;
|
|
1181
|
+
const { scale, timeMargin, laneWidth, logicalWidth, mmMarginY, ringBandWidth = 0 } = config;
|
|
1170
1182
|
|
|
1171
1183
|
const mmHeightTotal = _playingFrame * scale + mmMarginY * 2;
|
|
1172
1184
|
const canvases = createSplitCanvases(logicalWidth, mmHeightTotal);
|
|
@@ -1234,6 +1246,46 @@ const generateMinimapData = (_params, _isReverse) => {
|
|
|
1234
1246
|
});
|
|
1235
1247
|
}
|
|
1236
1248
|
|
|
1249
|
+
// 4. リングノート (レーンを持たないため、右側の専用帯に出現フレームのみ表示)
|
|
1250
|
+
// 座標は使わない。同一フレームに複数ある場合は帯内で横に散らして重なりを避ける
|
|
1251
|
+
if (ringBandWidth > 0 && _scoreObj.ringData !== undefined) {
|
|
1252
|
+
const bandX = timeMargin + laneWidth * _keyNum + 4;
|
|
1253
|
+
const bandW = ringBandWidth - 6;
|
|
1254
|
+
const cols = g_ringObj.cols;
|
|
1255
|
+
|
|
1256
|
+
// 帯の背景
|
|
1257
|
+
distributeDrawing(canvases, 0, mmHeightTotal, g_dpr, (ctx) => {
|
|
1258
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.05)';
|
|
1259
|
+
ctx.fillRect(bandX, 0, bandW, mmHeightTotal);
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
// フレーム毎の出現数
|
|
1263
|
+
const frameCnts = new Map();
|
|
1264
|
+
for (let k = 0; k < _scoreObj.ringData.length; k += cols) {
|
|
1265
|
+
const frame = parseFloat(_scoreObj.ringData[k]);
|
|
1266
|
+
if (!isNaN(frame)) {
|
|
1267
|
+
frameCnts.set(frame, (frameCnts.get(frame) ?? 0) + 1);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
const seen = new Map();
|
|
1272
|
+
for (let k = 0; k < _scoreObj.ringData.length; k += cols) {
|
|
1273
|
+
const frame = parseFloat(_scoreObj.ringData[k]);
|
|
1274
|
+
if (isNaN(frame)) continue;
|
|
1275
|
+
|
|
1276
|
+
const idx = seen.get(frame) ?? 0;
|
|
1277
|
+
seen.set(frame, idx + 1);
|
|
1278
|
+
const x = bandX + bandW * (idx + 1) / (frameCnts.get(frame) + 1);
|
|
1279
|
+
const y = getY(frame);
|
|
1280
|
+
distributeDrawing(canvases, y - 4, 8, g_dpr, (ctx) => {
|
|
1281
|
+
ctx.fillStyle = 'rgba(255, 170, 60, 0.8)';
|
|
1282
|
+
ctx.beginPath();
|
|
1283
|
+
ctx.arc(x, y, 3, 0, Math.PI * 2);
|
|
1284
|
+
ctx.fill();
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1237
1289
|
return canvases.map(item => item.canvas);
|
|
1238
1290
|
};
|
|
1239
1291
|
|
|
@@ -1828,7 +1880,7 @@ const createOptionWindow = _sprite => {
|
|
|
1828
1880
|
|
|
1829
1881
|
// ---------------------------------------------------
|
|
1830
1882
|
// タイミング調整 (Adjustment)
|
|
1831
|
-
// 縦位置: 10.
|
|
1883
|
+
// 縦位置: 10.6 短縮ショートカットあり
|
|
1832
1884
|
createGeneralSetting(spriteList.adjustment, `adjustment`, {
|
|
1833
1885
|
skipTerms: g_settings.adjustmentTerms, hiddenBtn: true, scLabel: g_lblNameObj.sc_adjustment, roundNum: 5,
|
|
1834
1886
|
unitName: g_lblNameObj.frame, addRFunc: () => viewAdjustment(),
|
|
@@ -1857,7 +1909,7 @@ const createOptionWindow = _sprite => {
|
|
|
1857
1909
|
|
|
1858
1910
|
// ---------------------------------------------------
|
|
1859
1911
|
// フェードイン (Fadein)
|
|
1860
|
-
// 縦位置: 11.
|
|
1912
|
+
// 縦位置: 11.6 スライダーあり
|
|
1861
1913
|
spriteList.fadein.appendChild(createLblSetting(`Fadein`));
|
|
1862
1914
|
|
|
1863
1915
|
const lnkFadein = createDivCss2Label(`lnkFadein`, `${g_stateObj.fadein}${g_lblNameObj.percent}`,
|
|
@@ -1893,7 +1945,7 @@ const createOptionWindow = _sprite => {
|
|
|
1893
1945
|
|
|
1894
1946
|
// ---------------------------------------------------
|
|
1895
1947
|
// ボリューム (Volume)
|
|
1896
|
-
// 縦位置: 12.
|
|
1948
|
+
// 縦位置: 12.6
|
|
1897
1949
|
createGeneralSetting(spriteList.volume, `volume`, { unitName: g_lblNameObj.percent });
|
|
1898
1950
|
|
|
1899
1951
|
// 譜面番号の再取得
|
|
@@ -2051,9 +2103,15 @@ const openHelpOverlay = (_title, _msg, _stateKey) => {
|
|
|
2051
2103
|
if (![`option`, `exSetting`, `settingsDisplay`].includes(g_currentPage)) return;
|
|
2052
2104
|
|
|
2053
2105
|
// 現在値は、g_currentPage を切り替える前に取得する(getStgDetailName が画面名を参照するため)
|
|
2054
|
-
const
|
|
2055
|
-
const
|
|
2056
|
-
const
|
|
2106
|
+
const sub = g_helpPreviewDefs[_stateKey]?.sub || (_stateKey + `Type`);
|
|
2107
|
+
const subList = sub !== undefined ? (g_settings[`${sub}s`] ?? []) : [];
|
|
2108
|
+
const currentVals = [g_stateObj[_stateKey], ...(subList.length > 0 ? [g_stateObj[sub]] : [])];
|
|
2109
|
+
const currentNames = currentVals.flatMap(val => [val, getStgDetailName(val)]);
|
|
2110
|
+
const optNames = new Map([...(g_settings[`${_stateKey}s`] ?? []), ...subList]
|
|
2111
|
+
.map(val => [val, [val, getStgDetailName(val)]]));
|
|
2112
|
+
|
|
2113
|
+
// Typeがある設定は、本体の説明文の後にTypeの説明文を続ける
|
|
2114
|
+
const helpMsg = [_msg, ...(subList.length > 0 ? [g_msgObj[sub]] : [])].filter(hasVal).join(`\n\n`);
|
|
2057
2115
|
const rowElms = [];
|
|
2058
2116
|
const bright = g_headerObj.baseBrightFlg;
|
|
2059
2117
|
|
|
@@ -2080,7 +2138,7 @@ const openHelpOverlay = (_title, _msg, _stateKey) => {
|
|
|
2080
2138
|
});
|
|
2081
2139
|
g_handler.addListener(body, `click`, evt => evt.stopPropagation());
|
|
2082
2140
|
|
|
2083
|
-
parseHelpLines(
|
|
2141
|
+
parseHelpLines(helpMsg).forEach(line => {
|
|
2084
2142
|
const row = document.createElement(`div`);
|
|
2085
2143
|
Object.assign(row.style, {
|
|
2086
2144
|
fontFamily: getBasicFont(), fontSize: wUnit(14), lineHeight: `1.1`, textAlign: `left`,
|
|
@@ -2109,12 +2167,13 @@ const openHelpOverlay = (_title, _msg, _stateKey) => {
|
|
|
2109
2167
|
});
|
|
2110
2168
|
|
|
2111
2169
|
// 説明文の選択行 (背景) をプレビューのカーソルに追従させる
|
|
2112
|
-
const markHelpRows =
|
|
2113
|
-
const names = optNames.get(
|
|
2170
|
+
const markHelpRows = (_vals, _focus = 0) => {
|
|
2171
|
+
const names = _vals.flatMap(val => optNames.get(val) ?? [val]);
|
|
2172
|
+
const focusNames = optNames.get(_vals[_focus]) ?? [_vals[_focus]];
|
|
2114
2173
|
rowElms.forEach(({ nameList, row }) => {
|
|
2115
2174
|
const on = nameList.some(name => names.includes(name));
|
|
2116
2175
|
row.style.background = on ? `rgba(80,140,255,0.25)` : ``;
|
|
2117
|
-
if (
|
|
2176
|
+
if (nameList.some(name => focusNames.includes(name))) {
|
|
2118
2177
|
// 本文がスクロールする場合は、選択行が見える位置へ寄せる
|
|
2119
2178
|
const [top, bottom] = [row.offsetTop, row.offsetTop + row.offsetHeight];
|
|
2120
2179
|
if (top < body.scrollTop) {
|
|
@@ -2125,7 +2184,7 @@ const openHelpOverlay = (_title, _msg, _stateKey) => {
|
|
|
2125
2184
|
}
|
|
2126
2185
|
});
|
|
2127
2186
|
};
|
|
2128
|
-
markHelpRows(
|
|
2187
|
+
markHelpRows(currentVals);
|
|
2129
2188
|
g_helpSelectHook = markHelpRows;
|
|
2130
2189
|
|
|
2131
2190
|
// 上下キー: プレビューの選択肢移動、または本文のスクロール
|
|
@@ -2150,10 +2209,25 @@ const openHelpOverlay = (_title, _msg, _stateKey) => {
|
|
|
2150
2209
|
createCss2Button(`btnHelpNext`, ``, () => true, {
|
|
2151
2210
|
x: 0, y: 0, w: 0, h: 0, opacity: 0, resetFunc: () => moveHelp(1),
|
|
2152
2211
|
}, g_cssObj.button_Mini),
|
|
2212
|
+
|
|
2213
|
+
// 操作対象の列の切り替え (本体 / Type)
|
|
2214
|
+
createCss2Button(`btnHelpLeft`, ``, () => true, {
|
|
2215
|
+
x: 0, y: 0, w: 0, h: 0, opacity: 0, resetFunc: () => g_helpPreviewColumn(-1),
|
|
2216
|
+
}, g_cssObj.button_Mini),
|
|
2217
|
+
createCss2Button(`btnHelpRight`, ``, () => true, {
|
|
2218
|
+
x: 0, y: 0, w: 0, h: 0, opacity: 0, resetFunc: () => g_helpPreviewColumn(1),
|
|
2219
|
+
}, g_cssObj.button_Mini),
|
|
2153
2220
|
);
|
|
2154
2221
|
createScText(btnHelpClose, `HelpClose`, { displayName: `helpOverlay`, targetLabel: `btnHelpClose`, x: -5 });
|
|
2155
2222
|
if (previewH > 0) {
|
|
2156
|
-
|
|
2223
|
+
const def = g_helpPreviewDefs[_stateKey];
|
|
2224
|
+
let func;
|
|
2225
|
+
if (def?.ring) {
|
|
2226
|
+
func = makeRingHelpPreview;
|
|
2227
|
+
} else {
|
|
2228
|
+
func = makeHelpPreview;
|
|
2229
|
+
}
|
|
2230
|
+
func(overlay, _stateKey, g_sHeight - 30 - previewH, previewH);
|
|
2157
2231
|
}
|
|
2158
2232
|
// ユーザカスタムイベント(ヘルプ表示)
|
|
2159
2233
|
safeExecuteCustomHooks(`g_customJsObj.helpOverlay`, g_customJsObj.helpOverlay);
|
|
@@ -2202,17 +2276,21 @@ const isLongHelp = _msg =>
|
|
|
2202
2276
|
* - currentKey: サンプルキーではなく実際のキーに合わせてマッピングする
|
|
2203
2277
|
* (StepArea, Swappingは実際のキーには非対応)
|
|
2204
2278
|
* - frzUse : サンプルでフリーズアローを使用するかどうか
|
|
2279
|
+
* - sub : 派生する設定 (Typeなど)。選択肢の列を2列目に追加する
|
|
2280
|
+
* - ring : リングのプレビューを出す
|
|
2205
2281
|
*/
|
|
2206
2282
|
const g_helpPreviewDefs = {
|
|
2207
2283
|
motion: {},
|
|
2208
2284
|
scroll: { currentKey: true },
|
|
2209
2285
|
appearance: { splitLayer: true },
|
|
2210
|
-
playWindow: {},
|
|
2286
|
+
playWindow: { sub: `playWindowType` },
|
|
2211
2287
|
stepArea: {},
|
|
2212
2288
|
shaking: { splitLayer: true },
|
|
2213
|
-
frzReturn: {},
|
|
2214
|
-
effect: {
|
|
2289
|
+
frzReturn: { sub: `frzReturnType` },
|
|
2290
|
+
effect: { frzUse: true, splitLayer: true },
|
|
2215
2291
|
swapping: { frzUse: true },
|
|
2292
|
+
ringSpeed: { ring: true },
|
|
2293
|
+
ringGuide: { ring: true },
|
|
2216
2294
|
};
|
|
2217
2295
|
|
|
2218
2296
|
/**
|
|
@@ -2246,6 +2324,9 @@ let g_helpPreviewStop = () => true;
|
|
|
2246
2324
|
/** プレビューの選択肢を前後へ動かす処理 (プレビュー表示中のみ差し替わる) */
|
|
2247
2325
|
let g_helpPreviewMove = _dir => true;
|
|
2248
2326
|
|
|
2327
|
+
/** プレビューの操作対象の列を切り替える処理 (-1: 本体、1: Type。プレビュー表示中のみ差し替わる) */
|
|
2328
|
+
let g_helpPreviewColumn = _dir => true;
|
|
2329
|
+
|
|
2249
2330
|
/**
|
|
2250
2331
|
* 詳細ヘルプ内プレビューの作成
|
|
2251
2332
|
* @param {HTMLDivElement} _parent オーバーレイ
|
|
@@ -2260,7 +2341,10 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2260
2341
|
return;
|
|
2261
2342
|
}
|
|
2262
2343
|
const playH = g_headerObj.playingHeight || g_sHeight;
|
|
2263
|
-
const
|
|
2344
|
+
const subList = def.sub !== undefined ? g_settings[`${def.sub}s`] : undefined;
|
|
2345
|
+
const subUse = subList !== undefined;
|
|
2346
|
+
const colW = subUse ? 90 : 120;
|
|
2347
|
+
const chipW = subUse ? colW * 2 + 6 : colW; // 選択肢ボタン列の全体幅 (Typeがある場合は2列)
|
|
2264
2348
|
const areaW = g_sWidth - 60;
|
|
2265
2349
|
|
|
2266
2350
|
// レーン定義: Scrollのみ現在選択中のキー・パターンの定義を使用 (他は4レーン固定)
|
|
@@ -2432,6 +2516,8 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2432
2516
|
let stride = Math.max(Math.round(visibleLanes.length * 0.6), 1);
|
|
2433
2517
|
while (gcd(stride, visibleLanes.length) !== 1) stride++;
|
|
2434
2518
|
let selected = ``;
|
|
2519
|
+
let subSelected = subUse ? (subList.includes(g_stateObj[def.sub]) ? g_stateObj[def.sub] : subList[0]) : ``;
|
|
2520
|
+
let activeCol = 0; // 上下キーの対象列 (0: 本体、1: Type)
|
|
2435
2521
|
let frameCnt = 0;
|
|
2436
2522
|
let noteCnt = 0;
|
|
2437
2523
|
let autoCnt = 0; // 自動切替までの経過フレーム数
|
|
@@ -2443,7 +2529,7 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2443
2529
|
let filterElms = []; // Appearanceのフィルターバー・ラベル
|
|
2444
2530
|
const frzLen = 90; // Effectプレビュー用フリーズアローの長さ(px)
|
|
2445
2531
|
const work = { drunkXFlg: true, drunkYFlg: true, drunkAxisFlg: false, drunkRotateFlg: false };
|
|
2446
|
-
const frz = { seq: null, axes: [], start: 0, next: 0 };
|
|
2532
|
+
const frz = { seq: null, seqNo: 0, axes: [], start: 0, next: 0 };
|
|
2447
2533
|
|
|
2448
2534
|
/** 付与したtransformの初期化 */
|
|
2449
2535
|
const resetTransforms = () => spriteIds.forEach(id => {
|
|
@@ -2598,6 +2684,18 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2598
2684
|
});
|
|
2599
2685
|
};
|
|
2600
2686
|
|
|
2687
|
+
/** Typeの選択値を、ゲーム側の関数が参照する g_stateObj へ一時的に差し替えて実行 (g_playWindowDir など) */
|
|
2688
|
+
const withSub = func => {
|
|
2689
|
+
if (!subUse) return func();
|
|
2690
|
+
const org = g_stateObj[def.sub];
|
|
2691
|
+
g_stateObj[def.sub] = subSelected;
|
|
2692
|
+
try {
|
|
2693
|
+
return func();
|
|
2694
|
+
} finally {
|
|
2695
|
+
g_stateObj[def.sub] = org;
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
|
|
2601
2699
|
/**
|
|
2602
2700
|
* 選択肢の適用
|
|
2603
2701
|
* - Motion: 「配列の15がステップゾーン上」として、ステップゾーンからの距離を逆算する
|
|
@@ -2611,9 +2709,10 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2611
2709
|
|
|
2612
2710
|
assignSwapping(_key === `swapping` ? opt : null);
|
|
2613
2711
|
// レーンのレイヤー配置 (StepAreaは選択肢ごと、Shakingは右半分を逆スクロール側へ)
|
|
2614
|
-
assignLanes((_key
|
|
2615
|
-
?? (
|
|
2616
|
-
?? (def.splitLayer ? [0, 0, 1, 1] : [0, 0, 0, 0])
|
|
2712
|
+
assignLanes((g_helpPreviewDefs[_key]?.currentKey ? getScrollLayout(opt) : null)
|
|
2713
|
+
?? (_key === `stepArea` ? g_helpStepAreaLayouts[opt] : null)
|
|
2714
|
+
?? (def.splitLayer ? [0, 0, 1, 1] : [0, 0, 0, 0])
|
|
2715
|
+
.map(val => g_stateObj.reverse === C_FLG_OFF ? val : val ^ 1));
|
|
2617
2716
|
applyFlat(opt);
|
|
2618
2717
|
if (_key === `stepArea`) {
|
|
2619
2718
|
runStepArea(opt);
|
|
@@ -2624,7 +2723,7 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2624
2723
|
|
|
2625
2724
|
// SideScrollのみ矢印を90度回転 (ゲーム側の changeStepRtn と同じ符号)
|
|
2626
2725
|
rtnOffset = (_key === `playWindow` && opt === `SideScroll` && g_stateObj.rotateEnabled)
|
|
2627
|
-
? 90 * (
|
|
2726
|
+
? 90 * (subSelected === C_FLG_REVERSE2 ? -1 : 1) : 0;
|
|
2628
2727
|
if (g_stateObj.rotateEnabled) {
|
|
2629
2728
|
laneInfo.forEach((lane, j) => {
|
|
2630
2729
|
if (typeof getRtn(j) === `number`) {
|
|
@@ -2651,13 +2750,14 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2651
2750
|
dist[b] = dist[b - 1] + frameSpd + (trace[b] || 0);
|
|
2652
2751
|
}
|
|
2653
2752
|
if (_key === `playWindow`) {
|
|
2654
|
-
addTransform(`mainSprite`, `playWindow`, g_playWindowFunc.get(opt)(), g_transPriority.playWindow);
|
|
2753
|
+
addTransform(`mainSprite`, `playWindow`, withSub(() => g_playWindowFunc.get(opt)()), g_transPriority.playWindow);
|
|
2655
2754
|
}
|
|
2656
2755
|
if (_key === `shaking`) {
|
|
2657
2756
|
Object.assign(work, { drunkXFlg: true, drunkYFlg: true, drunkAxisFlg: false, drunkRotateFlg: false });
|
|
2658
2757
|
}
|
|
2659
2758
|
if (_key === `frzReturn`) {
|
|
2660
2759
|
frz.seq = null;
|
|
2760
|
+
frz.seqNo = 0;
|
|
2661
2761
|
frz.next = frameCnt + 30;
|
|
2662
2762
|
}
|
|
2663
2763
|
};
|
|
@@ -2737,8 +2837,8 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2737
2837
|
if (selected === C_FLG_OFF) return;
|
|
2738
2838
|
if (frz.seq === null) {
|
|
2739
2839
|
if (frameCnt < frz.next) return;
|
|
2740
|
-
const seqs = (g_frzReturnSeqFunc.get(
|
|
2741
|
-
frz.seq = seqs[
|
|
2840
|
+
const seqs = (g_frzReturnSeqFunc.get(subSelected) ?? g_frzReturnSeqFunc.get(`360deg`))();
|
|
2841
|
+
frz.seq = seqs[frz.seqNo++ % seqs.length];
|
|
2742
2842
|
frz.axes = [...g_frzReturnFunc.get(selected)()].filter(Boolean);
|
|
2743
2843
|
frz.start = frameCnt;
|
|
2744
2844
|
}
|
|
@@ -2813,38 +2913,83 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2813
2913
|
spriteIds.forEach(id => delete g_transforms[id]);
|
|
2814
2914
|
g_helpPreviewStop = () => true;
|
|
2815
2915
|
g_helpPreviewMove = _dir => true;
|
|
2916
|
+
g_helpPreviewColumn = _dir => true;
|
|
2816
2917
|
};
|
|
2817
2918
|
g_helpPreviewStop = stop;
|
|
2818
2919
|
|
|
2819
2920
|
// ---------------------------------------------------
|
|
2820
|
-
// 選択肢ボタン (
|
|
2821
|
-
const
|
|
2921
|
+
// 選択肢ボタン (本体の列と、Typeがある設定のみTypeの列)
|
|
2922
|
+
const chipX = baseX + viewW + 15;
|
|
2923
|
+
const colMain = createEmptySprite(area, `pvColMain`, { x: chipX, y: 0, w: colW, h: _height });
|
|
2924
|
+
const colSub = subUse ? createEmptySprite(area, `pvColSub`, { x: chipX + colW + 6, y: 0, w: colW, h: _height }) : null;
|
|
2925
|
+
|
|
2926
|
+
const makeChips = (_list, _prefix, _onSelect, _title = ``) => {
|
|
2927
|
+
const chipH = Math.min(20, Math.floor(_height / _list.length));
|
|
2928
|
+
return _list.map((opt, i) =>
|
|
2929
|
+
createCss2Button(`${_prefix}${i}`, getStgDetailName(opt), () => _onSelect(opt), {
|
|
2930
|
+
x: 0, y: i * chipH, w: colW, h: chipH - 2, siz: 12, title: _title,
|
|
2931
|
+
}, g_cssObj.button_Default));
|
|
2932
|
+
};
|
|
2933
|
+
const chips = makeChips(optList, `pvChip`, opt => selectMain(opt));
|
|
2934
|
+
const subChips = subUse ? makeChips(subList, `pvSubChip`, opt => selectSub(opt), g_msgObj[def.sub] ?? ``) : [];
|
|
2935
|
+
multiAppend(colMain, ...chips);
|
|
2936
|
+
if (subUse) {
|
|
2937
|
+
multiAppend(colSub, ...subChips);
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2822
2940
|
const markChips = () => {
|
|
2823
|
-
|
|
2941
|
+
const mark = (_list, _chips, _current) => _chips.forEach((chip, i) => {
|
|
2824
2942
|
chip.classList.remove(g_cssObj.button_Default, g_cssObj.button_Setting);
|
|
2825
|
-
chip.classList.add(
|
|
2943
|
+
chip.classList.add(_list[i] === _current ? g_cssObj.button_Setting : g_cssObj.button_Default);
|
|
2826
2944
|
});
|
|
2827
|
-
|
|
2945
|
+
mark(optList, chips, selected);
|
|
2946
|
+
if (subUse) {
|
|
2947
|
+
mark(subList, subChips, subSelected);
|
|
2948
|
+
colSub.style.opacity = activeCol === 1 ? 1 : 0.5; // 操作対象でない列は薄く表示
|
|
2949
|
+
}
|
|
2950
|
+
colMain.style.opacity = subUse && activeCol !== 0 ? 0.5 : 1;
|
|
2951
|
+
g_helpSelectHook(subUse ? [selected, subSelected] : [selected], activeCol);
|
|
2828
2952
|
safeExecuteCustomHooks(`g_customJsObj.helpPreview`, g_customJsObj.helpPreview, _key, selected,
|
|
2829
|
-
{ pvFrame, mainSprite, stepSprites, arrowSprites, playW, playH });
|
|
2953
|
+
{ pvFrame, mainSprite, stepSprites, arrowSprites, playW, playH, sub: subSelected });
|
|
2954
|
+
};
|
|
2955
|
+
|
|
2956
|
+
// 本体の選択(選択時は自動移動時間を2倍にする)
|
|
2957
|
+
const selectMain = opt => {
|
|
2958
|
+
autoCnt = -g_limitObj.helpPreviewAutoFrames;
|
|
2959
|
+
activeCol = 0;
|
|
2960
|
+
applyOption(opt);
|
|
2961
|
+
markChips();
|
|
2962
|
+
};
|
|
2963
|
+
|
|
2964
|
+
// Typeの選択 (本体がOFF/Defaultのときは、効果が見える基準の選択肢へ切り替える)
|
|
2965
|
+
const selectSub = opt => {
|
|
2966
|
+
autoCnt = -g_limitObj.helpPreviewAutoFrames;
|
|
2967
|
+
activeCol = 1;
|
|
2968
|
+
subSelected = opt;
|
|
2969
|
+
const base = optList[1];
|
|
2970
|
+
applyOption(selected === optList[0] && optList.includes(base) ? base : selected);
|
|
2971
|
+
markChips();
|
|
2830
2972
|
};
|
|
2831
|
-
const chips = optList.map((opt, i) =>
|
|
2832
|
-
createCss2Button(`pvChip${i}`, getStgDetailName(opt), () => {
|
|
2833
|
-
autoCnt = -g_limitObj.helpPreviewAutoFrames;
|
|
2834
|
-
applyOption(opt);
|
|
2835
|
-
markChips();
|
|
2836
|
-
}, { x: baseX + viewW + 15, y: i * chipH, w: chipW, h: chipH - 2, siz: 12 }, g_cssObj.button_Default)
|
|
2837
|
-
);
|
|
2838
|
-
multiAppend(area, ...chips);
|
|
2839
2973
|
|
|
2974
|
+
// 上下キー: 操作対象の列の選択肢を前後に動かす (端では反対側へ循環)
|
|
2840
2975
|
const moveOption = _dir => {
|
|
2841
2976
|
applyOption(optList[nextPos(optList.indexOf(selected), _dir, optList.length)]);
|
|
2842
2977
|
markChips();
|
|
2843
2978
|
};
|
|
2844
|
-
// 上下キー: 選択肢を前後に動かす (端では反対側へ循環)
|
|
2845
2979
|
g_helpPreviewMove = _dir => {
|
|
2846
|
-
|
|
2847
|
-
|
|
2980
|
+
if (subUse && activeCol === 1) {
|
|
2981
|
+
selectSub(subList[nextPos(subList.indexOf(subSelected), _dir, subList.length)]);
|
|
2982
|
+
} else {
|
|
2983
|
+
autoCnt = -g_limitObj.helpPreviewAutoFrames;
|
|
2984
|
+
moveOption(_dir);
|
|
2985
|
+
}
|
|
2986
|
+
};
|
|
2987
|
+
|
|
2988
|
+
// 左右キー: 操作対象の列を切り替える (←: 本体、→: Type)
|
|
2989
|
+
g_helpPreviewColumn = _dir => {
|
|
2990
|
+
if (!subUse) return;
|
|
2991
|
+
activeCol = _dir > 0 ? 1 : 0;
|
|
2992
|
+
markChips();
|
|
2848
2993
|
};
|
|
2849
2994
|
|
|
2850
2995
|
// 初期表示: 現在値 (OFF/Default の場合は最初の非OFFの選択肢)
|
|
@@ -2854,6 +2999,227 @@ const makeHelpPreview = (_parent, _key, _top, _height) => {
|
|
|
2854
2999
|
timerId = g_timerHandler.setTimeout(tick, 1000 / g_fps);
|
|
2855
3000
|
};
|
|
2856
3001
|
|
|
3002
|
+
/**
|
|
3003
|
+
* リングノート設定 (RingSpeed / RingGuide) 用の詳細ヘルプ内プレビュー
|
|
3004
|
+
* - ゲーム側 makeRing/updateRings と同じ構成 (ガイド円 + 収束する本体円) を縮小表示する
|
|
3005
|
+
* - 判定はオートプレイ相当 (到達フレームで判定済み扱い)。ポインタ判定は再現しない
|
|
3006
|
+
* - RingSpeed: 現在の倍率での出現~到達フレーム数で収束させる
|
|
3007
|
+
* - RingGuide: ガイドの違いが分かるよう、複数のリングが同時に出る固定の速さで表示する
|
|
3008
|
+
* @param {HTMLDivElement} _parent オーバーレイ
|
|
3009
|
+
* @param {string} _key ringSpeed / ringGuide
|
|
3010
|
+
* @param {number} _top 配置Y座標
|
|
3011
|
+
* @param {number} _height 領域の高さ
|
|
3012
|
+
*/
|
|
3013
|
+
const makeRingHelpPreview = (_parent, _key, _top, _height) => {
|
|
3014
|
+
if (document.getElementById(`pvRingFrame`) !== null) {
|
|
3015
|
+
return;
|
|
3016
|
+
}
|
|
3017
|
+
const isSpeed = _key === `ringSpeed`;
|
|
3018
|
+
const optList = g_settings[`${_key}s`];
|
|
3019
|
+
const round2 = _val => Math.round(_val * 100) / 100;
|
|
3020
|
+
const sameVal = (_a, _b) => (isSpeed ? round2(_a) === round2(_b) : _a === _b);
|
|
3021
|
+
|
|
3022
|
+
// チップに出す選択肢 (RingSpeedは数が多いため 0.5x 刻みの代表値のみ)
|
|
3023
|
+
const chipList = isSpeed ? optList.filter(val => Math.round(val * 100) % 50 === 0) : optList;
|
|
3024
|
+
|
|
3025
|
+
const playW = g_headerObj.playingWidth || g_sWidth;
|
|
3026
|
+
const playH = g_headerObj.playingHeight || g_sHeight;
|
|
3027
|
+
const chipW = 100;
|
|
3028
|
+
const areaW = g_sWidth - 60;
|
|
3029
|
+
const rate = Math.min(g_limitObj.helpPreviewMaxRate, _height / playH, (areaW - chipW - 15) / playW);
|
|
3030
|
+
const viewW = Math.round(playW * rate);
|
|
3031
|
+
const baseX = Math.max(Math.round((areaW - viewW - chipW - 15) / 2), 0);
|
|
3032
|
+
|
|
3033
|
+
// 領域内のクリックでオーバーレイが閉じないようにする
|
|
3034
|
+
const area = createEmptySprite(_parent, `helpPreview`, {
|
|
3035
|
+
x: 30, y: _top, w: areaW, h: _height, pointerEvents: C_DIS_AUTO,
|
|
3036
|
+
});
|
|
3037
|
+
g_handler.addListener(area, `click`, evt => evt.stopPropagation());
|
|
3038
|
+
|
|
3039
|
+
// ---------------------------------------------------
|
|
3040
|
+
// ミニプレイ画面 (縮小表示)
|
|
3041
|
+
const scaler = createEmptySprite(area, `pvRingScale`, {
|
|
3042
|
+
x: baseX, y: 0, w: playW, h: playH, transform: `scale(${rate})`, transformOrigin: `top left`,
|
|
3043
|
+
});
|
|
3044
|
+
const pvFrame = createEmptySprite(scaler, `pvRingFrame`, {
|
|
3045
|
+
w: playW, h: playH, background: g_headerObj.baseBrightFlg ? `#eeeeee` : `#111111`,
|
|
3046
|
+
border: `1px solid #444444`, boxSizing: `border-box`, overflow: `hidden`,
|
|
3047
|
+
});
|
|
3048
|
+
const ringSprite = createEmptySprite(pvFrame, `pvRingSprite`, { w: playW, h: playH });
|
|
3049
|
+
|
|
3050
|
+
// ---------------------------------------------------
|
|
3051
|
+
// プレビュー状態
|
|
3052
|
+
const radius = g_ringObj.radius;
|
|
3053
|
+
const ringColor = g_headerObj.baseBrightFlg ? g_ringObj.colorLight : g_ringObj.color;
|
|
3054
|
+
const spots = [[0.3, 0.3], [0.65, 0.4], [0.45, 0.62], [0.75, 0.72], [0.25, 0.68], [0.55, 0.25]];
|
|
3055
|
+
const holdFrames = 6; // 到達後に残すフレーム数 (判定の瞬間を見せる)
|
|
3056
|
+
const guideDuration = 90, guideInterval = 28; // RingGuide用 (同時に3個前後出る)
|
|
3057
|
+
const speedInterval = 45; // RingSpeed用
|
|
3058
|
+
|
|
3059
|
+
let selected = isSpeed
|
|
3060
|
+
? (optList.find(val => sameVal(val, g_stateObj.ringSpeed)) ?? optList[0])
|
|
3061
|
+
: (optList.includes(g_stateObj.ringGuide) ? g_stateObj.ringGuide : optList[0]);
|
|
3062
|
+
let rings = [];
|
|
3063
|
+
let frameCnt = 0;
|
|
3064
|
+
let noteCnt = 0;
|
|
3065
|
+
let autoCnt = 0;
|
|
3066
|
+
let timerId = null;
|
|
3067
|
+
|
|
3068
|
+
// ゲーム側 getRingDuration (ringSpeedData なし) と同じ式
|
|
3069
|
+
const getTotal = () => isSpeed ? Math.max(Math.round(g_ringObj.baseFrame / selected), 1) : guideDuration;
|
|
3070
|
+
const getInterval = () => isSpeed ? speedInterval : guideInterval;
|
|
3071
|
+
const guideMode = () => isSpeed ? `ALL` : selected;
|
|
3072
|
+
|
|
3073
|
+
const clearRings = () => {
|
|
3074
|
+
rings.forEach(ring => {
|
|
3075
|
+
ring.guide.remove();
|
|
3076
|
+
ring.body.remove();
|
|
3077
|
+
});
|
|
3078
|
+
rings = [];
|
|
3079
|
+
};
|
|
3080
|
+
|
|
3081
|
+
const spawn = () => {
|
|
3082
|
+
const [rx, ry] = spots[noteCnt % spots.length];
|
|
3083
|
+
const no = ++noteCnt;
|
|
3084
|
+
const base = {
|
|
3085
|
+
x: Math.round(playW * rx) - radius, y: Math.round(playH * ry) - radius,
|
|
3086
|
+
w: radius * 2, h: radius * 2, borderRadius: `50%`, boxSizing: `border-box`,
|
|
3087
|
+
};
|
|
3088
|
+
const guide = createEmptySprite(ringSprite, `pvRingGuide${no}`, {
|
|
3089
|
+
...base, border: `solid ${g_ringObj.lineWidth}px ${g_ringObj.guideColor}`,
|
|
3090
|
+
visibility: guideMode() === `Hidden` ? `hidden` : `visible`,
|
|
3091
|
+
});
|
|
3092
|
+
const body = createEmptySprite(ringSprite, `pvRingBody${no}`, {
|
|
3093
|
+
...base, border: `solid ${g_ringObj.lineWidth}px ${ringColor}`,
|
|
3094
|
+
transform: `scale(${g_ringObj.startRate})`,
|
|
3095
|
+
});
|
|
3096
|
+
const total = getTotal();
|
|
3097
|
+
rings.push({ guide, body, total, cnt: total + 1, guideVisible: guideMode() !== `Hidden` });
|
|
3098
|
+
};
|
|
3099
|
+
|
|
3100
|
+
const updateRings = () => {
|
|
3101
|
+
rings = rings.filter(ring => {
|
|
3102
|
+
ring.cnt--;
|
|
3103
|
+
if (ring.cnt < -holdFrames) {
|
|
3104
|
+
ring.guide.remove();
|
|
3105
|
+
ring.body.remove();
|
|
3106
|
+
return false;
|
|
3107
|
+
}
|
|
3108
|
+
// 本体の大きさ (cnt === 0 でガイドと一致)
|
|
3109
|
+
const t = Math.min(Math.max(1 - ring.cnt / ring.total, 0), 1);
|
|
3110
|
+
ring.body.style.transform = `scale(${g_ringObj.startRate + (1 - g_ringObj.startRate) * t})`;
|
|
3111
|
+
ring.body.style.opacity = ring.cnt > 0 ? 1 : Math.max((holdFrames + ring.cnt) / holdFrames, 0);
|
|
3112
|
+
return true;
|
|
3113
|
+
});
|
|
3114
|
+
|
|
3115
|
+
// ガイド表示 (ゲーム側 updateRings と同じ条件。Next: 到達済み or 次に判定するリングのみ)
|
|
3116
|
+
const mode = guideMode();
|
|
3117
|
+
const nextRing = rings.find(ring => ring.cnt > 0);
|
|
3118
|
+
rings.forEach(ring => {
|
|
3119
|
+
const visible = mode === `Hidden` ? false
|
|
3120
|
+
: mode === `Next` ? (ring.cnt <= 0 || ring === nextRing) : true;
|
|
3121
|
+
if (ring.guideVisible !== visible) {
|
|
3122
|
+
ring.guide.style.visibility = visible ? `visible` : `hidden`;
|
|
3123
|
+
ring.guideVisible = visible;
|
|
3124
|
+
}
|
|
3125
|
+
});
|
|
3126
|
+
};
|
|
3127
|
+
|
|
3128
|
+
// ---------------------------------------------------
|
|
3129
|
+
// 選択肢ボタン
|
|
3130
|
+
const colMain = createEmptySprite(area, `pvColMain`, { x: baseX + viewW + 15, y: 0, w: chipW, h: _height });
|
|
3131
|
+
const chipH = Math.min(20, Math.floor(_height / (chipList.length + 2)));
|
|
3132
|
+
const getChipName = opt => isSpeed ? `${opt} ${g_lblNameObj.multi}` : getStgDetailName(opt);
|
|
3133
|
+
const chips = chipList.map((opt, i) =>
|
|
3134
|
+
createCss2Button(`pvRingChip${i}`, getChipName(opt), () => selectOption(opt), {
|
|
3135
|
+
x: 0, y: i * chipH, w: chipW, h: chipH - 2, siz: 12,
|
|
3136
|
+
}, g_cssObj.button_Default));
|
|
3137
|
+
multiAppend(colMain, ...chips);
|
|
3138
|
+
|
|
3139
|
+
// RingSpeed: 現在の倍率と出現~到達フレーム数
|
|
3140
|
+
const lblInfo = createDivCss2Label(`pvRingInfo`, ``, {
|
|
3141
|
+
x: 0, y: chipList.length * chipH + 4, w: chipW, h: 32, siz: 12, align: C_ALIGN_LEFT,
|
|
3142
|
+
});
|
|
3143
|
+
colMain.appendChild(lblInfo);
|
|
3144
|
+
|
|
3145
|
+
const markChips = () => {
|
|
3146
|
+
chips.forEach((chip, i) => {
|
|
3147
|
+
chip.classList.remove(g_cssObj.button_Default, g_cssObj.button_Setting);
|
|
3148
|
+
chip.classList.add(sameVal(chipList[i], selected) ? g_cssObj.button_Setting : g_cssObj.button_Default);
|
|
3149
|
+
});
|
|
3150
|
+
lblInfo.textContent = isSpeed ? `${round2(selected)} ${g_lblNameObj.multi} (${getTotal()}f)` : ``;
|
|
3151
|
+
g_helpSelectHook([selected], 0);
|
|
3152
|
+
safeExecuteCustomHooks(`g_customJsObj.helpPreview`, g_customJsObj.helpPreview, _key, selected,
|
|
3153
|
+
{ pvFrame, ringSprite, playW, playH });
|
|
3154
|
+
};
|
|
3155
|
+
|
|
3156
|
+
const applyOption = opt => {
|
|
3157
|
+
selected = opt;
|
|
3158
|
+
clearRings();
|
|
3159
|
+
frameCnt = 0;
|
|
3160
|
+
spawn();
|
|
3161
|
+
};
|
|
3162
|
+
const selectOption = opt => {
|
|
3163
|
+
autoCnt = -g_limitObj.helpPreviewAutoFrames;
|
|
3164
|
+
applyOption(opt);
|
|
3165
|
+
markChips();
|
|
3166
|
+
};
|
|
3167
|
+
|
|
3168
|
+
// 上下キー: RingSpeedは内側ボタンと同じ5つ刻み (0.25x)、RingGuideは1つずつ
|
|
3169
|
+
const moveOption = _step => {
|
|
3170
|
+
const idx = Math.max(optList.findIndex(val => sameVal(val, selected)), 0);
|
|
3171
|
+
applyOption(optList[nextPos(idx, _step, optList.length)]);
|
|
3172
|
+
markChips();
|
|
3173
|
+
};
|
|
3174
|
+
// 自動切替: RingSpeedは代表値 (チップ) を順に巡回
|
|
3175
|
+
const autoOption = () => {
|
|
3176
|
+
if (!isSpeed) {
|
|
3177
|
+
moveOption(1);
|
|
3178
|
+
return;
|
|
3179
|
+
}
|
|
3180
|
+
const next = chipList.find(val => round2(val) > round2(selected));
|
|
3181
|
+
applyOption(next ?? chipList[0]);
|
|
3182
|
+
markChips();
|
|
3183
|
+
};
|
|
3184
|
+
|
|
3185
|
+
const stop = () => {
|
|
3186
|
+
if (timerId !== null) {
|
|
3187
|
+
g_timerHandler.clearTimeout(timerId);
|
|
3188
|
+
}
|
|
3189
|
+
timerId = null;
|
|
3190
|
+
g_helpPreviewStop = () => true;
|
|
3191
|
+
g_helpPreviewMove = _dir => true;
|
|
3192
|
+
g_helpPreviewColumn = _dir => true;
|
|
3193
|
+
};
|
|
3194
|
+
g_helpPreviewStop = stop;
|
|
3195
|
+
g_helpPreviewMove = _dir => {
|
|
3196
|
+
autoCnt = -g_limitObj.helpPreviewAutoFrames;
|
|
3197
|
+
moveOption(_dir * (isSpeed ? g_settings.ringSpeedTerms[1] : 1));
|
|
3198
|
+
};
|
|
3199
|
+
|
|
3200
|
+
const tick = () => {
|
|
3201
|
+
if (document.getElementById(`pvRingFrame`) === null) {
|
|
3202
|
+
stop();
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
frameCnt++;
|
|
3206
|
+
if (g_limitObj.helpPreviewAutoFrames > 0 && ++autoCnt >= g_limitObj.helpPreviewAutoFrames) {
|
|
3207
|
+
autoCnt = 0;
|
|
3208
|
+
autoOption();
|
|
3209
|
+
}
|
|
3210
|
+
if (frameCnt % getInterval() === 0) {
|
|
3211
|
+
spawn();
|
|
3212
|
+
}
|
|
3213
|
+
updateRings();
|
|
3214
|
+
timerId = g_timerHandler.setTimeout(tick, 1000 / g_fps);
|
|
3215
|
+
};
|
|
3216
|
+
|
|
3217
|
+
// 初期表示: 現在の設定値
|
|
3218
|
+
applyOption(selected);
|
|
3219
|
+
markChips();
|
|
3220
|
+
timerId = g_timerHandler.setTimeout(tick, 1000 / g_fps);
|
|
3221
|
+
};
|
|
3222
|
+
|
|
2857
3223
|
/**
|
|
2858
3224
|
* 設定名の置き換え処理(未指定時は原則g_lblNameObjの値を使用、明示的な指定がある場合のみ元の名前を返却)
|
|
2859
3225
|
* @param {string} _name
|
|
@@ -3005,7 +3371,8 @@ const gaugeFormat = (_mode, _border, _rcv, _dmg, _init, _lifeValFlg) => {
|
|
|
3005
3371
|
let rcvText = round2(_rcv), dmgText = round2(_dmg);
|
|
3006
3372
|
let realRcv = _rcv, realDmg = _dmg;
|
|
3007
3373
|
const allCnt = sumData(g_detailObj.arrowCnt[g_stateObj.scoreId]) +
|
|
3008
|
-
(g_headerObj.frzStartjdgUse ? 2 : 1) * sumData(g_detailObj.frzCnt[g_stateObj.scoreId])
|
|
3374
|
+
(g_headerObj.frzStartjdgUse ? 2 : 1) * sumData(g_detailObj.frzCnt[g_stateObj.scoreId]) +
|
|
3375
|
+
g_detailObj.ringCnt[g_stateObj.scoreId];
|
|
3009
3376
|
|
|
3010
3377
|
// ゲージ設定が矢印数依存の場合、実際の値に変換して表示する
|
|
3011
3378
|
// 表示上、計算した値は小数第二位までの表示とする(それ以外はそのまま)
|
|
@@ -3985,8 +4352,30 @@ const createSettingsDisplayWindow = _sprite => {
|
|
|
3985
4352
|
unitName: g_lblNameObj.pixel,
|
|
3986
4353
|
addRFunc: () => updatePreviewHitPositionLine(g_stateObj.hitPosition),
|
|
3987
4354
|
});
|
|
4355
|
+
|
|
4356
|
+
if (hasRingNotes()) {
|
|
4357
|
+
// ---------------------------------------------------
|
|
4358
|
+
// リングノートの速度 (RingSpeed)
|
|
4359
|
+
// 縦位置: 9.8
|
|
4360
|
+
g_settings.ringSpeedNum = Math.max(g_settings.ringSpeeds.indexOf(g_stateObj.ringSpeed), 0);
|
|
4361
|
+
g_stateObj.ringSpeed = g_settings.ringSpeeds[g_settings.ringSpeedNum];
|
|
4362
|
+
createGeneralSetting(spriteList.ringSpeed, `ringSpeed`, { skipTerms: g_settings.ringSpeedTerms, unitName: ` ${g_lblNameObj.multi}` });
|
|
4363
|
+
|
|
4364
|
+
// ---------------------------------------------------
|
|
4365
|
+
// リングノートのガイド表示 (RingGuide)
|
|
4366
|
+
// 縦位置: 10.8
|
|
4367
|
+
g_settings.ringGuideNum = Math.max(g_settings.ringGuides.indexOf(g_stateObj.ringGuide), 0);
|
|
4368
|
+
g_stateObj.ringGuide = g_settings.ringGuides[g_settings.ringGuideNum];
|
|
4369
|
+
createGeneralSetting(spriteList.ringGuide, `ringGuide`);
|
|
4370
|
+
}
|
|
3988
4371
|
};
|
|
3989
4372
|
|
|
4373
|
+
/**
|
|
4374
|
+
* 選択中の譜面にリングノートが含まれるかをチェック
|
|
4375
|
+
* @returns {boolean}
|
|
4376
|
+
*/
|
|
4377
|
+
const hasRingNotes = () => (g_detailObj.ringCnt[g_stateObj.scoreId] ?? 0) > 0;
|
|
4378
|
+
|
|
3990
4379
|
/**
|
|
3991
4380
|
* Displayボタンを切り替えたときに連動して切り替えるボタンの設定
|
|
3992
4381
|
* @param {object} _headerObj
|
|
@@ -4155,7 +4544,10 @@ const createGeneralSettingEx = (_spriteList, _name, { defaultList = [C_FLG_OFF],
|
|
|
4155
4544
|
funcEx();
|
|
4156
4545
|
createExpandedScView(_name);
|
|
4157
4546
|
},
|
|
4158
|
-
|
|
4547
|
+
// 拡張ボタンの説明文表示は「拡張元」設定のヘルプが長い場合、詳細ヘルプ表示側で表示するため省略する
|
|
4548
|
+
title: isLongHelp(g_msgObj[`${_name}`] ?? ``) || g_helpPreviewDefs[_name] !== undefined
|
|
4549
|
+
? ``
|
|
4550
|
+
: (g_msgObj[`${_name}Type`] ?? ``),
|
|
4159
4551
|
}, g_cssObj.button_Default, g_cssObj.button_RevON);
|
|
4160
4552
|
|
|
4161
4553
|
/**
|