poi-plugin-remodel-planner 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,8 +70,10 @@
70
70
  * 缺什么会精确到件:`改装設計図×1 (差1)`、`鋼材×370 (差270)`
71
71
  * 只持有基础形态时提示「需先改到 赤城改」
72
72
  * 持有更高形态(如赤城改二戊)时,上游形态(赤城改二/赤城改)自动算「已改造」
73
- * **筛选**:搜索(日文名/英文名/舰型/图鉴号/api id)、舰种、状态(点统计条上的标签即筛选)、
74
- 只看已持有 / 只看愿望单 / 只剩现在能改。
73
+ * **筛选**:搜索(日文名/英文名/舰型/图鉴号/api id)、舰种、只看已持有 / 只看愿望单 / 只剩现在能改。
74
+ * **状态筛选(正选 + 反选)**:统计条上的标签同样是三态 —— 点一下=只看它(蓝色 ✓)→
75
+ 再点一下=**排除它**(红色 ✕,例如「已改造」点两下=只看没改过的)→ 第三下=取消;
76
+ 「全部」标签一键清空。多选时:正选=任选其一,反选=这些状态都不显示。
75
77
  * **道具筛选(正选 + 反选)**:每个道具一个标签,**点一下=只看需要它的**(正选,蓝色 ✓)→
76
78
  **再点一下=只看不需要它的**(反选,红色 ✕)→ **第三下=取消**;标签上的数字是「需要该道具的改造条数」。
77
79
  多选时:正选=任选其一(需要其中任意一个即可),反选=任一都不需要(例如
@@ -81,6 +83,11 @@
81
83
  * **愿望单**:点 ★ 收集目标;面板顶部实时汇总「还差 N 张设计图 / N 个卡特 / 资源缺口」,
82
84
  可一键复制成文本(含每个目标的等级与消耗),方便贴给舰队群里一起规划。
83
85
  * **切回成本**:改二 ↔ 改二戊 这类可切换形态,会额外显示「从本形态切回上一形态」的成本(25 条)。
86
+ * **多号机(复制舰)**:起点列会把**每一艘**的等级都列出来(`持有 3 艘:Lv.99 / Lv.85 / Lv.30`),
87
+ 并标出「N 艘已够等级」;如果这个形态**已经改出来过、但还有复制舰够等级**,
88
+ 会额外标绿色的「可再改 N 艘」,统计条上也有「多号机可再改 N」标签(点一下=只看这些)。
89
+ 例:秋月改 Lv99(已改出秋月改二)+ 秋月改 Lv85 → 秋月改二那行显示「已改造 + 可再改 1 艘」。
90
+ 每艘各算一份消耗,所以照着同一行的消耗再备一份资材即可。
84
91
  * 设置面板(插件列表里的小齿轮):自动联网开关、缓存有效期、立刻更新。
85
92
 
86
93
  ## 5. 开发 / 自测(不需要装 Node.js)
package/i18n/en-US.json CHANGED
@@ -35,7 +35,6 @@
35
35
  "重置": "Reset",
36
36
  "显示 {{shown}} / {{total}}": "showing {{shown}} / {{total}}",
37
37
  "加入 / 移出愿望单": "add / remove wishlist",
38
- "持有 {{n}} 艘(最高 Lv.{{lv}})": "{{n}} owned (best Lv.{{lv}})",
39
38
  "需先改到 {{name}}": "remodel to {{name}} first",
40
39
  "起点数据缺": "source form unknown",
41
40
  "未持有": "not owned",
@@ -75,5 +74,14 @@
75
74
  "现在是:只看需要它的(再点一下=改成「不需要它」)": "Now: only those that need it (click again = does not need it)",
76
75
  "现在是:只看不需要它的(再点一下=取消)": "Now: only those that do not need it (click again = clear)",
77
76
  "点一下=只看需要它的,再点一下=只看不需要它,第三下=取消": "Click = only those needing it, click again = only those not needing it, third = clear",
78
- "清除道具筛选": "Clear item filter"
77
+ "清除道具筛选": "Clear item filter",
78
+ "持有 {{n}} 艘:{{levels}}": "Owned {{n}}: {{levels}}",
79
+ "{{n}} 艘已够等级": "{{n}} of them meets the level",
80
+ "可再改 {{n}} 艘": "Can remodel {{n}} more",
81
+ "多号机可再改": "Duplicates ready",
82
+ "已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)": "Already remodelled once, but another copy meets the level - you can remodel one more",
83
+ "清空状态筛选": "Clear status filter",
84
+ "点一下=只看它,再点一下=排除它,第三下=取消": "Click = only it, click again = exclude it, third = clear",
85
+ "现在是:只看它(再点一下=排除它)": "Now: only it (click again = exclude)",
86
+ "现在是:排除它(再点一下=取消)": "Now: excluded (click again = clear)"
79
87
  }
