poi-plugin-lbas-bis 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.
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ const React = require('react');
4
+
5
+ const h = React.createElement;
6
+
7
+ function OptimizerPanel(props) {
8
+ const {
9
+ candidateMode,
10
+ equipmentCount,
11
+ theoreticalCount,
12
+ messages,
13
+ results,
14
+ onCandidateModeChange,
15
+ onOptimize,
16
+ onImportPlan,
17
+ t,
18
+ styles,
19
+ } = props;
20
+
21
+ return h(
22
+ 'section',
23
+ { style: styles.optimizerPanel },
24
+ h('h3', { style: styles.sectionTitle }, t('optimizerTitle')),
25
+ h(
26
+ 'div',
27
+ { style: styles.optimizerControls },
28
+ h(
29
+ 'label',
30
+ { style: styles.radioLabel },
31
+ h('input', {
32
+ type: 'radio',
33
+ name: 'candidateMode',
34
+ value: 'owned',
35
+ checked: candidateMode !== 'theoretical',
36
+ onChange: () => onCandidateModeChange('owned'),
37
+ }),
38
+ t('ownedOnly'),
39
+ ),
40
+ h(
41
+ 'label',
42
+ { style: styles.radioLabel },
43
+ h('input', {
44
+ type: 'radio',
45
+ name: 'candidateMode',
46
+ value: 'theoretical',
47
+ checked: candidateMode === 'theoretical',
48
+ onChange: () => onCandidateModeChange('theoretical'),
49
+ }),
50
+ t('includeMissing'),
51
+ ),
52
+ h('button', { type: 'button', onClick: onOptimize, style: styles.primaryButton }, t('optimize')),
53
+ h('span', { style: styles.meta }, `${t('availablePlanes')}: ${equipmentCount} / ${t('candidatePlanes')}: ${theoreticalCount}`),
54
+ ),
55
+ renderMessages(messages, styles),
56
+ renderResults({ results, onImportPlan, t, styles }),
57
+ );
58
+ }
59
+
60
+ function renderMessages(messages, styles) {
61
+ if (!messages.length) {
62
+ return null;
63
+ }
64
+ return h(
65
+ 'ul',
66
+ { style: styles.messages },
67
+ messages.map((message) => h('li', { key: message }, message)),
68
+ );
69
+ }
70
+
71
+ function renderResults({ results, onImportPlan, t, styles }) {
72
+ if (!results.length) {
73
+ return h(
74
+ 'table',
75
+ { style: styles.table },
76
+ h(
77
+ 'thead',
78
+ null,
79
+ h(
80
+ 'tr',
81
+ null,
82
+ h('th', { style: styles.th }, t('plan')),
83
+ h('th', { style: styles.th }, t('sixWaveState')),
84
+ h('th', { style: styles.th }, t('damagePower')),
85
+ h('th', { style: styles.th }, t('missingEquipment')),
86
+ h('th', { style: styles.th }, t('importToSimulator')),
87
+ ),
88
+ ),
89
+ h('tbody', null, h('tr', null, h('td', { colSpan: 5, style: styles.emptyCell }, t('noResult')))),
90
+ );
91
+ }
92
+
93
+ return h(
94
+ 'div',
95
+ { style: styles.results },
96
+ results.map((plan, planIndex) =>
97
+ h(
98
+ 'section',
99
+ { key: `plan-${planIndex}`, style: styles.plan },
100
+ h(
101
+ 'div',
102
+ { style: styles.planHeader },
103
+ h('strong', null, `${t('plan')} ${planIndex + 1}`),
104
+ h('span', null, `${t('damagePower')} ${plan.totalDamagePower}`),
105
+ h('span', null, `${t('worstMargin')} ${plan.worstMargin}`),
106
+ h('button', { type: 'button', onClick: () => onImportPlan(plan), style: styles.button }, t('importToSimulator')),
107
+ ),
108
+ h('div', { style: styles.planSummary }, formatMissing(plan.missingEquipment, t)),
109
+ h(
110
+ 'div',
111
+ { style: styles.waves },
112
+ plan.waves.map((wave) =>
113
+ h(
114
+ 'span',
115
+ { key: wave.waveIndex, style: styles.wave },
116
+ `${format(t('wave'), { index: wave.waveIndex + 1 })}: ${t(wave.state.key)} / ${t('targetState')} ${t(wave.targetState)} / ${t('airPower')} ${wave.airPower}`,
117
+ ),
118
+ ),
119
+ ),
120
+ ...plan.bases.map((base, baseIndex) =>
121
+ h(
122
+ 'ol',
123
+ { key: `base-${baseIndex}`, style: styles.loadout },
124
+ base.loadout.map((item) =>
125
+ h(
126
+ 'li',
127
+ { key: item.instanceId, style: item.available === false ? styles.missingItem : null },
128
+ `${item.name} #${item.instanceId} · ${t('airPower')} ${item.antiAir} · ${t('radius')} ${item.radius}`,
129
+ ),
130
+ ),
131
+ ),
132
+ ),
133
+ ),
134
+ ),
135
+ );
136
+ }
137
+
138
+ function formatMissing(missingEquipment, t) {
139
+ if (!missingEquipment || !missingEquipment.length) {
140
+ return `${t('missingEquipment')}: -`;
141
+ }
142
+ return `${t('missingEquipment')}: ${missingEquipment.map((item) => `${item.name} x${item.count}`).join(', ')}`;
143
+ }
144
+
145
+ function format(template, values) {
146
+ return String(template).replace(/\{\{(\w+)\}\}/g, (_, key) => values[key] ?? '');
147
+ }
148
+
149
+ module.exports = OptimizerPanel;
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ const React = require('react');
4
+ const BaseTable = require('./BaseTable');
5
+ const EnemyPanel = require('./EnemyPanel');
6
+ const WaveStatusTable = require('./WaveStatusTable');
7
+
8
+ const h = React.createElement;
9
+
10
+ function SimulatorPanel(props) {
11
+ const {
12
+ simulator,
13
+ summary,
14
+ equipment,
15
+ onBaseCountChange,
16
+ onTargetRadiusChange,
17
+ onEnemyAirChange,
18
+ onSlotPlaneChange,
19
+ onSlotLockChange,
20
+ onWaveTargetChange,
21
+ onClear,
22
+ t,
23
+ styles,
24
+ } = props;
25
+
26
+ return h(
27
+ 'section',
28
+ { style: styles.simulatorPanel },
29
+ h('h2', { style: styles.title }, t('simulatorTitle')),
30
+ h(
31
+ 'div',
32
+ { style: styles.simulatorGrid },
33
+ h(
34
+ 'div',
35
+ null,
36
+ h(
37
+ 'div',
38
+ { style: styles.toolbar },
39
+ h(
40
+ 'label',
41
+ { style: styles.fieldInline },
42
+ h('span', null, t('targetRadius')),
43
+ h('input', {
44
+ type: 'number',
45
+ min: 1,
46
+ value: simulator.targetRadius,
47
+ onChange: (event) => onTargetRadiusChange(Number(event.target.value)),
48
+ style: styles.numberInput,
49
+ }),
50
+ ),
51
+ h(
52
+ 'label',
53
+ { style: styles.fieldInline },
54
+ h('span', null, t('baseCount')),
55
+ h(
56
+ 'select',
57
+ {
58
+ value: simulator.baseCount,
59
+ onChange: (event) => onBaseCountChange(Number(event.target.value)),
60
+ style: styles.smallSelect,
61
+ },
62
+ [1, 2, 3].map((count) => h('option', { key: count, value: count }, count)),
63
+ ),
64
+ ),
65
+ h('span', { style: styles.meta }, `${t('displayWaves')}: ${simulator.baseCount * 2}`),
66
+ h('button', { type: 'button', onClick: onClear, style: styles.button }, t('clearComposition')),
67
+ ),
68
+ h(BaseTable, {
69
+ bases: simulator.bases,
70
+ equipment,
71
+ summaries: summary.bases,
72
+ onSlotPlaneChange,
73
+ onSlotLockChange,
74
+ t,
75
+ styles,
76
+ }),
77
+ h(WaveStatusTable, {
78
+ waves: summary.waves,
79
+ onWaveTargetChange,
80
+ t,
81
+ styles,
82
+ }),
83
+ ),
84
+ h(EnemyPanel, {
85
+ enemy: simulator.enemy,
86
+ lines: summary.enemyAirLines,
87
+ onEnemyAirChange,
88
+ t,
89
+ styles,
90
+ }),
91
+ ),
92
+ );
93
+ }
94
+
95
+ module.exports = SimulatorPanel;
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const React = require('react');
4
+
5
+ const h = React.createElement;
6
+ const STATE_OPTIONS = ['denial', 'parity', 'superiority', 'supremacy'];
7
+
8
+ function WaveStatusTable(props) {
9
+ const { waves, onWaveTargetChange, t, styles } = props;
10
+ return h(
11
+ 'div',
12
+ { style: styles.wavePanel },
13
+ h(
14
+ 'div',
15
+ { style: styles.waveList },
16
+ waves.map((wave) =>
17
+ h(
18
+ 'label',
19
+ { key: `wave-${wave.waveIndex}`, style: styles.waveField },
20
+ h('span', null, format(t('wave'), { index: wave.waveIndex + 1 })),
21
+ h(
22
+ 'select',
23
+ {
24
+ value: wave.targetState,
25
+ onChange: (event) => onWaveTargetChange(wave.waveIndex, event.target.value),
26
+ style: styles.smallSelect,
27
+ },
28
+ STATE_OPTIONS.map((option) => h('option', { key: option, value: option }, t(option))),
29
+ ),
30
+ h('strong', { style: wave.fulfilled ? styles.goodState : styles.badState }, t(wave.state.key)),
31
+ ),
32
+ ),
33
+ ),
34
+ );
35
+ }
36
+
37
+ function format(template, values) {
38
+ return String(template).replace(/\{\{(\w+)\}\}/g, (_, key) => values[key] ?? '');
39
+ }
40
+
41
+ module.exports = WaveStatusTable;