001package com.fs.starfarer.api.impl.campaign.submarkets; 002 003import java.util.Random; 004 005import org.apache.log4j.Logger; 006 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.campaign.CargoAPI; 009import com.fs.starfarer.api.campaign.CargoStackAPI; 010import com.fs.starfarer.api.campaign.CoreUIAPI; 011import com.fs.starfarer.api.campaign.FactionAPI; 012import com.fs.starfarer.api.campaign.FactionDoctrineAPI; 013import com.fs.starfarer.api.campaign.PlayerMarketTransaction; 014import com.fs.starfarer.api.campaign.SpecialItemPlugin; 015import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 016import com.fs.starfarer.api.campaign.econ.SubmarketAPI; 017import com.fs.starfarer.api.campaign.impl.items.BlueprintProviderItem; 018import com.fs.starfarer.api.impl.campaign.CoreCampaignPluginImpl; 019import com.fs.starfarer.api.impl.campaign.DelayedBlueprintLearnScript; 020import com.fs.starfarer.api.impl.campaign.ids.Factions; 021import com.fs.starfarer.api.util.Highlights; 022import com.fs.starfarer.api.util.Misc; 023import com.fs.starfarer.api.util.WeightedRandomPicker; 024 025public class BlackMarketPlugin extends BaseSubmarketPlugin { 026 027 public static Logger log = Global.getLogger(BlackMarketPlugin.class); 028 029 public void init(SubmarketAPI submarket) { 030 super.init(submarket); 031 } 032 033 034 public void updateCargoPrePlayerInteraction() { 035 float seconds = Global.getSector().getClock().convertToSeconds(sinceLastCargoUpdate); 036 addAndRemoveStockpiledResources(seconds, false, true, true); 037 sinceLastCargoUpdate = 0f; 038 039 040 if (okToUpdateShipsAndWeapons()) { 041 sinceSWUpdate = 0f; 042 float stability = market.getStabilityValue(); 043 044 pruneWeapons(0f); 045 046 boolean military = Misc.isMilitary(market); 047// boolean hiddenBase = market.getMemoryWithoutUpdate().getBoolean(MemFlags.HIDDEN_BASE_MEM_FLAG); 048// 049// float extraShips = 0f; 050// int extraShipSize = 0; 051// if (military && hiddenBase && !market.hasSubmarket(Submarkets.GENERIC_MILITARY)) { 052// extraShips = 500f; 053// extraShipSize = 1; 054// } 055 056 WeightedRandomPicker<String> factionPicker = new WeightedRandomPicker<String>(); 057 factionPicker.add(market.getFactionId(), 15f - stability); 058 factionPicker.add(Factions.INDEPENDENT, 4f); 059 factionPicker.add(submarket.getFaction().getId(), 6f); 060 061 int weapons = 6 + Math.max(0, market.getSize() - 1) + (military ? 5 : 0); 062 int fighters = 2 + Math.max(0, (market.getSize() - 3) / 2) + (military ? 2 : 0); 063 weapons = 6 + Math.max(0, market.getSize() - 1); 064 fighters = 2 + Math.max(0, (market.getSize() - 3) / 2); 065 066 addWeapons(weapons, weapons + 2, 3, factionPicker); 067 addFighters(fighters, fighters + 2, 3, factionPicker); 068 069 if (military) { 070 weapons = market.getSize(); 071 fighters = Math.max(1, market.getSize() / 3); 072 addWeapons(weapons, weapons + 2, 3, market.getFactionId(), false); 073 addFighters(fighters, fighters + 2, 3, market.getFactionId()); 074 } 075 076 float sMult = 0.5f + Math.max(0, (1f - stability / 10f)) * 0.5f; 077 getCargo().getMothballedShips().clear(); 078 float pOther = 0.1f; 079 080 FactionDoctrineAPI doctrine = market.getFaction().getDoctrine().clone(); 081// FactionDoctrineAPI doctrine = submarket.getFaction().getDoctrine().clone(); 082// doctrine.setWarships(3); 083 if (doctrine.getWarships() > 0) { 084 doctrine.setWarships(Math.max(2, doctrine.getWarships())); 085 } 086 if (doctrine.getCarriers() > 0) { 087 doctrine.setCarriers(Math.max(2, doctrine.getCarriers())); 088 } 089 if (doctrine.getPhaseShips() > 0) { 090 doctrine.setPhaseShips(Math.max(2, doctrine.getPhaseShips())); 091 } 092// doctrine.setPhaseShips(2); 093 094 addShips(market.getFactionId(), 095 70f * sMult, // combat 096 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // freighter 097 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // tanker 098 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // transport 099 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // liner 100 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // utilityPts 101 null, 102 0f, // qualityMod 103 null, 104 doctrine); 105 FactionDoctrineAPI doctrineOverride = submarket.getFaction().getDoctrine().clone(); 106 doctrineOverride.setWarships(3); 107 doctrineOverride.setPhaseShips(2); 108 doctrineOverride.setCarriers(2); 109 doctrineOverride.setCombatFreighterProbability(1f); 110 doctrineOverride.setShipSize(5); 111 addShips(submarket.getFaction().getId(), 112 70f, // combat 113 10f, // freighter 114 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // tanker 115 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // transport 116 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // liner 117 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // utilityPts 118 //0.8f, 119 Math.min(1f, Misc.getShipQuality(market, market.getFactionId()) + 0.5f), 120 0f, // qualityMod 121 null, 122 doctrineOverride, 123 3 // no capital ships, max size cruiser 124 ); 125 addShips(Factions.INDEPENDENT, 126 15f + 15f * sMult, // combat 127 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // freighter 128 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // tanker 129 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // transport 130 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // liner 131 itemGenRandom.nextFloat() > pOther ? 0f : 10f, // utilityPts 132 //0.8f, 133 Math.min(1f, Misc.getShipQuality(market, market.getFactionId()) + 0.5f), 134 0f, // qualityMod 135 null, 136 null, 137 3 // no capital ships, max size cruiser 138 ); 139 140 addHullMods(4, 1 + itemGenRandom.nextInt(3)); 141 } 142 143 getCargo().sort(); 144 } 145 146 protected Object writeReplace() { 147 if (okToUpdateShipsAndWeapons()) { 148 pruneWeapons(0f); 149 getCargo().getMothballedShips().clear(); 150 } 151 return this; 152 } 153 154 @Override 155 public int getStockpileLimit(CommodityOnMarketAPI com) { 156// int demand = com.getMaxDemand(); 157// int available = com.getAvailable(); 158// 159// //float limit = BaseIndustry.getSizeMult(available) - BaseIndustry.getSizeMult(Math.max(0, demand - 2)); 160// float limit = BaseIndustry.getSizeMult(available); 161// limit *= com.getCommodity().getEconUnit(); 162 163 //limit *= com.getMarket().getStockpileMult().getModifiedValue(); 164 165 float limit = OpenMarketPlugin.getBaseStockpileLimit(com); 166 167 Random random = new Random(market.getId().hashCode() + submarket.getSpecId().hashCode() + Global.getSector().getClock().getMonth() * 170000); 168 limit *= 0.9f + 0.2f * random.nextFloat(); 169 170 float sm = 1f - market.getStabilityValue() / 10f; 171 limit *= (0.25f + 0.75f * sm); 172 173 if (limit < 0) limit = 0; 174 175 return (int) limit; 176 } 177 178 @Override 179 public PlayerEconomyImpactMode getPlayerEconomyImpactMode() { 180 //return PlayerEconomyImpactMode.PLAYER_BUY_ONLY; 181 // if the player buying stuff can cause a shortage, it can result in profitable buy/sell cycles, so: don't do that 182 //return PlayerEconomyImpactMode.NONE; 183 return PlayerEconomyImpactMode.PLAYER_SELL_ONLY; 184 } 185 186 187 public float getDesiredCommodityQuantity(CommodityOnMarketAPI com) { 188 boolean illegal = market.isIllegal(com.getId()); 189 if (illegal) return com.getStockpile(); 190 191 float blackMarketLegalFraction = 1f - 0.09f * market.getStabilityValue(); 192 return com.getStockpile() * blackMarketLegalFraction; 193 } 194 195 196 @Override 197 public void reportPlayerMarketTransaction(PlayerMarketTransaction transaction) { 198 super.reportPlayerMarketTransaction(transaction); 199 200 FactionAPI faction = submarket.getFaction(); 201 delayedLearnBlueprintsFromTransaction(faction, getCargo(), transaction, 60f + 60 * (float) Math.random()); 202 } 203 204 public static void delayedLearnBlueprintsFromTransaction(FactionAPI faction, CargoAPI cargo, PlayerMarketTransaction transaction) { 205 delayedLearnBlueprintsFromTransaction(faction, cargo, transaction, 60f + 60 * (float) Math.random()); 206 } 207 public static void delayedLearnBlueprintsFromTransaction(FactionAPI faction, CargoAPI cargo, PlayerMarketTransaction transaction, float daysDelay) { 208 DelayedBlueprintLearnScript script = new DelayedBlueprintLearnScript(faction.getId(), daysDelay); 209 for (CargoStackAPI stack : transaction.getSold().getStacksCopy()) { 210 SpecialItemPlugin plugin = stack.getPlugin(); 211 if (plugin instanceof BlueprintProviderItem) { 212 BlueprintProviderItem bpi = (BlueprintProviderItem) plugin; 213 214 boolean learnedSomething = false; 215 if (bpi.getProvidedFighters() != null) { 216 for (String id : bpi.getProvidedFighters()) { 217 if (faction.knowsFighter(id)) continue; 218 script.getFighters().add(id); 219 learnedSomething = true; 220 } 221 } 222 if (bpi.getProvidedWeapons() != null) { 223 for (String id : bpi.getProvidedWeapons()) { 224 if (faction.knowsWeapon(id)) continue; 225 script.getWeapons().add(id); 226 learnedSomething = true; 227 } 228 } 229 if (bpi.getProvidedShips() != null) { 230 for (String id : bpi.getProvidedShips()) { 231 if (faction.knowsShip(id)) continue; 232 script.getShips().add(id); 233 learnedSomething = true; 234 } 235 } 236 if (bpi.getProvidedIndustries() != null) { 237 for (String id : bpi.getProvidedIndustries()) { 238 if (faction.knowsIndustry(id)) continue; 239 script.getIndustries().add(id); 240 learnedSomething = true; 241 } 242 } 243 244 if (learnedSomething) { 245 cargo.removeItems(stack.getType(), stack.getData(), 1); 246 } 247 } 248 } 249 250 if (!script.getFighters().isEmpty() || !script.getWeapons().isEmpty() || 251 !script.getShips().isEmpty() || !script.getIndustries().isEmpty()) { 252 Global.getSector().addScript(script); 253 cargo.sort(); 254 } 255 } 256 257 258 @Override 259 public boolean isIllegalOnSubmarket(CargoStackAPI stack, TransferAction action) { 260 return false; 261 } 262 263 @Override 264 public boolean isIllegalOnSubmarket(String commodityId, TransferAction action) { 265 return false; 266 } 267 268 public float getTariff() { 269 return 0f; 270 } 271 272 273 @Override 274 public boolean isBlackMarket() { 275 return true; 276 } 277 278 279 public String getTooltipAppendix(CoreUIAPI ui) { 280 if (isEnabled(ui)) { 281// CampaignEventManagerAPI manager = Global.getSector().getEventManager(); 282// EventProbabilityAPI ep = manager.getProbability(Events.INVESTIGATION_SMUGGLING, market); 283// float p = ep.getProbability(); 284 285 float p = CoreCampaignPluginImpl.computeSmugglingSuspicionLevel(market); 286 if (p < 0.05f) return "Suspicion level: none"; 287 288 if (p < 0.1f) { 289 return "Suspicion level: minimal"; 290 } 291 if (p < 0.2f) { 292 return "Suspicion level: medium"; 293 } 294 if (p < 0.3f) { 295 return "Suspicion level: high"; 296 } 297 if (p < 0.5f) { 298 return "Suspicion level: very high"; 299 } 300 return "Suspicion level: extreme"; 301 } 302 303 return null; 304 } 305 306 public Highlights getTooltipAppendixHighlights(CoreUIAPI ui) { 307 String appendix = getTooltipAppendix(ui); 308 if (appendix == null) return null; 309 310 Highlights h = new Highlights(); 311 h.setText(appendix); 312 h.setColors(Misc.getNegativeHighlightColor()); 313 return h; 314 } 315} 316 317 318