poi-plugin-gimmick-tracker 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +66 -86
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -79,25 +79,36 @@ var AIR_ALIAS = {
79
79
  };
80
80
  var AIR_STATE_TO_TEXT = { 1: "\u7A7A\u786E", 2: "\u7A7A\u4F18", 3: "\u7A7A\u5747", 4: "\u7A7A\u52A3" };
81
81
  var CHINESE_DIGIT = { \u4E00: 1, \u4E24: 2, \u4E8C: 2, \u4E09: 3, \u56DB: 4, \u4E94: 5, \u516D: 6, \u4E03: 7, \u516B: 8, \u4E5D: 9, \u5341: 10 };
82
- var NODE = "([A-Za-z]{1,2}\\d{0,2})";
83
- var DIAN = "[\u70B9\u70B9\u9EDE]?";
84
- var CNT = "(\\d+|[\u4E00\u4E24\u4E8C\u4E09\u56DB\u4E94\u516D\u4E03\u516B\u4E5D\u5341])";
85
- var CNT_UNIT = "[\u6B21\u56DE\u51F8]?";
86
- var RANKC = "(SS|S|A|B|C|D|E)";
87
- var AIR = "(\u7A7A\u786E|\u7A7A\u512A|\u7A7A\u4F18|\u7A7A\u5747|\u7A7A\u52A3|\u5236\u7A7A\u786E\u4FDD|\u5236\u7A7A\u78BA\u4FDD|\u5236\u7A7A\u6743\u786E\u4FDD|\u5236\u7A7A\u6B0A\u78BA\u4FDD|\u822A\u7A7A\u512A\u52E2|\u822A\u7A7A\u4F18\u52BF)";
88
82
  var WIN = "[\u80DC\u52DD]?";
89
83
  var KILL = "[\u51FB\u64CA]\u7834";
90
84
  var REACH = "\u5230[\u8FBE\u9054]";
