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/index.js
CHANGED
|
@@ -647,7 +647,7 @@ var FitContext = class {
|
|
|
647
647
|
const root = this.resolveDomain(modifier.domain, source);
|
|
648
648
|
switch (modifier.func) {
|
|
649
649
|
case "ItemModifier":
|
|
650
|
-
if (modifier.domain === "charID" && source.kind === "module") {
|
|
650
|
+
if (modifier.domain === "charID" && source.kind === "module" && modifier.modifiedAttributeID === 212) {
|
|
651
651
|
const out = [];
|
|
652
652
|
for (const m of this.modules) {
|
|
653
653
|
if (m.charge) out.push(m.charge);
|
|
@@ -1059,6 +1059,299 @@ function makeModeState(modeTypeID, type) {
|
|
|
1059
1059
|
return new ItemState({ kind: "mode", id: `mode:${modeTypeID}`, type, state: "ACTIVE" });
|
|
1060
1060
|
}
|
|
1061
1061
|
|
|
1062
|
+
// src/stackingGroups.ts
|
|
1063
|
+
var STACKING_PENALTY_GROUPS = /* @__PURE__ */ new Map([
|
|
1064
|
+
[89, "default"],
|
|
1065
|
+
[91, "default"],
|
|
1066
|
+
[92, "default"],
|
|
1067
|
+
[93, "default"],
|
|
1068
|
+
[95, "default"],
|
|
1069
|
+
[96, "default"],
|
|
1070
|
+
[394, "default"],
|
|
1071
|
+
[395, "default"],
|
|
1072
|
+
[494, "default"],
|
|
1073
|
+
[607, "postMul"],
|
|
1074
|
+
[657, "default"],
|
|
1075
|
+
[699, "default"],
|
|
1076
|
+
[763, "default"],
|
|
1077
|
+
[784, "default"],
|
|
1078
|
+
[854, "cloakingScanResolutionMultiplier"],
|
|
1079
|
+
[856, "default"],
|
|
1080
|
+
[889, "default"],
|
|
1081
|
+
[891, "default"],
|
|
1082
|
+
[892, "default"],
|
|
1083
|
+
[1024, "default"],
|
|
1084
|
+
[1230, "default"],
|
|
1085
|
+
[1281, "default"],
|
|
1086
|
+
[1318, "default"],
|
|
1087
|
+
[1445, "default"],
|
|
1088
|
+
[1446, "default"],
|
|
1089
|
+
[1448, "default"],
|
|
1090
|
+
[1452, "default"],
|
|
1091
|
+
[1472, "default"],
|
|
1092
|
+
[1590, "default"],
|
|
1093
|
+
[1617, "default"],
|
|
1094
|
+
[1720, "default"],
|
|
1095
|
+
[1764, "default"],
|
|
1096
|
+
[1885, "postPerc"],
|
|
1097
|
+
[1886, "postPerc"],
|
|
1098
|
+
[2013, "default"],
|
|
1099
|
+
[2014, "default"],
|
|
1100
|
+
[2019, "default"],
|
|
1101
|
+
[2020, "default"],
|
|
1102
|
+
[2041, "default"],
|
|
1103
|
+
[2052, "default"],
|
|
1104
|
+
[2152, "default"],
|
|
1105
|
+
[2232, "default"],
|
|
1106
|
+
[2302, "preMul"],
|
|
1107
|
+
[2644, "default"],
|
|
1108
|
+
[2645, "default"],
|
|
1109
|
+
[2646, "default"],
|
|
1110
|
+
[2670, "default"],
|
|
1111
|
+
[2693, "default"],
|
|
1112
|
+
[2694, "default"],
|
|
1113
|
+
[2695, "default"],
|
|
1114
|
+
[2696, "default"],
|
|
1115
|
+
[2697, "default"],
|
|
1116
|
+
[2698, "default"],
|
|
1117
|
+
[2716, "default"],
|
|
1118
|
+
[2717, "default"],
|
|
1119
|
+
[2792, "default"],
|
|
1120
|
+
[2795, "default"],
|
|
1121
|
+
[2796, "default"],
|
|
1122
|
+
[2797, "default"],
|
|
1123
|
+
[2798, "default"],
|
|
1124
|
+
[2799, "default"],
|
|
1125
|
+
[2801, "default"],
|
|
1126
|
+
[2802, "default"],
|
|
1127
|
+
[2803, "default"],
|
|
1128
|
+
[2804, "default"],
|
|
1129
|
+
[2851, "default"],
|
|
1130
|
+
[2858, "default"],
|
|
1131
|
+
[2865, "default"],
|
|
1132
|
+
[2867, "default"],
|
|
1133
|
+
[2868, "default"],
|
|
1134
|
+
[3001, "postPerc"],
|
|
1135
|
+
[3046, "default"],
|
|
1136
|
+
[3174, "default"],
|
|
1137
|
+
[3175, "default"],
|
|
1138
|
+
[3182, "default"],
|
|
1139
|
+
[3200, "default"],
|
|
1140
|
+
[3201, "default"],
|
|
1141
|
+
[3586, "default"],
|
|
1142
|
+
[3655, "default"],
|
|
1143
|
+
[3656, "default"],
|
|
1144
|
+
[3657, "default"],
|
|
1145
|
+
[3659, "default"],
|
|
1146
|
+
[3674, "default"],
|
|
1147
|
+
[3726, "default"],
|
|
1148
|
+
[3727, "default"],
|
|
1149
|
+
[3993, "postMul"],
|
|
1150
|
+
[3995, "postMul"],
|
|
1151
|
+
[3996, "default"],
|
|
1152
|
+
[3997, "default"],
|
|
1153
|
+
[3998, "default"],
|
|
1154
|
+
[3999, "default"],
|
|
1155
|
+
[4002, "postMul"],
|
|
1156
|
+
[4003, "postMul"],
|
|
1157
|
+
[4016, "default"],
|
|
1158
|
+
[4017, "postMul"],
|
|
1159
|
+
[4018, "postMul"],
|
|
1160
|
+
[4019, "postMul"],
|
|
1161
|
+
[4020, "postMul"],
|
|
1162
|
+
[4021, "postMul"],
|
|
1163
|
+
[4022, "postMul"],
|
|
1164
|
+
[4023, "postMul"],
|
|
1165
|
+
[4054, "default"],
|
|
1166
|
+
[4055, "default"],
|
|
1167
|
+
[4056, "default"],
|
|
1168
|
+
[4057, "postMul"],
|
|
1169
|
+
[4058, "postMul"],
|
|
1170
|
+
[4059, "postMul"],
|
|
1171
|
+
[4060, "postMul"],
|
|
1172
|
+
[4061, "postMul"],
|
|
1173
|
+
[4062, "postMul"],
|
|
1174
|
+
[4063, "postMul"],
|
|
1175
|
+
[4086, "postMul"],
|
|
1176
|
+
[4088, "postMul"],
|
|
1177
|
+
[4089, "postMul"],
|
|
1178
|
+
[4135, "default"],
|
|
1179
|
+
[4136, "default"],
|
|
1180
|
+
[4137, "default"],
|
|
1181
|
+
[4138, "default"],
|
|
1182
|
+
[4162, "default"],
|
|
1183
|
+
[4280, "default"],
|
|
1184
|
+
[4358, "default"],
|
|
1185
|
+
[4464, "default"],
|
|
1186
|
+
[4489, "default"],
|
|
1187
|
+
[4490, "default"],
|
|
1188
|
+
[4491, "default"],
|
|
1189
|
+
[4492, "default"],
|
|
1190
|
+
[4527, "default"],
|
|
1191
|
+
[4559, "default"],
|
|
1192
|
+
[4575, "default"],
|
|
1193
|
+
[4809, "default"],
|
|
1194
|
+
[4810, "default"],
|
|
1195
|
+
[4811, "default"],
|
|
1196
|
+
[4812, "default"],
|
|
1197
|
+
[4906, "postMul"],
|
|
1198
|
+
[4928, "preMul"],
|
|
1199
|
+
[4961, "postMul"],
|
|
1200
|
+
[5081, "default"],
|
|
1201
|
+
[5188, "default"],
|
|
1202
|
+
[5189, "default"],
|
|
1203
|
+
[5190, "default"],
|
|
1204
|
+
[5213, "default"],
|
|
1205
|
+
[5214, "default"],
|
|
1206
|
+
[5230, "default"],
|
|
1207
|
+
[5231, "default"],
|
|
1208
|
+
[5397, "default"],
|
|
1209
|
+
[5399, "default"],
|
|
1210
|
+
[5440, "postMul"],
|
|
1211
|
+
[5468, "default"],
|
|
1212
|
+
[5560, "default"],
|
|
1213
|
+
[5618, "postPerc"],
|
|
1214
|
+
[5757, "default"],
|
|
1215
|
+
[5867, "default"],
|
|
1216
|
+
[5911, "default"],
|
|
1217
|
+
[5912, "postMul"],
|
|
1218
|
+
[5914, "postMul"],
|
|
1219
|
+
[5915, "postMul"],
|
|
1220
|
+
[5916, "postMul"],
|
|
1221
|
+
[5917, "postMul"],
|
|
1222
|
+
[5918, "postMul"],
|
|
1223
|
+
[5919, "postMul"],
|
|
1224
|
+
[5920, "postMul"],
|
|
1225
|
+
[5921, "postMul"],
|
|
1226
|
+
[5922, "postMul"],
|
|
1227
|
+
[5923, "postMul"],
|
|
1228
|
+
[5924, "postMul"],
|
|
1229
|
+
[5925, "postMul"],
|
|
1230
|
+
[5926, "postMul"],
|
|
1231
|
+
[5927, "postMul"],
|
|
1232
|
+
[5929, "postMul"],
|
|
1233
|
+
[5951, "default"],
|
|
1234
|
+
[5998, "default"],
|
|
1235
|
+
[6010, "postDiv"],
|
|
1236
|
+
[6011, "postDiv"],
|
|
1237
|
+
[6012, "postDiv"],
|
|
1238
|
+
[6014, "postDiv"],
|
|
1239
|
+
[6015, "postDiv"],
|
|
1240
|
+
[6016, "postDiv"],
|
|
1241
|
+
[6017, "postDiv"],
|
|
1242
|
+
[6039, "postDiv"],
|
|
1243
|
+
[6040, "postDiv"],
|
|
1244
|
+
[6041, "postDiv"],
|
|
1245
|
+
[6063, "default"],
|
|
1246
|
+
[6076, "postDiv"],
|
|
1247
|
+
[6110, "default"],
|
|
1248
|
+
[6111, "default"],
|
|
1249
|
+
[6112, "default"],
|
|
1250
|
+
[6113, "default"],
|
|
1251
|
+
[6135, "default"],
|
|
1252
|
+
[6152, "postDiv"],
|
|
1253
|
+
[6154, "postDiv"],
|
|
1254
|
+
[6164, "default"],
|
|
1255
|
+
[6201, "default"],
|
|
1256
|
+
[6208, "default"],
|
|
1257
|
+
[6402, "default"],
|
|
1258
|
+
[6403, "default"],
|
|
1259
|
+
[6404, "default"],
|
|
1260
|
+
[6405, "default"],
|
|
1261
|
+
[6406, "default"],
|
|
1262
|
+
[6409, "default"],
|
|
1263
|
+
[6410, "default"],
|
|
1264
|
+
[6411, "default"],
|
|
1265
|
+
[6412, "default"],
|
|
1266
|
+
[6422, "default"],
|
|
1267
|
+
[6423, "default"],
|
|
1268
|
+
[6424, "default"],
|
|
1269
|
+
[6425, "default"],
|
|
1270
|
+
[6426, "default"],
|
|
1271
|
+
[6427, "default"],
|
|
1272
|
+
[6428, "default"],
|
|
1273
|
+
[6435, "default"],
|
|
1274
|
+
[6439, "default"],
|
|
1275
|
+
[6440, "default"],
|
|
1276
|
+
[6441, "default"],
|
|
1277
|
+
[6448, "default"],
|
|
1278
|
+
[6449, "default"],
|
|
1279
|
+
[6472, "default"],
|
|
1280
|
+
[6473, "default"],
|
|
1281
|
+
[6474, "default"],
|
|
1282
|
+
[6476, "default"],
|
|
1283
|
+
[6478, "default"],
|
|
1284
|
+
[6479, "default"],
|
|
1285
|
+
[6481, "default"],
|
|
1286
|
+
[6484, "postMul"],
|
|
1287
|
+
[6487, "default"],
|
|
1288
|
+
[6555, "default"],
|
|
1289
|
+
[6556, "default"],
|
|
1290
|
+
[6557, "default"],
|
|
1291
|
+
[6559, "default"],
|
|
1292
|
+
[6566, "postMul"],
|
|
1293
|
+
[6567, "default"],
|
|
1294
|
+
[6581, "default"],
|
|
1295
|
+
[6582, "postPercent"],
|
|
1296
|
+
[6658, "preMul"],
|
|
1297
|
+
[6670, "default"],
|
|
1298
|
+
[6671, "default"],
|
|
1299
|
+
[6682, "default"],
|
|
1300
|
+
[6683, "default"],
|
|
1301
|
+
[6684, "default"],
|
|
1302
|
+
[6686, "default"],
|
|
1303
|
+
[6690, "default"],
|
|
1304
|
+
[6692, "default"],
|
|
1305
|
+
[6693, "default"],
|
|
1306
|
+
[6694, "default"],
|
|
1307
|
+
[6727, "default"],
|
|
1308
|
+
[6730, "postMul"],
|
|
1309
|
+
[6731, "postMul"],
|
|
1310
|
+
[6796, "postDiv"],
|
|
1311
|
+
[6797, "postDiv"],
|
|
1312
|
+
[6798, "postDiv"],
|
|
1313
|
+
[6799, "postDiv"],
|
|
1314
|
+
[6801, "postDiv"],
|
|
1315
|
+
[6877, "default"],
|
|
1316
|
+
[7029, "default"],
|
|
1317
|
+
[7077, "default"],
|
|
1318
|
+
[7078, "default"],
|
|
1319
|
+
[7098, "default"],
|
|
1320
|
+
[7111, "default"],
|
|
1321
|
+
[7142, "default"],
|
|
1322
|
+
[7202, "default"],
|
|
1323
|
+
[7203, "default"],
|
|
1324
|
+
[7223, "default"],
|
|
1325
|
+
[7237, "default"],
|
|
1326
|
+
[8033, "default"],
|
|
1327
|
+
[8057, "default"],
|
|
1328
|
+
[8076, "default"],
|
|
1329
|
+
[8082, "default"],
|
|
1330
|
+
[8108, "postMul"],
|
|
1331
|
+
[8109, "postMul"],
|
|
1332
|
+
[8111, "default"],
|
|
1333
|
+
[8112, "default"],
|
|
1334
|
+
[8113, "postMul"],
|
|
1335
|
+
[8114, "postMul"],
|
|
1336
|
+
[8119, "default"],
|
|
1337
|
+
[11445, "default"],
|
|
1338
|
+
[11691, "default"],
|
|
1339
|
+
[11946, "default"],
|
|
1340
|
+
[11947, "postMul"],
|
|
1341
|
+
[11948, "postMul"],
|
|
1342
|
+
[11953, "postMul"],
|
|
1343
|
+
[12126, "default"],
|
|
1344
|
+
[12597, "default"],
|
|
1345
|
+
[12761, "default"],
|
|
1346
|
+
[12794, "postDiv"],
|
|
1347
|
+
[12795, "postDiv"],
|
|
1348
|
+
[12796, "postDiv"],
|
|
1349
|
+
[12798, "postDiv"],
|
|
1350
|
+
[12799, "postDiv"],
|
|
1351
|
+
[12838, "postMul"],
|
|
1352
|
+
[12839, "postMul"]
|
|
1353
|
+
]);
|
|
1354
|
+
|
|
1062
1355
|
// src/modifierEngine.ts
|
|
1063
1356
|
var NO_PENALTY_KINDS = /* @__PURE__ */ new Set([
|
|
1064
1357
|
"skill",
|
|
@@ -1073,16 +1366,21 @@ function scaleForPipeline(rawValue, _unitID, op) {
|
|
|
1073
1366
|
}
|
|
1074
1367
|
function applySourceItem(source, ctx, dataset) {
|
|
1075
1368
|
const isLocalModule = source.kind === "module";
|
|
1369
|
+
const selfMods = [];
|
|
1370
|
+
const outMods = [];
|
|
1076
1371
|
for (const eid of source.effectIDs) {
|
|
1077
1372
|
if (LEGACY_HANDLED_EFFECT_IDS.has(eid)) continue;
|
|
1373
|
+
if (SEC_STATUS_SCALED_EFFECT_IDS.has(eid)) continue;
|
|
1078
1374
|
if (isLocalModule && ctx.stoppedLocalEffectIDs.has(eid)) continue;
|
|
1079
1375
|
const effect = dataset.effects.get(eid);
|
|
1080
1376
|
if (!effect) continue;
|
|
1081
1377
|
if (!source.appliesAtState(effect)) continue;
|
|
1082
1378
|
for (const mi of effect.modifierInfo) {
|
|
1083
|
-
|
|
1379
|
+
(mi.domain === "itemID" ? selfMods : outMods).push({ effect, mi });
|
|
1084
1380
|
}
|
|
1085
1381
|
}
|
|
1382
|
+
for (const { effect, mi } of selfMods) applyOneModifier(source, effect, mi, ctx, dataset);
|
|
1383
|
+
for (const { effect, mi } of outMods) applyOneModifier(source, effect, mi, ctx, dataset);
|
|
1086
1384
|
}
|
|
1087
1385
|
function collectEffectStoppers(projectedSources, dataset) {
|
|
1088
1386
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -1148,7 +1446,7 @@ function applyOneModifier(source, effect, mi, ctx, dataset) {
|
|
|
1148
1446
|
if (computed === null) return;
|
|
1149
1447
|
const targets = ctx.targetsForModifier(mi, source);
|
|
1150
1448
|
if (targets.length === 0) return;
|
|
1151
|
-
const stackingGroup = computeStackingGroup(source, mi, dataset);
|
|
1449
|
+
const stackingGroup = computeStackingGroup(source, mi, dataset, effect);
|
|
1152
1450
|
const isMul = op === "PreMul" || op === "PostMul" || op === "PreDiv" || op === "PostDiv";
|
|
1153
1451
|
const value = isMul && computed.scaled ? 1 + computed.value : computed.value;
|
|
1154
1452
|
const sdeDefault = isMul ? dataset.attributes.get(mi.modifiedAttributeID)?.defaultValue ?? 0 : 0;
|
|
@@ -1656,7 +1954,52 @@ var SHIP_BONUS_SCALING_SKILL = /* @__PURE__ */ new Map([
|
|
|
1656
1954
|
[6112, 24313],
|
|
1657
1955
|
[6113, 24312],
|
|
1658
1956
|
[6114, 24311],
|
|
1659
|
-
[6116, 24314]
|
|
1957
|
+
[6116, 24314],
|
|
1958
|
+
// ----- Auto-derived from the SDE: attrs that are BOTH skill-level-scaled
|
|
1959
|
+
// (a skill effect does `attr ×= skillLevel` via attr 280) AND read by a
|
|
1960
|
+
// SHIP-side effect as the bonus value. These are per-racial/role-skill hull
|
|
1961
|
+
// bonuses whose ship-side reader must scale by the skill level. Previously
|
|
1962
|
+
// missing → the bonus was taken at base (×1) instead of ×5 at All-V.
|
|
1963
|
+
// Notable: Exhumer/Barge shield+armor resist role bonuses (Hulk/Skiff/
|
|
1964
|
+
// Mackinaw shield resist 4 %→20 %), Bhaalgorn drone+laser (492), industrial
|
|
1965
|
+
// command (Orca/Rorqual), Marauder/pirate/expedition hull bonuses.
|
|
1966
|
+
[66, 89611],
|
|
1967
|
+
[310, 3432],
|
|
1968
|
+
[349, 19760],
|
|
1969
|
+
[492, 3339],
|
|
1970
|
+
[1296, 21610],
|
|
1971
|
+
[1669, 3184],
|
|
1972
|
+
[1670, 3184],
|
|
1973
|
+
[1842, 32918],
|
|
1974
|
+
[3167, 33856],
|
|
1975
|
+
[3181, 17940],
|
|
1976
|
+
[3182, 17940],
|
|
1977
|
+
[3183, 17940],
|
|
1978
|
+
[3184, 17940],
|
|
1979
|
+
[3185, 17940],
|
|
1980
|
+
[3187, 17940],
|
|
1981
|
+
[3188, 17940],
|
|
1982
|
+
[3190, 33856],
|
|
1983
|
+
[3191, 33856],
|
|
1984
|
+
[3192, 33856],
|
|
1985
|
+
[3193, 22551],
|
|
1986
|
+
[3194, 22551],
|
|
1987
|
+
[3197, 22551],
|
|
1988
|
+
[3198, 22551],
|
|
1989
|
+
[3199, 22551],
|
|
1990
|
+
[3203, 29637],
|
|
1991
|
+
[3204, 29637],
|
|
1992
|
+
[3205, 29637],
|
|
1993
|
+
[3210, 3341],
|
|
1994
|
+
[3221, 29637],
|
|
1995
|
+
[3222, 29637],
|
|
1996
|
+
[3223, 28374],
|
|
1997
|
+
[3224, 28374],
|
|
1998
|
+
[3237, 32918],
|
|
1999
|
+
[3240, 32918],
|
|
2000
|
+
[3326, 28374],
|
|
2001
|
+
[6088, 33092],
|
|
2002
|
+
[6089, 33094]
|
|
1660
2003
|
]);
|
|
1661
2004
|
var SUBSYSTEM_BONUS_SCALING_SKILL = /* @__PURE__ */ new Map([
|
|
1662
2005
|
// Amarr
|
|
@@ -1711,8 +2054,42 @@ var SUBSYSTEM_BONUS_SCALING_SKILL = /* @__PURE__ */ new Map([
|
|
|
1711
2054
|
// MinmatarDefensive
|
|
1712
2055
|
[1449, 30551],
|
|
1713
2056
|
// MinmatarOffensive
|
|
1714
|
-
[1450, 30554]
|
|
2057
|
+
[1450, 30554],
|
|
1715
2058
|
// MinmatarPropulsion
|
|
2059
|
+
// ----- Authoritative completion: every subsystem-bonus attr that the SDE
|
|
2060
|
+
// ----- scales by a racial subsystem skill, derived verbatim from the
|
|
2061
|
+
// ----- `subsystemSkillLevel*` skill effects (modAttr = bonus attr,
|
|
2062
|
+
// ----- modifying = 280/skillLevel, PreMul). The Amarr/Caldari secondaries
|
|
2063
|
+
// ----- above were hand-added; Gallente + Minmatar secondaries (and the
|
|
2064
|
+
// ----- 2680-2687 defensive/core block) were MISSING, so subsystem-sourced
|
|
2065
|
+
// ----- bonuses on those races (e.g. Loki Propulsion agility 1523, Offensive
|
|
2066
|
+
// ----- RoF 1522 / 1534) fell to the flat path and applied ×1 instead of ×5.
|
|
2067
|
+
// ----- Duplicates of the entries above are harmless (same value).
|
|
2068
|
+
[1517, 30540],
|
|
2069
|
+
[1519, 30546],
|
|
2070
|
+
[1520, 30553],
|
|
2071
|
+
[1521, 30550],
|
|
2072
|
+
// Gallente Def/Core/Prop/Off secondaries
|
|
2073
|
+
[1522, 30551],
|
|
2074
|
+
[1523, 30554],
|
|
2075
|
+
[1525, 30547],
|
|
2076
|
+
[1526, 30545],
|
|
2077
|
+
// Minmatar Off/Prop/Core/Def secondaries
|
|
2078
|
+
[1531, 30537],
|
|
2079
|
+
[1532, 30550],
|
|
2080
|
+
[1533, 30549],
|
|
2081
|
+
[1534, 30551],
|
|
2082
|
+
// Off cross-race tertiaries (Amarr/Gallente/Caldari/Minmatar)
|
|
2083
|
+
[2680, 30532],
|
|
2084
|
+
[2681, 30539],
|
|
2085
|
+
[2682, 30544],
|
|
2086
|
+
[2683, 30548],
|
|
2087
|
+
// Def/Core extra (Amarr/Caldari)
|
|
2088
|
+
[2684, 30540],
|
|
2089
|
+
[2685, 30546],
|
|
2090
|
+
[2686, 30545],
|
|
2091
|
+
[2687, 30547]
|
|
2092
|
+
// Def/Core extra (Gallente/Minmatar)
|
|
1716
2093
|
]);
|
|
1717
2094
|
function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
1718
2095
|
if (mi.modifyingAttributeID === void 0) return null;
|
|
@@ -1752,7 +2129,7 @@ function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
|
1752
2129
|
if (skillID === void 0) return null;
|
|
1753
2130
|
const level = ctx.skillLevel(skillID);
|
|
1754
2131
|
if (level === 0) return null;
|
|
1755
|
-
if (source.kind === "ship"
|
|
2132
|
+
if (source.kind === "ship") {
|
|
1756
2133
|
return { value: baseValue, scaled: false };
|
|
1757
2134
|
}
|
|
1758
2135
|
if (itemRequiresSkill(source, skillID)) {
|
|
@@ -1762,46 +2139,21 @@ function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
|
1762
2139
|
}
|
|
1763
2140
|
return { value: baseValue, scaled: false };
|
|
1764
2141
|
}
|
|
1765
|
-
|
|
1766
|
-
793,
|
|
1767
|
-
// shipBonusRole7
|
|
1768
|
-
1688,
|
|
1769
|
-
// shipBonusRole8
|
|
1770
|
-
1803,
|
|
1771
|
-
// MWDSignatureRadiusBonus — Assault Frigate / Interceptor MWD sig role bonus (flat)
|
|
1772
|
-
2059,
|
|
1773
|
-
// eliteBonusCommandDestroyer1 — Command Destroyer T2 specialisation (flat per-level applied via skill, but the SHIP-side reader is flat)
|
|
1774
|
-
2060,
|
|
1775
|
-
// eliteBonusCommandDestroyer2
|
|
1776
|
-
2064,
|
|
1777
|
-
// roleBonusCD — Command Destroyer command burst PG / activation cost reduction.
|
|
1778
|
-
// Without this entry, effect 6214 (Draugur's `roleBonusCDLinksPGReduction`)
|
|
1779
|
-
// double-applies the -95 % bonus at × Skirmish Command Burst V → -475 %
|
|
1780
|
-
// PostPercent on Skirmish Command Burst II `power` (110) → -412.5 MW per
|
|
1781
|
-
// burst → total ship power used reads −738 MW instead of +97 MW.
|
|
1782
|
-
2298,
|
|
1783
|
-
// shipBonusRole1
|
|
1784
|
-
2299,
|
|
1785
|
-
// shipBonusRole2
|
|
1786
|
-
2300,
|
|
1787
|
-
// shipBonusRole3
|
|
1788
|
-
2301,
|
|
1789
|
-
// shipBonusRole4
|
|
1790
|
-
2302,
|
|
1791
|
-
// shipBonusRole5
|
|
1792
|
-
2303,
|
|
1793
|
-
// shipBonusRole6
|
|
1794
|
-
5952
|
|
1795
|
-
// shipBonusGasCloudDurationRoleBonusOreMiningDestroyer
|
|
1796
|
-
]);
|
|
1797
|
-
function computeStackingGroup(source, mi, dataset) {
|
|
2142
|
+
function computeStackingGroup(source, mi, dataset, effect) {
|
|
1798
2143
|
if (NO_PENALTY_KINDS.has(source.kind)) return null;
|
|
1799
2144
|
if (mi.modifiedAttributeID !== void 0) {
|
|
1800
2145
|
const attr = dataset.attributes.get(mi.modifiedAttributeID);
|
|
1801
2146
|
if (attr?.stackable) return null;
|
|
1802
2147
|
}
|
|
2148
|
+
const group = STACKING_PENALTY_GROUPS.get(effect.id);
|
|
2149
|
+
if (group !== void 0 && group !== "default" && CUSTOM_STACK_GROUPS_HONOURED.has(group)) {
|
|
2150
|
+
return `${group}:${mi.modifiedAttributeID}`;
|
|
2151
|
+
}
|
|
1803
2152
|
return `attr:${mi.modifiedAttributeID}`;
|
|
1804
2153
|
}
|
|
2154
|
+
var CUSTOM_STACK_GROUPS_HONOURED = /* @__PURE__ */ new Set([
|
|
2155
|
+
"cloakingScanResolutionMultiplier"
|
|
2156
|
+
]);
|
|
1805
2157
|
function mapSourceKind(kind) {
|
|
1806
2158
|
switch (kind) {
|
|
1807
2159
|
case "ship":
|
|
@@ -3519,6 +3871,13 @@ var LEGACY_HANDLED_HARDCODED_EFFECTS = /* @__PURE__ */ new Set([
|
|
|
3519
3871
|
6658
|
|
3520
3872
|
// Bastion Module — applyLegacyBastion
|
|
3521
3873
|
]);
|
|
3874
|
+
var SEC_STATUS_SCALED_EFFECT_IDS = /* @__PURE__ */ new Set([
|
|
3875
|
+
6871,
|
|
3876
|
+
12165,
|
|
3877
|
+
12181,
|
|
3878
|
+
12185,
|
|
3879
|
+
12202
|
|
3880
|
+
]);
|
|
3522
3881
|
var LEGACY_HANDLED_EFFECT_IDS = /* @__PURE__ */ new Set([
|
|
3523
3882
|
...LEGACY_HANDLED_PASSIVE_ADD_EFFECTS,
|
|
3524
3883
|
...LEGACY_HANDLED_HARDCODED_EFFECTS
|
|
@@ -3589,6 +3948,176 @@ function computeTotalEhp(shield, armor, hull, useProfile) {
|
|
|
3589
3948
|
return Number.isFinite(total) ? total : Number.MAX_SAFE_INTEGER;
|
|
3590
3949
|
}
|
|
3591
3950
|
|
|
3951
|
+
// src/fitChecks.ts
|
|
3952
|
+
function readAttr(t, id) {
|
|
3953
|
+
return t.attributes.find((a) => a.id === id)?.v;
|
|
3954
|
+
}
|
|
3955
|
+
function typeFitsSlotType(t, slot) {
|
|
3956
|
+
for (const e of t.effects) {
|
|
3957
|
+
const mapped = SLOT_EFFECT_TO_SLOT_TYPE[e.id];
|
|
3958
|
+
if (mapped === slot) return true;
|
|
3959
|
+
}
|
|
3960
|
+
return false;
|
|
3961
|
+
}
|
|
3962
|
+
function isTurretWeapon(t) {
|
|
3963
|
+
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "TURRET");
|
|
3964
|
+
}
|
|
3965
|
+
function isMissileLauncher(t) {
|
|
3966
|
+
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "MISSILE");
|
|
3967
|
+
}
|
|
3968
|
+
function isSmartBomb(t) {
|
|
3969
|
+
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "SMARTBOMB");
|
|
3970
|
+
}
|
|
3971
|
+
function shipGroupRestrictions(t) {
|
|
3972
|
+
const out = [];
|
|
3973
|
+
for (const id of CAN_FIT_SHIP_GROUP_ATTRS) {
|
|
3974
|
+
const v = readAttr(t, id);
|
|
3975
|
+
if (v != null && v > 0) out.push(Math.round(v));
|
|
3976
|
+
}
|
|
3977
|
+
return out;
|
|
3978
|
+
}
|
|
3979
|
+
function shipTypeRestrictions(t) {
|
|
3980
|
+
const out = [];
|
|
3981
|
+
for (const id of CAN_FIT_SHIP_TYPE_ATTRS) {
|
|
3982
|
+
const v = readAttr(t, id);
|
|
3983
|
+
if (v != null && v > 0) out.push(Math.round(v));
|
|
3984
|
+
}
|
|
3985
|
+
return out;
|
|
3986
|
+
}
|
|
3987
|
+
function maxGroupFittedFor(mod) {
|
|
3988
|
+
const v = readAttr(mod, ATTR.MAX_GROUP_FITTED);
|
|
3989
|
+
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
3990
|
+
}
|
|
3991
|
+
function maxTypeFittedFor(mod) {
|
|
3992
|
+
const v = readAttr(mod, ATTR.MAX_TYPE_FITTED);
|
|
3993
|
+
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
3994
|
+
}
|
|
3995
|
+
function freeFitGroupSlotsFor(mod, fittedModules, dataset) {
|
|
3996
|
+
const groupCap = maxGroupFittedFor(mod);
|
|
3997
|
+
const typeCap = maxTypeFittedFor(mod);
|
|
3998
|
+
if (groupCap === void 0 && typeCap === void 0) return Number.POSITIVE_INFINITY;
|
|
3999
|
+
let groupCount = 0;
|
|
4000
|
+
let typeCount = 0;
|
|
4001
|
+
for (const fm of fittedModules) {
|
|
4002
|
+
if (typeCap !== void 0 && fm.typeID === mod.id) typeCount++;
|
|
4003
|
+
if (groupCap !== void 0) {
|
|
4004
|
+
const t = dataset.getType(fm.typeID);
|
|
4005
|
+
if (t && t.groupID === mod.groupID) groupCount++;
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
let free = Number.POSITIVE_INFINITY;
|
|
4009
|
+
if (groupCap !== void 0) free = Math.min(free, groupCap - groupCount);
|
|
4010
|
+
if (typeCap !== void 0) free = Math.min(free, typeCap - typeCount);
|
|
4011
|
+
return Math.max(0, free);
|
|
4012
|
+
}
|
|
4013
|
+
function canFitModuleOnShip(mod, ship, slot, fitContext) {
|
|
4014
|
+
if (!typeFitsSlotType(mod, slot)) {
|
|
4015
|
+
return { ok: false, reason: `Module does not declare a ${slot} slot effect` };
|
|
4016
|
+
}
|
|
4017
|
+
if (!ship) return { ok: true };
|
|
4018
|
+
const allowedGroups = shipGroupRestrictions(mod);
|
|
4019
|
+
const allowedTypes = shipTypeRestrictions(mod);
|
|
4020
|
+
if (allowedGroups.length > 0 || allowedTypes.length > 0) {
|
|
4021
|
+
const groupOk = allowedGroups.includes(ship.groupID);
|
|
4022
|
+
const typeOk = allowedTypes.includes(ship.id);
|
|
4023
|
+
if (!groupOk && !typeOk) {
|
|
4024
|
+
return { ok: false, reason: "Ship hull/group not allowed by this module" };
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
if (slot === "RIG") {
|
|
4028
|
+
const modSize = readAttr(mod, ATTR.RIG_SIZE);
|
|
4029
|
+
const shipSize = readAttr(ship, ATTR.RIG_SIZE);
|
|
4030
|
+
if (modSize != null && shipSize != null && Math.round(modSize) !== Math.round(shipSize)) {
|
|
4031
|
+
return { ok: false, reason: "Rig size mismatch" };
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
if (slot === "SUBSYSTEM") {
|
|
4035
|
+
const fitsTo = readAttr(mod, 1380);
|
|
4036
|
+
if (fitsTo != null && fitsTo > 0 && Math.round(fitsTo) !== ship.id) {
|
|
4037
|
+
return { ok: false, reason: "Subsystem locked to a different T3C hull" };
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
if (slot === "HI") {
|
|
4041
|
+
if (isTurretWeapon(mod)) {
|
|
4042
|
+
const turrets = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
4043
|
+
if (turrets <= 0) {
|
|
4044
|
+
return { ok: false, reason: "Ship has no turret hardpoints" };
|
|
4045
|
+
}
|
|
4046
|
+
} else if (isMissileLauncher(mod)) {
|
|
4047
|
+
const launchers = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
4048
|
+
if (launchers <= 0) {
|
|
4049
|
+
return { ok: false, reason: "Ship has no launcher hardpoints" };
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
if (fitContext) {
|
|
4054
|
+
const free = freeFitGroupSlotsFor(mod, fitContext.fittedModules, fitContext.dataset);
|
|
4055
|
+
if (free <= 0) {
|
|
4056
|
+
const cap = maxTypeFittedFor(mod) ?? maxGroupFittedFor(mod);
|
|
4057
|
+
return { ok: false, reason: `Only ${cap} of this module type can be fitted to a ship` };
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
return { ok: true };
|
|
4061
|
+
}
|
|
4062
|
+
function chargeGroupsForModule(mod) {
|
|
4063
|
+
const out = [];
|
|
4064
|
+
for (const attrID of CHARGE_GROUP_ATTRS) {
|
|
4065
|
+
const v = readAttr(mod, attrID);
|
|
4066
|
+
if (v != null && v > 0) out.push(Math.round(v));
|
|
4067
|
+
}
|
|
4068
|
+
return out;
|
|
4069
|
+
}
|
|
4070
|
+
function moduleAcceptsAnyCharge(mod) {
|
|
4071
|
+
return chargeGroupsForModule(mod).length > 0;
|
|
4072
|
+
}
|
|
4073
|
+
function moduleAcceptsChargeType(mod, charge) {
|
|
4074
|
+
const allowedGroups = chargeGroupsForModule(mod);
|
|
4075
|
+
if (allowedGroups.length === 0) return false;
|
|
4076
|
+
if (!allowedGroups.includes(charge.groupID)) return false;
|
|
4077
|
+
const modSize = readAttr(mod, ATTR.CHARGE_SIZE);
|
|
4078
|
+
const chargeSize = readAttr(charge, ATTR.CHARGE_SIZE);
|
|
4079
|
+
if (modSize != null && chargeSize != null && Math.round(chargeSize) > Math.round(modSize)) {
|
|
4080
|
+
return false;
|
|
4081
|
+
}
|
|
4082
|
+
return true;
|
|
4083
|
+
}
|
|
4084
|
+
var ACTIVE_EFFECT_CATEGORIES = /* @__PURE__ */ new Set([1, 2, 3]);
|
|
4085
|
+
function isActivatableModule(mod, effects) {
|
|
4086
|
+
for (const e of mod.effects) {
|
|
4087
|
+
const eff = effects.get(e.id);
|
|
4088
|
+
if (eff && eff.effectCategoryID !== void 0 && ACTIVE_EFFECT_CATEGORIES.has(eff.effectCategoryID)) {
|
|
4089
|
+
return true;
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
return false;
|
|
4093
|
+
}
|
|
4094
|
+
var DEFAULT_OFFLINE_ACTIVATION_GROUPS = /* @__PURE__ */ new Set([
|
|
4095
|
+
330,
|
|
4096
|
+
4117
|
|
4097
|
+
]);
|
|
4098
|
+
function defaultStateForModule(mod, effects) {
|
|
4099
|
+
if (DEFAULT_OFFLINE_ACTIVATION_GROUPS.has(mod.groupID)) return "ONLINE";
|
|
4100
|
+
return isActivatableModule(mod, effects) ? "ACTIVE" : "ONLINE";
|
|
4101
|
+
}
|
|
4102
|
+
function freeHardpointsFor(mod, ship, fittedHiModules, dataset) {
|
|
4103
|
+
const turretCap = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
4104
|
+
const launcherCap = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
4105
|
+
let turretUsed = 0;
|
|
4106
|
+
let launcherUsed = 0;
|
|
4107
|
+
for (const m of fittedHiModules) {
|
|
4108
|
+
const t = dataset.getType(m.typeID);
|
|
4109
|
+
if (!t) continue;
|
|
4110
|
+
if (isTurretWeapon(t)) turretUsed++;
|
|
4111
|
+
else if (isMissileLauncher(t)) launcherUsed++;
|
|
4112
|
+
}
|
|
4113
|
+
let hardpointFree;
|
|
4114
|
+
if (isTurretWeapon(mod)) hardpointFree = Math.max(0, turretCap - turretUsed);
|
|
4115
|
+
else if (isMissileLauncher(mod)) hardpointFree = Math.max(0, launcherCap - launcherUsed);
|
|
4116
|
+
else hardpointFree = Number.POSITIVE_INFINITY;
|
|
4117
|
+
const groupFree = freeFitGroupSlotsFor(mod, fittedHiModules, dataset);
|
|
4118
|
+
return Math.min(hardpointFree, groupFree);
|
|
4119
|
+
}
|
|
4120
|
+
|
|
3592
4121
|
// src/derived/capacitor.ts
|
|
3593
4122
|
function computeCapacitor(ctx, dataset) {
|
|
3594
4123
|
const ship = ctx.ship;
|
|
@@ -3668,6 +4197,7 @@ var CAP_BOOSTER_EFFECT_ID = 48;
|
|
|
3668
4197
|
var ATTR_CAPACITOR_BONUS = 67;
|
|
3669
4198
|
var ATTR_CAPACITOR_NEED2 = 6;
|
|
3670
4199
|
var ATTR_DURATION_MS2 = 73;
|
|
4200
|
+
var ATTR_REACTIVATION_MS = 669;
|
|
3671
4201
|
var ATTR_RELOAD_TIME = 1795;
|
|
3672
4202
|
var ATTR_CAPACITY = 38;
|
|
3673
4203
|
var ATTR_VOLUME = 161;
|
|
@@ -3702,8 +4232,9 @@ function drainEntryFromEffect(effect, mod) {
|
|
|
3702
4232
|
if (effect.dischargeAttributeID === void 0) return null;
|
|
3703
4233
|
const discharge = mod.getFinal(effect.dischargeAttributeID, 0);
|
|
3704
4234
|
if (discharge <= 0) return null;
|
|
3705
|
-
const
|
|
3706
|
-
if (
|
|
4235
|
+
const baseCycleMs = effect.durationAttributeID !== void 0 ? mod.getFinal(effect.durationAttributeID, 0) : 1e3;
|
|
4236
|
+
if (baseCycleMs <= 0) return null;
|
|
4237
|
+
const cycleMs = Math.round(baseCycleMs + mod.getFinal(ATTR_REACTIVATION_MS, 0));
|
|
3707
4238
|
return {
|
|
3708
4239
|
cycleMs,
|
|
3709
4240
|
capNeed: discharge,
|
|
@@ -3711,7 +4242,12 @@ function drainEntryFromEffect(effect, mod) {
|
|
|
3711
4242
|
// ammo doesn't reload for cap purposes on regular modules
|
|
3712
4243
|
reloadMs: 0,
|
|
3713
4244
|
isInjector: false,
|
|
3714
|
-
|
|
4245
|
+
// Pyfa: `disableStagger = mod.hardpoint == TURRET`. Turrets fire as a
|
|
4246
|
+
// synchronized volley (their cap drains aggregate, capNeed × N at the
|
|
4247
|
+
// shared cycle); everything else is staggered evenly across its cycle.
|
|
4248
|
+
// Without this, N turrets were staggered (one drain at cycle/N) instead
|
|
4249
|
+
// of N together, shifting the cap timeline (time-to-empty off ~5-10 %).
|
|
4250
|
+
disableStagger: isTurretWeapon(mod.type)
|
|
3715
4251
|
};
|
|
3716
4252
|
}
|
|
3717
4253
|
function boosterDrainEntry(mod) {
|
|
@@ -3719,19 +4255,19 @@ function boosterDrainEntry(mod) {
|
|
|
3719
4255
|
if (!charge) return null;
|
|
3720
4256
|
const capNeed = mod.getFinal(ATTR_CAPACITOR_NEED2, 0);
|
|
3721
4257
|
const inject = charge.getFinal(ATTR_CAPACITOR_BONUS, 0);
|
|
3722
|
-
const cycleMs = mod.getFinal(ATTR_DURATION_MS2, 0);
|
|
4258
|
+
const cycleMs = Math.round(mod.getFinal(ATTR_DURATION_MS2, 0));
|
|
3723
4259
|
if (cycleMs <= 0) return null;
|
|
3724
|
-
let reloadMs = mod.getFinal(ATTR_RELOAD_TIME, 0);
|
|
4260
|
+
let reloadMs = Math.round(mod.getFinal(ATTR_RELOAD_TIME, 0));
|
|
3725
4261
|
if (reloadMs <= 0) reloadMs = 1e4;
|
|
3726
4262
|
const capacity = mod.getFinal(ATTR_CAPACITY, 0);
|
|
3727
4263
|
const chargeVol = charge.getFinal(ATTR_VOLUME, 0);
|
|
3728
|
-
const charges = capacity > 0 && chargeVol > 0 ? Math.floor(capacity / chargeVol) :
|
|
4264
|
+
const charges = capacity > 0 && chargeVol > 0 ? Math.floor(capacity / chargeVol) : 0;
|
|
3729
4265
|
return {
|
|
3730
4266
|
cycleMs,
|
|
3731
4267
|
// Pyfa convention: positive capNeed = drain, negative = injection.
|
|
3732
4268
|
// For boosters the *net* per-cycle change is (capNeed_module - inject_charge).
|
|
3733
4269
|
capNeed: capNeed - inject,
|
|
3734
|
-
clipSize:
|
|
4270
|
+
clipSize: charges,
|
|
3735
4271
|
reloadMs,
|
|
3736
4272
|
isInjector: true,
|
|
3737
4273
|
disableStagger: false
|
|
@@ -4663,8 +5199,8 @@ function computeFit(fit, dataset, opts) {
|
|
|
4663
5199
|
}
|
|
4664
5200
|
ctx.stoppedLocalEffectIDs = collectEffectStoppers(earlyProjected, dataset);
|
|
4665
5201
|
}
|
|
4666
|
-
for (const m of modules) applySourceItem(m, ctx, dataset);
|
|
4667
5202
|
for (const m of modules) if (m.charge) applySourceItem(m.charge, ctx, dataset);
|
|
5203
|
+
for (const m of modules) applySourceItem(m, ctx, dataset);
|
|
4668
5204
|
for (const d of drones) applySourceItem(d, ctx, dataset);
|
|
4669
5205
|
for (const f of fighters) applySourceItem(f, ctx, dataset);
|
|
4670
5206
|
for (const i of implants) applySourceItem(i, ctx, dataset);
|
|
@@ -4827,6 +5363,7 @@ function buildProjectionReport(source, ctx) {
|
|
|
4827
5363
|
summary: labels[cls.kind] ?? cls.kind
|
|
4828
5364
|
};
|
|
4829
5365
|
}
|
|
5366
|
+
var CHAR_BASE_MAX_LOCKED_TARGETS = 2;
|
|
4830
5367
|
function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
4831
5368
|
const ship = ctx.ship;
|
|
4832
5369
|
const slotUsed = { HI: 0, MED: 0, LO: 0, RIG: 0, SUBSYSTEM: 0, SERVICE: 0 };
|
|
@@ -4947,7 +5484,18 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
4947
5484
|
ship.getFinal(ATTR.MAX_TARGET_RANGE, 0),
|
|
4948
5485
|
ship.getFinal(ATTR.MAX_TARGET_RANGE_CAP, 3e5)
|
|
4949
5486
|
),
|
|
4950
|
-
|
|
5487
|
+
// In-game lockable-target count is the LOWER of the ship's
|
|
5488
|
+
// maxLockedTargets and the pilot's skill-derived cap. Pyfa:
|
|
5489
|
+
// ceil(min(maxTargetsLockedFromSkills, ship maxLockedTargets)),
|
|
5490
|
+
// where maxTargetsLockedFromSkills seeds at a base of 2 (every
|
|
5491
|
+
// capsuleer locks 2 without skills) + Target Management / Advanced
|
|
5492
|
+
// Target Management (+1/level each) → 2 + 5 + 5 = 12 at All-V. The
|
|
5493
|
+
// character item carries only the skill ModAdds (10), so add the
|
|
5494
|
+
// base 2. Without the char cap, high-slot capitals reported 14-20.
|
|
5495
|
+
maxLockedTargets: Math.ceil(Math.min(
|
|
5496
|
+
CHAR_BASE_MAX_LOCKED_TARGETS + ctx.character.getFinal(ATTR.MAX_LOCKED_TARGETS, 0),
|
|
5497
|
+
ship.getFinal(ATTR.MAX_LOCKED_TARGETS, 0)
|
|
5498
|
+
)),
|
|
4951
5499
|
signatureRadius: ship.getFinal(ATTR.SIGNATURE_RADIUS, 0),
|
|
4952
5500
|
scanResolution: ship.getFinal(ATTR.SCAN_RESOLUTION, 0),
|
|
4953
5501
|
sensorStrength: pickSensorStrength(ship).value,
|
|
@@ -5666,174 +6214,4 @@ var TARGET_PROFILE_PRESETS = [
|
|
|
5666
6214
|
{ name: "Keepstar", signatureRadius: 3e4, maxVelocity: 0, emResist: 0.7, thermalResist: 0.7, kineticResist: 0.7, explosiveResist: 0.7, isPreset: true }
|
|
5667
6215
|
];
|
|
5668
6216
|
|
|
5669
|
-
// src/fitChecks.ts
|
|
5670
|
-
function readAttr(t, id) {
|
|
5671
|
-
return t.attributes.find((a) => a.id === id)?.v;
|
|
5672
|
-
}
|
|
5673
|
-
function typeFitsSlotType(t, slot) {
|
|
5674
|
-
for (const e of t.effects) {
|
|
5675
|
-
const mapped = SLOT_EFFECT_TO_SLOT_TYPE[e.id];
|
|
5676
|
-
if (mapped === slot) return true;
|
|
5677
|
-
}
|
|
5678
|
-
return false;
|
|
5679
|
-
}
|
|
5680
|
-
function isTurretWeapon(t) {
|
|
5681
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "TURRET");
|
|
5682
|
-
}
|
|
5683
|
-
function isMissileLauncher(t) {
|
|
5684
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "MISSILE");
|
|
5685
|
-
}
|
|
5686
|
-
function isSmartBomb(t) {
|
|
5687
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "SMARTBOMB");
|
|
5688
|
-
}
|
|
5689
|
-
function shipGroupRestrictions(t) {
|
|
5690
|
-
const out = [];
|
|
5691
|
-
for (const id of CAN_FIT_SHIP_GROUP_ATTRS) {
|
|
5692
|
-
const v = readAttr(t, id);
|
|
5693
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5694
|
-
}
|
|
5695
|
-
return out;
|
|
5696
|
-
}
|
|
5697
|
-
function shipTypeRestrictions(t) {
|
|
5698
|
-
const out = [];
|
|
5699
|
-
for (const id of CAN_FIT_SHIP_TYPE_ATTRS) {
|
|
5700
|
-
const v = readAttr(t, id);
|
|
5701
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5702
|
-
}
|
|
5703
|
-
return out;
|
|
5704
|
-
}
|
|
5705
|
-
function maxGroupFittedFor(mod) {
|
|
5706
|
-
const v = readAttr(mod, ATTR.MAX_GROUP_FITTED);
|
|
5707
|
-
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
5708
|
-
}
|
|
5709
|
-
function maxTypeFittedFor(mod) {
|
|
5710
|
-
const v = readAttr(mod, ATTR.MAX_TYPE_FITTED);
|
|
5711
|
-
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
5712
|
-
}
|
|
5713
|
-
function freeFitGroupSlotsFor(mod, fittedModules, dataset) {
|
|
5714
|
-
const groupCap = maxGroupFittedFor(mod);
|
|
5715
|
-
const typeCap = maxTypeFittedFor(mod);
|
|
5716
|
-
if (groupCap === void 0 && typeCap === void 0) return Number.POSITIVE_INFINITY;
|
|
5717
|
-
let groupCount = 0;
|
|
5718
|
-
let typeCount = 0;
|
|
5719
|
-
for (const fm of fittedModules) {
|
|
5720
|
-
if (typeCap !== void 0 && fm.typeID === mod.id) typeCount++;
|
|
5721
|
-
if (groupCap !== void 0) {
|
|
5722
|
-
const t = dataset.getType(fm.typeID);
|
|
5723
|
-
if (t && t.groupID === mod.groupID) groupCount++;
|
|
5724
|
-
}
|
|
5725
|
-
}
|
|
5726
|
-
let free = Number.POSITIVE_INFINITY;
|
|
5727
|
-
if (groupCap !== void 0) free = Math.min(free, groupCap - groupCount);
|
|
5728
|
-
if (typeCap !== void 0) free = Math.min(free, typeCap - typeCount);
|
|
5729
|
-
return Math.max(0, free);
|
|
5730
|
-
}
|
|
5731
|
-
function canFitModuleOnShip(mod, ship, slot, fitContext) {
|
|
5732
|
-
if (!typeFitsSlotType(mod, slot)) {
|
|
5733
|
-
return { ok: false, reason: `Module does not declare a ${slot} slot effect` };
|
|
5734
|
-
}
|
|
5735
|
-
if (!ship) return { ok: true };
|
|
5736
|
-
const allowedGroups = shipGroupRestrictions(mod);
|
|
5737
|
-
const allowedTypes = shipTypeRestrictions(mod);
|
|
5738
|
-
if (allowedGroups.length > 0 || allowedTypes.length > 0) {
|
|
5739
|
-
const groupOk = allowedGroups.includes(ship.groupID);
|
|
5740
|
-
const typeOk = allowedTypes.includes(ship.id);
|
|
5741
|
-
if (!groupOk && !typeOk) {
|
|
5742
|
-
return { ok: false, reason: "Ship hull/group not allowed by this module" };
|
|
5743
|
-
}
|
|
5744
|
-
}
|
|
5745
|
-
if (slot === "RIG") {
|
|
5746
|
-
const modSize = readAttr(mod, ATTR.RIG_SIZE);
|
|
5747
|
-
const shipSize = readAttr(ship, ATTR.RIG_SIZE);
|
|
5748
|
-
if (modSize != null && shipSize != null && Math.round(modSize) !== Math.round(shipSize)) {
|
|
5749
|
-
return { ok: false, reason: "Rig size mismatch" };
|
|
5750
|
-
}
|
|
5751
|
-
}
|
|
5752
|
-
if (slot === "SUBSYSTEM") {
|
|
5753
|
-
const fitsTo = readAttr(mod, 1380);
|
|
5754
|
-
if (fitsTo != null && fitsTo > 0 && Math.round(fitsTo) !== ship.id) {
|
|
5755
|
-
return { ok: false, reason: "Subsystem locked to a different T3C hull" };
|
|
5756
|
-
}
|
|
5757
|
-
}
|
|
5758
|
-
if (slot === "HI") {
|
|
5759
|
-
if (isTurretWeapon(mod)) {
|
|
5760
|
-
const turrets = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
5761
|
-
if (turrets <= 0) {
|
|
5762
|
-
return { ok: false, reason: "Ship has no turret hardpoints" };
|
|
5763
|
-
}
|
|
5764
|
-
} else if (isMissileLauncher(mod)) {
|
|
5765
|
-
const launchers = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
5766
|
-
if (launchers <= 0) {
|
|
5767
|
-
return { ok: false, reason: "Ship has no launcher hardpoints" };
|
|
5768
|
-
}
|
|
5769
|
-
}
|
|
5770
|
-
}
|
|
5771
|
-
if (fitContext) {
|
|
5772
|
-
const free = freeFitGroupSlotsFor(mod, fitContext.fittedModules, fitContext.dataset);
|
|
5773
|
-
if (free <= 0) {
|
|
5774
|
-
const cap = maxTypeFittedFor(mod) ?? maxGroupFittedFor(mod);
|
|
5775
|
-
return { ok: false, reason: `Only ${cap} of this module type can be fitted to a ship` };
|
|
5776
|
-
}
|
|
5777
|
-
}
|
|
5778
|
-
return { ok: true };
|
|
5779
|
-
}
|
|
5780
|
-
function chargeGroupsForModule(mod) {
|
|
5781
|
-
const out = [];
|
|
5782
|
-
for (const attrID of CHARGE_GROUP_ATTRS) {
|
|
5783
|
-
const v = readAttr(mod, attrID);
|
|
5784
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5785
|
-
}
|
|
5786
|
-
return out;
|
|
5787
|
-
}
|
|
5788
|
-
function moduleAcceptsAnyCharge(mod) {
|
|
5789
|
-
return chargeGroupsForModule(mod).length > 0;
|
|
5790
|
-
}
|
|
5791
|
-
function moduleAcceptsChargeType(mod, charge) {
|
|
5792
|
-
const allowedGroups = chargeGroupsForModule(mod);
|
|
5793
|
-
if (allowedGroups.length === 0) return false;
|
|
5794
|
-
if (!allowedGroups.includes(charge.groupID)) return false;
|
|
5795
|
-
const modSize = readAttr(mod, ATTR.CHARGE_SIZE);
|
|
5796
|
-
const chargeSize = readAttr(charge, ATTR.CHARGE_SIZE);
|
|
5797
|
-
if (modSize != null && chargeSize != null && Math.round(chargeSize) > Math.round(modSize)) {
|
|
5798
|
-
return false;
|
|
5799
|
-
}
|
|
5800
|
-
return true;
|
|
5801
|
-
}
|
|
5802
|
-
var ACTIVE_EFFECT_CATEGORIES = /* @__PURE__ */ new Set([1, 2, 3]);
|
|
5803
|
-
function isActivatableModule(mod, effects) {
|
|
5804
|
-
for (const e of mod.effects) {
|
|
5805
|
-
const eff = effects.get(e.id);
|
|
5806
|
-
if (eff && eff.effectCategoryID !== void 0 && ACTIVE_EFFECT_CATEGORIES.has(eff.effectCategoryID)) {
|
|
5807
|
-
return true;
|
|
5808
|
-
}
|
|
5809
|
-
}
|
|
5810
|
-
return false;
|
|
5811
|
-
}
|
|
5812
|
-
var DEFAULT_OFFLINE_ACTIVATION_GROUPS = /* @__PURE__ */ new Set([
|
|
5813
|
-
330,
|
|
5814
|
-
4117
|
|
5815
|
-
]);
|
|
5816
|
-
function defaultStateForModule(mod, effects) {
|
|
5817
|
-
if (DEFAULT_OFFLINE_ACTIVATION_GROUPS.has(mod.groupID)) return "ONLINE";
|
|
5818
|
-
return isActivatableModule(mod, effects) ? "ACTIVE" : "ONLINE";
|
|
5819
|
-
}
|
|
5820
|
-
function freeHardpointsFor(mod, ship, fittedHiModules, dataset) {
|
|
5821
|
-
const turretCap = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
5822
|
-
const launcherCap = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
5823
|
-
let turretUsed = 0;
|
|
5824
|
-
let launcherUsed = 0;
|
|
5825
|
-
for (const m of fittedHiModules) {
|
|
5826
|
-
const t = dataset.getType(m.typeID);
|
|
5827
|
-
if (!t) continue;
|
|
5828
|
-
if (isTurretWeapon(t)) turretUsed++;
|
|
5829
|
-
else if (isMissileLauncher(t)) launcherUsed++;
|
|
5830
|
-
}
|
|
5831
|
-
let hardpointFree;
|
|
5832
|
-
if (isTurretWeapon(mod)) hardpointFree = Math.max(0, turretCap - turretUsed);
|
|
5833
|
-
else if (isMissileLauncher(mod)) hardpointFree = Math.max(0, launcherCap - launcherUsed);
|
|
5834
|
-
else hardpointFree = Number.POSITIVE_INFINITY;
|
|
5835
|
-
const groupFree = freeFitGroupSlotsFor(mod, fittedHiModules, dataset);
|
|
5836
|
-
return Math.min(hardpointFree, groupFree);
|
|
5837
|
-
}
|
|
5838
|
-
|
|
5839
6217
|
export { ACTIVATION_EFFECT_ID, ATTR, CATEGORY, DAMAGE_PROFILE_PRESETS, FitContext, ItemState, LEGACY_EFFECT_IDS, LEGACY_HANDLED_EFFECT_IDS, ModifiedAttribute, OPERATION_BY_SDE_CODE, OUT_OF_SCOPE_EFFECT_IDS, REPAIR_EFFECT_AMOUNT_ATTR, REQUIRED_SKILL_PAIRS, SLOT_EFFECT_ID, SLOT_EFFECT_TO_SLOT_TYPE, STACKING_PENALTY_K, TARGET_PROFILE_PRESETS, WEAPON_EFFECT_KIND, applyOneModifier, applySkills, applySourceItem, buildNameIndex, canFitModuleOnShip, chargeGroupsForModule, checkSkills, classifyEwar, classifyWeapon, combineJamChances, combineMultiplicative, combinePenalized, combineUnstacked, computeCapacitor, computeFit, computeLayerEhp, computeOffense, computeStructureMeta, computeT3CVariantCode, computeTank, computeTotalEhp, defaultStateForModule, disintegratorCyclesToFullSpool, disintegratorSpoolBonus, ecmJamChance, effectiveDps, ehpUnderProfile, formatDna, formatEft, formatMultibuy, formatTypeIds, freeFitGroupSlotsFor, freeHardpointsFor, isActivatableModule, isMissileLauncher, isSmartBomb, isTurretWeapon, marketGroupPlacement, maxGroupFittedFor, maxTypeFittedFor, moduleAcceptsAnyCharge, moduleAcceptsCharge, moduleAcceptsChargeType, parseEft, peakRecharge, readCycleInfo, readDamageComponents, readRangeInfo, rechargeRateAt, shipGroupRestrictions, shipTypeRestrictions, typeFitsSlotType, verifyLegacyEffectIds };
|