package/i18n/ja-JP.json CHANGED
@@ -35,7 +35,6 @@
35
35
  "重置": "リセット",
36
36
  "显示 {{shown}} / {{total}}": "{{shown}} / {{total}} 件を表示",
37
37
  "加入 / 移出愿望单": "ウィッシュリストに追加 / 削除",
38
- "持有 {{n}} 艘(最高 Lv.{{lv}})": "{{n}} 隻所持(最高 Lv.{{lv}})",
39
38
  "需先改到 {{name}}": "先に {{name}} へ改装が必要",
40
39
  "起点数据缺": "改装元のデータなし",
41
40
  "未持有": "未所持",
@@ -75,5 +74,14 @@
75
74
  "现在是:只看需要它的(再点一下=改成「不需要它」)": "現在:必要とするもののみ(もう一度で「不要」へ)",
76
75
  "现在是:只看不需要它的(再点一下=取消)": "現在:必要としないもののみ(もう一度で解除)",
77
76
  "点一下=只看需要它的,再点一下=只看不需要它,第三下=取消": "1回=必要のみ、2回=不要のみ、3回=解除",
78
- "清除道具筛选": "アイテム絞り込みを解除"
77
+ "清除道具筛选": "アイテム絞り込みを解除",
78
+ "持有 {{n}} 艘:{{levels}}": "所持 {{n}} 隻:{{levels}}",
79
+ "{{n}} 艘已够等级": "{{n}} 隻がLv達成",
80
+ "可再改 {{n}} 艘": "あと {{n}} 隻改装可",
81
+ "多号机可再改": "複製艦の追加改装",
82
+ "已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)": "すでに改装済みですが、Lvを満たす複製艦がいます → もう1隻改装できます",
83
+ "清空状态筛选": "状態フィルタを解除",
84
+ "点一下=只看它,再点一下=排除它,第三下=取消": "1回=それのみ、2回=除外、3回=解除",
85
+ "现在是:只看它(再点一下=排除它)": "現在:それのみ(もう一度で除外)",
86
+ "现在是:排除它(再点一下=取消)": "現在:除外(もう一度で解除)"
79
87
  }
package/i18n/ko-KR.json CHANGED
@@ -35,7 +35,6 @@
35
35
  "重置": "초기화",
36
36
  "显示 {{shown}} / {{total}}": "{{shown}} / {{total}} 표시",
37
37
  "加入 / 移出愿望单": "위시리스트 추가 / 제거",
38
- "持有 {{n}} 艘(最高 Lv.{{lv}})": "{{n}}척 보유 (최고 Lv.{{lv}})",
39
38
  "需先改到 {{name}}": "먼저 {{name}}로 개장 필요",
40
39
  "起点数据缺": "시작 형태 데이터 없음",
41
40
  "未持有": "미보유",
@@ -75,5 +74,14 @@
75
74
  "现在是:只看需要它的(再点一下=改成「不需要它」)": "현재: 필요한 것만 (다시 클릭=불필요)",
