poi-plugin-leveling-plan 0.0.4 → 0.0.6
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/assets/initial_equip_ships.json +5560 -0
- package/assets/main.css +72 -0
- package/i18n/en-US.json +27 -1
- package/i18n/ja-JP.json +27 -1
- package/i18n/zh-CN.json +27 -1
- package/i18n/zh-TW.json +27 -1
- package/index.js +27 -0
- package/package.json +1 -1
- package/services/equip-sync-service.js +83 -0
- package/utils/constants.js +13 -2
- package/utils/equip-provider.js +149 -0
- package/utils/plan-helpers.js +55 -26
- package/utils/selectors.js +142 -7
- package/views/components/equip-ship-selector.js +632 -0
- package/views/components/farming-plan-list.js +50 -0
- package/views/components/map-selector.js +114 -65
- package/views/components/plan-form.js +155 -53
- package/views/components/plan-item.js +165 -9
- package/views/components/plan-settings.js +81 -3
- package/views/components/ship-selector.js +40 -23
- package/views/leveling-plan-area.js +56 -15
package/assets/main.css
CHANGED
|
@@ -178,6 +178,71 @@
|
|
|
178
178
|
position: relative;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
#leveling-plan .map-selector-trigger {
|
|
182
|
+
display: flex;
|
|
183
|
+
align-items: center;
|
|
184
|
+
gap: 4px;
|
|
185
|
+
min-height: 32px;
|
|
186
|
+
padding: 2px 4px;
|
|
187
|
+
border: 1px solid #ccc;
|
|
188
|
+
border-radius: 3px;
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
background: #fff;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#leveling-plan .map-selector-trigger:hover {
|
|
194
|
+
border-color: #137cbd;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.bp5-dark #leveling-plan .map-selector-trigger {
|
|
198
|
+
background: #30404d;
|
|
199
|
+
border-color: #5f6b7c;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.bp5-dark #leveling-plan .map-selector-trigger:hover {
|
|
203
|
+
border-color: #137cbd;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#leveling-plan .map-selector-trigger-tags {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-wrap: wrap;
|
|
209
|
+
gap: 4px;
|
|
210
|
+
flex: 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#leveling-plan .map-selector-trigger-placeholder {
|
|
214
|
+
flex: 1;
|
|
215
|
+
padding: 0 4px;
|
|
216
|
+
color: #aaa;
|
|
217
|
+
font-size: 13px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#leveling-plan .map-selector-popover-header {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
justify-content: space-between;
|
|
224
|
+
padding: 6px 8px;
|
|
225
|
+
border-bottom: 1px solid #e5e5e5;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.bp5-dark #leveling-plan .map-selector-popover-header {
|
|
229
|
+
border-bottom-color: #404854;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#leveling-plan .map-selector-popover-title {
|
|
233
|
+
font-size: 13px;
|
|
234
|
+
font-weight: 600;
|
|
235
|
+
color: #333;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.bp5-dark #leveling-plan .map-selector-popover-title {
|
|
239
|
+
color: #ddd;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#leveling-plan .map-selector-popover-search {
|
|
243
|
+
padding: 4px 8px;
|
|
244
|
+
}
|
|
245
|
+
|
|
181
246
|
#leveling-plan .map-selector-dropdown {
|
|
182
247
|
max-height: 200px;
|
|
183
248
|
overflow-y: auto;
|
|
@@ -539,3 +604,10 @@
|
|
|
539
604
|
object-fit: contain;
|
|
540
605
|
vertical-align: middle;
|
|
541
606
|
}
|
|
607
|
+
|
|
608
|
+
/* 养殖计划行分隔线 */
|
|
609
|
+
#leveling-plan .farming-divider {
|
|
610
|
+
height: 0;
|
|
611
|
+
border-top: 1px solid rgba(128, 128, 128, 0.2);
|
|
612
|
+
margin: 2px 0;
|
|
613
|
+
}
|
package/i18n/en-US.json
CHANGED
|
@@ -52,5 +52,31 @@
|
|
|
52
52
|
"Sample Count": "Sample Count",
|
|
53
53
|
"Currently Used": "Currently Used",
|
|
54
54
|
"Samples Insufficient": "Samples Insufficient",
|
|
55
|
-
"No personal data": "No personal data"
|
|
55
|
+
"No personal data": "No personal data",
|
|
56
|
+
"Farming": "Farming",
|
|
57
|
+
"Add Farming Plan": "Add Farming Plan",
|
|
58
|
+
"Edit Farming Plan": "Edit Farming Plan",
|
|
59
|
+
"Ship Type": "Ship Type",
|
|
60
|
+
"No farming plans yet": "No farming plans yet",
|
|
61
|
+
"Below target": "Below target",
|
|
62
|
+
"ships": "ships",
|
|
63
|
+
"All ships have reached the target level": "All ships have reached the target level",
|
|
64
|
+
"By Ship Name": "By Ship Name",
|
|
65
|
+
"By Equipment": "By Equipment",
|
|
66
|
+
"Equipment-Ship Data Sync": "Equipment Data Sync",
|
|
67
|
+
"Last sync": "Last sync",
|
|
68
|
+
"Equipment entries": "Equipment entries",
|
|
69
|
+
"Ship entries": "Ship entries",
|
|
70
|
+
"Sync Now": "Sync Now",
|
|
71
|
+
"Syncing...": "Syncing...",
|
|
72
|
+
"Already up to date": "Already up to date",
|
|
73
|
+
"Sync completed": "Sync completed",
|
|
74
|
+
"Sync failed": "Sync failed",
|
|
75
|
+
"No data yet": "No data yet",
|
|
76
|
+
"Apply All": "Apply All",
|
|
77
|
+
"Search equipment...": "Search equipment...",
|
|
78
|
+
"No equipment data. Try syncing in Settings.": "No equipment data. Try syncing in Settings.",
|
|
79
|
+
"No equipment matches filter.": "No equipment matches filter.",
|
|
80
|
+
"Total": "Total",
|
|
81
|
+
"EXP": "EXP"
|
|
56
82
|
}
|
package/i18n/ja-JP.json
CHANGED
|
@@ -52,5 +52,31 @@
|
|
|
52
52
|
"Sample Count": "サンプル数",
|
|
53
53
|
"Currently Used": "現在使用中",
|
|
54
54
|
"Samples Insufficient": "サンプル不足",
|
|
55
|
-
"No personal data": "個人データなし"
|
|
55
|
+
"No personal data": "個人データなし",
|
|
56
|
+
"Farming": "養殖",
|
|
57
|
+
"Add Farming Plan": "養殖計画を追加",
|
|
58
|
+
"Edit Farming Plan": "養殖計画を編集",
|
|
59
|
+
"Ship Type": "艦種",
|
|
60
|
+
"No farming plans yet": "養殖計画がありません",
|
|
61
|
+
"Below target": "未達成",
|
|
62
|
+
"ships": "隻",
|
|
63
|
+
"All ships have reached the target level": "全隻目標レベル達成",
|
|
64
|
+
"By Ship Name": "艦娘名で選ぶ",
|
|
65
|
+
"By Equipment": "装備で選ぶ",
|
|
66
|
+
"Equipment-Ship Data Sync": "装備データ同期",
|
|
67
|
+
"Last sync": "最終同期",
|
|
68
|
+
"Equipment entries": "装備エントリ",
|
|
69
|
+
"Ship entries": "艦娘エントリ",
|
|
70
|
+
"Sync Now": "今すぐ同期",
|
|
71
|
+
"Syncing...": "同期中...",
|
|
72
|
+
"Already up to date": "最新です",
|
|
73
|
+
"Sync completed": "同期完了",
|
|
74
|
+
"Sync failed": "同期失敗",
|
|
75
|
+
"No data yet": "データなし",
|
|
76
|
+
"Apply All": "全て適用",
|
|
77
|
+
"Search equipment...": "装備を検索...",
|
|
78
|
+
"No equipment data. Try syncing in Settings.": "装備データがありません。設定で同期してください",
|
|
79
|
+
"No equipment matches filter.": "一致する装備がありません",
|
|
80
|
+
"Total": "合計",
|
|
81
|
+
"EXP": "経験値"
|
|
56
82
|
}
|
package/i18n/zh-CN.json
CHANGED
|
@@ -52,5 +52,31 @@
|
|
|
52
52
|
"Sample Count": "样本数",
|
|
53
53
|
"Currently Used": "当前使用",
|
|
54
54
|
"Samples Insufficient": "样本不足",
|
|
55
|
-
"No personal data": "无个人数据"
|
|
55
|
+
"No personal data": "无个人数据",
|
|
56
|
+
"Farming": "养殖",
|
|
57
|
+
"Add Farming Plan": "添加养殖计划",
|
|
58
|
+
"Edit Farming Plan": "编辑养殖计划",
|
|
59
|
+
"Ship Type": "船型",
|
|
60
|
+
"No farming plans yet": "暂无养殖计划",
|
|
61
|
+
"Below target": "未达标",
|
|
62
|
+
"ships": "艘",
|
|
63
|
+
"All ships have reached the target level": "所有船只已达标",
|
|
64
|
+
"By Ship Name": "按舰娘名",
|
|
65
|
+
"By Equipment": "按装备",
|
|
66
|
+
"Equipment-Ship Data Sync": "装备数据同步",
|
|
67
|
+
"Last sync": "上次同步",
|
|
68
|
+
"Equipment entries": "装备条目",
|
|
69
|
+
"Ship entries": "舰娘条目",
|
|
70
|
+
"Sync Now": "立即同步",
|
|
71
|
+
"Syncing...": "同步中...",
|
|
72
|
+
"Already up to date": "已是最新",
|
|
73
|
+
"Sync completed": "同步完成",
|
|
74
|
+
"Sync failed": "同步失败",
|
|
75
|
+
"No data yet": "暂无数据",
|
|
76
|
+
"Apply All": "全部应用",
|
|
77
|
+
"Search equipment...": "搜索装备...",
|
|
78
|
+
"No equipment data. Try syncing in Settings.": "暂无装备数据,请在设置中同步",
|
|
79
|
+
"No equipment matches filter.": "无匹配装备",
|
|
80
|
+
"Total": "总计",
|
|
81
|
+
"EXP": "经验"
|
|
56
82
|
}
|
package/i18n/zh-TW.json
CHANGED
|
@@ -52,5 +52,31 @@
|
|
|
52
52
|
"Sample Count": "樣本數",
|
|
53
53
|
"Currently Used": "當前使用",
|
|
54
54
|
"Samples Insufficient": "樣本不足",
|
|
55
|
-
"No personal data": "無個人數據"
|
|
55
|
+
"No personal data": "無個人數據",
|
|
56
|
+
"Farming": "養殖",
|
|
57
|
+
"Add Farming Plan": "新增養殖計劃",
|
|
58
|
+
"Edit Farming Plan": "編輯養殖計劃",
|
|
59
|
+
"Ship Type": "艦種",
|
|
60
|
+
"No farming plans yet": "暫無養殖計劃",
|
|
61
|
+
"Below target": "未達標",
|
|
62
|
+
"ships": "艘",
|
|
63
|
+
"All ships have reached the target level": "所有船隻已達標",
|
|
64
|
+
"By Ship Name": "按艦娘名",
|
|
65
|
+
"By Equipment": "按裝備",
|
|
66
|
+
"Equipment-Ship Data Sync": "裝備數據同步",
|
|
67
|
+
"Last sync": "上次同步",
|
|
68
|
+
"Equipment entries": "裝備條目",
|
|
69
|
+
"Ship entries": "艦娘條目",
|
|
70
|
+
"Sync Now": "立即同步",
|
|
71
|
+
"Syncing...": "同步中...",
|
|
72
|
+
"Already up to date": "已是最新",
|
|
73
|
+
"Sync completed": "同步完成",
|
|
74
|
+
"Sync failed": "同步失敗",
|
|
75
|
+
"No data yet": "暫無數據",
|
|
76
|
+
"Apply All": "全部應用",
|
|
77
|
+
"Search equipment...": "搜尋裝備...",
|
|
78
|
+
"No equipment data. Try syncing in Settings.": "暫無裝備數據,請在設定中同步",
|
|
79
|
+
"No equipment matches filter.": "無匹配裝備",
|
|
80
|
+
"Total": "總計",
|
|
81
|
+
"EXP": "經驗"
|
|
56
82
|
}
|
package/index.js
CHANGED
|
@@ -66,4 +66,31 @@ if (typeof setImmediate === 'function') {
|
|
|
66
66
|
console.error('[LevelingPlan] Failed to load battle observer:', error);
|
|
67
67
|
}
|
|
68
68
|
}, 0);
|
|
69
|
+
} // Initialize equip sync in background
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if (typeof setImmediate === 'function') {
|
|
73
|
+
setImmediate(() => {
|
|
74
|
+
try {
|
|
75
|
+
const {
|
|
76
|
+
initEquipSync
|
|
77
|
+
} = require('./services/equip-sync-service');
|
|
78
|
+
|
|
79
|
+
initEquipSync();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('[LevelingPlan] Failed to init equip sync:', error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
try {
|
|
87
|
+
const {
|
|
88
|
+
initEquipSync
|
|
89
|
+
} = require('./services/equip-sync-service');
|
|
90
|
+
|
|
91
|
+
initEquipSync();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('[LevelingPlan] Failed to init equip sync:', error);
|
|
94
|
+
}
|
|
95
|
+
}, 0);
|
|
69
96
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.initEquipSync = initEquipSync;
|
|
5
|
+
exports.manualSync = manualSync;
|
|
6
|
+
const PAGES_BASE = 'https://yuki.github.io/poi-equip-ships-data';
|
|
7
|
+
const CONFIG_KEY = 'plugin.poi-plugin-leveling-plan';
|
|
8
|
+
const META_KEY = `${CONFIG_KEY}.equipSyncMeta`;
|
|
9
|
+
const DATA_KEY = `${CONFIG_KEY}.equipShipsData`;
|
|
10
|
+
|
|
11
|
+
async function fetchJSON(url) {
|
|
12
|
+
const resp = await fetch(url, {
|
|
13
|
+
cache: 'no-cache',
|
|
14
|
+
headers: {
|
|
15
|
+
Accept: 'application/json'
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
19
|
+
return resp.json();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function initEquipSync() {
|
|
23
|
+
try {
|
|
24
|
+
const meta = await fetchJSON(`${PAGES_BASE}/index.json`);
|
|
25
|
+
if (!meta || !meta.updated_at) return;
|
|
26
|
+
const cachedMeta = window.config.get(META_KEY, {});
|
|
27
|
+
|
|
28
|
+
if (meta.updated_at === cachedMeta.updated_at) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const data = await fetchJSON(`${PAGES_BASE}/initial_equip_ships.json`);
|
|
33
|
+
if (!data || Object.keys(data).length === 0) return;
|
|
34
|
+
window.config.set(DATA_KEY, data);
|
|
35
|
+
window.config.set(META_KEY, meta);
|
|
36
|
+
} catch (e) {// 静默失败,由 equip-provider 的 loadSyncedData 降级
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function manualSync() {
|
|
41
|
+
try {
|
|
42
|
+
const meta = await fetchJSON(`${PAGES_BASE}/index.json`);
|
|
43
|
+
|
|
44
|
+
if (!meta || !meta.updated_at) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: 'Invalid metadata response'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const cachedMeta = window.config.get(META_KEY, {});
|
|
52
|
+
|
|
53
|
+
if (meta.updated_at === cachedMeta.updated_at) {
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
meta,
|
|
57
|
+
unchanged: true
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = await fetchJSON(`${PAGES_BASE}/initial_equip_ships.json`);
|
|
62
|
+
|
|
63
|
+
if (!data || Object.keys(data).length === 0) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: 'Empty data response'
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
window.config.set(DATA_KEY, data);
|
|
71
|
+
window.config.set(META_KEY, meta);
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
meta,
|
|
75
|
+
unchanged: false
|
|
76
|
+
};
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: e.message || 'Network error'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
package/utils/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.searchOptions = exports.catMap = exports.expClass = exports.bonusExpScaleNonFlagship = exports.bonusExpScaleFlagship = exports.expPercent = exports.expLevel = exports.shipCat = exports.frequentMaps = exports.EXP_BY_POI_DB = exports.MAX_LEVEL = exports.exp = void 0;
|
|
4
|
+
exports.searchOptions = exports.catMap = exports.expClass = exports.bonusExpScaleNonFlagship = exports.bonusExpScaleFlagship = exports.expPercent = exports.expLevel = exports.shipCat = exports.frequentMaps = exports.WORLD_NAMES = exports.EXP_BY_POI_DB = exports.MAX_LEVEL = exports.exp = void 0;
|
|
5
5
|
// 等级经验表(1-185级)
|
|
6
6
|
// 数据来源:poi-plugin-exp-calc
|
|
7
7
|
const exp = {
|
|
@@ -229,10 +229,21 @@ const EXP_BY_POI_DB = {
|
|
|
229
229
|
64: 236,
|
|
230
230
|
65: 242,
|
|
231
231
|
71: 220,
|
|
232
|
-
72: 213 //
|
|
232
|
+
72: 213 // 世界名称映射
|
|
233
233
|
|
|
234
234
|
};
|
|
235
235
|
exports.EXP_BY_POI_DB = EXP_BY_POI_DB;
|
|
236
|
+
const WORLD_NAMES = {
|
|
237
|
+
1: '鎮守府海域',
|
|
238
|
+
2: '南西諸島海域',
|
|
239
|
+
3: '北方海域',
|
|
240
|
+
4: '西方海域',
|
|
241
|
+
5: '南方海域',
|
|
242
|
+
6: '中部海域',
|
|
243
|
+
7: '南西海域' // 常用练级海图
|
|
244
|
+
|
|
245
|
+
};
|
|
246
|
+
exports.WORLD_NAMES = WORLD_NAMES;
|
|
236
247
|
const frequentMaps = [15, 21, 22, 42, 52, 53, 71]; // 舰种分类
|
|
237
248
|
|
|
238
249
|
exports.frequentMaps = frequentMaps;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.loadSyncedData = loadSyncedData;
|
|
5
|
+
exports.getFarmingMap = getFarmingMap;
|
|
6
|
+
exports.composeEquipmentList = composeEquipmentList;
|
|
7
|
+
|
|
8
|
+
var _initial_equip_ships = _interopRequireDefault(require("../assets/initial_equip_ships.json"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
|
|
12
|
+
let cachedFarmingMap = null;
|
|
13
|
+
let cached$Ships = null;
|
|
14
|
+
let cachedDataVersion = null;
|
|
15
|
+
|
|
16
|
+
const getDataVersion = () => {
|
|
17
|
+
try {
|
|
18
|
+
const meta = window.config.get('plugin.poi-plugin-leveling-plan.equipSyncMeta', {});
|
|
19
|
+
return meta.updated_at || 'bundled';
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return 'bundled';
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let cachedParentMap = {};
|
|
26
|
+
|
|
27
|
+
const findRoot = (id, parentMap) => {
|
|
28
|
+
let curr = id;
|
|
29
|
+
let steps = 0;
|
|
30
|
+
|
|
31
|
+
while (parentMap[curr] && steps < 20) {
|
|
32
|
+
curr = parentMap[curr];
|
|
33
|
+
steps++;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return curr;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function loadSyncedData() {
|
|
40
|
+
try {
|
|
41
|
+
const data = window.config.get('plugin.poi-plugin-leveling-plan.equipShipsData', null);
|
|
42
|
+
|
|
43
|
+
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {// ignore
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getFarmingMap($ships) {
|
|
53
|
+
if (!$ships || Object.keys($ships).length === 0) return {};
|
|
54
|
+
if (cachedFarmingMap && cached$Ships === $ships && cachedDataVersion === getDataVersion()) return cachedFarmingMap;
|
|
55
|
+
const parentMap = {};
|
|
56
|
+
const nameToId = {};
|
|
57
|
+
Object.values($ships).forEach(s => {
|
|
58
|
+
const afterId = parseInt(s.api_aftershipid, 10);
|
|
59
|
+
|
|
60
|
+
if (afterId > 0 && !parentMap[afterId]) {
|
|
61
|
+
parentMap[afterId] = s.api_id;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (s.api_name) {
|
|
65
|
+
nameToId[s.api_name] = s.api_id;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
cachedParentMap = parentMap;
|
|
69
|
+
|
|
70
|
+
const activeData = loadSyncedData() || _initial_equip_ships.default;
|
|
71
|
+
|
|
72
|
+
const map = {};
|
|
73
|
+
Object.entries(activeData).forEach(([eqIdStr, providers]) => {
|
|
74
|
+
const equipId = parseInt(eqIdStr, 10);
|
|
75
|
+
if (!Array.isArray(providers)) return;
|
|
76
|
+
providers.forEach(({
|
|
77
|
+
name,
|
|
78
|
+
level
|
|
79
|
+
}) => {
|
|
80
|
+
const shipId = nameToId[name];
|
|
81
|
+
if (!shipId) return;
|
|
82
|
+
const rootId = findRoot(shipId, parentMap);
|
|
83
|
+
|
|
84
|
+
if (!map[rootId]) {
|
|
85
|
+
map[rootId] = {
|
|
86
|
+
baseId: rootId,
|
|
87
|
+
provides: []
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const exists = map[rootId].provides.some(p => p.equipId === equipId && p.providerId === shipId && p.level === level);
|
|
92
|
+
|
|
93
|
+
if (!exists) {
|
|
94
|
+
map[rootId].provides.push({
|
|
95
|
+
equipId,
|
|
96
|
+
providerId: shipId,
|
|
97
|
+
level
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
cachedFarmingMap = map;
|
|
103
|
+
cached$Ships = $ships;
|
|
104
|
+
cachedDataVersion = getDataVersion();
|
|
105
|
+
return map;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function composeEquipmentList(farmingMap, $ships, $equipments, $equipTypes) {
|
|
109
|
+
const equipmentMap = {};
|
|
110
|
+
Object.entries(farmingMap).forEach(([baseShipIdStr, info]) => {
|
|
111
|
+
const baseShipId = parseInt(baseShipIdStr, 10);
|
|
112
|
+
const baseShipMaster = $ships[baseShipId] || {};
|
|
113
|
+
const baseShipName = baseShipMaster.api_name || `Ship#${baseShipId}`;
|
|
114
|
+
info.provides.forEach(p => {
|
|
115
|
+
const equipId = p.equipId;
|
|
116
|
+
|
|
117
|
+
if (!equipmentMap[equipId]) {
|
|
118
|
+
const masterEquip = $equipments[equipId] || {};
|
|
119
|
+
const typeId = masterEquip.api_type && masterEquip.api_type[2] || 0;
|
|
120
|
+
equipmentMap[equipId] = {
|
|
121
|
+
id: equipId,
|
|
122
|
+
name: masterEquip.api_name || `Equip#${equipId}`,
|
|
123
|
+
iconId: masterEquip.api_type && masterEquip.api_type[3] || 0,
|
|
124
|
+
typeName: ($equipTypes[typeId] || {}).api_name || 'Unknown',
|
|
125
|
+
typeId,
|
|
126
|
+
ships: []
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const providerMaster = $ships[p.providerId] || {};
|
|
131
|
+
const providerName = providerMaster.api_name || `Form#${p.providerId}`;
|
|
132
|
+
equipmentMap[equipId].ships.push({
|
|
133
|
+
shipId: baseShipId,
|
|
134
|
+
shipName: baseShipName,
|
|
135
|
+
providerId: p.providerId,
|
|
136
|
+
providerName,
|
|
137
|
+
level: p.level
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
const equipmentList = Object.values(equipmentMap);
|
|
142
|
+
equipmentList.forEach(eq => {
|
|
143
|
+
eq.ships.sort((a, b) => a.level - b.level);
|
|
144
|
+
});
|
|
145
|
+
return equipmentList.sort((a, b) => {
|
|
146
|
+
if (a.typeId !== b.typeId) return a.typeId - b.typeId;
|
|
147
|
+
return a.id - b.id;
|
|
148
|
+
});
|
|
149
|
+
}
|
package/utils/plan-helpers.js
CHANGED
|
@@ -28,21 +28,38 @@ exports.generatePlanId = generatePlanId;
|
|
|
28
28
|
|
|
29
29
|
const validatePlan = (plan, ship = null) => {
|
|
30
30
|
const errors = [];
|
|
31
|
+
const isFarming = plan.type === 'farming';
|
|
31
32
|
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!plan.shipMasterId) {
|
|
37
|
-
errors.push('shipMasterId is required');
|
|
38
|
-
}
|
|
33
|
+
if (isFarming && plan.targets) {
|
|
34
|
+
if (!plan.targets.length) {
|
|
35
|
+
errors.push('at least one ship target is required');
|
|
36
|
+
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
plan.targets.forEach((t, i) => {
|
|
39
|
+
if (!t.shipMasterId) {
|
|
40
|
+
errors.push(`targets[${i}]: shipMasterId is required`);
|
|
41
|
+
}
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if (!t.targetLevel || t.targetLevel < 1 || t.targetLevel > 185) {
|
|
44
|
+
errors.push(`targets[${i}]: targetLevel must be between 1 and 185`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
if (!isFarming && !plan.shipId) {
|
|
49
|
+
errors.push('shipId is required');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!plan.shipMasterId) {
|
|
53
|
+
errors.push('shipMasterId is required');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!plan.targetLevel || plan.targetLevel < 1 || plan.targetLevel > 185) {
|
|
57
|
+
errors.push('targetLevel must be between 1 and 185');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!isFarming && ship && plan.targetLevel <= ship.api_lv) {
|
|
61
|
+
errors.push('targetLevel must be greater than current level');
|
|
62
|
+
}
|
|
46
63
|
}
|
|
47
64
|
|
|
48
65
|
if (!plan.maps || !Array.isArray(plan.maps) || plan.maps.length === 0) {
|
|
@@ -171,12 +188,7 @@ const shouldAutoComplete = (plan, ship) => {
|
|
|
171
188
|
exports.shouldAutoComplete = shouldAutoComplete;
|
|
172
189
|
|
|
173
190
|
const formatMapName = mapId => {
|
|
174
|
-
if (!mapId || typeof mapId !== 'string') return '';
|
|
175
|
-
|
|
176
|
-
if (mapId.length === 2) {
|
|
177
|
-
return `${mapId[0]}-${mapId[1]}`;
|
|
178
|
-
} // '11' -> '1-1'
|
|
179
|
-
|
|
191
|
+
if (!mapId || typeof mapId !== 'string') return '';
|
|
180
192
|
|
|
181
193
|
if (mapId.length === 2) {
|
|
182
194
|
return `${mapId[0]}-${mapId[1]}`;
|
|
@@ -197,21 +209,38 @@ const formatMapName = mapId => {
|
|
|
197
209
|
|
|
198
210
|
exports.formatMapName = formatMapName;
|
|
199
211
|
|
|
200
|
-
const createPlan = (shipId, shipMasterId, startLevel, targetLevel, maps, notes = '') => {
|
|
212
|
+
const createPlan = (shipId, shipMasterId, startLevel, targetLevel, maps, notes = '', type = 'normal', targets = null) => {
|
|
201
213
|
const now = Date.now();
|
|
202
|
-
|
|
214
|
+
const base = {
|
|
203
215
|
id: generatePlanId(),
|
|
204
|
-
shipId,
|
|
205
|
-
shipMasterId,
|
|
206
|
-
startLevel,
|
|
207
|
-
targetLevel,
|
|
208
216
|
maps,
|
|
209
217
|
notes,
|
|
210
|
-
|
|
211
|
-
completedAt: null,
|
|
218
|
+
type,
|
|
212
219
|
createdAt: now,
|
|
213
220
|
updatedAt: now
|
|
214
221
|
};
|
|
222
|
+
|
|
223
|
+
if (type === 'farming') {
|
|
224
|
+
if (targets && targets.length > 0) {
|
|
225
|
+
return _extends({}, base, {
|
|
226
|
+
targets
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return _extends({}, base, {
|
|
231
|
+
shipMasterId,
|
|
232
|
+
targetLevel
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return _extends({}, base, {
|
|
237
|
+
shipId,
|
|
238
|
+
shipMasterId,
|
|
239
|
+
startLevel,
|
|
240
|
+
targetLevel,
|
|
241
|
+
completed: false,
|
|
242
|
+
completedAt: null
|
|
243
|
+
});
|
|
215
244
|
};
|
|
216
245
|
/**
|
|
217
246
|
* 更新计划对象
|