001package com.fs.starfarer.api.impl.campaign.submarkets; 002 003import java.util.ArrayList; 004import java.util.List; 005import java.util.Random; 006 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.campaign.CampaignFleetAPI; 009import com.fs.starfarer.api.campaign.CampaignUIAPI.CoreUITradeMode; 010import com.fs.starfarer.api.campaign.CargoAPI; 011import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 012import com.fs.starfarer.api.campaign.CargoStackAPI; 013import com.fs.starfarer.api.campaign.CoreUIAPI; 014import com.fs.starfarer.api.campaign.FactionAPI; 015import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode; 016import com.fs.starfarer.api.campaign.FactionDoctrineAPI; 017import com.fs.starfarer.api.campaign.FleetDataAPI; 018import com.fs.starfarer.api.campaign.PlayerMarketTransaction; 019import com.fs.starfarer.api.campaign.SpecialItemData; 020import com.fs.starfarer.api.campaign.SubmarketPlugin; 021import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 022import com.fs.starfarer.api.campaign.econ.MarketAPI; 023import com.fs.starfarer.api.campaign.econ.SubmarketAPI; 024import com.fs.starfarer.api.combat.WeaponAPI.AIHints; 025import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize; 026import com.fs.starfarer.api.fleet.FleetMemberAPI; 027import com.fs.starfarer.api.fleet.FleetMemberType; 028import com.fs.starfarer.api.impl.campaign.DModManager; 029import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflater; 030import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3; 031import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3; 032import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 033import com.fs.starfarer.api.impl.campaign.ids.Items; 034import com.fs.starfarer.api.impl.campaign.ids.Tags; 035import com.fs.starfarer.api.impl.campaign.shared.SharedData; 036import com.fs.starfarer.api.loading.FighterWingSpecAPI; 037import com.fs.starfarer.api.loading.HullModSpecAPI; 038import com.fs.starfarer.api.loading.WeaponSpecAPI; 039import com.fs.starfarer.api.plugins.impl.CoreAutofitPlugin; 040import com.fs.starfarer.api.ui.LabelAPI; 041import com.fs.starfarer.api.ui.TooltipMakerAPI; 042import com.fs.starfarer.api.util.Highlights; 043import com.fs.starfarer.api.util.Misc; 044import com.fs.starfarer.api.util.WeightedRandomPicker; 045 046public class BaseSubmarketPlugin implements SubmarketPlugin { 047 048 //public static float TRADE_IMPACT_DAYS = 30f; 049 public static float TRADE_IMPACT_DAYS = 120f; 050 051 public static class ShipSalesData { 052 private String variantId; 053 private float numShips; 054 private float totalValue; 055 public String getVariantId() { 056 return variantId; 057 } 058 public void setVariantId(String variantId) { 059 this.variantId = variantId; 060 } 061 public float getNumShips() { 062 return numShips; 063 } 064 public void setNumShips(float numShips) { 065 this.numShips = numShips; 066 } 067 public float getTotalValue() { 068 return totalValue; 069 } 070 public void setTotalValue(float totalValue) { 071 this.totalValue = totalValue; 072 } 073 } 074 075 protected MarketAPI market; 076 protected SubmarketAPI submarket; 077 078 protected CargoAPI cargo; 079 protected float minSWUpdateInterval = 30; // campaign days 080 protected float sinceSWUpdate = 30f + 1; 081 protected float sinceLastCargoUpdate = 30f + 1; 082 083 protected Random itemGenRandom = new Random(); 084 085 086 public void init(SubmarketAPI submarket) { 087 this.submarket = submarket; 088 this.market = submarket.getMarket(); 089 } 090 091 protected Object readResolve() { 092 return this; 093 } 094 095 public String getName() { 096 return null; 097 } 098 099 public CargoAPI getCargo() { 100 if (cargo == null) { 101 this.cargo = Global.getFactory().createCargo(true); 102 this.cargo.initMothballedShips(submarket.getFaction().getId()); 103 } 104 return cargo; 105 } 106 107 public CargoAPI getCargoNullOk() { 108 return cargo; 109 } 110 111 public void setCargo(CargoAPI cargo) { 112 this.cargo = cargo; 113 } 114 115 public void updateCargoPrePlayerInteraction() { 116 117 } 118 119 public void advance(float amount) { 120 float days = Global.getSector().getClock().convertToDays(amount); 121 sinceLastCargoUpdate += days; 122 sinceSWUpdate += days; 123 } 124 125 public boolean okToUpdateShipsAndWeapons() { 126 //if (true) return true; 127 return sinceSWUpdate >= minSWUpdateInterval; 128 } 129 130 public void addAllCargo(CargoAPI otherCargo) { 131 for (CargoStackAPI stack : otherCargo.getStacksCopy()) { 132 if (stack.isNull()) continue; 133 getCargo().addItems(stack.getType(), stack.getData(), stack.getSize()); 134 } 135 } 136 137 138 public float getTariff() { 139 return market.getTariff().getModifiedValue(); 140 } 141 142 public String getBuyVerb() { 143 return "Buy"; 144 } 145 146 public String getSellVerb() { 147 return "Sell"; 148 } 149 150 public boolean isFreeTransfer() { 151 return false; 152 } 153 154 public boolean isEnabled(CoreUIAPI ui) { 155 return ui.getTradeMode() == CoreUITradeMode.OPEN || isBlackMarket(); 156 //return true; 157 } 158 public OnClickAction getOnClickAction(CoreUIAPI ui) { 159 return OnClickAction.OPEN_SUBMARKET; 160 } 161 public String getDialogText(CoreUIAPI ui) { 162 return null; 163 } 164 public Highlights getDialogTextHighlights(CoreUIAPI ui) { 165 return null; 166 } 167 public DialogOption [] getDialogOptions(CoreUIAPI ui) { 168 return null; 169 } 170 public String getTooltipAppendix(CoreUIAPI ui) { 171 return null; 172 } 173 public Highlights getTooltipAppendixHighlights(CoreUIAPI ui) { 174 return null; 175 } 176 177 178 public PlayerEconomyImpactMode getPlayerEconomyImpactMode() { 179 return PlayerEconomyImpactMode.NONE; 180 } 181 182 public float getPlayerTradeImpactMult() { 183 return 1f; 184 } 185 186 public void reportPlayerMarketTransaction(PlayerMarketTransaction transaction) { 187 if (!isParticipatesInEconomy()) return; 188 189 PlayerEconomyImpactMode mode = getPlayerEconomyImpactMode(); 190 //if (mode == PlayerEconomyImpactMode.NONE) return; 191 192 //mode = PlayerEconomyImpactMode.NONE; 193 194 SharedData.getData().getPlayerActivityTracker().getPlayerTradeData(submarket).addTransaction(transaction); 195 196 197 for (CargoStackAPI stack : transaction.getSold().getStacksCopy()) { 198 if (stack.isCommodityStack()) { 199 float qty = stack.getSize() * getPlayerTradeImpactMult(); 200 if (qty <= 0) continue; 201 CommodityOnMarketAPI com = market.getCommodityData(stack.getCommodityId()); 202 203 if (mode == PlayerEconomyImpactMode.BOTH) { 204 com.addTradeMod("sell_" + Misc.genUID(), qty, TRADE_IMPACT_DAYS); 205 } else if (mode == PlayerEconomyImpactMode.PLAYER_SELL_ONLY) { 206 com.addTradeModPlus("sell_" + Misc.genUID(), qty, TRADE_IMPACT_DAYS); 207 } else if (mode == PlayerEconomyImpactMode.PLAYER_BUY_ONLY || mode == PlayerEconomyImpactMode.NONE) { 208 com.addTradeModMinus("sell_" + Misc.genUID(), qty, TRADE_IMPACT_DAYS); 209 } 210 } 211 } 212 for (CargoStackAPI stack : transaction.getBought().getStacksCopy()) { 213 if (stack.isCommodityStack()) { 214 float qty = stack.getSize() * getPlayerTradeImpactMult(); 215 if (qty <= 0) continue; 216 CommodityOnMarketAPI com = market.getCommodityData(stack.getCommodityId()); 217 218 if (mode == PlayerEconomyImpactMode.BOTH) { 219 com.addTradeMod("buy_" + Misc.genUID(), -qty, TRADE_IMPACT_DAYS); 220 } else if (mode == PlayerEconomyImpactMode.PLAYER_SELL_ONLY || mode == PlayerEconomyImpactMode.NONE) { 221 com.addTradeModPlus("buy_" + Misc.genUID(), -qty, TRADE_IMPACT_DAYS); 222 } else if (mode == PlayerEconomyImpactMode.PLAYER_BUY_ONLY) { 223 com.addTradeModMinus("buy_" + Misc.genUID(), -qty, TRADE_IMPACT_DAYS); 224 } 225 } 226 } 227 } 228 229 public boolean isMilitaryMarket() { 230 return false; 231 } 232 233 public boolean isBlackMarket() { 234 //return false; 235 return market.getFaction().isHostileTo(submarket.getFaction()); 236 } 237 238 public boolean isOpenMarket() { 239 return false; 240 } 241 242 public boolean isParticipatesInEconomy() { 243 return true; 244 } 245 246 247 public boolean isIllegalOnSubmarket(String commodityId, TransferAction action) { 248// if (market.hasCondition(Conditions.FREE_PORT)) return false; 249// //return market.isIllegal(commodityId); 250// return submarket.getFaction().isIllegal(commodityId); 251 return market.isIllegal(commodityId); 252 } 253 254 public boolean isIllegalOnSubmarket(CargoStackAPI stack, TransferAction action) { 255 if (!stack.isCommodityStack()) return false; 256 return isIllegalOnSubmarket((String) stack.getData(), action); 257 } 258 259 public String getIllegalTransferText(CargoStackAPI stack, TransferAction action) { 260 return "Illegal to trade on the " + submarket.getNameOneLine().toLowerCase() + " here"; 261 } 262 263 public boolean isIllegalOnSubmarket(FleetMemberAPI member, TransferAction action) { 264 if (action == TransferAction.PLAYER_SELL && !isBlackMarket() && Misc.isAutomated(member)) { 265 return true; 266 } 267 return false; 268 } 269 270 public String getIllegalTransferText(FleetMemberAPI member, TransferAction action) { 271 //return "Illegal to trade on the " + submarket.getNameOneLine().toLowerCase() + " here"; 272 if (action == TransferAction.PLAYER_BUY) { 273 return "Illegal to buy"; // this shouldn't happen 274 } else { 275 if (isFreeTransfer()) { 276 return "Illegal to store"; 277 } 278 return "Illegal to sell"; 279 } 280 } 281 282 283// protected void addWeapons(int min, int max, int maxTier, WeightedRandomPicker<String> factionPicker) { 284// int num = min + itemGenRandom.nextInt(max - min + 1); 285// for (int i = 0; i < num; i++) { 286// String factionId = factionPicker.pick(); 287// addWeapons(1, 1, maxTier, factionId); 288// } 289// } 290 291 protected void addFighters(int min, int max, int maxTier, WeightedRandomPicker<String> factionPicker) { 292 int num = min + itemGenRandom.nextInt(max - min + 1); 293 for (int i = 0; i < num; i++) { 294 String factionId = factionPicker.pick(); 295 addFighters(1, 1, maxTier, factionId); 296 } 297 } 298 299 protected void addWeapons(int min, int max, int maxTier, String factionId) { 300 addWeapons(min, max, maxTier, factionId, true); 301 } 302 protected void addWeapons(int min, int max, int maxTier, String factionId, boolean withCategories) { 303 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(itemGenRandom); 304 picker.add(factionId); 305 addWeapons(min, max, maxTier, picker, withCategories); 306 } 307 308 protected void addWeapons(int min, int max, int maxTier, WeightedRandomPicker<String> factionPicker) { 309 addWeapons(min, max, maxTier, factionPicker, true); 310 } 311 protected void addWeapons(int min, int max, int maxTier, WeightedRandomPicker<String> factionPicker, boolean withCategories) { 312 WeightedRandomPicker<WeaponSpecAPI> picker = new WeightedRandomPicker<WeaponSpecAPI>(itemGenRandom); 313 314 WeightedRandomPicker<WeaponSpecAPI> pd = new WeightedRandomPicker<WeaponSpecAPI>(itemGenRandom); 315 WeightedRandomPicker<WeaponSpecAPI> kinetic = new WeightedRandomPicker<WeaponSpecAPI>(itemGenRandom); 316 WeightedRandomPicker<WeaponSpecAPI> nonKinetic = new WeightedRandomPicker<WeaponSpecAPI>(itemGenRandom); 317 WeightedRandomPicker<WeaponSpecAPI> missile = new WeightedRandomPicker<WeaponSpecAPI>(itemGenRandom); 318 WeightedRandomPicker<WeaponSpecAPI> strike = new WeightedRandomPicker<WeaponSpecAPI>(itemGenRandom); 319 320 for (int i = 0; i < factionPicker.getItems().size(); i++) { 321 String factionId = factionPicker.getItems().get(i); 322 float w = factionPicker.getWeight(i); 323 if (factionId == null) factionId = market.getFactionId(); 324 325 float quality = Misc.getShipQuality(market, factionId); 326 FactionAPI faction = Global.getSector().getFaction(factionId); 327 328 for (String id : faction.getKnownWeapons()) { 329 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(id); 330 if (spec.getTier() > maxTier) continue; 331 if (spec.getAIHints().contains(AIHints.SYSTEM)) continue; 332 if (spec.hasTag(Tags.WEAPON_NO_SELL)) continue; 333 334 float p = DefaultFleetInflater.getTierProbability(spec.getTier(), quality); 335 p = 1f; // 336 p *= w; 337 if (faction.getWeaponSellFrequency().containsKey(id)) { 338 p *= faction.getWeaponSellFrequency().get(id); 339 } 340 picker.add(spec, p); 341 342 String cat = spec.getAutofitCategory(); 343 if (cat != null && spec.getSize() != WeaponSize.LARGE) { 344 if (CoreAutofitPlugin.PD.equals(cat)) { 345 pd.add(spec, p); 346 } else if (CoreAutofitPlugin.STRIKE.equals(cat)) { 347 strike.add(spec, p); 348 } else if (CoreAutofitPlugin.KINETIC.equals(cat)) { 349 kinetic.add(spec, p); 350 } else if (CoreAutofitPlugin.MISSILE.equals(cat) || CoreAutofitPlugin.ROCKET.equals(cat)) { 351 missile.add(spec, p); 352 } else if (CoreAutofitPlugin.HE.equals(cat) || CoreAutofitPlugin.ENERGY.equals(cat)) { 353 nonKinetic.add(spec, p); 354 } 355 } 356 } 357 } 358 359 int num = min + itemGenRandom.nextInt(max - min + 1); 360 361 if (withCategories) { 362 if (num > 0 && !pd.isEmpty()) { 363 pickAndAddWeapons(pd); 364 num--; 365 } 366 if (num > 0 && !kinetic.isEmpty()) { 367 pickAndAddWeapons(kinetic); 368 num--; 369 } 370 if (num > 0 && !missile.isEmpty()) { 371 pickAndAddWeapons(missile); 372 num--; 373 } 374 if (num > 0 && !nonKinetic.isEmpty()) { 375 pickAndAddWeapons(nonKinetic); 376 num--; 377 } 378 if (num > 0 && !strike.isEmpty()) { 379 pickAndAddWeapons(strike); 380 num--; 381 } 382 } 383 384 385 for (int i = 0; i < num && !picker.isEmpty(); i++) { 386 pickAndAddWeapons(picker); 387 } 388 } 389 390 protected void pickAndAddWeapons(WeightedRandomPicker<WeaponSpecAPI> picker) { 391 WeaponSpecAPI spec = picker.pick(); 392 if (spec == null) return; 393 394// int count = 2; 395// switch (spec.getSize()) { 396// case LARGE: count = 2; break; 397// case MEDIUM: count = 4; break; 398// case SMALL: count = 8; break; 399// } 400// count = count + itemGenRandom.nextInt(count + 1) - count/2; 401 402 int count = 1; 403 switch (spec.getSize()) { 404 case LARGE: count = 1; break; 405 case MEDIUM: count = 2; break; 406 case SMALL: count = 3; break; 407 } 408 count = count + itemGenRandom.nextInt(count + 2) - itemGenRandom.nextInt(count + 1); 409 if (count < 1) count = 1; 410 cargo.addWeapons(spec.getWeaponId(), count); 411 } 412 413 414 protected void addFighters(int min, int max, int maxTier, String factionId) { 415 if (factionId == null) factionId = market.getFactionId(); 416 417 int num = min + itemGenRandom.nextInt(max - min + 1); 418 float quality = Misc.getShipQuality(market, factionId); 419 420 FactionAPI faction = Global.getSector().getFaction(factionId); 421 422 WeightedRandomPicker<FighterWingSpecAPI> picker = new WeightedRandomPicker<FighterWingSpecAPI>(itemGenRandom); 423 for (String id : faction.getKnownFighters()) { 424 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(id); 425 if (spec == null) { 426 throw new RuntimeException("Fighter wing spec with id [" + id + "] not found"); 427 } 428 if (spec.getTier() > maxTier) continue; 429 if (spec.hasTag(Tags.WING_NO_SELL)) continue; 430 431 float p = DefaultFleetInflater.getTierProbability(spec.getTier(), quality); 432 p = 1f; 433 if (faction.getFighterSellFrequency().containsKey(id)) { 434 p *= faction.getFighterSellFrequency().get(id); 435 } 436 picker.add(spec, p); 437 } 438 439 for (int i = 0; i < num && !picker.isEmpty(); i++) { 440 FighterWingSpecAPI spec = picker.pick(); 441 442 int count = 2; 443 switch (spec.getRole()) { 444 case ASSAULT: count = 2; break; 445 case BOMBER: count = 2; break; 446 case INTERCEPTOR: count = 4; break; 447 case FIGHTER: count = 3; break; 448 case SUPPORT: count = 2; break; 449 } 450 451 count = count + itemGenRandom.nextInt(count + 1) - count/2; 452 453 cargo.addItems(CargoItemType.FIGHTER_CHIP, spec.getId(), count); 454 } 455 } 456 protected void pruneWeapons(float keepFraction) { 457 CargoAPI cargo = getCargo(); 458 for (CargoStackAPI stack : cargo.getStacksCopy()) { 459 if (stack.isWeaponStack() || stack.isFighterWingStack()) { 460 float qty = stack.getSize(); 461 if (qty <= 1) { 462 if (itemGenRandom.nextFloat() > keepFraction) { 463 cargo.removeItems(stack.getType(), stack.getData(), 1); 464 } 465 } else { 466 cargo.removeItems(stack.getType(), stack.getData(), Math.round(qty * (1f - keepFraction))); 467 } 468 } 469 } 470 } 471 472 public void addShips(String factionId, 473 float combat, 474 float freighter, 475 float tanker, 476 float transport, 477 float liner, 478 float utility, 479 Float qualityOverride, 480 float qualityMod, 481 ShipPickMode modeOverride, 482 FactionDoctrineAPI doctrineOverride) { 483 addShips(factionId, combat, freighter, tanker, transport, liner, utility, qualityOverride, qualityMod, modeOverride, doctrineOverride, 1000); 484 485 } 486 public void addShips(String factionId, 487 float combat, 488 float freighter, 489 float tanker, 490 float transport, 491 float liner, 492 float utility, 493 Float qualityOverride, 494 float qualityMod, 495 ShipPickMode modeOverride, 496 FactionDoctrineAPI doctrineOverride, 497 int maxShipSize) { 498 FleetParamsV3 params = new FleetParamsV3( 499 market, 500 Global.getSector().getPlayerFleet().getLocationInHyperspace(), 501 factionId, 502 null, // qualityOverride 503 FleetTypes.PATROL_LARGE, 504 combat, // combatPts 505 freighter, // freighterPts 506 tanker, // tankerPts 507 transport, // transportPts 508 liner, // linerPts 509 utility, // utilityPts 510 0f // qualityMod 511 ); 512 params.maxShipSize = maxShipSize; 513 params.random = new Random(itemGenRandom.nextLong()); 514 params.qualityOverride = Misc.getShipQuality(market, factionId) + qualityMod; 515 if (qualityOverride != null) { 516 params.qualityOverride = qualityOverride + qualityMod; 517 } 518 //params.qualityMod = qualityMod; 519 520 params.withOfficers = false; 521 522 params.forceAllowPhaseShipsEtc = true; 523 params.treatCombatFreighterSettingAsFraction = true; 524 525 params.modeOverride = Misc.getShipPickMode(market, factionId); 526 if (modeOverride != null) { 527 params.modeOverride = modeOverride; 528 } 529 530 params.doctrineOverride = doctrineOverride; 531 532 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params); 533 if (fleet != null) { 534 //float p = 0.5f; 535 //p = 1f; 536 537 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<>(itemGenRandom); 538 FactionAPI faction = Global.getSector().getFaction(factionId); 539 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 540 float f = 1f; 541 if (faction != null) { 542 Float mult = faction.getFactionSpec().getShipSellFrequency().get(member.getHullId()); 543 if (mult != null) { 544 f *= mult; 545 } 546 } 547 if (itemGenRandom.nextFloat() > f * 0.5f) continue; 548 if (member.getHullSpec().hasTag(Tags.NO_SELL)) continue; 549 if (!isMilitaryMarket() && member.getHullSpec().hasTag(Tags.MILITARY_MARKET_ONLY)) continue; 550 551 picker.add(member, f); 552 } 553 554 List<FleetMemberAPI> members = new ArrayList<>(); 555 while (!picker.isEmpty()) { 556 members.add(picker.pickAndRemove()); 557 } 558 559 for (FleetMemberAPI member : members) { 560 //if (itemGenRandom.nextFloat() > p) continue; 561// if (member.getHullSpec().hasTag(Tags.NO_SELL)) continue; 562// if (!isMilitaryMarket() && member.getHullSpec().hasTag(Tags.MILITARY_MARKET_ONLY)) continue; 563 String emptyVariantId = member.getHullId() + "_Hull"; 564 addShip(emptyVariantId, true, params.qualityOverride); 565 } 566 } 567 } 568 569 protected FleetMemberAPI addShip(String variantOrWingId, boolean withDmods, float quality) { 570 FleetMemberAPI member = null; 571 if (variantOrWingId.endsWith("_wing")) { 572 member = Global.getFactory().createFleetMember(FleetMemberType.FIGHTER_WING, variantOrWingId); 573 } else { 574 member = Global.getFactory().createFleetMember(FleetMemberType.SHIP, variantOrWingId); 575 } 576 577 if (withDmods) { 578 float averageDmods = DefaultFleetInflater.getAverageDmodsForQuality(quality); 579 int addDmods = DefaultFleetInflater.getNumDModsToAdd(member.getVariant(), averageDmods, itemGenRandom); 580 if (addDmods > 0) { 581 DModManager.setDHull(member.getVariant()); 582 DModManager.addDMods(member, true, addDmods, itemGenRandom); 583 } 584 } 585 586 member.getRepairTracker().setMothballed(true); 587 member.getRepairTracker().setCR(0.5f); 588// assignShipName(member, submarket.getFaction().getId()); 589 getCargo().getMothballedShips().addFleetMember(member); 590 return member; 591 } 592 593// TODO not 100% sure about performance implications, maybe look at this later 594// public void assignShipName(FleetMemberAPI member, String factionId) { 595// CampaignFleetAPI fleet = Global.getFactory().createEmptyFleet(factionId, null, true); 596// fleet.getFleetData().setShipNameRandom(itemGenRandom); 597// fleet.getFleetData().addFleetMember(member); 598// fleet.getFleetData().removeFleetMember(member); 599// } 600 601 protected void pruneShips(float mult) { 602 CargoAPI cargo = getCargo(); 603 FleetDataAPI data = cargo.getMothballedShips(); 604 for (FleetMemberAPI member : data.getMembersListCopy()) { 605 if (itemGenRandom.nextFloat() > mult) { 606 data.removeFleetMember(member); 607 } 608 } 609 } 610 611 protected void addHullMods(int maxTier, int num) { 612 addHullMods(maxTier, num, null); 613 } 614 protected void addHullMods(int maxTier, int num, String factionId) { 615 //float p = Global.getSettings().getFloat("sellHullmodProb"); 616 617 CargoAPI cargo = getCargo(); 618 for (CargoStackAPI stack : cargo.getStacksCopy()) { 619 if (stack.isModSpecStack()) { 620 cargo.removeStack(stack); 621 } 622 } 623 624 FactionAPI faction = null; 625 if (factionId != null) { 626 faction = Global.getSector().getFaction(factionId); 627 } 628 629 WeightedRandomPicker<HullModSpecAPI> picker = new WeightedRandomPicker<HullModSpecAPI>(itemGenRandom); 630 for (String id : submarket.getFaction().getKnownHullMods()) { 631 //if (Global.getSector().getCharacterData().knowsHullMod(id)) continue; 632 HullModSpecAPI spec = Global.getSettings().getHullModSpec(id); 633 if (spec.isHidden()) continue; 634 if (spec.isAlwaysUnlocked()) continue; 635 if (spec.getTier() > maxTier) continue; 636 float p = spec.getRarity(); 637 if (faction != null && faction.getHullmodSellFrequency().containsKey(id) && 638 !Global.getSector().getPlayerFaction().knowsHullMod(id)) { 639 p *= faction.getHullmodSellFrequency().get(id); 640 } 641// if (Global.getSector().getPlayerFaction().knowsHullMod(id)) { 642// p *= 0.25f; 643// } 644 645 picker.add(spec, p); 646 } 647 //picker.getItems().contains("missile_autoloader"); 648 for (int i = 0; i < num; i++) { 649 HullModSpecAPI pick = picker.pickAndRemove(); 650 if (pick == null) continue; 651 652 String id = pick.getId(); 653 if (cargoAlreadyHasMod(id)) continue; 654 655 if (Global.getSector().getPlayerFaction().knowsHullMod(id)) continue; 656 657 //cargo.addItems(CargoItemType.MOD_SPEC, id, 1); 658 659 cargo.addItems(CargoItemType.SPECIAL, new SpecialItemData(Items.TAG_MODSPEC, id), 1); 660 } 661 662 } 663 664 protected boolean removeModFromCargo(String id) { 665 CargoAPI cargo = getCargo(); 666 for (CargoStackAPI stack : cargo.getStacksCopy()) { 667 if (stack.isModSpecStack() && stack.getData().equals(id)) { 668 cargo.removeStack(stack); 669 } 670 } 671 return false; 672 } 673 674 protected boolean cargoAlreadyHasMod(String id) { 675 CargoAPI cargo = getCargo(); 676 for (CargoStackAPI stack : cargo.getStacksCopy()) { 677 //if (stack.isModSpecStack() && stack.getData().equals(id)) return true; 678 if (stack.isSpecialStack() && stack.getSpecialDataIfSpecial().getId().equals(Items.TAG_MODSPEC) && 679 stack.getSpecialDataIfSpecial().getData().equals(id)) return true; 680 } 681 return false; 682 } 683 684 685 public Highlights getIllegalTransferTextHighlights(CargoStackAPI stack, TransferAction action) { 686 return null; 687 } 688 689 public Highlights getIllegalTransferTextHighlights(FleetMemberAPI member, TransferAction action) { 690 return null; 691 } 692 693 public float getMinSWUpdateInterval() { 694 return minSWUpdateInterval; 695 } 696 697 public void setMinSWUpdateInterval(float minCargoUpdateInterval) { 698 this.minSWUpdateInterval = minCargoUpdateInterval; 699 } 700 701 public float getSinceLastCargoUpdate() { 702 return sinceLastCargoUpdate; 703 } 704 705 public void setSinceLastCargoUpdate(float sinceLastCargoUpdate) { 706 this.sinceLastCargoUpdate = sinceLastCargoUpdate; 707 } 708 709 public float getSinceSWUpdate() { 710 return sinceSWUpdate; 711 } 712 713 public void setSinceSWUpdate(float sinceSWUpdate) { 714 this.sinceSWUpdate = sinceSWUpdate; 715 } 716 717 public boolean hasCustomTooltip() { 718 return true; 719 } 720 721 public void createTooltip(CoreUIAPI ui, TooltipMakerAPI tooltip, boolean expanded) { 722 float opad = 10f; 723 724// tooltip.setTitleSmallOrbitron(); 725// tooltip.setParaSmallInsignia(); 726 727 tooltip.addTitle(submarket.getNameOneLine()); 728 String desc = submarket.getSpec().getDesc(); 729 730 desc = Global.getSector().getRules().performTokenReplacement(null, desc, market.getPrimaryEntity(), null); 731 732 String appendix = submarket.getPlugin().getTooltipAppendix(ui); 733 if (appendix != null) desc = desc + "\n\n" + appendix; 734 735 if (desc != null && !desc.isEmpty()) { 736 LabelAPI body = tooltip.addPara(desc, opad); 737 738 if (getTooltipAppendixHighlights(ui) != null) { 739 Highlights h = submarket.getPlugin().getTooltipAppendixHighlights(ui); 740 if (h != null) { 741 body.setHighlightColors(h.getColors()); 742 body.setHighlight(h.getText()); 743 } 744 } 745 } 746 747 createTooltipAfterDescription(tooltip, expanded); 748 } 749 750 751 protected void createTooltipAfterDescription(TooltipMakerAPI tooltip, boolean expanded) { 752 753 } 754 755 public boolean isTooltipExpandable() { 756 return false; 757 } 758 759 public float getTooltipWidth() { 760 return 400f; 761 } 762 763 public boolean isHidden() { 764 return false; 765 } 766 767 public boolean showInFleetScreen() { 768 return true; 769 } 770 771 public boolean showInCargoScreen() { 772 return true; 773 } 774 775 public MarketAPI getMarket() { 776 return market; 777 } 778 779 public SubmarketAPI getSubmarket() { 780 return submarket; 781 } 782 783 784 public int getStockpileLimit(CommodityOnMarketAPI com) { 785 return 0; 786 } 787 788 public float getStockpilingAddRateMult(CommodityOnMarketAPI com) { 789 return 1f; 790 } 791 792 public boolean shouldHaveCommodity(CommodityOnMarketAPI com) { 793 return true; 794 } 795 796 public void addAndRemoveStockpiledResources(float amount, 797 boolean withShortageCountering, 798 boolean withDecreaseToLimit, 799 boolean withCargoUpdate) { 800 for (CommodityOnMarketAPI com : market.getCommoditiesCopy()) { 801 if (com.isNonEcon()) continue; 802 if (com.getCommodity().isMeta()) continue; 803 804 //if (com.getMaxSupply() <= 0 && com.getMaxDemand() <= 0) continue; 805 806// if (market.getId().equals("mazalot") && com.getId().equals("ore")) { 807// System.out.println("wefwefew"); 808// } 809// if (com.isIllegal() && com.getMarket().isPlayerOwned()) { 810// System.out.println("wefwefew"); 811// } 812 addAndRemoveStockpiledResources(com, amount, withShortageCountering, withDecreaseToLimit, withCargoUpdate); 813 } 814 } 815 816 protected boolean doShortageCountering(CommodityOnMarketAPI com, float amount, boolean withShortageCountering) { 817 return false; 818 } 819 820 public void addAndRemoveStockpiledResources(CommodityOnMarketAPI com, float amount, 821 boolean withShortageCountering, 822 boolean withDecreaseToLimit, 823 boolean withCargoUpdate) { 824 825// if (com.isIllegal() && com.getMarket().isPlayerOwned()) { 826// System.out.println("wefwefew"); 827// } 828 829 float days = Global.getSector().getClock().convertToDays(amount); 830 //if (days <= 0) return; 831 832 if (com.isNonEcon()) return; 833 if (com.getCommodity().isMeta()) return; 834 //if (com.getMaxSupply() <= 0 && com.getMaxDemand() <= 0) return; 835 836 CargoAPI cargo = getCargo(); 837 //String modId = "localRes"; 838// String modId = submarket.getSpecId(); 839// 840// com.getAvailableStat().unmodifyFlat(modId); 841// 842// int demand = com.getMaxDemand(); 843// int available = com.getAvailable(); 844 845 846 if (withShortageCountering) { 847 withShortageCountering = market.isUseStockpilesForShortages(); 848 } 849 850 //if (demand > available && withShortageCountering) { 851 if (doShortageCountering(com, amount, withShortageCountering)) { 852 return; 853 } 854 855 if (!shouldHaveCommodity(com)) { 856 if (withDecreaseToLimit) { 857 //float days = Global.getSector().getClock().convertToDays(amount); 858 float limit = getStockpileLimit(com); 859 float curr = cargo.getCommodityQuantity(com.getId()); 860 if (curr > limit && withDecreaseToLimit) { 861 float removeRate = (curr - limit) * 2f / 30f; 862 float removeAmount = removeRate * days; 863 864 if (curr - removeAmount < limit) { 865 removeAmount = curr - limit; 866 } 867 if (removeAmount > 0 && curr <= 1) { 868 removeAmount = 1f; 869 } 870 871 if (removeAmount > 0) { 872 cargo.removeCommodity(com.getId(), removeAmount); 873 } 874 } 875 } 876 return; 877 } 878 879 // add stockpile, up to limit 880 float limit = getStockpileLimit(com); 881 float curr = cargo.getCommodityQuantity(com.getId()); 882 883 if (curr < limit && withCargoUpdate) { 884 if (limit <= 0) return; 885 886// if (market.isPlayerOwned() && market.getName().startsWith("Dark")) { 887// System.out.println("wefwef" + market.getName()); 888// } 889 890 float addRate = limit / 30f * getStockpilingAddRateMult(com); 891 892 // make it so the player constantly re-checking doesn't keep adding cargo more quickly than it should, 893 // due to having to add at least 1 unit if there's nothing 894 if (sinceLastCargoUpdate * addRate + curr < 1) { 895 return; 896 } 897 898 float addAmount = addRate * days; 899 900 901 if (curr + addAmount > limit) { 902 addAmount = limit - curr; 903 } 904 905 if (addAmount > 0) { 906 float q = cargo.getCommodityQuantity(com.getId()) + addAmount; 907 if (q < 1) { 908 addAmount = 1f; // add at least 1 unit or it won't do anything 909 } 910 911 cargo.addCommodity(com.getId(), addAmount); 912 913// if (market.isPlayerOwned()) { 914// MonthlyReport report = SharedData.getData().getCurrentReport(); 915// FDNode node = report.getStockpilingNode(market); 916// 917// CargoAPI tooltipCargo = (CargoAPI) node.custom2; 918// float addToTooltipCargo = addAmount; 919// q = tooltipCargo.getCommodityQuantity(com.getId()) + addToTooltipCargo; 920// if (q < 1) { 921// addToTooltipCargo = 1f; // add at least 1 unit or it won't do anything 922// } 923// tooltipCargo.addCommodity(com.getId(), addToTooltipCargo); 924// 925// float unitPrice = (int) getStockpilingUnitPrice(com); 926// //node.upkeep += unitPrice * addAmount; 927// 928// FDNode comNode = report.getNode(node, com.getId()); 929// 930// CommoditySpecAPI spec = com.getCommodity(); 931// comNode.icon = spec.getIconName(); 932// comNode.upkeep += unitPrice * addAmount; 933// comNode.custom = com; 934// 935// if (comNode.custom2 == null) { 936// comNode.custom2 = 0f; 937// } 938// comNode.custom2 = (Float)comNode.custom2 + addAmount; 939// 940// int qty = (int) Math.max(1, (Float) comNode.custom2); 941// comNode.name = spec.getName() + " " + Strings.X + Misc.getWithDGS(qty); 942// comNode.tooltipCreator = report.getMonthlyReportTooltip(); 943// 944// // use price market buys at, i.e. without a markup 945// } 946 } 947 948 return; 949 } 950 951 if (curr > limit && withDecreaseToLimit) { 952 float removeRate = (curr - limit) * 2f / 30f; 953 float removeAmount = removeRate * days; 954 955 956 if (curr - removeAmount < limit) { 957 removeAmount = curr - limit; 958 } 959 if (removeAmount > 0 && curr <= 1) { 960 removeAmount = 1f; 961 } 962 963 if (removeAmount > 0) { 964 cargo.removeCommodity(com.getId(), removeAmount); 965 } 966 return; 967 } 968 } 969 970 public String getTariffTextOverride() { 971 return null; 972 } 973 public String getTariffValueOverride() { 974 return null; 975 } 976 public String getTotalTextOverride() { 977 return null; 978 } 979 public String getTotalValueOverride() { 980 return null; 981 } 982} 983 984 985 986 987 988 989