Starsector API
Loading...
Searching...
No Matches
FleetFactoryV3.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.fleets;
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.LinkedHashSet;
10import java.util.List;
11import java.util.Map;
12import java.util.Random;
13import java.util.Set;
14
15import org.apache.log4j.Logger;
16
17import com.fs.starfarer.api.Global;
18import com.fs.starfarer.api.campaign.CampaignFleetAPI;
19import com.fs.starfarer.api.campaign.FactionAPI;
20import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode;
21import com.fs.starfarer.api.campaign.FactionAPI.ShipPickParams;
22import com.fs.starfarer.api.campaign.FactionDoctrineAPI;
23import com.fs.starfarer.api.campaign.FleetInflater;
24import com.fs.starfarer.api.campaign.SectorEntityToken;
25import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
26import com.fs.starfarer.api.campaign.econ.MarketAPI;
27import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
28import com.fs.starfarer.api.characters.PersonAPI;
29import com.fs.starfarer.api.characters.SkillSpecAPI;
30import com.fs.starfarer.api.combat.ShieldAPI.ShieldType;
31import com.fs.starfarer.api.combat.ShipAPI.HullSize;
32import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints;
33import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
34import com.fs.starfarer.api.fleet.FleetMemberAPI;
35import com.fs.starfarer.api.fleet.FleetMemberType;
36import com.fs.starfarer.api.fleet.ShipRolePick;
37import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent;
38import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent.SkillPickPreference;
39import com.fs.starfarer.api.impl.campaign.fleets.GenerateFleetOfficersPlugin.GenerateFleetOfficersPickData;
40import com.fs.starfarer.api.impl.campaign.ids.Commodities;
41import com.fs.starfarer.api.impl.campaign.ids.Factions;
42import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
43import com.fs.starfarer.api.impl.campaign.ids.Personalities;
44import com.fs.starfarer.api.impl.campaign.ids.Ranks;
45import com.fs.starfarer.api.impl.campaign.ids.ShipRoles;
46import com.fs.starfarer.api.impl.campaign.ids.Skills;
47import com.fs.starfarer.api.impl.campaign.ids.Stats;
48import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
49import com.fs.starfarer.api.loading.AbilitySpecAPI;
50import com.fs.starfarer.api.loading.WeaponSlotAPI;
51import com.fs.starfarer.api.plugins.CreateFleetPlugin;
52import com.fs.starfarer.api.plugins.OfficerLevelupPlugin;
53import com.fs.starfarer.api.util.Misc;
54import com.fs.starfarer.api.util.WeightedRandomPicker;
55
56public class FleetFactoryV3 {
57
58 public static String KEY_SPAWN_FP_MULT = "$spawnFPMult";
59
60 //public static float IMPORTED_QUALITY_PENALTY = Global.getSettings().getFloat("fleetQualityPenaltyForImports");
61 public static float BASE_QUALITY_WHEN_NO_MARKET = 0.5f;
62
64
65 public static float MIN_NUM_SHIPS_DEFICIT_MULT = 0.25f;
66
67 public static int [][] BASE_COUNTS_WITH_4 = new int [][]
68 {{9, 4, 2, 0},
69 {7, 4, 2, 0},
70 {4, 3, 3, 0},
71 {1, 1, 1, 0},
72 {1, 1, 1, 1},
73 };
74 public static int [][] MAX_EXTRA_WITH_4 = new int [][]
75 {{3, 2, 1, 1},
76 {2, 2, 2, 1},
77 {2, 2, 2, 1},
78 {2, 2, 2, 3},
79 {1, 1, 1, 1},
80 };
81
82 public static int [][] BASE_COUNTS_WITH_3 = new int [][]
83 {{6, 2, 1},
84 {4, 2, 1},
85 {3, 2, 1},
86 {1, 1, 1},
87 {1, 1, 1},
88 };
89 public static int [][] MAX_EXTRA_WITH_3 = new int [][]
90 {{2, 0, 0},
91 {2, 1, 0},
92 {2, 2, 0},
93 {2, 2, 0},
94 {1, 1, 0},
95 };
96
97 public static Logger log = Global.getLogger(FleetFactoryV3.class);
98
99
100
101
102 public static float getShipQualityModForStability(float stability) {
103 return (stability - 5f) * 0.05f;
104 }
105 public static float getNumShipsMultForStability(float stability) {
106 return 1f + (stability - 5f) * 0.05f;
107 }
108
109
110 public static float getNumShipsMultForMarketSize(float marketSize) {
111 if (marketSize < 3) marketSize = 3;
112
113// switch ((int)marketSize) {
114// case 3: return 0.5f;
115// case 4: return 0.7f;
116// case 5: return 0.85f;
117// case 6: return 1f;
118// case 7: return 1.15f;
119// case 8: return 1.3f;
120// case 9: return 1.5f;
121// case 10: return 2f;
122// }
123// switch ((int)marketSize) {
124// case 3: return 1f;
125// case 4: return 1.25f;
126// case 5: return 1.5f;
127// case 6: return 1.75f;
128// case 7: return 2.0f;
129// case 8: return 2.25f;
130// case 9: return 2.5f;
131// case 10: return 3f;
132// }
133 switch ((int)marketSize) {
134 case 3: return 0.5f;
135 case 4: return 0.75f;
136 case 5: return 1f;
137 case 6: return 1.25f;
138 case 7: return 1.5f;
139 case 8: return 1.75f;
140 case 9: return 2f;
141 case 10: return 2.5f;
142 }
143
144 return marketSize / 6f;
145 }
146 public static float getDoctrineNumShipsMult(int doctrineNumShips) {
147 float max = Global.getSettings().getFloat("maxDoctrineNumShipsMult");
148
149 return 1f + (float) (doctrineNumShips - 1f) * (max - 1f) / 4f;
150 }
151
152 public static CampaignFleetAPI createFleet(FleetParamsV3 params) {
153
154 CreateFleetPlugin plugin = Global.getSector().getGenericPlugins().pickPlugin(CreateFleetPlugin.class, params);
155 if (plugin != null) {
156 return plugin.createFleet(params);
157 }
158
159 Global.getSettings().profilerBegin("FleetFactoryV3.createFleet()");
160 try {
161
162 boolean fakeMarket = false;
163 MarketAPI market = pickMarket(params);
164 if (market == null) {
165 market = Global.getFactory().createMarket("fake", "fake", 5);
166 market.getStability().modifyFlat("fake", 10000);
167 market.setFactionId(params.factionId);
168 SectorEntityToken token = Global.getSector().getHyperspace().createToken(0, 0);
169 market.setPrimaryEntity(token);
170
171 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).modifyFlat("fake", BASE_QUALITY_WHEN_NO_MARKET);
172
173 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyFlat("fake", 1f);
174
175// CommodityOnMarketAPI com = market.getCommodityData(Commodities.SHIPS);
176// com.setMaxSupply(6);
177// com.setMaxDemand(6);
178// com.getAvailableStat().setBaseValue(6);
179// com.setSupplier(new SupplierData(6, com, false));
180 fakeMarket = true;
181 }
182 boolean sourceWasNull = params.source == null;
183 params.source = market;
184 if (sourceWasNull && params.qualityOverride == null) { // we picked a nearby market based on location
186 }
187
188 //params.timestamp = Global.getSector().getClock().getTimestamp() - (long)(3600 * 24 * 1000);
189// params.timestamp = Global.getSector().getClock().getTimestamp();
190// if (params.forceNoTimestamp != null && params.forceNoTimestamp) {
191// params.timestamp = null;
192// }
193
194// if (market.getName().equals("Jangala")) {
195// System.out.println("wfwdfwef");
196// }
197
198// ShipPickMode mode = ShipPickMode.PRIORITY_THEN_OTHER;
199// if (params.producer != null && params.producer.getFaction() != market.getFaction()) {
200// mode = ShipPickMode.IMPORTED;
201// }
202// if (params.modeOverride != null) mode = params.modeOverride;
203
204 String factionId = params.factionId;
205 if (factionId == null) factionId = params.source.getFactionId();
206
207 ShipPickMode mode = Misc.getShipPickMode(market, factionId);
208 if (params.modeOverride != null) mode = params.modeOverride;
209
210 CampaignFleetAPI fleet = createEmptyFleet(factionId, params.fleetType, market);
211 fleet.getFleetData().setOnlySyncMemberLists(true);
212
213 Misc.getSalvageSeed(fleet); // will set it
214
215// if (true) {
216// fleet.getFleetData().setOnlySyncMemberLists(false);
217// fleet.getFleetData().addFleetMember("atlas_Standard");
218// return fleet;
219// }
220
221 FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
222 if (params.doctrineOverride != null) {
223 doctrine = params.doctrineOverride;
224 }
225
226 float numShipsMult = 1f;
227 if (params.ignoreMarketFleetSizeMult == null || !params.ignoreMarketFleetSizeMult) {
228 numShipsMult = market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).computeEffective(0f);
229 }
230
231 float quality = params.quality + params.qualityMod;
232// if (mode == ShipPickMode.IMPORTED) { // factored in by FleetParamsV3 calculation of quality
233// quality -= IMPORTED_QUALITY_PENALTY;
234// }
235 if (params.qualityOverride != null) {
236 quality = params.qualityOverride;
237 }
238
239 Random random = new Random();
240 if (params.random != null) random = params.random;
241
242 //Misc.setSpawnFPMult(fleet, numShipsMult);
243
244 float combatPts = params.combatPts * numShipsMult;
245
247 numShipsMult = 1f;
248 }
249
250 float freighterPts = params.freighterPts * numShipsMult;
251 float tankerPts = params.tankerPts * numShipsMult;
252 float transportPts = params.transportPts * numShipsMult;
253 float linerPts = params.linerPts * numShipsMult;
254 float utilityPts = params.utilityPts * numShipsMult;
255
256
257
258 if (combatPts < 10 && combatPts > 0) {
259 combatPts = Math.max(combatPts, 5 + random.nextInt(6));
260 }
261
262 float dW = (float) doctrine.getWarships() + random.nextInt(3) - 2;
263 float dC = (float) doctrine.getCarriers() + random.nextInt(3) - 2;
264 float dP = (float) doctrine.getPhaseShips() + random.nextInt(3) - 2;
265
266 boolean strict = doctrine.isStrictComposition();
267 if (strict) {
268 dW = (float) doctrine.getWarships() - 1;
269 dC = (float) doctrine.getCarriers() - 1;
270 dP = (float) doctrine.getPhaseShips() -1;
271 }
272
273 if (!strict) {
274 float r1 = random.nextFloat();
275 float r2 = random.nextFloat();
276 float min = Math.min(r1, r2);
277 float max = Math.max(r1, r2);
278
279 float mag = 1f;
280 float v1 = min;
281 float v2 = max - min;
282 float v3 = 1f - max;
283
284 v1 *= mag;
285 v2 *= mag;
286 v3 *= mag;
287
288 v1 -= mag/3f;
289 v2 -= mag/3f;
290 v3 -= mag/3f;
291
292 //System.out.println(v1 + "," + v2 + "," + v3);
293 dW += v1;
294 dC += v2;
295 dP += v3;
296 }
297
298 if (doctrine.getWarships() <= 0) dW = 0;
299 if (doctrine.getCarriers() <= 0) dC = 0;
300 if (doctrine.getPhaseShips() <= 0) dP = 0;
301
302
303// float dW = (float) doctrine.getWarships() + random.nextInt(2) - 1;
304// float dC = (float) doctrine.getCarriers() + random.nextInt(2) - 1;
305// float dP = (float) doctrine.getPhaseShips() + random.nextInt(2) - 1;
306
307 boolean banPhaseShipsEtc = !fleet.getFaction().isPlayerFaction() &&
309 if (params.forceAllowPhaseShipsEtc != null && params.forceAllowPhaseShipsEtc) {
310 banPhaseShipsEtc = !params.forceAllowPhaseShipsEtc;
311 }
312
313 params.mode = mode;
314 params.banPhaseShipsEtc = banPhaseShipsEtc;
315
316 // with the phase AI changes: allow phase ships in smaller fleets
317 // but still ban the "etc" (i.e. hyperion, ships with damper field, etc -
318 // anything not in the "combatSmallForSmallFleet" role
319// if (banPhaseShipsEtc) {
320// dP = 0;
321// };
322
323 if (dW < 0) dW = 0;
324 if (dC < 0) dC = 0;
325 if (dP < 0) dP = 0;
326
327 float extra = 7 - (dC + dP + dW);
328 if (extra < 0) extra = 0f;
329 if (doctrine.getWarships() > doctrine.getCarriers() && doctrine.getWarships() > doctrine.getPhaseShips()) {
330 dW += extra;
331 } else if (doctrine.getCarriers() > doctrine.getWarships() && doctrine.getCarriers() > doctrine.getPhaseShips()) {
332 dC += extra;
333 } else if (doctrine.getPhaseShips() > doctrine.getWarships() && doctrine.getPhaseShips() > doctrine.getCarriers()) {
334 dP += extra;
335 }
336
337
338 float doctrineTotal = dW + dC + dP;
339
340 //System.out.println("DW: " + dW + ", DC: " + dC + " DP: " + dP);
341
342 combatPts = (int) combatPts;
343 int warships = (int) (combatPts * dW / doctrineTotal);
344 int carriers = (int) (combatPts * dC / doctrineTotal);
345 int phase = (int) (combatPts * dP / doctrineTotal);
346
347 warships += (combatPts - warships - carriers - phase);
348
349 if (params.addShips != null) {
350 for (String variantId : params.addShips) {
351 ShipRolePick pick = new ShipRolePick(variantId);
352 warships -= addToFleet(pick, fleet, random);
353 }
354 if (warships < 0) warships = 0;
355 }
356
357
359 float combatFreighters = (int) Math.min(freighterPts * 1.5f, warships * 1.5f) * doctrine.getCombatFreighterProbability();
360 float added = addCombatFreighterFleetPoints(fleet, random, combatFreighters, params);
361 freighterPts -= added * 0.5f;
362 warships -= added * 0.5f;
363 } else if (freighterPts > 0 && random.nextFloat() < doctrine.getCombatFreighterProbability()) {
364 float combatFreighters = (int) Math.min(freighterPts * 1.5f, warships * 1.5f);
365 float added = addCombatFreighterFleetPoints(fleet, random, combatFreighters, params);
366 freighterPts -= added * 0.5f;
367 warships -= added * 0.5f;
368 }
369
370 addCombatFleetPoints(fleet, random, warships, carriers, phase, params);
371
372
373 addFreighterFleetPoints(fleet, random, freighterPts, params);
374 addTankerFleetPoints(fleet, random, tankerPts, params);
375 addTransportFleetPoints(fleet, random, transportPts, params);
376 addLinerFleetPoints(fleet, random, linerPts, params);
377 addUtilityFleetPoints(fleet, random, utilityPts, params);
378
379
380 //System.out.println("FLEET POINTS: " + getFP(fleet));
381 int maxShips = Global.getSettings().getInt("maxShipsInAIFleet");
382 if (params.maxNumShips != null) {
383 maxShips = params.maxNumShips;
384 }
385 if (fleet.getFleetData().getNumMembers() > maxShips) {
386 if (params.doNotPrune == null || !params.doNotPrune) {
387 float targetFP = getFP(fleet);
388 if (params.doNotAddShipsBeforePruning == null || !params.doNotAddShipsBeforePruning) {
389 sizeOverride = 5;
390 addCombatFleetPoints(fleet, random, warships, carriers, phase, params);
391 addFreighterFleetPoints(fleet, random, freighterPts, params);
392 addTankerFleetPoints(fleet, random, tankerPts, params);
393 addTransportFleetPoints(fleet, random, transportPts, params);
394 addLinerFleetPoints(fleet, random, linerPts, params);
395 addUtilityFleetPoints(fleet, random, utilityPts, params);
396 sizeOverride = 0;
397 }
398
399 int size = doctrine.getShipSize();
400 pruneFleet(maxShips, size, fleet, targetFP, random);
401
402 float currFP = getFP(fleet);
403 //currFP = getFP(fleet);
404// if (currFP < targetFP) {
405// extraOfficers = (int) Math.round ((targetFP / Math.max(10f, currFP) - 1f) * 10f);
406// if (extraOfficers > 30) extraOfficers = 30;
407// if (extraOfficers < 0) extraOfficers = 0;
408// }
409 }
410
411 fleet.getFleetData().sort();
412
413 //System.out.println("FLEET POINTS: " + getFP(fleet));
414
415
416 } else {
417 fleet.getFleetData().sort();
418 }
419
420 fleet.getFleetData().sort();
421
422 if (params.withOfficers) {
423 addCommanderAndOfficers(fleet, params, random);
424 }
425
426 if (fleet.getFlagship() != null) {
427 if (params.flagshipVariantId != null) {
428 fleet.getFlagship().setVariant(Global.getSettings().getVariant(params.flagshipVariantId), false, true);
429 } else if (params.flagshipVariant != null) {
430 fleet.getFlagship().setVariant(params.flagshipVariant, false, true);
431 }
432 }
433
434 if (params.onlyRetainFlagship != null && params.onlyRetainFlagship) {
435 for (FleetMemberAPI curr : fleet.getFleetData().getMembersListCopy()) {
436 if (curr.isFlagship()) continue;
437 fleet.getFleetData().removeFleetMember(curr);
438 }
439 }
440 //fleet.getFlagship()
441 fleet.forceSync();
442
443 //FleetFactoryV2.doctrine = null;
444
445 if (fleet.getFleetData().getNumMembers() <= 0 ||
446 fleet.getFleetData().getNumMembers() == fleet.getNumFighters()) {
447// if (params.allowEmptyFleet == null || !params.allowEmptyFleet){
448// return null;
449// }
450 }
451
452 if (fakeMarket) {
453 params.source = null;
454 }
455
457 p.quality = quality;
458 if (params.averageSMods != null) {
459 p.averageSMods = params.averageSMods;
460 }
461 p.persistent = true;
462 p.seed = random.nextLong();
463 p.mode = mode;
464 p.timestamp = params.timestamp;
465 p.allWeapons = params.allWeapons;
466 if (params.doctrineOverride != null) {
467 p.rProb = params.doctrineOverride.getAutofitRandomizeProbability();
468 }
469 if (params.factionId != null) {
470 p.factionId = params.factionId;
471 }
472
473 FleetInflater inflater = Misc.getInflater(fleet, p);
474 fleet.setInflater(inflater);
475
476 fleet.getFleetData().setOnlySyncMemberLists(false);
477 fleet.getFleetData().sort();
478
479 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
480 for (FleetMemberAPI member : members) {
481 member.getRepairTracker().setCR(member.getRepairTracker().getMaxCR());
482 }
483
484 float requestedPoints = params.getTotalPts();
485 float actualPoints = fleet.getFleetPoints();
486
487 Misc.setSpawnFPMult(fleet, actualPoints / Math.max(1f, requestedPoints));
488
489
490 return fleet;
491
492 } finally {
494 }
495 }
496
497 public static void pruneFleet(int maxShips, int doctrineSize, CampaignFleetAPI fleet, float targetFP, Random random) {
498 //int maxShips = Global.getSettings().getInt("maxShipsInAIFleet");
499
500 float combatFP = 0;
501 float civFP = 0;
502
503 List<FleetMemberAPI> copy = fleet.getFleetData().getMembersListCopy();
504 List<FleetMemberAPI> combat = new ArrayList<FleetMemberAPI>();
505 //List<FleetMemberAPI> civ = new ArrayList<FleetMemberAPI>();
506 List<FleetMemberAPI> tanker = new ArrayList<FleetMemberAPI>();
507 List<FleetMemberAPI> freighter = new ArrayList<FleetMemberAPI>();
508 List<FleetMemberAPI> liner = new ArrayList<FleetMemberAPI>();
509 List<FleetMemberAPI> other = new ArrayList<FleetMemberAPI>();
510
511 for (FleetMemberAPI member : copy) {
512 if (member.isCivilian()) {
513 civFP += member.getFleetPointCost();
514 //civ.add(member);
515
516 if (member.getHullSpec().getHints().contains(ShipTypeHints.FREIGHTER)) {
517 freighter.add(member);
518 } else if (member.getHullSpec().getHints().contains(ShipTypeHints.TANKER)) {
519 tanker.add(member);
520 } else if (member.getHullSpec().getHints().contains(ShipTypeHints.TRANSPORT) ||
521 member.getHullSpec().getHints().contains(ShipTypeHints.LINER)) {
522 liner.add(member);
523 } else {
524 other.add(member);
525 }
526
527 } else {
528 combatFP += member.getFleetPointCost();
529 combat.add(member);
530 }
531 }
532 if (civFP < 1) civFP = 1;
533 if (combatFP < 1) combatFP = 1;
534
535 int keepCombat = (int) ((float)maxShips * combatFP / (civFP + combatFP));
536 int keepCiv = maxShips - keepCombat;
537 if (civFP > 10 && keepCiv < 2) {
538 keepCiv = 2;
539 if (!freighter.isEmpty()) keepCiv++;
540 if (!tanker.isEmpty()) keepCiv++;
541 if (!liner.isEmpty()) keepCiv++;
542 if (!other.isEmpty()) keepCiv++;
543
544 keepCiv = maxShips - keepCiv;
545 }
546
547
548 float f = 0, t = 0, l = 0, o = 0;
549 float total = freighter.size() + tanker.size() + liner.size() + other.size();
550 if (total < 1) total = 1;
551
552 f = (float) freighter.size() / total;
553 t = (float) tanker.size() / total;
554 l = (float) liner.size() / total;
555 o = (float) other.size() / total;
556
557 f *= keepCiv;
558 t *= keepCiv;
559 l *= keepCiv;
560 o *= keepCiv;
561
562 if (f > 0) f = Math.round(f);
563 if (t > 0) t = Math.round(t);
564 if (l > 0) l = Math.round(l);
565 if (o > 0) o = Math.round(o);
566
567 if (freighter.size() > 0 && f < 1) f = 1;
568 if (tanker.size() > 0 && t < 1) t = 1;
569 if (liner.size() > 0 && l < 1) l = 1;
570 if (other.size() > 0 && o < 1) o = 1;
571
572 int extra = (int) ((f + t + l + o) - keepCiv);
573 //if (extra < 0) keepCombat += Math.abs(extra);
574 if (extra > 0 && o >= 2) {
575 extra--;
576 o--;
577 }
578 if (extra > 0 && l >= 2) {
579 extra--;
580 l--;
581 }
582 if (extra > 0 && t >= 2) {
583 extra--;
584 t--;
585 }
586 if (extra > 0 && f >= 2) {
587 extra--;
588 f--;
589 }
590
591
592 LinkedHashSet<FleetMemberAPI> keep = new LinkedHashSet<FleetMemberAPI>();
593
594 Comparator<FleetMemberAPI> c = new Comparator<FleetMemberAPI>() {
595 public int compare(FleetMemberAPI o1, FleetMemberAPI o2) {
596 return o2.getHullSpec().getHullSize().ordinal() - o1.getHullSpec().getHullSize().ordinal();
597 }
598 };
599 Collections.sort(combat, c);
600 Collections.sort(freighter, c);
601 Collections.sort(tanker, c);
602 Collections.sort(liner, c);
603 Collections.sort(other, c);
604
605 int [] ratio = new int [] { 4, 2, 1, 1 };
606 //int [] ratio = new int [] { 1, 2, 2, 1 };
607
608 //doctrineSize = 2;
609// if (doctrineSize == 4) {
610// ratio = new int [] { 3, 3, 1, 1 };
611// } else if (doctrineSize == 3) {
612// ratio = new int [] { 2, 3, 2, 1 };
613// } else if (doctrineSize <= 2) {
614// ratio = new int [] { 2, 3, 2, 1 };
615// }
616 //ratio[3] = 0;
617 //ratio = new int [] { 4, 0, 0, 0 };
618
619 addAll(ratio, combat, keep, keepCombat, random);
620 //addAll(ratio, civ, keep, keepCiv, random);
621
622 addAll(ratio, freighter, keep, (int)f, random);
623 addAll(ratio, tanker, keep, (int)t, random);
624 addAll(ratio, liner, keep, (int)l, random);
625 addAll(ratio, other, keep, (int)o, random); // adds a Hermes since that's "other" but we don't really care
626
627 for (FleetMemberAPI member : copy) {
628 if (!keep.contains(member)) {
629 fleet.getFleetData().removeFleetMember(member);
630 }
631 }
632
633 float currFP = getFP(fleet);
634 if (currFP > targetFP) {
635 fleet.getFleetData().sort();
636 copy = fleet.getFleetData().getMembersListCopy();
637 //Collections.reverse(copy);
638 //Collections.shuffle(copy, random);
639 for (int i = 0; i < copy.size()/2; i+=2) {
640 FleetMemberAPI f1 = copy.get(i);
641 FleetMemberAPI f2 = copy.get(copy.size() - 1 - i);
642 copy.set(i, f2);
643 copy.set(copy.size() - 1 - i, f1);
644 }
645//
646// float fpGoal = currFP - targetFP;
647// float fpDone = 0;
648// for (FleetMemberAPI curr : copy) {
649// if (curr.isCivilian()) continue;
650// for (FleetMemberAPI replace : combat) {
651// float fpCurr = curr.getFleetPointCost();
652// float fpReplace = replace.getFleetPointCost();
653// if (fpCurr > fpReplace) {
654// fpDone += fpCurr - fpReplace;
655// combat.remove(replace);
656// fleet.getFleetData().removeFleetMember(curr);
657// fleet.getFleetData().addFleetMember(replace);
658// break;
659// }
660// }
661// if (fpDone >= fpGoal) {
662// break;
663// }
664// }
665
666 float fpGoal = currFP - targetFP;
667 float fpDone = 0;
668 for (FleetMemberAPI curr : copy) {
669 if (curr.isCivilian()) continue;
670 FleetMemberAPI best = null;
671 float bestDiff = 0f;
672 for (FleetMemberAPI replace : combat) {
673 float fpCurr = curr.getFleetPointCost();
674 float fpReplace = replace.getFleetPointCost();
675 if (fpCurr > fpReplace) {
676 float fpDiff = fpCurr - fpReplace;
677 if (fpDone + fpDiff <= fpGoal) {
678 best = replace;
679 bestDiff = fpDiff;
680 break;
681 } else {
682 if (fpDiff < bestDiff) {
683 best = replace;
684 bestDiff = fpDiff;
685 }
686 }
687 }
688 }
689 if (best != null) {
690 fpDone += bestDiff;
691 combat.remove(best);
692 fleet.getFleetData().removeFleetMember(curr);
693 fleet.getFleetData().addFleetMember(best);
694 }
695 if (fpDone >= fpGoal) {
696 break;
697 }
698 }
699
700 }
701
702 }
703
704 public static void addAll(int [] ratio, List<FleetMemberAPI> from, LinkedHashSet<FleetMemberAPI> to, int num, Random random) {
705 int added = 0;
706 if (num <= 5) {
707 while (added < num && !from.isEmpty()) {
708 to.add(from.remove(0));
709 added++;
710 }
711 return;
712 }
713
714 WeightedRandomPicker<HullSize> picker = makePicker(ratio, random);
715 for (int i = 0; i < num; i++) {
716 if (picker.isEmpty()) picker = makePicker(ratio, random);
717 OUTER: while (!picker.isEmpty()) {
718 HullSize size = picker.pickAndRemove();
719 for (FleetMemberAPI member : from) {
720 if (member.getHullSpec().getHullSize() == size) {
721 to.add(member);
722 from.remove(member);
723 added++;
724 break OUTER;
725 }
726 }
727 }
728
729 }
730
731 // if we failed to add up to num, add the largest ships until we've got num
732 // assumes from list is sorted descending by size
733 while (added < num && !from.isEmpty()) {
734 to.add(from.remove(0));
735 added++;
736 }
737
738 }
739
740 public static WeightedRandomPicker<HullSize> makePicker(int [] ratio, Random random) {
741 WeightedRandomPicker<HullSize> picker = new WeightedRandomPicker<HullSize>(random);
742 for (int i = 0; i < ratio[0]; i++) {
743 picker.add(HullSize.CAPITAL_SHIP);
744 }
745 for (int i = 0; i < ratio[1]; i++) {
746 picker.add(HullSize.CRUISER);
747 }
748 for (int i = 0; i < ratio[2]; i++) {
749 picker.add(HullSize.DESTROYER);
750 }
751 for (int i = 0; i < ratio[3]; i++) {
752 picker.add(HullSize.FRIGATE);
753 }
754// picker.add(HullSize.CAPITAL_SHIP, ratio[0]);
755// picker.add(HullSize.CRUISER, ratio[1]);
756// picker.add(HullSize.DESTROYER, ratio[2]);
757// picker.add(HullSize.FRIGATE, ratio[3]);
758 return picker;
759 }
760
761
762 public static int getFP(CampaignFleetAPI fleet) {
763 int fp = 0;
764 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
765 fp += member.getFleetPointCost();
766 }
767 return fp;
768 }
769
770
771 public static List<FleetMemberAPI> getRemoveOrder(CampaignFleetAPI fleet) {
772 List<FleetMemberAPI> remove = new ArrayList<FleetMemberAPI>();
773 List<FleetMemberAPI> copy = fleet.getFleetData().getMembersListCopy();
774
775// Collections.sort(copy, new Comparator<FleetMemberAPI>() {
776// public int compare(FleetMemberAPI o1, FleetMemberAPI o2) {
777// int f1 = o1.getFleetPointCost();
778// int f2 = o2.getFleetPointCost();
779//
780// if (!o1.isCivilian()) f1 *=
781// return 0;
782// }
783// });
784
785 Collections.reverse(copy);
786
787 Iterator<FleetMemberAPI> iter;
788
789 iter = copy.iterator();
790 while (iter.hasNext()) {
791 FleetMemberAPI member = iter.next();
792 if (member.isCivilian() && member.getHullSpec().getHullSize().ordinal() <= HullSize.FRIGATE.ordinal()) {
793 remove.add(member);
794 iter.remove();
795 }
796 }
797
798 iter = copy.iterator();
799 while (iter.hasNext()) {
800 FleetMemberAPI member = iter.next();
801 if (!member.isCivilian() && member.getHullSpec().getHullSize().ordinal() <= HullSize.FRIGATE.ordinal()) {
802 remove.add(member);
803 iter.remove();
804 }
805 }
806
807 iter = copy.iterator();
808 while (iter.hasNext()) {
809 FleetMemberAPI member = iter.next();
810 if (member.isCivilian() && member.getHullSpec().getHullSize().ordinal() <= HullSize.DESTROYER.ordinal()) {
811 remove.add(member);
812 iter.remove();
813 }
814 }
815
816 iter = copy.iterator();
817 while (iter.hasNext()) {
818 FleetMemberAPI member = iter.next();
819 if (!member.isCivilian() && member.getHullSpec().getHullSize().ordinal() <= HullSize.DESTROYER.ordinal()) {
820 remove.add(member);
821 iter.remove();
822 }
823 }
824
825 iter = copy.iterator();
826 while (iter.hasNext()) {
827 FleetMemberAPI member = iter.next();
828 if (member.isCivilian() && member.getHullSpec().getHullSize().ordinal() <= HullSize.CRUISER.ordinal()) {
829 remove.add(member);
830 iter.remove();
831 }
832 }
833
834 iter = copy.iterator();
835 while (iter.hasNext()) {
836 FleetMemberAPI member = iter.next();
837 if (!member.isCivilian() && member.getHullSpec().getHullSize().ordinal() <= HullSize.CRUISER.ordinal()) {
838 remove.add(member);
839 iter.remove();
840 }
841 }
842
843 iter = copy.iterator();
844 while (iter.hasNext()) {
845 FleetMemberAPI member = iter.next();
846 if (member.isCivilian()) {
847 remove.add(member);
848 iter.remove();
849 }
850 }
851
852 iter = copy.iterator();
853 while (iter.hasNext()) {
854 FleetMemberAPI member = iter.next();
855 if (!member.isCivilian()) {
856 remove.add(member);
857 iter.remove();
858 }
859 }
860
861 return remove;
862 }
863
864 public static void addCommanderAndOfficers(CampaignFleetAPI fleet, FleetParamsV3 params, Random random) {
865 if (true) {
866 addCommanderAndOfficersV2(fleet, params, random);
867 return;
868 }
869 }
870
871
872 public static void addCommanderAndOfficersV2(CampaignFleetAPI fleet, FleetParamsV3 params, Random random) {
873
874 GenerateFleetOfficersPickData pickData = new GenerateFleetOfficersPickData(fleet, params);
875 GenerateFleetOfficersPlugin genPlugin = Global.getSector().getGenericPlugins().pickPlugin(GenerateFleetOfficersPlugin.class, pickData);
876 if (genPlugin != null) {
877 genPlugin.addCommanderAndOfficers(fleet, params, random);
878 return;
879 }
880
881 FactionAPI faction = fleet.getFaction();
882 FactionDoctrineAPI doctrine = faction.getDoctrine();
883 if (params.doctrineOverride != null) {
884 doctrine = params.doctrineOverride;
885 }
886 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
887 if (members.isEmpty()) return;
888
889 float combatPoints = 0f;
890 float combatShips = 0f;
891 for (FleetMemberAPI member : members) {
892 if (member.isCivilian()) continue;
893 if (member.isFighterWing()) continue;
894 combatPoints += member.getFleetPointCost();
895 combatShips++;
896 }
897 if (combatPoints < 1f) combatPoints = 1f;
898 if (combatShips < 1f) combatShips = 1f;
899
900 boolean debug = true;
901 debug = false;
902
903
904 int maxCommanderLevel = Global.getSettings().getInt("maxAIFleetCommanderLevel");
905 float mercMult = Global.getSettings().getFloat("officerAIMaxMercsMult");
906 //float mercFP = Global.getSettings().getFloat("officerAIMercsStartingFP");
907 int maxOfficers = Global.getSettings().getInt("officerAIMax");
908 int baseMaxOfficerLevel = Global.getSettings().getInt("officerMaxLevel");
909 OfficerLevelupPlugin plugin = (OfficerLevelupPlugin) Global.getSettings().getPlugin("officerLevelUp");
910
911 float officerQualityMult = (doctrine.getOfficerQuality() - 1f) / 4f;
912 if (officerQualityMult > 1f) officerQualityMult = 1f;
913
914// float baseFPPerOfficer = Global.getSettings().getFloat("baseFPPerOfficer");
915// float fpPerBaseOfficer = baseFPPerOfficer - (baseFPPerOfficer * 0.5f * officerQualityMult);
916// float fpPerExtraOfficer = fpPerBaseOfficer * 1f;
917
918 float baseShipsForMaxOfficerLevel = Global.getSettings().getFloat("baseCombatShipsForMaxOfficerLevel");
919 float baseCombatShipsPerOfficer = Global.getSettings().getFloat("baseCombatShipsPerOfficer");
920 float combatShipsPerOfficer = baseCombatShipsPerOfficer * (1f - officerQualityMult * 0.5f);
921
922 //float fleetSizeOfficerQualityMult = combatPoints / (fpPerBaseOfficer * maxOfficers);
923 float fleetSizeOfficerQualityMult = combatShips / (baseShipsForMaxOfficerLevel * (1f - officerQualityMult * 0.5f));
924 if (fleetSizeOfficerQualityMult > 1) fleetSizeOfficerQualityMult = 1;
925
926 maxOfficers += (int)((float)doctrine.getOfficerQuality() * mercMult) + params.officerNumberBonus;
927
928 //int numOfficers = (int) (combatPoints / fpPerBaseOfficer) + params.officerNumberBonus;
929 int numOfficers = (int) Math.min(maxOfficers, combatShips / combatShipsPerOfficer);
930 //numOfficers += (int) Math.max(0, (combatPoints - mercFP) / fpPerExtraOfficer);
931 numOfficers += params.officerNumberBonus;
932 numOfficers = Math.round(numOfficers * params.officerNumberMult);
933
934 if (debug) System.out.println("numOfficers: " + numOfficers);
935
936
937// if (params.maxOfficers >= 0) maxOfficers = params.maxOfficers;
938// if (params.minOfficers >= 0 && numOfficers < params.minOfficers) numOfficers = params.minOfficers;
939
940 if (numOfficers > maxOfficers) numOfficers = maxOfficers;
941
942 if (params.commander != null && params.commander.isPlayer()) {
943 numOfficers = (int) params.commander.getStats().getOfficerNumber().getModifiedInt();
944 }
945 if (params.maxOfficersToAdd != null) {
946 numOfficers = Math.min(numOfficers, params.maxOfficersToAdd);
947 }
948
949 //int maxOfficerLevel = (int) Math.round((officerQualityMult * 0.75f + fleetSizeOfficerQualityMult * 1f) * (float) baseMaxOfficerLevel);
950 int maxOfficerLevel = (int)Math.round(((float)doctrine.getOfficerQuality() / 2f) +
951 (fleetSizeOfficerQualityMult * 1f) * (float) baseMaxOfficerLevel);
952 if (maxOfficerLevel < 1) maxOfficerLevel = 1;
953 maxOfficerLevel += params.officerLevelBonus;
954 if (maxOfficerLevel < 1) maxOfficerLevel = 1;
955
956 if (debug) System.out.println("maxOfficers: " + maxOfficers);
957 if (debug) System.out.println("maxOfficerLevel: " + maxOfficerLevel);
958
959
960 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(random);
961 WeightedRandomPicker<FleetMemberAPI> flagshipPicker = new WeightedRandomPicker<FleetMemberAPI>(random);
962
963 int maxSize = 0;
964 for (FleetMemberAPI member : members) {
965 if (member.isFighterWing()) continue;
966 if (member.isFlagship()) continue;
967 if (member.isCivilian()) continue;
968 if (!member.getCaptain().isDefault()) continue;
969 int size = member.getHullSpec().getHullSize().ordinal();
970 if (size > maxSize) {
971 maxSize = size;
972 }
973 }
974 //maxSize = 2;
975 for (FleetMemberAPI member : members) {
976 if (member.isFighterWing()) continue;
977 if (member.isFlagship()) continue;
978 if (member.isCivilian()) continue;
979 if (!member.getCaptain().isDefault()) continue;
980
981 float weight = (float) member.getFleetPointCost();
982 int size = member.getHullSpec().getHullSize().ordinal();
983 if (size >= maxSize) {
984 flagshipPicker.add(member, weight);
985 }
986
987 picker.add(member, weight);
988 }
989
990 if (picker.isEmpty()) {
991 picker.add(members.get(0), 1f);
992 }
993 if (flagshipPicker.isEmpty()) {
994 flagshipPicker.add(members.get(0), 1f);
995 }
996
997
998 FleetMemberAPI flagship = flagshipPicker.pickAndRemove();
999 picker.remove(flagship);
1000 int commanderLevel = maxOfficerLevel;
1001 int commanderLevelLimit = maxCommanderLevel;
1002// if (commanderLevelLimit > params.officerLevelLimit) commanderLevelLimit = params.officerLevelLimit;
1003// if (commanderLevelLimit > maxCommanderLevel) commanderLevelLimit = maxCommanderLevel;
1004 if (params.commanderLevelLimit != 0) {
1005 commanderLevelLimit = params.commanderLevelLimit;
1006 }
1007 if (commanderLevel > commanderLevelLimit) commanderLevel = commanderLevelLimit;
1008
1009 SkillPickPreference pref = getSkillPrefForShip(flagship);
1010 PersonAPI commander = params.commander;
1011 if (commander == null) {
1012 commander = OfficerManagerEvent.createOfficer(fleet.getFaction(), commanderLevel, pref, false, null, true, true, -1, random);
1013 if (commander.getPersonalityAPI().getId().equals(Personalities.TIMID)) {
1014 commander.setPersonality(Personalities.CAUTIOUS);
1015 }
1016 addCommanderSkills(commander, fleet, params, random);
1017 }
1018 if (params.commander == null) {
1019 commander.setRankId(Ranks.SPACE_COMMANDER);
1020 commander.setPostId(Ranks.POST_FLEET_COMMANDER);
1021 }
1022 fleet.setCommander(commander);
1023 fleet.getFleetData().setFlagship(flagship);
1024
1025 int commanderOfficerLevelBonus = (int) commander.getStats().getDynamic().getMod(Stats.OFFICER_MAX_LEVEL_MOD).computeEffective(0);
1026 int officerLevelLimit = plugin.getMaxLevel(null) + commanderOfficerLevelBonus;
1027 //if (officerLevelLimit > params.officerLevelLimit) officerLevelLimit = params.officerLevelLimit;
1028 if (params.officerLevelLimit != 0) {
1029 officerLevelLimit = params.officerLevelLimit;
1030 }
1031
1032 if (debug) {
1033 System.out.println("Created level " + commander.getStats().getLevel() + " commander");
1034 System.out.println("Max officer level bonus: " + commanderOfficerLevelBonus + " (due to commander skill)");
1035 System.out.println("Adding up to " + numOfficers + " officers");
1036 }
1037
1038 int added = 0;
1039 for (int i = 0; i < numOfficers; i++) {
1040 FleetMemberAPI member = picker.pickAndRemove();
1041 if (member == null) {
1042 break; // out of ships that need officers
1043 }
1044
1045 int level = maxOfficerLevel - random.nextInt(3);
1046 if (Misc.isEasy()) {
1047 level = (int) Math.ceil((float) level * Global.getSettings().getFloat("easyOfficerLevelMult"));
1048 }
1049 if (level < 1) level = 1;
1050 if (level > officerLevelLimit) level = officerLevelLimit;
1051 if (params.commander != null && params.commander.isPlayer()) {
1052 level = (int) params.commander.getStats().getDynamic().getMod(Stats.OFFICER_MAX_LEVEL_MOD).computeEffective(Global.getSettings().getInt("officerMaxLevel"));
1053 }
1054
1055 pref = getSkillPrefForShip(member);
1056 PersonAPI person = OfficerManagerEvent.createOfficer(fleet.getFaction(), level, pref, false, fleet, true, true, -1, random);
1057 if (person.getPersonalityAPI().getId().equals(Personalities.TIMID)) {
1058 person.setPersonality(Personalities.CAUTIOUS);
1059 }
1060
1061 if (debug) {
1062 System.out.println("Added level " + person.getStats().getLevel() + " officer");
1063 }
1064 added++;
1065 member.setCaptain(person);
1066
1067 if (params.commander != null && params.commander.isPlayer()) {
1068 fleet.getFleetData().addOfficer(person);
1069 }
1070 }
1071
1072 if (debug) {
1073 System.out.println("Added " + added + " officers total");
1074 }
1075
1076 }
1077
1078 public static SkillPickPreference getSkillPrefForShip(FleetMemberAPI member) {
1079 float energy = 0f;
1080 float ballistic = 0f;
1081 float missile = 0f;
1082 float total = 0f;
1083
1084 for (WeaponSlotAPI slot : member.getHullSpec().getAllWeaponSlotsCopy()) {
1085 float w = 1f;
1086 switch (slot.getSlotSize()) {
1087 case LARGE: w = 4f; break;
1088 case MEDIUM: w = 2f; break;
1089 case SMALL: w = 1f; break;
1090 }
1091 WeaponType type = slot.getWeaponType();
1092 if (type == WeaponType.BALLISTIC || type == WeaponType.HYBRID) {
1093 ballistic += w;
1094 total += w;
1095 } else if (type == WeaponType.ENERGY) {
1096 energy += w;
1097 total += w;
1098 } else if (type == WeaponType.MISSILE || type == WeaponType.SYNERGY || type == WeaponType.COMPOSITE) {
1099 missile += w;
1100 total += w;
1101 }
1102 }
1103
1104 if (total <= 0f) total = 1f;
1105
1106 boolean e = energy >= total * 0.33f;
1107 boolean b = ballistic >= total * 0.33f;
1108 if (b && e) {
1109 if (ballistic * 1.5f >= energy) {
1110 e = false;
1111 } else {
1112 b = false;
1113 }
1114 }
1115 boolean m = missile >= total * 0.17f;
1116
1117 boolean d = member.getHullSpec().getShieldType() == ShieldType.FRONT ||
1118 member.getHullSpec().getShieldType() == ShieldType.OMNI ||
1119 member.getHullSpec().isPhase();
1120
1121 // doing things in this, ah, "elegant" way to keep method signatures the same for now...
1122 String n1 = e ? "YES_ENERGY" : "NO_ENERGY";
1123 String n2 = b ? "YES_BALLISTIC" : "NO_BALLISTIC";
1124 String n3 = m ? "YES_MISSILE" : "NO_MISSILE";
1125 String n4 = d ? "YES_DEFENSE" : "NO_DEFENSE";
1126 SkillPickPreference pref = SkillPickPreference.valueOf(n1 + "_" + n2 + "_" + n3 + "_" + n4);
1127
1128 return pref;
1129 }
1130
1131
1132 public static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, Random random) {
1133 if (params != null && params.noCommanderSkills != null && params.noCommanderSkills) return;
1134
1135 if (random == null) random = new Random();
1136
1137 MutableCharacterStatsAPI stats = commander.getStats();
1138 int level = stats.getLevel();
1139
1140 int forOne = Global.getSettings().getInt("commanderLevelForOneSkill");
1141 int forTwo = Global.getSettings().getInt("commanderLevelForTwoSkills");
1142
1143 int numSkills = 0;
1144 if (level >= forTwo) {
1145 numSkills = 2;
1146 } else if (level >= forOne) {
1147 numSkills = 1;
1148 }
1149
1150 if (numSkills <= 0) return;
1151
1152 FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
1153 if (params != null && params.doctrineOverride != null) {
1154 doctrine = params.doctrineOverride;
1155 }
1156
1157 List<String> skills = new ArrayList<String>(doctrine.getCommanderSkills());
1158
1159 Iterator<String> iter = skills.iterator();
1160 while (iter.hasNext()) {
1161 String id = iter.next();
1162 SkillSpecAPI spec = Global.getSettings().getSkillSpec(id);
1163 if (spec != null && spec.hasTag(Skills.TAG_PLAYER_ONLY)) {
1164 iter.remove();
1165 }
1166 }
1167
1168
1169 if (skills.isEmpty()) return;
1170
1171 if (random.nextFloat() < doctrine.getCommanderSkillsShuffleProbability()) {
1172 Collections.shuffle(skills, random);
1173 }
1174
1175 stats.setSkipRefresh(true);
1176
1177 boolean debug = true;
1178 debug = false;
1179 if (debug) System.out.println("Generating commander skills, person level " + stats.getLevel() + ", skills: " + numSkills);
1180 int picks = 0;
1181 for (String skillId : skills) {
1182 if (debug) System.out.println("Selected skill: [" + skillId + "]");
1183 stats.setSkillLevel(skillId, 1);
1184 picks++;
1185 if (picks >= numSkills) {
1186 break;
1187 }
1188 }
1189 if (debug) System.out.println("Done generating commander skills\n");
1190
1191 stats.setSkipRefresh(false);
1192 stats.refreshCharacterStatsEffects();
1193 }
1194
1195
1196 public static float getMemberWeight(FleetMemberAPI member) {
1197 boolean nonCombat = member.getVariant().isCivilian();
1198 float weight = 0;
1199 switch (member.getVariant().getHullSize()) {
1200 case CAPITAL_SHIP: weight += 8; break;
1201 case CRUISER: weight += 4; break;
1202 case DESTROYER: weight += 2; break;
1203 case FRIGATE: weight += 1; break;
1204 case FIGHTER: weight += 1; break;
1205 }
1206 if (nonCombat) weight *= 0.1f;
1207 return weight;
1208 }
1209
1210
1211
1212
1213 public static MarketAPI pickMarket(FleetParamsV3 params) {
1214 if (params.source != null) return params.source;
1215 if (params.locInHyper == null) return null;
1216
1217 List<MarketAPI> allMarkets = Global.getSector().getEconomy().getMarketsCopy();
1218
1219 int size = getMinPreferredMarketSize(params);
1220 float distToClosest = Float.MAX_VALUE;
1221 MarketAPI closest = null;
1222 float distToClosestMatchingSize = Float.MAX_VALUE;
1223 MarketAPI closestMatchingSize = null;
1224
1225
1226 FactionAPI creationFaction = Global.getSector().getFaction(params.factionId);
1227 boolean independent = Factions.INDEPENDENT.equals(params.factionId) ||
1228 Factions.SCAVENGERS.equals(params.factionId) ||
1229 creationFaction.getCustomBoolean(Factions.CUSTOM_SPAWNS_AS_INDEPENDENT);
1230
1231 for (MarketAPI market : allMarkets) {
1232 if (market.getPrimaryEntity() == null) continue;
1233
1234 if (independent) {
1235 boolean hostileToIndependent = market.getFaction().isHostileTo(Factions.INDEPENDENT);
1236 if (hostileToIndependent) continue;
1237 } else {
1238 if (!market.getFactionId().equals(params.factionId)) continue;
1239 }
1240
1241 float currDist = Misc.getDistance(market.getPrimaryEntity().getLocationInHyperspace(),
1242 params.locInHyper);
1243 if (currDist < distToClosest) {
1244 distToClosest = currDist;
1245 closest = market;
1246 }
1247
1248 if (market.getSize() >= size && currDist < distToClosestMatchingSize) {
1249 distToClosestMatchingSize = currDist;
1250 closestMatchingSize = market;
1251 }
1252 }
1253
1254 if (closestMatchingSize != null) {
1255 return closestMatchingSize;
1256 }
1257
1258 if (closest != null) {
1259 return closest;
1260 }
1261
1262// MarketAPI temp = Global.getFactory().createMarket("temp", "Temp", size);
1263// temp.setFactionId(params.factionId);
1264// return temp;
1265 return null;
1266 }
1267
1268 public static int getMinPreferredMarketSize(FleetParamsV3 params) {
1269 float fp = params.getTotalPts();
1270
1271 if (fp <= 20) return 1;
1272 if (fp <= 50) return 3;
1273 if (fp <= 100) return 5;
1274 if (fp <= 150) return 7;
1275
1276 return 8;
1277 }
1278
1279
1280
1281
1282 private static List<String> startingAbilities = null;
1283 public static CampaignFleetAPI createEmptyFleet(String factionId, String fleetType, MarketAPI market) {
1284 FactionAPI faction = Global.getSector().getFaction(factionId);
1285 String fleetName = faction.getFleetTypeName(fleetType);
1286 CampaignFleetAPI fleet = Global.getFactory().createEmptyFleet(factionId, fleetName, true);
1287 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FLEET_TYPE, fleetType);
1288
1289 if (market != null && !market.getId().equals("fake")) {
1290 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_SOURCE_MARKET, market.getId());
1291 }
1292
1293 if (startingAbilities == null) {
1294 startingAbilities = new ArrayList<String>();
1295 for (String id : Global.getSettings().getSortedAbilityIds()) {
1296 AbilitySpecAPI spec = Global.getSettings().getAbilitySpec(id);
1297 if (spec.isAIDefault()) {
1298 startingAbilities.add(id);
1299 }
1300 }
1301 }
1302
1303 for (String id : startingAbilities) {
1304 fleet.addAbility(id);
1305 }
1306
1307 return fleet;
1308 }
1309
1310 public static class FPRemaining {
1311 public int fp;
1312
1313 public FPRemaining(int fp) {
1314 this.fp = fp;
1315 }
1316 public FPRemaining() {
1317 }
1318 }
1319
1320 public static float addToFleet(String role, MarketAPI market, Random random, CampaignFleetAPI fleet, int maxFP, FleetParamsV3 params) {
1321 float total = 0f;
1322 List<ShipRolePick> picks = market.pickShipsForRole(role, fleet.getFaction().getId(),
1323 new ShipPickParams(params.mode, maxFP, params.timestamp, params.blockFallback), random, null);
1324 for (ShipRolePick pick : picks) {
1325 total += addToFleet(pick, fleet, random);
1326 }
1327 return total;
1328 }
1329
1330 protected static float addToFleet(ShipRolePick pick, CampaignFleetAPI fleet, Random random) {
1331 FleetMemberAPI member = Global.getFactory().createFleetMember(FleetMemberType.SHIP, pick.variantId);
1332 String name = fleet.getFleetData().pickShipName(member, random);
1333 member.setShipName(name);
1334 fleet.getFleetData().addFleetMember(member);
1335 return member.getFleetPointCost();
1336 }
1337
1338// public static float addCombatFleetPoints(CampaignFleetAPI fleet, Random random,
1339// float fp, FleetParamsV3 params) {
1340// FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
1341// if (params.doctrineOverride != null) {
1342// doctrine = params.doctrineOverride;
1343// }
1344//
1345// int size = doctrine.getShipSize();
1346//
1347// boolean addedSomething = true;
1348// FPRemaining rem = new FPRemaining();
1349// rem.fp = (int) fp;
1350//
1351// String smallRole = ShipRoles.COMBAT_SMALL_FOR_SMALL_FLEET;
1352// if (!params.banPhaseShipsEtc) {
1353// smallRole = ShipRoles.COMBAT_SMALL;
1354// }
1355//
1356// while (addedSomething && rem.fp > 0) {
1357// int small = BASE_COUNTS_WITH_4[size - 1][0] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][0] + 1);
1358// int medium = BASE_COUNTS_WITH_4[size - 1][1] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][1] + 1);
1359// int large = BASE_COUNTS_WITH_4[size - 1][2] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][2] + 1);
1360// int capital = BASE_COUNTS_WITH_4[size - 1][3] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][3] + 1);
1361//
1364//
1365// if (params.maxShipSize <= 1) medium = 0;
1366// if (params.maxShipSize <= 2) large = 0;
1367// if (params.maxShipSize <= 3) capital = 0;
1368//
1369// int smallPre = small / 2;
1370// small -= smallPre;
1371//
1372// int mediumPre = medium / 2;
1373// medium -= mediumPre;
1374//
1375// addedSomething = false;
1376//
1377// addedSomething |= addShips(smallRole, smallPre, params.source, random, fleet, rem, params);
1378//
1379// addedSomething |= addShips(ShipRoles.COMBAT_MEDIUM, mediumPre, params.source, random, fleet, rem, params);
1380// addedSomething |= addShips(smallRole, small, params.source, random, fleet, rem, params);
1381//
1382// addedSomething |= addShips(ShipRoles.COMBAT_LARGE, large, params.source, random, fleet, rem, params);
1383// addedSomething |= addShips(ShipRoles.COMBAT_MEDIUM, medium, params.source, random, fleet, rem, params);
1384//
1385// addedSomething |= addShips(ShipRoles.COMBAT_CAPITAL, capital, params.source, random, fleet, rem, params);
1386// }
1387//
1388// return fp - rem.fp;
1389// }
1390
1391 public static boolean addShips(String role, int count, MarketAPI market, Random random, CampaignFleetAPI fleet, FPRemaining rem, FleetParamsV3 params) {
1392 boolean addedSomething = false;
1393 for (int i = 0; i < count; i++) {
1394 if (rem.fp <= 0) break;
1395 float added = addToFleet(role, market, random, fleet, rem.fp, params);
1396 if (added > 0) {
1397 rem.fp -= added;
1398 addedSomething = true;
1399 }
1400 }
1401 return addedSomething;
1402 }
1403
1404
1405 public static float addPhaseFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1406 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.SMALL_IS_FRIGATE,
1407 ShipRoles.PHASE_SMALL, ShipRoles.PHASE_MEDIUM, ShipRoles.PHASE_LARGE);
1408// FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
1409// if (params.doctrineOverride != null) {
1410// doctrine = params.doctrineOverride;
1411// }
1412//
1413// int size = doctrine.getShipSize();
1414//
1415// boolean addedSomething = true;
1416// FPRemaining rem = new FPRemaining();
1417// rem.fp = (int) fp;
1418//
1419// while (addedSomething && rem.fp > 0) {
1420// int small = BASE_COUNTS_WITH_3[size - 1][0] + random.nextInt(MAX_EXTRA_WITH_3[size - 1][0] + 1);
1421// int medium = BASE_COUNTS_WITH_3[size - 1][1] + random.nextInt(MAX_EXTRA_WITH_3[size - 1][1] + 1);
1422// int large = BASE_COUNTS_WITH_3[size - 1][2] + random.nextInt(MAX_EXTRA_WITH_3[size - 1][2] + 1);
1423//
1424// //System.out.println(String.format("Small: %s Medium: %s Large: %s Capital: %s",
1425// //"" + small, "" + medium, "" + large, "" + capital));
1426//
1427// if (params.maxShipSize <= 1) medium = 0;
1428// if (params.maxShipSize <= 2) large = 0;
1429//
1430// int smallPre = small / 2;
1431// small -= smallPre;
1432//
1433// int mediumPre = medium / 2;
1434// medium -= mediumPre;
1435//
1436// addedSomething = false;
1437//
1438// addedSomething |= addShips(ShipRoles.PHASE_SMALL, smallPre, params.source, random, fleet, rem, params);
1439//
1440// addedSomething |= addShips(ShipRoles.PHASE_MEDIUM, mediumPre, params.source, random, fleet, rem, params);
1441// addedSomething |= addShips(ShipRoles.PHASE_SMALL, small, params.source, random, fleet, rem, params);
1442//
1443// addedSomething |= addShips(ShipRoles.PHASE_LARGE, large, params.source, random, fleet, rem, params);
1444// addedSomething |= addShips(ShipRoles.PHASE_MEDIUM, medium, params.source, random, fleet, rem, params);
1445// }
1446//
1447// return fp - rem.fp;
1448 }
1449
1450 public static enum SizeFilterMode {
1451 NONE,
1452 SMALL_IS_FRIGATE,
1453 SMALL_IS_DESTROYER,
1454 }
1455 public static float addCarrierFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1456 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.SMALL_IS_DESTROYER,
1457 ShipRoles.CARRIER_SMALL, ShipRoles.CARRIER_MEDIUM, ShipRoles.CARRIER_LARGE);
1458 }
1459 public static float addPriorityOnlyThenAll(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params,
1460 SizeFilterMode sizeFilterMode,
1461 String roleSmall, String roleMedium, String roleLarge) {
1462 if (fp <= 0) return 0f;
1463
1464 float added = 0f;
1465 if (params.mode == ShipPickMode.PRIORITY_THEN_ALL) {
1466 int numPriority = fleet.getFaction().getNumAvailableForRole(roleSmall, ShipPickMode.PRIORITY_ONLY) +
1467 fleet.getFaction().getNumAvailableForRole(roleMedium, ShipPickMode.PRIORITY_ONLY) +
1468 fleet.getFaction().getNumAvailableForRole(roleLarge, ShipPickMode.PRIORITY_ONLY);
1469
1470 if (numPriority > 0) {
1471 params.mode = ShipPickMode.PRIORITY_ONLY;
1472 added = addFleetPoints(fleet, random, fp, params, sizeFilterMode,
1473 roleSmall, roleMedium, roleLarge);
1474 params.mode = ShipPickMode.PRIORITY_THEN_ALL;
1475 } else {
1476 params.mode = ShipPickMode.ALL;
1477 added = addFleetPoints(fleet, random, fp, params, sizeFilterMode,
1478 roleSmall, roleMedium, roleLarge);
1479 params.mode = ShipPickMode.PRIORITY_THEN_ALL;
1480 }
1481 // if there ARE priority ships for a 3-type category (i.e. carriers/phases/various civs,
1482 // then ONLY use priority, and use nothing if a priority ship was not added (since that just means not enough FP
1483 // for likely a smaller fleet.)
1484// if (added <= 0) {
1485// added = addFleetPoints(fleet, random, fp, params, sizeFilterMode,
1486// roleSmall, roleMedium, roleLarge);
1487// }
1488 } else {
1489 added = addFleetPoints(fleet, random, fp, params, sizeFilterMode,
1490 roleSmall, roleMedium, roleLarge);
1491 }
1492 return added;
1493 }
1494
1495 public static float addTankerFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1496 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.SMALL_IS_DESTROYER,
1497 ShipRoles.TANKER_SMALL, ShipRoles.TANKER_MEDIUM, ShipRoles.TANKER_LARGE);
1498 }
1499
1500 public static float addFreighterFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1501 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.NONE,
1502 ShipRoles.FREIGHTER_SMALL, ShipRoles.FREIGHTER_MEDIUM, ShipRoles.FREIGHTER_LARGE);
1503 }
1504
1505 public static float addLinerFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1506 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.NONE,
1507 ShipRoles.LINER_SMALL, ShipRoles.LINER_MEDIUM, ShipRoles.LINER_LARGE);
1508 }
1509
1510 public static float addCombatFreighterFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1511 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.SMALL_IS_FRIGATE,
1512 ShipRoles.COMBAT_FREIGHTER_SMALL, ShipRoles.COMBAT_FREIGHTER_MEDIUM, ShipRoles.COMBAT_FREIGHTER_LARGE);
1513 }
1514
1515 public static float addTransportFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1516 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.NONE,
1517 ShipRoles.PERSONNEL_SMALL, ShipRoles.PERSONNEL_MEDIUM, ShipRoles.PERSONNEL_LARGE);
1518 }
1519
1520 public static float addUtilityFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params) {
1521 return addPriorityOnlyThenAll(fleet, random, fp, params, SizeFilterMode.NONE,
1522 ShipRoles.UTILITY, ShipRoles.UTILITY, ShipRoles.UTILITY);
1523 }
1524
1525
1526 protected static int sizeOverride = 0;
1527 // tend towards larger ships as fleets get more members, regardless of doctrine
1528 public static int getAdjustedDoctrineSize(int size, CampaignFleetAPI fleetSoFar) {
1529 if (sizeOverride > 0) return sizeOverride;
1530 else return size;
1531
1532// int num = fleetSoFar.getNumMembersFast();
1533// if (num > 8 && size <= 2) {
1534// size++;
1535// }
1536// if (num > 14 && size <= 3) {
1537// size++;
1538// }
1539// if (num > 20 && size <= 4) {
1540// size++;
1541// }
1542// if (size > 5) size = 5;
1543// return size;
1544 }
1545
1546
1547 public static float addFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params,
1548 SizeFilterMode sizeFilterMode,
1549 String ... roles) {
1550 FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
1551 if (params.doctrineOverride != null) {
1552 doctrine = params.doctrineOverride;
1553 }
1554
1555 int size = doctrine.getShipSize();
1556 //size = getAdjustedDoctrineSize(size, fleet);
1557
1558 boolean addedSomething = true;
1559 FPRemaining rem = new FPRemaining();
1560 rem.fp = (int) fp;
1561
1562 while (addedSomething && rem.fp > 0) {
1563 size = getAdjustedDoctrineSize(size, fleet);
1564
1565 int small = BASE_COUNTS_WITH_3[size - 1][0] + random.nextInt(MAX_EXTRA_WITH_3[size - 1][0] + 1);
1566 int medium = BASE_COUNTS_WITH_3[size - 1][1] + random.nextInt(MAX_EXTRA_WITH_3[size - 1][1] + 1);
1567 int large = BASE_COUNTS_WITH_3[size - 1][2] + random.nextInt(MAX_EXTRA_WITH_3[size - 1][2] + 1);
1568
1569// if (sizeOverride > 0) {
1570// small = 0;
1571// medium = 0;
1572// }
1573
1574 if (sizeFilterMode == SizeFilterMode.SMALL_IS_FRIGATE) {
1575 if (params.maxShipSize <= 1) medium = 0;
1576 if (params.maxShipSize <= 2) large = 0;
1577 } else if (sizeFilterMode == SizeFilterMode.SMALL_IS_DESTROYER) {
1578 if (params.maxShipSize <= 2) medium = 0;
1579 if (params.maxShipSize <= 3) large = 0;
1580 }
1581
1582 //System.out.println(String.format("Small: %s Medium: %s Large: %s Capital: %s",
1583 //"" + small, "" + medium, "" + large, "" + capital));
1584
1585 int smallPre = small / 2;
1586 small -= smallPre;
1587
1588 int mediumPre = medium / 2;
1589 medium -= mediumPre;
1590
1591 addedSomething = false;
1592
1593 addedSomething |= addShips(roles[0], smallPre, params.source, random, fleet, rem, params);
1594
1595 addedSomething |= addShips(roles[1], mediumPre, params.source, random, fleet, rem, params);
1596 addedSomething |= addShips(roles[0], small, params.source, random, fleet, rem, params);
1597
1598 addedSomething |= addShips(roles[2], large, params.source, random, fleet, rem, params);
1599 addedSomething |= addShips(roles[1], medium, params.source, random, fleet, rem, params);
1600 }
1601
1602 return fp - rem.fp;
1603 }
1604
1605
1606
1607
1608
1609 public static void addCombatFleetPoints(CampaignFleetAPI fleet, Random random,
1610 float warshipFP, float carrierFP, float phaseFP, FleetParamsV3 params) {
1611
1612 FactionAPI faction = fleet.getFaction();
1613 FactionDoctrineAPI doctrine = faction.getDoctrine();
1614 if (params.doctrineOverride != null) {
1615 doctrine = params.doctrineOverride;
1616 }
1617
1618 WeightedRandomPicker<String> smallPicker = new WeightedRandomPicker<String>(random);
1619 WeightedRandomPicker<String> mediumPicker = new WeightedRandomPicker<String>(random);
1620 WeightedRandomPicker<String> largePicker = new WeightedRandomPicker<String>(random);
1621 WeightedRandomPicker<String> capitalPicker = new WeightedRandomPicker<String>(random);
1622 WeightedRandomPicker<String> priorityCapitalPicker = new WeightedRandomPicker<String>(random);
1623
1624 String smallRole = ShipRoles.COMBAT_SMALL_FOR_SMALL_FLEET;
1625 if (!params.banPhaseShipsEtc) {
1626 smallRole = ShipRoles.COMBAT_SMALL;
1627 }
1628
1629// if (warshipFP > 0) smallPicker.add(smallRole, 1);
1630// if (phaseFP > 0) smallPicker.add(ShipRoles.PHASE_SMALL, 1);
1631//
1632// if (warshipFP > 0) mediumPicker.add(ShipRoles.COMBAT_MEDIUM, 1);
1633// if (phaseFP > 0) mediumPicker.add(ShipRoles.PHASE_MEDIUM, 1);
1634// if (carrierFP > 0) mediumPicker.add(ShipRoles.CARRIER_SMALL, 1);
1635//
1636// if (warshipFP > 0) largePicker.add(ShipRoles.COMBAT_LARGE, 1);
1637// if (phaseFP > 0) largePicker.add(ShipRoles.PHASE_LARGE, 1);
1638// if (carrierFP > 0) largePicker.add(ShipRoles.CARRIER_MEDIUM, 1);
1639//
1640// if (warshipFP > 0) capitalPicker.add(ShipRoles.COMBAT_CAPITAL, 1);
1641// if (phaseFP > 0) capitalPicker.add(ShipRoles.PHASE_CAPITAL, 1);
1642// if (carrierFP > 0) capitalPicker.add(ShipRoles.CARRIER_LARGE, 1);
1643
1644 smallPicker.add(smallRole, warshipFP);
1645 smallPicker.add(ShipRoles.PHASE_SMALL, phaseFP);
1646
1647 mediumPicker.add(ShipRoles.COMBAT_MEDIUM, warshipFP);
1648 mediumPicker.add(ShipRoles.PHASE_MEDIUM, phaseFP);
1649 mediumPicker.add(ShipRoles.CARRIER_SMALL, carrierFP);
1650
1651 largePicker.add(ShipRoles.COMBAT_LARGE, warshipFP);
1652 largePicker.add(ShipRoles.PHASE_LARGE, phaseFP);
1653 largePicker.add(ShipRoles.CARRIER_MEDIUM, carrierFP);
1654
1655 capitalPicker.add(ShipRoles.COMBAT_CAPITAL, warshipFP);
1656 capitalPicker.add(ShipRoles.PHASE_CAPITAL, phaseFP);
1657 capitalPicker.add(ShipRoles.CARRIER_LARGE, carrierFP);
1658
1659
1660 Set<String> usePriorityOnly = new HashSet<String>();
1661
1662 if (params.mode == ShipPickMode.PRIORITY_THEN_ALL) {
1663 float num = faction.getVariantWeightForRole(ShipRoles.COMBAT_CAPITAL, ShipPickMode.PRIORITY_ONLY);
1664 if (num > 0) {
1665 //priorityCapitalPicker.add(ShipRoles.COMBAT_CAPITAL, doctrine.getWarships() + 1);
1666 priorityCapitalPicker.add(ShipRoles.COMBAT_CAPITAL, num);
1667 }
1668 num = faction.getVariantWeightForRole(ShipRoles.CARRIER_LARGE, ShipPickMode.PRIORITY_ONLY);
1669 if (num > 0) {
1670 //priorityCapitalPicker.add(ShipRoles.CARRIER_LARGE, doctrine.getCarriers() + 1);
1671 priorityCapitalPicker.add(ShipRoles.CARRIER_LARGE, num);
1672 }
1673 num = faction.getVariantWeightForRole(ShipRoles.PHASE_CAPITAL, ShipPickMode.PRIORITY_ONLY);
1674 if (num > 0) {
1675 //priorityCapitalPicker.add(ShipRoles.PHASE_CAPITAL, doctrine.getPhaseShips() + 1);
1676 priorityCapitalPicker.add(ShipRoles.PHASE_CAPITAL, num);
1677 }
1678
1679 if (params.mode == ShipPickMode.PRIORITY_THEN_ALL) {
1680 addToPriorityOnlySet(fleet, usePriorityOnly, ShipRoles.PHASE_SMALL, ShipRoles.PHASE_MEDIUM, ShipRoles.PHASE_LARGE);
1681 addToPriorityOnlySet(fleet, usePriorityOnly, ShipRoles.CARRIER_SMALL, ShipRoles.CARRIER_MEDIUM, ShipRoles.CARRIER_LARGE);
1682 }
1683 }
1684
1685 Map<String, FPRemaining> remaining = new HashMap<String, FPRemaining>();
1686 FPRemaining remWarship = new FPRemaining((int)warshipFP);
1687 FPRemaining remCarrier = new FPRemaining((int)carrierFP);
1688 FPRemaining remPhase = new FPRemaining((int)phaseFP);
1689
1690 remaining.put(ShipRoles.COMBAT_SMALL_FOR_SMALL_FLEET, remWarship);
1691 remaining.put(ShipRoles.COMBAT_SMALL, remWarship);
1692 remaining.put(ShipRoles.COMBAT_MEDIUM, remWarship);
1693 remaining.put(ShipRoles.COMBAT_LARGE, remWarship);
1694 remaining.put(ShipRoles.COMBAT_CAPITAL, remWarship);
1695
1696 remaining.put(ShipRoles.CARRIER_SMALL, remCarrier);
1697 remaining.put(ShipRoles.CARRIER_MEDIUM, remCarrier);
1698 remaining.put(ShipRoles.CARRIER_LARGE, remCarrier);
1699
1700 remaining.put(ShipRoles.PHASE_SMALL, remPhase);
1701 remaining.put(ShipRoles.PHASE_MEDIUM, remPhase);
1702 remaining.put(ShipRoles.PHASE_LARGE, remPhase);
1703 remaining.put(ShipRoles.PHASE_CAPITAL, remPhase);
1704
1705
1706 if (params.maxShipSize <= 1) {
1707 mediumPicker.clear();
1708 }
1709 if (params.maxShipSize <= 2) {
1710 largePicker.clear();
1711 }
1712 if (params.maxShipSize <= 3) {
1713 capitalPicker.clear();
1714 }
1715
1716 if (params.minShipSize >= 2) {
1717 smallPicker.clear();
1718 }
1719 if (params.minShipSize >= 3) {
1720 mediumPicker.clear();
1721 }
1722 if (params.minShipSize >= 4) {
1723 largePicker.clear();
1724 }
1725
1726
1727 int size = doctrine.getShipSize();
1728 //size = getAdjustedDoctrineSize(size, fleet);
1729
1730 int numFails = 0;
1731 while (numFails < 2) {
1732 size = getAdjustedDoctrineSize(size, fleet);
1733
1734// if (size > 5) {
1735// System.out.println("wefwefe");
1736// }
1737
1738 int small = BASE_COUNTS_WITH_4[size - 1][0] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][0] + 1);
1739 int medium = BASE_COUNTS_WITH_4[size - 1][1] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][1] + 1);
1740 int large = BASE_COUNTS_WITH_4[size - 1][2] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][2] + 1);
1741 int capital = BASE_COUNTS_WITH_4[size - 1][3] + random.nextInt(MAX_EXTRA_WITH_4[size - 1][3] + 1);
1742
1743 if (size < 5 && capital > 1) {
1744 capital = 1;
1745 }
1746
1747 if (params.maxShipSize <= 1) medium = 0;
1748 if (params.maxShipSize <= 2) large = 0;
1749 if (params.maxShipSize <= 3) capital = 0;
1750
1751 if (params.minShipSize >= 2) small = 0;
1752 if (params.minShipSize >= 3) medium = 0;
1753 if (params.minShipSize >= 4) large = 0;
1754
1755 int smallPre = small / 2;
1756 small -= smallPre;
1757
1758 int mediumPre = medium / 2;
1759 medium -= mediumPre;
1760
1761 boolean addedSomething = false;
1762
1763 //System.out.println("Rem carrier pre: " + remCarrier.fp);
1764 addedSomething |= addShips(smallPicker, usePriorityOnly, remaining, null, smallPre, fleet, random, params);
1765 //System.out.println("Rem carrier after smallPre: " + remCarrier.fp);
1766 addedSomething |= addShips(mediumPicker, usePriorityOnly, remaining, null, mediumPre, fleet, random, params);
1767 //System.out.println("Rem carrier after mediumPre: " + remCarrier.fp);
1768 addedSomething |= addShips(smallPicker, usePriorityOnly, remaining, null, small, fleet, random, params);
1769 //System.out.println("Rem carrier after small: " + remCarrier.fp);
1770 addedSomething |= addShips(largePicker, usePriorityOnly, remaining, null, large, fleet, random, params);
1771 //System.out.println("Rem carrier after large: " + remCarrier.fp);
1772 addedSomething |= addShips(mediumPicker, usePriorityOnly, remaining, null, medium, fleet, random, params);
1773 //System.out.println("Rem carrier after medium: " + remCarrier.fp);
1774
1775
1776 if (!priorityCapitalPicker.isEmpty()) {
1777 params.mode = ShipPickMode.PRIORITY_ONLY;
1778 params.blockFallback = true;
1779 FPRemaining combined = new FPRemaining(remWarship.fp + remCarrier.fp + remPhase.fp);
1780 boolean addedCapital = addShips(priorityCapitalPicker, usePriorityOnly, remaining, combined, capital, fleet, random, params);
1781 addedSomething |= addedCapital;
1782 if (addedCapital) {
1783 redistributeFP(remWarship, remCarrier, remPhase, combined.fp);
1784 }
1785 params.mode = ShipPickMode.PRIORITY_THEN_ALL;
1786 params.blockFallback = null;
1787 //System.out.println("Rem carrier after capitals priority: " + remCarrier.fp);
1788 } else {
1789 addedSomething |= addShips(capitalPicker, usePriorityOnly, remaining, null, capital, fleet, random, params);
1790 //System.out.println("Rem carrier after capitals normal: " + remCarrier.fp);
1791 }
1792
1793 if (!addedSomething) {
1794 numFails++;
1795
1796 if (numFails == 2) {
1797 boolean goAgain = false;
1798 if (remPhase.fp > 0) {
1799 remWarship.fp += remPhase.fp;
1800 remPhase.fp = 0;
1801 goAgain = true;
1802 }
1803 if (remCarrier.fp > 0) {
1804 remWarship.fp += remCarrier.fp;
1805 remCarrier.fp = 0;
1806 goAgain = true;
1807 }
1808
1809 if (goAgain) {
1810 numFails = 0;
1811 smallPicker.add(smallRole, 1);
1812 mediumPicker.add(ShipRoles.COMBAT_MEDIUM, 1);
1813 largePicker.add(ShipRoles.COMBAT_LARGE, 1);
1814 capitalPicker.add(ShipRoles.COMBAT_CAPITAL, 1);
1815 }
1816 }
1817 }
1818 }
1819 }
1820
1821 protected static void addToPriorityOnlySet(CampaignFleetAPI fleet, Set<String> set, String small, String medium, String large) {
1822 int numPriority = fleet.getFaction().getNumAvailableForRole(small, ShipPickMode.PRIORITY_ONLY) +
1823 fleet.getFaction().getNumAvailableForRole(medium, ShipPickMode.PRIORITY_ONLY) +
1824 fleet.getFaction().getNumAvailableForRole(large, ShipPickMode.PRIORITY_ONLY);
1825 if (numPriority > 0) {
1826 set.add(small);
1827 set.add(medium);
1828 set.add(large);
1829 }
1830 }
1831
1832 protected static void redistributeFP(FPRemaining one, FPRemaining two, FPRemaining three, int newTotal) {
1833 float total = one.fp + two.fp + three.fp;
1834 if (total <= 0) return;
1835
1836 int f1 = (int) Math.round((float)one.fp / total * newTotal);
1837 int f2 = (int) Math.round((float)two.fp / total * newTotal);
1838 int f3 = (int) Math.round((float)three.fp / total * newTotal);
1839
1840 f1 += newTotal - f1 - f2 - f3;
1841
1842 one.fp = f1;
1843 two.fp = f2;
1844 three.fp = f3;
1845 }
1846
1847 public static boolean addShips(WeightedRandomPicker<String> rolePicker, Set<String> usePriorityOnly, Map<String, FPRemaining> remaining, FPRemaining remOverride, int count,
1848 CampaignFleetAPI fleet, Random random, FleetParamsV3 params) {
1849 if (rolePicker.isEmpty()) return false;
1850
1851 boolean addedSomething = false;
1852 for (int i = 0; i < count; i++) {
1853 String role = rolePicker.pick();
1854 if (role == null) break;
1855 FPRemaining rem = remaining.get(role);
1856 FPRemaining remForProperRole = rem;
1857 if (remOverride != null) rem = remOverride;
1858 if (usePriorityOnly.contains(role)) {
1859 params.mode = ShipPickMode.PRIORITY_ONLY;
1860 }
1861 int fpPrePick = rem.fp;
1862
1863 boolean added = addShips(role, 1, params.source, random, fleet, rem, params);
1864
1865 if (added && remOverride != null) {
1866 int fpSpent = fpPrePick - rem.fp;
1867 int maxToTakeFromProperRole = Math.min(remForProperRole.fp, fpSpent);
1868 remForProperRole.fp -= maxToTakeFromProperRole;
1869 }
1870
1871 if (usePriorityOnly.contains(role)) {
1872 params.mode = ShipPickMode.PRIORITY_THEN_ALL;
1873 }
1874 if (!added) {
1875 rolePicker.remove(role);
1876 i--;
1877 if (rolePicker.isEmpty()) {
1878 break;
1879 }
1880 }
1881 addedSomething |= added;
1882 }
1883 return addedSomething;
1884 }
1885
1886 public static float getShipDeficitFleetSizeMult(MarketAPI market) {
1887 float mult = 1f;
1888 CommodityOnMarketAPI com = market.getCommodityData(Commodities.SHIPS);
1889 float available = com.getAvailable();
1890 float demand = com.getMaxDemand();
1891 if (demand > 0) {
1892 float f = available / demand;
1894 mult *= f;
1895 }
1896 if (mult < 0) mult = 0;
1897 if (mult > 1) mult = 1;
1898 return mult;
1899 }
1900
1901
1902
1903 public static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, Random random) {
1904 addCommanderSkills(commander, fleet, null, random);
1905 }
1906
1907
1908 public static void applyDamageToFleet(CampaignFleetAPI fleet, float damage,
1909 boolean damageRemainingShips, Random random) {
1910 if (random == null) random = Misc.random;
1911 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>();
1912
1913 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy();
1914 for (FleetMemberAPI member : members) {
1915 float w = 1f;
1916 if (member.isCivilian()) w *= 0.25f;
1917
1918 picker.add(member, w);
1919 }
1920
1921 List<FleetMemberAPI> remove = new ArrayList<FleetMemberAPI>();
1922 float removedFP = 0f;
1923 float fpToRemove = fleet.getFleetPoints() * damage * 0.8f;
1924
1925 while (removedFP < fpToRemove && remove.size() < members.size() - 1 && !picker.isEmpty()) {
1926 FleetMemberAPI member = picker.pickAndRemove();
1927 removedFP += member.getFleetPointCost();
1928 remove.add(member);
1929 }
1930
1931 for (FleetMemberAPI member : remove) {
1932 fleet.getFleetData().removeFleetMember(member);
1933 }
1934
1935
1936 if (damageRemainingShips) {
1937 int numStrikes = (int) Math.round(picker.getItems().size() * damage);
1938
1939 for (int i = 0; i < numStrikes; i++) {
1940 FleetMemberAPI member = picker.pick();
1941 if (member == null) return;
1942
1943 if (random.nextFloat() > damage) continue;
1944
1945 float crPerDep = member.getDeployCost();
1946 //if (crPerDep <= 0) continue;
1947 float suppliesPerDep = member.getStats().getSuppliesToRecover().getModifiedValue();
1948 if (suppliesPerDep <= 0 || crPerDep <= 0) return;
1949 float suppliesPer100CR = suppliesPerDep * 1f / Math.max(0.01f, crPerDep);
1950
1951 float strikeSupplies = suppliesPer100CR * damage * (0.25f + 0.75f * random.nextFloat());
1952 float strikeDamage = strikeSupplies / suppliesPer100CR * (0.75f + (float) Math.random() * 0.5f);
1953
1954 if (strikeDamage > HyperspaceTerrainPlugin.STORM_MAX_STRIKE_DAMAGE) {
1955 strikeDamage = HyperspaceTerrainPlugin.STORM_MAX_STRIKE_DAMAGE;
1956 }
1957
1958 if (strikeDamage > 0) {
1959 float currCR = member.getRepairTracker().getBaseCR();
1960 float crDamage = Math.min(currCR, strikeDamage);
1961
1962 member.getRepairTracker().setCR(currCR - crDamage);
1963
1964 float hitStrength = member.getStats().getArmorBonus().computeEffective(member.getHullSpec().getArmorRating());
1965 int numHits = (int) (strikeDamage / 0.1f);
1966 if (numHits < 1) numHits = 1;
1967 for (int j = 0; j < numHits; j++) {
1968 member.getStatus().applyDamage(hitStrength);
1969 }
1970
1971 if (member.getStatus().getHullFraction() < 0.01f) {
1972 member.getStatus().setHullFraction(0.01f);
1973 picker.remove(member);
1974 } else {
1975 float w = picker.getWeight(member);
1976 picker.setWeight(picker.getItems().indexOf(member), w * 0.5f);
1977 }
1978 }
1979 }
1980 }
1981 }
1982}
1983
1984
1985
1986
1987
1988
1989
1990
1991
static SettingsAPI getSettings()
Definition Global.java:51
static FactoryAPI getFactory()
Definition Global.java:35
static Logger getLogger(Class c)
Definition Global.java:26
static SectorAPI getSector()
Definition Global.java:59
static void pruneFleet(int maxShips, int doctrineSize, CampaignFleetAPI fleet, float targetFP, Random random)
static float addFreighterFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static float addCarrierFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, Random random)
static float addToFleet(String role, MarketAPI market, Random random, CampaignFleetAPI fleet, int maxFP, FleetParamsV3 params)
static void addCommanderAndOfficersV2(CampaignFleetAPI fleet, FleetParamsV3 params, Random random)
static List< FleetMemberAPI > getRemoveOrder(CampaignFleetAPI fleet)
static float addUtilityFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static float addTankerFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static void addCombatFleetPoints(CampaignFleetAPI fleet, Random random, float warshipFP, float carrierFP, float phaseFP, FleetParamsV3 params)
static void redistributeFP(FPRemaining one, FPRemaining two, FPRemaining three, int newTotal)
static void addCommanderAndOfficers(CampaignFleetAPI fleet, FleetParamsV3 params, Random random)
static float getDoctrineNumShipsMult(int doctrineNumShips)
static SkillPickPreference getSkillPrefForShip(FleetMemberAPI member)
static int getAdjustedDoctrineSize(int size, CampaignFleetAPI fleetSoFar)
static void addToPriorityOnlySet(CampaignFleetAPI fleet, Set< String > set, String small, String medium, String large)
static CampaignFleetAPI createFleet(FleetParamsV3 params)
static float addPhaseFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static CampaignFleetAPI createEmptyFleet(String factionId, String fleetType, MarketAPI market)
static void applyDamageToFleet(CampaignFleetAPI fleet, float damage, boolean damageRemainingShips, Random random)
static float addLinerFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static void addAll(int[] ratio, List< FleetMemberAPI > from, LinkedHashSet< FleetMemberAPI > to, int num, Random random)
static float addFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params, SizeFilterMode sizeFilterMode, String ... roles)
static float addToFleet(ShipRolePick pick, CampaignFleetAPI fleet, Random random)
static boolean addShips(WeightedRandomPicker< String > rolePicker, Set< String > usePriorityOnly, Map< String, FPRemaining > remaining, FPRemaining remOverride, int count, CampaignFleetAPI fleet, Random random, FleetParamsV3 params)
static float addTransportFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static float addPriorityOnlyThenAll(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params, SizeFilterMode sizeFilterMode, String roleSmall, String roleMedium, String roleLarge)
static WeightedRandomPicker< HullSize > makePicker(int[] ratio, Random random)
static float addCombatFreighterFleetPoints(CampaignFleetAPI fleet, Random random, float fp, FleetParamsV3 params)
static void addCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, Random random)
static boolean addShips(String role, int count, MarketAPI market, Random random, CampaignFleetAPI fleet, FPRemaining rem, FleetParamsV3 params)
MarketAPI createMarket(String id, String name, int size)
CampaignFleetAPI createEmptyFleet(String factionId, String name, boolean aiMode)
FleetMemberAPI createFleetMember(FleetMemberType type, String variantOrWingId)
AbilitySpecAPI getAbilitySpec(String abilityId)
ShipVariantAPI getVariant(String variantId)
SkillSpecAPI getSkillSpec(String skillId)
List< String > getSortedAbilityIds()
void addCommanderAndOfficers(CampaignFleetAPI fleet, FleetParamsV3 params, Random random)