poi-plugin-compass 0.1.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.
Files changed (452) hide show
  1. package/README.md +79 -0
  2. package/air-status.js +28 -0
  3. package/assets/compass.css +647 -0
  4. package/assets/kcnav/icons/map_common/136.png +0 -0
  5. package/assets/kcnav/icons/map_common/137.png +0 -0
  6. package/assets/kcnav/icons/map_common/19.png +0 -0
  7. package/assets/kcnav/icons/map_main/17.png +0 -0
  8. package/assets/kcnav/icons/map_main/18.png +0 -0
  9. package/assets/kcnav/icons/map_main/40.png +0 -0
  10. package/assets/kcnav/icons/map_main/42.png +0 -0
  11. package/assets/kcnav/icons/map_main/46.png +0 -0
  12. package/assets/kcnav/icons/map_main/47.png +0 -0
  13. package/assets/kcnav/icons/map_main/48.png +0 -0
  14. package/assets/kcnav/icons/map_main/53.png +0 -0
  15. package/assets/kcnav/icons/map_main/54.png +0 -0
  16. package/assets/kcnav/icons/map_main/62.png +0 -0
  17. package/assets/kcnav/maps/1-1.webp +0 -0
  18. package/assets/kcnav/maps/1-2.webp +0 -0
  19. package/assets/kcnav/maps/1-3.webp +0 -0
  20. package/assets/kcnav/maps/1-4.webp +0 -0
  21. package/assets/kcnav/maps/1-5.webp +0 -0
  22. package/assets/kcnav/maps/1-6.webp +0 -0
  23. package/assets/kcnav/maps/2-1.webp +0 -0
  24. package/assets/kcnav/maps/2-2.webp +0 -0
  25. package/assets/kcnav/maps/2-3.webp +0 -0
  26. package/assets/kcnav/maps/2-4.webp +0 -0
  27. package/assets/kcnav/maps/2-5.webp +0 -0
  28. package/assets/kcnav/maps/3-1.webp +0 -0
  29. package/assets/kcnav/maps/3-2.webp +0 -0
  30. package/assets/kcnav/maps/3-3.webp +0 -0
  31. package/assets/kcnav/maps/3-4.webp +0 -0
  32. package/assets/kcnav/maps/3-5.webp +0 -0
  33. package/assets/kcnav/maps/4-1.webp +0 -0
  34. package/assets/kcnav/maps/4-2.webp +0 -0
  35. package/assets/kcnav/maps/4-3.webp +0 -0
  36. package/assets/kcnav/maps/4-4.webp +0 -0
  37. package/assets/kcnav/maps/4-5.webp +0 -0
  38. package/assets/kcnav/maps/5-1.webp +0 -0
  39. package/assets/kcnav/maps/5-2.webp +0 -0
  40. package/assets/kcnav/maps/5-3.webp +0 -0
  41. package/assets/kcnav/maps/5-4.webp +0 -0
  42. package/assets/kcnav/maps/5-5.webp +0 -0
  43. package/assets/kcnav/maps/5-6.webp +0 -0
  44. package/assets/kcnav/maps/6-1.webp +0 -0
  45. package/assets/kcnav/maps/6-2.webp +0 -0
  46. package/assets/kcnav/maps/6-3.webp +0 -0
  47. package/assets/kcnav/maps/6-4.webp +0 -0
  48. package/assets/kcnav/maps/6-5.webp +0 -0
  49. package/assets/kcnav/maps/7-1.webp +0 -0
  50. package/assets/kcnav/maps/7-2.webp +0 -0
  51. package/assets/kcnav/maps/7-3.webp +0 -0
  52. package/assets/kcnav/maps/7-4.webp +0 -0
  53. package/assets/kcnav/maps/7-5.webp +0 -0
  54. package/data/kc3kai-master-ids.json +1 -0
  55. package/data/kc3kai-ship-stats.json +21545 -0
  56. package/data/kcnav-node-types-by-map.json +606 -0
  57. package/data/kcnav-node-types.json +176 -0
  58. package/data/los-config.json +42 -0
  59. package/data/maps.json +590 -0
  60. package/data/normal-maps.json +1590 -0
  61. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-1/A.json +1 -0
  62. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-1/B.json +1 -0
  63. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-1/C.json +1 -0
  64. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-2/A.json +1 -0
  65. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-2/C.json +1 -0
  66. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-2/D.json +1 -0
  67. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-2/E.json +1 -0
  68. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-3/C.json +1 -0
  69. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-3/E.json +1 -0
  70. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-3/F.json +1 -0
  71. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-3/J.json +1 -0
  72. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-4/B.json +1 -0
  73. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-4/D.json +1 -0
  74. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-4/H.json +1 -0
  75. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-4/I.json +1 -0
  76. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-4/J.json +1 -0
  77. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-4/L.json +1 -0
  78. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/A.json +1 -0
  79. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/C.json +1 -0
  80. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/D.json +1 -0
  81. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/E.json +1 -0
  82. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/F.json +1 -0
  83. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/H.json +1 -0
  84. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/I.json +1 -0
  85. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-5/J.json +1 -0
  86. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/B.json +1 -0
  87. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/C.json +1 -0
  88. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/D.json +1 -0
  89. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/E.json +1 -0
  90. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/F.json +1 -0
  91. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/I.json +1 -0
  92. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/J.json +1 -0
  93. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/K.json +1 -0
  94. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/1-6/L.json +1 -0
  95. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-1/A.json +1 -0
  96. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-1/C.json +1 -0
  97. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-1/D.json +1 -0
  98. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-1/F.json +1 -0
  99. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-1/H.json +1 -0
  100. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-2/B.json +1 -0
  101. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-2/D.json +1 -0
  102. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-2/E.json +1 -0
  103. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-2/G.json +1 -0
  104. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-2/K.json +1 -0
  105. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/A.json +1 -0
  106. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/E.json +1 -0
  107. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/F.json +1 -0
  108. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/J.json +1 -0
  109. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/K.json +1 -0
  110. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/M.json +1 -0
  111. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-3/N.json +1 -0
  112. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/B.json +1 -0
  113. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/E.json +1 -0
  114. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/F.json +1 -0
  115. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/I.json +1 -0
  116. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/L.json +1 -0
  117. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/M.json +1 -0
  118. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-4/P.json +1 -0
  119. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/B.json +1 -0
  120. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/C.json +1 -0
  121. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/E.json +1 -0
  122. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/F.json +1 -0
  123. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/J.json +1 -0
  124. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/L.json +1 -0
  125. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/2-5/O.json +1 -0
  126. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-1/B.json +1 -0
  127. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-1/C.json +1 -0
  128. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-1/D.json +1 -0
  129. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-1/F.json +1 -0
  130. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-1/G.json +1 -0
  131. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-2/A.json +1 -0
  132. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-2/C.json +1 -0
  133. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-2/H.json +1 -0
  134. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-2/J.json +1 -0
  135. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-2/K.json +1 -0
  136. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-2/L.json +1 -0
  137. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-3/A.json +1 -0
  138. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-3/B.json +1 -0
  139. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-3/E.json +1 -0
  140. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-3/G.json +1 -0
  141. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-3/K.json +1 -0
  142. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-3/M.json +1 -0
  143. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/A.json +1 -0
  144. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/B.json +1 -0
  145. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/C.json +1 -0
  146. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/G.json +1 -0
  147. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/H.json +1 -0
  148. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/I.json +1 -0
  149. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/M.json +1 -0
  150. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/N.json +1 -0
  151. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-4/P.json +1 -0
  152. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/B.json +1 -0
  153. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/D.json +1 -0
  154. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/E.json +1 -0
  155. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/F.json +1 -0
  156. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/G.json +1 -0
  157. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/H.json +1 -0
  158. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/3-5/K.json +1 -0
  159. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/A.json +1 -0
  160. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/C.json +1 -0
  161. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/D.json +1 -0
  162. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/E.json +1 -0
  163. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/G.json +1 -0
  164. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/H.json +1 -0
  165. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-1/J.json +1 -0
  166. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/A.json +1 -0
  167. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/B.json +1 -0
  168. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/C.json +1 -0
  169. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/D.json +1 -0
  170. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/E.json +1 -0
  171. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/G.json +1 -0
  172. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/H.json +1 -0
  173. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-2/L.json +1 -0
  174. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/A.json +1 -0
  175. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/C.json +1 -0
  176. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/D.json +1 -0
  177. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/F.json +1 -0
  178. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/G.json +1 -0
  179. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/H.json +1 -0
  180. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/I.json +1 -0
  181. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/L.json +1 -0
  182. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-3/N.json +1 -0
  183. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/A.json +1 -0
  184. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/B.json +1 -0
  185. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/E.json +1 -0
  186. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/F.json +1 -0
  187. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/G.json +1 -0
  188. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/H.json +1 -0
  189. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/I.json +1 -0
  190. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-4/K.json +1 -0
  191. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/B.json +1 -0
  192. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/D.json +1 -0
  193. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/E.json +1 -0
  194. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/F.json +1 -0
  195. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/G.json +1 -0
  196. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/H.json +1 -0
  197. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/J.json +1 -0
  198. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/K.json +1 -0
  199. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/N.json +1 -0
  200. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/O.json +1 -0
  201. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/S.json +1 -0
  202. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/4-5/T.json +1 -0
  203. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-1/B.json +1 -0
  204. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-1/D.json +1 -0
  205. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-1/E.json +1 -0
  206. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-1/F.json +1 -0
  207. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-1/G.json +1 -0
  208. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-1/J.json +1 -0
  209. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/C.json +1 -0
  210. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/D.json +1 -0
  211. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/E.json +1 -0
  212. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/F.json +1 -0
  213. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/I.json +1 -0
  214. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/K.json +1 -0
  215. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/L.json +1 -0
  216. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-2/O.json +1 -0
  217. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/C.json +1 -0
  218. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/I.json +1 -0
  219. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/J.json +1 -0
  220. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/K.json +1 -0
  221. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/M.json +1 -0
  222. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/N.json +1 -0
  223. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/P.json +1 -0
  224. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-3/Q.json +1 -0
  225. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/C.json +1 -0
  226. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/E.json +1 -0
  227. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/F.json +1 -0
  228. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/G.json +1 -0
  229. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/H.json +1 -0
  230. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/J.json +1 -0
  231. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/L.json +1 -0
  232. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-4/P.json +1 -0
  233. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/B.json +1 -0
  234. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/C.json +1 -0
  235. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/G.json +1 -0
  236. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/H.json +1 -0
  237. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/J.json +1 -0
  238. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/K.json +1 -0
  239. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/M.json +1 -0
  240. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/N.json +1 -0
  241. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/P.json +1 -0
  242. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-5/S.json +1 -0
  243. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/A.json +1 -0
  244. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/A1.json +1 -0
  245. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/A2.json +1 -0
  246. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/B.json +1 -0
  247. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/C.json +1 -0
  248. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/C1.json +1 -0
  249. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/C2.json +1 -0
  250. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/D.json +1 -0
  251. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/G.json +1 -0
  252. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/H.json +1 -0
  253. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/J.json +1 -0
  254. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/K.json +1 -0
  255. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/K1.json +1 -0
  256. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/K2.json +1 -0
  257. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/L.json +1 -0
  258. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/N.json +1 -0
  259. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/P.json +1 -0
  260. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/Q.json +1 -0
  261. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/Q1.json +1 -0
  262. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/Q2.json +1 -0
  263. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/T.json +1 -0
  264. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/U.json +1 -0
  265. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/V.json +1 -0
  266. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/W.json +1 -0
  267. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/X.json +1 -0
  268. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/5-6/Z.json +1 -0
  269. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/C.json +1 -0
  270. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/D.json +1 -0
  271. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/F.json +1 -0
  272. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/H.json +1 -0
  273. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/I.json +1 -0
  274. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/J.json +1 -0
  275. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-1/K.json +1 -0
  276. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/B.json +1 -0
  277. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/C.json +1 -0
  278. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/F.json +1 -0
  279. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/H.json +1 -0
  280. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/I.json +1 -0
  281. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/J.json +1 -0
  282. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-2/K.json +1 -0
  283. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-3/B.json +1 -0
  284. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-3/C.json +1 -0
  285. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-3/D.json +1 -0
  286. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-3/E.json +1 -0
  287. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-3/F.json +1 -0
  288. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-3/J.json +1 -0
  289. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/A.json +1 -0
  290. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/B.json +1 -0
  291. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/C.json +1 -0
  292. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/D.json +1 -0
  293. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/E.json +1 -0
  294. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/F.json +1 -0
  295. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/G.json +1 -0
  296. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/H.json +1 -0
  297. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/I.json +1 -0
  298. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/J.json +1 -0
  299. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/K.json +1 -0
  300. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/L.json +1 -0
  301. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/M.json +1 -0
  302. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-4/N.json +1 -0
  303. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/A.json +1 -0
  304. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/B.json +1 -0
  305. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/C.json +1 -0
  306. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/D.json +1 -0
  307. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/E.json +1 -0
  308. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/F.json +1 -0
  309. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/G.json +1 -0
  310. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/H.json +1 -0
  311. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/I.json +1 -0
  312. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/J.json +1 -0
  313. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/6-5/M.json +1 -0
  314. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/B.json +1 -0
  315. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/C.json +1 -0
  316. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/D.json +1 -0
  317. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/F.json +1 -0
  318. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/G.json +1 -0
  319. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/H.json +1 -0
  320. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-1/K.json +1 -0
  321. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/B.json +1 -0
  322. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/C.json +1 -0
  323. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/E.json +1 -0
  324. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/G.json +1 -0
  325. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/H.json +1 -0
  326. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/I.json +1 -0
  327. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/J.json +1 -0
  328. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-2/M.json +1 -0
  329. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/B.json +1 -0
  330. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/C.json +1 -0
  331. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/D.json +1 -0
  332. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/E.json +1 -0
  333. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/J.json +1 -0
  334. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/M.json +1 -0
  335. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/N.json +1 -0
  336. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-3/P.json +1 -0
  337. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/B.json +1 -0
  338. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/C.json +1 -0
  339. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/D.json +1 -0
  340. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/E.json +1 -0
  341. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/G.json +1 -0
  342. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/H.json +1 -0
  343. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/J.json +1 -0
  344. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/K.json +1 -0
  345. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/L.json +1 -0
  346. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/M.json +1 -0
  347. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-4/P.json +1 -0
  348. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/A.json +1 -0
  349. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/B.json +1 -0
  350. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/C.json +1 -0
  351. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/D.json +1 -0
  352. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/E.json +1 -0
  353. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/G.json +1 -0
  354. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/J.json +1 -0
  355. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/K.json +1 -0
  356. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/L.json +1 -0
  357. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/M.json +1 -0
  358. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/N.json +1 -0
  359. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/Q.json +1 -0
  360. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/R.json +1 -0
  361. package/data/sources/kcnav/enemies/2026-08-11_2026-09-11/7-5/T.json +1 -0
  362. package/data/sources/kcnav/enemies/manifest.json +2120 -0
  363. package/data/sources/kcnav/maps/1-1.json +175 -0
  364. package/data/sources/kcnav/maps/1-2.json +277 -0
  365. package/data/sources/kcnav/maps/1-3.json +517 -0
  366. package/data/sources/kcnav/maps/1-4.json +652 -0
  367. package/data/sources/kcnav/maps/1-5.json +553 -0
  368. package/data/sources/kcnav/maps/1-6.json +668 -0
  369. package/data/sources/kcnav/maps/2-1.json +451 -0
  370. package/data/sources/kcnav/maps/2-2.json +557 -0
  371. package/data/sources/kcnav/maps/2-3.json +757 -0
  372. package/data/sources/kcnav/maps/2-4.json +817 -0
  373. package/data/sources/kcnav/maps/2-5.json +779 -0
  374. package/data/sources/kcnav/maps/3-1.json +379 -0
  375. package/data/sources/kcnav/maps/3-2.json +588 -0
  376. package/data/sources/kcnav/maps/3-3.json +654 -0
  377. package/data/sources/kcnav/maps/3-4.json +910 -0
  378. package/data/sources/kcnav/maps/3-5.json +643 -0
  379. package/data/sources/kcnav/maps/4-1.json +561 -0
  380. package/data/sources/kcnav/maps/4-2.json +664 -0
  381. package/data/sources/kcnav/maps/4-3.json +867 -0
  382. package/data/sources/kcnav/maps/4-4.json +690 -0
  383. package/data/sources/kcnav/maps/4-5.json +1232 -0
  384. package/data/sources/kcnav/maps/5-1.json +517 -0
  385. package/data/sources/kcnav/maps/5-2.json +791 -0
  386. package/data/sources/kcnav/maps/5-3.json +889 -0
  387. package/data/sources/kcnav/maps/5-4.json +851 -0
  388. package/data/sources/kcnav/maps/5-5.json +1070 -0
  389. package/data/sources/kcnav/maps/5-6.json +3200 -0
  390. package/data/sources/kcnav/maps/6-1.json +527 -0
  391. package/data/sources/kcnav/maps/6-2.json +717 -0
  392. package/data/sources/kcnav/maps/6-3.json +501 -0
  393. package/data/sources/kcnav/maps/6-4.json +870 -0
  394. package/data/sources/kcnav/maps/6-5.json +814 -0
  395. package/data/sources/kcnav/maps/7-1.json +640 -0
  396. package/data/sources/kcnav/maps/7-2.json +976 -0
  397. package/data/sources/kcnav/maps/7-3.json +1529 -0
  398. package/data/sources/kcnav/maps/7-4.json +1000 -0
  399. package/data/sources/kcnav/maps/7-5.json +1570 -0
  400. package/data/sources/kcnav/maps/manifest.json +340 -0
  401. package/data/sources/kcwiki/routes/1-1.wiki +2 -0
  402. package/data/sources/kcwiki/routes/1-2.wiki +1 -0
  403. package/data/sources/kcwiki/routes/1-3.wiki +1 -0
  404. package/data/sources/kcwiki/routes/1-4.wiki +1 -0
  405. package/data/sources/kcwiki/routes/1-5.wiki +1 -0
  406. package/data/sources/kcwiki/routes/1-6.wiki +1 -0
  407. package/data/sources/kcwiki/routes/2-1.wiki +1 -0
  408. package/data/sources/kcwiki/routes/2-2.wiki +1 -0
  409. package/data/sources/kcwiki/routes/2-3.wiki +1 -0
  410. package/data/sources/kcwiki/routes/2-4.wiki +81 -0
  411. package/data/sources/kcwiki/routes/2-5.wiki +1 -0
  412. package/data/sources/kcwiki/routes/3-1.wiki +1 -0
  413. package/data/sources/kcwiki/routes/3-2.wiki +1 -0
  414. package/data/sources/kcwiki/routes/3-3.wiki +1 -0
  415. package/data/sources/kcwiki/routes/3-4.wiki +1 -0
  416. package/data/sources/kcwiki/routes/3-5.wiki +1 -0
  417. package/data/sources/kcwiki/routes/4-1.wiki +1 -0
  418. package/data/sources/kcwiki/routes/4-2.wiki +1 -0
  419. package/data/sources/kcwiki/routes/4-3.wiki +1 -0
  420. package/data/sources/kcwiki/routes/4-4.wiki +1 -0
  421. package/data/sources/kcwiki/routes/4-5.wiki +1 -0
  422. package/data/sources/kcwiki/routes/5-1.wiki +57 -0
  423. package/data/sources/kcwiki/routes/5-2.wiki +1 -0
  424. package/data/sources/kcwiki/routes/5-3.wiki +1 -0
  425. package/data/sources/kcwiki/routes/5-4.wiki +1 -0
  426. package/data/sources/kcwiki/routes/5-5.wiki +80 -0
  427. package/data/sources/kcwiki/routes/5-6.wiki +753 -0
  428. package/data/sources/kcwiki/routes/6-1.wiki +1 -0
  429. package/data/sources/kcwiki/routes/6-2.wiki +1 -0
  430. package/data/sources/kcwiki/routes/6-3.wiki +1 -0
  431. package/data/sources/kcwiki/routes/6-4.wiki +1 -0
  432. package/data/sources/kcwiki/routes/6-5.wiki +1 -0
  433. package/data/sources/kcwiki/routes/7-1.wiki +1 -0
  434. package/data/sources/kcwiki/routes/7-2.wiki +1 -0
  435. package/data/sources/kcwiki/routes/7-3.wiki +1 -0
  436. package/data/sources/kcwiki/routes/7-4.wiki +1 -0
  437. package/data/sources/kcwiki/routes/7-5.wiki +1 -0
  438. package/data/sources/kcwiki/routes/README.md +11 -0
  439. package/data/sources/kcwiki/routes/manifest.json +450 -0
  440. package/docs/research.md +445 -0
  441. package/docs/sources.md +85 -0
  442. package/enemies.js +56 -0
  443. package/exporters.js +182 -0
  444. package/index.js +1081 -0
  445. package/logic.js +603 -0
  446. package/overview.js +54 -0
  447. package/package.json +66 -0
  448. package/scripts/cache-kcnav-enemies.js +97 -0
  449. package/scripts/cache-kcnav-maps.js +109 -0
  450. package/scripts/cache-kcwiki-routes.js +96 -0
  451. package/scripts/compact-kcnav-enemies.js +53 -0
  452. package/scripts/compress-kcnav-maps.js +90 -0
