eve-fit-engine 0.1.1 → 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 +561 -226
- package/dist/index.js +561 -226
- package/dist/node.cjs +561 -226
- package/dist/node.js +561 -226
- 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
|
|
@@ -1786,7 +2129,7 @@ function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
|
1786
2129
|
if (skillID === void 0) return null;
|
|
1787
2130
|
const level = ctx.skillLevel(skillID);
|
|
1788
2131
|
if (level === 0) return null;
|
|
1789
|
-
if (source.kind === "ship"
|
|
2132
|
+
if (source.kind === "ship") {
|
|
1790
2133
|
return { value: baseValue, scaled: false };
|
|
1791
2134
|
}
|
|
1792
2135
|
if (itemRequiresSkill(source, skillID)) {
|
|
@@ -1796,55 +2139,21 @@ function computeModifierValue(source, mi, ctx, dataset, op) {
|
|
|
1796
2139
|
}
|
|
1797
2140
|
return { value: baseValue, scaled: false };
|
|
1798
2141
|
}
|
|
1799
|
-
|
|
1800
|
-
793,
|
|
1801
|
-
// shipBonusRole7
|
|
1802
|
-
1688,
|
|
1803
|
-
// shipBonusRole8
|
|
1804
|
-
1803,
|
|
1805
|
-
// MWDSignatureRadiusBonus — Assault Frigate / Interceptor MWD sig role bonus (flat)
|
|
1806
|
-
2059,
|
|
1807
|
-
// eliteBonusCommandDestroyer1 — Command Destroyer T2 specialisation (flat per-level applied via skill, but the SHIP-side reader is flat)
|
|
1808
|
-
2060,
|
|
1809
|
-
// eliteBonusCommandDestroyer2
|
|
1810
|
-
2064,
|
|
1811
|
-
// roleBonusCD — Command Destroyer command burst PG / activation cost reduction.
|
|
1812
|
-
// Without this entry, effect 6214 (Draugur's `roleBonusCDLinksPGReduction`)
|
|
1813
|
-
// double-applies the -95 % bonus at × Skirmish Command Burst V → -475 %
|
|
1814
|
-
// PostPercent on Skirmish Command Burst II `power` (110) → -412.5 MW per
|
|
1815
|
-
// burst → total ship power used reads −738 MW instead of +97 MW.
|
|
1816
|
-
2298,
|
|
1817
|
-
// shipBonusRole1
|
|
1818
|
-
2299,
|
|
1819
|
-
// shipBonusRole2
|
|
1820
|
-
2300,
|
|
1821
|
-
// shipBonusRole3
|
|
1822
|
-
2301,
|
|
1823
|
-
// shipBonusRole4
|
|
1824
|
-
2302,
|
|
1825
|
-
// shipBonusRole5
|
|
1826
|
-
2303,
|
|
1827
|
-
// shipBonusRole6
|
|
1828
|
-
5952,
|
|
1829
|
-
// shipBonusGasCloudDurationRoleBonusOreMiningDestroyer
|
|
1830
|
-
1989
|
|
1831
|
-
// probeLauncherCPUPercentRoleBonusT3 value — effect 6009 on T3C hulls
|
|
1832
|
-
// (Loki/Tengu/…): "-99 % CPU for Scan Probe Launchers". Declared as
|
|
1833
|
-
// LocationRequiredSkillModifier gated on Astrometrics (3412), but
|
|
1834
|
-
// the skill only SELECTS the recipient (probe launchers) — the bonus
|
|
1835
|
-
// is FLAT. Without this entry the ship-domain reader scales it ×
|
|
1836
|
-
// Astrometrics level → -99 % becomes -495 % PostPercent → a Loki
|
|
1837
|
-
// Expanded Probe Launcher's 242 tf CPU flips to -955.9 tf and total
|
|
1838
|
-
// CPU used reads -388 instead of +569.5.
|
|
1839
|
-
]);
|
|
1840
|
-
function computeStackingGroup(source, mi, dataset) {
|
|
2142
|
+
function computeStackingGroup(source, mi, dataset, effect) {
|
|
1841
2143
|
if (NO_PENALTY_KINDS.has(source.kind)) return null;
|
|
1842
2144
|
if (mi.modifiedAttributeID !== void 0) {
|
|
1843
2145
|
const attr = dataset.attributes.get(mi.modifiedAttributeID);
|
|
1844
2146
|
if (attr?.stackable) return null;
|
|
1845
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
|
+
}
|
|
1846
2152
|
return `attr:${mi.modifiedAttributeID}`;
|
|
1847
2153
|
}
|
|
2154
|
+
var CUSTOM_STACK_GROUPS_HONOURED = /* @__PURE__ */ new Set([
|
|
2155
|
+
"cloakingScanResolutionMultiplier"
|
|
2156
|
+
]);
|
|
1848
2157
|
function mapSourceKind(kind) {
|
|
1849
2158
|
switch (kind) {
|
|
1850
2159
|
case "ship":
|
|
@@ -3562,6 +3871,13 @@ var LEGACY_HANDLED_HARDCODED_EFFECTS = /* @__PURE__ */ new Set([
|
|
|
3562
3871
|
6658
|
|
3563
3872
|
// Bastion Module — applyLegacyBastion
|
|
3564
3873
|
]);
|
|
3874
|
+
var SEC_STATUS_SCALED_EFFECT_IDS = /* @__PURE__ */ new Set([
|
|
3875
|
+
6871,
|
|
3876
|
+
12165,
|
|
3877
|
+
12181,
|
|
3878
|
+
12185,
|
|
3879
|
+
12202
|
|
3880
|
+
]);
|
|
3565
3881
|
var LEGACY_HANDLED_EFFECT_IDS = /* @__PURE__ */ new Set([
|
|
3566
3882
|
...LEGACY_HANDLED_PASSIVE_ADD_EFFECTS,
|
|
3567
3883
|
...LEGACY_HANDLED_HARDCODED_EFFECTS
|
|
@@ -3632,6 +3948,176 @@ function computeTotalEhp(shield, armor, hull, useProfile) {
|
|
|
3632
3948
|
return Number.isFinite(total) ? total : Number.MAX_SAFE_INTEGER;
|
|
3633
3949
|
}
|
|
3634
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
|
+
|
|
3635
4121
|
// src/derived/capacitor.ts
|
|
3636
4122
|
function computeCapacitor(ctx, dataset) {
|
|
3637
4123
|
const ship = ctx.ship;
|
|
@@ -3711,6 +4197,7 @@ var CAP_BOOSTER_EFFECT_ID = 48;
|
|
|
3711
4197
|
var ATTR_CAPACITOR_BONUS = 67;
|
|
3712
4198
|
var ATTR_CAPACITOR_NEED2 = 6;
|
|
3713
4199
|
var ATTR_DURATION_MS2 = 73;
|
|
4200
|
+
var ATTR_REACTIVATION_MS = 669;
|
|
3714
4201
|
var ATTR_RELOAD_TIME = 1795;
|
|
3715
4202
|
var ATTR_CAPACITY = 38;
|
|
3716
4203
|
var ATTR_VOLUME = 161;
|
|
@@ -3745,8 +4232,9 @@ function drainEntryFromEffect(effect, mod) {
|
|
|
3745
4232
|
if (effect.dischargeAttributeID === void 0) return null;
|
|
3746
4233
|
const discharge = mod.getFinal(effect.dischargeAttributeID, 0);
|
|
3747
4234
|
if (discharge <= 0) return null;
|
|
3748
|
-
const
|
|
3749
|
-
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));
|
|
3750
4238
|
return {
|
|
3751
4239
|
cycleMs,
|
|
3752
4240
|
capNeed: discharge,
|
|
@@ -3754,7 +4242,12 @@ function drainEntryFromEffect(effect, mod) {
|
|
|
3754
4242
|
// ammo doesn't reload for cap purposes on regular modules
|
|
3755
4243
|
reloadMs: 0,
|
|
3756
4244
|
isInjector: false,
|
|
3757
|
-
|
|
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)
|
|
3758
4251
|
};
|
|
3759
4252
|
}
|
|
3760
4253
|
function boosterDrainEntry(mod) {
|
|
@@ -3762,19 +4255,19 @@ function boosterDrainEntry(mod) {
|
|
|
3762
4255
|
if (!charge) return null;
|
|
3763
4256
|
const capNeed = mod.getFinal(ATTR_CAPACITOR_NEED2, 0);
|
|
3764
4257
|
const inject = charge.getFinal(ATTR_CAPACITOR_BONUS, 0);
|
|
3765
|
-
const cycleMs = mod.getFinal(ATTR_DURATION_MS2, 0);
|
|
4258
|
+
const cycleMs = Math.round(mod.getFinal(ATTR_DURATION_MS2, 0));
|
|
3766
4259
|
if (cycleMs <= 0) return null;
|
|
3767
|
-
let reloadMs = mod.getFinal(ATTR_RELOAD_TIME, 0);
|
|
4260
|
+
let reloadMs = Math.round(mod.getFinal(ATTR_RELOAD_TIME, 0));
|
|
3768
4261
|
if (reloadMs <= 0) reloadMs = 1e4;
|
|
3769
4262
|
const capacity = mod.getFinal(ATTR_CAPACITY, 0);
|
|
3770
4263
|
const chargeVol = charge.getFinal(ATTR_VOLUME, 0);
|
|
3771
|
-
const charges = capacity > 0 && chargeVol > 0 ? Math.floor(capacity / chargeVol) :
|
|
4264
|
+
const charges = capacity > 0 && chargeVol > 0 ? Math.floor(capacity / chargeVol) : 0;
|
|
3772
4265
|
return {
|
|
3773
4266
|
cycleMs,
|
|
3774
4267
|
// Pyfa convention: positive capNeed = drain, negative = injection.
|
|
3775
4268
|
// For boosters the *net* per-cycle change is (capNeed_module - inject_charge).
|
|
3776
4269
|
capNeed: capNeed - inject,
|
|
3777
|
-
clipSize:
|
|
4270
|
+
clipSize: charges,
|
|
3778
4271
|
reloadMs,
|
|
3779
4272
|
isInjector: true,
|
|
3780
4273
|
disableStagger: false
|
|
@@ -4706,8 +5199,8 @@ function computeFit(fit, dataset, opts) {
|
|
|
4706
5199
|
}
|
|
4707
5200
|
ctx.stoppedLocalEffectIDs = collectEffectStoppers(earlyProjected, dataset);
|
|
4708
5201
|
}
|
|
4709
|
-
for (const m of modules) applySourceItem(m, ctx, dataset);
|
|
4710
5202
|
for (const m of modules) if (m.charge) applySourceItem(m.charge, ctx, dataset);
|
|
5203
|
+
for (const m of modules) applySourceItem(m, ctx, dataset);
|
|
4711
5204
|
for (const d of drones) applySourceItem(d, ctx, dataset);
|
|
4712
5205
|
for (const f of fighters) applySourceItem(f, ctx, dataset);
|
|
4713
5206
|
for (const i of implants) applySourceItem(i, ctx, dataset);
|
|
@@ -4870,6 +5363,7 @@ function buildProjectionReport(source, ctx) {
|
|
|
4870
5363
|
summary: labels[cls.kind] ?? cls.kind
|
|
4871
5364
|
};
|
|
4872
5365
|
}
|
|
5366
|
+
var CHAR_BASE_MAX_LOCKED_TARGETS = 2;
|
|
4873
5367
|
function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
4874
5368
|
const ship = ctx.ship;
|
|
4875
5369
|
const slotUsed = { HI: 0, MED: 0, LO: 0, RIG: 0, SUBSYSTEM: 0, SERVICE: 0 };
|
|
@@ -4990,7 +5484,18 @@ function deriveStats(ctx, dataset, damageProfile, fit, projectionReports) {
|
|
|
4990
5484
|
ship.getFinal(ATTR.MAX_TARGET_RANGE, 0),
|
|
4991
5485
|
ship.getFinal(ATTR.MAX_TARGET_RANGE_CAP, 3e5)
|
|
4992
5486
|
),
|
|
4993
|
-
|
|
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
|
+
)),
|
|
4994
5499
|
signatureRadius: ship.getFinal(ATTR.SIGNATURE_RADIUS, 0),
|
|
4995
5500
|
scanResolution: ship.getFinal(ATTR.SCAN_RESOLUTION, 0),
|
|
4996
5501
|
sensorStrength: pickSensorStrength(ship).value,
|
|
@@ -5709,174 +6214,4 @@ var TARGET_PROFILE_PRESETS = [
|
|
|
5709
6214
|
{ name: "Keepstar", signatureRadius: 3e4, maxVelocity: 0, emResist: 0.7, thermalResist: 0.7, kineticResist: 0.7, explosiveResist: 0.7, isPreset: true }
|
|
5710
6215
|
];
|
|
5711
6216
|
|
|
5712
|
-
// src/fitChecks.ts
|
|
5713
|
-
function readAttr(t, id) {
|
|
5714
|
-
return t.attributes.find((a) => a.id === id)?.v;
|
|
5715
|
-
}
|
|
5716
|
-
function typeFitsSlotType(t, slot) {
|
|
5717
|
-
for (const e of t.effects) {
|
|
5718
|
-
const mapped = SLOT_EFFECT_TO_SLOT_TYPE[e.id];
|
|
5719
|
-
if (mapped === slot) return true;
|
|
5720
|
-
}
|
|
5721
|
-
return false;
|
|
5722
|
-
}
|
|
5723
|
-
function isTurretWeapon(t) {
|
|
5724
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "TURRET");
|
|
5725
|
-
}
|
|
5726
|
-
function isMissileLauncher(t) {
|
|
5727
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "MISSILE");
|
|
5728
|
-
}
|
|
5729
|
-
function isSmartBomb(t) {
|
|
5730
|
-
return t.effects.some((e) => WEAPON_EFFECT_KIND[e.id] === "SMARTBOMB");
|
|
5731
|
-
}
|
|
5732
|
-
function shipGroupRestrictions(t) {
|
|
5733
|
-
const out = [];
|
|
5734
|
-
for (const id of CAN_FIT_SHIP_GROUP_ATTRS) {
|
|
5735
|
-
const v = readAttr(t, id);
|
|
5736
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5737
|
-
}
|
|
5738
|
-
return out;
|
|
5739
|
-
}
|
|
5740
|
-
function shipTypeRestrictions(t) {
|
|
5741
|
-
const out = [];
|
|
5742
|
-
for (const id of CAN_FIT_SHIP_TYPE_ATTRS) {
|
|
5743
|
-
const v = readAttr(t, id);
|
|
5744
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5745
|
-
}
|
|
5746
|
-
return out;
|
|
5747
|
-
}
|
|
5748
|
-
function maxGroupFittedFor(mod) {
|
|
5749
|
-
const v = readAttr(mod, ATTR.MAX_GROUP_FITTED);
|
|
5750
|
-
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
5751
|
-
}
|
|
5752
|
-
function maxTypeFittedFor(mod) {
|
|
5753
|
-
const v = readAttr(mod, ATTR.MAX_TYPE_FITTED);
|
|
5754
|
-
return v != null && v > 0 ? Math.round(v) : void 0;
|
|
5755
|
-
}
|
|
5756
|
-
function freeFitGroupSlotsFor(mod, fittedModules, dataset) {
|
|
5757
|
-
const groupCap = maxGroupFittedFor(mod);
|
|
5758
|
-
const typeCap = maxTypeFittedFor(mod);
|
|
5759
|
-
if (groupCap === void 0 && typeCap === void 0) return Number.POSITIVE_INFINITY;
|
|
5760
|
-
let groupCount = 0;
|
|
5761
|
-
let typeCount = 0;
|
|
5762
|
-
for (const fm of fittedModules) {
|
|
5763
|
-
if (typeCap !== void 0 && fm.typeID === mod.id) typeCount++;
|
|
5764
|
-
if (groupCap !== void 0) {
|
|
5765
|
-
const t = dataset.getType(fm.typeID);
|
|
5766
|
-
if (t && t.groupID === mod.groupID) groupCount++;
|
|
5767
|
-
}
|
|
5768
|
-
}
|
|
5769
|
-
let free = Number.POSITIVE_INFINITY;
|
|
5770
|
-
if (groupCap !== void 0) free = Math.min(free, groupCap - groupCount);
|
|
5771
|
-
if (typeCap !== void 0) free = Math.min(free, typeCap - typeCount);
|
|
5772
|
-
return Math.max(0, free);
|
|
5773
|
-
}
|
|
5774
|
-
function canFitModuleOnShip(mod, ship, slot, fitContext) {
|
|
5775
|
-
if (!typeFitsSlotType(mod, slot)) {
|
|
5776
|
-
return { ok: false, reason: `Module does not declare a ${slot} slot effect` };
|
|
5777
|
-
}
|
|
5778
|
-
if (!ship) return { ok: true };
|
|
5779
|
-
const allowedGroups = shipGroupRestrictions(mod);
|
|
5780
|
-
const allowedTypes = shipTypeRestrictions(mod);
|
|
5781
|
-
if (allowedGroups.length > 0 || allowedTypes.length > 0) {
|
|
5782
|
-
const groupOk = allowedGroups.includes(ship.groupID);
|
|
5783
|
-
const typeOk = allowedTypes.includes(ship.id);
|
|
5784
|
-
if (!groupOk && !typeOk) {
|
|
5785
|
-
return { ok: false, reason: "Ship hull/group not allowed by this module" };
|
|
5786
|
-
}
|
|
5787
|
-
}
|
|
5788
|
-
if (slot === "RIG") {
|
|
5789
|
-
const modSize = readAttr(mod, ATTR.RIG_SIZE);
|
|
5790
|
-
const shipSize = readAttr(ship, ATTR.RIG_SIZE);
|
|
5791
|
-
if (modSize != null && shipSize != null && Math.round(modSize) !== Math.round(shipSize)) {
|
|
5792
|
-
return { ok: false, reason: "Rig size mismatch" };
|
|
5793
|
-
}
|
|
5794
|
-
}
|
|
5795
|
-
if (slot === "SUBSYSTEM") {
|
|
5796
|
-
const fitsTo = readAttr(mod, 1380);
|
|
5797
|
-
if (fitsTo != null && fitsTo > 0 && Math.round(fitsTo) !== ship.id) {
|
|
5798
|
-
return { ok: false, reason: "Subsystem locked to a different T3C hull" };
|
|
5799
|
-
}
|
|
5800
|
-
}
|
|
5801
|
-
if (slot === "HI") {
|
|
5802
|
-
if (isTurretWeapon(mod)) {
|
|
5803
|
-
const turrets = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
5804
|
-
if (turrets <= 0) {
|
|
5805
|
-
return { ok: false, reason: "Ship has no turret hardpoints" };
|
|
5806
|
-
}
|
|
5807
|
-
} else if (isMissileLauncher(mod)) {
|
|
5808
|
-
const launchers = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
5809
|
-
if (launchers <= 0) {
|
|
5810
|
-
return { ok: false, reason: "Ship has no launcher hardpoints" };
|
|
5811
|
-
}
|
|
5812
|
-
}
|
|
5813
|
-
}
|
|
5814
|
-
if (fitContext) {
|
|
5815
|
-
const free = freeFitGroupSlotsFor(mod, fitContext.fittedModules, fitContext.dataset);
|
|
5816
|
-
if (free <= 0) {
|
|
5817
|
-
const cap = maxTypeFittedFor(mod) ?? maxGroupFittedFor(mod);
|
|
5818
|
-
return { ok: false, reason: `Only ${cap} of this module type can be fitted to a ship` };
|
|
5819
|
-
}
|
|
5820
|
-
}
|
|
5821
|
-
return { ok: true };
|
|
5822
|
-
}
|
|
5823
|
-
function chargeGroupsForModule(mod) {
|
|
5824
|
-
const out = [];
|
|
5825
|
-
for (const attrID of CHARGE_GROUP_ATTRS) {
|
|
5826
|
-
const v = readAttr(mod, attrID);
|
|
5827
|
-
if (v != null && v > 0) out.push(Math.round(v));
|
|
5828
|
-
}
|
|
5829
|
-
return out;
|
|
5830
|
-
}
|
|
5831
|
-
function moduleAcceptsAnyCharge(mod) {
|
|
5832
|
-
return chargeGroupsForModule(mod).length > 0;
|
|
5833
|
-
}
|
|
5834
|
-
function moduleAcceptsChargeType(mod, charge) {
|
|
5835
|
-
const allowedGroups = chargeGroupsForModule(mod);
|
|
5836
|
-
if (allowedGroups.length === 0) return false;
|
|
5837
|
-
if (!allowedGroups.includes(charge.groupID)) return false;
|
|
5838
|
-
const modSize = readAttr(mod, ATTR.CHARGE_SIZE);
|
|
5839
|
-
const chargeSize = readAttr(charge, ATTR.CHARGE_SIZE);
|
|
5840
|
-
if (modSize != null && chargeSize != null && Math.round(chargeSize) > Math.round(modSize)) {
|
|
5841
|
-
return false;
|
|
5842
|
-
}
|
|
5843
|
-
return true;
|
|
5844
|
-
}
|
|
5845
|
-
var ACTIVE_EFFECT_CATEGORIES = /* @__PURE__ */ new Set([1, 2, 3]);
|
|
5846
|
-
function isActivatableModule(mod, effects) {
|
|
5847
|
-
for (const e of mod.effects) {
|
|
5848
|
-
const eff = effects.get(e.id);
|
|
5849
|
-
if (eff && eff.effectCategoryID !== void 0 && ACTIVE_EFFECT_CATEGORIES.has(eff.effectCategoryID)) {
|
|
5850
|
-
return true;
|
|
5851
|
-
}
|
|
5852
|
-
}
|
|
5853
|
-
return false;
|
|
5854
|
-
}
|
|
5855
|
-
var DEFAULT_OFFLINE_ACTIVATION_GROUPS = /* @__PURE__ */ new Set([
|
|
5856
|
-
330,
|
|
5857
|
-
4117
|
|
5858
|
-
]);
|
|
5859
|
-
function defaultStateForModule(mod, effects) {
|
|
5860
|
-
if (DEFAULT_OFFLINE_ACTIVATION_GROUPS.has(mod.groupID)) return "ONLINE";
|
|
5861
|
-
return isActivatableModule(mod, effects) ? "ACTIVE" : "ONLINE";
|
|
5862
|
-
}
|
|
5863
|
-
function freeHardpointsFor(mod, ship, fittedHiModules, dataset) {
|
|
5864
|
-
const turretCap = readAttr(ship, ATTR.TURRET_HARDPOINTS) ?? 0;
|
|
5865
|
-
const launcherCap = readAttr(ship, ATTR.LAUNCHER_HARDPOINTS) ?? 0;
|
|
5866
|
-
let turretUsed = 0;
|
|
5867
|
-
let launcherUsed = 0;
|
|
5868
|
-
for (const m of fittedHiModules) {
|
|
5869
|
-
const t = dataset.getType(m.typeID);
|
|
5870
|
-
if (!t) continue;
|
|
5871
|
-
if (isTurretWeapon(t)) turretUsed++;
|
|
5872
|
-
else if (isMissileLauncher(t)) launcherUsed++;
|
|
5873
|
-
}
|
|
5874
|
-
let hardpointFree;
|
|
5875
|
-
if (isTurretWeapon(mod)) hardpointFree = Math.max(0, turretCap - turretUsed);
|
|
5876
|
-
else if (isMissileLauncher(mod)) hardpointFree = Math.max(0, launcherCap - launcherUsed);
|
|
5877
|
-
else hardpointFree = Number.POSITIVE_INFINITY;
|
|
5878
|
-
const groupFree = freeFitGroupSlotsFor(mod, fittedHiModules, dataset);
|
|
5879
|
-
return Math.min(hardpointFree, groupFree);
|
|
5880
|
-
}
|
|
5881
|
-
|
|
5882
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 };
|