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