76
75
  "现在是:只看不需要它的(再点一下=取消)": "현재: 불필요한 것만 (다시 클릭=해제)",
77
76
  "点一下=只看需要它的,再点一下=只看不需要它,第三下=取消": "한 번=필요만, 두 번=불필요만, 세 번=해제",
78
- "清除道具筛选": "아이템 필터 해제"
77
+ "清除道具筛选": "아이템 필터 해제",
78
+ "持有 {{n}} 艘:{{levels}}": "보유 {{n}}척: {{levels}}",
79
+ "{{n}} 艘已够等级": "{{n}}척이 레벨 충족",
80
+ "可再改 {{n}} 艘": "{{n}}척 더 개장 가능",
81
+ "多号机可再改": "복제함 추가 개장",
82
+ "已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)": "이미 개장했지만 레벨을 충족하는 복제함이 있어 한 척 더 개장할 수 있습니다",
83
+ "清空状态筛选": "상태 필터 해제",
84
+ "点一下=只看它,再点一下=排除它,第三下=取消": "한 번=그것만, 두 번=제외, 세 번=해제",
85
+ "现在是:只看它(再点一下=排除它)": "현재: 그것만 (다시=제외)",
86
+ "现在是:排除它(再点一下=取消)": "현재: 제외 (다시=해제)"
79
87
  }
package/i18n/zh-CN.json CHANGED
@@ -35,7 +35,6 @@
35
35
  "重置": "重置",
36
36
  "显示 {{shown}} / {{total}}": "显示 {{shown}} / {{total}}",
37
37
  "加入 / 移出愿望单": "加入 / 移出愿望单",
38
- "持有 {{n}} 艘(最高 Lv.{{lv}})": "持有 {{n}} 艘(最高 Lv.{{lv}})",
39
38
  "需先改到 {{name}}": "需先改到 {{name}}",
40
39
  "起点数据缺": "起点数据缺",
41
40
  "未持有": "未持有",
@@ -75,5 +74,14 @@
75
74
  "现在是:只看需要它的(再点一下=改成「不需要它」)": "现在是:只看需要它的(再点一下=改成「不需要它」)",
76
75
  "现在是:只看不需要它的(再点一下=取消)": "现在是:只看不需要它的(再点一下=取消)",
77
76
  "点一下=只看需要它的,再点一下=只看不需要它,第三下=取消": "点一下=只看需要它的,再点一下=只看不需要它,第三下=取消",
78
- "清除道具筛选": "清除道具筛选"
77
+ "清除道具筛选": "清除道具筛选",
78
+ "持有 {{n}} 艘:{{levels}}": "持有 {{n}} 艘:{{levels}}",
79
+ "{{n}} 艘已够等级": "{{n}} 艘已够等级",
80
+ "可再改 {{n}} 艘": "可再改 {{n}} 艘",
81
+ "多号机可再改": "多号机可再改",
82
+ "已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)": "已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)",
83
+ "清空状态筛选": "清空状态筛选",
84
+ "点一下=只看它,再点一下=排除它,第三下=取消": "点一下=只看它,再点一下=排除它,第三下=取消",
85
+ "现在是:只看它(再点一下=排除它)": "现在是:只看它(再点一下=排除它)",
86
+ "现在是:排除它(再点一下=取消)": "现在是:排除它(再点一下=取消)"
79
87
  }
package/i18n/zh-TW.json CHANGED
@@ -35,7 +35,6 @@
35
35
  "重置": "重置",
36
36
  "显示 {{shown}} / {{total}}": "顯示 {{shown}} / {{total}}",
37
37
  "加入 / 移出愿望单": "加入 / 移出願望單",
38
- "持有 {{n}} 艘(最高 Lv.{{lv}})": "持有 {{n}} 艘(最高 Lv.{{lv}})",
39
38
  "需先改到 {{name}}": "需先改到 {{name}}",
40
39
  "起点数据缺": "起点數據缺",
