poi-plugin-equips-farm 1.0.2 → 1.0.5

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.
@@ -3,12 +3,28 @@
3
3
 
4
4
  let cachedFarmingMap = null
5
5
  let cachedWctfShips = null
6
+ let cachedMasterShips = null
7
+ let cachedParentMap = {} // Cache for parent lookups
8
+
9
+ // Helper: Find Base Ancestor (Module Level)
10
+ const findRoot = (id) => {
11
+ let curr = id
12
+ let steps = 0
13
+ while(cachedParentMap[curr] && steps < 20) {
14
+ curr = cachedParentMap[curr]
15
+ steps++
16
+ }
17
+ return curr
18
+ }
19
+
20
+
21
+
6
22
 
7
23
  // --- 1. WCTF Processing ---
8
24
 
9
- export function getFarmingMap(wctf) {
25
+ export function getFarmingMap(wctf, $ships) {
10
26
  if (!wctf || !wctf.ships) return {}
11
- if (cachedFarmingMap && cachedWctfShips === wctf.ships) return cachedFarmingMap
27
+ if (cachedFarmingMap && cachedWctfShips === wctf.ships && cachedMasterShips === $ships) return cachedFarmingMap
12
28
 
13
29
  const map = {}
14
30
  const ships = wctf.ships
@@ -69,6 +85,39 @@ export function getFarmingMap(wctf) {
69
85
  }
70
86
  })
71
87
 
88
+ // 2.1 Pass: Supplement Parent Map from Master Data (Block 2 & Runtime)
89
+ // Master Data links Forward (api_aftershipid), so we reverse it to get Child -> Parent
90
+ const processMasterForParents = (source) => {
91
+ if (!source) return
92
+ Object.values(source).forEach(s => {
93
+ const afterId = parseInt(s.api_aftershipid)
94
+ const currentId = s.api_id
95
+
96
+ // If this ship remodels into something valid
97
+ if (afterId > 0) {
98
+ // If the target (afterId) doesn't have a known parent yet, record this connection
99
+ // This links "Next Form" back to "Current Form"
100
+ if (!parentMap[afterId]) {
101
+ parentMap[afterId] = currentId
102
+ }
103
+ }
104
+ })
105
+ }
106
+
107
+ // Load Master Cache if not already loaded (Optimization: Reuse if loaded later? No, needed here for findRoot)
108
+ // We need to move the cache loading up or do it twice.
109
+ // Let's load it once at the top of function for consistency.
110
+ let masterCache = { ships: {} }
111
+ try {
112
+ const { loadMasterCache } = require('./master-cache')
113
+ masterCache = loadMasterCache() || { ships: {} }
114
+ } catch (e) {
115
+ // console.warn ...
116
+ }
117
+
118
+ processMasterForParents(masterCache.ships)
119
+ processMasterForParents($ships)
120
+
72
121
  // Helper: Find Base Ancestor
73
122
  const findRoot = (id) => {
74
123
  let curr = id
@@ -110,8 +159,95 @@ export function getFarmingMap(wctf) {
110
159
  })
111
160
  })
112
161
 
162
+ // 4th Pass: Merge External Initial Equipment Data (from Akashi-list)
163
+ try {
164
+ // Master Cache already loaded above
165
+
166
+ const initialEquipData = require('../initial_equip_ships.json')
167
+ if (initialEquipData) {
168
+ Object.keys(initialEquipData).forEach(eqIdStr => {
169
+ const eqId = parseInt(eqIdStr, 10)
170
+ const providers = initialEquipData[eqIdStr]
171
+
172
+
173
+
174
+
175
+ providers.forEach(p => {
176
+ // p has { name, level }
177
+
178
+ let foundId = -1;
179
+
180
+ // Priority 1: Check WCTF (Block 1) - Best for multilingual support
181
+ for (const sId in ships) {
182
+ const s = ships[sId];
183
+ const names = s.name || {};
184
+ if (
185
+ names.ja_jp === p.name ||
186
+ names.zh_cn === p.name ||
187
+ names.japanese === p.name ||
188
+ (s.api_name && s.api_name === p.name)
189
+ ) {
190
+ foundId = parseInt(sId);
191
+ break;
192
+ }
193
+ }
194
+
195
+ // Priority 2: Check Master Cache (Block 2) - For new ships via stored cache
196
+ if (foundId === -1 && masterCache && masterCache.ships) {
197
+ Object.values(masterCache.ships).forEach(ms => {
198
+ if (ms.api_name === p.name) {
199
+ foundId = ms.api_id
200
+ }
201
+ })
202
+ }
203
+
204
+ // Priority 3: Check Runtime Master Data ($ships) - For immediate validation if passed
205
+ if (foundId === -1 && $ships) {
206
+ Object.values($ships).forEach(ms => {
207
+ if (ms.api_name === p.name) {
208
+ foundId = ms.api_id
209
+ }
210
+ })
211
+ }
212
+
213
+ if (foundId > 0) {
214
+ // Ensure foundId is treated as a valid ship even if not in wctf.ships
215
+ // Effectively "mounting" the ID.
216
+ const rootId = findRoot(foundId) || foundId // Fallback to self if no root found
217
+
218
+ if (!map[rootId]) {
219
+ map[rootId] = {
220
+ baseId: rootId,
221
+ provides: []
222
+ }
223
+ }
224
+
225
+ // Check for duplicates
226
+ const exists = map[rootId].provides.some(existing =>
227
+ existing.equipId === eqId &&
228
+ existing.providerId === foundId &&
229
+ existing.level === p.level
230
+ )
231
+
232
+ if (!exists) {
233
+ map[rootId].provides.push({
234
+ equipId: eqId,
235
+ providerId: foundId,
236
+ level: p.level,
237
+ isInitial: true
238
+ })
239
+ }
240
+ }
241
+ })
242
+ })
243
+ }
244
+ } catch (e) {
245
+ console.error("Failed to load initial_equip_ships.json", e)
246
+ }
247
+
113
248
  cachedFarmingMap = map
