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