danoniplus 31.0.1 → 31.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/js/danoni_main.js +98 -43
- package/js/lib/danoni_constants.js +8 -3
- package/package.json +1 -1
package/js/danoni_main.js
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Source by tickle
|
|
6
6
|
* Created : 2018/10/08
|
|
7
|
-
* Revised : 2023/
|
|
7
|
+
* Revised : 2023/04/01
|
|
8
8
|
*
|
|
9
9
|
* https://github.com/cwtickle/danoniplus
|
|
10
10
|
*/
|
|
11
|
-
const g_version = `Ver 31.0
|
|
12
|
-
const g_revisedDate = `2023/
|
|
11
|
+
const g_version = `Ver 31.2.0`;
|
|
12
|
+
const g_revisedDate = `2023/04/01`;
|
|
13
13
|
const g_alphaVersion = ``;
|
|
14
14
|
|
|
15
15
|
// カスタム用バージョン (danoni_custom.js 等で指定可)
|
|
@@ -402,6 +402,39 @@ const makeBaseArray = (_array, _minLength, _defaultVal) => {
|
|
|
402
402
|
return baseArray;
|
|
403
403
|
};
|
|
404
404
|
|
|
405
|
+
/**
|
|
406
|
+
* 配列から上位N番目までに一致する位置を取得
|
|
407
|
+
*
|
|
408
|
+
* ex. 上位3番目 (_num = 3) の場合
|
|
409
|
+
* [1, 3, 2, 4, 6, 4, 5] -> [[4], [6], [3, 5]]
|
|
410
|
+
* [9, 6, 9, 9, 8, 7, 5] -> [[0, 2, 3]]
|
|
411
|
+
* @param {array} _array
|
|
412
|
+
* @param {number} _num
|
|
413
|
+
* @returns
|
|
414
|
+
*/
|
|
415
|
+
const getMaxValIdxs = (_array, _num = 1) => {
|
|
416
|
+
const getMaxVal = (_a, _b) => Math.max(_a, _b);
|
|
417
|
+
let baseArray = _array.concat();
|
|
418
|
+
const maxIdxs = [];
|
|
419
|
+
|
|
420
|
+
for (let j = 0; j < _num; j++) {
|
|
421
|
+
maxIdxs[j] = [];
|
|
422
|
+
const maxVal = baseArray.reduce((a, b) => getMaxVal(a, b));
|
|
423
|
+
_array.map((val, idx) => {
|
|
424
|
+
if (val === maxVal) {
|
|
425
|
+
maxIdxs[j].push(idx);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
baseArray = baseArray.filter(val => val < maxVal);
|
|
429
|
+
|
|
430
|
+
// 同率で上位N番目まで取得した場合は途中で終了
|
|
431
|
+
if (baseArray.length === 0 || maxIdxs.flat().length >= _num) {
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return maxIdxs;
|
|
436
|
+
};
|
|
437
|
+
|
|
405
438
|
/**
|
|
406
439
|
* 部分一致検索(リストのいずれかに合致、大小文字問わず)
|
|
407
440
|
* @param {string} _str 検索文字
|
|
@@ -2199,7 +2232,7 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
2199
2232
|
|
|
2200
2233
|
// 譜面密度グラフ用のデータ作成
|
|
2201
2234
|
const noteCnt = { arrow: [], frz: [] };
|
|
2202
|
-
const densityData = [...Array(
|
|
2235
|
+
const densityData = [...Array(g_limitObj.densityDivision)].fill(0);
|
|
2203
2236
|
let allData = 0;
|
|
2204
2237
|
|
|
2205
2238
|
const types = [`arrow`, `frz`];
|
|
@@ -2214,7 +2247,7 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
2214
2247
|
if (isNaN(parseFloat(note))) {
|
|
2215
2248
|
return;
|
|
2216
2249
|
}
|
|
2217
|
-
const point = Math.floor((note - firstArrowFrame) / playingFrame *
|
|
2250
|
+
const point = Math.floor((note - firstArrowFrame) / playingFrame * g_limitObj.densityDivision);
|
|
2218
2251
|
if (point >= 0) {
|
|
2219
2252
|
densityData[point]++;
|
|
2220
2253
|
noteCnt[types[m]][j]++;
|
|
@@ -2227,13 +2260,13 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
2227
2260
|
|
|
2228
2261
|
fullData = fullData.filter(val => !isNaN(parseFloat(val))).sort((a, b) => a - b);
|
|
2229
2262
|
let pushCnt = 1;
|
|
2230
|
-
const density2PushData = [...Array(
|
|
2231
|
-
const density3PushData = [...Array(
|
|
2263
|
+
const density2PushData = [...Array(g_limitObj.densityDivision)].fill(0);
|
|
2264
|
+
const density3PushData = [...Array(g_limitObj.densityDivision)].fill(0);
|
|
2232
2265
|
fullData.forEach((note, j) => {
|
|
2233
2266
|
if (fullData[j] === fullData[j + 1]) {
|
|
2234
2267
|
pushCnt++;
|
|
2235
2268
|
} else {
|
|
2236
|
-
const point = Math.floor((note - firstArrowFrame) / playingFrame *
|
|
2269
|
+
const point = Math.floor((note - firstArrowFrame) / playingFrame * g_limitObj.densityDivision);
|
|
2237
2270
|
if (point >= 0) {
|
|
2238
2271
|
if (pushCnt >= 2) {
|
|
2239
2272
|
density2PushData[point] += pushCnt;
|
|
@@ -2252,8 +2285,8 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
2252
2285
|
|
|
2253
2286
|
const storeDensity = _densityData => {
|
|
2254
2287
|
const dataList = [];
|
|
2255
|
-
for (let j = 0; j <
|
|
2256
|
-
dataList.push(allData === 0 ? 0 : Math.round(_densityData[j] / allData *
|
|
2288
|
+
for (let j = 0; j < g_limitObj.densityDivision; j++) {
|
|
2289
|
+
dataList.push(allData === 0 ? 0 : Math.round(_densityData[j] / allData * g_limitObj.densityDivision * 10000) / 100);
|
|
2257
2290
|
}
|
|
2258
2291
|
return dataList;
|
|
2259
2292
|
};
|
|
@@ -2270,7 +2303,7 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
2270
2303
|
g_detailObj.density2PushDiff[_scoreId] = diffArray(g_detailObj.density2PushData[_scoreId], g_detailObj.density3PushData[_scoreId]);
|
|
2271
2304
|
g_detailObj.density3PushDiff[_scoreId] = g_detailObj.density3PushData[_scoreId].concat();
|
|
2272
2305
|
|
|
2273
|
-
g_detailObj.maxDensity[_scoreId] = densityData.
|
|
2306
|
+
g_detailObj.maxDensity[_scoreId] = getMaxValIdxs(densityData, g_limitObj.densityMaxVals).flat();
|
|
2274
2307
|
|
|
2275
2308
|
g_detailObj.arrowCnt[_scoreId] = noteCnt.arrow.concat();
|
|
2276
2309
|
g_detailObj.frzCnt[_scoreId] = noteCnt.frz.concat();
|
|
@@ -3184,15 +3217,22 @@ const resetBaseColorList = (_baseObj, _dosObj, { scoreId = `` } = {}) => {
|
|
|
3184
3217
|
|
|
3185
3218
|
const obj = {};
|
|
3186
3219
|
const scoreIdHeader = setScoreIdHeader(scoreId);
|
|
3220
|
+
const getRefData = (_header, _dataName) => {
|
|
3221
|
+
const data = _dosObj[`${_header}${_dataName}`];
|
|
3222
|
+
return data?.startsWith(_header) ? _dosObj[data] : data;
|
|
3223
|
+
}
|
|
3187
3224
|
|
|
3188
3225
|
[``, `Shadow`].forEach(pattern => {
|
|
3189
|
-
const
|
|
3190
|
-
const
|
|
3191
|
-
|
|
3192
|
-
const
|
|
3226
|
+
const _arrowCommon = `set${pattern}Color`;
|
|
3227
|
+
const _frzCommon = `frz${pattern}Color`;
|
|
3228
|
+
|
|
3229
|
+
const _name = `${_arrowCommon}${scoreIdHeader}`;
|
|
3230
|
+
const _frzName = `${_frzCommon}${scoreIdHeader}`;
|
|
3231
|
+
const _arrowInit = `${_arrowCommon}Init`;
|
|
3232
|
+
const _frzInit = `${_frzCommon}Init`;
|
|
3193
3233
|
|
|
3194
|
-
const arrowColorTxt =
|
|
3195
|
-
const frzColorTxt =
|
|
3234
|
+
const arrowColorTxt = getRefData(_arrowCommon, scoreIdHeader) || _dosObj[_arrowCommon];
|
|
3235
|
+
const frzColorTxt = getRefData(_frzCommon, scoreIdHeader) || _dosObj[_frzCommon];
|
|
3196
3236
|
|
|
3197
3237
|
// 矢印色
|
|
3198
3238
|
Object.keys(_baseObj.dfColorgrdSet).forEach(type => {
|
|
@@ -4601,12 +4641,12 @@ const createOptionWindow = _sprite => {
|
|
|
4601
4641
|
const canvas = document.querySelector(`#graphDensity`);
|
|
4602
4642
|
const context = canvas.getContext(`2d`);
|
|
4603
4643
|
drawBaseLine(context);
|
|
4604
|
-
for (let j = 0; j <
|
|
4644
|
+
for (let j = 0; j < g_limitObj.densityDivision; j++) {
|
|
4605
4645
|
context.beginPath();
|
|
4606
4646
|
[``, `2Push`, `3Push`].forEach(val => {
|
|
4607
|
-
context.fillStyle = (
|
|
4608
|
-
context.fillRect(16 * j * 16 /
|
|
4609
|
-
15.5 * 16 /
|
|
4647
|
+
context.fillStyle = (g_detailObj.maxDensity[_scoreId].includes(j) ? g_graphColorObj[`max${val}`] : g_graphColorObj[`default${val}`]);
|
|
4648
|
+
context.fillRect(16 * j * 16 / g_limitObj.densityDivision + 30, 195 - 9 * g_detailObj[`density${val}Data`][_scoreId][j] / 10,
|
|
4649
|
+
15.5 * 16 / g_limitObj.densityDivision, 9 * g_detailObj[`density${val}Diff`][_scoreId][j] / 10
|
|
4610
4650
|
);
|
|
4611
4651
|
});
|
|
4612
4652
|
context.stroke();
|
|
@@ -7131,10 +7171,11 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7131
7171
|
* @param {string} _footer
|
|
7132
7172
|
*/
|
|
7133
7173
|
const setSpeedData = (_header, _scoreNo, _footer = `_data`) => {
|
|
7174
|
+
const dosSpeedData = getRefData(_header, `${_scoreNo}${_footer}`);
|
|
7134
7175
|
const speedData = [];
|
|
7135
7176
|
|
|
7136
|
-
if (hasVal(
|
|
7137
|
-
const tmpArrayData = splitLF(
|
|
7177
|
+
if (hasVal(dosSpeedData) && g_stateObj.d_speed === C_FLG_ON) {
|
|
7178
|
+
const tmpArrayData = splitLF(dosSpeedData);
|
|
7138
7179
|
|
|
7139
7180
|
tmpArrayData.filter(data => hasVal(data)).forEach(tmpData => {
|
|
7140
7181
|
const tmpSpeedData = tmpData.split(`,`);
|
|
@@ -7173,11 +7214,12 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7173
7214
|
* @param {number} _scoreNo
|
|
7174
7215
|
*/
|
|
7175
7216
|
const setColorData = (_header, _scoreNo) => {
|
|
7217
|
+
const dosColorData = getRefData(_header, `${_scoreNo}_data`);
|
|
7176
7218
|
const colorData = [];
|
|
7177
7219
|
const allFlg = (_header.charAt(0) === `a`);
|
|
7178
7220
|
|
|
7179
|
-
if (hasVal(
|
|
7180
|
-
const tmpArrayData = splitLF(
|
|
7221
|
+
if (hasVal(dosColorData) && g_stateObj.d_color === C_FLG_ON) {
|
|
7222
|
+
const tmpArrayData = splitLF(dosColorData);
|
|
7181
7223
|
|
|
7182
7224
|
tmpArrayData.filter(data => hasVal(data)).forEach(tmpData => {
|
|
7183
7225
|
const tmpColorData = tmpData.split(`,`);
|
|
@@ -7206,7 +7248,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7206
7248
|
* @param {number} _scoreNo
|
|
7207
7249
|
*/
|
|
7208
7250
|
const setCssMotionData = (_header, _scoreNo) => {
|
|
7209
|
-
const dosCssMotionData =
|
|
7251
|
+
const dosCssMotionData = getRefData(`${_header}Motion`, `${_scoreNo}_data`) || _dosObj[`${_header}Motion_data`];
|
|
7210
7252
|
const cssMotionData = [];
|
|
7211
7253
|
|
|
7212
7254
|
if (hasVal(dosCssMotionData) && g_stateObj.d_arroweffect === C_FLG_ON) {
|
|
@@ -7232,7 +7274,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7232
7274
|
* @param {number} _scoreNo
|
|
7233
7275
|
*/
|
|
7234
7276
|
const setScrollchData = (_scoreNo) => {
|
|
7235
|
-
const dosScrollchData =
|
|
7277
|
+
const dosScrollchData = getRefData(`scrollch`, `${_scoreNo}_data`) || _dosObj.scrollch_data;
|
|
7236
7278
|
const scrollchData = [];
|
|
7237
7279
|
|
|
7238
7280
|
if (hasVal(dosScrollchData)) {
|
|
@@ -7252,6 +7294,18 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7252
7294
|
return [];
|
|
7253
7295
|
};
|
|
7254
7296
|
|
|
7297
|
+
/**
|
|
7298
|
+
* 譜面データに別の関連名が含まれていた場合、関連名の変数を返す
|
|
7299
|
+
* 例) |backA2_data=back_data| -> back_dataで定義された値を使用
|
|
7300
|
+
* @param {string} _header
|
|
7301
|
+
* @param {string} _dataName
|
|
7302
|
+
* @returns
|
|
7303
|
+
*/
|
|
7304
|
+
const getRefData = (_header, _dataName) => {
|
|
7305
|
+
const data = _dosObj[`${_header}${_dataName}`];
|
|
7306
|
+
return data?.startsWith(_header) ? _dosObj[data] : data;
|
|
7307
|
+
}
|
|
7308
|
+
|
|
7255
7309
|
/**
|
|
7256
7310
|
* 譜面データの優先順配列の取得
|
|
7257
7311
|
* @param {string} _header
|
|
@@ -7260,10 +7314,10 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7260
7314
|
* @returns
|
|
7261
7315
|
*/
|
|
7262
7316
|
const getPriorityList = (_header, _type, _scoreNo) => [
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7317
|
+
getRefData(_header, `${_type}${g_localeObj.val}${_scoreNo}_data`),
|
|
7318
|
+
getRefData(_header, `${_type}${g_localeObj.val}_data`),
|
|
7319
|
+
getRefData(_header, `${_type}${_scoreNo}_data`),
|
|
7320
|
+
getRefData(_header, `${_type}_data`)
|
|
7267
7321
|
];
|
|
7268
7322
|
|
|
7269
7323
|
/**
|
|
@@ -7392,16 +7446,17 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7392
7446
|
|
|
7393
7447
|
/**
|
|
7394
7448
|
* リザルトモーションデータ(結果画面用背景・マスクデータ)の分解
|
|
7395
|
-
* @param {string} _header
|
|
7396
|
-
* @param {string}
|
|
7397
|
-
* @param {string}
|
|
7449
|
+
* @param {string} _header 背景、マスク (back, mask)
|
|
7450
|
+
* @param {string} _resultType リザルトモーションの種類 (result, failedB, failedS)
|
|
7451
|
+
* @param {string} _scoreNo 譜面番号
|
|
7452
|
+
* @param {string} _defaultType _resultTypeが無いときの代替名
|
|
7398
7453
|
*/
|
|
7399
|
-
const makeBackgroundResultData = (_header, _scoreNo,
|
|
7454
|
+
const makeBackgroundResultData = (_header, _resultType, _scoreNo, _defaultType = ``) => {
|
|
7400
7455
|
const dataList = [];
|
|
7401
|
-
const
|
|
7402
|
-
|
|
7403
|
-
if (
|
|
7404
|
-
|
|
7456
|
+
const addDataList = (_type = ``) => dataList.push(...getPriorityList(_header, _type, _scoreNo));
|
|
7457
|
+
addDataList(_resultType);
|
|
7458
|
+
if (_defaultType !== ``) {
|
|
7459
|
+
addDataList(_defaultType);
|
|
7405
7460
|
}
|
|
7406
7461
|
|
|
7407
7462
|
const data = dataList.find((v) => v !== undefined);
|
|
@@ -7479,17 +7534,17 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
|
|
|
7479
7534
|
} else {
|
|
7480
7535
|
g_animationData.forEach(sprite => {
|
|
7481
7536
|
[g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] =
|
|
7482
|
-
makeBackgroundResultData(
|
|
7537
|
+
makeBackgroundResultData(sprite, `result`, scoreIdHeader);
|
|
7483
7538
|
[g_headerObj[`${sprite}FailedData`], g_headerObj[`${sprite}FailedMaxDepth`]] =
|
|
7484
|
-
makeBackgroundResultData(
|
|
7539
|
+
makeBackgroundResultData(sprite, `failed${g_stateObj.lifeMode.slice(0, 1)}`, scoreIdHeader, `result`);
|
|
7485
7540
|
});
|
|
7486
7541
|
}
|
|
7487
7542
|
|
|
7488
7543
|
// キー変化定義
|
|
7489
7544
|
obj.keychFrames = [0];
|
|
7490
7545
|
obj.keychTarget = [`0`];
|
|
7491
|
-
if (hasVal(
|
|
7492
|
-
const keychdata = splitLF2(
|
|
7546
|
+
if (hasVal(getRefData(`keych`, `${scoreIdHeader}_data`))) {
|
|
7547
|
+
const keychdata = splitLF2(getRefData(`keych`, `${scoreIdHeader}_data`), `,`);
|
|
7493
7548
|
obj.keychFrames.push(...keychdata.filter((val, j) => j % 2 === 0));
|
|
7494
7549
|
obj.keychTarget.push(...keychdata.filter((val, j) => j % 2 === 1));
|
|
7495
7550
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Source by tickle
|
|
7
7
|
* Created : 2019/11/19
|
|
8
|
-
* Revised : 2023/03/
|
|
8
|
+
* Revised : 2023/03/25 (v31.1.0)
|
|
9
9
|
*
|
|
10
10
|
* https://github.com/cwtickle/danoniplus
|
|
11
11
|
*/
|
|
@@ -561,6 +561,9 @@ const g_settingBtnObj = {
|
|
|
561
561
|
const g_limitObj = {
|
|
562
562
|
adjustment: 30,
|
|
563
563
|
hitPosition: 50,
|
|
564
|
+
|
|
565
|
+
densityDivision: 16,
|
|
566
|
+
densityMaxVals: 3,
|
|
564
567
|
};
|
|
565
568
|
const C_MAX_ADJUSTMENT = 30;
|
|
566
569
|
const C_MAX_SPEED = 10;
|
|
@@ -2737,10 +2740,11 @@ const g_lang_msgObj = {
|
|
|
2737
2740
|
hitPosition: `判定位置にズレを感じる場合、\n数値を変えることで判定の中央位置を1px単位(プラス:手前, マイナス:奥側)で調整することができます。\n早押し・遅押し傾向にある場合に使用します。`,
|
|
2738
2741
|
|
|
2739
2742
|
configType: `キーコンフィグ対象を切り替えます。\n[Main] メインキーのみ, [Replaced] 代替キーのみ, [ALL] 全て`,
|
|
2740
|
-
colorType:
|
|
2743
|
+
colorType: `矢印・フリーズアローの配色セットをあらかじめ定義されたリストから選択できます。\nType1~4選択時は色変化が自動でOFFになり、カラーピッカーから好きな色に変更できます。\n[Type0] グラデーション切替, [Type1~4] デフォルトパターン`,
|
|
2741
2744
|
imgType: `矢印・フリーズアローなどのオブジェクトの見た目を変更します。`,
|
|
2742
2745
|
colorGroup: `矢印・フリーズアロー色グループの割り当てパターンを変更します。`,
|
|
2743
2746
|
shuffleGroup: `Mirror/Asym-Mirror/Random/S-Random選択時、シャッフルするグループを変更します。\n矢印の上にある同じ数字同士でシャッフルします。`,
|
|
2747
|
+
stepRtnGroup: `矢印などノーツの種類、回転に関するパターンを切り替えます。\nあらかじめ設定されている場合のみ変更可能です。`,
|
|
2744
2748
|
|
|
2745
2749
|
pickArrow: `色番号ごとの矢印色(枠、塗りつぶし)、通常時のフリーズアロー色(枠、帯)を\nカラーピッカーから選んで変更できます。`,
|
|
2746
2750
|
pickColorCopy: `このボタンを押すと、フリーズアローの配色を矢印(枠)の色で上書きします。\nヒット時のフリーズアローの色も上書きします。`,
|
|
@@ -2792,10 +2796,11 @@ const g_lang_msgObj = {
|
|
|
2792
2796
|
hitPosition: `If you feel a discrepancy in the judgment position, \nyou can adjust the center position of the judgment in 1px increments \n (plus: in front, minus: at the back) by changing the numerical value. \nUse this function when there is a tendency to push too fast or too slow.`,
|
|
2793
2797
|
|
|
2794
2798
|
configType: `Switch the key config target.\n[Main] main keys only, [Replaced] alternate keys only, [ALL] all keys`,
|
|
2795
|
-
colorType: `Change the color scheme
|
|
2799
|
+
colorType: `Change the color scheme set for arrows and freeze-arrows from the predefined set.\nWhen Type1 to 4 is selected, color change is automatically turned off and can be changed to any color from the color picker.\n[Type0] Switch the sequences color gradations, [Type1~4] default color scheme`,
|
|
2796
2800
|
imgType: `Change the appearance of sequences.`,
|
|
2797
2801
|
colorGroup: `Change the sequences color group assignment pattern.`,
|
|
2798
2802
|
shuffleGroup: `Change the shuffle group when Mirror, Asym-Mirror, Random or S-Random are selected.\nShuffle with the same numbers listed above.`,
|
|
2803
|
+
stepRtnGroup: `Switches the type of notes, such as arrows, and the pattern regarding rotation.\nThis can only be changed if it has been set in advance.`,
|
|
2799
2804
|
|
|
2800
2805
|
pickArrow: `Change the frame or fill of arrow color and the frame or bar of normal freeze-arrow color\nfor each color number from the color picker.`,
|
|
2801
2806
|
pickColorCopy: `Pressing this button will override the color scheme of the freeze arrow with the frame color of the arrow. \nIt also overrides the color of the freeze arrow on hit.`,
|