001package com.fs.starfarer.api.impl.campaign.procgen;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.LinkedHashSet;
006import java.util.List;
007import java.util.Map;
008import java.util.Random;
009import java.util.Set;
010
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.campaign.BaseCustomProductionPickerDelegateImpl;
013import com.fs.starfarer.api.campaign.CampaignFleetAPI;
014import com.fs.starfarer.api.campaign.CargoAPI;
015import com.fs.starfarer.api.campaign.FactionAPI;
016import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode;
017import com.fs.starfarer.api.campaign.FactionProductionAPI;
018import com.fs.starfarer.api.campaign.FactionProductionAPI.ItemInProductionAPI;
019import com.fs.starfarer.api.campaign.FactionProductionAPI.ProductionItemType;
020import com.fs.starfarer.api.campaign.FleetInflater;
021import com.fs.starfarer.api.campaign.InteractionDialogAPI;
022import com.fs.starfarer.api.campaign.InteractionDialogPlugin;
023import com.fs.starfarer.api.campaign.OptionPanelAPI;
024import com.fs.starfarer.api.campaign.TextPanelAPI;
025import com.fs.starfarer.api.campaign.VisualPanelAPI;
026import com.fs.starfarer.api.campaign.econ.MarketAPI;
027import com.fs.starfarer.api.campaign.rules.MemoryAPI;
028import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI;
029import com.fs.starfarer.api.characters.OfficerDataAPI;
030import com.fs.starfarer.api.characters.PersonAPI;
031import com.fs.starfarer.api.combat.EngagementResultAPI;
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.AIHints;
036import com.fs.starfarer.api.fleet.FleetMemberAPI;
037import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflaterParams;
038import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
039import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
040import com.fs.starfarer.api.impl.campaign.ids.Factions;
041import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
042import com.fs.starfarer.api.impl.campaign.ids.Tags;
043import com.fs.starfarer.api.loading.FighterWingSpecAPI;
044import com.fs.starfarer.api.loading.HullModSpecAPI;
045import com.fs.starfarer.api.loading.WeaponSpecAPI;
046import com.fs.starfarer.api.plugins.OfficerLevelupPlugin;
047import com.fs.starfarer.api.util.Misc;
048
049public class PlayerFleetGenPluginImpl implements InteractionDialogPlugin {
050
051        protected static enum FleetQuality {
052                MAX_DMODS,
053                NO_DMODS,
054                ONE_SMOD,
055                TWO_SMODS,
056                THREE_SMODS,
057        }
058        
059        protected static enum OptionId {
060                INIT,
061                SELECT_SHIPS,
062                ADD_UNSELECTED_OFFICERS,
063                OFFICER_LEVEL_5,
064                OFFICER_LEVEL_6,
065                OFFICER_LEVEL_7,
066                OFFICERS_GO_BACK,
067                
068                QUALITY_MAX_DMODS,
069                QUALITY_NO_DMODS,
070                QUALITY_1_SMODS,
071                QUALITY_2_SMODS,
072                QUALITY_2_SMODS_OMEGA,
073                QUALITY_3_SMODS,
074                QUALITY_3_SMODS_OMEGA,
075                
076//              SMODS_OFFICER_SHIPS_ONLY,
077//              SMODS_ALL_SHIPS,
078//              NORMAL_LEVEL_OFFICERS,
079//              HIGH_LEVEL_OFFICERS,
080                
081                ADD_OFFICERS,
082                ADD_OFFICERS_NO_SKILLS,
083                NO_OFFICERS,
084                
085                CREATE_REPLACE,
086                CREATE_ADD,
087                
088                LEAVE,
089        }
090        
091        
092        public static class FleetGenData {
093                public FactionProductionAPI prod;
094                public FleetQuality quality;
095                public boolean smodsOnAllShips = true;
096                public boolean allowOmega = false;
097                //public boolean eliteOfficers = false;
098                public boolean addOfficers = false;
099                public boolean keepOfficerPointsFree = false;
100                public boolean append = false;
101                
102                
103                public FleetGenData() {
104                        prod = Global.getSector().getPlayerFaction().getProduction().clone();
105                        prod.clear();
106                }
107        }
108        
109        
110        protected InteractionDialogAPI dialog;
111        protected TextPanelAPI textPanel;
112        protected OptionPanelAPI options;
113        protected VisualPanelAPI visual;
114        
115        protected FleetGenData data = new FleetGenData();
116        protected CampaignFleetAPI playerFleet;
117        
118        protected Set<String> ships = new LinkedHashSet<String>();
119        protected Set<String> fighters = new LinkedHashSet<String>();
120        protected Set<String> weapons = new LinkedHashSet<String>();
121        protected Set<String> unrestrictedFighters = new LinkedHashSet<String>();
122        protected Set<String> unrestrictedWeapons = new LinkedHashSet<String>();
123        protected Set<String> hullmods = new LinkedHashSet<String>();
124        
125        protected static final Color HIGHLIGHT_COLOR = Global.getSettings().getColor("buttonShortcut");
126        
127        public void init(InteractionDialogAPI dialog) {
128                this.dialog = dialog;
129                
130                
131                textPanel = dialog.getTextPanel();
132                options = dialog.getOptionPanel();
133                visual = dialog.getVisualPanel();
134
135                playerFleet = Global.getSector().getPlayerFleet();
136                
137                visual.setVisualFade(0.25f, 0.25f);
138                
139                initAvailable();
140                
141                dialog.setOptionOnEscape("Leave", OptionId.LEAVE);
142                optionSelected(null, OptionId.INIT);
143        }
144        
145        public Map<String, MemoryAPI> getMemoryMap() {
146                return null;
147        }
148        
149        public void backFromEngagement(EngagementResultAPI result) {
150                // no combat here, so this won't get called
151        }
152        
153        
154        public void optionSelected(String text, Object optionData) {
155                if (optionData == null) return;
156                
157                OptionId option = (OptionId) optionData;
158                
159                if (text != null) {
160                        //textPanel.addParagraph(text, Global.getSettings().getColor("buttonText"));
161                        dialog.addOptionSelectedText(option);
162                        //textPanel.addParagraph("");
163                }
164                
165                switch (option) {
166                case INIT:
167                        selectShipsAndWeapons();
168                        break;
169                case SELECT_SHIPS:
170                        showBlueprintPicker();
171                        break;
172                case ADD_UNSELECTED_OFFICERS:
173                        showOfficerSelector();
174                        break;
175                case OFFICERS_GO_BACK:
176                        selectShipsAndWeapons();
177                        break;
178                case OFFICER_LEVEL_5:
179                        addNoSkillOfficer(5);
180                        break;
181                case OFFICER_LEVEL_6:
182                        addNoSkillOfficer(6);
183                        break;
184                case OFFICER_LEVEL_7:
185                        addNoSkillOfficer(7);
186                        break;
187                case QUALITY_MAX_DMODS:
188                        data.quality = FleetQuality.MAX_DMODS;
189                        selectOfficerLevel();
190                        break;
191                case QUALITY_NO_DMODS:
192                        data.quality = FleetQuality.NO_DMODS;
193                        selectOfficerLevel();
194                        break;
195                case QUALITY_1_SMODS:
196                        data.quality = FleetQuality.ONE_SMOD;
197                        selectOfficerLevel();
198                        break;
199                case QUALITY_2_SMODS:
200                        data.quality = FleetQuality.TWO_SMODS;
201                        selectOfficerLevel();
202                        break;
203                case QUALITY_2_SMODS_OMEGA:
204                        data.quality = FleetQuality.TWO_SMODS;
205                        data.allowOmega = true;
206                        selectOfficerLevel();
207                        break;
208                case QUALITY_3_SMODS:
209                        data.quality = FleetQuality.THREE_SMODS;
210                        selectOfficerLevel();
211                        break;
212                case QUALITY_3_SMODS_OMEGA:
213                        data.quality = FleetQuality.THREE_SMODS;
214                        data.allowOmega = true;
215                        selectOfficerLevel();
216                        break;
217                case NO_OFFICERS:
218                        data.addOfficers = false;
219                        selectAppendOrReplace();
220                        break;
221                case ADD_OFFICERS:
222                        data.addOfficers = true;
223                        selectAppendOrReplace();
224                        break;
225                case ADD_OFFICERS_NO_SKILLS:
226                        data.addOfficers = true;
227                        data.keepOfficerPointsFree = true;
228                        selectAppendOrReplace();
229                        break;
230//              case NORMAL_LEVEL_OFFICERS:
231//                      data.eliteOfficers = false;
232//                      selectAppendOrReplace();
233//                      break;
234//              case HIGH_LEVEL_OFFICERS:
235//                      data.eliteOfficers = true;
236//                      selectAppendOrReplace();
237//                      break;
238                case CREATE_ADD:
239                        data.append = true;
240                        createFleet();
241                        dialog.dismiss();
242                        break;
243                case CREATE_REPLACE:
244                        data.append = false;
245                        createFleet();
246                        dialog.dismiss();
247                        break;
248                case LEAVE:
249                        dialog.dismiss();
250                        break;
251                default:
252                        break;
253                }
254        }
255        
256        protected MarketAPI getNearestMarket(boolean playerOnly) {
257                MarketAPI nearest = null;
258                float minDist = Float.MAX_VALUE;
259                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
260                for (MarketAPI curr : Global.getSector().getEconomy().getMarketsCopy()) {
261                        if (curr.isHidden()) continue;
262                        if (playerOnly && !curr.isPlayerOwned()) continue;
263                        
264                        float dist = Misc.getDistanceLY(pf, curr.getPrimaryEntity());
265                        boolean nearer = dist < minDist;
266                        if (dist == minDist && dist == 0 && nearest != null) {
267                                float d1 = Misc.getDistance(pf, curr.getPrimaryEntity());
268                                float d2 = Misc.getDistance(pf, nearest.getPrimaryEntity());
269                                nearer = d1 < d2;
270                        }
271                        if (nearer) {
272                                nearest = curr;
273                                minDist = dist;
274                        }
275                }
276                return nearest;
277        }
278        
279        protected void print(String str) {
280                textPanel.appendToLastParagraph("\n" + str);
281                System.out.println(str);
282        }
283        
284        protected void selectShipsAndWeapons() {
285                textPanel.addPara("--- QUICK PLAYER FLEET CREATOR ---");
286                textPanel.addPara("Ship variants will be randomly picked from those available for a given hull.");
287                textPanel.addPara("Selected weapons and fighters will be added to your cargo.");
288                
289                options.clearOptions();
290                options.addOption("Select ships and weapons", OptionId.SELECT_SHIPS);
291                options.addOption("Add officers with unselected skills", OptionId.ADD_UNSELECTED_OFFICERS);
292                options.addOption("Leave", OptionId.LEAVE, null);
293        }
294        
295        protected void showOfficerSelector() {
296                textPanel.addPara("Select option to instantly add an officer of that level with all unspent skills. The officer "
297                                + "level will be 1 higher than selected and they will not have an initial skill.");
298                options.clearOptions();
299                options.addOption("Level 5", OptionId.OFFICER_LEVEL_5);
300                options.addOption("Level 6", OptionId.OFFICER_LEVEL_6);
301                options.addOption("Level 7", OptionId.OFFICER_LEVEL_7);
302                options.addOption("Go back", OptionId.OFFICERS_GO_BACK);
303        }
304        
305        protected void selectQuality() {
306                textPanel.addPara("Select ship quality.");
307                options.clearOptions();
308                options.addOption("Maximum number of d-mods", OptionId.QUALITY_MAX_DMODS);
309                options.addOption("No d-mods", OptionId.QUALITY_NO_DMODS);
310                options.addOption("Average of 1 s-mod", OptionId.QUALITY_1_SMODS);
311                options.addOption("Average of 2 s-mods", OptionId.QUALITY_2_SMODS);
312                options.addOption("Average of 2 s-mods; allow some Omega weapons to be mounted", OptionId.QUALITY_2_SMODS_OMEGA);
313                options.addOption("Average of 3 s-mods", OptionId.QUALITY_3_SMODS);
314                options.addOption("Average of 3 s-mods; allow some Omega weapons to be mounted", OptionId.QUALITY_3_SMODS_OMEGA);
315                options.addOption("Leave", OptionId.LEAVE, null);
316        }
317        
318        protected void selectOfficerLevel() {
319                textPanel.addPara("The total number of officers and their level and elite skills will be based on your character's skills.");
320                options.clearOptions();
321                options.addOption("Add officers (number / level / elite skills based on your character's skills)", OptionId.ADD_OFFICERS);
322                //options.addOption("Add officers, but do not spend their skill points", OptionId.ADD_OFFICERS_NO_SKILLS);
323                options.addOption("Do not add officers", OptionId.NO_OFFICERS);
324                options.addOption("Leave", OptionId.LEAVE, null);
325        }
326        
327        protected void selectAppendOrReplace() {
328                textPanel.addPara("Select fleet creation mode.");
329                options.clearOptions();
330                options.addOption("Replace current fleet with new ships", OptionId.CREATE_REPLACE);
331                options.addOption("Add new ships to current fleet", OptionId.CREATE_ADD);
332                options.addOption("Leave", OptionId.LEAVE, null);
333        }
334        
335        protected OptionId lastOptionMousedOver = null;
336        public void optionMousedOver(String optionText, Object optionData) {
337
338        }
339        
340        public void advance(float amount) {
341                
342        }
343        
344        public Object getContext() {
345                return null;
346        }
347        
348        public void showBlueprintPicker() {
349                dialog.showCustomProductionPicker(new BaseCustomProductionPickerDelegateImpl() {
350                        @Override
351                        public Set<String> getAvailableFighters() {
352                                return fighters;
353                        }
354                        @Override
355                        public Set<String> getAvailableShipHulls() {
356                                return ships;
357                        }
358                        @Override
359                        public Set<String> getAvailableWeapons() {
360                                return weapons;
361                        }
362                        @Override
363                        public float getCostMult() {
364                                return 0.001f;
365                        }
366                        @Override
367                        public float getMaximumValue() {
368                                return 1000000;
369                        }
370                        public boolean withQuantityLimits() {
371                                return false;
372                        }
373                        @Override
374                        public void notifyProductionSelected(FactionProductionAPI production) {
375                                //convertProdToCargo(production);
376                                data.prod = production;
377                                selectQuality();
378                        }
379                });
380        }
381        
382        protected void createFleet() {
383                
384                FactionProductionAPI prod = data.prod;
385                CargoAPI cargo = Global.getFactory().createCargo(true);
386                
387
388                Random random = Misc.random;
389                
390                float quality = 2f;
391                if (data.quality == FleetQuality.MAX_DMODS) {
392                        quality = -2f;
393                }
394                
395                CampaignFleetAPI ships = Global.getFactory().createEmptyFleet(Factions.PLAYER, "temp", true);
396                ships.setCommander(Global.getSector().getPlayerPerson());
397                DefaultFleetInflaterParams p = new DefaultFleetInflaterParams();
398                p.quality = quality;
399                p.mode = ShipPickMode.PRIORITY_THEN_ALL;
400                p.persistent = false;
401                p.seed = random.nextLong();
402                p.timestamp = null;
403                p.rProb = 0f;
404                p.allWeapons = true;
405                
406                if (data.quality == FleetQuality.ONE_SMOD) {
407                        p.averageSMods = 1;
408                } else if (data.quality == FleetQuality.TWO_SMODS) {
409                        p.averageSMods = 2;
410                } else if (data.quality == FleetQuality.THREE_SMODS) {
411                        p.averageSMods = 3;
412                }
413                
414                FleetInflater inflater = Misc.getInflater(ships, p);
415                ships.setInflater(inflater);
416                
417                for (ItemInProductionAPI pick : prod.getCurrent()) {
418                        int count = pick.getQuantity();
419
420                        if (pick.getType() == ProductionItemType.SHIP) {
421                                List<String> variants = Global.getSettings().getHullIdToVariantListMap().get(pick.getSpecId());
422                                if (variants.isEmpty()) {
423                                        variants.add(pick.getSpecId() + "_Hull");
424                                }
425                                
426                                for (int i = 0; i < count; i++) {
427                                        int index = random.nextInt(variants.size());
428                                        ships.getFleetData().addFleetMember(variants.get(index));
429                                }
430                        } else if (pick.getType() == ProductionItemType.FIGHTER) {
431                                cargo.addFighters(pick.getSpecId(), count);
432                        } else if (pick.getType() == ProductionItemType.WEAPON) {
433                                cargo.addWeapons(pick.getSpecId(), count);
434                        }
435                        prod.removeItem(pick.getType(), pick.getSpecId(), count);
436                }
437                
438                FactionAPI pf = Global.getSector().getPlayerFaction();
439                List<String> addedWeapons = new ArrayList<String>();
440                List<String> addedFighters = new ArrayList<String>();
441                List<String> addedHullmods = new ArrayList<String>();
442                
443                if (data.allowOmega) {
444                        for (String id : weapons) {
445                                if (unrestrictedWeapons.contains(id)) continue;
446                                WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(id);
447                                if (spec.hasTag(Tags.OMEGA)) {
448                                        unrestrictedWeapons.add(id);
449                                }
450                        }
451                }
452                
453                for (String id : unrestrictedWeapons) {
454                        if (!pf.knowsWeapon(id)) {
455                                addedWeapons.add(id);
456                                pf.addKnownWeapon(id, false);
457                        }
458                }
459                for (String id : unrestrictedFighters) {
460                        if (!pf.knowsFighter(id)) {
461                                addedFighters.add(id);
462                                pf.addKnownFighter(id, false);
463                        }
464                }
465                for (String id : hullmods) {
466                        if (!pf.knowsHullMod(id)) {
467                                addedHullmods.add(id);
468                                pf.addKnownHullMod(id);
469                        }
470                }
471                
472                ships.inflateIfNeeded();
473                
474                for (String id : addedWeapons) {
475                        pf.removeKnownWeapon(id);
476                }
477                for (String id : addedFighters) {
478                        pf.removeKnownFighter(id);
479                }
480                for (String id : addedHullmods) {
481                        pf.removeKnownHullMod(id);
482                }
483                
484                if (!data.append && !ships.isEmpty()) {
485                        playerFleet.getFleetData().clear();
486                        if (data.addOfficers) {
487                                for (OfficerDataAPI od : playerFleet.getFleetData().getOfficersCopy()) {
488                                        playerFleet.getFleetData().removeOfficer(od.getPerson());
489                                }
490                        }
491                }
492                
493                playerFleet.getCargo().addAll(cargo);
494                
495                for (FleetMemberAPI member : ships.getFleetData().getMembersListCopy()) {
496                        playerFleet.getFleetData().addFleetMember(member);
497                }
498
499                if (data.addOfficers) {
500                        int maxOfficers = Misc.getMaxOfficers(playerFleet);
501                        int add = maxOfficers - playerFleet.getFleetData().getOfficersCopy().size();
502                        if (add > 0) {
503                                FleetParamsV3 fp = new FleetParamsV3();
504                                fp.commander = playerFleet.getCommander();
505                                fp.maxOfficersToAdd = add;
506                                FleetFactoryV3.addCommanderAndOfficersV2(playerFleet, fp, random);
507                        }
508                }
509                
510                
511                playerFleet.forceSync();
512                
513                cargo = playerFleet.getCargo();
514                
515                int neededCrew = (int) ((playerFleet.getFleetData().getMinCrew() + cargo.getMaxPersonnel()) / 2f 
516                                                                        - cargo.getCrew());
517                if (neededCrew > 0) {
518                        cargo.addCrew(neededCrew);
519                }
520                cargo.addFuel(cargo.getMaxFuel() - cargo.getFuel());
521                cargo.addSupplies(cargo.getSpaceLeft() * 0.5f);
522                
523                
524                playerFleet.forceSync();
525                
526                for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
527                        float max = member.getRepairTracker().getMaxCR();
528                        member.getRepairTracker().setCR(max);
529                }
530                
531                
532                
533        }
534        
535        protected void initAvailable() {
536                for (ShipHullSpecAPI spec : Global.getSettings().getAllShipHullSpecs()) {
537                        //if (spec.isDefaultDHull() || spec.isDHull()) continue;
538                        if (spec.isDefaultDHull()) continue;
539                        if (spec.getHullSize() == HullSize.FIGHTER) continue;
540                        if (!spec.hasHullName()) continue;
541                        if (spec.getHints().contains(ShipTypeHints.STATION)) continue;
542//                      if (spec.getHints().contains(ShipTypeHints.HIDE_IN_CODEX)) continue;
543//                      if (spec.getHints().contains(ShipTypeHints.UNBOARDABLE)) continue;
544                        if ("shuttlepod".equals(spec.getHullId())) continue;
545                        ships.add(spec.getHullId());
546                }
547                
548                for (WeaponSpecAPI spec : Global.getSettings().getAllWeaponSpecs()) {
549                        //if (!spec.hasTag(Items.TAG_RARE_BP) && !spec.hasTag(Items.TAG_DEALER)) continue;
550                        if (spec.getAIHints().contains(AIHints.SYSTEM)) continue;
551                        if (!spec.hasTag(Tags.RESTRICTED)) {
552                                unrestrictedWeapons.add(spec.getWeaponId());
553                        }
554                        weapons.add(spec.getWeaponId());
555                }
556                
557                for (FighterWingSpecAPI spec : Global.getSettings().getAllFighterWingSpecs()) {
558                        if (!spec.hasTag(Tags.RESTRICTED)) {
559                                unrestrictedFighters.add(spec.getId());
560                        }
561                        fighters.add(spec.getId());
562                }
563                
564                for (HullModSpecAPI spec : Global.getSettings().getAllHullModSpecs()) {
565                        hullmods.add(spec.getId());
566                }
567        }
568        
569        protected void addNoSkillOfficer(int level) {
570                PersonAPI p = Global.getSector().getPlayerFaction().createRandomPerson();
571                p.getMemoryWithoutUpdate().set(MemFlags.OFFICER_SKILL_PICKS_PER_LEVEL, 10);
572                p.getMemoryWithoutUpdate().set(MemFlags.OFFICER_MAX_LEVEL, level + 1);
573                if (level >= 7) {
574                        p.getMemoryWithoutUpdate().set(MemFlags.OFFICER_MAX_ELITE_SKILLS, 4);
575                }
576//              p.getStats().setLevel(level + 1);
577                for (SkillLevelAPI sl : p.getStats().getSkillsCopy()) {
578                        p.getStats().setSkillLevel(sl.getSkill().getId(), 0);
579                }
580                //p.setPersonality(Personalities.AGGRESSIVE);
581                OfficerLevelupPlugin plugin = (OfficerLevelupPlugin) Global.getSettings().getPlugin("officerLevelUp");
582                long xp = plugin.getXPForLevel(level + 1);
583                OfficerDataAPI od = Global.getFactory().createOfficerData(p);
584                od.addXP(xp);
585                Global.getSector().getPlayerFleet().getFleetData().addOfficer(od);
586                if (level >= 7) {
587                        textPanel.addPara("Added level " + (level + 1) + " officer with NO initial skill and 4 elite skills max.");     
588                } else {
589                        textPanel.addPara("Added level " + (level + 1) + " officer with NO initial skill.");
590                }
591        }
592}
593
594
595
596