poi-plugin-compass 0.1.3 → 0.1.4
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/air-status.js +5 -5
- package/assets/compass.css +62 -46
- package/assets/icons/COPYRIGHT.md +7 -0
- package/assets/icons/spot/2.svg +37 -0
- package/assets/icons/spot/3.svg +37 -0
- package/assets/icons/spot/4-1.svg +37 -0
- package/assets/icons/spot/4-2.svg +37 -0
- package/assets/icons/spot/4-3.svg +8 -0
- package/assets/icons/spot/4-4.svg +30 -0
- package/assets/icons/spot/5.svg +50 -0
- package/assets/icons/spot/8.svg +56 -0
- package/assets/icons/spot/9.svg +37 -0
- package/index.js +299 -40
- package/package.json +2 -2
package/air-status.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const states = [
|
|
4
|
-
{ label: '
|
|
5
|
-
{ label: '
|
|
6
|
-
{ label: '
|
|
7
|
-
{ label: '
|
|
8
|
-
{ label: '
|
|
4
|
+
{ label: '丧', shadow: '#ef5b5b' },
|
|
5
|
+
{ label: '劣', shadow: '#ed9f45' },
|
|
6
|
+
{ label: '均', shadow: '#9aa1aa' },
|
|
7
|
+
{ label: '优', shadow: '#91d7a2' },
|
|
8
|
+
{ label: '确', shadow: '#29975e' },
|
|
9
9
|
]
|
|
10
10
|
|
|
11
11
|
function airStateRange(air, data) {
|
package/assets/compass.css
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
min-height: 30px;
|
|
36
36
|
gap: 8px;
|
|
37
37
|
margin-bottom: 8px;
|
|
38
|
+
flex-wrap: wrap;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
.compass-toolbar-fleet,
|
|
@@ -60,6 +61,15 @@
|
|
|
60
61
|
.compass-los-icon { position: relative; display: inline-flex; width: 28px; height: 28px; align-items: center; justify-content: center; }
|
|
61
62
|
.compass-los-icon img { width: 28px; height: 28px; }
|
|
62
63
|
.compass-los-coefficient { position: absolute; right: -1px; bottom: -3px; font-size: 14px; font-weight: 800; color: white; -webkit-text-stroke: 3px #151a22; paint-order: stroke fill; }
|
|
64
|
+
.compass-option-group { display: inline-flex; align-items: center; flex-shrink: 0; }
|
|
65
|
+
.compass-option-label { padding: 0 4px; color: #c9d8e7; font-size: 12px; }
|
|
66
|
+
.compass-option-group button { border-radius: 0; }
|
|
67
|
+
.compass-option-group button:first-of-type { border-radius: 3px 0 0 3px; }
|
|
68
|
+
.compass-option-group button:last-of-type { border-radius: 0 3px 3px 0; }
|
|
69
|
+
.compass-option-group button + button { margin-left: -1px; }
|
|
70
|
+
.compass-option-group button.is-active { position: relative; z-index: 1; border-color: #8fe0a2; background: rgba(143, 224, 162, 0.2); }
|
|
71
|
+
.compass-option-toggle { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; color: #c9d8e7; font-size: 12px; white-space: nowrap; }
|
|
72
|
+
.compass-option-toggle input { margin: 0; accent-color: #8fe0a2; }
|
|
63
73
|
.compass-portrait {
|
|
64
74
|
position: relative;
|
|
65
75
|
display: inline-flex;
|
|
@@ -100,7 +110,8 @@
|
|
|
100
110
|
.compass-map-select summary,
|
|
101
111
|
.compass-reset,
|
|
102
112
|
.compass-manual button,
|
|
103
|
-
.compass-phase-group button
|
|
113
|
+
.compass-phase-group button,
|
|
114
|
+
.compass-option-group button {
|
|
104
115
|
color: #f1f5f9;
|
|
105
116
|
border: 1px solid #687587;
|
|
106
117
|
border-radius: 3px;
|
|
@@ -178,7 +189,9 @@
|
|
|
178
189
|
.compass-manual button:hover,
|
|
179
190
|
.compass-manual button:focus,
|
|
180
191
|
.compass-phase-group button:hover,
|
|
181
|
-
.compass-phase-group button:focus
|
|
192
|
+
.compass-phase-group button:focus,
|
|
193
|
+
.compass-option-group button:hover,
|
|
194
|
+
.compass-option-group button:focus {
|
|
182
195
|
border-color: #b8c7d9;
|
|
183
196
|
background: #3a4655;
|
|
184
197
|
}
|
|
@@ -216,60 +229,56 @@
|
|
|
216
229
|
pointer-events: none;
|
|
217
230
|
}
|
|
218
231
|
|
|
219
|
-
.compass-node-air-state {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
stroke:
|
|
228
|
-
stroke-width: 4;
|
|
229
|
-
opacity: 0.85;
|
|
230
|
-
transition: stroke 120ms ease, stroke-width 120ms ease, opacity 120ms ease;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.compass-edge.is-reachable line {
|
|
234
|
-
opacity: 1;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.compass-edge.is-probability-high line {
|
|
238
|
-
stroke: #78d99a;
|
|
239
|
-
stroke-width: 7;
|
|
232
|
+
.compass-node-air-state {
|
|
233
|
+
font-family: "Songti SC", "STSong", "SimSun", serif;
|
|
234
|
+
font-size: 14px;
|
|
235
|
+
font-weight: 700;
|
|
236
|
+
fill: #ffffff;
|
|
237
|
+
stroke: #151a22;
|
|
238
|
+
stroke-width: 2px;
|
|
239
|
+
paint-order: stroke fill;
|
|
240
|
+
stroke-linejoin: round;
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
.compass-
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
.compass-node-icon {
|
|
244
|
+
pointer-events: auto;
|
|
245
|
+
overflow: visible;
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
.compass-
|
|
248
|
-
|
|
249
|
-
stroke-width: 4.5;
|
|
248
|
+
.compass-node-icon.is-prophet {
|
|
249
|
+
opacity: 0.92;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
.compass-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
opacity: 0.85;
|
|
252
|
+
.compass-node-overlay {
|
|
253
|
+
overflow: visible;
|
|
254
|
+
pointer-events: none;
|
|
256
255
|
}
|
|
257
256
|
|
|
258
|
-
.compass-
|
|
259
|
-
|
|
260
|
-
|
|
257
|
+
.compass-node-overlay-content {
|
|
258
|
+
display: flex;
|
|
259
|
+
width: 100%;
|
|
260
|
+
height: 100%;
|
|
261
|
+
align-items: center;
|
|
262
|
+
justify-content: center;
|
|
263
|
+
overflow: visible;
|
|
261
264
|
}
|
|
262
265
|
|
|
263
|
-
.compass-
|
|
264
|
-
|
|
266
|
+
.compass-node-enemy-avatar .compass-portrait {
|
|
267
|
+
width: 100% !important;
|
|
268
|
+
height: 100% !important;
|
|
269
|
+
box-shadow: 0 0 0 1px #151a22, 0 0 0 2px #ffffff99;
|
|
265
270
|
}
|
|
266
271
|
|
|
267
|
-
.compass-edge
|
|
268
|
-
|
|
272
|
+
.compass-edge {
|
|
273
|
+
outline: none;
|
|
274
|
+
cursor: pointer;
|
|
269
275
|
}
|
|
270
276
|
|
|
271
|
-
.compass-edge
|
|
272
|
-
stroke
|
|
277
|
+
.compass-edge line {
|
|
278
|
+
stroke: var(--compass-edge-color, #87919a);
|
|
279
|
+
stroke-width: var(--compass-edge-width, 2.25);
|
|
280
|
+
opacity: var(--compass-edge-opacity, 0.3);
|
|
281
|
+
transition: stroke 120ms ease, stroke-width 120ms ease, opacity 120ms ease;
|
|
273
282
|
}
|
|
274
283
|
|
|
275
284
|
.compass-edge.is-manual-choice {
|
|
@@ -324,10 +333,8 @@
|
|
|
324
333
|
vector-effect: non-scaling-stroke;
|
|
325
334
|
}
|
|
326
335
|
|
|
327
|
-
.compass-node-icon
|
|
328
|
-
.compass-node-icon-secondary {
|
|
329
|
-
pointer-events: none;
|
|
330
|
-
}
|
|
336
|
+
.compass-node-icon { pointer-events: auto; }
|
|
337
|
+
.compass-node-icon-secondary { pointer-events: none; }
|
|
331
338
|
|
|
332
339
|
.compass-node:hover .compass-node-shape,
|
|
333
340
|
.compass-node:focus .compass-node-shape {
|
|
@@ -354,6 +361,15 @@
|
|
|
354
361
|
filter: brightness(1.15);
|
|
355
362
|
}
|
|
356
363
|
|
|
364
|
+
.compass-node.is-node-choice .compass-node-shape {
|
|
365
|
+
filter: drop-shadow(0 0 3px rgba(130, 233, 255, 0.95)) drop-shadow(0 0 8px rgba(130, 233, 255, 0.65));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.compass-node.is-node-choice:hover .compass-node-shape,
|
|
369
|
+
.compass-node.is-node-choice:focus .compass-node-shape {
|
|
370
|
+
filter: brightness(1.12) drop-shadow(0 0 3px rgba(130, 233, 255, 1)) drop-shadow(0 0 10px rgba(130, 233, 255, 0.75));
|
|
371
|
+
}
|
|
372
|
+
|
|
357
373
|
.compass-node-label {
|
|
358
374
|
text-anchor: middle;
|
|
359
375
|
pointer-events: none;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c)
|
|
2
|
+
2016, 2017 [かがみ](https://github.com/KagamiChan)
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
THESE SVG ICONS ARE **NOT** GOVERNED BY THE MIT LICENSE.
|
|
6
|
+
|
|
7
|
+
ICONS HERE CAN **ONLY** BE USED IN POI, INCLUDING PLUGINS, EXTENSIONS AND DERIVATIVES OF POI. ANY OTHER USAGE WITHOUT EXPLICIT PERMISSION IS PROHIBITED.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<circle style="fill:#B2FF59;stroke:#7CB342;stroke-width:50;stroke-miterlimit:10;" cx="200" cy="200" r="160"/>
|
|
7
|
+
<g>
|
|
8
|
+
</g>
|
|
9
|
+
<g>
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
</g>
|
|
13
|
+
<g>
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
</g>
|
|
17
|
+
<g>
|
|
18
|
+
</g>
|
|
19
|
+
<g>
|
|
20
|
+
</g>
|
|
21
|
+
<g>
|
|
22
|
+
</g>
|
|
23
|
+
<g>
|
|
24
|
+
</g>
|
|
25
|
+
<g>
|
|
26
|
+
</g>
|
|
27
|
+
<g>
|
|
28
|
+
</g>
|
|
29
|
+
<g>
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
</g>
|
|
33
|
+
<g>
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
</g>
|
|
37
|
+
</svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<circle style="fill:#B39DDB;stroke:#512DA8;stroke-width:50;stroke-miterlimit:10;" cx="200" cy="200" r="160"/>
|
|
7
|
+
<g>
|
|
8
|
+
</g>
|
|
9
|
+
<g>
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
</g>
|
|
13
|
+
<g>
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
</g>
|
|
17
|
+
<g>
|
|
18
|
+
</g>
|
|
19
|
+
<g>
|
|
20
|
+
</g>
|
|
21
|
+
<g>
|
|
22
|
+
</g>
|
|
23
|
+
<g>
|
|
24
|
+
</g>
|
|
25
|
+
<g>
|
|
26
|
+
</g>
|
|
27
|
+
<g>
|
|
28
|
+
</g>
|
|
29
|
+
<g>
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
</g>
|
|
33
|
+
<g>
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
</g>
|
|
37
|
+
</svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<circle style="fill:#EF9A9A;stroke:#D32F2F;stroke-width:50;stroke-miterlimit:10;" cx="200" cy="200" r="160"/>
|
|
7
|
+
<g>
|
|
8
|
+
</g>
|
|
9
|
+
<g>
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
</g>
|
|
13
|
+
<g>
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
</g>
|
|
17
|
+
<g>
|
|
18
|
+
</g>
|
|
19
|
+
<g>
|
|
20
|
+
</g>
|
|
21
|
+
<g>
|
|
22
|
+
</g>
|
|
23
|
+
<g>
|
|
24
|
+
</g>
|
|
25
|
+
<g>
|
|
26
|
+
</g>
|
|
27
|
+
<g>
|
|
28
|
+
</g>
|
|
29
|
+
<g>
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
</g>
|
|
33
|
+
<g>
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
</g>
|
|
37
|
+
</svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<circle style="fill:#BBDEFB;stroke:#1976D2;stroke-width:50;stroke-miterlimit:10;" cx="200" cy="200" r="160"/>
|
|
7
|
+
<g>
|
|
8
|
+
</g>
|
|
9
|
+
<g>
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
</g>
|
|
13
|
+
<g>
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
</g>
|
|
17
|
+
<g>
|
|
18
|
+
</g>
|
|
19
|
+
<g>
|
|
20
|
+
</g>
|
|
21
|
+
<g>
|
|
22
|
+
</g>
|
|
23
|
+
<g>
|
|
24
|
+
</g>
|
|
25
|
+
<g>
|
|
26
|
+
</g>
|
|
27
|
+
<g>
|
|
28
|
+
</g>
|
|
29
|
+
<g>
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
</g>
|
|
33
|
+
<g>
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
</g>
|
|
37
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" enable-background="new 0 0 400 400" xml:space="preserve">
|
|
6
|
+
<circle fill="none" stroke="#B39DDB" stroke-width="20" stroke-miterlimit="10" cx="200" cy="200" r="175"/>
|
|
7
|
+
<circle fill="#512DA8" cx="200" cy="199" r="135"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" enable-background="new 0 0 400 400" xml:space="preserve">
|
|
6
|
+
<polygon fill="none" points="0,300 0,0 400,0 400,100 220,160 200,200 180,240 "/>
|
|
7
|
+
<g>
|
|
8
|
+
<defs>
|
|
9
|
+
<polygon id="SVGID_1_" points="10,10 390,10 390,92.792 212.908,151.823 172.908,231.823 10,286.126 "/>
|
|
10
|
+
</defs>
|
|
11
|
+
<clipPath id="SVGID_2_">
|
|
12
|
+
<use xlink:href="#SVGID_1_" overflow="visible"/>
|
|
13
|
+
</clipPath>
|
|
14
|
+
|
|
15
|
+
<circle clip-path="url(#SVGID_2_)" fill="none" stroke="#B39DDB" stroke-width="20" stroke-miterlimit="10" cx="200" cy="199" r="175"/>
|
|
16
|
+
<circle clip-path="url(#SVGID_2_)" fill="#512DA8" cx="200" cy="198" r="135"/>
|
|
17
|
+
</g>
|
|
18
|
+
<polygon fill="none" points="400,100 400,400 0,400 0,300 180,240 200,200 220,160 "/>
|
|
19
|
+
<g>
|
|
20
|
+
<defs>
|
|
21
|
+
<polygon id="SVGID_3_" points="10,390 10,307.208 187.092,248.177 227.092,168.177 390,113.875 390,390 "/>
|
|
22
|
+
</defs>
|
|
23
|
+
<clipPath id="SVGID_4_">
|
|
24
|
+
<use xlink:href="#SVGID_3_" overflow="visible"/>
|
|
25
|
+
</clipPath>
|
|
26
|
+
|
|
27
|
+
<circle clip-path="url(#SVGID_4_)" fill="none" stroke="#EF9A9A" stroke-width="20" stroke-miterlimit="10" cx="200" cy="200" r="175"/>
|
|
28
|
+
<circle clip-path="url(#SVGID_4_)" fill="#D32F2F" cx="200" cy="199" r="135"/>
|
|
29
|
+
</g>
|
|
30
|
+
</svg>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<g>
|
|
7
|
+
<path style="fill:#9CCC65;stroke:#33691E;stroke-width:30;stroke-miterlimit:10;" d="M124.027,115.36"/>
|
|
8
|
+
<path style="fill:#D32F2F;" d="M199.726,104.727c0,0,46.718-0.637,71.481,24.681c24.337,6.663,51.808-99.407,51.808-99.407
|
|
9
|
+
s22.973,39.45,27.481,71.86s-4.077,101.078-4.077,101.078s3.981,2.502,5.833,16.916s10.311,44.288,0,75.072
|
|
10
|
+
c-10.311,30.784-63.914,64.056-87.788,70.736c-25.225,5.779-103.701,5.779-128.926,0c-23.874-6.68-77.477-39.834-87.788-70.618
|
|
11
|
+
s-1.851-60.717,0-75.131c1.851-14.414,5.833-16.946,5.833-16.946s-8.585-68.684-4.077-101.093s27.481-71.867,27.481-71.867
|
|
12
|
+
s27.471,106.067,51.808,99.403c24.762-25.319,71.481-24.683,71.481-24.683"/>
|
|
13
|
+
<g>
|
|
14
|
+
<path style="fill:#DF9A9A;" d="M101.707,161.397c0,0,24,32.333,65,47.667c-1,11.167-31.833,28-57.667,8.333
|
|
15
|
+
S101.707,161.397,101.707,161.397z"/>
|
|
16
|
+
<path style="fill:#DF9A9A;" d="M298.293,161.397c0,0-24,32.333-65,47.667c1,11.167,31.833,28,57.667,8.333
|
|
17
|
+
C316.793,197.73,298.293,161.397,298.293,161.397z"/>
|
|
18
|
+
</g>
|
|
19
|
+
</g>
|
|
20
|
+
<g>
|
|
21
|
+
</g>
|
|
22
|
+
<g>
|
|
23
|
+
</g>
|
|
24
|
+
<g>
|
|
25
|
+
</g>
|
|
26
|
+
<g>
|
|
27
|
+
</g>
|
|
28
|
+
<g>
|
|
29
|
+
</g>
|
|
30
|
+
<g>
|
|
31
|
+
</g>
|
|
32
|
+
<g>
|
|
33
|
+
</g>
|
|
34
|
+
<g>
|
|
35
|
+
</g>
|
|
36
|
+
<g>
|
|
37
|
+
</g>
|
|
38
|
+
<g>
|
|
39
|
+
</g>
|
|
40
|
+
<g>
|
|
41
|
+
</g>
|
|
42
|
+
<g>
|
|
43
|
+
</g>
|
|
44
|
+
<g>
|
|
45
|
+
</g>
|
|
46
|
+
<g>
|
|
47
|
+
</g>
|
|
48
|
+
<g>
|
|
49
|
+
</g>
|
|
50
|
+
</svg>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<circle style="fill:#E1F5FE;stroke:#33691E;stroke-width:20;stroke-miterlimit:10;" cx="200" cy="200" r="165"/>
|
|
7
|
+
<g>
|
|
8
|
+
<path style="fill:none;" d="M-138.068,124.63c-2.204,0-4.112,0.805-5.723,2.416c-1.611,1.611-2.416,3.518-2.416,5.723
|
|
9
|
+
s0.805,4.112,2.416,5.723c1.611,1.611,3.518,2.416,5.723,2.416s4.112-0.805,5.723-2.416c1.611-1.611,2.416-3.518,2.416-5.723
|
|
10
|
+
s-0.805-4.112-2.416-5.723C-133.956,125.435-135.864,124.63-138.068,124.63z"/>
|
|
11
|
+
<path style="fill:#33691E;" d="M281.414,252.786l-2.561,3.461c-6.119,8.311-14.844,15.379-25.88,21.01
|
|
12
|
+
c-10.841,5.532-23.06,9.23-36.195,10.994l-5.778,0.76V196h29.643c0.878,0,1.62-0.269,2.296-0.945
|
|
13
|
+
c0.677-0.676,1.061-1.303,1.061-2.18v-16.278c0-0.877-0.385-1.74-1.06-2.416c-0.677-0.676-1.419-1.181-2.297-1.181H211v-28.137
|
|
14
|
+
l2.585-1.449c4.22-2.474,7.621-5.75,10.175-10.018c2.518-4.208,3.745-8.713,3.745-13.774c0-7.677-2.637-14.05-8.07-19.483
|
|
15
|
+
c-5.433-5.433-11.8-8.073-19.477-8.073c-7.677,0-14.039,2.641-19.472,8.073c-5.433,5.433-8.052,11.806-8.052,19.483
|
|
16
|
+
c0,5.061,1.268,9.566,3.786,13.775c2.552,4.267,5.923,7.543,10.143,10.017l2.637,1.449V173h-29.748
|
|
17
|
+
c-0.877,0-1.568,0.505-2.243,1.18c-0.677,0.677-1.009,1.539-1.009,2.417v16.278c0,0.877,0.332,1.504,1.009,2.18
|
|
18
|
+
s1.366,0.945,2.243,0.945H189v93.011l-5.83-0.742c-13.136-1.764-25.463-5.444-36.305-10.976
|
|
19
|
+
c-11.036-5.631-19.732-12.665-25.851-20.974l-2.547-3.317L132.677,239H91l0.002,41.467l14.159-14.337l3.504,4.194
|
|
20
|
+
c9.522,11.442,22.506,20.649,38.592,27.382c16.221,6.789,33.949,10.227,52.691,10.227c18.742,0,36.485-3.445,52.705-10.234
|
|
21
|
+
c16.086-6.732,29.085-15.949,38.607-27.392l3.527-4.212L309,280.254v-41.684l-41.739,0.002L281.414,252.786z M199.948,132.762
|
|
22
|
+
c-3.518,0-6.72-1.342-9.258-3.88c-2.539-2.539-3.881-5.741-3.881-9.259c0-3.518,1.342-6.719,3.881-9.258s5.741-3.881,9.258-3.881
|
|
23
|
+
c3.519,0,6.72,1.342,9.258,3.88c2.539,2.538,3.881,5.74,3.881,9.259s-1.342,6.721-3.88,9.258
|
|
24
|
+
C206.668,131.42,203.466,132.762,199.948,132.762z"/>
|
|
25
|
+
</g>
|
|
26
|
+
<g>
|
|
27
|
+
</g>
|
|
28
|
+
<g>
|
|
29
|
+
</g>
|
|
30
|
+
<g>
|
|
31
|
+
</g>
|
|
32
|
+
<g>
|
|
33
|
+
</g>
|
|
34
|
+
<g>
|
|
35
|
+
</g>
|
|
36
|
+
<g>
|
|
37
|
+
</g>
|
|
38
|
+
<g>
|
|
39
|
+
</g>
|
|
40
|
+
<g>
|
|
41
|
+
</g>
|
|
42
|
+
<g>
|
|
43
|
+
</g>
|
|
44
|
+
<g>
|
|
45
|
+
</g>
|
|
46
|
+
<g>
|
|
47
|
+
</g>
|
|
48
|
+
<g>
|
|
49
|
+
</g>
|
|
50
|
+
<g>
|
|
51
|
+
</g>
|
|
52
|
+
<g>
|
|
53
|
+
</g>
|
|
54
|
+
<g>
|
|
55
|
+
</g>
|
|
56
|
+
</svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
5
|
+
width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
|
|
6
|
+
<circle style="fill:#9CCC65;stroke:#33691E;stroke-width:50;stroke-miterlimit:10;" cx="200" cy="200" r="160"/>
|
|
7
|
+
<g>
|
|
8
|
+
</g>
|
|
9
|
+
<g>
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
</g>
|
|
13
|
+
<g>
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
</g>
|
|
17
|
+
<g>
|
|
18
|
+
</g>
|
|
19
|
+
<g>
|
|
20
|
+
</g>
|
|
21
|
+
<g>
|
|
22
|
+
</g>
|
|
23
|
+
<g>
|
|
24
|
+
</g>
|
|
25
|
+
<g>
|
|
26
|
+
</g>
|
|
27
|
+
<g>
|
|
28
|
+
</g>
|
|
29
|
+
<g>
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
</g>
|
|
33
|
+
<g>
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
</g>
|
|
37
|
+
</svg>
|
package/index.js
CHANGED
|
@@ -26,6 +26,8 @@ const mapAreas = [
|
|
|
26
26
|
['4', '西方'], ['5', '南方'], ['6', '中部'],
|
|
27
27
|
]
|
|
28
28
|
const LOCAL_KCNAV_ASSET_ROOT = path.join(__dirname, 'assets', 'kcnav')
|
|
29
|
+
const LOCAL_PROPHET_SPOT_ICON_ROOT = path.join(__dirname, 'assets', 'icons', 'spot')
|
|
30
|
+
const PREFERENCES_STORAGE_KEY = 'poi-plugin-compass.preferences.v1'
|
|
29
31
|
|
|
30
32
|
const MAP_PHASES = {
|
|
31
33
|
'5-6': [
|
|
@@ -67,7 +69,6 @@ const {
|
|
|
67
69
|
evaluateMap,
|
|
68
70
|
evaluatePredicate,
|
|
69
71
|
fleetContextFromState,
|
|
70
|
-
formatPercent,
|
|
71
72
|
mapNodeLabel,
|
|
72
73
|
normalizeMapId,
|
|
73
74
|
normalizeOutcomes,
|
|
@@ -114,6 +115,18 @@ const NODE_DEFAULTS = {
|
|
|
114
115
|
border: '#999999',
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
const COMBAT_NODE_TYPES = new Set([4, 5, 11, 13, 15])
|
|
119
|
+
const AIR_NODE_TYPES = new Set([7, 10])
|
|
120
|
+
const AIR_STATUS_NODE_TYPES = new Set([4, 5, 7, 10, 11, 13, 15])
|
|
121
|
+
const ICON_ONLY_NODE_TYPES = new Set([0, 5, 7, 8, 10])
|
|
122
|
+
const PROPHET_NODE_ICONS = {
|
|
123
|
+
5: '5',
|
|
124
|
+
8: '8',
|
|
125
|
+
}
|
|
126
|
+
const NODE_ICON_SIZE = 64
|
|
127
|
+
const NODE_ICON_SIZE_BY_TYPE = { 7: 56, 8: 48, 10: 56 }
|
|
128
|
+
const NODE_ICON_OFFSET_Y_BY_TYPE = { 0: -2, 5: -6 }
|
|
129
|
+
|
|
117
130
|
function nodeVisualStyle(nodeInfo = {}) {
|
|
118
131
|
const style = { ...NODE_DEFAULTS, ...nodeInfo }
|
|
119
132
|
const size = Number.isFinite(Number(style.size)) ? Number(style.size) : Number(style.simpleSize)
|
|
@@ -156,6 +169,66 @@ function getRootState() {
|
|
|
156
169
|
return store && typeof store.getState === 'function' ? store.getState() : {}
|
|
157
170
|
}
|
|
158
171
|
|
|
172
|
+
function compassStorage() {
|
|
173
|
+
try {
|
|
174
|
+
return typeof window !== 'undefined' && window.localStorage ? window.localStorage : null
|
|
175
|
+
} catch (_) {
|
|
176
|
+
return null
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function isRecord(value) {
|
|
181
|
+
return value != null && typeof value === 'object' && !Array.isArray(value)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeOverrides(value) {
|
|
185
|
+
if (!isRecord(value)) return {}
|
|
186
|
+
return Object.fromEntries(Object.entries(value).filter(([node, target]) => node && typeof target === 'string' && target))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function readCompassPreferences() {
|
|
190
|
+
const storage = compassStorage()
|
|
191
|
+
if (!storage) return {}
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(storage.getItem(PREFERENCES_STORAGE_KEY) || '{}')
|
|
194
|
+
return isRecord(parsed) ? parsed : {}
|
|
195
|
+
} catch (_) {
|
|
196
|
+
return {}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function viewPreferenceKey(mapId, phaseId, deckId) {
|
|
201
|
+
return `${mapId}|${phaseId || ''}|${deckId}`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function storedOverridesForView(preferences, mapId, phaseId, deckId) {
|
|
205
|
+
const overridesByView = isRecord(preferences?.overridesByView) ? preferences.overridesByView : {}
|
|
206
|
+
return normalizeOverrides(overridesByView[viewPreferenceKey(mapId, phaseId, deckId)])
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function normalizeStringMap(value) {
|
|
210
|
+
if (!isRecord(value)) return {}
|
|
211
|
+
return Object.fromEntries(Object.entries(value).filter(([key, entry]) => key && typeof entry === 'string' && entry))
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function normalizeNumberMap(value) {
|
|
215
|
+
if (!isRecord(value)) return {}
|
|
216
|
+
return Object.fromEntries(Object.entries(value)
|
|
217
|
+
.map(([key, entry]) => [key, Number(entry)])
|
|
218
|
+
.filter(([key, entry]) => key && Number.isFinite(entry)))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function storedExportPreferencesForView(preferences, mapId, phaseId, deckId) {
|
|
222
|
+
const exportByView = isRecord(preferences?.exportByView) ? preferences.exportByView : {}
|
|
223
|
+
const stored = exportByView[viewPreferenceKey(mapId, phaseId, deckId)]
|
|
224
|
+
if (!isRecord(stored)) return { exportRoute: null, exportEnemies: {}, exportFormations: {} }
|
|
225
|
+
return {
|
|
226
|
+
exportRoute: typeof stored.route === 'string' && stored.route ? stored.route : null,
|
|
227
|
+
exportEnemies: normalizeStringMap(stored.enemies),
|
|
228
|
+
exportFormations: normalizeNumberMap(stored.formations),
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
159
232
|
function getFleet(state, deckId = 1) {
|
|
160
233
|
const fleets = state?.info?.fleets
|
|
161
234
|
if (Array.isArray(fleets)) return fleets[deckId - 1] || null
|
|
@@ -275,6 +348,21 @@ function nodeIconUrl(icon) {
|
|
|
275
348
|
return null
|
|
276
349
|
}
|
|
277
350
|
|
|
351
|
+
function prophetSpotIconUrl(icon) {
|
|
352
|
+
if (icon == null) return null
|
|
353
|
+
return pathToFileURL(path.join(LOCAL_PROPHET_SPOT_ICON_ROOT, `${icon}.svg`)).toString()
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function nodeMarker(nodeType, nodeInfo) {
|
|
357
|
+
const prophetIcon = PROPHET_NODE_ICONS[nodeType]
|
|
358
|
+
if (prophetIcon) return { source: 'prophet', url: prophetSpotIconUrl(prophetIcon) }
|
|
359
|
+
if ([0, 7, 10].includes(nodeType)) {
|
|
360
|
+
const kcnavIcon = nodeIconUrl(nodeInfo?.icon)
|
|
361
|
+
return kcnavIcon ? { source: 'kcnav', url: kcnavIcon } : null
|
|
362
|
+
}
|
|
363
|
+
return null
|
|
364
|
+
}
|
|
365
|
+
|
|
278
366
|
function localNodeTypesForMap(mapId, mapDefinition) {
|
|
279
367
|
const nodeTypes = { ...(localKcnavNodeTypes[mapId] || {}) }
|
|
280
368
|
if (mapDefinition?.start) nodeTypes[mapDefinition.start] = 0
|
|
@@ -400,8 +488,19 @@ function routeEdges(geometry) {
|
|
|
400
488
|
.filter((edge) => edge.from && edge.to)
|
|
401
489
|
}
|
|
402
490
|
|
|
403
|
-
function
|
|
404
|
-
|
|
491
|
+
function formatProbability(value, estimated = false, mode = 'percent') {
|
|
492
|
+
if (!Number.isFinite(Number(value))) return '未知'
|
|
493
|
+
const probability = Math.max(0, Math.min(1, Number(value)))
|
|
494
|
+
const suffix = estimated ? '?' : ''
|
|
495
|
+
if (mode === 'decimal') {
|
|
496
|
+
const rounded = Math.round(probability * 100) / 100
|
|
497
|
+
return `${rounded === 1 ? '1' : rounded.toFixed(2).replace(/^0/, '')}${suffix}`
|
|
498
|
+
}
|
|
499
|
+
return `${Math.round(probability * 100)}%${suffix}`
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function outcomeText(outcome, mode = 'percent') {
|
|
503
|
+
return `${outcome.to} ${formatProbability(outcome.probability, outcome.estimated, mode)}`
|
|
405
504
|
}
|
|
406
505
|
|
|
407
506
|
// These predicates came from separate list items in the cached KCWiki route
|
|
@@ -515,12 +614,21 @@ function edgeProbability(evaluation, from, to) {
|
|
|
515
614
|
}
|
|
516
615
|
}
|
|
517
616
|
|
|
518
|
-
function
|
|
519
|
-
|
|
520
|
-
if (Number(
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
617
|
+
function edgeVisualStyle(value) {
|
|
618
|
+
const probability = Number(value)
|
|
619
|
+
if (!Number.isFinite(probability) || probability <= 0) {
|
|
620
|
+
return { color: '#87919a', width: 2.25, opacity: 0.3 }
|
|
621
|
+
}
|
|
622
|
+
const ratio = Math.max(0, Math.min(1, probability))
|
|
623
|
+
if (ratio >= 0.999999) return { color: '#78d99a', width: 7, opacity: 1 }
|
|
624
|
+
const start = [125, 135, 145]
|
|
625
|
+
const end = [120, 217, 154]
|
|
626
|
+
const color = `rgb(${start.map((component, index) => Math.round(component + (end[index] - component) * ratio)).join(', ')})`
|
|
627
|
+
return {
|
|
628
|
+
color,
|
|
629
|
+
width: 2.25 + ratio * 4.75,
|
|
630
|
+
opacity: 0.3 + ratio * 0.7,
|
|
631
|
+
}
|
|
524
632
|
}
|
|
525
633
|
|
|
526
634
|
function mapBackgroundUrl(mapId) {
|
|
@@ -546,19 +654,27 @@ function manualSourceForTarget(evaluation, target) {
|
|
|
546
654
|
class Compass extends React.Component {
|
|
547
655
|
constructor(props) {
|
|
548
656
|
super(props)
|
|
657
|
+
const preferences = readCompassPreferences()
|
|
658
|
+
const storedMapId = typeof preferences.mapId === 'string' && mapCatalog.maps[preferences.mapId]
|
|
659
|
+
? preferences.mapId : null
|
|
660
|
+
const initialMapId = storedMapId
|
|
661
|
+
|| (pluginState.currentMapId && mapCatalog.maps[pluginState.currentMapId] ? pluginState.currentMapId : mapIds[0])
|
|
662
|
+
const initialDeckId = [1, 2, 3, 4].includes(Number(preferences.deckId)) ? Number(preferences.deckId) : 1
|
|
663
|
+
const preferredPhaseId = preferences.phaseByMap?.[initialMapId] ?? preferences.phaseId
|
|
664
|
+
const initialPhaseId = phaseOptionForMap(initialMapId, preferredPhaseId)?.id || null
|
|
665
|
+
const initialExportPreferences = storedExportPreferencesForView(preferences, initialMapId, initialPhaseId, initialDeckId)
|
|
549
666
|
this.state = {
|
|
550
|
-
deckId:
|
|
551
|
-
mapId:
|
|
552
|
-
|
|
553
|
-
: mapIds[0],
|
|
554
|
-
phaseId: phaseOptionsForMap(mapIds[0])[0]?.id || null,
|
|
667
|
+
deckId: initialDeckId,
|
|
668
|
+
mapId: initialMapId,
|
|
669
|
+
phaseId: initialPhaseId,
|
|
555
670
|
selectedNode: pluginState.currentNode || null,
|
|
556
671
|
selectedEdge: null,
|
|
557
|
-
overrides:
|
|
672
|
+
overrides: storedOverridesForView(preferences, initialMapId, initialPhaseId, initialDeckId),
|
|
673
|
+
probabilityFormat: preferences.probabilityFormat === 'decimal' ? 'decimal' : 'percent',
|
|
674
|
+
showEstimatedAir: typeof preferences.showEstimatedAir === 'boolean' ? preferences.showEstimatedAir : true,
|
|
675
|
+
showEnemyAvatars: typeof preferences.showEnemyAvatars === 'boolean' ? preferences.showEnemyAvatars : true,
|
|
558
676
|
exportOpen: false,
|
|
559
|
-
|
|
560
|
-
exportEnemies: {},
|
|
561
|
-
exportFormations: {},
|
|
677
|
+
...initialExportPreferences,
|
|
562
678
|
exportError: null,
|
|
563
679
|
exportBusy: false,
|
|
564
680
|
}
|
|
@@ -582,6 +698,42 @@ class Compass extends React.Component {
|
|
|
582
698
|
this.refresh()
|
|
583
699
|
}
|
|
584
700
|
|
|
701
|
+
componentDidUpdate() {
|
|
702
|
+
this.persistPreferences()
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
persistPreferences() {
|
|
706
|
+
const storage = compassStorage()
|
|
707
|
+
if (!storage) return
|
|
708
|
+
try {
|
|
709
|
+
const previous = readCompassPreferences()
|
|
710
|
+
const overridesByView = isRecord(previous.overridesByView) ? { ...previous.overridesByView } : {}
|
|
711
|
+
overridesByView[viewPreferenceKey(this.state.mapId, this.state.phaseId, this.state.deckId)] = normalizeOverrides(this.state.overrides)
|
|
712
|
+
const exportByView = isRecord(previous.exportByView) ? { ...previous.exportByView } : {}
|
|
713
|
+
exportByView[viewPreferenceKey(this.state.mapId, this.state.phaseId, this.state.deckId)] = {
|
|
714
|
+
route: typeof this.state.exportRoute === 'string' ? this.state.exportRoute : null,
|
|
715
|
+
enemies: normalizeStringMap(this.state.exportEnemies),
|
|
716
|
+
formations: normalizeNumberMap(this.state.exportFormations),
|
|
717
|
+
}
|
|
718
|
+
const phaseByMap = isRecord(previous.phaseByMap) ? { ...previous.phaseByMap } : {}
|
|
719
|
+
phaseByMap[this.state.mapId] = this.state.phaseId
|
|
720
|
+
storage.setItem(PREFERENCES_STORAGE_KEY, JSON.stringify({
|
|
721
|
+
...previous,
|
|
722
|
+
mapId: this.state.mapId,
|
|
723
|
+
phaseId: this.state.phaseId,
|
|
724
|
+
phaseByMap,
|
|
725
|
+
deckId: this.state.deckId,
|
|
726
|
+
probabilityFormat: this.state.probabilityFormat,
|
|
727
|
+
showEstimatedAir: this.state.showEstimatedAir,
|
|
728
|
+
showEnemyAvatars: this.state.showEnemyAvatars,
|
|
729
|
+
overridesByView,
|
|
730
|
+
exportByView,
|
|
731
|
+
}))
|
|
732
|
+
} catch (_) {
|
|
733
|
+
// Preferences are optional; a restricted localStorage must not break the plugin.
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
585
737
|
componentWillUnmount() {
|
|
586
738
|
listeners.delete(this.refresh)
|
|
587
739
|
this.unsubscribeStore?.()
|
|
@@ -589,15 +741,16 @@ class Compass extends React.Component {
|
|
|
589
741
|
}
|
|
590
742
|
|
|
591
743
|
selectMap(mapId) {
|
|
744
|
+
const preferences = readCompassPreferences()
|
|
745
|
+
const phaseId = phaseOptionForMap(mapId, preferences.phaseByMap?.[mapId])?.id || null
|
|
746
|
+
const exportPreferences = storedExportPreferencesForView(preferences, mapId, phaseId, this.state.deckId)
|
|
592
747
|
this.setState({
|
|
593
748
|
mapId,
|
|
594
|
-
phaseId
|
|
749
|
+
phaseId,
|
|
595
750
|
selectedNode: null,
|
|
596
751
|
selectedEdge: null,
|
|
597
|
-
overrides:
|
|
598
|
-
|
|
599
|
-
exportEnemies: {},
|
|
600
|
-
exportFormations: {},
|
|
752
|
+
overrides: storedOverridesForView(preferences, mapId, phaseId, this.state.deckId),
|
|
753
|
+
...exportPreferences,
|
|
601
754
|
exportError: null,
|
|
602
755
|
})
|
|
603
756
|
}
|
|
@@ -641,7 +794,10 @@ class Compass extends React.Component {
|
|
|
641
794
|
}
|
|
642
795
|
|
|
643
796
|
selectPhase(phaseId) {
|
|
644
|
-
|
|
797
|
+
const preferences = readCompassPreferences()
|
|
798
|
+
const overrides = storedOverridesForView(preferences, this.state.mapId, phaseId, this.state.deckId)
|
|
799
|
+
const exportPreferences = storedExportPreferencesForView(preferences, this.state.mapId, phaseId, this.state.deckId)
|
|
800
|
+
this.setState({ phaseId, selectedNode: null, selectedEdge: null, overrides, ...exportPreferences, exportError: null })
|
|
645
801
|
}
|
|
646
802
|
|
|
647
803
|
selectNode(node) {
|
|
@@ -649,7 +805,10 @@ class Compass extends React.Component {
|
|
|
649
805
|
}
|
|
650
806
|
|
|
651
807
|
selectFleet(deckId) {
|
|
652
|
-
|
|
808
|
+
const preferences = readCompassPreferences()
|
|
809
|
+
const overrides = storedOverridesForView(preferences, this.state.mapId, this.state.phaseId, deckId)
|
|
810
|
+
const exportPreferences = storedExportPreferencesForView(preferences, this.state.mapId, this.state.phaseId, deckId)
|
|
811
|
+
this.setState({ deckId, selectedNode: null, selectedEdge: null, overrides, ...exportPreferences, exportError: null })
|
|
653
812
|
}
|
|
654
813
|
|
|
655
814
|
renderLosIcons(scores) {
|
|
@@ -684,6 +843,35 @@ class Compass extends React.Component {
|
|
|
684
843
|
this.setState({ selectedEdge: edgeId })
|
|
685
844
|
}
|
|
686
845
|
|
|
846
|
+
renderNodeEnemyAvatar(ship, rootState, x, y, size) {
|
|
847
|
+
if (!ship?.id) return null
|
|
848
|
+
const name = ship.name || '敌舰'
|
|
849
|
+
return h('foreignObject', {
|
|
850
|
+
className: 'compass-node-overlay compass-node-enemy-avatar',
|
|
851
|
+
x,
|
|
852
|
+
y,
|
|
853
|
+
width: size,
|
|
854
|
+
height: size,
|
|
855
|
+
'aria-label': `最常见敌方旗舰:${name}`,
|
|
856
|
+
}, h('div', { className: 'compass-node-overlay-content' }, this.renderShipPortrait(ship.id, name, rootState, size)))
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
renderNodeIcon(marker, centerX, centerY, nodeType) {
|
|
860
|
+
if (!marker?.url) return null
|
|
861
|
+
const size = NODE_ICON_SIZE_BY_TYPE[nodeType] || NODE_ICON_SIZE
|
|
862
|
+
const offsetY = NODE_ICON_OFFSET_Y_BY_TYPE[nodeType] || 0
|
|
863
|
+
return h('image', {
|
|
864
|
+
className: `compass-node-icon is-${marker.source}`,
|
|
865
|
+
href: marker.url,
|
|
866
|
+
x: centerX - size / 2,
|
|
867
|
+
y: centerY + offsetY - size / 2,
|
|
868
|
+
width: size,
|
|
869
|
+
height: size,
|
|
870
|
+
preserveAspectRatio: 'xMidYMid meet',
|
|
871
|
+
'aria-hidden': 'true',
|
|
872
|
+
})
|
|
873
|
+
}
|
|
874
|
+
|
|
687
875
|
renderMap(mapDefinition, geometry, evaluation, selectedNode, currentNode, passedNodes = [], mapId = null) {
|
|
688
876
|
if (!mapGeometryAvailable(mapDefinition, geometry)) {
|
|
689
877
|
return h('div', { className: 'compass-empty-panel' }, 'poi 地图数据不可用')
|
|
@@ -693,7 +881,8 @@ class Compass extends React.Component {
|
|
|
693
881
|
const edges = routeEdges(geometry)
|
|
694
882
|
const passedEdges = passedEdgeKeys(passedNodes)
|
|
695
883
|
const nodeTypes = localNodeTypesForMap(mapId, mapDefinition)
|
|
696
|
-
const
|
|
884
|
+
const rootState = getRootState()
|
|
885
|
+
const airPower = calculatePoiFleetStat(rootState, this.state.deckId, 1, true)
|
|
697
886
|
const width = Math.max(1200, ...Object.values(positions).map((position) => position.x + 100))
|
|
698
887
|
const height = Math.max(720, ...Object.values(positions).map((position) => position.y + 100))
|
|
699
888
|
return h(
|
|
@@ -717,6 +906,7 @@ class Compass extends React.Component {
|
|
|
717
906
|
const to = positions[edge.to]
|
|
718
907
|
if (!from || !to) return null
|
|
719
908
|
const probability = edgeProbability(evaluation, edge.from, edge.to)
|
|
909
|
+
const edgeVisual = edgeVisualStyle(probability.global)
|
|
720
910
|
const manualOutcomes = evaluation.decisions[edge.from]?.manualOutcomes
|
|
721
911
|
|| evaluation.decisions[edge.from]?.baseOutcomes
|
|
722
912
|
|| []
|
|
@@ -727,7 +917,6 @@ class Compass extends React.Component {
|
|
|
727
917
|
'compass-edge',
|
|
728
918
|
probability.global != null ? 'is-reachable' : '',
|
|
729
919
|
probability.estimated ? 'is-estimated' : '',
|
|
730
|
-
probabilityTier(probability.global),
|
|
731
920
|
passedEdges.has(`${edge.from}->${edge.to}`) ? 'is-passed' : '',
|
|
732
921
|
manualChoice ? 'is-manual-choice' : '',
|
|
733
922
|
selected ? 'is-selected' : '',
|
|
@@ -745,6 +934,11 @@ class Compass extends React.Component {
|
|
|
745
934
|
role: 'button',
|
|
746
935
|
tabIndex: 0,
|
|
747
936
|
'aria-label': `边 ID ${edge.cell}:${edge.from}→${edge.to}`,
|
|
937
|
+
style: {
|
|
938
|
+
'--compass-edge-color': edgeVisual.color,
|
|
939
|
+
'--compass-edge-width': edgeVisual.width,
|
|
940
|
+
'--compass-edge-opacity': edgeVisual.opacity,
|
|
941
|
+
},
|
|
748
942
|
onClick: () => manualChoice
|
|
749
943
|
? this.setManualOverride(edge.from, edge.to, edge.cell)
|
|
750
944
|
: this.selectEdge(edge.cell),
|
|
@@ -754,7 +948,7 @@ class Compass extends React.Component {
|
|
|
754
948
|
`边 ID ${edge.cell}:${edge.from} → ${edge.to}`,
|
|
755
949
|
manualChoice ? '点击选择方向' : null,
|
|
756
950
|
probability.global != null && Number(probability.global) > 0
|
|
757
|
-
? `局部 ${
|
|
951
|
+
? `局部 ${formatProbability(probability.local, probability.estimated, this.state.probabilityFormat)}`
|
|
758
952
|
: null,
|
|
759
953
|
].filter(Boolean).join(' · ')),
|
|
760
954
|
h('line', {
|
|
@@ -763,13 +957,13 @@ class Compass extends React.Component {
|
|
|
763
957
|
x2: to.x,
|
|
764
958
|
y2: to.y,
|
|
765
959
|
}),
|
|
766
|
-
probability.global != null && Number(probability.global) > 0
|
|
960
|
+
probability.global != null && Number(probability.global) > 0 && Math.round(Number(probability.global) * 100) > 0
|
|
767
961
|
? h('text', {
|
|
768
962
|
x: (from.x + to.x) / 2,
|
|
769
963
|
y: (from.y + to.y) / 2,
|
|
770
964
|
dominantBaseline: 'middle',
|
|
771
965
|
className: 'compass-edge-label',
|
|
772
|
-
},
|
|
966
|
+
}, formatProbability(probability.global, probability.estimated, this.state.probabilityFormat))
|
|
773
967
|
: null,
|
|
774
968
|
selected
|
|
775
969
|
? h('text', {
|
|
@@ -787,10 +981,20 @@ class Compass extends React.Component {
|
|
|
787
981
|
? kcnavNodeCatalog['-1']
|
|
788
982
|
: kcnavNodeCatalog[String(nodeType)] || kcnavNodeCatalog['-1']
|
|
789
983
|
const visual = nodeVisualStyle(nodeInfo)
|
|
790
|
-
const
|
|
791
|
-
|
|
984
|
+
const marker = nodeMarker(nodeType, nodeInfo)
|
|
985
|
+
const iconOnly = ICON_ONLY_NODE_TYPES.has(nodeType)
|
|
986
|
+
const enemyData = (COMBAT_NODE_TYPES.has(nodeType) || AIR_NODE_TYPES.has(nodeType) || AIR_STATUS_NODE_TYPES.has(nodeType))
|
|
987
|
+
? loadEnemies(mapId, node) : null
|
|
988
|
+
const commonFlagship = this.state.showEnemyAvatars
|
|
989
|
+
&& (COMBAT_NODE_TYPES.has(nodeType) || AIR_NODE_TYPES.has(nodeType))
|
|
990
|
+
&& enemyData?.status === 'ready'
|
|
991
|
+
? enemyData.rows?.[0]?.mainFleet?.[0] : null
|
|
992
|
+
const airState = evaluation.probability.reach[node] > 0 && AIR_STATUS_NODE_TYPES.has(nodeType)
|
|
993
|
+
? airStateRange(airPower, enemyData) : null
|
|
994
|
+
const displayAirState = this.state.showEstimatedAir ? airState : null
|
|
792
995
|
const centerX = position.x + visual.offsetX
|
|
793
996
|
const centerY = position.y + visual.offsetY
|
|
997
|
+
const overlaySize = Math.min(20, Math.max(12, Math.round(Math.min(visual.rx, visual.ry))))
|
|
794
998
|
const manualSource = manualSourceForTarget(evaluation, node)
|
|
795
999
|
const manualTarget = manualSource && manualSource !== node
|
|
796
1000
|
const classNames = [
|
|
@@ -816,7 +1020,7 @@ class Compass extends React.Component {
|
|
|
816
1020
|
onKeyDown: activateNode,
|
|
817
1021
|
},
|
|
818
1022
|
h('title', null, nodeInfo?.label ? `${node} · ${nodeInfo.label}` : node),
|
|
819
|
-
h('ellipse', {
|
|
1023
|
+
iconOnly ? null : h('ellipse', {
|
|
820
1024
|
className: 'compass-node-shape',
|
|
821
1025
|
cx: centerX,
|
|
822
1026
|
cy: centerY,
|
|
@@ -827,7 +1031,7 @@ class Compass extends React.Component {
|
|
|
827
1031
|
strokeWidth: visual.stroke,
|
|
828
1032
|
opacity: visual.opacity,
|
|
829
1033
|
}),
|
|
830
|
-
visual.simpleBorder
|
|
1034
|
+
!iconOnly && visual.simpleBorder
|
|
831
1035
|
? h('ellipse', {
|
|
832
1036
|
className: 'compass-node-simple-border',
|
|
833
1037
|
cx: centerX,
|
|
@@ -839,9 +1043,10 @@ class Compass extends React.Component {
|
|
|
839
1043
|
opacity: visual.opacity,
|
|
840
1044
|
})
|
|
841
1045
|
: null,
|
|
1046
|
+
this.renderNodeIcon(marker, centerX, centerY, nodeType),
|
|
842
1047
|
h('text', {
|
|
843
1048
|
x: centerX,
|
|
844
|
-
y: centerY + visual.textOffsetY,
|
|
1049
|
+
y: centerY + (iconOnly ? 0 : visual.textOffsetY),
|
|
845
1050
|
dominantBaseline: 'middle',
|
|
846
1051
|
className: 'compass-node-label',
|
|
847
1052
|
style: {
|
|
@@ -851,11 +1056,32 @@ class Compass extends React.Component {
|
|
|
851
1056
|
fontSize: visual.fontSize,
|
|
852
1057
|
},
|
|
853
1058
|
}, node),
|
|
854
|
-
|
|
1059
|
+
commonFlagship ? this.renderNodeEnemyAvatar(
|
|
1060
|
+
commonFlagship,
|
|
1061
|
+
rootState,
|
|
1062
|
+
centerX + visual.rx * 0.7 - overlaySize / 2,
|
|
1063
|
+
centerY + visual.ry * 0.7 - overlaySize / 2,
|
|
1064
|
+
overlaySize,
|
|
1065
|
+
) : null,
|
|
1066
|
+
displayAirState ? h('text', {
|
|
1067
|
+
x: centerX + visual.rx * 0.85,
|
|
1068
|
+
y: centerY - visual.ry * 0.85,
|
|
1069
|
+
className: 'compass-node-air-state',
|
|
1070
|
+
textAnchor: 'middle',
|
|
1071
|
+
dominantBaseline: 'middle',
|
|
1072
|
+
},
|
|
855
1073
|
h('title', null, `当前舰队制空 ${airPower.min}~${airPower.max};未计沿途损耗、陆航和支援${airState.uncertain ? ';敌编成数据不完整或不确定' : ''}`),
|
|
856
|
-
...
|
|
1074
|
+
...displayAirState.states.flatMap((state, index) => [
|
|
857
1075
|
index ? h('tspan', { key: 'separator', fill: '#ffffff' }, '/') : null,
|
|
858
|
-
h('tspan', {
|
|
1076
|
+
h('tspan', {
|
|
1077
|
+
key: `${state.label}-${index}`,
|
|
1078
|
+
fill: '#ffffff',
|
|
1079
|
+
style: {
|
|
1080
|
+
fill: '#ffffff',
|
|
1081
|
+
textShadow: `1px 1px 0 ${state.shadow}`,
|
|
1082
|
+
filter: `drop-shadow(1px 1px 0 ${state.shadow})`,
|
|
1083
|
+
},
|
|
1084
|
+
}, state.label),
|
|
859
1085
|
]), airState.uncertain ? '?' : null) : null,
|
|
860
1086
|
)
|
|
861
1087
|
}),
|
|
@@ -909,7 +1135,7 @@ class Compass extends React.Component {
|
|
|
909
1135
|
route ? h('label', { className: 'compass-export-route' }, '路线',
|
|
910
1136
|
h('select', { value: route.key, 'aria-label': '导出路线', onChange: (event) => this.setState({ exportRoute: event.target.value, exportError: null }) },
|
|
911
1137
|
...choices.routes.map((candidate) => h('option', { key: candidate.key, value: candidate.key },
|
|
912
|
-
`${candidate.nodes.join('→')}${candidate.boss ? ' · Boss' : ''} · ${candidate.probability == null ? '含能动选路' :
|
|
1138
|
+
`${candidate.nodes.join('→')}${candidate.boss ? ' · Boss' : ''} · ${candidate.probability == null ? '含能动选路' : formatProbability(candidate.probability, candidate.estimated, this.state.probabilityFormat)}`))),
|
|
913
1139
|
) : null,
|
|
914
1140
|
choices?.blocked.length ? h('div', { className: 'compass-warnings' }, `未知分歧:${choices.blocked.join('、')}`) : null,
|
|
915
1141
|
battles?.length ? h('div', { className: 'compass-export-battles' },
|
|
@@ -1024,7 +1250,9 @@ class Compass extends React.Component {
|
|
|
1024
1250
|
const displayedOutcomes = active && (decision?.matchedRuleIds?.length || 0) <= 1
|
|
1025
1251
|
? decision.outcomes
|
|
1026
1252
|
: ruleOutcomes
|
|
1027
|
-
const outcomes = displayedOutcomes.length
|
|
1253
|
+
const outcomes = displayedOutcomes.length
|
|
1254
|
+
? displayedOutcomes.map((outcome) => outcomeText(outcome, this.state.probabilityFormat)).join(' / ')
|
|
1255
|
+
: '—'
|
|
1028
1256
|
const rawLabel = predicateLabel(rule.predicate)
|
|
1029
1257
|
const label = visibleRuleLabel(rule)
|
|
1030
1258
|
const notes = rulePopoverNotes(rule, rawLabel, label)
|
|
@@ -1114,6 +1342,35 @@ class Compass extends React.Component {
|
|
|
1114
1342
|
}, option.label)),
|
|
1115
1343
|
)
|
|
1116
1344
|
: null,
|
|
1345
|
+
h('div', { className: 'compass-option-group', role: 'group', 'aria-label': '概率标签格式' },
|
|
1346
|
+
h('span', { className: 'compass-option-label' }, '概率'),
|
|
1347
|
+
...[
|
|
1348
|
+
['percent', '%'],
|
|
1349
|
+
['decimal', '小数'],
|
|
1350
|
+
].map(([value, label]) => h('button', {
|
|
1351
|
+
key: value,
|
|
1352
|
+
type: 'button',
|
|
1353
|
+
className: this.state.probabilityFormat === value ? 'is-active' : '',
|
|
1354
|
+
'aria-pressed': this.state.probabilityFormat === value,
|
|
1355
|
+
onClick: () => this.setState({ probabilityFormat: value }),
|
|
1356
|
+
}, label)),
|
|
1357
|
+
),
|
|
1358
|
+
h('label', { className: 'compass-option-toggle' },
|
|
1359
|
+
h('input', {
|
|
1360
|
+
type: 'checkbox',
|
|
1361
|
+
checked: this.state.showEstimatedAir,
|
|
1362
|
+
onChange: (event) => this.setState({ showEstimatedAir: event.target.checked }),
|
|
1363
|
+
}),
|
|
1364
|
+
h('span', null, '推测制空'),
|
|
1365
|
+
),
|
|
1366
|
+
h('label', { className: 'compass-option-toggle' },
|
|
1367
|
+
h('input', {
|
|
1368
|
+
type: 'checkbox',
|
|
1369
|
+
checked: this.state.showEnemyAvatars,
|
|
1370
|
+
onChange: (event) => this.setState({ showEnemyAvatars: event.target.checked }),
|
|
1371
|
+
}),
|
|
1372
|
+
h('span', null, '敌舰头像'),
|
|
1373
|
+
),
|
|
1117
1374
|
h('span', {
|
|
1118
1375
|
className: 'compass-toolbar-fleet',
|
|
1119
1376
|
title: `舰队 ${this.state.deckId}:${fleetSummary(context)};${mapLosText}`,
|
|
@@ -1152,6 +1409,8 @@ exports.pluginWillUnload = stopPlugin
|
|
|
1152
1409
|
exports.__test = {
|
|
1153
1410
|
calculatePoiLos33,
|
|
1154
1411
|
cellFromDetail,
|
|
1412
|
+
edgeVisualStyle,
|
|
1413
|
+
formatProbability,
|
|
1155
1414
|
handleGameResponse,
|
|
1156
1415
|
kcnavNodeTypesFromMap,
|
|
1157
1416
|
localNodeTypesForMap,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poi-plugin-compass",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "通常海域舰队罗盘",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"icon": "fa/compass",
|
|
53
53
|
"priority": 20,
|
|
54
54
|
"apiVer": {
|
|
55
|
-
"11.99.99": "0.1.
|
|
55
|
+
"11.99.99": "0.1.4"
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|