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