41
40
  "未持有": "未持有",
@@ -75,5 +74,14 @@
75
74
  "现在是:只看需要它的(再点一下=改成「不需要它」)": "現在是:只看需要它的(再點一下=改成「不需要它」)",
76
75
  "现在是:只看不需要它的(再点一下=取消)": "現在是:只看不需要它的(再點一下=取消)",
77
76
  "点一下=只看需要它的,再点一下=只看不需要它,第三下=取消": "點一下=只看需要它的,再點一下=只看不需要它,第三下=取消",
78
- "清除道具筛选": "清除道具篩選"
77
+ "清除道具筛选": "清除道具篩選",
78
+ "持有 {{n}} 艘:{{levels}}": "持有 {{n}} 艘:{{levels}}",
79
+ "{{n}} 艘已够等级": "{{n}} 艘已夠等級",
80
+ "可再改 {{n}} 艘": "可再改 {{n}} 艘",
81
+ "多号机可再改": "多號機可再改",
82
+ "已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)": "已經改出來過,但還有複製艦夠等級 → 可以再改一隻(多號機)",
83
+ "清空状态筛选": "清空狀態篩選",
84
+ "点一下=只看它,再点一下=排除它,第三下=取消": "點一下=只看它,再點一下=排除它,第三下=取消",
85
+ "现在是:只看它(再点一下=排除它)": "現在是:只看它(再點一下=排除它)",
86
+ "现在是:排除它(再点一下=取消)": "現在是:排除它(再點一下=取消)"
79
87
  }
package/index.es CHANGED
@@ -159,7 +159,7 @@ const CostChips = ({ entry, have, t }) => {
159
159
  )
160
160
  }
161
161
 
