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