@@ -0,0 +1,445 @@
1
+ # 罗盘插件调研笔记
2
+
3
+ ## 1. 目标与假设
4
+
5
+ 目标是为指定舰队(默认第一舰队)回答四类问题:
6
+
7
+ 1. 这张图每个节点可能往哪里走,当前舰队满足哪些带路条件。
8
+ 2. 在当前分歧规则下,每条边的局部条件概率和从起点传播后的全局到达概率是多少。
9
+ 3. 每个节点会遇到什么敌舰编成,敌方制空和当前舰队制空关系如何。
10
+ 4. 能否把当前舰队、当前路径和节点敌编成导出到 noro6 / KC3Kai 等外部工具。
11
+
12
+ 默认假设:插件是信息展示和计算工具,不自动替用户出击或点击路线;外部网站不可用时,已经缓存的数据仍然可以工作。
13
+
14
+ ## 2. 关键结论
15
+
16
+ ### 2.1 需要“规则引擎”,不是简单查表
17
+
18
+ 路线分歧并不只有舰种数量。日文 Wiki 的路线分歧总页明确列出以下条件类别:
19
+
20
+ - 艦种、特定舰和史实舰;
21
+ - 艦队数量、旗舰类型;
22
+ - 索敌分数和分歧点系数;
23
+ - 航速、装备数量;
24
+ - 联合舰队类型;
25
+ - 已经过的节点、地图机关、札、血条、难度和出击舰队枠。
26
+
27
+ 而且条件通常按页面顺序优先匹配;游戏是在按下进击按钮时依据当时状态给出路线。因此同一地图不能只保存“舰种组合 -> 目的地”,必须保存有优先级的规则序列,并在求值时保存“当前已经过的节点、当前舰队状态、地图阶段”等上下文。
28
+
29
+ ### 2.2 概率必须分层表达
30
+
31
+ 应同时保存三种不同含义的数字:
32
+
33
+ - `ruleProbability`:规则明确给出的固定/偏向/随机概率;例如 Wiki 写明 80%/20%。
34
+ - `observedProbability`:TsunDB/KCNav 等出击样本按条件筛选后的统计比例。
35
+ - `reachProbability`:从起点概率质量传播到该节点或该边的结果。
36
+
37
+ 三者不能混成一个数字。规则缺失或舰队条件未知时,UI 显示 `unknown`;仅当非能动分歧出口存在但没有可靠概率时,才将出口按均等概率处理并加 `?`。能动分歧没有手动方向且没有可用 phase 默认规则时暂停概率传播并提示用户选择,不擅自采用统计结果替代规则结果。统计样本可以作为辅助提示,例如“规则未收录,近 N 次样本为……”,但必须显示样本量和过滤条件。
38
+
39
+ ### 2.3 最小可行架构:离线规范数据 + poi 实时状态
40
+
41
+ 推荐的运行边界:
42
+
43
+ ```text
44
+ 中文 Wiki / 日文 Wiki / NGA 人工复核 / TsunKit API
45
+ │ 建构期抓取、人工审阅、保留 sourceRefs
46
+
47
+ normalized maps / rules / enemy-comps
48
+ │ 插件运行时只读
49
+ ├── poi fcd.map + poi const/info state
50
+ └── UI + probability engine + external exporters
51
+ ```
52
+
53
+ 不建议把网页 HTML 解析和运行时请求直接放进插件:NGA 当前直接访问就会返回 403,Wiki 页面格式也会变化,TsunKit API 也可能暂时不可用。将网络访问限制在显式的刷新脚本,并把源版本、抓取时间、响应 hash 和未解决项写入数据清单,更容易回归和人工复核。
54
+
55
+ ## 3. poi 本地数据和事件路径
56
+
57
+ ### 3.1 地图几何
58
+
59
+ 当前 checkout 的 `assets/data/fcd/map.json` 是单行 JSON,已检查到:
60
+
61
+ - `meta.name = "map"`;
62
+ - `meta.version = "2026/07/28/02"`;
63
+ - `data` 有 136 个海图键;
64
+ - 以 `1-1` 为例:`route` 是数字 cell 编号到 `[from, to]` 的映射,`spots` 是节点到 `[x, y, kind]`;
65
+ - 以 `2-5` 为例,几何包含合流/分流的所有边,并保留每条边的数字编号。
66
+
67
+ 现有 `custom-plugins/poi-plugin-data-uploader/index.js` 已使用同一结构:
68
+
69
+ ```js
70
+ const mapKey = `${Math.floor(mapId / 10)}-${mapId % 10}`
71
+ const label = maps?.[mapKey]?.route?.[Number(cell)]?.[1]
72
+ ```
73
+
74
+ 因此第一版不需要重新下载地图坐标。插件运行时从 poi store 读取 `fcd.map`;该结构缺失时地图几何标为不可用,不从其他来源静默替代。
75
+
76
+ ### 3.2 当前舰队和 master ID
77
+
78
+ 现有插件代码已经验证了下面这组运行时输入足以组装计算行:
79
+
80
+ - `info.fleets`:舰队槽位及 `api_ship` roster ID;
81
+ - `info.ships`:舰娘实例、`api_ship_id`、HP、装备实例、当前搭载量、改修/熟练度;
82
+ - `info.equips`:装备实例;
83
+ - `const.$ships` / `const.$equips`:舰娘和装备 master 数据;
84
+ - `const.$shipTypes`:舰种展示数据;
85
+ - `info.basic.api_level`:司令部等级;
86
+ - `sortie.combinedFlag`、`sortie.sortieMapId`、`sortie.currentNode`:当前出击上下文(字段的可用性需以当前 poi 版本实测为准)。
87
+
88
+ 索敌值已在当前 poi 源码中确认:`views/utils/game-utils.ts` 的 `getSaku33` 返回 `{ ship, item, teitoku, total }`,分别对应舰船项、装备项、提督等级惩罚和总值。插件运行时组装与 poi `fleet-stat` 相同的 `[舰船实例, 舰船 master]` 和装备数据并调用该函数:装备槽按 `api_slotnum` 截断,保留 `api_slot_ex` 作为最后一槽,并把 `api_onslot` 与补强槽的 0 传入;出击中已护卫舰也按 poi selector 规则排除。插件分别以 `mapModifier = 1/2/3/4` 调用 poi,顶部显示四个 `total` 值;地图规则用 `data/los-config.json` 选择对应的分歧点系数。master、装备、提督等级或任一 33 式结果缺失时直接报告 poi 数据不可用,不使用本地公式替代。
89
+
90
+ 根据当前提供的通常图表格,已记录的索敌分歧点系数 / 司令部系数为:1-6 `3/0.35`、2-5 `1/0.4`、3-5 `4/0.35`、4-5 `2/0.35`、5-2 `2/0.35`、5-4 `2/0.35`、5-5 `2/0.35`、5-6 `4/0.4?`、6-1 `4/0.35`、6-2 `3/0.35`、6-3 `3/0.35`、6-5 `3/0.4?`、7-2 `4/0.4`、7-4 `4/0.35`、7-5 `4/0.35`。5-6 的 KCWiki 页面明确给出分歧点系数 4,并说明页面索敌线按 120 级司令部记录,低于 120 级时建议额外提高 1,因此司令部系数暂标为近似。其余通常图在该表中没有索敌分歧线,配置记为不适用;这不代表可以把它们的系数猜成 1 或 0.4。
91
+
92
+ 所有规则求值都应基于 master ID、舰种 ID、装备 type2、速度值和当前实例数据;名称只用于显示和 source note。敌舰也用敌舰 master ID,不用翻译后的名字作为身份。
93
+
94
+ 当前实现还把装备 type2=12/13(poi 的小型/大型电探)归入 `radar` 舰数,用于 3-2 等通常图。poi 的 `getFleetSpeed` 从舰娘实例的 `api_soku` 取舰队最低速力;该实例字段就是 poi 舰队页展示的有效速力,插件据此区分普通高速、高速+和最速。
95
+
96
+ 规则引擎另外支持 `containsName` / `nameCount`,用于长门、陆奥、秋津洲、如月以及 7-3 的史实舰组合。这里的名称只作为当前 poi master 数据的匹配键,实际舰队对象仍保留 master ID。地图阶段通过手动 phase 选择传入上下文;7-3 一期/二期使用 `flag` 条件表达,其他多阶段图仍需继续拆分来源规则。
97
+
98
+ 出击路径会在插件状态中记录已经经过的节点,并通过 `visited` 条件支持 4-5 的“经过 E/F 点”规则。路径历史只用于当前出击过程,不写回游戏状态。
99
+
100
+ ### 3.3 事件监听
101
+
102
+ 现有 sibling plugin 使用 `window.addEventListener('game.response', ...)`,按 `event.detail.path` 分派:
103
+
104
+ - `/kcsapi/api_req_map/start`:出击开始,响应里有 `api_maparea_id`、`api_mapinfo_no`、`api_cell_data`;
105
+ - `/kcsapi/api_req_map/next`:前进到下一节点;
106
+ - `/kcsapi/api_req_sortie/battleresult` 和 combined battle 对应路径:战斗结果/血条信息;
107
+ - `game.response` 之后节流刷新 UI,避免每个 API 回包都触发完整渲染。
108
+
109
+ 罗盘第一版至少要监听 `api_req_map/start` 和 `api_req_map/next`,才能把“当前节点”和“用户选择/实际路线”同步到图上;不应在出击前假设舰队状态一直不变。
110
+
111
+ ## 4. 各数据源的具体研究
112
+
113
+ ### 4.1 中文 Wiki
114
+
115
+ `https://zh.kcwiki.cn/wiki/1-1` 可直接读取,页面展示了:海域基本信息、路线分支表、舰种缩写定义、概率数值、索敌说明和原始 NGA 引用。
116
+
117
+ `2-5` 页面更能说明数据复杂度:同一分歧点同时存在潜艇/舰种/运输桶/航速/索敌条件,且有固定、随机和“约”三种语义。页面还给出路线示例、索敌阈值和制空线。这个页面适合作为第一批规则 fixture,但不能把攻略文案直接当作机器规则。
118
+
119
+ 建议建构器只抽取明确的结构化候选,保留原文:
120
+
121
+ ```json
122
+ {
123
+ "node": "G",
124
+ "priority": 30,
125
+ "predicate": { "kind": "los33", "coefficient": 1, "min": 41 },
126
+ "outcomes": [{ "to": "L", "probability": 1 }],
127
+ "sourceRefs": [{ "url": "...", "quote": "...", "checkedAt": "..." }],
128
+ "confidence": "verified"
129
+ }
130
+ ```
131
+
132
+ 无法确定“约 80%”是否为长期规则、样本估计还是攻略近似时,应使用 `approximate` 或 `unknown`,并把原文保留到审阅队列。
133
+
134
+ 本轮已将通常海域 36 张 `/{带路条件}` 子页以及 5-6 主页面的原始 wikitext 缓存到 `data/sources/kcwiki/routes/`,并在 `manifest.json` 中记录 revision ID、修订时间和 SHA-256。规则校正以这批缓存为审阅入口:`CV系`/`CV*` 归入 `CV_ALL`,明确的 `CV+CVB` 归入 `CV_MAIN`;`CL系`、`CL+CT`、`BB系` 和单独 `BB` 也分别使用不同令牌,避免把 CVL、CT 或 BBV 静默漏掉或误计入。
135
+
136
+ ### 4.2 日文 Wiki
137
+
138
+ `出撃` 总页列出通常海域并通过子页面链接到各海域;`ルート分岐` 总页提供路线条件分类、索敌公式和“梦美的常规图带路 & 出击配置”等交叉来源入口。
139
+
140
+ 它适合:
141
+
142
+ - 查找中文页面没有同步的条件;
143
+ - 统一日文舰种/装备术语;
144
+ - 复核条件优先级、索敌公式和特殊地图状态。
145
+
146
+ 它不适合被当作单一真相:页面自己也建议多来源检查,而且有些条件只以攻略/讨论形式出现。解析器应保留 `raw` 和页面标题,而不是只保存最终中文文案。
147
+
148
+ ### 4.3 NGA:浏览器会话是可行的采集边界
149
+
150
+ 本次调研验证了用户的判断:直接 HTTP 请求仍返回 403,网页检索也不能稳定打开带查询串的页面;但在已经登录 NGA 的 Firefox 会话中,用户给出的主帖第 2~5 页和关联帖可以正常读取。因此“命令行抓取失败”不能作为“资料不可用”的结论,但也不适合把 NGA 变成插件运行时依赖。
151
+
152
+ 浏览器读取到的内容特征:
153
+
154
+ - 主帖第 2 页包含 1-1、1-2、1-3、1-4、2-4、2-5 的路线说明、分歧条件、敌舰行、制空线和攻略注释;第 3~5 页继续覆盖 3-1 至 7-5。帖文是人工维护的半结构化 BBCode,不是稳定 JSON。
155
+ - 2-5 的示例同时出现舰种/数量、全高速、索敌阈值、装备类别和低速等条件,说明它可以提供很有价值的规则原文,但解析器不能只依赖一个固定表格列。
156
+ - 关联帖标题为“地图配置2.0”。第 1 页可见按海域/节点拆分的回复,例如 `pid=330412919` 的 1-1 敌方配置;回复中包含 `[table]`、`[dict]`、`[collapse]`,并给出敌舰 master ID、装备、阵型、经验值及制空劣势/均势/优势/确保门槛。主帖中的“敌方配置”链接会指向这些 PID。
157
+ - 页面会显示帖子修改时间;主帖较新的页面和关联帖早期回帖混在一起,因此同一规则可能需要以 `tid`、`page`、`pid` 和帖子时间区分版本。
158
+
159
+ 建议的 NGA 工作流:
160
+
161
+ 1. 用户在已登录浏览器中打开指定页面,插件或辅助工具接收用户明确复制的 BBCode/纯文本;不在出击运行时自动访问 NGA。
162
+ 2. 预处理器先按 `[collapse]`、`[table]`、`[dict]`、`===地图/节点===` 分段,保留无法识别的原文。
163
+ 3. 将路线候选、敌编成和制空门槛写入待审阅规范数据;每条记录保存 `sourceRef = { tid, page, pid, url, fetchedAt }`,并由人工确认后才成为 `verified`。
164
+ 4. 若主帖只给出路线而敌舰细节来自关联帖,两个 PID 都写入同一条规范记录;不能只保留主帖 URL。
165
+
166
+ 这种设计既利用浏览器会话可以读取 NGA 的事实,也把登录状态、页面布局变化和帖子修改造成的不确定性隔离在建构期。不要增加绕过验证、代理或高频访问逻辑。
167
+
168
+ ### 4.4 TsunKit API
169
+
170
+ 当前实际请求到的地图 API(`2-5`)形状如下:
171
+
172
+ ```text
173
+ GET /api/routing/maps/2-5/
174
+ result.route: { cellNo: [from, to, ...] }
175
+ result.spots: { nodeLabel: [x, y, kind] }
176
+ result.mapSet: { map, frameSet, breakpoints }
177
+ ```
178
+
179
+ 敌编成 API(`2-5/O`)形状如下:
180
+
181
+ ```text
182
+ GET /api/routing/maps/2-5/nodes/O/enemycomps/
183
+ result.entryCount
184
+ result.entries[]:
185
+ map, node, mainFleet, escortFleet, formation,
186
+ count, airpower, lbasAirpower,
187
+ uncertainAirpowerItems, masterId
188
+ ```
189
+
190
+ `mainFleet` 的每艘敌舰包含 `id`、名字、等级、HP/火力/对空/装甲和 `equips` master ID。`masterId` 是整套编成的标识,`count` 可用于统计权重。
191
+
192
+ 调研中观察到 `airpower` 是四个制空门槛的数组,例如 `[10, 19, 42, 84]`;`lbasAirpower` 是另一组带陆航语义的门槛。实现时必须用已知敌编成手算值和 Wiki 表格建立映射测试,确认数组顺序后再显示中文档位。`uncertainAirpowerItems` 非空时,制空结论应降低置信度。
193
+
194
+ 推荐数据策略:
195
+
196
+ 1. TsunKit 的 `entries` 作为敌编成和敌方制空的优先来源;
197
+ 2. 用 `mainFleet[].id` / `equips` 保存可重算依据;
198
+ 3. Wiki 的敌编成/制空值用来交叉核对和补齐缺项;
199
+ 4. 未知或冲突的值同时保留 `sourceValues`,不静默覆盖。
200
+
201
+ ### 4.5 地图图像
202
+
203
+ poi `fcd.map` 已有可用的节点坐标和边信息,仍作为路线绘制和数据可用性的主来源。进一步检查 TsunKit KCNav 前端后确认:地图背景使用 `https://tsunkit.net/api/assets/images/maps/{mapId}/background`;路线 API 的 `result.route[cell]` 第三个值是节点类型编号;KCNav 前端用该编号推导每个节点的 `nodeType`,再从 `map_main/{icon}` 或 `map_common/{icon}` 加载图标。
204
+
205
+ 插件沿用 KCNav 的节点类型表,把起点、Boss、运输、航空战、空袭、夜战、修理、能动分歧等类型映射到地图主图标和公共图标。由于路线 API 在连续请求时可能返回 `401 Unauthorized API automation detected`,当前实现把 37 张通常图背景提前转为质量 85 的 WebP,并把节点图标提前下载到 `assets/kcnav/`,运行时完全使用本地 `file://` 资源,不再请求远程;当前节点图标暂不渲染,仅保留样式数据。37 张通常图的地图 API 原始响应另缓存到 `data/sources/kcnav/maps/`,并从每条 `route` 的第三项生成各图节点类型表;只有缓存缺失的地图才会保留圆形节点和字母。
206
+
207
+ ## 5. 规范数据模型建议
208
+
209
+ ### 5.1 地图、边和节点
210
+
211
+ ```ts
212
+ type MapId = string // "2-5"
213
+ type NodeLabel = string // "O"
214
+
215
+ interface CompassMap {
216
+ mapId: MapId
217
+ sourceSnapshot: SourceSnapshot
218
+ nodes: Record<NodeLabel, {
219
+ x: number
220
+ y: number
221
+ kind: 'start' | 'battle' | 'air' | 'resource' | 'boss' | 'unknown'
222
+ sourceRefs: SourceRef[]
223
+ }>
224
+ edges: Array<{
225
+ id: string
226
+ cellNo: number
227
+ from: NodeLabel | null
228
+ to: NodeLabel
229
+ sourceRefs: SourceRef[]
230
+ }>
231
+ rules: RouteRule[]
232
+ }
233
+ ```
234
+
235
+ 边的 `cellNo` 必须保留,因为 poi API 的数字节点和实际地图字母节点都需要互相定位。
236
+
237
+ ### 5.2 带路规则
238
+
239
+ 规则建议采用小型 AST,不把自然语言直接拼成 JavaScript:
240
+
241
+ ```ts
242
+ interface RouteRule {
243
+ node: NodeLabel
244
+ priority: number
245
+ predicate: Predicate
246
+ outcomes: Array<{
247
+ to: NodeLabel
248
+ probability: number | 'unknown'
249
+ }>
250
+ appliesWhen?: { difficulty?: string; gauge?: number; phase?: number }
251
+ sourceRefs: SourceRef[]
252
+ confidence: 'exact' | 'verified' | 'parsed' | 'approximate' | 'unknown'
253
+ raw?: string
254
+ unresolved?: string[]
255
+ }
256
+ ```
257
+
258
+ 初始 predicate 集合只实现当前需求需要的部分:舰种/特定 master ID 计数、舰队数量、旗舰、速度、装备类别/数量、33 式索敌、联合舰队类型、已经过节点、地图阶段/难度/血条。没有匹配的表达式返回 `unknown`,不执行猜测。
259
+
260
+ ### 5.3 敌编成
261
+
262
+ ```ts
263
+ interface EnemyComposition {
264
+ mapId: MapId
265
+ node: NodeLabel
266
+ masterId: number
267
+ count: number
268
+ formation: number
269
+ mainFleet: Array<{ masterId: number; equipMasterIds: number[]; stats?: object }>
270
+ escortFleet: Array<{ masterId: number; equipMasterIds: number[]; stats?: object }>
271
+ airpower?: {
272
+ source: 'tsunkit' | 'computed' | 'wiki' | 'unknown'
273
+ thresholds?: [number, number, number, number]
274
+ uncertainItems?: number[]
275
+ }
276
+ sourceRefs: SourceRef[]
277
+ }
278
+ ```
279
+
280
+ 名称、舰种翻译和图标均是 presentation 层派生值。`masterId`、装备 ID 和来源快照才是数据层身份。
281
+
282
+ ## 6. 概率计算设计
283
+
284
+ ### 6.1 规则求值
285
+
286
+ 在节点 `N`:
287
+
288
+ 1. 按 `priority` 排序;
289
+ 2. 依次求值规则的 predicate;
290
+ 3. 取第一个确定匹配的规则;
291
+ 4. 若匹配规则产生多个出口,使用其明确权重;
292
+ 5. 若没有规则或规则含未知状态,返回 `unknown` 并附上原因。
293
+
294
+ 这里的“第一个”不是实现者猜测的优先级,而必须来自页面表格顺序或人工标注的 `priority`。
295
+
296
+ ### 6.2 概率质量传播
297
+
298
+ 从起点注入 `mass = 1`:
299
+
300
+ ```text
301
+ reach(from) = incoming mass
302
+ edgeMass(from -> to) = reach(from) × localProbability(from -> to)
303
+ reach(to) += edgeMass(from -> to)
304
+ ```
305
+
306
+ UI 同时显示:
307
+
308
+ - 当前节点的到达率 `reachProbability`;
309
+ - 当前边的局部概率 `localProbability`;
310
+ - 当前边承载的全局概率 `edgeMass`。
311
+
312
+ 图中若有合流节点,应按路径状态合并“只读的可交换上下文”;若规则依赖曾经经过的节点或阶段,则保留路径上下文,不能只按节点字符串去重。对非能动分歧中完全没有可靠概率的出口,按均等概率传播并把不确定性沿全局路径传递,在概率后附加 `?`;能动分歧则要求手动方向或使用命中的 phase 默认规则。真正无法求值的舰队条件仍显示未知,不与概率估算混淆。
313
+
314
+ ### 6.3 能动分歧
315
+
316
+ 2026-09-11 全通常图拓扑复查:以本地 KCNav 响应交叉检查规则出口、遗漏续航和 Boss 类型。修正 2-4 的 A/D 为终点(E→D),7-5 的 M 为终点,补充 5-6 H→R 和 5-2 出发点其余编成→B。4-5 K 的随机去 M 条件此前误写 K→N,现按索敌 <60、60~70、≥70 分别列 M/L、M/T/L、M/T,未知分布均分并标记问号。6-1、6-2、7-1 Boss 为 K,5-1 为 J;1-6 没有 Boss。7-4 J 的缓存类型为 4,KCWiki 也说明该点发生含潜艇的战斗,保留该类型。
317
+
318
+ 明确终点规则使用 `terminal: true`,避免与 `outcomes: []` 的“规则未收录”混淆;存在分歧规则却没有命中时仍为未知。测试覆盖全部规则的实际出口和显式终点,仅排除 6-4/6-5 用于选择起点的虚拟 1→2 转移。节点颜色以 KCNav 缓存类型为准,旧 Boss 字段不覆盖已知类型;圆形节点不应用图片位置偏移,航空战圆半径与空袭一致为 15。基础路径为 4px 蓝色线,透明度 0.85。
319
+
320
+ 用户选择能动分歧后,对该节点加入临时 `manualOverride`:
321
+
322
+ 当前通常海域已核对的能动分歧点为:4-5(A、C、I)、5-3(O)、5-5(F)、6-3(A)、7-4(F)、7-5(F、H、O)。7-5 的 F/H/O 分别对应 G/J、I/K、P/Q 两个出口;有 phase 规则时先采用命中的 phase 默认出口,用户手动选择后再覆盖;没有 phase 默认规则的能动点必须由用户指定方向,不再默认均分。
323
+
324
+ - 选中的边:`localProbability = 1`;
325
+ - 其他边:`localProbability = 0`;
326
+ - 原始规则概率保留,UI 以“手动选择覆盖”标识;
327
+ - 下游所有 `reachProbability` 重新计算;
328
+ - 到达实际游戏节点后,应清除已经经过节点的旧 override,避免跨出击污染。
329
+
330
+ 如果游戏里的分歧不是能动分歧,UI 只能展示规则,不应提供“选择路线”按钮。
331
+
332
+ ## 7. 制空实现建议
333
+
334
+ ### 7.1 当前舰队
335
+
336
+ 现有 `poi-plugin-sortie-checker` 的纯计算模块已经给出可复用的验证方向:
337
+
338
+ - 按装备 type2 识别舰战、爆战、舰爆、舰攻、水战、水爆和喷式机;
339
+ - 单格制空大致为 `floor((对空 + 改修补正) * sqrt(当前搭载) + 熟练度加成)`;
340
+ - 舰战/水战/喷式战和水爆的熟练度加成不同;
341
+ - `api_onslot` 必须取当前搭载,不应使用 master 最大搭载;
342
+ - 扩张槽装备不贡献搭载制空,但应按游戏规则处理其对空效果。
343
+
344
+ 在本插件中应把公式移到独立纯函数,并用已知编成测试;不要把 HKGY 插件的 UI 或静态 mapdata 当成运行时依赖。
345
+
346
+ ### 7.2 敌方制空
347
+
348
+ 优先顺序:
349
+
350
+ 1. TsunKit 的 `airpower`/`lbasAirpower` 门槛数组;
351
+ 2. 有完整敌舰装备和 slot 数据时,按 master 数据重新计算;
352
+ 3. Wiki 明确列出的敌制空值;
353
+ 4. 否则显示“未知”。
354
+
355
+ 若多个来源冲突,显示来源对照和置信度,不直接取最大值掩盖冲突。`uncertainAirpowerItems` 出现时,制空档位可以显示,但必须带“装备不确定”提示。
356
+
357
+ ## 8. 外部模拟器链接
358
+
359
+ ### 8.1 KC3Kai:优先实现
360
+
361
+ KC3Kai 的 import help 明确支持:
362
+
363
+ - URL fragment 中直接放 JSON;
364
+ - `fleetF.ships[]` 使用玩家舰船 master ID、等级、状态和 `equips[]`;
365
+ - `nodes[]` 可放单个 `fleetE` 或带 `weight` 的 `fleetEComps`;
366
+ - 每个节点可以指定 formation、夜战、空袭等行为。
367
+
368
+ 因此第一版 exporter 可以把“当前舰队 + 当前预测路径上的每个节点的一组敌编成”生成 JSON。TsunKit `count` 可转换为敌编成权重,但应标记为“样本权重”,不能表示官方概率。
369
+
370
+ 风险是 URL 长度和 simulator 对新 master ID 的支持。如果 simulator 数据库没有某个 master ID,import help 要求同时提供 stats;插件应捕获这个情况并允许只导出舰队或复制 JSON。
371
+
372
+ 2026-09-11 重新核对已部署的 `js/simulator-ui/ui-main.js`:URL hash 中的 `{ fleetF, nodes }` 会调用 `initSimImport`,直接执行 `SIM.runStats`,并非仅填充编辑界面。若按钮语义是“打开编辑”,应使用其 `#backup=`(LZMA + Base64)存档导入流程,或先验证其他编辑导入路径。`convert.js` 确认 `fleetEComps[].weight`、`NBOnly`、`airOnly`、`airRaid` 等字段。需要保留补给消耗、夜战和我方阵型的准确含义。
373
+
374
+ ### 8.2 noro6:DeckBuilder 可导入出击信息
375
+
376
+ 我方裸值来源修正:ship-info 无装备模式确实从 WCTF 成长数据计算,但本机及 npm 最新 WCTF 均为 20240308.0.1,缺少 #736 榧改等新舰,故最终移除该依赖。KC3Kai `FLEET_MODEL.getDefaultShip` 在传入等级时按 EVbase/EV、ASWbase/ASW、LOSbase/LOS 推算裸值,备份导入仅覆盖实际提供的字段,因此省略 EV/LOS 与未改修 ASW。对潜改修不能从等级推断,使用本地 `data/kc3kai-ship-stats.json` 成长快照补入;noro6 的 `convert.ts` 用 `s.asw`(总面板)反推出额外改修,因此保留 poi 实例总对潜。火雷空甲/运/耐久保留真实状态,敌方不删 KCNav 数据。已回归验证没有 WCTF 时 #736 可导出、175 级对潜改修、正常成长字段省略;不新增任何插件运行时远程数据库请求。
377
+
378
+ 2026-09-11 核对 [App.vue](https://github.com/noro6/kc-web/blob/main/src/App.vue) 与 [convert.ts](https://github.com/noro6/kc-web/blob/main/src/classes/convert.ts):支持 `?predeck=<encodeURIComponent(JSON)>`,DeckBuilder v4 除玩家舰队之外还支持 `s: { a: 海域编号, i: 海图编号, c: 节点列表 }`,每个节点使用 `{ c: 数字cell, pf: 我方阵型, ef: 敌方阵型, f1: { s: 敌舰列表 }, f2?: { s: 护卫敌舰列表 } }`。节点数字 cell 应从当前选定路线的 KCNav/poi 边映射取得。
379
+
380
+ 限制:当前 `loadDeckBuilder` 按敌舰 master ID 调用 `Enemy.createEnemyFromMasterId`,没有读取敌舰 `items`;同一 master ID 的不同装备/属性配置不能通过这条路径精确保留。此外每个节点只接受一套敌编成,没有 `fleetEComps` 权重列表。第一版建议由玩家选择敌编成(可预选样本最多的一组),明确使用目标站默认敌舰数据;若要求忠实传递装备变体,再验证其内部 SaveData 导入格式。
381
+
382
+ 两者都导出一条具体路线,不把所有有非零概率的节点串成一条路线。当前 `exporters.js` 按路径历史逐分歧求值,排除零概率边,并在导出面板提供完整路线选择;默认优先当前阶段 Boss,再按概率排序。未选择的能动分歧列出候选但不显示伪造概率,命中的 phase 默认规则仍适用。noro6 每节点默认样本最多的一套,可单独修改;我方阵型亦可调整。
383
+
384
+ 2026-09-11 实施与验证:KC3Kai 采用 v2 `#backup=`,已在浏览器用合成舰队确认编辑界面恢复两套敌编成及 90%/10% 权重,没有自动运行模拟。源码 `ui-main.js` 限 9 战、每节点 12 编成,导出超限直接提示,不截断。master ID 快照用于拦截目标站不支持的舰娘/装备。noro6 浏览器确认恢复合成睦月 Lv50、改修装备、1-4 L 点和複縦陣;`f1.t=0` 必须用于通常舰队,`t=1` 会错误变成空母机动部队。noro6 导入页面会自行计算。单测覆盖链接编解码、补强槽/空槽、编成选择、phase 默认、能动候选和航空/夜战映射;尚未验证真实 poi 舰队端到端及完整多战航空损耗。陆航、支援和漩涡损耗不在本次导出范围。
385
+
386
+ ## 9. 分阶段实施计划
387
+
388
+ ### P0:研究仓库和样本(已完成)
389
+
390
+ - 保存来源、访问结果、许可/署名注意事项;
391
+ - 固定 `1-1`、`2-5`、至少一张含索敌和随机分歧的 fixture;
392
+ - 定义 normalized schema、置信度和 unknown 语义。
393
+
394
+ 当前实现已在独立 `poi-plugin-compass` 仓库中落地规则 AST、来源引用、置信度和未知结果语义;首批 fixture 为 1-1、1-2、1-3、1-4、2-1、2-5 与 3-1。
395
+
396
+ ### P1:离线通常海域 MVP(进行中)
397
+
398
+ - 从 poi store 读取第一舰队和 master 数据;
399
+ - 使用 `fcd.map` 画节点/边;
400
+ - 手工审阅并录入少量路线 AST;
401
+ - 展示每个节点所有规则、当前匹配状态和路线质量传播;
402
+ - 支持能动分歧 override;
403
+ - 不接运行时网络,不复制第三方地图图片。
404
+
405
+ 已完成:CommonJS poi 插件壳、全部通常海域地图目录与 SVG 几何、第一舰队状态读取、1-1/1-2/1-3/1-4/2-1/2-5/3-1 规则样本、局部/全局概率传播、手动覆盖引擎,以及直接复用 poi `getSaku33` 的 33 式索敌读取。
406
+ 未完成:其余通常海域规则、敌编成/制空数据面板和联合舰队状态。
407
+
408
+ ### P2:敌编成和制空
409
+
410
+ - 增加 TsunKit refresh 脚本、原始响应摘要、source hash 和 normalized cache;
411
+ - 将敌舰/装备统一到 master ID;
412
+ - 接入制空纯函数和 TsunKit 门槛数组;
413
+ - 显示 `unknown`、`uncertain` 和来源冲突。
414
+
415
+ ### P3:活动图和人工来源
416
+
417
+ - 加入难度、血条、阶段、札和已开点状态;
418
+ - 为 NGA 文本提供人工导入审阅格式;
419
+ - 活动图规则默认只展示已验证项,未验证项不参与自动概率传播。
420
+
421
+ ### P4:外部工具
422
+
423
+ - 先实现 KC3Kai JSON fragment exporter;
424
+ - 再做 noro6 manager JSON POC;
425
+ - 增加 URL 长度检查、复制 JSON 备用按钮和外部版本兼容提示。
426
+
427
+ ## 10. 验收和测试边界
428
+
429
+ - 单元测试:predicate 求值、规则优先级、固定/加权出口、unknown、概率质量和 manual override;
430
+ - fixture 测试:1-1 的舰数随机分歧、2-5 的舰种/桶/速度/索敌组合;
431
+ - 敌编成测试:TsunKit `mainFleet` 的 master ID 保持不变,`count` 权重不被误当成官方概率;
432
+ - 制空测试:至少一组无空母、一组含舰载机、一组 `uncertainAirpowerItems`;
433
+ - exporter 测试:KC3Kai JSON 能被 import-help schema 接受,Noro6 `?data=` 在浏览器中能恢复最小 fixture;
434
+ - 运行时验证:poi 重载插件后切换编成、装备、联合舰队和实际推进节点,确认图和计算刷新;
435
+ - 不把完整 poi 根目录 sweep 作为验收门槛。未来有运行时代码时,遵循 `custom-plugins` sibling plugin 的最小测试边界。
436
+
437
+ ## 11. 主要风险
438
+
439
+ 1. **规则漂移**:Wiki、NGA 帖子、TsunDB 统计和游戏实装可能不同。必须保留 sourceRefs、时间和置信度。
440
+ 2. **网络依赖**:NGA 已验证存在直接访问阻断;所有外部来源都应可脱网运行。
441
+ 3. **统计误读**:TsunKit 的 `count` 是样本数,不能直接改成规则概率。
442
+ 4. **门槛数组误读**:TsunKit 的 `airpower` 需要 fixture 验证四个元素的顺序;在确认前不应硬编码中文档位。
443
+ 5. **资源许可**:中文 Wiki 条款要求 BY-NC-SA;NGA、日文 Wiki、TsunKit 图像许可未确认。MVP 不复制图片和大段原文。
444
+ 6. **外部链接失效**:noro6/KC3Kai 的 URL 数据结构和 master 数据库会更新;导出器必须有版本/失败提示和复制原始 JSON 兜底。
445
+ 7. **poi 状态时序**:分歧判定使用的是进击按钮时的状态;退避、编成变化和联合舰队状态可能改变上下文,不能只依赖出击开始快照。
@@ -0,0 +1,85 @@
1
+ # 来源清单与访问记录
2
+
3
+ 调研日期:2026-09-11(Asia/Shanghai)。URL 中的 `{mapId}` 表示类似 `1-1`、`2-5` 或活动图编号的海域标识;`{nodeLabel}` 表示字母节点。
4
+
5
+ ## 用户指定来源
6
+
7
+ | 来源 | 用途 | 当前访问结果 | 计划角色 |
8
+ | --- | --- | --- | --- |
9
+ | [poi 内置 `assets/data/fcd/map.json`](https://github.com/poooi/poi/blob/master/assets/data/fcd/map.json) | 地图坐标、边、数字节点和字母节点 | 当前 checkout 已有本地快照 | 运行时地图几何主来源 |
10
+ | [中文 Wiki 海域页](https://zh.kcwiki.cn/wiki/1-1) | 中文海域名、带路表、敌编成说明、制空和攻略注释 | 可访问;页面将条件表和来源链接一起展示 | 建构时规则/说明来源,保留来源引用 |
11
+ | [日文 Wiki 出击总表](https://wikiwiki.jp/kancolle/%E5%87%BA%E6%92%83) | 海域列表及子页面入口 | 可访问;总表下含通常海域子页面入口 | 子页面发现入口,不直接作为唯一规则源 |
12
+ | [日文 Wiki 路线分歧](https://wikiwiki.jp/kancolle/%E3%83%AB%E3%83%BC%E3%83%88%E5%88%86%E5%B2%90) | 带路条件语义、优先级、索敌公式、舰种/航速/装备等条件 | 可访问;明确说明进击时状态、条件优先级和未知因素 | 规则 DSL 设计和人工核对参考 |
13
+ | [NGA 梦美帖第 2 页](https://bbs.nga.cn/read.php?tid=23451223&authorid=39227030&opt=262144&noBBCode&page=2) 至 [第 5 页](https://bbs.nga.cn/read.php?tid=23451223&authorid=39227030&opt=262144&noBBCode&page=5) | 常规海域带路和敌舰配置 | 直接 HTTP 仍返回 403;但在 Firefox 登录会话中已成功读取第 2~5 页 | 浏览器人工采集/复核来源;不得把网页访问器做成运行时高频依赖 |
14
+ | [NGA 关联回复帖](https://bbs.nga.cn/read.php?tid=16820397&authorid=39227030&opt=262144&noBBCode) | 被上述带路和敌舰配置引用的回复 | Firefox 登录会话可读取;主题为“地图配置2.0”,第 1 页可见按地图/节点拆分的敌编成表、master ID、装备和制空门槛 | 浏览器人工采集/复核来源;以 `tid/page/pid` 建立可追溯引用 |
15
+ | [TsunKit 地图 API](https://tsunkit.net/api/routing/maps/{mapId}/) | 路由、节点坐标、mapSet 素材元数据 | 当前调研用 `1-1`、`2-5` 请求成功;末尾 `/` 更稳定 | 建构时地图补充/校验,必要时缓存原始响应 |
16
+ | [TsunKit 敌编成 API](https://tsunkit.net/api/routing/maps/{mapId}/nodes/{nodeLabel}/enemycomps/) | 敌舰 master ID、装备 ID、编成出现次数、制空门槛 | 当前调研用 `2-5/O`、`2-5/C`、`2-5/I` 请求成功 | 建构时敌编成主来源,运行时读取规范缓存 |
17
+ | [TsunKit KCNav](https://tsunkit.net/nav/{mapId}) / [地图背景接口](https://tsunkit.net/api/assets/images/maps/{mapId}/background) | 地图和其他图像资源、人工交叉检查 | KCNav 前端实际使用背景接口;通常图背景源文件返回 `image/png`,约 1200×720 | 构建期下载后转换为质量 85 的 WebP,保存到插件本地 `assets/kcnav/`;运行时不请求远程 |
18
+ | [TsunKit 路线 API / 图标接口](https://tsunkit.net/api/routing/maps/{mapId}/) | 节点类型编号、KCNav 节点图标 | `result.route[cell][2]` 与 KCNav `getNodeTypes` 一致;图标接口返回 `image/png`;路线 API 连续请求时曾返回 `401 Unauthorized API automation detected` | 构建期来源;图标和已核对的节点类型快照保存到插件本地,运行时不请求远程 |
19
+
20
+ KCNav 地图路线响应由 `scripts/cache-kcnav-maps.js` 构建期获取,默认每次请求间隔 15 秒:
21
+
22
+ ```text
23
+ data/sources/kcnav/maps/{mapId}.json
24
+ data/sources/kcnav/maps/manifest.json
25
+ data/kcnav-node-types-by-map.json
26
+ ```
27
+
28
+ 其中第一项保存完整的 `GET /api/routing/maps/{mapId}/` JSON 响应;节点类型表从 `result.route[*][2]` 提取,映射到本地 KCNav 节点类型 ID。刷新命令为 `npm run cache:kcnav-maps`,如需缩短本地测试间隔可显式传入 `--interval-ms`。
29
+
30
+ ## 外部集成和实现参考
31
+
32
+ ### 敌编成日期筛选(2026-09-11)
33
+
34
+ 核对 `https://tsunkit.net/nav/scripts/routing.js`:`startDate`/`endDate` 分别使用参数 `start`/`end`,格式为 `YYYY-MM-DD`;`enemies` 面板将同一组筛选参数传给节点的 `enemycomps` 接口。本插件固定本轮起止日期,仅发送这两个参数,不发送司令部等级、血条、phase、舰队或路线限制。最近一个月采用当天 UTC 日期向前一个日历月(月末截到上月末日)。
35
+
36
+ `https://tsunkit.net/lang/en/kcnav.json` 的 `panelEnemiesAirPowerHeader` 顺序为 `termAirAD / termAirAP / termAirAS / termAirASPlus`;与前端 `airpower[0..3]` 对应,分别为劣势、均势、优势、确保。`lbasAirpower` 使用相同顺序但单独显示。原始响应按日期窗口/海图/节点保存,manifest 记录请求 URL、SHA-256、每节点抓取时间和完成状态。遇到请求失败即停止,保留进度供重跑;运行时只读取本地缓存。
37
+
38
+ 每个 API entry 原样保留舰船顺序、装备、阵型及编成 masterId;舰船实体用 `mainFleet[].id` / `escortFleet[].id`。界面按 `count` 降序显示,以本节点返回记录的 count 总和计算样本占比。零样本不生成百分比;四档制空缺失时显示未知;任何编成缺制空时,节点整体范围也显示未知。混合阶段和司令部等级的占比仅描述当前抓取样本。
39
+
40
+ | 来源 | 发现 |
41
+ | --- | --- |
42
+ | [HKGY/poi-plugin-sortie-checker](https://github.com/HKGY/poi-plugin-sortie-checker) | 已有 MIT 许可的 poi 插件,覆盖当前舰队组装、制空值、33 式索敌和静态分歧检查;可作为公式与 fixture 参考,但本插件的路线概率图和交互式路径仍需独立设计。 |
43
+ | [noro6/kc-web](https://github.com/noro6/kc-web) | 本插件采用 `?predeck=` 的 DeckBuilder v4 导入,包含出击节点和单套敌编成;通常舰队必须 `f1.t=0`。已用合成舰队浏览器验证;敌装备使用目标站默认值。 |
44
+ | [noro6/kc-web 当前版本/发布页](https://github.com/noro6/kc-web/releases) | 2026-09-11 调研时可见最新发布为 `2.53.5`;外部版本变动不能作为插件运行时契约。 |
45
+ | [KC3Kai simulator import help](https://kc3kai.github.io/kancolle-replay/simulator-import-help.html) | 原始 JSON fragment 会直接运行统计;本插件采用已部署 `js/simulator-ui/ui-main.js` 和 `convert.js` 的 v2 `#backup=` LZMA/Base64 编辑入口,保留全部敌编成权重。已浏览器验证两套编成 90%/10%;限制 9 战、每节点 12 编成。 |
46
+ | [KC3Kai replay index](https://github.com/KC3Kai/kancolle-replay/blob/master/index.html) | 目前支持 `?s=` 模拟器短数据和 `?r=` replay 数据;插件不应假设可以匿名写入短链服务。 |
47
+ | [poi 主仓库](https://github.com/poooi/poi) | 插件生态和主仓库参考;实际运行时以当前 checkout 的 poi API 和 sibling plugin 代码为准。 |
48
+
49
+ ## 本地快照和可复现信息
50
+
51
+ ### KCWiki 路线原文缓存
52
+
53
+ 通常海域目录中的 37 张地图,其中文 Wiki `/{带路条件}` 子页以及 5-6 主页面的原始 wikitext 保存在:
54
+
55
+ ```text
56
+ data/sources/kcwiki/routes/{mapId}.wiki
57
+ data/sources/kcwiki/routes/manifest.json
58
+ ```
59
+
60
+ 缓存脚本使用 KCWiki MediaWiki API 的 `revisions` 内容接口,保留页面标题、page ID、revision ID、修订时间、抓取时间、页面 URL、API URL 和 SHA-256。刷新命令为 `npm run cache:kcwiki`;它是建构/校对步骤,插件运行时不访问 Wiki。原文缓存与规范化规则分开保存,后者仍需人工审阅后才可进入 `data/maps.json` 或 `data/normal-maps.json`。
61
+
62
+ 缓存内容可能受中文 Wiki 的 BY-NC-SA 条款约束,发布或分发前应继续保留来源和许可说明,并确认是否需要缩减为内部校对快照。
63
+
64
+ - `assets/data/fcd/map.json`:`meta.version = 2026/07/28/02`,当前 JSON 的海图键数量为 136。
65
+ - poi 现有 `custom-plugins/poi-plugin-data-uploader/index.js` 已验证 `fcd.map[mapKey].route[cell][1]` 可把出击 API 的数字节点映射为字母节点。
66
+ - NGA 主帖的第 2~5 页在浏览器会话中可读,内容是按海域组织的半结构化 BBCode:路线说明、分歧条件、敌舰配置、制空线、攻略注释、图片/回帖链接混在同一帖文中;第 2 页覆盖 1-1、1-2、1-3、1-4、2-4、2-5,第 3~5 页继续覆盖 3-1 至 7-5 的常规图内容。
67
+ - NGA 关联帖“地图配置2.0”第 1 页可见 `pid=330412919` 等按海域/节点拆分的回复;回复中直接带 `[table]`、`[dict]`、`[collapse]` 等 BBCode,并包含敌舰 master ID、装备、阵型和制空劣势/均势/优势/确保门槛。主帖链接到这些回复的 PID,因此导入时必须保存 `tid`、`page`、`pid`,而不是只保存一个主题 URL。
68
+ - NGA 浏览器读取依赖已有登录会话;本次没有提交表单或绕过验证。TsunKit 图片资源已在构建期下载到插件本地;未来若做采集器,应优先采用用户在浏览器中触发的人工导入/复制文本流程,并为每份规范数据记录 `fetchedAt`、来源 PID、原文 hash 和人工复核状态。
69
+ - TsunKit `GET /api/routing/maps/2-5/` 的结果顶层为 `result.route`、`result.spots`、`result.mapSet`;KCNav 前端的地图背景请求为 `GET /api/assets/images/maps/{mapId}/background`,当前 `5-3` 返回 1200×720 PNG。KCNav `flowchart.js` 的 `getNodeTypes` 使用 `result.route[cell][2]`,数值图标通过 `/api/assets/images/spritesheets/map_main/{icon}` 加载,`map_common/{icon}` 字符串则通过 `/api/assets/images/spritesheets/{icon}` 加载。
70
+ - TsunKit `GET /api/routing/maps/2-5/nodes/O/enemycomps/` 的结果顶层为 `result.entryCount`、`result.entries`;entry 当前观察到的字段为 `map`、`node`、`mainFleet`、`escortFleet`、`formation`、`count`、`airpower`、`lbasAirpower`、`uncertainAirpowerItems`、`masterId`。
71
+ - `airpower` 观察到类似 `[10, 19, 42, 84]` 的四档门槛数组;必须在实现时用已知 Wiki 数值写测试,确认数组顺序后再给用户显示“劣势/均衡/优势/确保”标签。
72
+
73
+ ## 许可、署名和数据处理
74
+
75
+ - 中文 Wiki 条款要求遵守 BY-NC-SA 并尽量署名出处;若把其文字或结构化内容纳入仓库,必须保留来源、抓取时间、许可说明和修改记录,不能把这些内容包装成 MIT 数据。
76
+ - 日文 Wiki、NGA、TsunKit 的具体内容/图片许可不能从本次调研中推断。当前已将 TsunKit 的通常图背景和 KCNav 图标复制到插件 `assets/kcnav/` 以满足离线运行;后续发布前需要补充 TsunKit 图片资源的许可/署名确认,也不把未确认的网页内容作为已验证规则发布。
77
+ - 第三方响应原文只适合作为建构期审计材料;未来若提交仓库,需要先明确体积、许可、缓存期限和是否只保存摘要/规范化结果。
78
+
79
+ ## 当前通常图规则录入
80
+
81
+ 本轮通过中文 Wiki 的 MediaWiki API 读取各海域的 `/{带路条件}` 子页面,并把规则按地图拆入 `data/normal-maps.json`;来源页面同时保留对应 NGA 回复 PID。已录入并通过几何出边覆盖检查的地图为:
82
+
83
+ `1-1`~`1-6`、`2-1`~`2-5`、`3-1`~`3-5`、`4-1`~`4-5`、`5-1`~`5-6`、`6-1`~`6-5`、`7-1`~`7-5`。5-6 的路线分支直接记录在海域主页面 `南方海域/5-6`,因此缓存脚本对该图使用主页面标题,而其他通常图继续使用 `/{带路条件}` 子页。
84
+
85
+ 其中带有“约”“随机但未给比例”“原文带问号”或依赖分歧点系数/司令部系数的条件,使用 `approximate` 或 `unknown`;这类记录仍提供可能出口,但不产生伪造的全局百分比。后续继续补录时,先以同一 API 子页取得规则原文,再用日文 Wiki 或浏览器中的 NGA 页面复核有冲突的条件。
package/enemies.js ADDED
@@ -0,0 +1,56 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const cacheRoot = path.join(__dirname, 'data/sources/kcnav/enemies')
6
+ const files = new Map()
7
+
8
+ function readLocalJson(file) {
9
+ const stat = fs.statSync(file)
10
+ const cached = files.get(file)
11
+ if (cached?.mtime === stat.mtimeMs && cached.size === stat.size) return cached.value
12
+ const value = JSON.parse(fs.readFileSync(file, 'utf8'))
13
+ files.set(file, { mtime: stat.mtimeMs, size: stat.size, value })
14
+ return value
15
+ }
16
+
17
+ function thresholds(values) {
18
+ return Array.isArray(values) && values.length === 4
19
+ && values.every((value) => Number.isFinite(value) && value >= 0)
20
+ ? { disadvantage: values[0], parity: values[1], superiority: values[2], supremacy: values[3] }
21
+ : null
22
+ }
23
+
24
+ function summarizeEntries(entries) {
25
+ const total = entries.reduce((sum, entry) => sum + entry.count, 0)
26
+ const rows = entries.map((entry) => ({
27
+ ...entry,
28
+ share: total > 0 ? entry.count / total : null,
29
+ thresholds: thresholds(entry.airpower),
30
+ lbasThresholds: thresholds(entry.lbasAirpower),
31
+ uncertain: Boolean(entry.uncertainAirpowerItems?.length),
32
+ })).sort((a, b) => b.count - a.count)
33
+ function range(key) {
34
+ if (!rows.length || rows.some((row) => !row.thresholds)) return null
35
+ const values = rows.map((row) => row.thresholds[key])
36
+ return [Math.min(...values), Math.max(...values)]
37
+ }
38
+ return { rows, total, superiority: range('superiority'), supremacy: range('supremacy'), uncertain: rows.some((row) => row.uncertain) }
39
+ }
40
+
41
+ function loadEnemies(mapId, node) {
42
+ try {
43
+ const manifest = readLocalJson(path.join(cacheRoot, 'manifest.json'))
44
+ const source = manifest.nodes[`${mapId}/${node}`]
45
+ if (!source) return { status: 'missing', window: manifest.window }
46
+ const payload = readLocalJson(path.join(cacheRoot, source.file))
47
+ return { status: 'ready', window: manifest.window, source, ...summarizeEntries(payload.result.entries) }
48
+ } catch (error) {
49
+ return { status: error.code === 'ENOENT' ? 'missing' : 'invalid' }
50
+ }
51
+ }
52
+
53
+ const formations = { 1: '单纵', 2: '复纵', 3: '轮型', 4: '梯形', 5: '单横', 6: '警戒', 11: '第一警戒', 12: '第二警戒', 13: '第三警戒', 14: '第四警戒' }
54
+ function formationLabel(id) { return formations[id] || `阵型 ${id}` }
55
+
56
+ module.exports = { loadEnemies, summarizeEntries, thresholds, formationLabel }