162
- /** 统计行:点标签 = 按该状态筛选 */
162
+ /** 统计行:点标签 = 按该状态筛选(再点 = 排除它,第三下 = 取消) */
163
163
  const StatsBar = ({ stats, filter, setFilter, t }) => {
164
164
  const items = [
165
165
  { key: 'all', label: '全部', value: stats.total },
@@ -170,31 +170,75 @@ const StatsBar = ({ stats, filter, setFilter, t }) => {
170
170
  { key: STATUS.UNOWNED, label: STATUS_META[STATUS.UNOWNED].label, value: stats.unowned },
171
171
  { key: STATUS.DONE, label: STATUS_META[STATUS.DONE].label, value: stats.done },
172
172
  ]
173
- const isActive = (key) => (key === 'all' ? !filter.statuses.length : filter.statuses.includes(key))
174
- const toggle = (key) => {
173
+ const stateOf = (key) => {
174
+ if (key === 'all') return ''
175
+ if (filter.statuses.includes(key)) return 'in'
176
+ if ((filter.excludeStatuses || []).includes(key)) return 'out'
177
+ return ''
178
+ }
179
+ const cycle = (key) => {
175
180
  if (key === 'all') {
176
- setFilter({ ...filter, statuses: [] })
181
+ setFilter({ ...filter, statuses: [], excludeStatuses: [] })
177
182
  return
178
183
  }
179
- const has = filter.statuses.includes(key)
180
- setFilter({
181
- ...filter,
182
- statuses: has ? filter.statuses.filter((s) => s !== key) : filter.statuses.concat([key]),
183
- })
184
+ const st = stateOf(key)
185
+ const next = {
186
+ statuses: filter.statuses.slice(),
187
+ excludeStatuses: (filter.excludeStatuses || []).slice(),
188
+ }
189
+ if (st === '') {
190
+ next.statuses.push(key)
191
+ } else if (st === 'in') {
192
+ next.statuses = next.statuses.filter((s) => s !== key)
193
+ next.excludeStatuses.push(key)
194
+ } else {
195
+ next.excludeStatuses = next.excludeStatuses.filter((s) => s !== key)
196
+ }
197
+ setFilter({ ...filter, ...next })
184
198
  }
185
199
  return (
186
200
  <div style={S.row}>
187
- {items.map((it) => (
188
- <Tag
189
- key={it.key}
190
- interactive
191
- minimal={!isActive(it.key)}
192
- intent={isActive(it.key) ? 'primary' : undefined}
193
- onClick={() => toggle(it.key)}
194
- >
195
- {t(it.label)} <b>{num(it.value)}</b>
196
- </Tag>
197
- ))}
201
+ {items.map((it) => {
202
+ const st = stateOf(it.key)
203
+ const active = st === 'in'
204
+ const activeAll = it.key === 'all' && !filter.statuses.length && !(filter.excludeStatuses || []).length
205
+ const tip =
206
+ it.key === 'all'
207
+ ? t('清空状态筛选')
208
+ : st === 'in'
209
+ ? t('现在是:只看它(再点一下=排除它)')
210
+ : st === 'out'
211
+ ? t('现在是:排除它(再点一下=取消)')
212
+ : t('点一下=只看它,再点一下=排除它,第三下=取消')
213
+ return (
214
+ <Tooltip key={it.key} content={tip}>
215
+ <Tag
216
+ interactive
217
+ minimal={!active && !(st === 'out') && !activeAll}
218
+ intent={active || activeAll ? 'primary' : st === 'out' ? 'danger' : undefined}
219
+ onClick={() => cycle(it.key)}
220
+ >
221
+ {active ? <Icon icon="tick" /> : st === 'out' ? <Icon icon="cross" /> : null}
222
+ {' '}
223
+ {t(it.label)} <b>{num(it.value)}</b>
224
+ </Tag>
225
+ </Tooltip>
226
+ )
227
+ })}
228
+ {stats.again ? (
229
+ <Tooltip content={t('已经改出来过,但还有复制舰够等级 → 可以再改一只(多号机)')}>
230
+ <Tag
231
+ interactive
232
+ minimal={!filter.againOnly}
233
+ intent={filter.againOnly ? 'success' : undefined}
234
+ onClick={() => setFilter({ ...filter, againOnly: !filter.againOnly })}
235
+ >
236
+ <Icon icon="duplicate" />
237
+ {' '}
238
+ {t('多号机可再改')} <b>{num(stats.again)}</b>
239
+ </Tag>
240
+ </Tooltip>
241
+ ) : null}
198
242
  <span style={S.muted}>{t('持有 {{n}} 个改造分支的起点舰', { n: num(stats.owned) })}</span>
199
243
  </div>
200
244
  )
@@ -420,8 +464,16 @@ const toast = (message, type) => {
420
464
 
421
465
  /** 表格一行 */
422
466
  const TableRow = ({ e, have, t, onWish, sortno, typeLabel }) => {
423
- const ownedText = e.ownedCount
424
- ? t('持有 {{n}} 艘(最高 Lv.{{lv}})', { n: e.ownedCount, lv: e.bestLv })
467
+ // 多号机:把每一艘的等级都列出来,并标出有几艘已经够等级
468
+ const levelText = e.ownedCount
469
+ ? t('持有 {{n}} 艘:{{levels}}', {
470
+ n: e.ownedCount,
471
+ levels:
472
+ e.owned
473
+ .slice(0, 5)
474
+ .map((c) => `Lv.${c.lv}`)
475
+ .join(' / ') + (e.owned.length > 5 ? ' …' : ''),
476
+ })
425
477
  : e.viaJp
426
478
  ? t('需先改到 {{name}}', { name: e.viaJp })
427
479
  : e.fromUnknown
@@ -451,7 +503,13 @@ const TableRow = ({ e, have, t, onWish, sortno, typeLabel }) => {
451
503
  </td>
452
504
  <td style={S.td}>
453
505
  <div>{e.fromJp || '—'}</div>
454
- <div style={S.muted}>{ownedText}</div>
506
+ <div style={S.muted}>{levelText}</div>
507
+ {e.ownedCount > 1 ? <div style={S.small}>{t('{{n}} 艘已够等级', { n: e.readyCount })}</div> : null}
508
+ {e.again ? (
509
+ <Tag minimal intent="success">
510
+ {t('可再改 {{n}} 艘', { n: e.readyCount })}
511
+ </Tag>
512
+ ) : null}
455
513
  </td>
456
514
  <td style={{ ...S.td, whiteSpace: 'nowrap' }}>
457
515
  <div>Lv.{e.level}</div>
package/logic.es CHANGED
@@ -295,6 +295,9 @@ export const buildEntries = (table, ctx = {}) => {
295
295
 
296
296
  const bestLv = starters.length ? starters[0].lv : 0
297
297
  const lvMissing = starters.length ? Math.max(0, row.level - bestLv) : 0
298
+ // 多号机(复制舰):有几艘已经够改造等级。每艘各算一份消耗,
299
+ // 所以「已经改出来一只」+「还有一只够等级」= 可以再改一只。
300
+ const readyCount = starters.filter((s) => s.lv >= row.level).length
298
301
 
299
302
  const resMissing = {}
300
303
  const itemMissing = {}
@@ -351,8 +354,10 @@ export const buildEntries = (table, ctx = {}) => {
351
354
  viaApiId,
352
355
  viaJp: viaApiId ? nameOf(viaApiId) : '',
353
356
  done,
357
+ again: done && readyCount > 0, // 已经改出来过,但还有复制舰够等级 → 还能再改一只
354
358
  status,
355
359
  lvMissing,
360
+ readyCount,
356
361
  resMissing,
357
362
  itemMissing,
358
363
  wish: !!wish[apiId],
@@ -371,6 +376,7 @@ export const computeStats = (entries, ctx = {}) => {
371
376
  res: 0,
372
377
  unowned: 0,
373
378
  owned: 0,
379
+ again: 0,
374
380
  wishCount: 0,
375
381
  }
376
382
  const wishItems = {}
@@ -379,6 +385,7 @@ export const computeStats = (entries, ctx = {}) => {
379
385
  entries.forEach((e) => {
380
386
  if (stats[e.status] !== undefined) stats[e.status] += 1
381
387
  if (e.ownedCount) stats.owned += 1
388
+ if (e.again) stats.again += 1
382
389
  if (!e.wish) return
383
390
  stats.wishCount += 1
384
391
  if (e.done) return
@@ -408,9 +415,11 @@ const emptyFilter = {
408
415
  q: '',
409
416
  types: [],
410
417
  statuses: [],
418
+ excludeStatuses: [],
411
419
  onlyOwned: false,
412
420
  onlyWish: false,
413
421
  onlyReady: false,
422
+ againOnly: false,
414
423
  needItems: [],
415
424
  excludeItems: [],
416
425
  levelReady: false,
@@ -441,9 +450,11 @@ export const applyFilter = (entries, filter = emptyFilter) => {
441
450
  if (!matchQuery(e, f.q)) return false
442
451
  if (f.types.length && !f.types.includes(e.type)) return false
443
452
  if (f.statuses.length && !f.statuses.includes(e.status)) return false
453
+ if (f.excludeStatuses.length && f.excludeStatuses.includes(e.status)) return false
444
454
  if (f.onlyOwned && !e.ownedCount) return false
445
455
  if (f.onlyWish && !e.wish) return false
446
456
  if (f.onlyReady && e.status !== STATUS.READY) return false
457
+ if (f.againOnly && !e.again) return false
447
458
  if (f.levelReady && e.ownedCount > 0 && e.lvMissing > 0) return false
448
459
  if (f.needItems.length) {
449
460
  // 多选 = 任选其一:只要需要其中任意一个道具就算命中
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-remodel-planner",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "查看全部舰娘的改造需求(等级/资源/设计图/卡特/详报等),按游戏进度自动标记哪些现在就能改、还差什么。",
5
5
  "main": "index.es",
6
6
  "files": [