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