114
249
  cachedWctfShips = wctf.ships
250
+ cachedMasterShips = $ships
115
251
  return map
116
252
  }
117
253
 
@@ -132,26 +268,19 @@ export function checkQuota(targetCount, equipId, userEquips, userShips, farmingM
132
268
  Object.values(userShips).forEach(ship => {
133
269
  const masterId = ship.api_ship_id
134
270
 
135
- // farmingMap is Keyed by BASE ID.
136
- // If masterId is in farmingMap, it is a Base ship match.
137
- // What if masterId is an intermediate form (e.g. Kai), but not yet Provider (Kai Ni)?
138
- // Our farmingMap keys are ROOTS.
139
- // We need to know if `masterId` belongs to the tree of a Root that provides `equipId`.
140
-
141
- // Simplification for v4:
142
- // We check if `masterId` matches the 'baseId' of a group.
143
- // (This assumes we keep Base copies. If we have intermediate, this check might fail.)
144
- // Ideally we check: findRoot(masterId) -> match map Key.
145
- // But `wctf` is needed for findRoot. We closed over it? No.
271
+ // Use findRoot to handle merged ships (e.g. Eidsvold Kai -> Eidsvold)
272
+ // If we don't do this, we won't find the entry in farmingMap which is keyed by Root ID.
273
+ const rootId = findRoot(masterId)
146
274
 
147
- // Let's rely on the fact that most farming involves keeping Base ships.
148
- // Or, we can do a quick lookup if we exported the parentMap or findRoot logic.
149
- // For now, strict Base ID match is "Safe" (undercounts rather than overcounts).
275
+ const info = farmingMap[rootId] || farmingMap[masterId] // Try Root first, then direct (fallback)
150
276
 
151
- const info = farmingMap[masterId]
152
277
  if (info && info.provides) {
278
+ // Check if this ship (or its family) provides the target equip
279
+ // And specifically, if the form I have is NOT the one that provides it (or I have multiple forms)
280
+ // Simplified Logic: If I have a ship in this family, count it as potential.
281
+ // Refined: If I have usage for this ship family to get the equip.
282
+
153
283
  const useful = info.provides.some(p => p.equipId === equipId && p.providerId !== masterId)
154
- // If I have the Base, and Base != Provider, it's potential.
155
284
  if (useful) {
156
285
  potential++
157
286
  }
@@ -0,0 +1,56 @@
1
+ const fs = require('fs-extra')
2
+ const path = require('path-extra')
3
+
4
+ const CACHE_DIR = path.join(window.APPDATA_PATH, 'poi-plugin-equips-farm')
5
+ const MASTER_CACHE_FILE = path.join(CACHE_DIR, 'master_cache.json')
6
+
7
+ // Ensure directory exists
8
+ fs.ensureDirSync(CACHE_DIR)
9
+
10
+ export function saveMasterCache(body) {
11
+ try {
12
+ if (!body || !body.api_mst_ship || !body.api_mst_shipgraph) return
13
+
14
+ const cacheData = {
15
+ ships: {},
16
+ shipgraph: {}
17
+ }
18
+
19
+ // Extract essential ship data
20
+ body.api_mst_ship.forEach(s => {
21
+ cacheData.ships[s.api_id] = {
22
+ api_id: s.api_id,
23
+ api_name: s.api_name,
24
+ api_yomi: s.api_yomi,
25
+ api_sortno: s.api_sortno,
26
+ api_aftershipid: s.api_aftershipid,
27
+ api_stype: s.api_stype
28
+ }
29
+ })
30
+
31
+ // Extract essential graph data (filename)
32
+ body.api_mst_shipgraph.forEach(g => {
33
+ cacheData.shipgraph[g.api_id] = {
34
+ api_id: g.api_id,
35
+ api_filename: g.api_filename,
36
+ api_version: g.api_version
37
+ }
38
+ })
39
+
40
+ fs.writeJsonSync(MASTER_CACHE_FILE, cacheData)
41
+ console.log('[Farming Plugin] Master data cached successfully.')
42
+ } catch (e) {
43
+ console.error('[Farming Plugin] Failed to save master cache:', e)
44
+ }
45
+ }
46
+
47
+ export function loadMasterCache() {
48
+ try {
49
+ if (fs.existsSync(MASTER_CACHE_FILE)) {
50
+ return fs.readJsonSync(MASTER_CACHE_FILE)
51
+ }
52
+ } catch (e) {
53
+ console.error('[Farming Plugin] Failed to load master cache:', e)
54
+ }
55
+ return null
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-equips-farm",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "A POI plugin to assist players in farming ships for specific equipment with enhanced multi-language search support",
5
5
  "main": "index.es",
6
6
  "poiPlugin": {
@@ -9,12 +9,23 @@
9
9
  "icon": "fa/wrench",
10
10
  "priority": 100
11
11
  },
12
+ "files": [
13
+ "index.es",
14
+ "lib",
15
+ "views",
16
+ "redux",
17
+ "assets",
18
+ "initial_equip_ships.json"
19
+ ],
12
20
  "scripts": {
13
21
  "test": "echo \"Error: no test specified\" && exit 1"
14
22
  },
23
+ "publishConfig": {
24
+ "registry": "https://registry.npmjs.org/"
25
+ },
15
26
  "repository": {
16
27
  "type": "git",
17
- "url": "https://github.com/Lanyangzhi/poi-plugin-equips-farm.git"
28
+ "url": "git+https://github.com/Lanyangzhi/poi-plugin-equips-farm.git"
18
29
  },
19
30
  "keywords": [
20
31
  "poi",
@@ -41,9 +41,12 @@ export default class ShipList extends Component {
41
41
  const chineseName = wctfShip.name && (wctfShip.name.zh_cn || wctfShip.name.chs || wctfShip.name.chinese)
42
42
  const yomiName = wctfShip.name && wctfShip.name.yomi
43
43
 
44
+ // Fallback for names if WCTF is missing (e.g. for Initial only ships if they somehow aren't in WCTF index but exist in $ships)
45
+ const displayName = s.shipName || masterShip.api_name || chineseName
46
+
44
47
  shipMap[s.shipId] = {
45
48
  baseId: s.shipId,
46
- name: s.shipName,
49
+ name: displayName,
47
50
  // Add fields for enhanced search
48
51
  api_name: masterShip.api_name,
49
52
  chinese_name: chineseName || masterShip.chinese_name,
@@ -51,6 +54,10 @@ export default class ShipList extends Component {
51
54
  api_yomi: masterShip.api_yomi,
52
55
  filename: wctfShip.filename || masterShip.filename,
53
56
  wiki_id: wctfShip.wiki_id || masterShip.wiki_id,
57
+
58
+ // IMPORTANT: ensure we have something to search against if WCTF is missing
59
+ // This handles cases where we matched by ID but WCTF data might be sparse
60
+ _rawName: s.shipName,
54
61
  items: [],
55
62
  hasActiveTarget: false
56
63
  }
@@ -62,7 +69,8 @@ export default class ShipList extends Component {
62
69
  level: s.level,
63
70
  isTarget: isTarget,
64
71
  providerName: s.providerName,
65
- providerId: s.providerId
72
+ providerId: s.providerId,
73
+ isInitial: s.isInitial
66
74
  })
67
75
 
68
76
  if (isTarget) shipMap[s.shipId].hasActiveTarget = true
@@ -127,9 +135,10 @@ export default class ShipList extends Component {
127
135
  <div style={{ display: 'flex', flexDirection: 'column' }}>
128
136
  <span style={{ fontWeight: '600' }}>{item.equipName}</span>
129
137
  <span className="bp3-text-muted" style={{ fontSize: '0.85em' }}>
130
- via {item.providerName}
138
+ {item.isInitial ? 'Initial' : 'via ' + item.providerName}
131
139
  </span>
132
140
  </div>
141
+ {item.isInitial && <Tag minimal={true} intent="success" style={{marginRight: 5}}>Init</Tag>}
133
142
  <Tag minimal={true} className={item.isTarget ? "bp3-intent-primary" : ""}>Lv.{item.level}</Tag>
134
143
  </div>
135
144
  ))}