91
- var FORMS = [
92
- { kind: "base-air-defense", re: new RegExp(`(\u5B88\u5BB6|\u57FA\u5730\u9632\u7A7A\u6218|\u57FA\u5730\u9632\u7A7A|\u5BB6\u91CC|\u9632\u7A7A|\u57FA\u5730)${AIR}${CNT}${CNT_UNIT}`, "i") },
93
- { kind: "reach-after", re: new RegExp(`${REACH}${NODE}${DIAN}${CNT}${CNT_UNIT}`, "i") },
94
- { kind: "node-arrival", re: new RegExp(`${NODE}${DIAN}${REACH}${CNT}${CNT_UNIT}`, "i") },
95
- { kind: "kill", re: new RegExp(`${NODE}${DIAN}${KILL}${CNT}${CNT_UNIT}`, "i") },
96
- { kind: "victory-rank", re: new RegExp(`${NODE}${DIAN}${RANKC}${WIN}${CNT}${CNT_UNIT}`, "i") },
97
- { kind: "count-first", re: new RegExp(`${NODE}${DIAN}${CNT}${CNT_UNIT}${RANKC}${WIN}`, "i") },
98
- { kind: "node-air-state", re: new RegExp(`${NODE}${DIAN}${AIR}${CNT}${CNT_UNIT}`, "i") }
85
+ var RANKC = "(?<rank>SS|S|A|B|C|D|E)";
86
+ var AIR = "(?<air>\u7A7A\u786E|\u7A7A\u512A|\u7A7A\u4F18|\u7A7A\u5747|\u7A7A\u52A3|\u5236\u7A7A\u786E\u4FDD|\u5236\u7A7A\u78BA\u4FDD|\u5236\u7A7A\u6743\u786E\u4FDD|\u5236\u7A7A\u6B0A\u78BA\u4FDD|\u822A\u7A7A\u512A\u52E2|\u822A\u7A7A\u4F18\u52BF)";
87
+ var CNT = "(?<cnt>\\d+|[\u4E00\u4E24\u4E8C\u4E09\u56DB\u4E94\u516D\u4E03\u516B\u4E5D\u5341])?";
88
+ var CNT_UNIT = "[\u6B21\u56DE\u51F8]?";
89
+ var NODE_DIAN = "(?<node>[A-Za-z]{1,2}\\d{0,2})[\u70B9\u70B9\u9EDE]";
90
+ var NODE_BARE = "(?<node>(?:[A-Za-z]{1,2}\\d{1,2})|(?:[A-Za-z](?!\\d)))";
91
+ var BASE = "(?<base>\u5B88\u5BB6|\u57FA\u5730\u9632\u7A7A\u6218|\u57FA\u5730\u9632\u7A7A|\u5BB6\u91CC|\u9632\u7A7A|\u57FA\u5730)";
92
+ var DECORATION = /(boss|王点|王點|門神|门神|斬殺|斩杀|斬殺線|斩杀线|第\d+[次回凸]?|第[一两二三四五六七八九十]+[次回凸]?|第)/gi;
93
+ var NOISE = /[^A-Za-z0-9〇㐀-䶿一-鿿]/g;
94
+ var FORM_TEMPLATES = [
95
+ { kind: "base-air-defense", build: () => new RegExp(`${BASE}${AIR}${CNT}${CNT_UNIT}`, "i"), usesNode: false },
96
+ { kind: "reach-after", build: (node) => new RegExp(`${REACH}${node}${CNT}${CNT_UNIT}`, "i"), usesNode: true },
97
+ { kind: "node-arrival", build: (node) => new RegExp(`${node}${REACH}${CNT}${CNT_UNIT}`, "i"), usesNode: true },
98
+ { kind: "kill", build: (node) => new RegExp(`${node}${KILL}${CNT}${CNT_UNIT}`, "i"), usesNode: true },
99
+ { kind: "victory-rank", build: (node) => new RegExp(`${node}${RANKC}${WIN}${CNT}${CNT_UNIT}`, "i"), usesNode: true },
100
+ { kind: "count-first", build: (node) => new RegExp(`${node}${CNT}${CNT_UNIT}${RANKC}${WIN}`, "i"), usesNode: true },
101
+ { kind: "node-air-state", build: (node) => new RegExp(`${node}${AIR}${CNT}${CNT_UNIT}`, "i"), usesNode: true }
99
102
  ];
100
- var DECORATION = /(boss|王点|王點|門神|门神|斬殺|斩杀|斬殺線|斩杀线)/gi;
103
+ var FORMS = [];
104
+ for (const template of FORM_TEMPLATES) {
105
+ if (!template.usesNode) {
106
+ FORMS.push({ kind: template.kind, re: template.build(null), countGroup: 3 });
107
+ continue;
108
+ }
109
+ FORMS.push({ kind: template.kind, re: template.build(NODE_DIAN), countGroup: 2, nodeGroup: 1 });
110
+ FORMS.push({ kind: template.kind, re: template.build(NODE_BARE), countGroup: 2, nodeGroup: 1 });
111
+ }
101
112
  function normalizeNodeLabel(value) {
102
113
  return String(value ?? "").trim().toUpperCase();
103
114
  }
@@ -112,15 +123,20 @@ function toHalfWidth(text) {
112
123
  return out;
113
124
  }
114
125
  function chineseCountToNumber(count) {
126
+ if (count == null || count === "") return 1;
115
127
  return CHINESE_DIGIT[count] ?? Number(count);
116
128
  }
117
129
  function stripAnnotation(text) {
118
130
  const notes = [];
119
- const cleaned = String(text ?? "").replace(/[((【〔「]([^()()【】〔〕「」]*)[))】〕」]/g, (_m, inner) => {
120
- const note = String(inner ?? "").trim();
121
- if (note !== "") notes.push(note);
122
- return "";
123
- });
131
+ const cleaned = String(text ?? "").replace(
132
+ /[((【〔「]([^()()【】〔〕「」]*)[))】〕」]/g,
133
+ (_m, inner) => {
134
+ const value = String(inner ?? "").trim();
135
+ if (/^[A-Za-z]{1,2}\d{0,2}[点点點]?$/.test(value)) return value;
136
+ if (value !== "") notes.push(value);
137
+ return "";
138
+ }
139
+ );
124
140
  return { cleaned: cleaned.trim(), notes };
125
141
  }
126
142
  function airStateOf(word) {
@@ -128,10 +144,9 @@ function airStateOf(word) {
128
144
  }
129
145
  function parseSegmentBody(segment) {
130
146
  const { cleaned, notes } = stripAnnotation(segment);
131
- let text = toHalfWidth(cleaned).replace(DECORATION, "").replace(/[×✕**]/g, "").replace(/\s+/g, "");
147
+ let text = toHalfWidth(cleaned).replace(DECORATION, "").replace(NOISE, "");
132
148
  if (text === "") return { matches: null, notes };
133
149
  const bossSeen = /boss/i.test(segment) || /boss/i.test(text);
134
- text = text.replace(/([一两二三四五六七八九十])(?=[次回凸]|$)/g, (m) => String(CHINESE_DIGIT[m]));
135
150
  const matches = [];
136
151
  let rest = text;
137
152
  let guard = 0;
@@ -145,94 +160,59 @@ function parseSegmentBody(segment) {
145
160
  }
146
161
  }
147
162
  if (matched == null && bossSeen) {
148
- const bare = new RegExp(`^${NODE}${DIAN}${CNT}${CNT_UNIT}`, "i").exec(rest);
149
- if (bare != null) {
163
+ const bare = /^([A-Za-z]{1,2})(\d+)?([一两二三四五六七八九十]|\d+)?[次回凸]?/.exec(rest);
164
+ if (bare != null && bare[1] != null && (bare[2] != null || bare[3] != null)) {
165
+ const letters = bare[1];
166
+ const digits = bare[2] ?? "";
167
+ const countWord = bare[3] ?? "";
168
+ const restAfter = rest.slice(bare[0].length);
169
+ const digitsAreCount = digits !== "" && /^[次回凸]/.test(restAfter);
170
+ const nodeLabel2 = normalizeNodeLabel(digitsAreCount || digits === "" ? letters : letters + digits);
171
+ const count2 = digitsAreCount ? Number(digits) : chineseCountToNumber(countWord);
150
172
  matches.push({
151
173
  ok: true,
152
174
  condition: {
153
175
  kind: "victory-rank",
154
- nodeLabel: normalizeNodeLabel(bare[1]),
176
+ nodeLabel: nodeLabel2,
155
177
  requiredRank: "A",
156
- requiredCount: chineseCountToNumber(bare[2]),
178
+ requiredCount: count2,
157
179
  notes: [...notes, "boss\u2192\u6309\u51FB\u7834(A\u80DC)\u8BA1"]
158
180
  }
159
181
  });
160
- rest = rest.slice(bare[0].length);
182
+ rest = restAfter;
161
183
  continue;
162
184
  }
163
185
  break;
164
186
  }
165
187
  if (matched == null) break;
166
188
  const { form, m } = matched;
167
- const count = chineseCountToNumber(form.kind === "count-first" ? m[2] : m[m.length - 1]);
168
- if (!Number.isInteger(count) || count <= 0) return { matches: [{ ok: false, reason: "\u6B21\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570" }], notes };
189
+ const count = chineseCountToNumber(m.groups?.cnt);
190
+ if (!Number.isInteger(count) || count <= 0) {
191
+ return { matches: [{ ok: false, reason: "\u6B21\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570" }], notes };
192
+ }
193
+ const common = { requiredCount: count, notes };
194
+ const nodeLabel = normalizeNodeLabel(m.groups?.node);
169
195
  if (form.kind === "base-air-defense") {
170
- const airState = airStateOf(m[2]);
171
- if (airState == null) return { matches: [{ ok: false, reason: `\u672A\u77E5\u5236\u7A7A\u8BCD\u300C${m[2]}\u300D` }], notes };
172
- matches.push({
173
- ok: true,
174
- condition: { kind: "base-air-defense", requiredAirState: airState, requiredCount: count, notes }
175
- });
196
+ const airState = airStateOf(m.groups.air);
197
+ if (airState == null) return { matches: [{ ok: false, reason: `\u672A\u77E5\u5236\u7A7A\u8BCD\u300C${m.groups.air}\u300D` }], notes };
198
+ matches.push({ ok: true, condition: { kind: "base-air-defense", requiredAirState: airState, ...common } });
176
199
  } else if (form.kind === "node-arrival" || form.kind === "reach-after") {
177
- matches.push({
178
- ok: true,
179
- condition: {
180
- kind: "node-arrival",
181
- nodeLabel: normalizeNodeLabel(m[1]),
182
- requiredCount: count,
183
- notes
184
- }
185
- });
200
+ matches.push({ ok: true, condition: { kind: "node-arrival", nodeLabel, ...common } });
186
201
  } else if (form.kind === "kill") {
187
202
  matches.push({
188
203
  ok: true,
189
- condition: {
190
- kind: "victory-rank",
191
- nodeLabel: normalizeNodeLabel(m[1]),
192
- requiredRank: "A",
193
- requiredCount: count,
194
- notes: [...notes, "\u51FB\u7834\u2192\u6309A\u80DC\u8BA1"]
195
- }
196
- });
197
- } else if (form.kind === "victory-rank") {
198
- matches.push({
199
- ok: true,
200
- condition: {
201
- kind: "victory-rank",
202
- nodeLabel: normalizeNodeLabel(m[1]),
203
- requiredRank: m[2].toUpperCase(),
204
- requiredCount: count,
205
- notes
206
- }
207
- });
208
- } else if (form.kind === "count-first") {
209
- matches.push({
210
- ok: true,
211
- condition: {
212
- kind: "victory-rank",
213
- nodeLabel: normalizeNodeLabel(m[1]),
214
- requiredRank: m[3].toUpperCase(),
215
- requiredCount: count,
216
- notes
217
- }
204
+ condition: { kind: "victory-rank", nodeLabel, requiredRank: "A", ...common, notes: [...notes, "\u51FB\u7834\u2192\u6309A\u80DC\u8BA1"] }
218
205
  });
206
+ } else if (form.kind === "victory-rank" || form.kind === "count-first") {
207
+ matches.push({ ok: true, condition: { kind: "victory-rank", nodeLabel, requiredRank: m.groups.rank.toUpperCase(), ...common } });
219
208
  } else {
220
- const airState = airStateOf(m[2]);
221
- if (airState == null) return { matches: [{ ok: false, reason: `\u672A\u77E5\u5236\u7A7A\u8BCD\u300C${m[2]}\u300D` }], notes };
222
- matches.push({
223
- ok: true,
224
- condition: {
225
- kind: "node-air-state",
226
- nodeLabel: normalizeNodeLabel(m[1]),
227
- requiredAirState: airState,
228
- requiredCount: count,
229
- notes
230
- }
231
- });
209
+ const airState = airStateOf(m.groups.air);
210
+ if (airState == null) return { matches: [{ ok: false, reason: `\u672A\u77E5\u5236\u7A7A\u8BCD\u300C${m.groups.air}\u300D` }], notes };
211
+ matches.push({ ok: true, condition: { kind: "node-air-state", nodeLabel, requiredAirState: airState, ...common } });
232
212
  }
233
213
  rest = rest.slice(m.index + m[0].length);
234
214
  }
235
- return { matches, notes, bossSeen };
215
+ return { matches, notes };
236
216
  }
237
217
  var BUCKET_HEADER = /^(解谜|解密|谜题|削甲|装甲|debuff|gimmick)[::]/;
238
218
  var BUCKET_WORDS = { \u89E3\u8C1C: "gimmick", \u89E3\u5BC6: "gimmick", \u8C1C\u9898: "gimmick", gimmick: "gimmick", \u524A\u7532: "debuff", \u88C5\u7532: "debuff", debuff: "debuff" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-gimmick-tracker",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "解密·削甲进度:自动对账活动海域解密/削甲条件进度(轮次账本,阶段自动旋转,无人工确认卡)",
5
5
  "main": "index.js",
6
6
  "files": [