Starsector API
Loading...
Searching...
No Matches
CoreAutofitPlugin.java
Go to the documentation of this file.
1package com.fs.starfarer.api.plugins.impl;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.Iterator;
9import java.util.LinkedHashMap;
10import java.util.LinkedHashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Random;
14import java.util.Set;
15
16import com.fs.starfarer.api.Global;
17import com.fs.starfarer.api.campaign.CampaignFleetAPI;
18import com.fs.starfarer.api.campaign.FactionAPI;
19import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
20import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI;
21import com.fs.starfarer.api.characters.OfficerDataAPI;
22import com.fs.starfarer.api.characters.PersonAPI;
23import com.fs.starfarer.api.characters.SkillSpecAPI;
24import com.fs.starfarer.api.combat.ShieldAPI.ShieldType;
25import com.fs.starfarer.api.combat.ShipAPI;
26import com.fs.starfarer.api.combat.ShipAPI.HullSize;
27import com.fs.starfarer.api.combat.ShipHullSpecAPI;
28import com.fs.starfarer.api.combat.ShipVariantAPI;
29import com.fs.starfarer.api.combat.WeaponAPI.AIHints;
30import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
31import com.fs.starfarer.api.fleet.FleetMemberAPI;
32import com.fs.starfarer.api.impl.campaign.DModManager;
33import com.fs.starfarer.api.impl.campaign.HullModItemManager;
34import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflater;
35import com.fs.starfarer.api.impl.campaign.ids.Factions;
36import com.fs.starfarer.api.impl.campaign.ids.HullMods;
37import com.fs.starfarer.api.impl.campaign.ids.Skills;
38import com.fs.starfarer.api.impl.campaign.ids.Tags;
39import com.fs.starfarer.api.impl.campaign.tutorial.TutorialMissionIntel;
40import com.fs.starfarer.api.loading.FighterWingSpecAPI;
41import com.fs.starfarer.api.loading.HullModSpecAPI;
42import com.fs.starfarer.api.loading.VariantSource;
43import com.fs.starfarer.api.loading.WeaponGroupSpec;
44import com.fs.starfarer.api.loading.WeaponSlotAPI;
45import com.fs.starfarer.api.loading.WeaponSpecAPI;
46import com.fs.starfarer.api.util.Misc;
47import com.fs.starfarer.api.util.WeightedRandomPicker;
48
50
51 public static float RANDOMIZE_CHANCE = 0.5f;
52
53 public static int PRIORITY = 1000;
54
55 public static String BUY_FROM_MARKET = new String("buy_from_market");
56 public static String USE_FROM_CARGO = new String("use_from_cargo");
57 public static String USE_FROM_STORAGE = new String("use_from_storage");
58 public static String BUY_FROM_BLACK_MARKET = new String("black_market");
59 //public static String USE_BETTER = new String("use_better");
60 public static String UPGRADE = new String("upgrade");
61 public static String ALWAYS_REINFORCED_HULL = new String("always_reinforced_hull");
62 public static String ALWAYS_BLAST_DOORS = new String("always_blast_doors");
63 public static String STRIP = new String("strip");
64 public static String RANDOMIZE = new String("randomize");
65 //public static String USE_OTHER = new String("use_other");
66
67
68 public static String LR = "LR";
69 public static String SR = "SR";
70
71 public static String KINETIC = "kinetic";
72 public static String HE = "he";
73 public static String ENERGY = "energy";
74 public static String PD = "pd";
75 public static String BEAM = "beam";
76
77 public static String STRIKE = "strike";
78 public static String MISSILE = "missile";
79 public static String UTILITY = "utility";
80 public static String ROCKET = "rocket";
81
82 public static String INTERCEPTOR = "interceptor";
83 public static String BOMBER = "bomber";
84 public static String FIGHTER = "fighter";
85 public static String SUPPORT = "support";
86
87
88 protected static Map<String, Category> reusableCategories = null;
89
90 public static class Category {
91 public String base;
92 public Set<String> tags = new HashSet<String>();
93
94 public List<String> fallback = new ArrayList<String>();
95
96 public Category(String base, Map<String, Category> categories) {
97 this.base = base;
98
99 categories.put(base, this);
100 for (int i = 0; i < 100; i++) {
101 String id = base + i;
102 tags.add(id);
103 categories.put(id, this);
104 }
105 }
106
107 public void addFallback(String ... categories) {
108 for (String catId : categories) {
109 fallback.add(catId);
110 }
111 }
112 }
113
114 protected List<AutofitOption> options = new ArrayList<AutofitOption>();
115
116 protected Map<String, Category> categories = new LinkedHashMap<String, Category>();
117
118 protected Map<WeaponSpecAPI, List<String>> altWeaponCats = new LinkedHashMap<WeaponSpecAPI, List<String>>();
119 protected Map<FighterWingSpecAPI, List<String>> altFighterCats = new LinkedHashMap<FighterWingSpecAPI, List<String>>();
120
121 protected boolean debug = false;
124
125 protected Random random;
126
127 protected boolean randomize = false;
128 protected long weaponFilterSeed = 0;
129 protected String emptyWingTarget = null;
130
131 public Random getRandom() {
132 return random;
133 }
134
135 public void setRandom(Random random) {
136 this.random = random;
137 }
138
139 public boolean isChecked(String id) {
140 for (AutofitOption option : options) {
141 if (option.id.equals(id)) return option.checked;
142 }
143 return false;
144 }
145
146 public void setChecked(String id, boolean checked) {
147 for (AutofitOption option : options) {
148 if (option.id.equals(id)) {
149 option.checked = checked;
150 return;
151 }
152 }
153 }
154
156 this.fleetCommander = fleetCommander;
158 options.add(new AutofitOption(USE_FROM_CARGO, "Use ordnance from cargo", true,
159 "Use weapons and fighter LPCs from your fleet's cargo holds."));
160 options.add(new AutofitOption(USE_FROM_STORAGE, "Use ordnance from storage", true,
161 "Use weapons and fighter LPCs from your local storage facilities."));
162 options.add(new AutofitOption(BUY_FROM_MARKET, "Buy ordnance from market", true,
163 "Buy weapons and fighter LPCs from market, if docked at one.\n\n" +
164 "Ordnance from your cargo will be preferred if that option is checked and if the alternatives are of equal quality."));
165 options.add(new AutofitOption(BUY_FROM_BLACK_MARKET, "Allow black market purchases", true,
166 "Buy weapons and fighter LPCs from the black market.\n\n" +
167 "Non-black-market options will be preferred if the alternatives are of equal quality."));
168 options.add(new AutofitOption(UPGRADE, "Upgrade weapons using extra OP", false,
169 "Use weapons better than the ones specified in the goal variant, if there are ordnance points left to mount them.\n\n" +
170 "Will add flux vents and capacitors up to the number specified in the goal variant first, " +
171 "then upgrade weapons, and then add more vents and some common hullmods.\n\n" +
172 "Leaving some unspent ordnance points in a goal variant can help take advantage of this option."));
173 options.add(new AutofitOption(STRIP, "Strip before autofitting", true,
174 "Remove everything possible prior to autofitting; generally results in a better fit.\n\n" +
175 "However, refitting outside of port reduces a ship's combat readiness, and this option tends to lead to more changes and more readiness lost."));
176 options.add(new AutofitOption(ALWAYS_REINFORCED_HULL, "Always add \"Reinforced Bulkheads\"", false,
177 "Prioritizes installing the \"Reinforced Bulkheads\" hullmod, which increases hull integrity and " +
178 "makes a ship virtually certain to be recoverable if lost in battle.\n\n" +
179 "\"Reinforced Bulkheads\" may still be added if this option isn't checked, provided there are enough ordnance points."));
180 options.add(new AutofitOption(ALWAYS_BLAST_DOORS, "Always add \"Blast Doors\"", false,
181 "Prioritizes installing the \"Blast Doors\" hullmod, which increases hull integrity and " +
182 "greatly reduces crew losses suffered due to hull damage.\n\n" +
183 "\"Blast Doors\" may still be added if this option isn't checked, provided there are enough ordnance points."));
184 options.add(new AutofitOption(RANDOMIZE, "Randomize weapons and hullmods", false,
185 "Makes the loadout only loosely based on the goal variant."));
186
187
188 //reusableCategories = null;
189 if (reusableCategories != null) {
191 } else {
192 new Category(KINETIC, categories).addFallback(KINETIC, ENERGY, HE, BEAM, PD, ROCKET, MISSILE, UTILITY, STRIKE);
193 new Category(HE, categories).addFallback(HE, ENERGY, KINETIC, BEAM, PD, ROCKET, MISSILE, UTILITY, STRIKE);
194 new Category(ENERGY, categories).addFallback(ENERGY, KINETIC, HE, BEAM, PD, ROCKET, MISSILE, UTILITY, STRIKE);
195 new Category(PD, categories).addFallback(PD, BEAM, HE, KINETIC, UTILITY, ROCKET, MISSILE, STRIKE);
196 new Category(BEAM, categories).addFallback(BEAM, ENERGY, HE, KINETIC, ROCKET, MISSILE, UTILITY, STRIKE);
197
198 new Category(STRIKE, categories).addFallback(STRIKE, MISSILE, ROCKET, HE, ENERGY, KINETIC, UTILITY, BEAM, PD);
199 new Category(MISSILE, categories).addFallback(MISSILE, STRIKE, ROCKET, HE, ENERGY, KINETIC, UTILITY, BEAM, PD);
200 new Category(UTILITY, categories).addFallback(UTILITY, MISSILE, ROCKET, STRIKE, HE, KINETIC, ENERGY, BEAM, PD);
201 new Category(ROCKET, categories).addFallback(ROCKET, UTILITY, MISSILE, STRIKE, HE, ENERGY, KINETIC, BEAM, PD);
202
203 new Category(INTERCEPTOR, categories).addFallback(INTERCEPTOR, FIGHTER, SUPPORT, BOMBER);
204 new Category(BOMBER, categories).addFallback(BOMBER, FIGHTER, INTERCEPTOR, SUPPORT);
205 new Category(FIGHTER, categories).addFallback(FIGHTER, INTERCEPTOR, BOMBER, SUPPORT);
206 new Category(SUPPORT, categories).addFallback(SUPPORT, INTERCEPTOR, FIGHTER, BOMBER);
207
209 }
210
211
212 //RANDOMIZE_CHANCE = 0.5f;
213 //RANDOMIZE_CHANCE = 1f;
214
215 //if (random == null) random = new Random();
216 }
217
218
219 protected void stripWeapons(ShipVariantAPI current, AutofitPluginDelegate delegate) {
220 for (String id : current.getFittedWeaponSlots()) {
221 WeaponSlotAPI slot = current.getSlot(id);
222 if (slot.isDecorative() || slot.isBuiltIn() || slot.isHidden() ||
223 slot.isSystemSlot() || slot.isStationModule()) continue;
224 clearWeaponSlot(slot, delegate, current);
225 }
226 }
227
228 protected void stripFighters(ShipVariantAPI current, AutofitPluginDelegate delegate) {
229 int numBays = 20; // well above whatever it might actually be
230 for (int i = 0; i < numBays; i++) {
231 if (current.getWingId(i) != null) {
232 clearFighterSlot(i, delegate, current);
233 }
234 }
235 }
236
237// protected Map<WeaponSlotAPI, AvailableWeapon> fittedWeapons = new HashMap<WeaponSlotAPI, AvailableWeapon>();
238// protected Map<Integer, AvailableFighter> fittedFighters = new HashMap<Integer, AvailableFighter>();
239 protected Map<String, AvailableWeapon> fittedWeapons = new HashMap<String, AvailableWeapon>();
240 protected Map<String, AvailableFighter> fittedFighters = new HashMap<String, AvailableFighter>();
241
242
243 public int getCreditCost() {
244 int cost = 0;
245 for (AvailableWeapon w : fittedWeapons.values()) {
246 cost += w.getPrice();
247 }
248 for (AvailableFighter w : fittedFighters.values()) {
249 cost += w.getPrice();
250 }
251 return cost;
252 }
253
254 protected Set<String> availableMods;
255 protected Set<String> slotsToSkip = new HashSet<String>();
256 protected Set<Integer> baysToSkip = new HashSet<Integer>();
257 protected boolean fittingModule = false;
258 protected int missilesWithAmmoOnCurrent = 0;
259 public void doFit(ShipVariantAPI current, ShipVariantAPI target, int maxSMods, AutofitPluginDelegate delegate) {
260
261
262// if (stats == null) {
263// stats = Global.getFactory().createPerson().getStats();
264// stats.getShipOrdnancePointBonus().modifyPercent("test", 10f);
265// stats.getMaxVentsBonus().modifyPercent("test", 20f);
266// stats.getMaxCapacitorsBonus().modifyPercent("test", 20f);
267// }
268 boolean player = fleetCommander != null && fleetCommander.isPlayer();
269
270 if (!fittingModule) {
271 fittedWeapons.clear();
272 fittedFighters.clear();
273
275
276 availableMods = new LinkedHashSet<String>(delegate.getAvailableHullmods());
277 }
278
279// if (fittingModule && current.getHullSpec().getHullId().equals("module_hightech_hangar")) {
280// System.out.println("wfweffewfew");
281// }
282
283 current.setMayAutoAssignWeapons(false);
284 current.getStationModules().putAll(target.getStationModules());
285
286 int index = 0;
287 for (String slotId : current.getStationModules().keySet()) {
288 ShipVariantAPI moduleCurrent = current.getModuleVariant(slotId);
289 boolean forceClone = false;
290 if (moduleCurrent == null) {
291 // when the target variant is not stock and has custom variants for the modules, grab them
292 forceClone = true;
293 moduleCurrent = target.getModuleVariant(slotId);
294 //continue;
295 }
296 if (moduleCurrent == null) {
297 String variantId = current.getHullVariantId();
298 throw new RuntimeException("Module variant for slotId [" + slotId + "] not found for " +
299 "variantId [" + variantId + "] of hull [" + current.getHullSpec().getHullId() + "]");
300 //continue;
301 }
302 if (moduleCurrent.isStockVariant() || forceClone) {
303 moduleCurrent = moduleCurrent.clone();
304 moduleCurrent.setSource(VariantSource.REFIT);
305 if (!forceClone) {
306 moduleCurrent.setHullVariantId(moduleCurrent.getHullVariantId() + "_" + index);
307 }
308 }
309 index++;
310
311// String variantId = current.getStationModules().get(slotId);
312// ShipVariantAPI moduleTarget = Global.getSettings().getVariant(variantId);
313 ShipVariantAPI moduleTarget = target.getModuleVariant(slotId);
314 if (moduleTarget == null) continue;
315
316 fittingModule = true;
317 doFit(moduleCurrent, moduleTarget, 0, delegate);
318 fittingModule = false;
319
320 current.setModuleVariant(slotId, moduleCurrent);
321 }
323
324 weaponFilterSeed = random.nextLong();
325
326 emptyWingTarget = null;
327 if (delegate.getAvailableFighters().size() > 0) {
328 emptyWingTarget = delegate.getAvailableFighters().get(random.nextInt(delegate.getAvailableFighters().size())).getId();
329 }
330
331 altWeaponCats.clear();
332 altFighterCats.clear();
333
334 slotsToSkip.clear();
335 baysToSkip.clear();
336
338
339 boolean strip = isChecked(STRIP);
340 if (strip) {
341 stripWeapons(current, delegate);
342 stripFighters(current, delegate);
343
344 current.setNumFluxCapacitors(0);
345 current.setNumFluxVents(0);
346 if (delegate.isPlayerCampaignRefit()) {
347 for (String modId : current.getNonBuiltInHullmods()) {
348 boolean canRemove = delegate.canAddRemoveHullmodInPlayerCampaignRefit(modId);
349 if (canRemove) {
350 current.removeMod(modId);
351 }
352 }
353 } else {
354 current.clearHullMods();
355 }
356 if (!fittingModule) {
357 delegate.syncUIWithVariant(current);
358 }
359 } else {
360 slotsToSkip.addAll(current.getFittedWeaponSlots());
361 for (int i = 0; i < 20; i++) {
362 String wingId = current.getWingId(i);
363 if (wingId != null && !wingId.isEmpty()) {
364 baysToSkip.add(i);
365 }
366 }
367 }
368
369 //boolean randomize = isChecked(RANDOMIZE);
370
371
372 boolean reinforcedHull = isChecked(ALWAYS_REINFORCED_HULL);
373 boolean blastDoors = isChecked(ALWAYS_BLAST_DOORS);
374
375 if (reinforcedHull) {
376 addHullmods(current, delegate, HullMods.REINFORCEDHULL);
377 }
378 if (blastDoors) {
379 addHullmods(current, delegate, HullMods.BLAST_DOORS);
380 }
381
382 List<String> targetMods = new ArrayList<String>();
383 for (String id : target.getSMods()) {
384 if (target.getSModdedBuiltIns().contains(id)) continue;
385 targetMods.add(id);
386 }
387 for (String id : target.getNonBuiltInHullmods()) {
388 //if (HullMods.FLUX_DISTRIBUTOR.equals(id) || HullMods.FLUX_COIL.equals(id)) continue;
389 targetMods.add(id);
390 }
391 if (!targetMods.isEmpty()) {
392 addHullmods(current, delegate, targetMods.toArray(new String[0]));
393 }
394
395 int addedRandomHullmodPts = 0;
396 if (randomize) {
397 addedRandomHullmodPts = addRandomizedHullmodsPre(current, delegate);
398 }
399
400
401 fitFighters(current, target, false, delegate);
402 fitWeapons(current, target, false, delegate);
403
404 if (current.hasHullMod(HullMods.FRAGILE_SUBSYSTEMS) &&
405 (current.getHullSize() == HullSize.FRIGATE || current.getHullSize() == HullSize.DESTROYER)) {
406 addHullmods(current, delegate, HullMods.HARDENED_SUBSYSTEMS);
407 }
408
409
410 float addedMax = current.getHullSpec().getOrdnancePoints(stats) * 0.1f;
411 if (randomize && addedRandomHullmodPts <= addedMax) {
412 addRandomizedHullmodsPost(current, delegate);
413 }
414
415 float ventsCapsFraction = 1f;
416 boolean upgrade = isChecked(UPGRADE);
417 if (upgrade) {
418 ventsCapsFraction = 0.5f;
419 //ventsCapsFraction = 0f;
420 }
421
422 addVentsAndCaps(current, target, ventsCapsFraction);
423
424
425 // now that we're at the target level of vents and caps
426 // see if we can upgrade some weapons
427 if (upgrade) {
428 fitFighters(current, target, true, delegate);
429 fitWeapons(current, target, true, delegate);
430 addVentsAndCaps(current, target, 1f - ventsCapsFraction);
431 }
432
433// float dissipation = current.getHullSpec().getFluxDissipation() + current.getNumFluxVents() * 10f;
434// float generation = 0f;
435// for (String slotId : current.getFittedWeaponSlots()) {
436// WeaponSpecAPI spec = current.getWeaponSpec(slotId);
437// generation += spec.getDerivedStats().getSustainedFluxPerSecond();
438// }
439
440 addExtraVentsAndCaps(current, target);
442 addModsWithSpareOPIfAny(current, target, false, delegate);
443
444 //maxSMods = 2;
445 if (maxSMods > 0) {
446 int added = convertToSMods(current, maxSMods);
447 addExtraVents(current);
448 addExtraCaps(current);
449 //addHullmods(current, delegate, HullMods.FLUX_DISTRIBUTOR, HullMods.FLUX_COIL);
450 if (!current.hasHullMod(HullMods.FLUX_DISTRIBUTOR)) {
451 addDistributor(current, delegate);
452 }
453 if (!current.hasHullMod(HullMods.FLUX_COIL)) {
454 addCoil(current, delegate);
455 }
456 //addModsWithSpareOPIfAny(current, target, true, delegate);
457 //addHullmods(current, delegate, HullMods.FLUX_DISTRIBUTOR, HullMods.FLUX_COIL);
458 if (current.getHullSize() == HullSize.FRIGATE || current.hasHullMod(HullMods.SAFETYOVERRIDES)) {
460 } else {
462 }
463 int remaining = maxSMods - added;
464 if (remaining > 0) {
465 List<String> mods = new ArrayList<String>();
466 mods.add(HullMods.FLUX_DISTRIBUTOR);
467 mods.add(HullMods.FLUX_COIL);
468 if (current.getHullSize() == HullSize.FRIGATE || current.hasHullMod(HullMods.SAFETYOVERRIDES)) {
470 mods.add(HullMods.REINFORCEDHULL);
471 } else {
472 mods.add(HullMods.REINFORCEDHULL);
474 }
475 mods.add(HullMods.BLAST_DOORS);
476 Iterator<String> iter = mods.iterator();
477 while (iter.hasNext()) {
478 String modId = iter.next();
479 if (current.getPermaMods().contains(modId)) {
480 iter.remove();
481 }
482 }
483// while (!mods.isEmpty() && current.hasHullMod(mods.get(0))) {
484// mods.remove(0);
485// }
486 for (int i = 0; i < remaining && !mods.isEmpty(); i++) {
487 current.setNumFluxCapacitors(0);
488 current.setNumFluxVents(0);
489 String modId = mods.get(Math.min(i, mods.size() - 1));
490 addHullmods(current, delegate, modId);
491 convertToSMods(current, 1);
492// addExtraVents(current);
493// addExtraCaps(current);
494 }
495 }
496 }
497
498
499 if (current.getHullSpec().isPhase()) {
500 addExtraCaps(current);
501 } else {
502 addExtraVents(current);
503 }
504
505 addHullmods(current, delegate, HullMods.ARMOREDWEAPONS);
506 int opCost = current.computeOPCost(stats);
507 int opMax = current.getHullSpec().getOrdnancePoints(stats);
508 int opLeft = opMax - opCost;
509 if (opLeft > 0) {
510 addRandomizedHullmodsPost(current, delegate);
511 }
512
513 if (current.getHullSpec().isPhase()) {
514 addExtraVents(current);
515 } else {
516 addExtraCaps(current);
517 }
518
519
520 current.setVariantDisplayName(target.getDisplayName());
521
522 current.getWeaponGroups().clear();
523 for (WeaponGroupSpec group : target.getWeaponGroups()) {
524 WeaponGroupSpec copy = new WeaponGroupSpec(group.getType());
525 copy.setAutofireOnByDefault(group.isAutofireOnByDefault());
526 for (String slotId : group.getSlots()) {
527 if (current.getWeaponId(slotId) != null) {
528 copy.addSlot(slotId);
529 }
530 }
531 if (!copy.getSlots().isEmpty()) {
532 current.addWeaponGroup(copy);
533 }
534 }
535
536 if (player) {
537 if (current.getWeaponGroups().isEmpty() || randomize || current.hasUnassignedWeapons()) {
538 current.setMayAutoAssignWeapons(true);
539 current.autoGenerateWeaponGroups();
540 }
541 //current.assignUnassignedWeapons();
542 } else {
543 current.getWeaponGroups().clear(); // will get auto-assigned when deployed in combat; until then don't care
544 }
545
546 if (!fittingModule) {
547 delegate.syncUIWithVariant(current);
548 }
549 }
550
551 protected int convertToSMods(ShipVariantAPI current, int num) {
552 if (num <= 0) return 0;
553
554 List<HullModSpecAPI> mods = new ArrayList<HullModSpecAPI>();
555 for (String id : current.getHullMods()) {
556 if (current.getPermaMods().contains(id)) continue;
557 if (current.getHullSpec().getBuiltInMods().contains(id)) continue;
559 if (mod.hasTag(Tags.HULLMOD_NO_BUILD_IN)) continue;
560 mods.add(mod);
561 }
562
563 final HullSize size = current.getHullSize();
564 Collections.sort(mods, new Comparator<HullModSpecAPI>() {
565 public int compare(HullModSpecAPI o1, HullModSpecAPI o2) {
566 return Misc.getOPCost(o2, size) - Misc.getOPCost(o1, size);
567 }
568 });
569
570 int count = 0;
571 for (int i = 0; i < num && i < mods.size(); i++) {
572 String id = mods.get(i).getId();
573 current.addPermaMod(id, true);
574 count++;
575 }
576 return count;
577 }
578
579 protected void addModsWithSpareOPIfAny(ShipVariantAPI current, ShipVariantAPI target, boolean sModMode, AutofitPluginDelegate delegate) {
580 int opCost = current.computeOPCost(stats);
581 int opMax = current.getHullSpec().getOrdnancePoints(stats);
582 int opLeft = opMax - opCost;
583
584 if (opLeft <= 0) return;
585
586 float total = target.getNumFluxVents() + target.getNumFluxCapacitors();
587 float ventsFraction = 1f;
588 if (total > 0) {
589 ventsFraction = target.getNumFluxVents() / total;
590 }
591
592 if (sModMode) {
593 if (ventsFraction >= 0.5f) {
594 addDistributorRemoveVentsIfNeeded(current, delegate);
595 addCoilRemoveCapsIfNeeded(current, delegate);
596 } else {
597 addCoil(current, delegate);
598 addCoilRemoveCapsIfNeeded(current, delegate);
599 }
600 } else {
601 if (ventsFraction >= 0.5f) {
602 addDistributor(current, delegate);
603 addCoil(current, delegate);
604 } else {
605 addCoil(current, delegate);
606 addDistributor(current, delegate);
607 }
608 }
609 }
610
611 protected void addCoil(ShipVariantAPI current, AutofitPluginDelegate delegate) {
612 int opCost = current.computeOPCost(stats);
613 int opMax = current.getHullSpec().getOrdnancePoints(stats);
614 int opLeft = opMax - opCost;
615
616 if (opLeft <= 0) return;
617
618 int vents = current.getNumFluxVents();
619
621 int cost = coil.getCostFor(current.getHullSize());
622
623 if (cost < opLeft + vents * 0.3f) {
624 int remove = cost - opLeft;
625 if (remove > 0) {
626 opLeft -= addVents(-remove, current, 1000);
627 }
628 opLeft -= addModIfPossible(HullMods.FLUX_COIL, delegate, current, opLeft);
629 }
630 }
631
633 int opCost = current.computeOPCost(stats);
634 int opMax = current.getHullSpec().getOrdnancePoints(stats);
635 int opLeft = opMax - opCost;
636
637 if (opLeft <= 0) return;
638
639 int caps = current.getNumFluxCapacitors();
640
642 int cost = coil.getCostFor(current.getHullSize());
643
644 if (cost < opLeft + caps * 0.3f) {
645 int remove = cost - opLeft;
646 if (remove > 0) {
647 opLeft -= addCapacitors(-remove, current, 1000);
648 }
649 opLeft -= addModIfPossible(HullMods.FLUX_COIL, delegate, current, opLeft);
650 }
651 }
652
653 protected void addDistributor(ShipVariantAPI current, AutofitPluginDelegate delegate) {
654 int opCost = current.computeOPCost(stats);
655 int opMax = current.getHullSpec().getOrdnancePoints(stats);
656 int opLeft = opMax - opCost;
657
658 if (opLeft <= 0) return;
659
660 int caps = current.getNumFluxCapacitors();
661
663 int cost = distributor.getCostFor(current.getHullSize());
664
665 if (cost <= opLeft + caps * 0.3f) {
666 int remove = cost - opLeft;
667 if (remove > 0) {
668 opLeft -= addCapacitors(-remove, current, 1000);
669 }
670 opLeft -= addModIfPossible(HullMods.FLUX_DISTRIBUTOR, delegate, current, opLeft);
671 }
672 }
673
675 int opCost = current.computeOPCost(stats);
676 int opMax = current.getHullSpec().getOrdnancePoints(stats);
677 int opLeft = opMax - opCost;
678
679 if (opLeft <= 0) return;
680
681 int vents = current.getNumFluxVents();
682
684 int cost = distributor.getCostFor(current.getHullSize());
685
686 if (cost <= opLeft + vents * 0.3f) {
687 int remove = cost - opLeft;
688 if (remove > 0) {
689 opLeft -= addVents(-remove, current, 1000);
690 }
691 opLeft -= addModIfPossible(HullMods.FLUX_DISTRIBUTOR, delegate, current, opLeft);
692 }
693 }
694
695
696
697 protected List<AvailableWeapon> getWeapons(AutofitPluginDelegate delegate) {
698 boolean buy = isChecked(BUY_FROM_MARKET);
699 boolean storage = isChecked(USE_FROM_STORAGE);
700 boolean useCargo = isChecked(USE_FROM_CARGO);
701 boolean useBlack = isChecked(BUY_FROM_BLACK_MARKET);
702
703 List<AvailableWeapon> weapons = new ArrayList<AvailableWeapon>(delegate.getAvailableWeapons());
704
705 Iterator<AvailableWeapon> iter = weapons.iterator();
706 while (iter.hasNext()) {
707 AvailableWeapon w = iter.next();
708 if ((!buy && w.getPrice() > 0) ||
709 (!storage && w.getPrice() <= 0 && w.getSubmarket() != null) ||
710 (!useCargo && w.getSubmarket() == null) ||
711 (!useBlack && w.getSubmarket() != null && w.getSubmarket().getPlugin().isBlackMarket())) {
712 iter.remove();
713 }
714 }
715 return weapons;
716 }
717
718 protected List<AvailableFighter> getFighters(AutofitPluginDelegate delegate) {
719 boolean buy = isChecked(BUY_FROM_MARKET);
720 boolean storage = isChecked(USE_FROM_STORAGE);
721 boolean useCargo = isChecked(USE_FROM_CARGO);
722 boolean useBlack = isChecked(BUY_FROM_BLACK_MARKET);
723
724 boolean automated = Misc.isAutomated(delegate.getShip());
725 List<AvailableFighter> fighters = new ArrayList<AvailableFighter>(delegate.getAvailableFighters());
726 Iterator<AvailableFighter> iter = fighters.iterator();
727 while (iter.hasNext()) {
728 AvailableFighter f = iter.next();
729 if ((!buy && f.getPrice() > 0) ||
730 (automated && !f.getWingSpec().hasTag(Tags.AUTOMATED_FIGHTER)) ||
731 (!storage && f.getPrice() <= 0 && f.getSubmarket() != null) ||
732 (!useCargo && f.getSubmarket() == null) ||
733 (!useBlack && f.getSubmarket() != null && f.getSubmarket().getPlugin().isBlackMarket())) {
734 iter.remove();
735 }
736 }
737 return fighters;
738 }
739
740 public int addHullmods(ShipVariantAPI current, AutofitPluginDelegate delegate, String ... mods) {
741 if (fittingModule) return 0;
742
743 int opCost = current.computeOPCost(stats);
744 int opMax = current.getHullSpec().getOrdnancePoints(stats);
745 int opLeft = opMax - opCost;
746
747 int addedTotal = 0;
748 for (String mod : mods) {
749 if (current.hasHullMod(mod)) continue;
750// if (mod.equals(HullMods.INTEGRATED_TARGETING_UNIT)) {
751// System.out.println("wefwefwefe");
752// }
753 if (!availableMods.contains(mod)) {
754 if (mod.equals(HullMods.INTEGRATED_TARGETING_UNIT) &&
755 current.getHullSize().ordinal() >= HullSize.CRUISER.ordinal()) {
757 } else {
758 continue;
759 }
760 }
761
762 if (mod.equals(HullMods.DEDICATED_TARGETING_CORE) &&
765 }
766
767 HullModSpecAPI modSpec = Misc.getMod(mod);
768
769 if (mod.equals(HullMods.INTEGRATED_TARGETING_UNIT) &&
773 int cost = dtc.getCostFor(current.getHullSize());;
774 addedTotal -= cost;
775 opLeft += cost;
776 }
777
778
780 if (mod.equals(HullMods.INTEGRATED_TARGETING_UNIT)) {
781 continue;
782 }
783 if (mod.equals(HullMods.DEDICATED_TARGETING_CORE)) {
784 continue;
785 }
786 }
787
788 if (current.getHullSpec().isPhase()) {
789 if (modSpec.hasTag(HullMods.TAG_NON_PHASE)) {
790 continue;
791 }
792 }
793 if (!current.getHullSpec().isPhase()) {
794 if (modSpec.hasTag(HullMods.TAG_PHASE)) {
795 continue;
796 }
797 }
798
799 int cost = addModIfPossible(modSpec, delegate, current, opLeft);;
800 //int cost = addModIfPossible(mod, delegate, current, opLeft);
801
802 opLeft -= cost;
803 addedTotal += cost;
804 }
805 return addedTotal;
806 }
807
808 public int addModIfPossible(String id, AutofitPluginDelegate delegate, ShipVariantAPI current, int opLeft) {
809 if (current.hasHullMod(id)) return 0;
810 if (delegate.isPlayerCampaignRefit() && !delegate.canAddRemoveHullmodInPlayerCampaignRefit(id)) return 0;
811
812 HullModSpecAPI mod = Misc.getMod(id);
813 return addModIfPossible(mod, delegate, current, opLeft);
814 }
815
816 public int addModIfPossible(HullModSpecAPI mod, AutofitPluginDelegate delegate, ShipVariantAPI current, int opLeft) {
817 if (mod == null) return 0;
818
819 if (current.hasHullMod(mod.getId())) return 0;
820 if (delegate.isPlayerCampaignRefit() && !delegate.canAddRemoveHullmodInPlayerCampaignRefit(mod.getId())) return 0;
821
822
823 int cost = mod.getCostFor(current.getHullSize());
824 if (cost > opLeft) return 0;
825
826 ShipAPI ship = delegate.getShip();
827 ShipVariantAPI orig = null;
828 // why is this commented out? It fixes an issue with logistics hullmods not being properly applied
829 // if the current variant already has some
830 // but probably? causes some other issues
831 // possibly: it was not setting the orig variant back when returning 0; this is now fixed
832 if (ship != null) {
833 orig = ship.getVariant();
834 ship.setVariantForHullmodCheckOnly(current);
835 }
836 if (ship != null && mod.getEffect() != null && ship.getVariant() != null && !mod.getEffect().isApplicableToShip(ship)
837 && !ship.getVariant().hasHullMod(mod.getId())) {
838 if (orig != null) {
840 }
841 return 0;
842 }
843
844 boolean hasItemIfAny = HullModItemManager.getInstance().isRequiredItemAvailable(mod.getId(),
845 delegate.getFleetMember(), current, delegate.getMarket());
846 if (!hasItemIfAny) {
847 if (orig != null) {
849 }
850 return 0;
851 }
852
853
854 if (orig != null && ship != null) {
856 }
857
858 current.addMod(mod.getId());
859
860 if (ship != null && mod.getId() != null && mod.getEffect() != null) {
864 }
865 }
866 return cost;
867 }
868
869
870
871 public void addVentsAndCaps(ShipVariantAPI current, ShipVariantAPI target, float fraction) {
872 if (fraction < 0) return;
873
874 int opCost = current.computeOPCost(stats);
875 int opMax = current.getHullSpec().getOrdnancePoints(stats);
876 int opLeft = opMax - opCost;
877
878 int maxVents = getMaxVents(current.getHullSize());
879 int maxCapacitors = getMaxCaps(current.getHullSize());
880
881 int add = Math.max((int)Math.ceil(target.getNumFluxVents() * fraction) - current.getNumFluxVents(), 0);
882 if (add > opLeft) add = opLeft;
883 opLeft -= addVents(add, current, maxVents);
884
885 add = Math.max((int)Math.ceil(target.getNumFluxCapacitors() * fraction) - current.getNumFluxCapacitors(), 0);
886 if (add > opLeft) add = opLeft;
887 opLeft -= addCapacitors(add, current, maxCapacitors);
888 }
889
890 public void addExtraVents(ShipVariantAPI current) {
891 int opCost = current.computeOPCost(stats);
892 int opMax = current.getHullSpec().getOrdnancePoints(stats);
893 int opLeft = opMax - opCost;
894
895 if (opLeft > 0) {
896 int maxVents = getMaxVents(current.getHullSize());
897 opLeft -= addVents((int) opLeft, current, maxVents);
898 }
899 }
900
901 public void addExtraCaps(ShipVariantAPI current) {
902 int opCost = current.computeOPCost(stats);
903 int opMax = current.getHullSpec().getOrdnancePoints(stats);
904 int opLeft = opMax - opCost;
905
906 if (opLeft > 0) {
907 int maxCaps = getMaxCaps(current.getHullSize());
908 opLeft -= addCapacitors((int) opLeft, current, maxCaps);
909 }
910 }
911
913 int opCost = current.computeOPCost(stats);
914 int opMax = current.getHullSpec().getOrdnancePoints(stats);
915 int opLeft = opMax - opCost;
916
917 int maxVents = getMaxVents(current.getHullSize());
918 int maxCapacitors = getMaxCaps(current.getHullSize());
919 if (opLeft > 0) {
920
921 float total = current.getNumFluxVents() + current.getNumFluxCapacitors();
922 float ventsFraction = 1f;
923 if (total > 0) {
924 ventsFraction = current.getNumFluxVents() / total;
925 }
926
927 int add = (int) (opLeft * ventsFraction);
928 opLeft -= addVents(add, current, maxVents);
929 add = opLeft;
930 opLeft -= addCapacitors(add, current, maxCapacitors);
931
932 add = opLeft;
933 opLeft -= addVents(add, current, maxVents);
934
935 // if we ended up with more capacitors than desired, move some of them to vents
936 if (target != null) {
937 float targetVents = target.getNumFluxVents();
938 float targetCaps = target.getNumFluxCapacitors();
939
940 if (targetVents > targetCaps || targetVents >= maxVents) {
941 float currVents = current.getNumFluxVents();
942 float currCaps = current.getNumFluxCapacitors();
943 float currTotal = currVents + currCaps;
944
945 int currVentsDesired = (int) (currVents + currCaps * 0.5f);
946 if (currVentsDesired > maxVents) currVentsDesired = maxVents;
947 int currCapsDesired = (int) (currTotal - currVentsDesired);
948 if (currCapsDesired > maxCapacitors) currCapsDesired = maxCapacitors;
949 current.setNumFluxVents(currVentsDesired);
950 current.setNumFluxCapacitors(currCapsDesired);
951 }
952
953 // if (targetVents > 0 && currVents + currCaps > 0) {
954 // float ratioTarget = targetVents / (targetVents + targetCaps);
955 // float ratioCurr = currVents / (currVents + currCaps);
956 // if (ratioTarget > ratioCurr) {
957 // float currTotal = currVents + currCaps;
958 // int currVentsDesired = (int) (ratioTarget * currTotal);
959 // if (currVentsDesired > maxVents) currVentsDesired = maxVents;
960 // int currCapsDesired = (int) (currTotal - currVents);
961 // if (currCapsDesired > maxCapacitors) currCapsDesired = maxCapacitors;
962 // current.setNumFluxVents(currVentsDesired);
963 // current.setNumFluxCapacitors(currCapsDesired);
964 // }
965 // }
966 }
967 }
968
969 }
970
971 public int getMaxVents(HullSize size) {
972 int maxVents = getBaseMax(size);
973 if (stats != null) {
974 maxVents = (int) stats.getMaxVentsBonus().computeEffective(maxVents);
975 }
976 return maxVents;
977 }
978
979 public int getMaxCaps(HullSize size) {
980 int maxCapacitors = getBaseMax(size);
981 if (stats != null) {
982 maxCapacitors = (int) stats.getMaxCapacitorsBonus().computeEffective(maxCapacitors);
983 }
984 return maxCapacitors;
985 }
986
987 public static int getBaseMax(HullSize size) {
988 int max = 100;
989 switch (size) {
990 case CAPITAL_SHIP: max = 50; break;
991 case CRUISER: max = 30; break;
992 case DESTROYER: max = 20; break;
993 case FRIGATE: max = 10; break;
994 case FIGHTER: max = 5; break;
995 }
996 return max;
997 }
998
999 public int addVents(int add, ShipVariantAPI current, int max) {
1000 int target = current.getNumFluxVents() + add;
1001 if (target > max) target = max;
1002 if (target < 0) target = 0;
1003 int actual = target - current.getNumFluxVents();
1004 current.setNumFluxVents(target);
1005 return actual;
1006 }
1007
1008 public int addCapacitors(int add, ShipVariantAPI current, int max) {
1009 int target = current.getNumFluxCapacitors() + add;
1010 if (target > max) target = max;
1011 if (target < 0) target = 0;
1012 int actual = target - current.getNumFluxCapacitors();
1013 current.setNumFluxCapacitors(target);
1014 return actual;
1015 }
1016
1018 fittedWeapons.remove(variant.getHullVariantId() + "_" + slot.getId());
1019 delegate.clearWeaponSlot(slot, variant);
1020 }
1021
1022 public void clearFighterSlot(int index, AutofitPluginDelegate delegate, ShipVariantAPI variant) {
1023 fittedFighters.remove(variant.getHullVariantId() + "_" + index);
1024 delegate.clearFighterSlot(index, variant);
1025 }
1026
1027 public void fitWeapons(ShipVariantAPI current, ShipVariantAPI target, boolean upgradeMode, AutofitPluginDelegate delegate) {
1028
1029 //upgradeMode = false;
1030 //boolean upgradeWhenNothingMatchingInPrimary = isChecked(UPGRADE);
1031
1032 //boolean randomize = isChecked(RANDOMIZE);
1033
1034 Set<String> alreadyUsed = new HashSet<String>();
1035 for (WeaponSlotAPI slot : getWeaponSlotsInPriorityOrder(current, target, upgradeMode)) {
1036 if (slotsToSkip.contains(slot.getId())) continue;
1037
1038// if (slot.getId().equals("WS 004")) {
1039// System.out.println("wefwefwef");
1040// }
1041
1042 float opCost = current.computeOPCost(stats);
1043 float opMax = current.getHullSpec().getOrdnancePoints(stats);
1044 float opLeft = opMax - opCost;
1045
1046 float levelToBeat = -1;
1047 if (upgradeMode) {
1048 WeaponSpecAPI curr = current.getWeaponSpec(slot.getId());
1049 if (curr != null) {
1050 float cost = curr.getOrdnancePointCost(stats, current.getStatsForOpCosts());
1051 opLeft += cost;
1052
1053 for (String tag : curr.getTags()) {
1054 levelToBeat = Math.max(levelToBeat, getLevel(tag));
1055 }
1056 if (delegate.isPriority(curr)) {
1057 levelToBeat += PRIORITY;
1058 }
1059 }
1060 }
1061
1062 WeaponSpecAPI desired = target.getWeaponSpec(slot.getId());
1063 // shouldn't happen since it should be filtered out by getWeaponSlotsInPriorityOrder()
1064 if (desired == null) continue;
1065
1066 List<AvailableWeapon> weapons = getWeapons(delegate);
1067 List<AvailableWeapon> possible = getPossibleWeapons(slot, desired, current, opLeft, weapons);
1068 if (possible.isEmpty()) continue;
1069
1070// for (AvailableWeapon w : possible) {
1071// if (w.getSpec().getWeaponId().equals("harpoonpod")) {
1072// System.out.println("wefwef");
1073// }
1074// }
1075
1076
1077 List<String> categories = desired.getAutofitCategoriesInPriorityOrder();
1078 List<String> alternate = altWeaponCats.get(desired);
1079 RANDOMIZE_CHANCE = 1f;
1080 if (false && randomize && (alternate != null || random.nextFloat() < RANDOMIZE_CHANCE)) {
1081 if (alternate == null) {
1082 alternate = new ArrayList<String>();
1083 for (String cat : categories) {
1084 Category category = this.categories.get(cat);
1085 if (category == null) {
1086 //System.out.println("ewfwefew");
1087 continue;
1088 }
1089 if (!category.fallback.isEmpty()) {
1090 int index = random.nextInt(category.fallback.size()/2) + 1;
1091 //int index = random.nextInt(category.fallback.size());
1092 if (index != 0) {
1093 alternate.add(category.fallback.get(index));
1094 }
1095 }
1096 }
1097 altWeaponCats.put(desired, alternate);
1098 }
1099 if (!alternate.isEmpty()) {
1100 categories = alternate;
1101 }
1102 } else if (randomize) {
1103 altWeaponCats.put(desired, new ArrayList<String>());
1104 }
1105
1106
1107 AvailableWeapon pick = null;
1108 for (String catId : categories) {
1109 pick = getBestMatch(desired, upgradeMode, catId, alreadyUsed, possible, slot, delegate);
1110 if (pick != null) {
1111 break;
1112 }
1113 if (upgradeMode) break; // don't pick from secondary categories when upgrading
1114 }
1115
1116 if (pick == null && !upgradeMode) {
1117 OUTER: for (String catId : categories) {
1118 Category cat = this.categories.get(catId);
1119 if (cat == null) continue;
1120
1121 for (String fallbackCatId : cat.fallback) {
1122 pick = getBestMatch(desired, true, fallbackCatId, alreadyUsed, possible, delegate);
1123 if (pick != null) {
1124 break OUTER;
1125 }
1126 }
1127 }
1128 }
1129
1130 if (pick != null) {
1131 if (upgradeMode) {
1132 float pickLevel = -1;
1133 if (!categories.isEmpty()) {
1134 Category cat = this.categories.get(categories.get(0));
1135 if (cat != null) {
1136 String tag = getCategoryTag(cat, pick.getSpec().getTags());
1137 pickLevel = getLevel(tag);
1138 if (delegate.isPriority(pick.getSpec())) {
1139 pickLevel += PRIORITY;
1140 }
1141 }
1142 }
1143 if (pickLevel <= levelToBeat) continue;
1144 }
1145
1146 alreadyUsed.add(pick.getId());
1147
1148 clearWeaponSlot(slot, delegate, current);
1149 delegate.fitWeaponInSlot(slot, pick, current);
1150 fittedWeapons.put(current.getHullVariantId() + "_" + slot.getId(), pick);
1151
1152 if (pick.getSpec().getType() == WeaponType.MISSILE && pick.getSpec().usesAmmo()) {
1154 }
1155 }
1156 }
1157
1158 }
1159
1160
1161 public void fitFighters(ShipVariantAPI current, ShipVariantAPI target, boolean upgradeMode, AutofitPluginDelegate delegate) {
1162
1163 //boolean randomize = isChecked(RANDOMIZE);
1164
1165 int numBays = Global.getSettings().computeNumFighterBays(current);
1166
1167 Set<String> alreadyUsed = new HashSet<String>();
1168
1169 for (int i = 0; i < numBays; i++) {
1170 if (baysToSkip.contains(i)) continue;
1171
1172 float opCost = current.computeOPCost(stats);
1173 float opMax = current.getHullSpec().getOrdnancePoints(stats);
1174 float opLeft = opMax - opCost;
1175
1176 float levelToBeat = -1;
1177 if (upgradeMode) {
1178 FighterWingSpecAPI curr = current.getWing(i);
1179 if (curr != null) {
1180 float cost = curr.getOpCost(current.getStatsForOpCosts());
1181 opLeft += cost;
1182
1183 for (String tag : curr.getTags()) {
1184 levelToBeat = Math.max(levelToBeat, getLevel(tag));
1185 }
1186 if (delegate.isPriority(curr)) {
1187 levelToBeat += PRIORITY;
1188 }
1189 }
1190 } else {
1191 if (current.getWingId(i) != null) {
1192 continue;
1193 }
1194 }
1195
1196 List<AvailableFighter> fighters = getFighters(delegate);
1197 List<AvailableFighter> possible = getPossibleFighters(current, opLeft, fighters);
1198 if (possible.isEmpty()) continue;
1199
1200 String desiredWingId = target.getWingId(i);
1201 if (desiredWingId == null || desiredWingId.isEmpty()) {
1202 if (randomize) {
1203 desiredWingId = emptyWingTarget;
1204 } else {
1205 continue;
1206 }
1207 }
1208
1209 FighterWingSpecAPI desired = Global.getSettings().getFighterWingSpec(desiredWingId);
1210 if (desired == null) continue;
1211
1212 //List<String> categories = getCategoriesInPriorityOrder(desired.getTags());
1213 List<String> categories = desired.getAutofitCategoriesInPriorityOrder();
1214
1215 List<String> alternate = altFighterCats.get(desired);
1216 if (randomize && (alternate != null || random.nextFloat() < RANDOMIZE_CHANCE)) {
1217 if (alternate == null) {
1218 alternate = new ArrayList<String>();
1219 for (String cat : categories) {
1220 Category category = this.categories.get(cat);
1221 if (category == null) {
1222 //System.out.println("ewfwefew");
1223 continue;
1224 }
1225 if (!category.fallback.isEmpty()) {
1226 int index = random.nextInt(category.fallback.size() - 1) + 1;
1227 if (index != 0) {
1228 alternate.add(category.fallback.get(index));
1229 }
1230 }
1231 }
1232 altFighterCats.put(desired, alternate);
1233 }
1234 if (!alternate.isEmpty()) {
1235 categories = alternate;
1236 }
1237 } else if (randomize) {
1238 altFighterCats.put(desired, new ArrayList<String>());
1239 }
1240
1241
1242 AvailableFighter pick = null;
1243 for (String catId : categories) {
1244 pick = getBestMatch(desired, upgradeMode, catId, alreadyUsed, possible, delegate);
1245 if (pick != null) {
1246 break;
1247 }
1248 if (upgradeMode) break; // don't pick from secondary categories when upgrading
1249 }
1250
1251 if (pick == null && !upgradeMode) {
1252 OUTER: for (String catId : categories) {
1253 Category cat = this.categories.get(catId);
1254 if (cat == null) continue;
1255
1256 for (String fallbackCatId : cat.fallback) {
1257 pick = getBestMatch(desired, true, fallbackCatId, alreadyUsed, possible, delegate);
1258 if (pick != null) {
1259 break OUTER;
1260 }
1261 }
1262 }
1263 }
1264
1265 if (pick != null) {
1266 if (upgradeMode) {
1267 float pickLevel = -1;
1268 if (!categories.isEmpty()) {
1269 Category cat = this.categories.get(categories.get(0));
1270 if (cat != null) {
1271 String tag = getCategoryTag(cat, pick.getWingSpec().getTags());
1272 pickLevel = getLevel(tag);
1273 if (delegate.isPriority(pick.getWingSpec())) {
1274 pickLevel += PRIORITY;
1275 }
1276 }
1277 }
1278 if (pickLevel <= levelToBeat) continue;
1279 }
1280
1281 alreadyUsed.add(pick.getId());
1282
1283 clearFighterSlot(i, delegate, current);
1284 delegate.fitFighterInSlot(i, pick, current);
1285 fittedFighters.put(current.getHullVariantId() + "_" + i, pick);
1286 }
1287 }
1288
1289 }
1290
1291
1292
1293
1294 public AvailableWeapon getBestMatch(WeaponSpecAPI desired, boolean useBetter,
1295 String catId, Set<String> alreadyUsed, List<AvailableWeapon> possible,
1296 AutofitPluginDelegate delegate) {
1297 return getBestMatch(desired, useBetter, catId, alreadyUsed, possible, null, delegate);
1298 }
1299
1300 public AvailableWeapon getBestMatch(WeaponSpecAPI desired, boolean useBetter,
1301 String catId, Set<String> alreadyUsed, List<AvailableWeapon> possible,
1302 WeaponSlotAPI slot,
1303 AutofitPluginDelegate delegate) {
1304 //AvailableWeapon best = null;
1305 float bestScore = -1f;
1306 boolean bestIsPriority = false;
1307 int bestSize = -1;
1308
1309 Category cat = categories.get(catId);
1310 if (cat == null) return null;
1311
1312 String desiredTag = getCategoryTag(cat, desired.getTags());
1313 float desiredLevel = getLevel(desiredTag);
1314
1315 if (desiredTag == null) {
1316 // fallback to categories that aren't in the tags of the desired weapon
1317// for (String tag : desired.getTags()) {
1318// desiredLevel = Math.max(desiredLevel, getLevel(tag));
1319// }
1320 desiredLevel = 10000f;
1321 }
1322
1323 boolean longRange = desired.hasTag(LR);
1324 boolean shortRange = desired.hasTag(SR);
1325 boolean midRange = !longRange && !shortRange;
1326 boolean desiredPD = desired.getAIHints().contains(AIHints.PD);
1327
1329
1330
1331// boolean randomize = isChecked(RANDOMIZE);
1332// if (randomize) {
1333// shortRange = true;
1334// longRange = false;
1335// midRange = !longRange && !shortRange;
1336// desiredPD = true;
1337// }
1338
1339 int iter = 0;
1340 for (AvailableWeapon w : possible) {
1341 iter++;
1342 WeaponSpecAPI spec = w.getSpec();
1343 String catTag = getCategoryTag(cat, spec.getTags());
1344 if (catTag == null) continue; // not in this category
1345
1346// if (desired.getWeaponId().equals("autopulse") && spec.getWeaponId().contains("phase")) {
1347// System.out.println("wefwefwe");
1348// }
1349
1350 boolean currLongRange = spec.hasTag(LR);
1351 boolean currShortRange = spec.hasTag(SR);
1352 boolean currMidRange = !currLongRange && !currShortRange;
1353
1354 // don't fit short-range weapons instead of long-range ones unless it's PD
1355 if (!desiredPD && currShortRange && (midRange || longRange)) continue;
1356 //if (currMidRange && longRange) continue;
1357
1358 boolean isPrimaryCategory = cat.base.equals(spec.getAutofitCategory());
1359 boolean currIsPriority = isPrimaryCategory && delegate.isPriority(spec);
1360 int currSize = spec.getSize().ordinal();
1361 boolean betterDueToPriority = currSize >= bestSize && currIsPriority && !bestIsPriority;
1362 boolean worseDueToPriority = currSize <= bestSize && !currIsPriority && bestIsPriority;
1363
1364 if (worseDueToPriority) continue;
1365
1366 float level = getLevel(catTag);
1367 //if (randomize) level += random.nextInt(20);
1368 if (!randomize && !useBetter && !betterDueToPriority && level > desiredLevel) continue;
1369 int rMag = 0;
1370 if (randomize && desired.getSize() == spec.getSize()) {
1371 rMag = 20;
1372 } else if (desired.getSize() == spec.getSize()) {
1373 //if (delegate.getFaction() != null && delegate.getFaction().getDoctrine().getAutofitRandomizeProbability() > 0) {
1374 if (delegate.isAllowSlightRandomization()) {
1375 rMag = 4;
1376 }
1377 }
1378 if (rMag > 0) {
1379 boolean symmetric = random.nextFloat() < 0.75f;
1380 if (slot != null && symmetric) {
1381 long seed = (Math.abs((int)(slot.getLocation().x/2f)) * 723489413945245311L) ^ 1181783497276652981L;
1382 Random r = new Random((seed + weaponFilterSeed) * iter);
1383 level += r.nextInt(rMag);
1384 } else {
1385 level += random.nextInt(rMag);
1386 }
1387 }
1388
1389
1390 float score = level;
1391// if (delegate.isPriority(spec)) {
1392// score += PRIORITY;
1393// }
1394 if ((score > bestScore || betterDueToPriority)) {
1395 //best = w;
1396 best.clear();
1397 best.add(w);
1398 bestScore = score;
1399 bestSize = currSize;
1400 bestIsPriority = currIsPriority;
1401 } else if (score == bestScore) {
1402 best.add(w);
1403 }
1404 }
1405// if (desired.getWeaponId().equals("autopulse")) {
1406// System.out.println("wefwefwe");
1407// }
1408
1409
1410 // if the best-match tier includes the weapon specified in the target variant, use that
1411 // prefer one we already have to buying
1412 List<AvailableWeapon> allMatches = new ArrayList<AvailableWeapon>();
1413 List<AvailableWeapon> freeMatches = new ArrayList<AvailableWeapon>();
1414 for (AvailableWeapon w : best.getItems()) {
1415 if (desired.getWeaponId().equals(w.getId())) {
1416 allMatches.add(w);
1417 if (w.getPrice() <= 0) {
1418 freeMatches.add(w);
1419 }
1420 }
1421 }
1422 if (!freeMatches.isEmpty()) return freeMatches.get(0);
1423 if (!allMatches.isEmpty()) return allMatches.get(0);
1424
1425 // if the best-match tier includes a weapon that we already own, filter out all non-free ones
1426 boolean hasFree = false;
1427 boolean hasNonBlackMarket = false;
1428 for (AvailableWeapon w : best.getItems()) {
1429 if (w.getPrice() <= 0) {
1430 hasFree = true;
1431 }
1432 if (w.getSubmarket() == null || !w.getSubmarket().getPlugin().isBlackMarket()) {
1433 hasNonBlackMarket = true;
1434 }
1435 }
1436 if (hasFree) {
1437 for (AvailableWeapon w : new ArrayList<AvailableWeapon>(best.getItems())) {
1438 if (w.getPrice() > 0) {
1439 best.remove(w);
1440 }
1441 }
1442 } else if (hasNonBlackMarket) {
1443 for (AvailableWeapon w : new ArrayList<AvailableWeapon>(best.getItems())) {
1444 if (w.getSubmarket() != null && w.getSubmarket().getPlugin().isBlackMarket()) {
1445 best.remove(w);
1446 }
1447 }
1448 }
1449
1450 // if the best-match tier includes a weapon we used already, use that
1451 if (!alreadyUsed.isEmpty()) {
1452 for (AvailableWeapon w : best.getItems()) {
1453 if (alreadyUsed.contains(w.getId())) return w;
1454 }
1455 }
1456
1457 if (best.isEmpty()) return null;
1458
1459 //return best.getItems().get(0);
1460 return best.pick();
1461 }
1462
1463
1464 public AvailableFighter getBestMatch(FighterWingSpecAPI desired, boolean useBetter,
1465 String catId, Set<String> alreadyUsed, List<AvailableFighter> possible,
1466 AutofitPluginDelegate delegate) {
1467 float bestScore = -1f;
1468 boolean bestIsPriority = false;
1469
1470 Category cat = categories.get(catId);
1471 if (cat == null) return null;
1472
1473 String desiredTag = getCategoryTag(cat, desired.getTags());
1474 float desiredLevel = getLevel(desiredTag);
1475
1477
1478 for (AvailableFighter f : possible) {
1479 FighterWingSpecAPI spec = f.getWingSpec();
1480 String catTag = getCategoryTag(cat, spec.getTags());
1481 if (catTag == null) continue; // not in this category
1482
1483 boolean isPrimaryCategory = cat.base.equals(spec.getAutofitCategory());
1484 boolean currIsPriority = isPrimaryCategory && delegate.isPriority(spec);
1485 boolean betterDueToPriority = currIsPriority && !bestIsPriority;
1486 boolean worseDueToPriority = !currIsPriority && bestIsPriority;
1487
1488 if (worseDueToPriority) continue;
1489
1490 float level = getLevel(catTag);
1491 if (!randomize && !useBetter && !betterDueToPriority && level > desiredLevel) continue;
1492 //if (randomize) level += random.nextInt(20);
1493
1494 int rMag = 0;
1495 if (randomize) {
1496 rMag = 20;
1497 } else {
1498 if (delegate.isAllowSlightRandomization()) {
1499 rMag = 2;
1500 }
1501 }
1502 if (rMag > 0) {
1503 level += random.nextInt(rMag);
1504 }
1505
1506 float score = level;
1507// if (delegate.isPriority(spec)) {
1508// score += PRIORITY;
1509// }
1510 if (score > bestScore || betterDueToPriority) {
1511 best.clear();
1512 best.add(f);
1513 bestScore = score;
1514 bestScore = score;
1515 bestIsPriority = currIsPriority;
1516 } else if (score == bestScore) {
1517 best.add(f);
1518 }
1519 }
1520
1521
1522 // if the best-match tier includes the fighter specified in the target variant, use that
1523 List<AvailableFighter> allMatches = new ArrayList<AvailableFighter>();
1524 List<AvailableFighter> freeMatches = new ArrayList<AvailableFighter>();
1525 for (AvailableFighter f : best.getItems()) {
1526 if (desired.getId().equals(f.getId())) {
1527 allMatches.add(f);
1528 if (f.getPrice() <= 0) {
1529 freeMatches.add(f);
1530 }
1531 }
1532 }
1533 if (!freeMatches.isEmpty()) return freeMatches.get(0);
1534 if (!allMatches.isEmpty()) return allMatches.get(0);
1535
1536 // if the best-match tier includes a fighter that we already own, filter out all non-free ones
1537 // prefer one we already have to buying
1538 boolean hasFree = false;
1539 boolean hasNonBlackMarket = false;
1540 for (AvailableFighter f : best.getItems()) {
1541 if (f.getPrice() <= 0) {
1542 hasFree = true;
1543 }
1544 if (f.getSubmarket() == null || !f.getSubmarket().getPlugin().isBlackMarket()) {
1545 hasNonBlackMarket = true;
1546 }
1547 }
1548 if (hasFree) {
1549 for (AvailableFighter f : new ArrayList<AvailableFighter>(best.getItems())) {
1550 if (f.getPrice() > 0) {
1551 best.remove(f);
1552 }
1553 }
1554 } else if (hasNonBlackMarket) {
1555 for (AvailableFighter f : new ArrayList<AvailableFighter>(best.getItems())) {
1556 if (f.getSubmarket() != null && f.getSubmarket().getPlugin().isBlackMarket()) {
1557 best.remove(f);
1558 }
1559 }
1560 }
1561
1562
1563 // if the best-match tier includes a fighter we used already, use that
1564 if (!alreadyUsed.isEmpty()) {
1565 for (AvailableFighter f : best.getItems()) {
1566 if (alreadyUsed.contains(f.getId())) return f;
1567 }
1568 }
1569
1570 if (best.isEmpty()) return null;
1571
1572 //return best.getItems().get(0);
1573 return best.pick();
1574 }
1575
1576 public String getCategoryTag(Category cat, Set<String> tags) {
1577 String catTag = null;
1578 for (String tag : tags) {
1579 if (cat.tags.contains(tag)) {
1580 catTag = tag;
1581 break;
1582 }
1583 }
1584 return catTag;
1585 }
1586
1587
1588 protected static transient Map<String, Integer> tagLevels = new HashMap<String, Integer>();
1589
1590
1591 public float getLevel(String tag) {
1592 Integer result = tagLevels.get(tag);
1593 if (result != null) return result;
1594 Category cat = categories.get(tag);
1595 if (cat == null) {
1596 tagLevels.put(tag, -1);
1597 return -1f;
1598 }
1599 try {
1600 result = (int) Float.parseFloat(tag.replaceAll(cat.base, ""));
1601 tagLevels.put(tag, result);
1602 return result;
1603 } catch (Throwable t) {
1604 tagLevels.put(tag, -1);
1605 return -1f;
1606 }
1607 }
1608
1609// public List<String> getCategoriesInPriorityOrder(Set<String> tags) {
1611// List<String> result = new ArrayList<String>();
1612// result.addAll(tags);
1619//
1620// Collections.sort(result, new Comparator<String>() {
1621// public int compare(String o1, String o2) {
1622// //return (int)Math.signum(levels.get(o2) - levels.get(o1));
1623// return (int)Math.signum(getLevel(o2) - getLevel(o1));
1624// }
1625// });
1626//
1627// return result;
1628// }
1629
1630
1631 public List<WeaponSlotAPI> getWeaponSlotsInPriorityOrder(ShipVariantAPI current, ShipVariantAPI target, boolean upgradeMode) {
1632 List<WeaponSlotAPI> result = new ArrayList<WeaponSlotAPI>();
1633
1634 for (WeaponSlotAPI slot : current.getHullSpec().getAllWeaponSlotsCopy()) {
1635 if (slot.isBuiltIn() || slot.isDecorative()) continue;
1636 if (target.getWeaponId(slot.getId()) == null) continue;
1637 if (!upgradeMode && current.getWeaponId(slot.getId()) != null) continue;
1638 result.add(slot);
1639 }
1640
1641 Collections.sort(result, new Comparator<WeaponSlotAPI>() {
1642 public int compare(WeaponSlotAPI w1, WeaponSlotAPI w2) {
1643 float s1 = getSlotPriorityScore(w1);
1644 float s2 = getSlotPriorityScore(w2);
1645 return (int) Math.signum(s2 - s1);
1646 }
1647 });
1648
1649 return result;
1650 }
1651
1653 float score = 0;
1654
1655 switch (slot.getSlotSize()) {
1656 case LARGE: score = 10000; break;
1657 case MEDIUM: score = 5000; break;
1658 case SMALL: score = 2500; break;
1659 }
1660 float angleDiff = Misc.getAngleDiff(slot.getAngle(), 0);
1661 boolean front = Misc.isInArc(slot.getAngle(), slot.getArc(), 0);
1662 if (front) {
1663 //score += 10f;
1664 score += 180f - angleDiff;
1665 }
1666
1667 return score;
1668 }
1669
1670
1671
1672 public List<AvailableWeapon> getPossibleWeapons(WeaponSlotAPI slot, WeaponSpecAPI desired, ShipVariantAPI current, float opLeft, List<AvailableWeapon> weapons) {
1673 List<AvailableWeapon> result = new ArrayList<AvailableWeapon>();
1674
1675 for (AvailableWeapon w : weapons) {
1676 if (w.getQuantity() <= 0) continue;
1677
1678 WeaponSpecAPI spec = w.getSpec();
1679 //float cost = spec.getOrdnancePointCost(stats, current.getStatsForOpCosts());
1680 float cost = w.getOPCost(stats, current.getStatsForOpCosts());
1681 if (cost > opLeft) continue;
1682 if (!slot.weaponFits(spec)) continue;
1683
1684 if (spec != desired &&
1685 (spec.getType() == WeaponType.MISSILE || spec.getAIHints().contains(AIHints.STRIKE))) {
1686 boolean guided = spec.getAIHints().contains(AIHints.DO_NOT_AIM);
1687 if (!guided) {
1688 boolean guidedPoor = spec.getAIHints().contains(AIHints.GUIDED_POOR);
1689 float angleDiff = Misc.getDistanceFromArc(slot.getAngle(), slot.getArc(), 0);
1690 if (angleDiff > 45 || (!guidedPoor && angleDiff > 20)) continue;
1691 }
1692 }
1693
1694 result.add(w);
1695 }
1696
1697 if (randomize && false) {
1698 Random filterRandom = new Random(weaponFilterSeed);
1699 int num = Math.max(1, result.size() / 3 * 2);
1700 Set<Integer> picks = DefaultFleetInflater.makePicks(num, result.size(), filterRandom);
1701 List<AvailableWeapon> filtered = new ArrayList<AvailableWeapon>();
1702 for (Integer pick : picks) {
1703 filtered.add(result.get(pick));
1704 }
1705 result = filtered;
1706 }
1707
1709 current.getHullSpec() != null && current.getHullSpec().hasTag(Factions.DERELICT)) {
1710 List<AvailableWeapon> remove = new ArrayList<AvailableWeapon>();
1711 for (AvailableWeapon w : result) {
1712 if (w.getId().equals("heatseeker")) {
1713 remove.add(w);
1714 }
1715 }
1716 result.removeAll(remove);
1717 }
1718
1719 return result;
1720 }
1721
1722 public List<AvailableFighter> getPossibleFighters(ShipVariantAPI current, float opLeft, List<AvailableFighter> fighters) {
1723 List<AvailableFighter> result = new ArrayList<AvailableFighter>();
1724
1725 for (AvailableFighter f : fighters) {
1726 if (f.getQuantity() <= 0) continue;
1727
1728 FighterWingSpecAPI spec = f.getWingSpec();
1729 float cost = spec.getOpCost(current.getStatsForOpCosts());
1730 if (cost > opLeft) continue;
1731
1732 result.add(f);
1733 }
1734
1735 if (randomize) {
1736 Random filterRandom = new Random(weaponFilterSeed);
1737 int num = Math.max(1, result.size() / 3 * 2);
1738 Set<Integer> picks = DefaultFleetInflater.makePicks(num, result.size(), filterRandom);
1739 List<AvailableFighter> filtered = new ArrayList<AvailableFighter>();
1740 for (Integer pick : picks) {
1741 filtered.add(result.get(pick));
1742 }
1743 result = filtered;
1744 }
1745
1746 return result;
1747 }
1748
1749
1750 public List<AutofitOption> getOptions() {
1751 return options;
1752 }
1753
1754 public float getRating(ShipVariantAPI current, ShipVariantAPI target, AutofitPluginDelegate delegate) {
1755 return 0;
1756 }
1757
1758 @Override
1759 public void doQuickAction(ShipVariantAPI current, AutofitPluginDelegate delegate) {
1760// if (!fittingModule) {
1761// availableMods = new LinkedHashSet<String>(delegate.getAvailableHullmods());
1762// }
1763//
1764// int index = 0;
1765// for (String slotId : current.getStationModules().keySet()) {
1766// ShipVariantAPI moduleCurrent = current.getModuleVariant(slotId);
1767// if (moduleCurrent == null) continue;
1768// if (moduleCurrent.isStockVariant()) {
1769// moduleCurrent = moduleCurrent.clone();
1770// moduleCurrent.setSource(VariantSource.REFIT);
1771// //moduleCurrent.setHullVariantId(Misc.genUID());
1772// moduleCurrent.setHullVariantId(moduleCurrent.getHullVariantId() + "_" + index);
1773// }
1774// index++;
1775//
1776// fittingModule = true;
1777// doQuickAction(moduleCurrent, delegate);
1778// fittingModule = false;
1779//
1780// current.setModuleVariant(slotId, moduleCurrent);
1781// current.setSource(VariantSource.REFIT);
1782// }
1783 availableMods = new LinkedHashSet<String>(delegate.getAvailableHullmods());
1784
1785 if (current.getHullSize().ordinal() >= HullSize.DESTROYER.ordinal() && !current.isCivilian()) {
1787 }
1788
1789 //addHullmods(current, delegate, HullMods.REINFORCEDHULL);
1790 addExtraVentsAndCaps(current, null);
1791// addExtraVents(current);
1792// addExtraCaps(current);
1793 addDistributor(current, delegate);
1794 addDistributorRemoveVentsIfNeeded(current, delegate);
1795 addCoilRemoveCapsIfNeeded(current, delegate);
1796 //addModsWithSpareOPIfAny(current, current, false, delegate);
1798
1799 if (!fittingModule) {
1800 delegate.syncUIWithVariant(current);
1801 }
1802 }
1803
1804 @Override
1805 public String getQuickActionText() {
1806 return "Spend free OP";
1807 }
1808
1809 public String getQuickActionTooltip() {
1810 return "Spend any unused ordnance points on flux vents, capacitors, and essential hullmods.\n\n" +
1811 //"Will not make any changes to weapon loadout or changes that would reduce the combat readiness of the ship, and will not spend any credits.";
1812 "Will not make any changes to weapon loadout, will not affect ship modules (if any), and will not spend any credits.";
1813 }
1814
1815 public boolean isQuickActionEnabled(ShipVariantAPI currentVariant) {
1816 int unusedOpTotal = 0;
1817 for (String slotId : currentVariant.getStationModules().keySet()) {
1818 ShipVariantAPI moduleCurrent = currentVariant.getModuleVariant(slotId);
1819 if (moduleCurrent == null) continue;
1820 unusedOpTotal += moduleCurrent.getUnusedOP(stats);
1821 }
1822 unusedOpTotal += currentVariant.getUnusedOP(stats);
1823 return unusedOpTotal > 0;
1824
1825 //return currentVariant.getUnusedOP(stats) > 0;
1826 }
1827
1828
1829 public static class AutoAssignScore {
1830 public float [] score;
1831 public FleetMemberAPI member;
1832 public PersonAPI officer;
1833 }
1834
1835
1836 @Override
1838 List<FleetMemberAPI> members = new ArrayList<FleetMemberAPI>();
1839 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
1840 if (member.isMothballed()) {
1841 continue;
1842 }
1843 if (!member.getCaptain().isDefault()) {
1844 continue;
1845 }
1846 if (fleet.isPlayerFleet() && Misc.isAutomated(member)) continue;
1847 members.add(member);
1848 }
1849
1850 List<OfficerDataAPI> officers = new ArrayList<OfficerDataAPI>();
1851 int max = (int) fleet.getCommander().getStats().getOfficerNumber().getModifiedValue();
1852 int count = 0;
1853 for (OfficerDataAPI officer : fleet.getFleetData().getOfficersCopy()) {
1854 boolean merc = Misc.isMercenary(officer.getPerson());
1855 if (!merc) {
1856 count++;
1857 }
1858 if (count > max && !merc) continue;
1859
1860 boolean found = false;
1861 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
1862 if (member.getCaptain() == officer.getPerson()) {
1863 found = true;
1864 break;
1865 }
1866 }
1867 if (!found) {
1868 officers.add(officer);
1869 }
1870 }
1871
1872
1873 List<AutoAssignScore> shipScores = new ArrayList<AutoAssignScore>();
1874 List<AutoAssignScore> officerScores = new ArrayList<AutoAssignScore>();
1875
1876 float maxMemberTotal = 1f;
1877 float maxOfficerTotal = 1f;
1878
1879 for (FleetMemberAPI member : members) {
1880 AutoAssignScore score = new AutoAssignScore();
1881 shipScores.add(score);
1882 score.member = member;
1883 score.score = computeMemberScore(member);
1884
1885 maxMemberTotal = Math.max(maxMemberTotal, score.score[4]);
1886 }
1887
1888 for (OfficerDataAPI officer : officers) {
1889 AutoAssignScore score = new AutoAssignScore();
1890 officerScores.add(score);
1891 score.officer = officer.getPerson();
1892 score.score = computeOfficerScore(officer.getPerson());
1893 maxOfficerTotal = Math.max(maxOfficerTotal, score.score[4]);
1894 }
1895
1896 for (AutoAssignScore score : officerScores) {
1897 // so that the best officers are closer to the best ships
1898 // and the lowest-level officers are still closer to the best ships than to the worst ships
1899 score.score[4] = maxMemberTotal + (maxOfficerTotal - score.score[4]);
1900 }
1901
1902 while (!shipScores.isEmpty() && !officerScores.isEmpty()) {
1903 float minDist = Float.MAX_VALUE;
1904 AutoAssignScore bestShip = null;
1905 AutoAssignScore bestOfficer = null;
1906 for (AutoAssignScore ship : shipScores) {
1907// if (ship.member.getHullId().equals("condor")) {
1908// System.out.println("wefewfew");
1909// }
1910 for (AutoAssignScore officer : officerScores) {
1911 float dist = Math.abs(ship.score[0] - officer.score[0]) +
1912 Math.abs(ship.score[1] - officer.score[1]) +
1913 Math.abs(ship.score[2] - officer.score[2]) +
1914 Math.abs(ship.score[3] - officer.score[3]) +
1915 Math.abs(ship.score[4] - officer.score[4]);
1916
1917 if (dist < minDist) {
1918 minDist = dist;
1919 bestShip = ship;
1920 bestOfficer = officer;
1921 }
1922 }
1923 }
1924 if (bestShip == null) {
1925 break;
1926 }
1927
1928 shipScores.remove(bestShip);
1929 officerScores.remove(bestOfficer);
1930 bestShip.member.setCaptain(bestOfficer.officer);
1931 }
1932 }
1933
1934 public float [] computeOfficerScore(PersonAPI officer) {
1935 float energy = 0f;
1936 float ballistic = 0f;
1937 float missile = 0f;
1938 float defense = 0f;
1939 float total = 0f;
1940
1941 for (SkillLevelAPI sl : officer.getStats().getSkillsCopy()) {
1942 if (!sl.getSkill().isCombatOfficerSkill()) continue;
1943 float w = sl.getLevel();
1944 if (w == 2) w = 1.33f; // weigh elite skills as less than double
1945 if (w <= 0f) {
1946 continue;
1947 }
1948
1949 if (sl.getSkill().hasTag(Skills.TAG_ENERGY_WEAPONS)) {
1950 energy++;
1951 } else if (sl.getSkill().hasTag(Skills.TAG_BALLISTIC_WEAPONS)) {
1952 ballistic++;
1953 } else if (sl.getSkill().hasTag(Skills.TAG_MISSILE_WEAPONS)) {
1954 missile++;
1955 } else if (sl.getSkill().hasTag(Skills.TAG_ACTIVE_DEFENSES)) {
1956 defense++;
1957 }
1958 total++;
1959 }
1960
1961 if (total < 1f) total = 1f;
1962 energy /= total;
1963 ballistic /= total;
1964 missile /= total;
1965 defense /= total;
1966
1967 float [] result = new float [5];
1968 result[0] = energy;
1969 result[1] = ballistic;
1970 result[2] = missile;
1971 result[3] = defense;
1972 result[4] = total;
1973 return result;
1974 }
1975
1976 public float [] computeMemberScore(FleetMemberAPI member) {
1977 float energy = 0f;
1978 float ballistic = 0f;
1979 float missile = 0f;
1980 float total = 0f;
1981
1982 boolean civ = member.isCivilian();
1983
1984 for (String slotId : member.getVariant().getFittedWeaponSlots()) {
1985 WeaponSlotAPI slot = member.getVariant().getSlot(slotId);
1986 if (slot.isDecorative() || slot.isSystemSlot()) continue;
1987
1988 WeaponSpecAPI weapon = member.getVariant().getWeaponSpec(slotId);
1989 float w = 1f;
1990 switch (weapon.getSize()) {
1991 case LARGE: w = 4f; break;
1992 case MEDIUM: w = 2f; break;
1993 case SMALL: w = 1f; break;
1994 }
1995 if (civ) w *= 0.1f;
1996 WeaponType type = weapon.getType();
1997 if (type == WeaponType.BALLISTIC) {
1998 ballistic += w;
1999 total += w;
2000 } else if (type == WeaponType.ENERGY) {
2001 energy += w;
2002 total += w;
2003 } else if (type == WeaponType.MISSILE) {
2004 missile += w;
2005 total += w;
2006 } else {
2007 total += w;
2008 }
2009 }
2010 if (total < 1f) total = 1f;
2011 energy /= total;
2012 ballistic /= total;
2013 missile /= total;
2014
2015 boolean d = member.getHullSpec().getShieldType() == ShieldType.FRONT ||
2016 member.getHullSpec().getShieldType() == ShieldType.OMNI ||
2017 member.getHullSpec().isPhase();
2018
2019 float [] result = new float [5];
2020 result[0] = energy;
2021 result[1] = ballistic;
2022 result[2] = missile;
2023 if (d) {
2024 result[3] = 1f;
2025 } else {
2026 result[3] = 0f;
2027 }
2028 result[4] = total;
2029
2030 return result;
2031 }
2032
2033
2034
2036 float f = 1f;
2037 float op = member.getVariant().getHullSpec().getOrdnancePoints(stats);
2038 if (op > 0) {
2039 f = (op - member.getVariant().getUnusedOP(stats)) / op;
2040 }
2041 return f;
2042 }
2043
2044 public float getSkillTotal(OfficerDataAPI officer, boolean carrier) {
2045 float total = 0f;
2046 for (SkillLevelAPI skill : officer.getPerson().getStats().getSkillsCopy()) {
2047 SkillSpecAPI spec = skill.getSkill();
2048 if (!spec.isCombatOfficerSkill()) continue;
2049
2050 float level = skill.getLevel();
2051 if (level <= 0) continue;
2052
2053 if (!carrier || spec.hasTag(Skills.TAG_CARRIER)) {
2054 total += level;
2055 }
2056 }
2057 return total;
2058 }
2059
2060
2061
2063 int num = 0;
2064 if (random.nextFloat() > 0.5f){
2065 num++;
2066 if (random.nextFloat() > 0.75f) {
2067 num++;
2068 }
2069 }
2070
2071 if (num <= 0) return 0;
2072
2073 ShipHullSpecAPI hull = current.getHullSpec();
2074 boolean omni = hull.getShieldType() == ShieldType.OMNI;
2075 boolean front = hull.getShieldType() == ShieldType.FRONT;
2076 boolean shield = omni || front;
2077 boolean phase = hull.getShieldType() == ShieldType.PHASE;
2078 int bays = hull.getFighterBays();
2079 float shieldArc = hull.getShieldSpec().getArc();
2080
2081
2083
2085 if (omni && shieldArc < 270) {
2087 }
2088 }
2089
2090 if (availableMods.contains(HullMods.EXTENDED_SHIELDS)) {
2091 if (shield && shieldArc <= 300) {
2092 picker.add(HullMods.EXTENDED_SHIELDS, 1f);
2093 }
2094 }
2095
2096 if (availableMods.contains(HullMods.CONVERTED_HANGAR) && hull.getHullSize() != HullSize.FRIGATE) {
2097 if (bays <= 0) {
2098 FactionAPI faction = delegate.getFaction();
2099 if (faction == null) {
2100 if (random.nextFloat() < 0.2f) {
2101 picker.add(HullMods.CONVERTED_HANGAR, 1f);
2102 }
2103 } else {
2104 if (random.nextFloat() < (float) faction.getDoctrine().getCarriers() / 5f) {
2105 picker.add(HullMods.CONVERTED_HANGAR, 1f);
2106 }
2107 }
2108 }
2109 }
2110
2112 if (!shield && !phase) {
2113 picker.add(HullMods.MAKESHIFT_GENERATOR, 1f);
2114 }
2115 }
2116
2118 if (bays >= 2) {
2119 picker.add(HullMods.EXPANDED_DECK_CREW, 1f);
2120 }
2121 }
2122
2123 if (availableMods.contains(HullMods.ECM)) {
2124 picker.add(HullMods.ECM, 1f);
2125 }
2126
2129 } else if (availableMods.contains(HullMods.DEDICATED_TARGETING_CORE)) {
2130 if (hull.getHullSize().ordinal() >= HullSize.CRUISER.ordinal()) {
2132 }
2133 }
2134
2135 if (availableMods.contains(HullMods.HARDENED_SHIELDS)) {
2136 if (shield) {
2137 picker.add(HullMods.HARDENED_SHIELDS, 1f);
2138 }
2139 }
2140
2142 if (shield) {
2144 }
2145 }
2146
2147 if (availableMods.contains(HullMods.HEAVYARMOR)) {
2148 picker.add(HullMods.HEAVYARMOR, 1f);
2149 }
2150
2151 if (availableMods.contains(HullMods.INSULATEDENGINE)) {
2152 if (!omni) {
2153 picker.add(HullMods.INSULATEDENGINE, 1f);
2154 }
2155 }
2156
2157 if (availableMods.contains(HullMods.FLUXBREAKERS)) {
2158 if (shield) {
2159 picker.add(HullMods.FLUXBREAKERS, 1f);
2160 } else {
2161 picker.add(HullMods.FLUXBREAKERS, 10f);
2162 }
2163 }
2164
2166 picker.add(HullMods.UNSTABLE_INJECTOR, 1f);
2167 }
2168
2169// if (availableMods.contains(HullMods.SAFETYOVERRIDES)) {
2170// if (hull.getHullSize().ordinal() <= HullSize.CRUISER.ordinal()) {
2171// picker.add(HullMods.SAFETYOVERRIDES, 1f);
2172// }
2173// }
2174
2175
2176 float addedTotal = 0;
2177 float addedMax = current.getHullSpec().getOrdnancePoints(stats) * 0.2f;
2178 for (int i = 0; i < num; i++) {
2179 String modId = picker.pickAndRemove();
2180 if (modId == null) break;
2181 if (current.hasHullMod(modId)) {
2182 i--;
2183 continue;
2184 }
2185
2186 if (modId.equals(HullMods.EXTENDED_SHIELDS)) {
2188 } else if (modId.equals(HullMods.FRONT_SHIELD_CONVERSION) && shieldArc >= 180) {
2190 }
2191 addedTotal = addHullmods(current, delegate, modId);
2192 if (addedTotal >= addedMax) break;
2193 }
2194
2195 return (int) addedTotal;
2196 }
2197
2198
2200 int num = 0;
2201 if (random.nextFloat() > 0.5f){
2202 num++;
2203 if (random.nextFloat() > 0.75f) {
2204 num++;
2205 }
2206 }
2207
2208 if (num <= 0) return 0;
2209
2210 ShipHullSpecAPI hull = current.getHullSpec();
2211 boolean omni = hull.getShieldType() == ShieldType.OMNI;
2212 boolean front = hull.getShieldType() == ShieldType.FRONT;
2213// boolean shield = omni || front;
2214// boolean phase = hull.getShieldType() == ShieldType.PHASE;
2215// int bays = hull.getFighterBays();
2216// float shieldArc = hull.getShieldSpec().getArc();
2217
2218
2220
2221 if (availableMods.contains(HullMods.ARMOREDWEAPONS)) {
2222 picker.add(HullMods.ARMOREDWEAPONS, 1f);
2223 }
2224
2225 if (availableMods.contains(HullMods.MISSLERACKS)) {
2226 if (missilesWithAmmoOnCurrent >= 2) {
2228 }
2229 }
2230
2231 if (availableMods.contains(HullMods.ECCM)) {
2232 if (missilesWithAmmoOnCurrent >= 2) {
2233 picker.add(HullMods.ECCM, 1f);
2234 }
2235 }
2236
2237 float addedTotal = 0;
2238 float addedMax = current.getHullSpec().getOrdnancePoints(stats) * 0.2f;
2239 for (int i = 0; i < num; i++) {
2240 String modId = picker.pickAndRemove();
2241 if (modId == null) break;
2242 if (current.hasHullMod(modId)) {
2243 i--;
2244 continue;
2245 }
2246
2247 addedTotal = addHullmods(current, delegate, modId);
2248 if (addedTotal >= addedMax) break;
2249 }
2250
2251 return (int) addedTotal;
2252 }
2253
2254
2255
2256 public void addSMods(FleetMemberAPI member, int numSmods, AutofitPluginDelegate delegate) {
2257 availableMods = new LinkedHashSet<String>(delegate.getAvailableHullmods());
2258
2259 ShipVariantAPI current = member.getVariant();
2260
2261 int added = convertToSMods(current, numSmods);
2262 addExtraVents(current);
2263 addExtraCaps(current);
2264 //addHullmods(current, delegate, HullMods.FLUX_DISTRIBUTOR, HullMods.FLUX_COIL);
2265 if (!current.hasHullMod(HullMods.FLUX_DISTRIBUTOR)) {
2266 addDistributor(current, delegate);
2267 }
2268 if (!current.hasHullMod(HullMods.FLUX_COIL)) {
2269 addCoil(current, delegate);
2270 }
2271 //addModsWithSpareOPIfAny(current, target, true, delegate);
2272 //addHullmods(current, delegate, HullMods.FLUX_DISTRIBUTOR, HullMods.FLUX_COIL);
2273 if (current.getHullSize() == HullSize.FRIGATE || current.hasHullMod(HullMods.SAFETYOVERRIDES)) {
2275 } else {
2277 }
2278 int remaining = numSmods - added;
2279 if (remaining > 0) {
2280 List<String> mods = new ArrayList<String>();
2281 mods.add(HullMods.FLUX_DISTRIBUTOR);
2282 mods.add(HullMods.FLUX_COIL);
2283 if (current.getHullSize() == HullSize.FRIGATE || current.hasHullMod(HullMods.SAFETYOVERRIDES)) {
2284 mods.add(HullMods.HARDENED_SUBSYSTEMS);
2285 mods.add(HullMods.REINFORCEDHULL);
2286 } else {
2287 mods.add(HullMods.REINFORCEDHULL);
2288 mods.add(HullMods.HARDENED_SUBSYSTEMS);
2289 }
2290 mods.add(HullMods.BLAST_DOORS);
2291 Iterator<String> iter = mods.iterator();
2292 while (iter.hasNext()) {
2293 String modId = iter.next();
2294 if (current.getPermaMods().contains(modId)) {
2295 iter.remove();
2296 }
2297 }
2298// while (!mods.isEmpty() && current.hasHullMod(mods.get(0))) {
2299// mods.remove(0);
2300// }
2301 for (int i = 0; i < remaining && !mods.isEmpty(); i++) {
2302 current.setNumFluxCapacitors(0);
2303 current.setNumFluxVents(0);
2304 String modId = mods.get(Math.min(i, mods.size() - 1));
2305 addHullmods(current, delegate, modId);
2306 convertToSMods(current, 1);
2307 }
2308 }
2309 }
2310
2311}
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
static SettingsAPI getSettings()
Definition Global.java:57
float computeEffective(float baseValue)
static HullModSpecAPI getMod(String id)
boolean isRequiredItemAvailable(String modId, FleetMemberAPI member, ShipVariantAPI currentVariant, MarketAPI dockedAt)
static Set< Integer > makePicks(int num, int max, Random random)
static final String DO_NOT_APPLY_HULLMOD_DURING_AUTOFIT
Definition Tags.java:33
void setAutofireOnByDefault(boolean autofireOnByDefault)
Map< FighterWingSpecAPI, List< String > > altFighterCats
void clearFighterSlot(int index, AutofitPluginDelegate delegate, ShipVariantAPI variant)
List< AvailableWeapon > getWeapons(AutofitPluginDelegate delegate)
void addVentsAndCaps(ShipVariantAPI current, ShipVariantAPI target, float fraction)
AvailableWeapon getBestMatch(WeaponSpecAPI desired, boolean useBetter, String catId, Set< String > alreadyUsed, List< AvailableWeapon > possible, WeaponSlotAPI slot, AutofitPluginDelegate delegate)
int addRandomizedHullmodsPre(ShipVariantAPI current, AutofitPluginDelegate delegate)
void addModsWithSpareOPIfAny(ShipVariantAPI current, ShipVariantAPI target, boolean sModMode, AutofitPluginDelegate delegate)
AvailableWeapon getBestMatch(WeaponSpecAPI desired, boolean useBetter, String catId, Set< String > alreadyUsed, List< AvailableWeapon > possible, AutofitPluginDelegate delegate)
static transient Map< String, Integer > tagLevels
void fitWeapons(ShipVariantAPI current, ShipVariantAPI target, boolean upgradeMode, AutofitPluginDelegate delegate)
void addCoil(ShipVariantAPI current, AutofitPluginDelegate delegate)
AvailableFighter getBestMatch(FighterWingSpecAPI desired, boolean useBetter, String catId, Set< String > alreadyUsed, List< AvailableFighter > possible, AutofitPluginDelegate delegate)
void doQuickAction(ShipVariantAPI current, AutofitPluginDelegate delegate)
int addModIfPossible(String id, AutofitPluginDelegate delegate, ShipVariantAPI current, int opLeft)
int addHullmods(ShipVariantAPI current, AutofitPluginDelegate delegate, String ... mods)
void addCoilRemoveCapsIfNeeded(ShipVariantAPI current, AutofitPluginDelegate delegate)
Map< WeaponSpecAPI, List< String > > altWeaponCats
int addCapacitors(int add, ShipVariantAPI current, int max)
List< WeaponSlotAPI > getWeaponSlotsInPriorityOrder(ShipVariantAPI current, ShipVariantAPI target, boolean upgradeMode)
int addVents(int add, ShipVariantAPI current, int max)
void fitFighters(ShipVariantAPI current, ShipVariantAPI target, boolean upgradeMode, AutofitPluginDelegate delegate)
void addDistributor(ShipVariantAPI current, AutofitPluginDelegate delegate)
int addRandomizedHullmodsPost(ShipVariantAPI current, AutofitPluginDelegate delegate)
void clearWeaponSlot(WeaponSlotAPI slot, AutofitPluginDelegate delegate, ShipVariantAPI variant)
void addDistributorRemoveVentsIfNeeded(ShipVariantAPI current, AutofitPluginDelegate delegate)
void doFit(ShipVariantAPI current, ShipVariantAPI target, int maxSMods, AutofitPluginDelegate delegate)
String getCategoryTag(Category cat, Set< String > tags)
void stripFighters(ShipVariantAPI current, AutofitPluginDelegate delegate)
List< AvailableFighter > getFighters(AutofitPluginDelegate delegate)
void addExtraVentsAndCaps(ShipVariantAPI current, ShipVariantAPI target)
List< AvailableWeapon > getPossibleWeapons(WeaponSlotAPI slot, WeaponSpecAPI desired, ShipVariantAPI current, float opLeft, List< AvailableWeapon > weapons)
int convertToSMods(ShipVariantAPI current, int num)
List< AvailableFighter > getPossibleFighters(ShipVariantAPI current, float opLeft, List< AvailableFighter > fighters)
float getRating(ShipVariantAPI current, ShipVariantAPI target, AutofitPluginDelegate delegate)
boolean isQuickActionEnabled(ShipVariantAPI currentVariant)
int addModIfPossible(HullModSpecAPI mod, AutofitPluginDelegate delegate, ShipVariantAPI current, int opLeft)
void stripWeapons(ShipVariantAPI current, AutofitPluginDelegate delegate)
float getSkillTotal(OfficerDataAPI officer, boolean carrier)
void addSMods(FleetMemberAPI member, int numSmods, AutofitPluginDelegate delegate)
static float getDistanceFromArc(float direction, float arc, float angle)
Definition Misc.java:3564
static boolean isMercenary(PersonAPI person)
Definition Misc.java:5454
static boolean isAutomated(MutableShipStatsAPI stats)
Definition Misc.java:5491
static HullModSpecAPI getMod(String id)
Definition Misc.java:3560
static float getAngleDiff(float from, float to)
Definition Misc.java:1716
static int getOPCost(HullModSpecAPI mod, HullSize size)
Definition Misc.java:5580
static boolean isInArc(float direction, float arc, Vector2f from, Vector2f to)
Definition Misc.java:1722
int computeNumFighterBays(ShipVariantAPI variant)
FighterWingSpecAPI getFighterWingSpec(String wingId)
List< OfficerDataAPI > getOfficersCopy()
List< FleetMemberAPI > getMembersListCopy()
MutableCharacterStatsAPI getStats()
boolean isApplicableToShip(ShipAPI ship)
void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id)
void applyEffectsAfterShipCreation(ShipAPI ship, String id)
MutableShipStatsAPI getMutableStats()
void setVariantForHullmodCheckOnly(ShipVariantAPI variant)
List< WeaponSlotAPI > getAllWeaponSlotsCopy()
int getOrdnancePoints(MutableCharacterStatsAPI stats)
void setVariantDisplayName(String variantName)
void setHullVariantId(String hullVariantId)
int computeOPCost(MutableCharacterStatsAPI stats)
LinkedHashSet< String > getSMods()
Map< String, String > getStationModules()
FighterWingSpecAPI getWing(int index)
ShipVariantAPI getModuleVariant(String slotId)
Collection< String > getFittedWeaponSlots()
Collection< String > getNonBuiltInHullmods()
void setModuleVariant(String slotId, ShipVariantAPI variant)
LinkedHashSet< String > getSModdedBuiltIns()
void setSource(VariantSource source)
void setMayAutoAssignWeapons(boolean mayAutoAssign)
int getUnusedOP(MutableCharacterStatsAPI stats)
WeaponSlotAPI getSlot(String slotId)
WeaponSpecAPI getWeaponSpec(String slotId)
List< WeaponGroupSpec > getWeaponGroups()
void addWeaponGroup(WeaponGroupSpec group)
float getOpCost(MutableShipStatsAPI shipStats)
boolean weaponFits(WeaponSpecAPI spec)
EnumSet< WeaponAPI.AIHints > getAIHints()
List< String > getAutofitCategoriesInPriorityOrder()
float getOrdnancePointCost(MutableCharacterStatsAPI stats)
void clearWeaponSlot(WeaponSlotAPI slot, ShipVariantAPI variant)
void fitWeaponInSlot(WeaponSlotAPI slot, AvailableWeapon weapon, ShipVariantAPI variant)
void fitFighterInSlot(int index, AvailableFighter fighter, ShipVariantAPI variant)
void clearFighterSlot(int index, ShipVariantAPI variant)