001package com.fs.starfarer.api.impl.campaign.missions; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.LinkedHashMap; 007import java.util.LinkedHashSet; 008import java.util.List; 009import java.util.Map; 010import java.util.Set; 011 012import java.awt.Color; 013 014import org.lwjgl.util.vector.Vector2f; 015 016import com.fs.starfarer.api.Global; 017import com.fs.starfarer.api.campaign.BaseCustomProductionPickerDelegateImpl; 018import com.fs.starfarer.api.campaign.CampaignFleetAPI; 019import com.fs.starfarer.api.campaign.CargoAPI; 020import com.fs.starfarer.api.campaign.FactionAPI; 021import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode; 022import com.fs.starfarer.api.campaign.FactionProductionAPI; 023import com.fs.starfarer.api.campaign.FactionProductionAPI.ItemInProductionAPI; 024import com.fs.starfarer.api.campaign.FactionProductionAPI.ProductionItemType; 025import com.fs.starfarer.api.campaign.FleetInflater; 026import com.fs.starfarer.api.campaign.InteractionDialogAPI; 027import com.fs.starfarer.api.campaign.PersonImportance; 028import com.fs.starfarer.api.campaign.SectorEntityToken; 029import com.fs.starfarer.api.campaign.econ.MarketAPI; 030import com.fs.starfarer.api.campaign.rules.MemoryAPI; 031import com.fs.starfarer.api.characters.PersonAPI; 032import com.fs.starfarer.api.combat.ShipAPI.HullSize; 033import com.fs.starfarer.api.combat.ShipHullSpecAPI; 034import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints; 035import com.fs.starfarer.api.combat.WeaponAPI; 036import com.fs.starfarer.api.combat.WeaponAPI.AIHints; 037import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize; 038import com.fs.starfarer.api.combat.WeaponAPI.WeaponType; 039import com.fs.starfarer.api.fleet.FleetMemberAPI; 040import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 041import com.fs.starfarer.api.impl.campaign.econ.impl.ShipQuality; 042import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflaterParams; 043import com.fs.starfarer.api.impl.campaign.ids.Commodities; 044import com.fs.starfarer.api.impl.campaign.ids.Conditions; 045import com.fs.starfarer.api.impl.campaign.ids.Factions; 046import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 047import com.fs.starfarer.api.impl.campaign.ids.Items; 048import com.fs.starfarer.api.impl.campaign.ids.Ranks; 049import com.fs.starfarer.api.impl.campaign.ids.Tags; 050import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseManager; 051import com.fs.starfarer.api.impl.campaign.intel.contacts.ContactIntel; 052import com.fs.starfarer.api.impl.campaign.intel.misc.ProductionReportIntel.ProductionData; 053import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithBarEvent; 054import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity; 055import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest; 056import com.fs.starfarer.api.impl.campaign.submarkets.StoragePlugin; 057import com.fs.starfarer.api.loading.FighterWingSpecAPI; 058import com.fs.starfarer.api.loading.VariantSource; 059import com.fs.starfarer.api.loading.WeaponSpecAPI; 060import com.fs.starfarer.api.ui.Alignment; 061import com.fs.starfarer.api.ui.LabelAPI; 062import com.fs.starfarer.api.ui.SectorMapAPI; 063import com.fs.starfarer.api.ui.TooltipMakerAPI; 064import com.fs.starfarer.api.util.CountingMap; 065import com.fs.starfarer.api.util.Misc; 066import com.fs.starfarer.api.util.Misc.Token; 067import com.fs.starfarer.api.util.WeightedRandomPicker; 068 069public class CustomProductionContract extends HubMissionWithBarEvent { 070 071 public static float ARMS_DEALER_PROB_PATROL_AFTER = 0.5f; 072 073 public static float PROD_DAYS = 60f; 074 075 public static float PROB_ARMS_DEALER_BAR = 0.25f; 076 public static float PROB_MILITARY_BAR = 0.33f; 077 public static float PROB_INDEPENDENT_BAR = 0.5f; 078 079 public static float PROB_ARMS_DEALER_IS_CONTACT = 0.05f; 080 081 public static float MIN_CAPACITY = 100000; 082 public static float MAX_CAPACITY = 500000; 083 public static int BAR_CAPACITY_BONUS_MIN = 50000; 084 public static int BAR_CAPACITY_BONUS_MAX = 150000; 085 086 087 public static float MAX_PROD_CAPACITY_AT_SHIP_UNITS = 10; 088 public static float MAX_PROD_CAPACITY_MULT = 0.25f; 089 090 public static float DEALER_MIN_CAPACITY = 1000000; 091 public static float DEALER_MAX_CAPACITY = 2000000; 092 public static Map<PersonImportance, Float> DEALER_MULT = new LinkedHashMap<PersonImportance, Float>(); 093 static { 094 DEALER_MULT.put(PersonImportance.VERY_LOW, 0.3f); 095 DEALER_MULT.put(PersonImportance.LOW, 0.3f); 096 DEALER_MULT.put(PersonImportance.MEDIUM, 0.3f); 097 DEALER_MULT.put(PersonImportance.HIGH, 0.6f); 098 DEALER_MULT.put(PersonImportance.VERY_HIGH, 1f); 099 } 100 101 public static float MILITARY_CAP_MULT = 0.6f; 102 103 public static float MILITARY_MAX_COST_DECREASE = 0.3f; 104 public static float TRADE_MAX_COST_INCREASE = 0.3f; 105 public static float DEALER_FIXED_COST_INCREASE = 0.5f; 106 public static float DEALER_VARIABLE_COST_INCREASE = 0.5f; 107 108 public static enum Stage { 109 WAITING, 110 DELIVERED, 111 COMPLETED, // unused, left in for save compat 112 FAILED, 113 } 114 115 protected Set<String> ships = new LinkedHashSet<String>(); 116 protected Set<String> weapons = new LinkedHashSet<String>(); 117 protected Set<String> fighters = new LinkedHashSet<String>(); 118 119 protected boolean armsDealer = false; 120 protected int maxCapacity; 121 protected float costMult; 122 protected ProductionData data; 123 protected int cost; 124 protected FactionAPI faction; 125 protected MarketAPI market; 126 127 @Override 128 protected boolean create(MarketAPI createdAt, boolean barEvent) { 129 //genRandom = Misc.random; 130 131 boolean allowArmsDealer = true; // anywhere is fine 132 boolean allowTrader = createdAt != null && createdAt.getCommodityData(Commodities.SHIPS).getMaxSupply() > 0; 133 boolean allowMilitary = allowTrader && createdAt != null && Misc.isMilitary(createdAt); 134 if (createdAt.isPlayerOwned()) { 135 allowTrader = false; 136 allowMilitary = false; 137 } 138 if (Factions.PIRATES.equals(createdAt.getFaction().getId())) { 139 allowMilitary = false; 140 } 141 142 if (barEvent) { 143 String post = null; 144 if (rollProbability(PROB_ARMS_DEALER_BAR) && allowArmsDealer) { 145 setGiverRank(Ranks.CITIZEN); 146 post = Ranks.POST_ARMS_DEALER; 147 setGiverTags(Tags.CONTACT_UNDERWORLD); 148 setGiverFaction(Factions.PIRATES); 149 } else if (rollProbability(PROB_MILITARY_BAR) && allowMilitary) { 150 List<String> posts = new ArrayList<String>(); 151 posts.add(Ranks.POST_SUPPLY_OFFICER); 152 if (Misc.isMilitary(createdAt)) { 153 posts.add(Ranks.POST_BASE_COMMANDER); 154 } 155 if (Misc.hasOrbitalStation(createdAt)) { 156 posts.add(Ranks.POST_STATION_COMMANDER); 157 } 158 post = pickOne(posts); 159 setGiverRank(pickOne(Ranks.GROUND_CAPTAIN, Ranks.GROUND_COLONEL, Ranks.GROUND_MAJOR, 160 Ranks.SPACE_COMMANDER, Ranks.SPACE_CAPTAIN, Ranks.SPACE_ADMIRAL)); 161 setGiverTags(Tags.CONTACT_MILITARY); 162 } else if (allowTrader) { 163 setGiverRank(Ranks.CITIZEN); 164 post = pickOne(Ranks.POST_TRADER, Ranks.POST_COMMODITIES_AGENT, Ranks.POST_PORTMASTER, 165 Ranks.POST_MERCHANT, Ranks.POST_INVESTOR, Ranks.POST_EXECUTIVE, 166 Ranks.POST_SENIOR_EXECUTIVE, Ranks.POST_ADMINISTRATOR); 167 setGiverTags(Tags.CONTACT_TRADE); 168 if (rollProbability(PROB_INDEPENDENT_BAR)) { 169 setGiverFaction(Factions.INDEPENDENT); 170 } 171 } 172 if (post == null && allowArmsDealer) { 173 setGiverRank(Ranks.CITIZEN); 174 post = Ranks.POST_ARMS_DEALER; 175 setGiverTags(Tags.CONTACT_UNDERWORLD); 176 setGiverFaction(Factions.PIRATES); 177 } 178 if (post == null) return false; 179 180 setGiverPost(post); 181 if (post.equals(Ranks.POST_SENIOR_EXECUTIVE) || 182 post.equals(Ranks.POST_BASE_COMMANDER) || 183 post.equals(Ranks.POST_ADMINISTRATOR)) { 184 setGiverImportance(pickHighImportance()); 185 } else if (post.equals(Ranks.POST_ARMS_DEALER)) { 186 setGiverImportance(pickArmsDealerImportance()); 187 } else { 188 setGiverImportance(pickImportance()); 189 190 } 191 findOrCreateGiver(createdAt, false, false); 192 setGiverIsPotentialContactOnSuccess(); 193 } 194 195 PersonAPI person = getPerson(); 196 if (person == null) return false; 197 198 if (!setPersonMissionRef(person, "$cpc_ref")) { 199 return false; 200 } 201 202 market = getPerson().getMarket(); 203 if (market == null) return false; 204 if (Misc.getStorage(market) == null) return false; 205 206 faction = person.getFaction(); 207 208// armsDealer = Ranks.POST_ARMS_DEALER.equals(person.getPostId()); 209// if (!armsDealer) allowArmsDealer = false; 210 armsDealer = getPerson().hasTag(Tags.CONTACT_UNDERWORLD); 211 212 maxCapacity = getRoundNumber(MIN_CAPACITY + (MAX_CAPACITY - MIN_CAPACITY) * getQuality()); 213 if (barEvent) { 214 maxCapacity += genRoundNumber(BAR_CAPACITY_BONUS_MIN, BAR_CAPACITY_BONUS_MAX); 215 } 216 float capMult = market.getCommodityData(Commodities.SHIPS).getMaxSupply() / MAX_PROD_CAPACITY_AT_SHIP_UNITS; 217 if (capMult > 1) capMult = 1f; 218 if (capMult < MAX_PROD_CAPACITY_MULT) capMult = MAX_PROD_CAPACITY_MULT; 219 maxCapacity *= capMult; 220 if (person.hasTag(Tags.CONTACT_MILITARY) && allowMilitary) { 221 maxCapacity *= MILITARY_CAP_MULT; 222 } 223 maxCapacity = getRoundNumber(maxCapacity); 224 225 if (armsDealer && allowArmsDealer) { // don't care about ship production, since it's just acquisition from wherever 226 PersonImportance imp = getPerson().getImportance(); 227 float mult = DEALER_MULT.get(imp); 228 maxCapacity = getRoundNumber(mult * 229 (DEALER_MIN_CAPACITY + (DEALER_MAX_CAPACITY - DEALER_MIN_CAPACITY) * getQuality())); 230 } 231 232 if (armsDealer && allowArmsDealer) { 233 costMult = 1f + DEALER_FIXED_COST_INCREASE + DEALER_VARIABLE_COST_INCREASE * (1f - getRewardMultFraction()); 234 addArmsDealerBlueprints(); 235 if (ships.isEmpty() && weapons.isEmpty() && fighters.isEmpty()) return false; 236 } else if (person.hasTag(Tags.CONTACT_MILITARY) && allowMilitary) { 237 costMult = 1f - MILITARY_MAX_COST_DECREASE * getRewardMultFraction(); 238 addMilitaryBlueprints(); 239 if (ships.isEmpty() && weapons.isEmpty() && fighters.isEmpty()) return false; 240 } else if (person.hasTag(Tags.CONTACT_TRADE) && allowTrader) { 241 costMult = 1f + TRADE_MAX_COST_INCREASE * (1f - getRewardMultFraction()); 242 } else { 243 return false; 244 } 245 246 setStartingStage(Stage.WAITING); 247 setSuccessStage(Stage.DELIVERED); 248 setFailureStage(Stage.FAILED); 249 setNoAbandon(); 250 251 connectWithDaysElapsed(Stage.WAITING, Stage.DELIVERED, PROD_DAYS); 252 //connectWithDaysElapsed(Stage.WAITING, Stage.DELIVERED, 1f); 253 setStageOnMarketDecivilized(Stage.FAILED, market); 254 255 return true; 256 } 257 258 259 protected void addArmsDealerBlueprints() { 260 boolean [] add = new boolean[3]; 261 add[genRandom.nextInt(add.length)] = true; 262 add[genRandom.nextInt(add.length)] = true; 263 add[genRandom.nextInt(add.length)] = true; 264 265 PersonImportance imp = getPerson().getImportance(); 266 if (imp == PersonImportance.VERY_HIGH) { 267 add[0] = true; 268 add[1] = true; 269 add[2] = true; 270 } 271 272 Set<WeaponType> wTypes = new LinkedHashSet<WeaponAPI.WeaponType>(); 273 Set<WeaponSize> wSizes = new LinkedHashSet<WeaponAPI.WeaponSize>(); 274 Set<HullSize> hullSizes = new LinkedHashSet<HullSize>(); 275 276 WeightedRandomPicker<WeaponType> wTypePicker = new WeightedRandomPicker<WeaponType>(genRandom); 277 wTypePicker.add(WeaponType.BALLISTIC); 278 wTypePicker.add(WeaponType.ENERGY); 279 wTypePicker.add(WeaponType.MISSILE); 280 WeightedRandomPicker<WeaponSize> wSizePicker = new WeightedRandomPicker<WeaponSize>(genRandom); 281 wSizePicker.add(WeaponSize.SMALL); 282 wSizePicker.add(WeaponSize.MEDIUM); 283 wSizePicker.add(WeaponSize.LARGE); 284 285 int nWeapons = 0; 286 int nShips = 0; 287 int nFighters = 0; 288 289 switch (imp) { 290 case VERY_LOW: 291 add[1] = true; 292 wSizes.add(WeaponSize.SMALL); 293 wTypes.add(wTypePicker.pickAndRemove()); 294 nWeapons = 5 + genRandom.nextInt(6); 295 nFighters = 1 + genRandom.nextInt(3); 296 break; 297 case LOW: 298 add[1] = true; 299 wSizePicker.remove(WeaponSize.LARGE); 300 wSizes.add(wSizePicker.pickAndRemove()); 301 wTypes.add(wTypePicker.pickAndRemove()); 302 hullSizes.add(HullSize.FRIGATE); 303 nWeapons = 10 + genRandom.nextInt(6); 304 nShips = 5 + genRandom.nextInt(3); 305 nFighters = 3 + genRandom.nextInt(3); 306 break; 307// case LOW: 308// case VERY_LOW: 309 case MEDIUM: 310 add[1] = true; 311 wSizes.add(wSizePicker.pickAndRemove()); 312 wSizes.add(wSizePicker.pickAndRemove()); 313 wTypes.add(wTypePicker.pickAndRemove()); 314 hullSizes.add(HullSize.FRIGATE); 315 hullSizes.add(HullSize.DESTROYER); 316 nWeapons = 20 + genRandom.nextInt(6); 317 nShips = 10 + genRandom.nextInt(3); 318 nFighters = 5 + genRandom.nextInt(3); 319 break; 320 case HIGH: 321 add[1] = true; 322 wSizes.add(wSizePicker.pickAndRemove()); 323 wSizes.add(wSizePicker.pickAndRemove()); 324 wTypes.add(wTypePicker.pickAndRemove()); 325 wTypes.add(wTypePicker.pickAndRemove()); 326 hullSizes.add(HullSize.FRIGATE); 327 hullSizes.add(HullSize.DESTROYER); 328 hullSizes.add(HullSize.CRUISER); 329 nWeapons = 20 + genRandom.nextInt(6); 330 nShips = 10 + genRandom.nextInt(3); 331 nFighters = 7 + genRandom.nextInt(3); 332 break; 333 case VERY_HIGH: 334 wSizes.add(WeaponSize.SMALL); 335 wSizes.add(WeaponSize.MEDIUM); 336 wSizes.add(WeaponSize.LARGE); 337 338 hullSizes.add(HullSize.FRIGATE); 339 hullSizes.add(HullSize.DESTROYER); 340 hullSizes.add(HullSize.CRUISER); 341 hullSizes.add(HullSize.CAPITAL_SHIP); 342 343 wTypes.add(WeaponType.BALLISTIC); 344 wTypes.add(WeaponType.ENERGY); 345 wTypes.add(WeaponType.MISSILE); 346 nWeapons = 1000; 347 nShips = 1000; 348 nFighters = 1000; 349 break; 350 } 351 352 353 FactionProductionAPI prod = Global.getSector().getPlayerFaction().getProduction().clone(); 354 prod.clear(); 355 356 if (add[0]) { 357 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(genRandom); 358 for (ShipHullSpecAPI spec : Global.getSettings().getAllShipHullSpecs()) { 359 //if (!spec.hasTag(Items.TAG_RARE_BP) && !spec.hasTag(Items.TAG_DEALER)) continue; 360 if (spec.hasTag(Items.TAG_NO_DEALER)) continue; 361 if (spec.hasTag(Tags.NO_SELL) && !spec.hasTag(Items.TAG_DEALER)) continue; 362 if (spec.hasTag(Tags.RESTRICTED)) continue; 363 if (spec.getHints().contains(ShipTypeHints.HIDE_IN_CODEX)) continue; 364 if (spec.getHints().contains(ShipTypeHints.UNBOARDABLE)) continue; 365 if (spec.isDefaultDHull()) continue; // || spec.isDHull()) continue; 366 if (spec.isDHullOldMethod()) continue; 367 if ("shuttlepod".equals(spec.getHullId())) continue; 368 if (ships.contains(spec.getHullId())) continue; 369 if (!hullSizes.contains(spec.getHullSize())) continue; 370 float cost = prod.createSampleItem(ProductionItemType.SHIP, spec.getHullId(), 1).getBaseCost(); 371 cost = (int)Math.round(cost * costMult); 372 if (cost > maxCapacity) continue; 373 picker.add(spec.getHullId(), 10f); 374 } 375// int num = 2 + (int)Math.round(genRandom.nextInt(5) * getQuality()); 376// num += imp.ordinal() * 2; 377// if (imp == PersonImportance.VERY_HIGH) num = 1000; 378 int num = nShips; 379 for (int i = 0; i < num && !picker.isEmpty(); i++) { 380 ships.add(picker.pickAndRemove()); 381 } 382 } 383 384 if (add[1]) { 385 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(genRandom); 386 for (WeaponSpecAPI spec : Global.getSettings().getAllWeaponSpecs()) { 387 //if (!spec.hasTag(Items.TAG_RARE_BP) && !spec.hasTag(Items.TAG_DEALER)) continue; 388 if (spec.hasTag(Items.TAG_NO_DEALER)) continue; 389 if (spec.hasTag(Tags.NO_SELL) && !spec.hasTag(Items.TAG_DEALER)) continue; 390 if (spec.hasTag(Tags.RESTRICTED)) continue; 391 if (spec.getAIHints().contains(AIHints.SYSTEM)) continue; 392 if (weapons.contains(spec.getWeaponId())) continue; 393 if (!wTypes.contains(spec.getType())) continue; 394 if (!wSizes.contains(spec.getSize())) continue; 395 float cost = prod.createSampleItem(ProductionItemType.WEAPON, spec.getWeaponId(), 1).getBaseCost(); 396 cost = (int)Math.round(cost * costMult); 397 if (cost > maxCapacity) continue; 398 picker.add(spec.getWeaponId(), 10f); 399 } 400// int num = 3 + (int)Math.round(genRandom.nextInt(7) * getQuality()); 401// num += imp.ordinal() * 2; 402// if (imp == PersonImportance.VERY_HIGH) num = 1000; 403 int num = nWeapons; 404 for (int i = 0; i < num && !picker.isEmpty(); i++) { 405 weapons.add(picker.pickAndRemove()); 406 } 407 } 408 409 if (add[2]) { 410 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(genRandom); 411 for (FighterWingSpecAPI spec : Global.getSettings().getAllFighterWingSpecs()) { 412 //if (!spec.hasTag(Items.TAG_RARE_BP) && !spec.hasTag(Items.TAG_DEALER)) continue; 413// if (spec.hasTag(Tags.NO_DROP)) continue; 414// if (spec.hasTag(Tags.NO_SELL)) continue; 415 if (spec.hasTag(Items.TAG_NO_DEALER)) continue; 416 if (spec.hasTag(Tags.NO_SELL) && !spec.hasTag(Items.TAG_DEALER)) continue; 417 if (spec.hasTag(Tags.RESTRICTED)) continue; 418 if (fighters.contains(spec.getId())) continue; 419 float cost = prod.createSampleItem(ProductionItemType.FIGHTER, spec.getId(), 1).getBaseCost(); 420 cost = (int)Math.round(cost * costMult); 421 if (cost > maxCapacity) continue; 422 picker.add(spec.getId(), 10f); 423 } 424// int num = 1 + (int)Math.round(genRandom.nextInt(3) * getQuality()); 425// num += imp.ordinal() * 2; 426// if (imp == PersonImportance.VERY_HIGH) num = 1000; 427 int num = nFighters; 428 for (int i = 0; i < num && !picker.isEmpty(); i++) { 429 fighters.add(picker.pickAndRemove()); 430 } 431 } 432 } 433 434 protected void addMilitaryBlueprints() { 435 for (String id : faction.getKnownShips()) { 436 ShipHullSpecAPI spec = Global.getSettings().getHullSpec(id); 437 if (spec.hasTag(Tags.NO_SELL)) continue; 438 if (spec.isDHullOldMethod()) continue; 439 //if (spec.isDHull()) continue; 440 ships.add(id); 441 } 442 for (String id : faction.getKnownWeapons()) { 443 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(id); 444 if (spec.hasTag(Tags.NO_DROP)) continue; 445 if (spec.hasTag(Tags.NO_SELL)) continue; 446 weapons.add(id); 447 } 448 for (String id : faction.getKnownFighters()) { 449 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(id); 450 if (spec.hasTag(Tags.NO_DROP)) continue; 451 if (spec.hasTag(Tags.NO_SELL)) continue; 452 fighters.add(id); 453 } 454 } 455 456 457 protected void updateInteractionDataImpl() { 458 set("$cpc_military", getPerson().hasTag(Tags.CONTACT_MILITARY)); 459 set("$cpc_trade", getPerson().hasTag(Tags.CONTACT_TRADE)); 460 set("$cpc_armsDealer", armsDealer); 461 462 set("$cpc_barEvent", isBarEvent()); 463 set("$cpc_manOrWoman", getPerson().getManOrWoman()); 464 set("$cpc_maxCapacity", Misc.getWithDGS(maxCapacity)); 465 set("$cpc_costPercent", (int)Math.round(costMult * 100f) + "%"); 466 set("$cpc_days", "" + (int) PROD_DAYS); 467 } 468 469 @Override 470 public void addDescriptionForCurrentStage(TooltipMakerAPI info, float width, float height) { 471 float opad = 10f; 472 Color h = Misc.getHighlightColor(); 473 if (currentStage == Stage.WAITING) { 474 float elapsed = getElapsedInCurrentStage(); 475 int d = (int) Math.round(PROD_DAYS - elapsed); 476 PersonAPI person = getPerson(); 477 478 LabelAPI label = info.addPara("The order will be delivered to storage " + market.getOnOrAt() + " " + market.getName() + 479 " in %s " + getDayOrDays(d) + ".", opad, 480 Misc.getHighlightColor(), "" + d); 481 label.setHighlight(market.getName(), "" + d); 482 label.setHighlightColors(market.getFaction().getBaseUIColor(), h); 483 484 //intel.createSmallDescription(info, width, height); 485 showCargoContents(info, width, height); 486 487 488 } else if (currentStage == Stage.DELIVERED) { 489 float elapsed = getElapsedInCurrentStage(); 490 int d = (int) Math.round(elapsed); 491 LabelAPI label = info.addPara("The order was delivered to storage " + market.getOnOrAt() + " " + market.getName() + 492 " %s " + getDayOrDays(d) + " ago.", opad, 493 Misc.getHighlightColor(), "" + d); 494 label.setHighlight(market.getName(), "" + d); 495 label.setHighlightColors(market.getFaction().getBaseUIColor(), h); 496 497 showCargoContents(info, width, height); 498 addDeleteButton(info, width); 499 } else if (currentStage == Stage.FAILED) { 500 if (market.hasCondition(Conditions.DECIVILIZED)) { 501 info.addPara("This order will not be completed because %s" + 502 " has decivilized.", opad, 503 faction.getBaseUIColor(), market.getName()); 504 } else { 505 info.addPara("You've learned that this order will not be completed.", opad); 506 } 507 } 508 } 509 510 @Override 511 public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) { 512 Color h = Misc.getHighlightColor(); 513 if (currentStage == Stage.WAITING) { 514 float elapsed = getElapsedInCurrentStage(); 515 addDays(info, "until delivery", PROD_DAYS - elapsed, tc, pad); 516 return true; 517 } else if (currentStage == Stage.DELIVERED) { 518 info.addPara("Delivered to %s", pad, tc, market.getFaction().getBaseUIColor(), market.getName()); 519 return true; 520 } 521 return false; 522 } 523 524 @Override 525 public String getBaseName() { 526 return "Custom Production Order"; 527 } 528 529 protected String getMissionTypeNoun() { 530 return "contract"; 531 } 532 533 @Override 534 public SectorEntityToken getMapLocation(SectorMapAPI map) { 535 return market.getPrimaryEntity(); 536 } 537 538 @Override 539 public void acceptImpl(InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) { 540 float f = (float) cost / (float) maxCapacity; 541 float p = ContactIntel.DEFAULT_POTENTIAL_CONTACT_PROB * f; 542 if (armsDealer) { 543 p = PROB_ARMS_DEALER_IS_CONTACT * f; 544 } 545 if (potentialContactsOnMissionSuccess != null) { 546 for (PotentialContactData data : potentialContactsOnMissionSuccess) { 547 data.probability = p; 548 } 549 } 550 551 AddRemoveCommodity.addCreditsLossText(cost, dialog.getTextPanel()); 552 Global.getSector().getPlayerFleet().getCargo().getCredits().subtract(cost); 553 adjustRep(dialog.getTextPanel(), null, RepActions.MISSION_SUCCESS); 554 addPotentialContacts(dialog); 555 556 ships = null; 557 fighters = null; 558 weapons = null; 559 } 560 561 562 @Override 563 public void setCurrentStage(Object next, InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) { 564 super.setCurrentStage(next, dialog, memoryMap); 565 566 if (currentStage == Stage.DELIVERED) { 567 StoragePlugin plugin = (StoragePlugin) Misc.getStorage(getPerson().getMarket()); 568 if (plugin == null) return; 569 plugin.setPlayerPaidToUnlock(true); 570 571 CargoAPI cargo = plugin.getCargo(); 572 for (CargoAPI curr : data.data.values()) { 573 cargo.addAll(curr, true); 574 } 575 576 //endSuccess(dialog, memoryMap); 577 578 if (armsDealer && rollProbability(ARMS_DEALER_PROB_PATROL_AFTER)) { 579 PersonAPI person = getPerson(); 580 if (person == null || person.getMarket() == null) return; 581 String patrolFaction = person.getMarket().getFactionId(); 582 if (patrolFaction.equals(person.getFaction().getId()) || 583 Misc.isPirateFaction(person.getMarket().getFaction()) || 584 Misc.isDecentralized(person.getMarket().getFaction()) || 585 patrolFaction.equals(Factions.PLAYER)) { 586 return; 587 } 588 589 DelayedFleetEncounter e = new DelayedFleetEncounter(genRandom, getMissionId()); 590 e.setDelayMedium(); 591 e.setLocationCoreOnly(true, patrolFaction); 592 e.beginCreate(); 593 e.triggerCreateFleet(FleetSize.LARGE, FleetQuality.DEFAULT, patrolFaction, FleetTypes.PATROL_LARGE, new Vector2f()); 594 e.setFleetWantsThing(patrolFaction, 595 "information regarding the arms dealer", "it", 596 "information concerning the activities of known arms dealer, " + person.getNameString(), 597 getRoundNumber(cost / 2), 598 false, ComplicationRepImpact.FULL, 599 DelayedFleetEncounter.TRIGGER_REP_LOSS_HIGH, getPerson()); 600 e.triggerSetAdjustStrengthBasedOnQuality(true, getQuality()); 601 e.triggerSetPatrol(); 602 e.triggerSetStandardAggroInterceptFlags(); 603 e.endCreate(); 604 } 605 } 606 } 607 608 609 @Override 610 protected boolean callAction(final String action, final String ruleId, final InteractionDialogAPI dialog, 611 final List<Token> params, 612 final Map<String, MemoryAPI> memoryMap) { 613 if ("pickPlayerBP".equals(action)) { 614 dialog.showCustomProductionPicker(new BaseCustomProductionPickerDelegateImpl() { 615 @Override 616 public float getCostMult() { 617 return costMult; 618 } 619 @Override 620 public float getMaximumValue() { 621 return maxCapacity; 622 } 623 @Override 624 public void notifyProductionSelected(FactionProductionAPI production) { 625 convertProdToCargo(production); 626 FireBest.fire(null, dialog, memoryMap, "CPCBlueprintsPicked"); 627 } 628 }); 629 return true; 630 } 631 if ("pickContactBP".equals(action)) { 632 dialog.showCustomProductionPicker(new BaseCustomProductionPickerDelegateImpl() { 633 @Override 634 public Set<String> getAvailableFighters() { 635 return fighters; 636 } 637 @Override 638 public Set<String> getAvailableShipHulls() { 639 return ships; 640 } 641 @Override 642 public Set<String> getAvailableWeapons() { 643 return weapons; 644 } 645 @Override 646 public float getCostMult() { 647 return costMult; 648 } 649 @Override 650 public float getMaximumValue() { 651 return maxCapacity; 652 } 653 @Override 654 public void notifyProductionSelected(FactionProductionAPI production) { 655 convertProdToCargo(production); 656 FireBest.fire(null, dialog, memoryMap, "CPCBlueprintsPicked"); 657 } 658 }); 659 return true; 660 } 661 662 return super.callAction(action, ruleId, dialog, params, memoryMap); 663 } 664 665 666 protected void convertProdToCargo(FactionProductionAPI prod) { 667 cost = prod.getTotalCurrentCost(); 668 data = new ProductionData(); 669 CargoAPI cargo = data.getCargo("Order manifest"); 670 671 float quality = ShipQuality.getShipQuality(market, market.getFactionId()); 672 if (armsDealer) { 673 quality = Math.max(quality, 1.5f); // high enough (with some margin, at that) for no d-mods 674 } 675 676 CampaignFleetAPI ships = Global.getFactory().createEmptyFleet(market.getFactionId(), "temp", true); 677 ships.setCommander(Global.getSector().getPlayerPerson()); 678 ships.getFleetData().setShipNameRandom(genRandom); 679 DefaultFleetInflaterParams p = new DefaultFleetInflaterParams(); 680 p.quality = quality; 681 p.mode = ShipPickMode.PRIORITY_THEN_ALL; 682 p.persistent = false; 683 p.seed = genRandom.nextLong(); 684 p.timestamp = null; 685 686 FleetInflater inflater = Misc.getInflater(ships, p); 687 ships.setInflater(inflater); 688 689 for (ItemInProductionAPI item : prod.getCurrent()) { 690 int count = item.getQuantity(); 691 692 if (item.getType() == ProductionItemType.SHIP) { 693 for (int i = 0; i < count; i++) { 694 ships.getFleetData().addFleetMember(item.getSpecId() + "_Hull"); 695 } 696 } else if (item.getType() == ProductionItemType.FIGHTER) { 697 cargo.addFighters(item.getSpecId(), count); 698 } else if (item.getType() == ProductionItemType.WEAPON) { 699 cargo.addWeapons(item.getSpecId(), count); 700 } 701 } 702 703 // so that it adds d-mods 704 ships.inflateIfNeeded(); 705 for (FleetMemberAPI member : ships.getFleetData().getMembersListCopy()) { 706 // it should be due to the inflateIfNeeded() call, this is just a safety check 707 if (member.getVariant().getSource() == VariantSource.REFIT) { 708 member.getVariant().clear(); 709 } 710 cargo.getMothballedShips().addFleetMember(member); 711 } 712 } 713 714 public void showCargoContents(TooltipMakerAPI info, float width, float height) { 715 if (data == null) return; 716 717 Color h = Misc.getHighlightColor(); 718 Color g = Misc.getGrayColor(); 719 Color tc = Misc.getTextColor(); 720 float pad = 3f; 721 float small = 3f; 722 float opad = 10f; 723 724 List<String> keys = new ArrayList<String>(data.data.keySet()); 725 Collections.sort(keys, new Comparator<String>() { 726 public int compare(String o1, String o2) { 727 return o1.compareTo(o2); 728 } 729 }); 730 731 for (String key : keys) { 732 CargoAPI cargo = data.data.get(key); 733 if (cargo.isEmpty() && 734 ((cargo.getMothballedShips() == null || 735 cargo.getMothballedShips().getMembersListCopy().isEmpty()))) { 736 continue; 737 } 738 739 info.addSectionHeading(key, faction.getBaseUIColor(), faction.getDarkUIColor(), 740 Alignment.MID, opad); 741 742 if (!cargo.getStacksCopy().isEmpty()) { 743 info.addPara("Ship weapons and fighters:", opad); 744 info.showCargo(cargo, 20, true, opad); 745 } 746 747 if (!cargo.getMothballedShips().getMembersListCopy().isEmpty()) { 748 CountingMap<String> counts = new CountingMap<String>(); 749 for (FleetMemberAPI member : cargo.getMothballedShips().getMembersListCopy()) { 750 counts.add(member.getVariant().getHullSpec().getHullName() + " " + member.getVariant().getDesignation()); 751 } 752 753 info.addPara("Ship hulls:", opad); 754 info.showShips(cargo.getMothballedShips().getMembersListCopy(), 20, true, 755 getCurrentStage() == Stage.WAITING, opad); 756 } 757 } 758 } 759 760 public PersonImportance pickArmsDealerImportance() { 761 WeightedRandomPicker<PersonImportance> picker = new WeightedRandomPicker<PersonImportance>(genRandom); 762 763// picker.add(PersonImportance.VERY_LOW, 10f); 764// picker.add(PersonImportance.LOW, 10f); 765 picker.add(PersonImportance.MEDIUM, 10f); 766 767// int credits = (int) Global.getSector().getPlayerFleet().getCargo().getCredits().get(); 768// if (credits >= 200000) { 769// picker.add(PersonImportance.MEDIUM, 10f); 770// } 771// if (credits >= 1000000) { 772// picker.add(PersonImportance.HIGH, 10f); 773// } 774// if (credits >= 200000) { 775// picker.add(PersonImportance.VERY_HIGH, 10f); 776// } 777 778 float cycles = PirateBaseManager.getInstance().getDaysSinceStart() / 365f; 779 if (cycles > 1f) { 780// picker.remove(PersonImportance.VERY_LOW); 781// picker.add(PersonImportance.MEDIUM, 20f); 782 picker.add(PersonImportance.HIGH, 5f); 783 } 784 if (cycles > 3f) { 785// picker.remove(PersonImportance.LOW); 786// picker.add(PersonImportance.HIGH, 10f); 787 picker.remove(PersonImportance.MEDIUM); 788 picker.add(PersonImportance.VERY_HIGH, 5f); 789 } 790 if (cycles > 5f) { 791 //picker.add(PersonImportance.VERY_HIGH, 10f); 792 // always very high importance past a certain point, since the goal is to allow easier procurement 793 // of almost any "generally available" hull 794 return PersonImportance.VERY_HIGH; 795 } 796 797 return picker.pick(); 798 } 799 800} 801 802 803 804 805 806 807 808 809 810 811