eve-fit-engine 0.1.0 → 0.1.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.
- package/README.md +18 -8
- package/dist/index.cjs +596 -218
- package/dist/index.js +596 -218
- package/dist/node.cjs +596 -218
- package/dist/node.js +596 -218
- package/package.json +4 -1
package/dist/node.js
CHANGED
|
@@ -653,7 +653,7 @@ var FitContext = class {
|
|
|
653
653
|
const root = this.resolveDomain(modifier.domain, source);
|
|
654
654
|
switch (modifier.func) {
|
|
655
655
|
case "ItemModifier":
|
|
656
|
-
if (modifier.domain === "charID" && source.kind === "module") {
|
|
656
|
+
if (modifier.domain === "charID" && source.kind === "module" && modifier.modifiedAttributeID === 212) {
|
|
657
657
|
const out = [];
|
|
658
658
|
for (const m of this.modules) {
|
|
659
659
|
if (m.charge) out.push(m.charge);
|
|
@@ -1065,6 +1065,299 @@ function makeModeState(modeTypeID, type) {
|
|
|
1065
1065
|
return new ItemState({ kind: "mode", id: `mode:${modeTypeID}`, type, state: "ACTIVE" });
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
+
// src/stackingGroups.ts
|
|
1069
|
+
var STACKING_PENALTY_GROUPS = /* @__PURE__ */ new Map([
|
|
1070
|
+
[89, "default"],
|
|
1071
|
+
[91, "default"],
|
|
1072
|
+
[92, "default"],
|
|
1073
|
+
[93, "default"],
|
|
1074
|
+
[95, "default"],
|
|
1075
|
+
[96, "default"],
|
|
1076
|
+
[394, "default"],
|
|
1077
|
+
[395, "default"],
|
|
1078
|
+
[494, "default"],
|
|
1079
|
+
[607, "postMul"],
|
|
1080
|
+
[657, "default"],
|
|
1081
|
+
[699, "default"],
|
|
1082
|
+
[763, "default"],
|
|
1083
|
+
[784, "default"],
|
|
1084
|
+
[854, "cloakingScanResolutionMultiplier"],
|
|
1085
|
+
[856, "default"],
|
|
1086
|
+
[889, "default"],
|
|
1087
|
+
[891, "default"],
|
|
1088
|
+
[892, "default"],
|
|
1089
|
+
[1024, "default"],
|
|
1090
|
+
[1230, "default"],
|
|
1091
|
+
[1281, "default"],
|
|
1092
|
+
[1318, "default"],
|
|
1093
|
+
[1445, "default"],
|
|
1094
|
+
[1446, "default"],
|
|
1095
|
+
[1448, "default"],
|
|
1096
|
+
[1452, "default"],
|
|
1097
|
+
[1472, "default"],
|
|
1098
|
+
[1590, "default"],
|
|
1099
|
+
[1617, "default"],
|
|
1100
|
+
[1720, "default"],
|
|
1101
|
+
[1764, "default"],
|
|
1102
|
+
[1885, "postPerc"],
|
|
1103
|
+
[1886, "postPerc"],
|
|
1104
|
+
[2013, "default"],
|
|
1105
|
+
[2014, "default"],
|
|
1106
|
+
[2019, "default"],
|
|
1107
|
+
[2020, "default"],
|
|
1108
|
+
[2041, "default"],
|
|
1109
|
+
[2052, "default"],
|
|
1110
|
+
[2152, "default"],
|
|
1111
|
+
[2232, "default"],
|
|
1112
|
+
[2302, "preMul"],
|
|
1113
|
+
[2644, "default"],
|
|
1114
|
+
[2645, "default"],
|
|
1115
|
+
[2646, "default"],
|
|
1116
|
+
[2670, "default"],
|
|
1117
|
+
[2693, "default"],
|
|
1118
|
+
[2694, "default"],
|
|
1119
|
+
[2695, "default"],
|
|
1120
|
+
[2696, "default"],
|
|
1121
|
+
[2697, "default"],
|
|
1122
|
+
[2698, "default"],
|
|
1123
|
+
[2716, "default"],
|
|
1124
|
+
[2717, "default"],
|
|
1125
|
+
[2792, "default"],
|
|
1126
|
+
[2795, "default"],
|
|
1127
|
+
[2796, "default"],
|
|
1128
|
+
[2797, "default"],
|
|
1129
|
+
[2798, "default"],
|
|
1130
|
+
[2799, "default"],
|
|
1131
|
+
[2801, "default"],
|
|
1132
|
+
[2802, "default"],
|
|
1133
|
+
[2803, "default"],
|
|
1134
|
+
[2804, "default"],
|
|
1135
|
+
[2851, "default"],
|
|
1136
|
+
[2858, "default"],
|
|
1137
|
+
[2865, "default"],
|
|
1138
|
+
[2867, "default"],
|
|
1139
|
+
[2868, "default"],
|
|
1140
|
+
[3001, "postPerc"],
|
|
1141
|
+
[3046, "default"],
|
|
1142
|
+
[3174, "default"],
|
|
1143
|
+
[3175, "default"],
|
|
1144
|
+
[3182, "default"],
|
|
1145
|
+
[3200, "default"],
|
|
1146
|
+
[3201, "default"],
|
|
1147
|
+
[3586, "default"],
|
|
1148
|
+
[3655, "default"],
|
|
1149
|
+
[3656, "default"],
|
|
1150
|
+
[3657, "default"],
|
|
1151
|
+
[3659, "default"],
|
|
1152
|
+
[3674, "default"],
|
|
1153
|
+
[3726, "default"],
|
|
1154
|
+
[3727, "default"],
|
|
1155
|
+
[3993, "postMul"],
|
|
1156
|
+
[3995, "postMul"],
|
|
1157
|
+
[3996, "default"],
|
|
1158
|
+
[3997, "default"],
|
|
1159
|
+
[3998, "default"],
|
|
1160
|
+
[3999, "default"],
|
|
1161
|
+
[4002, "postMul"],
|
|
1162
|
+
[4003, "postMul"],
|
|
1163
|
+
[4016, "default"],
|
|
1164
|
+
[4017, "postMul"],
|
|
1165
|
+
[4018, "postMul"],
|
|
1166
|
+
[4019, "postMul"],
|
|
1167
|
+
[4020, "postMul"],
|
|
1168
|
+
[4021, "postMul"],
|
|
1169
|
+
[4022, "postMul"],
|
|
1170
|
+
[4023, "postMul"],
|
|
1171
|
+
[4054, "default"],
|
|
1172
|
+
[4055, "default"],
|
|
1173
|
+
[4056, "default"],
|
|
1174
|
+
[4057, "postMul"],
|
|
1175
|
+
[4058, "postMul"],
|
|
1176
|
+
[4059, "postMul"],
|
|
1177
|
+
[4060, "postMul"],
|
|
1178
|
+
[4061, "postMul"],
|
|
1179
|
+
[4062, "postMul"],
|
|
1180
|
+
[4063, "postMul"],
|
|
1181
|
+
[4086, "postMul"],
|
|
1182
|
+
[4088, "postMul"],
|
|
1183
|
+
[4089, "postMul"],
|
|
1184
|
+
[4135, "default"],
|
|
1185
|
+
[4136, "default"],
|
|
1186
|
+
[4137, "default"],
|
|
1187
|
+
[4138, "default"],
|
|
1188
|
+
[4162, "default"],
|
|
1189
|
+
[4280, "default"],
|
|
1190
|
+
[4358, "default"],
|
|
1191
|
+
[4464, "default"],
|
|
1192
|
+
[4489, "default"],
|
|
1193
|
+
[4490, "default"],
|
|
1194
|
+
[4491, "default"],
|
|
1195
|
+
[4492, "default"],
|
|
1196
|
+
[4527, "default"],
|
|
1197
|
+
[4559, "default"],
|
|
1198
|
+
[4575, "default"],
|
|
1199
|
+
[4809, "default"],
|
|
1200
|
+
[4810, "default"],
|
|
1201
|
+
[4811, "default"],
|
|
1202
|
+
[4812, "default"],
|
|
1203
|
+
[4906, "postMul"],
|
|
1204
|
+
[4928, "preMul"],
|
|
1205
|
+
[4961, "postMul"],
|
|
1206
|
+
[5081, "default"],
|
|
1207
|
+
[5188, "default"],
|
|
1208
|
+
[5189, "default"],
|
|
1209
|
+
[5190, "default"],
|
|
1210
|
+
[5213, "default"],
|
|
1211
|
+
[5214, "default"],
|
|
1212
|
+
[5230, "default"],
|
|
1213
|
+
[5231, "default"],
|
|
1214
|
+
[5397, "default"],
|
|
1215
|
+
[5399, "default"],
|
|
1216
|
+
[5440, "postMul"],
|
|
1217
|
+
[5468, "default"],
|
|
1218
|
+
[5560, "default"],
|
|
1219
|
+
[5618, "postPerc"],
|
|
1220
|
+
[5757, "default"],
|
|
1221
|
+
[5867, "default"],
|
|
1222
|
+
[5911, "default"],
|
|
1223
|
+
[5912, "postMul"],
|
|
1224
|
+
[5914, "postMul"],
|
|
1225
|
+
[5915, "postMul"],
|
|
1226
|
+
[5916, "postMul"],
|
|
1227
|
+
[5917, "postMul"],
|
|
1228
|
+
[5918, "postMul"],
|
|
1229
|
+
[5919, "postMul"],
|
|
1230
|
+
[5920, "postMul"],
|
|
1231
|
+
[5921, "postMul"],
|
|
1232
|
+
[5922, "postMul"],
|
|
1233
|
+
[5923, "postMul"],
|
|
1234
|
+
[5924, "postMul"],
|
|
1235
|
+
[5925, "postMul"],
|
|
1236
|
+
[5926, "postMul"],
|
|
1237
|
+
[5927, "postMul"],
|
|
1238
|
+
[5929, "postMul"],
|
|
1239
|
+
[5951, "default"],
|
|
1240
|
+
[5998, "default"],
|
|
1241
|
+
[6010, "postDiv"],
|
|
1242
|
+
[6011, "postDiv"],
|
|
1243
|
+
[6012, "postDiv"],
|
|
1244
|
+
[6014, "postDiv"],
|
|
1245
|
+
[6015, "postDiv"],
|
|
1246
|
+
[6016, "postDiv"],
|
|
1247
|
+
[6017, "postDiv"],
|
|
1248
|
+
[6039, "postDiv"],
|
|
1249
|
+
[6040, "postDiv"],
|
|
1250
|
+
[6041, "postDiv"],
|
|
1251
|
+
[6063, "default"],
|
|
1252
|
+
[6076, "postDiv"],
|
|
1253
|
+
[6110, "default"],
|
|
1254
|
+
[6111, "default"],
|
|
1255
|
+
[6112, "default"],
|
|
1256
|
+
[6113, "default"],
|
|
1257
|
+
[6135, "default"],
|
|
1258
|
+
[6152, "postDiv"],
|
|
1259
|
+
[6154, "postDiv"],
|
|
1260
|
+
[6164, "default"],
|
|
1261
|
+
[6201, "default"],
|
|
1262
|
+
[6208, "default"],
|
|
1263
|
+
[6402, "default"],
|
|
1264
|
+
[6403, "default"],
|
|
1265
|
+
[6404, "default"],
|
|
1266
|
+
[6405, "default"],
|
|
1267
|
+
[6406, "default"],
|
|
1268
|
+
[6409, "default"],
|
|
1269
|
+
[6410, "default"],
|
|
1270
|
+
[6411, "default"],
|
|
1271
|
+
[6412, "default"],
|
|
1272
|
+
[6422, "default"],
|
|
1273
|
+
[6423, "default"],
|
|
1274
|
+
[6424, "default"],
|
|
1275
|
+
[6425, "default"],
|
|
1276
|
+
[6426, "default"],
|
|
1277
|
+
[6427, "default"],
|
|
1278
|
+
[6428, "default"],
|
|
1279
|
+
[6435, "default"],
|
|
1280
|
+
[6439, "default"],
|
|
1281
|
+
[6440, "default"],
|
|
1282
|
+
[6441, "default"],
|
|
1283
|
+
[6448, "default"],
|
|
1284
|
+
[6449, "default"],
|
|
1285
|
+
[6472, "default"],
|
|
1286
|
+
[6473, "default"],
|
|
1287
|
+
[6474, "default"],
|
|
1288
|
+
[6476, "default"],
|
|
1289
|
+
[6478, "default"],
|
|
1290
|
+
[6479, "default"],
|
|
1291
|
+
[6481, "default"],
|
|
1292
|
+
[6484, "postMul"],
|
|
1293
|
+
[6487, "default"],
|
|
1294
|
+
[6555, "default"],
|
|
1295
|
+
[6556, "default"],
|
|
1296
|
+
[6557, "default"],
|
|
1297
|
+
[6559, "default"],
|
|
1298
|
+
[6566, "postMul"],
|
|
1299
|
+
[6567, "default"],
|
|
1300
|
+
[6581, "default"],
|
|
1301
|
+
[6582, "postPercent"],
|
|
1302
|
+
[6658, "preMul"],
|
|
1303
|
+
[6670, "default"],
|
|
1304
|
+
[6671, "default"],
|
|
1305
|
+
[6682, "default"],
|
|
1306
|
+
[6683, "default"],
|
|
1307
|
+
[6684, "default"],
|
|
1308
|
+
[6686, "default"],
|
|
1309
|
+
[6690, "default"],
|
|
1310
|
+
[6692, "default"],
|
|
1311
|
+
[6693, "default"],
|
|
1312
|
+
[6694, "default"],
|
|
1313
|
+
[6727, "default"],
|
|
1314
|
+
[6730, "postMul"],
|
|
1315
|
+
[6731, "postMul"],
|
|
1316
|
+
[6796, "postDiv"],
|
|
1317
|
+
[6797, "postDiv"],
|
|
1318
|
+
[6798, "postDiv"],
|
|
1319
|
+
[6799, "postDiv"],
|
|
1320
|
+
[6801, "postDiv"],
|
|
1321
|
+
[6877, "default"],
|
|
1322
|
+
[7029, "default"],
|
|
1323
|
+
[7077, "default"],
|
|
1324
|
+
[7078, "default"],
|
|
1325
|
+
[7098, "default"],
|
|
1326
|
+
[7111, "default"],
|
|
1327
|
+
[7142, "default"],
|
|
1328
|
+
[7202, "default"],
|
|
1329
|
+
[7203, "default"],
|
|
1330
|
+
[7223, "default"],
|
|
1331
|
+
[7237, "default"],
|
|
1332
|
+
[8033, "default"],
|
|
1333
|
+
[8057, "default"],
|
|
1334
|
+
[8076, "default"],
|
|
1335
|
+
[8082, "default"],
|
|
1336
|
+
[8108, "postMul"],
|
|
1337
|
+
[8109, "postMul"],
|
|
1338
|
+
[8111, "default"],
|
|
1339
|
+
[8112, "default"],
|
|
1340
|
+
[8113, "postMul"],
|
|
1341
|
+
[8114, "postMul"],
|
|
1342
|
+
[8119, "default"],
|
|
1343
|
+
[11445, "default"],
|
|
1344
|
+
[11691, "default"],
|
|
1345
|
+
[11946, "default"],
|
|
1346
|
+
[11947, "postMul"],
|
|
1347
|
+
[11948, "postMul"],
|
|
1348
|
+
[11953, "postMul"],
|
|
1349
|
+
[12126, "default"],
|
|
1350
|
+
[12597, "default"],
|
|
1351
|
+
[12761, "default"],
|
|
1352
|
+
[12794, "postDiv"],
|
|
1353
|
+
[12795, "postDiv"],
|
|
1354
|
+
[12796, "postDiv"],
|
|
1355
|
+
[12798, "postDiv"],
|
|
1356
|
+
[12799, "postDiv"],
|
|
1357
|
+
[12838, "postMul"],
|
|
1358
|
+
[12839, "postMul"]
|
|
1359
|
+
]);
|
|
1360
|
+
|
|
1068
1361
|
// src/modifierEngine.ts
|
|
1069
1362
|
var NO_PENALTY_KINDS = /* @__PURE__ */ new Set([
|
|
1070
1363
|
"skill",
|
|
@@ -1079,16 +1372,21 @@ function scaleForPipeline(rawValue, _unitID, op) {
|
|
|
1079
1372
|
}
|
|
1080
1373
|
function applySourceItem(source, ctx, dataset) {
|
|
1081
1374
|
const isLocalModule = source.kind === "module";
|
|
1375
|
+
const selfMods = [];
|
|
1376
|
+
const outMods = [];
|
|
1082
1377
|
for (const eid of source.effectIDs) {
|
|
1083
1378
|
if (LEGACY_HANDLED_EFFECT_IDS.has(eid)) continue;
|
|
1379
|
+
if (SEC_STATUS_SCALED_EFFECT_IDS.has(eid)) continue;
|
|
1084
1380
|
if (isLocalModule && ctx.stoppedLocalEffectIDs.has(eid)) continue;
|
|
1085
1381
|
const effect = dataset.effects.get(eid);
|
|
1086
1382
|
if (!effect) continue;
|
|
1087
1383
|
if (!source.appliesAtState(effect)) continue;
|
|
1088
1384
|
for (const mi of effect.modifierInfo) {
|
|
1089
|
-
|
|
1385
|
+
(mi.domain === "itemID" ? selfMods : outMods).push({ effect, mi });
|
|
1090
1386
|
}
|
|
1091
1387
|
}
|
|
1388
|
+
for (const { effect, mi } of selfMods) applyOneModifier(source, effect, mi, ctx, dataset);
|
|
1389
|
+
for (const { effect, mi } of outMods) applyOneModifier(source, effect, mi, ctx, dataset);
|
|
1092
1390
|
}
|
|
1093
1391
|
function collectEffectStoppers(projectedSources, dataset) {
|
|
1094
1392
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -1154,7 +1452,7 @@ function applyOneModifier(source, effect, mi, ctx, dataset) {
|
|
|
1154
1452
|
if (computed === null) return;
|
|
1155
1453
|
const targets = ctx.targetsForModifier(mi, source);
|
|
1156
1454
|
if (targets.length === 0) return;
|
|
1157
|
-
const stackingGroup = computeStackingGroup(source, mi, dataset);
|
|
1455
|
+
const stackingGroup = computeStackingGroup(source, mi, dataset, effect);
|
|
1158
1456
|
const isMul = op === "PreMul" || op === "PostMul" || op === "PreDiv" || op === "PostDiv";
|
|
1159
1457
|
const value = isMul && computed.scaled ? 1 + computed.value : computed.value;
|
|
1160
1458
|
const sdeDefault = isMul ? dataset.attributes.get(mi.modifiedAttributeID)?.defaultValue ?? 0 : 0;
|
|
@@ -1662,7 +1960,52 @@ var SHIP_BONUS_SCALING_SKILL = /* @__PURE__ */ new Map([
|
|
|
1662
1960
|
[6112, 24313],
|
|
1663
1961
|
[6113, 24312],
|
|
1664
1962
|
[6114, 24311],
|
|
1665
|
-
[6116, 24314]
|
|
1963
|
+
[6116, 24314],
|
|
1964
|
+
// ----- Auto-derived from the SDE: attrs that are BOTH skill-level-scaled
|
|
1965
|
+
// (a skill effect does `attr ×= skillLevel` via attr 280) AND read by a
|
|
1966
|
+
// SHIP-side effect as the bonus value. These are per-racial/role-skill hull
|
|
1967
|
+
// bonuses whose ship-side reader must scale by the skill level. Previously
|
|
1968
|
+
// missing → the bonus was taken at base (×1) instead of ×5 at All-V.
|
|
1969
|
+
// Notable: Exhumer/Barge shield+armor resist role bonuses (Hulk/Skiff/
|
|
1970
|
+
// Mackinaw shield resist 4 %→20 %), Bhaalgorn drone+laser (492), industrial
|
|
1971
|
+
// command (Orca/Rorqual), Marauder/pirate/expedition hull bonuses.
|
|
1972
|
+
[66, 89611],
|
|
1973
|
+
[310, 3432],
|
|
1974
|
+
[349, 19760],
|
|
1975
|
+
[492, 3339],
|
|
1976
|
+
[1296, 21610],
|
|
1977
|
+
[1669, 3184],
|
|
1978
|
+
[1670, 3184],
|
|
1979
|
+
[1842, 32918],
|
|
1980
|
+
[3167, 33856],
|
|
1981
|
+
[3181, 17940],
|
|
1982
|
+
[3182, 17940],
|
|
1983
|
+
[3183, 17940],
|
|
1984
|
+
[3184, 17940],
|
|
1985
|
+
[3185, 17940],
|
|
1986
|
+
[3187, 17940],
|
|
1987
|
+
[3188, 17940],
|
|
1988
|
+
[3190, 33856],
|
|
1989
|
+
[3191, 33856],
|
|
1990
|
+
[3192, 33856],
|
|
1991
|
+
[3193, 22551],
|
|
1992
|
+
[3194, 22551],
|
|
1993
|
+
[3197, 22551],
|
|
1994
|
+
[3198, 22551],
|
|
1995
|
+
[3199, 22551],
|
|
1996
|
+
[3203, 29637],
|
|
1997
|
+
[3204, 29637],
|
|
1998
|
+
[3205, 29637],
|
|
1999
|
+
[3210, 3341],
|
|
2000
|
+
[3221, 29637],
|
|
2001
|
+
[3222, 29637],
|
|
2002
|
+
[3223, 28374],
|
|
2003
|
+
[3224, 28374],
|
|
2004
|
+
[3237, 32918],
|
|
2005
|
+
[3240, 32918],
|
|
2006
|
+
[3326, 28374],
|
|
2007
|
+
[6088, 33092],
|
|
2008
|
+
[6089, 33094]
|
|
1666
2009
|
]);
|
|
1667
2010
|
var SUBSYSTEM_BONUS_SCALING_SKILL = /* @__PURE__ */ new Map([
|
|
1668
2011
|
// Amarr
|
|
@@ -1717,8 +2060,42 @@ var SUBSYSTEM_BONUS_SCALING_SKILL = /* @__PURE__ */ new Map([
|
|
|
1717
2060
|
// MinmatarDefensive
|
|
1718
2061
|
[1449, 30551],
|
|
1719
2062
|
// MinmatarOffensive
|
|
1720
|
-
[1450, 30554]
|
|
2063
|
+
[1450, 30554],
|
|
1721
2064
|
// MinmatarPropulsion
|
|
2065
|
+
// ----- Authoritative completion: every subsystem-bonus attr that the SDE
|
|
2066
|
+
// ----- scales by a racial subsystem skill, derived verbatim from the
|
|
2067
|
+
// ----- `subsystemSkillLevel*` skill effects (modAttr = bonus attr,
|
|
2068
|
+
// ----- modifying = 280/skillLevel, PreMul). The Amarr/Caldari secondaries
|
|
2069
|
+
// ----- above were hand-added; Gallente + Minmatar secondaries (and the
|
|
2070
|
+
// ----- 2680-2687 defensive/core block) were MISSING, so subsystem-sourced
|
|
2071
|
+
// ----- bonuses on those races (e.g. Loki Propulsion agility 1523, Offensive
|
|
2072
|
+
// ----- RoF 1522 / 1534) fell to the flat path and applied ×1 instead of ×5.
|
|
2073
|
+
// ----- Duplicates of the entries above are harmless (same value).
|
|
2074
|
+
[1517, 30540],
|
|
2075
|
+
[1519, 30546],
|
|
2076
|
+
[1520, 30553],
|
|
2077
|
+
[1521, 30550],
|
|
2078
|
+
// Gallente Def/Core/Prop/Off secondaries
|
|
2079
|
+
[1522, 30551],
|
|
2080
|
+
[1523, 30554],
|
|
2081
|
+
[1525, 30547],
|
|
2082
|
+
[1526, 30545],
|
|
2083
|
+
// Minmatar Off/Prop/Core/Def secondaries
|
|
2084
|
+
[1531, 30537],
|
|
2085
|
+
[1532, 30550],
|
|
2086
|
+
[1533, 30549],
|
|
2087
|
+
[1534, 30551],
|
|
2088
|
+
// Off cross-race tertiaries (Amarr/Gallente/Caldari/Minmatar)
|
|
2089
|
+
[2680, 30532],
|
|
2090
|
+
[2681, 30539],
|
|
2091
|
+
[2682, 30544],
|
|
2092
|
+
[2683, 30548],
|
|
2093
|
+
// Def/Core extra (Amarr/Caldari)
|
|
2094
|
+
[2684, 30540],
|
|
2095
|
+
[2685, 30546],
|
|
2096
|
+
[2686, 30545],
|
|
2097
|
+
[2687, 30547]
|
|
2098
|
+
// Def/Core extra (Gallente/Minmatar)
|
|
1722
2099
|
]);
|
|
1723
2100
|
function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
1724
2101
|
if (mi.modifyingAttributeID === void 0) return null;
|
|
@@ -1758,7 +2135,7 @@ function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
|
1758
2135
|
if (skillID === void 0) return null;
|
|
1759
2136
|
const level = ctx.skillLevel(skillID);
|
|
1760
2137
|
if (level === 0) return null;
|
|
1761
|
-
if (source.kind === "ship"
|
|
2138
|
+
if (source.kind === "ship") {
|
|
1762
2139
|
return { value: baseValue, scaled: false };
|
|
1763
2140
|
}
|
|
1764
2141
|
if (itemRequiresSkill(source, skillID)) {
|
|
@@ -1768,46 +2145,21 @@ function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
|
1768
2145
|
}
|
|
1769
2146
|
return { value: baseValue, scaled: false };
|
|
1770
2147
|
}
|
|
1771
|
-
|
|
1772
|
-
793,
|
|
1773
|
-
// shipBonusRole7
|
|
1774
|
-
1688,
|
|
1775
|
-
// shipBonusRole8
|
|
1776
|
-
1803,
|
|
1777
|
-
// MWDSignatureRadiusBonus — Assault Frigate / Interceptor MWD sig role bonus (flat)
|
|
1778
|
-
2059,
|
|
1779
|
-
// eliteBonusCommandDestroyer1 — Command Destroyer T2 specialisation (flat per-level applied via skill, but the SHIP-side reader is flat)
|
|
1780
|
-
2060,
|
|
1781
|
-
// eliteBonusCommandDestroyer2
|
|
1782
|
-
2064,
|
|
1783
|
-
// roleBonusCD — Command Destroyer command burst PG / activation cost reduction.
|
|
1784
|
-
// Without this entry, effect 6214 (Draugur's `roleBonusCDLinksPGReduction`)
|
|
1785
|
-
// double-applies the -95 % bonus at × Skirmish Command Burst V → -475 %
|
|
1786
|
-
// PostPercent on Skirmish Command Burst II `power` (110) → -412.5 MW per
|
|
1787
|
-
// burst → total ship power used reads −738 MW instead of +97 MW.
|
|
1788
|
-
2298,
|
|
1789
|
-
// shipBonusRole1
|
|
1790
|
-
2299,
|
|
1791
|
-
// shipBonusRole2
|
|
1792
|
-
2300,
|
|
1793
|
-
// shipBonusRole3
|
|
1794
|
-
2301,
|
|
1795
|
-
// shipBonusRole4
|
|
1796
|
-
2302,
|
|
1797
|
-
// shipBonusRole5
|
|
1798
|
-
2303,
|
|
1799
|
-
// shipBonusRole6
|
|
1800
|
-
5952
|
|
1801
|
-
// shipBonusGasCloudDurationRoleBonusOreMiningDestroyer
|
|
1802
|
-
]);
|
|
1803
|
-
function computeStackingGroup(source, mi, dataset) {
|
|
2148
|
+
function computeStackingGroup(source, mi, dataset, effect) {
|
|
1804
2149
|
if (NO_PENALTY_KINDS.has(source.kind)) return null;
|
|
1805
2150
|
if (mi.modifiedAttributeID !== void 0) {
|
|
1806
2151
|
const attr = dataset.attributes.get(mi.modifiedAttributeID);
|
|
1807
2152
|
if (attr?.stackable) return null;
|
|
1808
2153
|
}
|
|
2154
|
+
const group = STACKING_PENALTY_GROUPS.get(effect.id);
|
|
2155
|
+
if (group !== void 0 && group !== "default" && CUSTOM_STACK_GROUPS_HONOURED.has(group)) {
|
|
2156
|
+
return `${group}:${mi.modifiedAttributeID}`;
|
|
2157
|
+
}
|
|
1809
2158
|
return `attr:${mi.modifiedAttributeID}`;
|
|
1810
2159
|
}
|
|
2160
|
+
var CUSTOM_STACK_GROUPS_HONOURED = /* @__PURE__ */ new Set([
|
|
2161
|
+
"cloakingScanResolutionMultiplier"
|
|
2162
|
+
]);
|
|
1811
2163
|
function mapSourceKind(kind) {
|
|
1812
2164
|
switch (kind) {
|
|
1813
2165
|
case "ship":
|
|
@@ -3525,6 +3877,13 @@ var LEGACY_HANDLED_HARDCODED_EFFECTS = /* @__PURE__ */ new Set([
|
|
|
3525
3877
|
6658
|
|
3526
3878
|
// Bastion Module — applyLegacyBastion
|
|
3527
3879
|
]);
|
|
3880
|
+
var SEC_STATUS_SCALED_EFFECT_IDS = /* @__PURE__ */ new Set([
|
|
3881
|
+
6871,
|
|
3882
|
+
12165,
|
|
3883
|
+
12181,
|
|
3884
|
+
12185,
|
|
3885
|
+
12202
|
|
3886
|
+
]);
|
|
3528
3887
|
var LEGACY_HANDLED_EFFECT_IDS = /* @__PURE__ */ new Set([
|
|
3529
3888
|
...LEGACY_HANDLED_PASSIVE_ADD_EFFECTS,
|
|
3530
3889
|
...LEGACY_HANDLED_HARDCODED_EFFECTS
|
|
@@ -3595,6 +3954,176 @@ function computeTotalEhp(shield, armor, hull, useProfile) {
|
|
|
3595
3954
|
return Number.isFinite(total) ? total : Number.MAX_SAFE_INTEGER;
|
|
3596
3955
|
}
|
|
3597
3956
|
|
|
3957
|
+
// src/fitChecks.ts
|
|
3958
|
+
function readAttr(t, id) {
|
|
3959
|
+
return t.attributes.find((a) => a.id === id)?.v;
|
|
3960
|
+
}
|
|
3961
|
+
function typeFitsSlotType(t, slot) {
|
|
3962
|
+
for (const e of t.effects) {
|
|
3963
|
+
const mapped = SLOT_EFFECT_TO_SLOT_TYPE[e.id];
|
|
3964
|
+
if (mapped === slot) return true;
|
|
3965
|
+
}
|
|
3966
|
+
return false;
|
|
3967
|
+
}
|
|
3968
|
+
function isTurretWeapon(t) {
|
|
3969
|
+
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "TURRET");
|
|
3970
|
+
}
|
|
3971
|
+
function isMissileLauncher(t) {
|
|
3972
|
+
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "MISSILE");
|
|
3973
|
+
}
|
|
3974
|
+
function isSmartBomb(t) {
|
|
3975
|
+
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "SMARTBOMB");
|
|
3976
|
+
}
|
|
3977
|
+
function shipGroupRestrictions(t) {
|
|
3978
|
+
const out = [];
|
|
3979
|
+
for (const id of CAN_FIT_SHIP_GROUP_ATTRS) {
|
|
3980
|
+
const v = readAttr(t, id);
|
|
3981
|
+
if (v != null && v > 0) out.push(Math.round(v));
|
|
3982
|
+
}
|
|
3983
|
+
return out;
|
|
3984
|
+
}
|
|
3985
|
+
function shipTypeRestrictions(t) {
|
|
3986
|
+
const out = [];
|
|
3987
|
+
for (const id of CAN_FIT_SHIP_TYPE_ATTRS) {
|
|
3988
|
+
const v = readAttr(t, id);
|
|
3989
|
+
if (v != null && v > 0) out.push(Math.round(v));
|
|
3990
|
+
}
|
|
3991
|
+
return out;
|
|
3992
|
+
}
|
|
3993
|
+
function maxGroupFittedFor(mod) {
|
|
3994
|
+
const v = readAttr(mod, ATTR.MAX_GROUP_FITTED);
|
|
3995
|
+
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
3996
|
+
}
|
|
3997
|
+
function maxTypeFittedFor(mod) {
|
|
3998
|
+
const v = readAttr(mod, ATTR.MAX_TYPE_FITTED);
|
|
3999
|
+
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
4000
|
+
}
|
|
4001
|
+
function freeFitGroupSlotsFor(mod, fittedModules, dataset) {
|
|
4002
|
+
const groupCap = maxGroupFittedFor(mod);
|
|
4003
|
+
const typeCap = maxTypeFittedFor(mod);
|
|
4004
|
+
if (groupCap === void 0 && typeCap === void 0) return Number.POSITIVE_INFINITY;
|
|
4005
|
+
let groupCount = 0;
|
|
4006
|
+
let typeCount = 0;
|
|
4007
|
+
for (const fm of fittedModules) {
|
|
4008
|
+
if (typeCap !== void 0 && fm.typeID === mod.id) typeCount++;
|
|
4009
|
+
if (groupCap !== void 0) {
|
|
4010
|
+
const t = dataset.getType(fm.typeID);
|
|
4011
|
+
if (t && t.groupID === mod.groupID) groupCount++;
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
let free = Number.POSITIVE_INFINITY;
|
|
4015
|
+
if (groupCap !== void 0) free = Math.min(free, groupCap - groupCount);
|
|
4016
|
+
if (typeCap !== void 0) free = Math.min(free, typeCap - typeCount);
|
|
4017
|
+
return Math.max(0, free);
|
|
4018
|
+
}
|
|
4019
|
+
function canFitModuleOnShip(mod, ship, slot, fitContext) {
|
|
4020
|
+
if (!typeFitsSlotType(mod, slot)) {
|
|
4021
|
+
return { ok: false, reason: `Module does not declare a ${slot} slot effect` };
|
|
4022
|
+
}
|
|
4023
|
+
if (!ship) return { ok: true };
|
|
4024
|
+
const allowedGroups = shipGroupRestrictions(mod);
|
|
4025
|
+
const allowedTypes = shipTypeRestrictions(mod);
|
|
4026
|
+
if (allowedGroups.length > 0 || allowedTypes.length > 0) {
|
|
4027
|
+
const groupOk = allowedGroups.includes(ship.groupID);
|
|
4028
|
+
const typeOk = allowedTypes.includes(ship.id);
|
|
4029
|
+
if (!groupOk && !typeOk) {
|
|
4030
|
+
return { ok: false, reason: "Ship hull/group not allowed by this module" };
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
if (slot === "RIG") {
|
|
4034
|
+
const modSize = readAttr(mod, ATTR.RIG_SIZE);
|
|
4035
|
+
const shipSize = readAttr(ship, ATTR.RIG_SIZE);
|
|
4036
|
+
if (modSize != null && shipSize != null && Math.round(modSize) !== Math.round(shipSize)) {
|
|
4037
|
+
return { ok: false, reason: "Rig size mismatch" };
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
if (slot === "SUBSYSTEM") {
|
|
4041
|
+
const fitsTo = readAttr(mod, 1380);
|
|
4042
|
+
if (fitsTo != null && fitsTo > 0 && Math.round(fitsTo) !== ship.id) {
|
|
4043
|
+
return { ok: false, reason: "Subsystem locked to a different T3C hull" };
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
if (slot === "HI") {
|
|
4047
|
+
if (isTurretWeapon(mod)) {
|
|
4048
|
+
const turrets = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
4049
|
+
if (turrets <= 0) {
|
|
4050
|
+
return { ok: false, reason: "Ship has no turret hardpoints" };
|
|
4051
|
+
}
|
|
4052
|
+
} else if (isMissileLauncher(mod)) {
|
|
4053
|
+
const launchers = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
4054
|
+
if (launchers <= 0) {
|
|
4055
|
+
return { ok: false, reason: "Ship has no launcher hardpoints" };
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
if (fitContext) {
|
|
4060
|
+
const free = freeFitGroupSlotsFor(mod, fitContext.fittedModules, fitContext.dataset);
|
|
4061
|
+
if (free <= 0) {
|
|
4062
|
+
const cap = maxTypeFittedFor(mod) ?? maxGroupFittedFor(mod);
|
|
4063
|
+
return { ok: false, reason: `Only ${cap} of this module type can be fitted to a ship` };
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
return { ok: true };
|
|
4067
|
+
}
|
|
4068
|
+
function chargeGroupsForModule(mod) {
|
|
4069
|
+
const out = [];
|
|
4070
|
+
for (const attrID of CHARGE_GROUP_ATTRS) {
|
|
4071
|
+
const v = readAttr(mod, attrID);
|
|
4072
|
+
if (v != null && v > 0) out.push(Math.round(v));
|
|
4073
|
+
}
|
|
4074
|
+
return out;
|
|
4075
|
+
}
|
|
4076
|
+
function moduleAcceptsAnyCharge(mod) {
|
|
4077
|
+
return chargeGroupsForModule(mod).length > 0;
|
|
4078
|
+
}
|
|
4079
|
+
function moduleAcceptsChargeType(mod, charge) {
|
|
4080
|
+
const allowedGroups = chargeGroupsForModule(mod);
|
|
4081
|
+
if (allowedGroups.length === 0) return false;
|
|
4082
|
+
if (!allowedGroups.includes(charge.groupID)) return false;
|
|
4083
|
+
const modSize = readAttr(mod, ATTR.CHARGE_SIZE);
|
|
4084
|
+
const chargeSize = readAttr(charge, ATTR.CHARGE_SIZE);
|
|
4085
|
+
if (modSize != null && chargeSize != null && Math.round(chargeSize) > Math.round(modSize)) {
|
|
4086
|
+
return false;
|
|
4087
|
+
}
|
|
4088
|
+
return true;
|
|
4089
|
+
}
|
|
4090
|
+
var ACTIVE_EFFECT_CATEGORIES = /* @__PURE__ */ new Set([1, 2, 3]);
|
|
4091
|
+
function isActivatableModule(mod, effects) {
|
|
4092
|
+
for (const e of mod.effects) {
|
|
4093
|
+
const eff = effects.get(e.id);
|
|
4094
|
+
if (eff && eff.effectCategoryID !== void 0 && ACTIVE_EFFECT_CATEGORIES.has(eff.effectCategoryID)) {
|
|
4095
|
+
return true;
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
return false;
|
|
4099
|
+
}
|
|
4100
|
+
var DEFAULT_OFFLINE_ACTIVATION_GROUPS = /* @__PURE__ */ new Set([
|
|
4101
|
+
330,
|
|
4102
|
+
4117
|
|
4103
|
+
]);
|
|
4104
|
+
function defaultStateForModule(mod, effects) {
|
|
4105
|
+
if (DEFAULT_OFFLINE_ACTIVATION_GROUPS.has(mod.groupID)) return "ONLINE";
|
|
4106
|
+
return isActivatableModule(mod, effects) ? "ACTIVE" : "ONLINE";
|
|
4107
|
+
}
|
|
4108
|
+
function freeHardpointsFor(mod, ship, fittedHiModules, dataset) {
|
|
4109
|
+
const turretCap = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
4110
|
+
const launcherCap = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
4111
|
+
let turretUsed = 0;
|
|
4112
|
+
let launcherUsed = 0;
|
|
4113
|
+
for (const m of fittedHiModules) {
|
|
4114
|
+
const t = dataset.getType(m.typeID);
|
|
4115
|
+
if (!t) continue;
|
|
4116
|
+
if (isTurretWeapon(t)) turretUsed++;
|
|
4117
|
+
else if (isMissileLauncher(t)) launcherUsed++;
|
|
4118
|
+
}
|
|
4119
|
+
let hardpointFree;
|
|
4120
|
+
if (isTurretWeapon(mod)) hardpointFree = Math.max(0, turretCap - turretUsed);
|
|
4121
|
+
else if (isMissileLauncher(mod)) hardpointFree = Math.max(0, launcherCap - launcherUsed);
|
|
4122
|
+
else hardpointFree = Number.POSITIVE_INFINITY;
|
|
4123
|
+
const groupFree = freeFitGroupSlotsFor(mod, fittedHiModules, dataset);
|
|
4124
|
+
return Math.min(hardpointFree, groupFree);
|
|
4125
|
+
}
|
|
4126
|
+
|
|
3598
4127
|
// src/derived/capacitor.ts
|
|
3599
4128
|
function computeCapacitor(ctx, dataset) {
|
|
3600
4129
|
const ship = ctx.ship;
|
|
@@ -3674,6 +4203,7 @@ var CAP_BOOSTER_EFFECT_ID = 48;
|
|
|
3674
4203
|
var ATTR_CAPACITOR_BONUS = 67;
|
|
3675
4204
|
var ATTR_CAPACITOR_NEED2 = 6;
|
|
3676
4205
|
var ATTR_DURATION_MS2 = 73;
|
|
4206
|
+
var ATTR_REACTIVATION_MS = 669;
|
|
3677
4207
|
var ATTR_RELOAD_TIME = 1795;
|
|
3678
4208
|
var ATTR_CAPACITY = 38;
|
|
3679
4209
|
var ATTR_VOLUME = 161;
|
|
@@ -3708,8 +4238,9 @@ function drainEntryFromEffect(effect, mod) {
|
|
|
3708
4238
|
if (effect.dischargeAttributeID === void 0) return null;
|
|
3709
4239
|
const discharge = mod.getFinal(effect.dischargeAttributeID, 0);
|
|
3710
4240
|
if (discharge <= 0) return null;
|
|
3711
|
-
const
|
|
3712
|
-
if (
|
|
4241
|
+
const baseCycleMs = effect.durationAttributeID !== void 0 ? mod.getFinal(effect.durationAttributeID, 0) : 1e3;
|
|
4242
|
+
if (baseCycleMs <= 0) return null;
|
|
4243
|
+
const cycleMs = Math.round(baseCycleMs + mod.getFinal(ATTR_REACTIVATION_MS, 0));
|
|
3713
4244
|
return {
|
|
3714
4245
|
cycleMs,
|
|
3715
4246
|
capNeed: discharge,
|
|
@@ -3717,7 +4248,12 @@ function drainEntryFromEffect(effect, mod) {
|
|
|
3717
4248
|
// ammo doesn't reload for cap purposes on regular modules
|
|
3718
4249
|
reloadMs: 0,
|
|
3719
4250
|
isInjector: false,
|
|
3720
|
-
|
|
4251
|
+
// Pyfa: `disableStagger = mod.hardpoint == TURRET`. Turrets fire as a
|
|
4252
|
+
// synchronized volley (their cap drains aggregate, capNeed × N at the
|
|
4253
|
+
// shared cycle); everything else is staggered evenly across its cycle.
|
|
4254
|
+
// Without this, N turrets were staggered (one drain at cycle/N) instead
|
|
4255
|
+
// of N together, shifting the cap timeline (time-to-empty off ~5-10 %).
|
|
4256
|
+
disableStagger: isTurretWeapon(mod.type)
|
|
3721
4257
|
};
|
|
3722
4258
|
}
|
|
3723
4259
|
function boosterDrainEntry(mod) {
|
|
@@ -3725,19 +4261,19 @@ function boosterDrainEntry(mod) {
|
|
|
3725
4261
|
if (!charge) return null;
|
|
3726
4262
|
const capNeed = mod.getFinal(ATTR_CAPACITOR_NEED2, 0);
|
|
3727
4263
|
const inject = charge.getFinal(ATTR_CAPACITOR_BONUS, 0);
|
|
3728
|
-
const cycleMs = mod.getFinal(ATTR_DURATION_MS2, 0);
|
|
4264
|
+
const cycleMs = Math.round(mod.getFinal(ATTR_DURATION_MS2, 0));
|
|
3729
4265
|
if (cycleMs <= 0) return null;
|
|
3730
|
-
let reloadMs = mod.getFinal(ATTR_RELOAD_TIME, 0);
|
|
4266
|
+
let reloadMs = Math.round(mod.getFinal(ATTR_RELOAD_TIME, 0));
|
|
3731
4267
|
if (reloadMs <= 0) reloadMs = 1e4;
|
|
3732
4268
|
const capacity = mod.getFinal(ATTR_CAPACITY, 0);
|
|
3733
4269
|
const chargeVol = charge.getFinal(ATTR_VOLUME, 0);
|
|
3734
|
-
const charges = capacity > 0 && chargeVol > 0 ? Math.floor(capacity / chargeVol) :
|
|
4270
|
+
const charges = capacity > 0 && chargeVol > 0 ? Math.floor(capacity / chargeVol) : 0;
|
|
3735
4271
|
return {
|
|
3736
4272
|
cycleMs,
|
|
3737
4273
|
// Pyfa convention: positive capNeed = drain, negative = injection.
|
|
3738
4274
|
// For boosters the *net* per-cycle change is (capNeed_module - inject_charge).
|
|
3739
4275
|
capNeed: capNeed - inject,
|
|
3740
|
-
clipSize:
|
|
4276
|
+
clipSize: charges,
|
|
3741
4277
|
reloadMs,
|
|
3742
4278
|
isInjector: true,
|
|
3743
4279
|
disableStagger: false
|
|
@@ -4669,8 +5205,8 @@ function computeFit(fit, dataset, opts) {
|
|
|
4669
5205
|
}
|
|
4670
5206
|
ctx.stoppedLocalEffectIDs = collectEffectStoppers(earlyProjected, dataset);
|
|
4671
5207
|
}
|
|
4672
|
-
for (const m of modules) applySourceItem(m, ctx, dataset);
|
|
4673
5208
|
for (const m of modules) if (m.charge) applySourceItem(m.charge, ctx, dataset);
|
|
5209
|
+
for (const m of modules) applySourceItem(m, ctx, dataset);
|
|
4674
5210
|
for (const d of drones) applySourceItem(d, ctx, dataset);
|
|
4675
5211
|
for (const f of fighters) applySourceItem(f, ctx, dataset);
|
|
4676
5212
|
for (const i of implants) applySourceItem(i, ctx, dataset);
|
|
@@ -4833,6 +5369,7 @@ function buildProjectionReport(source, ctx) {
|
|
|
4833
5369
|
summary: labels[cls.kind] ?? cls.kind
|
|
4834
5370
|
};
|
|
4835
5371
|
}
|
|
5372
|
+
var CHAR_BASE_MAX_LOCKED_TARGETS = 2;
|
|
4836
5373
|
function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
4837
5374
|
const ship = ctx.ship;
|
|
4838
5375
|
const slotUsed = { HI: 0, MED: 0, LO: 0, RIG: 0, SUBSYSTEM: 0, SERVICE: 0 };
|
|
@@ -4953,7 +5490,18 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
4953
5490
|
ship.getFinal(ATTR.MAX_TARGET_RANGE, 0),
|
|
4954
5491
|
ship.getFinal(ATTR.MAX_TARGET_RANGE_CAP, 3e5)
|
|
4955
5492
|
),
|
|
4956
|
-
|
|
5493
|
+
// In-game lockable-target count is the LOWER of the ship's
|
|
5494
|
+
// maxLockedTargets and the pilot's skill-derived cap. Pyfa:
|
|
5495
|
+
// ceil(min(maxTargetsLockedFromSkills, ship maxLockedTargets)),
|
|
5496
|
+
// where maxTargetsLockedFromSkills seeds at a base of 2 (every
|
|
5497
|
+
// capsuleer locks 2 without skills) + Target Management / Advanced
|
|
5498
|
+
// Target Management (+1/level each) → 2 + 5 + 5 = 12 at All-V. The
|
|
5499
|
+
// character item carries only the skill ModAdds (10), so add the
|
|
5500
|
+
// base 2. Without the char cap, high-slot capitals reported 14-20.
|
|
5501
|
+
maxLockedTargets: Math.ceil(Math.min(
|
|
5502
|
+
CHAR_BASE_MAX_LOCKED_TARGETS + ctx.character.getFinal(ATTR.MAX_LOCKED_TARGETS, 0),
|
|
5503
|
+
ship.getFinal(ATTR.MAX_LOCKED_TARGETS, 0)
|
|
5504
|
+
)),
|
|
4957
5505
|
signatureRadius: ship.getFinal(ATTR.SIGNATURE_RADIUS, 0),
|
|
4958
5506
|
scanResolution: ship.getFinal(ATTR.SCAN_RESOLUTION, 0),
|
|
4959
5507
|
sensorStrength: pickSensorStrength(ship).value,
|
|
@@ -5213,176 +5761,6 @@ function tempId() {
|
|
|
5213
5761
|
return `tmp:${++tempCounter}:${Date.now().toString(36)}`;
|
|
5214
5762
|
}
|
|
5215
5763
|
|
|
5216
|
-
// src/fitChecks.ts
|
|
5217
|
-
function readAttr(t, id) {
|
|
5218
|
-
return t.attributes.find((a) => a.id === id)?.v;
|
|
5219
|
-
}
|
|
5220
|
-
function typeFitsSlotType(t, slot) {
|
|
5221
|
-
for (const e of t.effects) {
|
|
5222
|
-
const mapped = SLOT_EFFECT_TO_SLOT_TYPE[e.id];
|
|
5223
|
-
if (mapped === slot) return true;
|
|
5224
|
-
}
|
|
5225
|
-
return false;
|
|
5226
|
-
}
|
|
5227
|
-
function isTurretWeapon(t) {
|
|
5228
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "TURRET");
|
|
5229
|
-
}
|
|
5230
|
-
function isMissileLauncher(t) {
|
|
5231
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "MISSILE");
|
|
5232
|
-
}
|
|
5233
|
-
function isSmartBomb(t) {
|
|
5234
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "SMARTBOMB");
|
|
5235
|
-
}
|
|
5236
|
-
function shipGroupRestrictions(t) {
|
|
5237
|
-
const out = [];
|
|
5238
|
-
for (const id of CAN_FIT_SHIP_GROUP_ATTRS) {
|
|
5239
|
-
const v = readAttr(t, id);
|
|
5240
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5241
|
-
}
|
|
5242
|
-
return out;
|
|
5243
|
-
}
|
|
5244
|
-
function shipTypeRestrictions(t) {
|
|
5245
|
-
const out = [];
|
|
5246
|
-
for (const id of CAN_FIT_SHIP_TYPE_ATTRS) {
|
|
5247
|
-
const v = readAttr(t, id);
|
|
5248
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5249
|
-
}
|
|
5250
|
-
return out;
|
|
5251
|
-
}
|
|
5252
|
-
function maxGroupFittedFor(mod) {
|
|
5253
|
-
const v = readAttr(mod, ATTR.MAX_GROUP_FITTED);
|
|
5254
|
-
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
5255
|
-
}
|
|
5256
|
-
function maxTypeFittedFor(mod) {
|
|
5257
|
-
const v = readAttr(mod, ATTR.MAX_TYPE_FITTED);
|
|
5258
|
-
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
5259
|
-
}
|
|
5260
|
-
function freeFitGroupSlotsFor(mod, fittedModules, dataset) {
|
|
5261
|
-
const groupCap = maxGroupFittedFor(mod);
|
|
5262
|
-
const typeCap = maxTypeFittedFor(mod);
|
|
5263
|
-
if (groupCap === void 0 && typeCap === void 0) return Number.POSITIVE_INFINITY;
|
|
5264
|
-
let groupCount = 0;
|
|
5265
|
-
let typeCount = 0;
|
|
5266
|
-
for (const fm of fittedModules) {
|
|
5267
|
-
if (typeCap !== void 0 && fm.typeID === mod.id) typeCount++;
|
|
5268
|
-
if (groupCap !== void 0) {
|
|
5269
|
-
const t = dataset.getType(fm.typeID);
|
|
5270
|
-
if (t && t.groupID === mod.groupID) groupCount++;
|
|
5271
|
-
}
|
|
5272
|
-
}
|
|
5273
|
-
let free = Number.POSITIVE_INFINITY;
|
|
5274
|
-
if (groupCap !== void 0) free = Math.min(free, groupCap - groupCount);
|
|
5275
|
-
if (typeCap !== void 0) free = Math.min(free, typeCap - typeCount);
|
|
5276
|
-
return Math.max(0, free);
|
|
5277
|
-
}
|
|
5278
|
-
function canFitModuleOnShip(mod, ship, slot, fitContext) {
|
|
5279
|
-
if (!typeFitsSlotType(mod, slot)) {
|
|
5280
|
-
return { ok: false, reason: `Module does not declare a ${slot} slot effect` };
|
|
5281
|
-
}
|
|
5282
|
-
if (!ship) return { ok: true };
|
|
5283
|
-
const allowedGroups = shipGroupRestrictions(mod);
|
|
5284
|
-
const allowedTypes = shipTypeRestrictions(mod);
|
|
5285
|
-
if (allowedGroups.length > 0 || allowedTypes.length > 0) {
|
|
5286
|
-
const groupOk = allowedGroups.includes(ship.groupID);
|
|
5287
|
-
const typeOk = allowedTypes.includes(ship.id);
|
|
5288
|
-
if (!groupOk && !typeOk) {
|
|
5289
|
-
return { ok: false, reason: "Ship hull/group not allowed by this module" };
|
|
5290
|
-
}
|
|
5291
|
-
}
|
|
5292
|
-
if (slot === "RIG") {
|
|
5293
|
-
const modSize = readAttr(mod, ATTR.RIG_SIZE);
|
|
5294
|
-
const shipSize = readAttr(ship, ATTR.RIG_SIZE);
|
|
5295
|
-
if (modSize != null && shipSize != null && Math.round(modSize) !== Math.round(shipSize)) {
|
|
5296
|
-
return { ok: false, reason: "Rig size mismatch" };
|
|
5297
|
-
}
|
|
5298
|
-
}
|
|
5299
|
-
if (slot === "SUBSYSTEM") {
|
|
5300
|
-
const fitsTo = readAttr(mod, 1380);
|
|
5301
|
-
if (fitsTo != null && fitsTo > 0 && Math.round(fitsTo) !== ship.id) {
|
|
5302
|
-
return { ok: false, reason: "Subsystem locked to a different T3C hull" };
|
|
5303
|
-
}
|
|
5304
|
-
}
|
|
5305
|
-
if (slot === "HI") {
|
|
5306
|
-
if (isTurretWeapon(mod)) {
|
|
5307
|
-
const turrets = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
5308
|
-
if (turrets <= 0) {
|
|
5309
|
-
return { ok: false, reason: "Ship has no turret hardpoints" };
|
|
5310
|
-
}
|
|
5311
|
-
} else if (isMissileLauncher(mod)) {
|
|
5312
|
-
const launchers = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
5313
|
-
if (launchers <= 0) {
|
|
5314
|
-
return { ok: false, reason: "Ship has no launcher hardpoints" };
|
|
5315
|
-
}
|
|
5316
|
-
}
|
|
5317
|
-
}
|
|
5318
|
-
if (fitContext) {
|
|
5319
|
-
const free = freeFitGroupSlotsFor(mod, fitContext.fittedModules, fitContext.dataset);
|
|
5320
|
-
if (free <= 0) {
|
|
5321
|
-
const cap = maxTypeFittedFor(mod) ?? maxGroupFittedFor(mod);
|
|
5322
|
-
return { ok: false, reason: `Only ${cap} of this module type can be fitted to a ship` };
|
|
5323
|
-
}
|
|
5324
|
-
}
|
|
5325
|
-
return { ok: true };
|
|
5326
|
-
}
|
|
5327
|
-
function chargeGroupsForModule(mod) {
|
|
5328
|
-
const out = [];
|
|
5329
|
-
for (const attrID of CHARGE_GROUP_ATTRS) {
|
|
5330
|
-
const v = readAttr(mod, attrID);
|
|
5331
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5332
|
-
}
|
|
5333
|
-
return out;
|
|
5334
|
-
}
|
|
5335
|
-
function moduleAcceptsAnyCharge(mod) {
|
|
5336
|
-
return chargeGroupsForModule(mod).length > 0;
|
|
5337
|
-
}
|
|
5338
|
-
function moduleAcceptsChargeType(mod, charge) {
|
|
5339
|
-
const allowedGroups = chargeGroupsForModule(mod);
|
|
5340
|
-
if (allowedGroups.length === 0) return false;
|
|
5341
|
-
if (!allowedGroups.includes(charge.groupID)) return false;
|
|
5342
|
-
const modSize = readAttr(mod, ATTR.CHARGE_SIZE);
|
|
5343
|
-
const chargeSize = readAttr(charge, ATTR.CHARGE_SIZE);
|
|
5344
|
-
if (modSize != null && chargeSize != null && Math.round(chargeSize) > Math.round(modSize)) {
|
|
5345
|
-
return false;
|
|
5346
|
-
}
|
|
5347
|
-
return true;
|
|
5348
|
-
}
|
|
5349
|
-
var ACTIVE_EFFECT_CATEGORIES = /* @__PURE__ */ new Set([1, 2, 3]);
|
|
5350
|
-
function isActivatableModule(mod, effects) {
|
|
5351
|
-
for (const e of mod.effects) {
|
|
5352
|
-
const eff = effects.get(e.id);
|
|
5353
|
-
if (eff && eff.effectCategoryID !== void 0 && ACTIVE_EFFECT_CATEGORIES.has(eff.effectCategoryID)) {
|
|
5354
|
-
return true;
|
|
5355
|
-
}
|
|
5356
|
-
}
|
|
5357
|
-
return false;
|
|
5358
|
-
}
|
|
5359
|
-
var DEFAULT_OFFLINE_ACTIVATION_GROUPS = /* @__PURE__ */ new Set([
|
|
5360
|
-
330,
|
|
5361
|
-
4117
|
|
5362
|
-
]);
|
|
5363
|
-
function defaultStateForModule(mod, effects) {
|
|
5364
|
-
if (DEFAULT_OFFLINE_ACTIVATION_GROUPS.has(mod.groupID)) return "ONLINE";
|
|
5365
|
-
return isActivatableModule(mod, effects) ? "ACTIVE" : "ONLINE";
|
|
5366
|
-
}
|
|
5367
|
-
function freeHardpointsFor(mod, ship, fittedHiModules, dataset) {
|
|
5368
|
-
const turretCap = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
5369
|
-
const launcherCap = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
5370
|
-
let turretUsed = 0;
|
|
5371
|
-
let launcherUsed = 0;
|
|
5372
|
-
for (const m of fittedHiModules) {
|
|
5373
|
-
const t = dataset.getType(m.typeID);
|
|
5374
|
-
if (!t) continue;
|
|
5375
|
-
if (isTurretWeapon(t)) turretUsed++;
|
|
5376
|
-
else if (isMissileLauncher(t)) launcherUsed++;
|
|
5377
|
-
}
|
|
5378
|
-
let hardpointFree;
|
|
5379
|
-
if (isTurretWeapon(mod)) hardpointFree = Math.max(0, turretCap - turretUsed);
|
|
5380
|
-
else if (isMissileLauncher(mod)) hardpointFree = Math.max(0, launcherCap - launcherUsed);
|
|
5381
|
-
else hardpointFree = Number.POSITIVE_INFINITY;
|
|
5382
|
-
const groupFree = freeFitGroupSlotsFor(mod, fittedHiModules, dataset);
|
|
5383
|
-
return Math.min(hardpointFree, groupFree);
|
|
5384
|
-
}
|
|
5385
|
-
|
|
5386
5764
|
// src/eft/format.ts
|
|
5387
5765
|
var SLOT_ORDER = ["LO", "MED", "HI", "RIG"];
|
|
5388
5766
|
function formatEft(fit, dataset) {
|