poi-plugin-koushu-rate 2.0.8 → 2.0.10

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.
Files changed (2) hide show
  1. package/index.js +132 -14
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -341,13 +341,22 @@ const CSS = `
341
341
  }
342
342
  .kr2-plan-bar-left {
343
343
  display: flex;
344
- align-items: center;
344
+ align-items: flex-end;
345
345
  gap: 10px;
346
346
  }
347
347
  .kr2-plan-total-label {
348
348
  font-size: 12px;
349
349
  color: #abb3bf;
350
350
  }
351
+ .kr2-plan-dev-toggle {
352
+ display: inline-flex;
353
+ align-items: center;
354
+ gap: 4px;
355
+ font-size: 14px;
356
+ color: #ffd54f;
357
+ cursor: pointer;
358
+ white-space: nowrap;
359
+ }
351
360
  .kr2-plan-table td.kr2-plan-mat-col,
352
361
  .kr2-plan-table td.kr2-plan-consumed {
353
362
  text-align: left;
@@ -1590,12 +1599,30 @@ function kcDevRecipes(data, equipId) {
1590
1599
  }
1591
1600
  out.sort((a, b) => {
1592
1601
  if (a.rate !== b.rate) return b.rate - a.rate
1602
+ if (a.failRate !== b.failRate) return b.failRate - a.failRate
1593
1603
  if (Math.abs(a.total - b.total) > 1) return a.total - b.total
1594
- return b.failRate - a.failRate
1604
+ return 0
1595
1605
  })
1596
1606
  return out
1597
1607
  }
1598
1608
 
1609
+ let kcDevBestCache = {}
1610
+ let kcDevBestCacheKey = ""
1611
+ function kcDevBestFormula(data, equipId) {
1612
+ if (!data) return null
1613
+ const dataKey = String(data.updatedAt || data.generatedAt || "bundled")
1614
+ if (kcDevBestCacheKey !== dataKey) {
1615
+ kcDevBestCache = {}
1616
+ kcDevBestCacheKey = dataKey
1617
+ }
1618
+ const key = String(equipId)
1619
+ if (Object.prototype.hasOwnProperty.call(kcDevBestCache, key)) return kcDevBestCache[key]
1620
+ const bestList = kcDevRecipes(data, key).slice().sort((a, b) => b.rate - a.rate || a.failRate - b.failRate || (Math.abs(a.total - b.total) > 1 ? a.total - b.total : 0))
1621
+ const best = bestList.length ? bestList[0] : null
1622
+ kcDevBestCache[key] = best
1623
+ return best
1624
+ }
1625
+
1599
1626
  function openExternalUrl(url) {
1600
1627
  try {
1601
1628
  let remote = null
@@ -2198,17 +2225,38 @@ function planFormatScrews(n) {
2198
2225
  return v.toFixed(1)
2199
2226
  }
2200
2227
 
2201
- function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onToggle, onTarget, onQty, onSelectAll, onMaterialClick }) {
2228
+ function planDevExpectedValue(item, count, kcDevData) {
2229
+ if (!item || item.item_equipment_id == null || !kcDevData) return 0
2230
+ const best = kcDevBestFormula(kcDevData, item.item_equipment_id)
2231
+ if (!best || !Number.isFinite(best.rate) || best.rate <= 0) return 0
2232
+ const qty = Number(count) > 0 ? Number(count) : 1
2233
+ return qty * (100 / best.rate)
2234
+ }
2235
+
2236
+ function planDevExpectedText(item, count, kcDevData) {
2237
+ const v = planDevExpectedValue(item, count, kcDevData)
2238
+ return v > 0 ? planFormatScrews(v) : "--"
2239
+ }
2240
+
2241
+ function planMaterialDevExpected(costs, kcDevData) {
2242
+ let total = 0
2243
+ for (const m of costs.materials || []) total += planDevExpectedValue(m.item, m.count, kcDevData)
2244
+ return total
2245
+ }
2246
+
2247
+ function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onToggle, onTarget, onQty, onSelectAll, onMaterialClick, kcDevData, includeDevExpected, onToggleIncludeDevExpected }) {
2202
2248
  const planRows = rows.map((row) => {
2203
2249
  const sel = (selection || {})[row.id] || { selected: true, target: "max", qty: 1 }
2204
- return { row, sel, costs: planRowCosts(row, sel.target, sel.qty, inventoryByEquip), display: planCurrentDisplay(row, sel, inventoryByEquip) }
2250
+ const costs = planRowCosts(row, sel.target, sel.qty, inventoryByEquip)
2251
+ return { row, sel, costs, matDev: planMaterialDevExpected(costs, kcDevData), display: planCurrentDisplay(row, sel, inventoryByEquip) }
2205
2252
  })
2206
2253
  const selected = planRows.filter((p) => p.sel.selected)
2207
2254
  const totals = { screws: 0, dev: 0, fuel: 0, ammo: 0, steel: 0, bauxite: 0 }
2208
2255
  const materialMap = {}
2256
+ const includeDev = !!includeDevExpected
2209
2257
  for (const p of selected) {
2210
2258
  totals.screws += p.costs.screws
2211
- totals.dev += p.costs.dev
2259
+ totals.dev += p.costs.dev + (includeDev ? p.matDev : 0)
2212
2260
  totals.fuel += p.costs.fuel
2213
2261
  totals.ammo += p.costs.ammo
2214
2262
  totals.steel += p.costs.steel
@@ -2241,7 +2289,8 @@ function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onTogg
2241
2289
  "div",
2242
2290
  { className: "kr2-plan-bar-left" },
2243
2291
  React.createElement("span", { className: "kr2-plan-total-label" }, "总资源消耗"),
2244
- React.createElement("span", { className: "kr2-status" }, "已选 " + selected.length + " / " + planRows.length + " 件收藏装备")
2292
+ React.createElement("span", { className: "kr2-status" }, "已选 " + selected.length + " / " + planRows.length + " 件收藏装备"),
2293
+ React.createElement("label", { className: "kr2-plan-dev-toggle" }, React.createElement("input", { type: "checkbox", checked: !!includeDevExpected, onChange: onToggleIncludeDevExpected }), "统计素材开发的紫菜消耗期望")
2245
2294
  ),
2246
2295
  React.createElement(
2247
2296
  "div",
@@ -2276,7 +2325,7 @@ function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onTogg
2276
2325
  React.createElement("th", null, "当前"),
2277
2326
  React.createElement("th", null, "目标"),
2278
2327
  React.createElement("th", null, "目标数量"),
2279
- React.createElement("th", null, "资材"),
2328
+ React.createElement("th", null, "开发资材"),
2280
2329
  React.createElement("th", null, "螺丝"),
2281
2330
  React.createElement("th", null, "素材")
2282
2331
  )
@@ -2319,7 +2368,7 @@ function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onTogg
2319
2368
  React.createElement("button", { type: "button", className: "kr2-plan-qty-btn", onClick: () => onQty(p.row.id, Number(p.sel.qty || 1) + 1) }, "+")
2320
2369
  )
2321
2370
  ),
2322
- React.createElement("td", null, planFormatNumber(p.costs.dev)),
2371
+ React.createElement("td", null, planFormatNumber(p.costs.dev + (includeDev ? p.matDev : 0))),
2323
2372
  React.createElement("td", null, planFormatScrews(p.costs.screws)),
2324
2373
  React.createElement("td", { className: "kr2-plan-mat-col" }, matText)
2325
2374
  )
@@ -2336,13 +2385,13 @@ function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onTogg
2336
2385
  React.createElement(
2337
2386
  "thead",
2338
2387
  null,
2339
- React.createElement("tr", null, React.createElement("th", { className: "kr2-plan-mat-th" }, "素材名称"), React.createElement("th", null, "需求"), React.createElement("th", null, "库存"), React.createElement("th", { className: "kr2-plan-mat-th" }, "消耗于装备"))
2388
+ React.createElement("tr", null, React.createElement("th", { className: "kr2-plan-mat-th" }, "素材名称"), React.createElement("th", null, "需求"), React.createElement("th", null, "库存"), React.createElement("th", null, "紫菜开发期望"), React.createElement("th", { className: "kr2-plan-mat-th" }, "消耗于装备"))
2340
2389
  ),
2341
2390
  React.createElement(
2342
2391
  "tbody",
2343
2392
  null,
2344
2393
  materialList.length === 0
2345
- ? React.createElement("tr", null, React.createElement("td", { colSpan: 4 }, "暂无素材需求"))
2394
+ ? React.createElement("tr", null, React.createElement("td", { colSpan: 5 }, "暂无素材需求"))
2346
2395
  : materialList.map((m) =>
2347
2396
  React.createElement(
2348
2397
  "tr",
@@ -2350,6 +2399,7 @@ function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onTogg
2350
2399
  React.createElement("td", { className: "kr2-plan-name" }, React.createElement("button", { className: "kr2-plan-mat-btn", onClick: () => onMaterialClick && onMaterialClick(m.item) }, m.name)),
2351
2400
  React.createElement("td", null, String(m.required)),
2352
2401
  React.createElement("td", { className: stockClass(m.stock, m.required) }, m.stock == null ? "--" : String(m.stock)),
2402
+ React.createElement("td", null, planDevExpectedText(m.item, m.required, kcDevData)),
2353
2403
  React.createElement("td", { className: "kr2-plan-consumed" }, m.equips.join("、"))
2354
2404
  )
2355
2405
  )
@@ -2628,6 +2678,47 @@ function saveFavorites(favorites) {
2628
2678
  } catch (_) {}
2629
2679
  }
2630
2680
 
2681
+ const PLAN_SELECTION_STORAGE_KEY = "poi-plugin-koushu-rate:plan-selection";
2682
+
2683
+ function readPlanSelection() {
2684
+ try {
2685
+ const storage = typeof localStorage !== "undefined" ? localStorage : (typeof window !== "undefined" ? window.localStorage : null)
2686
+ if (!storage) return {}
2687
+ const raw = storage.getItem(PLAN_SELECTION_STORAGE_KEY)
2688
+ if (!raw) return {}
2689
+ const parsed = JSON.parse(raw)
2690
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}
2691
+ } catch (_) {
2692
+ return {}
2693
+ }
2694
+ }
2695
+
2696
+ function savePlanSelection(selection) {
2697
+ try {
2698
+ const storage = typeof localStorage !== "undefined" ? localStorage : (typeof window !== "undefined" ? window.localStorage : null)
2699
+ if (storage) storage.setItem(PLAN_SELECTION_STORAGE_KEY, JSON.stringify(selection || {}))
2700
+ } catch (_) {}
2701
+ }
2702
+
2703
+ const PLAN_INCLUDE_DEV_STORAGE_KEY = "poi-plugin-koushu-rate:plan-include-dev";
2704
+
2705
+ function readPlanIncludeDev() {
2706
+ try {
2707
+ const storage = typeof localStorage !== "undefined" ? localStorage : (typeof window !== "undefined" ? window.localStorage : null)
2708
+ if (!storage) return false
2709
+ return storage.getItem(PLAN_INCLUDE_DEV_STORAGE_KEY) === "1"
2710
+ } catch (_) {
2711
+ return false
2712
+ }
2713
+ }
2714
+
2715
+ function savePlanIncludeDev(value) {
2716
+ try {
2717
+ const storage = typeof localStorage !== "undefined" ? localStorage : (typeof window !== "undefined" ? window.localStorage : null)
2718
+ if (storage) storage.setItem(PLAN_INCLUDE_DEV_STORAGE_KEY, value ? "1" : "0")
2719
+ } catch (_) {}
2720
+ }
2721
+
2631
2722
  class KoushuRateApp extends React.Component {
2632
2723
  state = {
2633
2724
  query: "",
@@ -2646,17 +2737,20 @@ class KoushuRateApp extends React.Component {
2646
2737
  inventoryPopup: null,
2647
2738
  inventoryByEquip: {},
2648
2739
  useItemCounts: {},
2740
+ kcDevData: null,
2649
2741
  favorites: readFavorites(),
2650
2742
  favoritesOnly: false,
2651
2743
  starSort: null,
2652
2744
  activeTab: "list",
2653
- planSelection: {},
2745
+ planSelection: readPlanSelection(),
2746
+ includeDevExpected: readPlanIncludeDev(),
2654
2747
  }
2655
2748
 
2656
2749
  componentDidMount() {
2657
2750
  this.refresh()
2658
2751
  this.checkAkashiUpdate(false)
2659
2752
  refreshKcDevData().catch(() => {})
2753
+ this.loadKcDevExpectations()
2660
2754
  this.refreshInventory()
2661
2755
  this.inventoryTimer = setInterval(() => this.refreshInventory(), 1000)
2662
2756
  }
@@ -2665,6 +2759,12 @@ class KoushuRateApp extends React.Component {
2665
2759
  if (this.inventoryTimer) clearInterval(this.inventoryTimer)
2666
2760
  }
2667
2761
 
2762
+ loadKcDevExpectations = () => {
2763
+ loadKcDevData(false)
2764
+ .then((data) => this.setState({ kcDevData: data }))
2765
+ .catch(() => {})
2766
+ }
2767
+
2668
2768
  refreshInventory = () => {
2669
2769
  const nextEquips = getEquipInventory(getInventoryEquips(this.props.envWindow))
2670
2770
  const nextUseItems = getUseItemCounts(this.props.envWindow)
@@ -2817,14 +2917,18 @@ class KoushuRateApp extends React.Component {
2817
2917
  togglePlanRow = (id) => {
2818
2918
  this.setState((prev) => {
2819
2919
  const cur = prev.planSelection[id] || this.planDefaultSel(id)
2820
- return { planSelection: Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { selected: !cur.selected }) }) }
2920
+ const planSelection = Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { selected: !cur.selected }) })
2921
+ savePlanSelection(planSelection)
2922
+ return { planSelection }
2821
2923
  })
2822
2924
  }
2823
2925
 
2824
2926
  setPlanTarget = (id, target) => {
2825
2927
  this.setState((prev) => {
2826
2928
  const cur = prev.planSelection[id] || this.planDefaultSel(id)
2827
- return { planSelection: Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { target }) }) }
2929
+ const planSelection = Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { target }) })
2930
+ savePlanSelection(planSelection)
2931
+ return { planSelection }
2828
2932
  })
2829
2933
  }
2830
2934
 
@@ -2832,7 +2936,9 @@ class KoushuRateApp extends React.Component {
2832
2936
  const n = Math.max(1, Math.min(99, Math.round(Number(qty) || 1)))
2833
2937
  this.setState((prev) => {
2834
2938
  const cur = prev.planSelection[id] || this.planDefaultSel(id)
2835
- return { planSelection: Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { qty: n }) }) }
2939
+ const planSelection = Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { qty: n }) })
2940
+ savePlanSelection(planSelection)
2941
+ return { planSelection }
2836
2942
  })
2837
2943
  }
2838
2944
 
@@ -2844,10 +2950,19 @@ class KoushuRateApp extends React.Component {
2844
2950
  const cur = planSelection[row.id] || this.planDefaultSel(row.id)
2845
2951
  planSelection[row.id] = Object.assign({}, cur, { selected })
2846
2952
  }
2953
+ savePlanSelection(planSelection)
2847
2954
  return { planSelection }
2848
2955
  })
2849
2956
  }
2850
2957
 
2958
+ toggleIncludeDevExpected = () => {
2959
+ this.setState((prev) => {
2960
+ const includeDevExpected = !prev.includeDevExpected
2961
+ savePlanIncludeDev(includeDevExpected)
2962
+ return { includeDevExpected }
2963
+ })
2964
+ }
2965
+
2851
2966
  setKcDevSort = (key) => {
2852
2967
  this.setState((prev) => {
2853
2968
  const popup = prev.kcDevPopup
@@ -3084,6 +3199,9 @@ class KoushuRateApp extends React.Component {
3084
3199
  onQty: this.setPlanQty,
3085
3200
  onSelectAll: this.selectAllPlan,
3086
3201
  onMaterialClick: this.openKcDevPopup,
3202
+ kcDevData: this.state.kcDevData,
3203
+ includeDevExpected: !!this.state.includeDevExpected,
3204
+ onToggleIncludeDevExpected: this.toggleIncludeDevExpected,
3087
3205
  })
3088
3206
  : null,
3089
3207
  kcDevPopup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-koushu-rate",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "全装备改修资材计算与推荐",
5
5
  "main": "index.js",
6
6
  "files": [