001package com.fs.starfarer.api.impl;
002
003import java.io.IOException;
004import java.util.ArrayList;
005import java.util.Collections;
006import java.util.Comparator;
007import java.util.LinkedHashSet;
008import java.util.List;
009import java.util.Map;
010import java.util.Random;
011import java.util.Set;
012
013import org.json.JSONArray;
014import org.json.JSONException;
015import org.json.JSONObject;
016
017import com.fs.starfarer.api.Global;
018import com.fs.starfarer.api.campaign.BattleAPI;
019import com.fs.starfarer.api.campaign.CampaignFleetAPI;
020import com.fs.starfarer.api.campaign.CargoAPI;
021import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
022import com.fs.starfarer.api.campaign.FactionAPI;
023import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode;
024import com.fs.starfarer.api.campaign.FactionDoctrineAPI;
025import com.fs.starfarer.api.campaign.FactionSpecAPI;
026import com.fs.starfarer.api.campaign.econ.MarketAPI;
027import com.fs.starfarer.api.characters.PersonAPI;
028import com.fs.starfarer.api.characters.SkillSpecAPI;
029import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
030import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
031import com.fs.starfarer.api.combat.ShipAPI;
032import com.fs.starfarer.api.combat.ShipAPI.HullSize;
033import com.fs.starfarer.api.combat.ShipCommand;
034import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints;
035import com.fs.starfarer.api.combat.ShipSystemSpecAPI;
036import com.fs.starfarer.api.combat.ShipVariantAPI;
037import com.fs.starfarer.api.fleet.FleetMemberAPI;
038import com.fs.starfarer.api.impl.campaign.DModManager;
039import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflater;
040import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflaterParams;
041import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
042import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
043import com.fs.starfarer.api.impl.campaign.ids.Commodities;
044import com.fs.starfarer.api.impl.campaign.ids.Factions;
045import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
046import com.fs.starfarer.api.impl.campaign.ids.Personalities;
047import com.fs.starfarer.api.impl.campaign.ids.ShipRoles;
048import com.fs.starfarer.api.impl.campaign.ids.Skills;
049import com.fs.starfarer.api.impl.campaign.ids.Tags;
050import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
051import com.fs.starfarer.api.impl.campaign.intel.misc.SimUpdateIntel;
052import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithTriggers.OfficerQuality;
053import com.fs.starfarer.api.impl.campaign.procgen.themes.RemnantOfficerGeneratorPlugin;
054import com.fs.starfarer.api.input.InputEventAPI;
055import com.fs.starfarer.api.loading.FighterWingSpecAPI;
056import com.fs.starfarer.api.loading.HullModSpecAPI;
057import com.fs.starfarer.api.loading.VariantSource;
058import com.fs.starfarer.api.loading.WeaponSlotAPI;
059import com.fs.starfarer.api.loading.WeaponSpecAPI;
060import com.fs.starfarer.api.mission.FleetSide;
061import com.fs.starfarer.api.plugins.AutofitPlugin.AutofitPluginDelegate;
062import com.fs.starfarer.api.plugins.AutofitPlugin.AvailableFighter;
063import com.fs.starfarer.api.plugins.AutofitPlugin.AvailableWeapon;
064import com.fs.starfarer.api.plugins.SimulatorPlugin;
065import com.fs.starfarer.api.plugins.impl.CoreAutofitPlugin;
066import com.fs.starfarer.api.ui.Alignment;
067import com.fs.starfarer.api.ui.TooltipMakerAPI;
068import com.fs.starfarer.api.util.CountingMap;
069import com.fs.starfarer.api.util.Misc;
070import com.fs.starfarer.api.util.WeightedRandomPicker;
071
072public class SimulatorPluginImpl implements SimulatorPlugin, AutofitPluginDelegate {
073
074        public static boolean INCLUDE_PLAYER_BLUEPRINTS = false;
075        public static boolean REQUIRE_AI_CORES = true;
076        public static boolean REQUIRE_AI_CORES_IN_CARGO = Global.getSettings().getBoolean("requireAICoresInCargoForSimulator");
077        
078        
079        public static String UNLOCKS_DATA_FILE          = "core_sim_unlocks.json";
080        public static String CUSTOM_OPPONENTS_FILE      = "core_sim_custom_opponents.json";
081        public static String UI_STATE_DATA_FILE         = "core_sim_settings.json";
082        
083        public static String DEFAULT_CAT_ID             = "cat_default";
084        public static String CUSTOM_CAT_ID                      = "cat_custom";
085        public static String OTHER_CAT_ID                       = "cat_other";
086        public static String CIV_CAT_ID                         = "cat_civ";
087        public static String DEV_CAT_ID                         = "cat_dev";
088        
089        public static String AGGRO_ID                           = "aggro";
090        public static String AGGRO_ID_CORES_ONLY        = "aggro_cores";
091        public static String AGGRO_DEFAULT                      = "default";
092        public static String AGGRO_CAUTIOUS             = "cautious";
093        public static String AGGRO_STEADY                       = "steady";
094        public static String AGGRO_AGGRESSIVE           = "aggressive";
095        public static String AGGRO_RECKLESS             = "reckless";
096        public static String AGGRO_NORMAL                       = "normal";
097        public static String AGGRO_DO_NOTHING           = "do_nothing";
098        public static String AGGRO_DEFENSES             = "defenses_only";
099        public static String AGGRO_STATIONARY           = "stationary";
100        
101        
102        public static String OFFICERS_CUSTOM_ID         = "officers_custom";
103        public static String OFFICERS_CUSTOM_NONE       = "none";
104        public static String OFFICERS_CUSTOM_SOME       = "some";
105        public static String OFFICERS_CUSTOM_5          = "level5";
106        public static String OFFICERS_CUSTOM_6          = "level6";
107        
108        public static String OFFICERS_ID                        = "officers";
109        public static String OFFICERS_NONE                      = "none";
110        public static String OFFICERS_DEFAULT           = "default";
111        public static String OFFICERS_ALL                       = "all";
112        
113        public static String QUALITY_ID                         = "quality";
114        public static String QUALITY_MAX_DMODS          = "max_dmods";
115        public static String QUALITY_SOME_DMODS         = "some_dmods";
116        public static String QUALITY_NO_DMODS           = "no_dmods";
117        public static String QUALITY_SOME_SDMODS        = "some_smods";
118        public static String QUALITY_MANY_SMODS         = "many_smods";
119        
120        public static String AI_CORES_ID                        = "ai_cores";
121        public static String AI_CORES_DERELICT_ID       = "ai_cores_derelict";
122        public static String AI_CORES_OMEGA_ID          = "ai_cores_omega";
123        public static String AI_CORES_DEV_ID            = "ai_cores_dev";
124        public static String AI_CORES_NONE                      = "none";
125        public static String AI_CORES_SOME                      = "some";
126        public static String AI_CORES_GAMMA                     = "gamma";
127        public static String AI_CORES_BETA                      = "beta";
128        public static String AI_CORES_ALPHA                     = "alpha";
129        public static String AI_CORES_OMEGA                     = "omega";
130
131        public static String RANDOMIZE_VARIANTS_ID      = "randomize_variants";
132        public static String INTEGRATE_CORES_ID = "integrate_cores";
133        
134                
135        
136        public static boolean isShowDevCategories() {
137                return (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) ||
138                                !(Global.getCombatEngine().isInCampaignSim() || Global.getCombatEngine().isInMissionSim());
139        }
140        
141        public static boolean isSimFullyUnlocked() {
142                return (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) ||
143                                !(Global.getCombatEngine().isInCampaignSim() || Global.getCombatEngine().isInMissionSim());
144        }
145        
146        public static boolean isAllStandardStuffUnlocked() {
147                return Global.getSettings().getBoolean("allStandardShipsAndFactionsUnlockedInSimulator");
148        }
149
150        public static class SimUnlocksData {
151                public LinkedHashSet<String> factions = new LinkedHashSet<String>();
152                public LinkedHashSet<String> variants = new LinkedHashSet<String>();
153                
154                public void fromJSON(JSONObject json) {
155                        factions = new LinkedHashSet<String>();
156                        JSONArray arr = json.optJSONArray("factions");
157                        if (arr != null) {
158                                for (int i = 0; i < arr.length(); i++) {
159                                        String value = arr.optString(i);
160                                        if (value != null) {
161                                                factions.add(value);
162                                        }
163                                }
164                        }
165                        variants = new LinkedHashSet<String>();
166                        arr = json.optJSONArray("variants");
167                        if (arr != null) {
168                                for (int i = 0; i < arr.length(); i++) {
169                                        String value = arr.optString(i);
170                                        if (value != null) {
171                                                variants.add(value);
172                                        }
173                                }
174                        }
175                }
176                
177                public JSONObject toJSON() throws JSONException {
178                        JSONObject json = new JSONObject();
179
180                        JSONArray arr1 = new JSONArray();
181                        for (String value : factions) {
182                                arr1.put(value);
183                        }
184                        JSONArray arr2 = new JSONArray();
185                        for (String value : variants) {
186                                arr2.put(value);
187                        }
188                        
189                        json.put("factions", arr1);
190                        json.put("variants", arr2);
191
192                        return json;
193                }
194        }
195        
196        
197        
198        
199        protected SimUIStateData uiStateData = new SimUIStateData();
200        protected Set<String> defaultOpponents = new LinkedHashSet<String>();
201        protected Set<String> customOpponents = new LinkedHashSet<String>();
202        protected boolean loadedStuff = false;
203        protected SimUnlocksData unlocksData = new SimUnlocksData();
204        
205        public SimulatorPluginImpl() {
206                loadUIStateData();
207        }
208        
209        public boolean coreReqsMet(String coreId) {
210                if (!REQUIRE_AI_CORES || !Global.getCombatEngine().isInCampaignSim()) return true;
211                if (isSimFullyUnlocked()) return true;
212                return Global.getSector().getPlayerFleet().getCargo().getQuantity(CargoItemType.RESOURCES, coreId) > 0;
213        }
214        
215        public boolean isRequireAICoresInCargo() {
216                if (!REQUIRE_AI_CORES_IN_CARGO) return false;
217                if (isSimFullyUnlocked()) return false;
218                return Global.getCombatEngine().isInCampaignSim();
219        }
220        
221        
222        public void addCustomOpponents(List<String> variants) {
223                customOpponents.addAll(variants);
224        }
225        
226        public void removeCustomOpponents(List<String> variants) {
227                customOpponents.removeAll(variants);
228        }
229        
230        public void clearCustomOpponents() {
231                customOpponents = new LinkedHashSet<String>();
232//              for (String variantId : Global.getSettings().getSimOpponents()) {
233//                      if (Global.getSettings().getVariant(variantId) == null) continue;
234//                      customOpponents.add(variantId);
235//              }
236        }
237        
238        public void loadCustomOpponents() {
239                try {
240                        if (Global.getSettings().fileExistsInCommon(CUSTOM_OPPONENTS_FILE)) {
241                                customOpponents = new LinkedHashSet<String>();
242                                JSONObject json = Global.getSettings().readJSONFromCommon(CUSTOM_OPPONENTS_FILE, true);
243                                JSONArray arr = json.optJSONArray("opponents");
244                                if (arr != null) {
245                                        for (int i = 0; i < arr.length(); i++) {
246                                                String variantId = arr.getString(i);
247                                                if (Global.getSettings().getVariant(variantId) == null) continue;
248                                                customOpponents.add(variantId);
249                                        }
250                                }
251                        } else {
252                                clearCustomOpponents();
253                        }
254                } catch (IOException e) {
255                        e.printStackTrace();
256                } catch (JSONException e) {
257                        e.printStackTrace();
258                }
259        }
260        
261        public void saveCustomOpponents() {
262                try {
263                        JSONObject json = new JSONObject();
264                        JSONArray arr = new JSONArray();
265                        for (String variantId : customOpponents) {
266                                arr.put(variantId);
267                        }
268                        json.put("opponents", arr);
269                        Global.getSettings().writeJSONToCommon(CUSTOM_OPPONENTS_FILE, json, true);
270                } catch (JSONException e) {
271                        e.printStackTrace();
272                } catch (IOException e) {
273                        e.printStackTrace();
274                }
275        }
276        
277        public void resetToDefaults(boolean withSave) {
278                uiStateData.settings.clear();
279                uiStateData.settings.put(AGGRO_ID, AGGRO_DEFAULT);
280                uiStateData.settings.put(OFFICERS_CUSTOM_ID, OFFICERS_NONE);
281                uiStateData.settings.put(OFFICERS_ID, OFFICERS_NONE);
282                uiStateData.settings.put(QUALITY_ID, QUALITY_NO_DMODS);
283                uiStateData.settings.put(AI_CORES_ID, AI_CORES_NONE);
284                uiStateData.settings.put(AI_CORES_DERELICT_ID, AI_CORES_NONE);
285                uiStateData.settings.put(AI_CORES_OMEGA_ID, AI_CORES_NONE);
286                uiStateData.settings.put(RANDOMIZE_VARIANTS_ID, "false");
287                uiStateData.settings.put(INTEGRATE_CORES_ID, "false");
288                uiStateData.groupSize = 0;
289                if (withSave) {
290                        saveUIStateData();
291                }
292        }
293        
294        public void loadUIStateData() {
295                try {
296                        if (Global.getSettings().fileExistsInCommon(UI_STATE_DATA_FILE)) {
297                                JSONObject json = Global.getSettings().readJSONFromCommon(UI_STATE_DATA_FILE, true);
298                                uiStateData.fromJSON(json);
299                        } else {
300                                uiStateData.selectedCategory = DEFAULT_CAT_ID;
301                                uiStateData.showAdvanced = false;
302                                resetToDefaults(false);
303                        }
304                } catch (IOException e) {
305                        e.printStackTrace();
306                } catch (JSONException e) {
307                        e.printStackTrace();
308                }
309        }
310        
311        public void saveUIStateData() {
312                try {
313                        JSONObject json = uiStateData.toJSON();
314                        Global.getSettings().writeJSONToCommon(UI_STATE_DATA_FILE, json, true);
315                } catch (JSONException e) {
316                        e.printStackTrace();
317                } catch (IOException e) {
318                        e.printStackTrace();
319                }
320        }
321        
322        public SimUIStateData getUIStateData() {
323                return uiStateData;
324        }
325
326        public void loadUnlocksData() {
327                try {
328                        defaultOpponents.clear();
329                        defaultOpponents.addAll(Global.getSettings().getSimOpponents());
330                        
331                        if (Global.getSettings().fileExistsInCommon(UNLOCKS_DATA_FILE)) {
332                                JSONObject json = Global.getSettings().readJSONFromCommon(UNLOCKS_DATA_FILE, true);
333                                unlocksData.fromJSON(json);
334                                
335                                //unlocksData.variants.addAll(Global.getSettings().getSimOpponents());
336                        } else {
337                        
338                        }
339                } catch (IOException e) {
340                        e.printStackTrace();
341                } catch (JSONException e) {
342                        e.printStackTrace();
343                }
344        }
345        
346        public void saveUnlocksData() {
347                try {
348                        JSONObject json = unlocksData.toJSON();
349                        Global.getSettings().writeJSONToCommon(UNLOCKS_DATA_FILE, json, true);
350                } catch (JSONException e) {
351                        e.printStackTrace();
352                } catch (IOException e) {
353                        e.printStackTrace();
354                }
355        }
356        
357        public SimUnlocksData getUnlocksData() {
358                return unlocksData;
359        }
360
361        public List<AdvancedSimOption> getSimOptions(SimCategoryData category) {
362                List<AdvancedSimOption> result = new ArrayList<AdvancedSimOption>();
363
364                boolean custom = CUSTOM_CAT_ID.equals(category.id);
365                boolean other = OTHER_CAT_ID.equals(category.id);
366                boolean defaultCat = DEFAULT_CAT_ID.equals(category.id);
367                boolean civ = CIV_CAT_ID.equals(category.id);
368                boolean dev = DEV_CAT_ID.equals(category.id);
369                boolean customFaction = custom || civ || dev || defaultCat || other;
370                
371                JSONObject json = category.faction.getCustom().optJSONObject("simulatorData");
372                boolean standardAICores = json != null && json.optBoolean("standardAICores");
373                boolean derelictAICores = json != null && json.optBoolean("derelictAICores");
374                boolean omegaAICores = json != null && json.optBoolean("omegaAICores");
375                boolean noOfficers = json != null && json.optBoolean("noOfficers");
376                
377                boolean showOfficers = !standardAICores && !derelictAICores && !omegaAICores && !noOfficers;
378                boolean integrateCores = standardAICores || derelictAICores || dev || custom || other;
379                
380                if (custom || defaultCat || other) {
381                        showOfficers = true;
382                        standardAICores = true;
383                        integrateCores = true;
384                }
385                
386                String aggroExtra = "";
387                String aiExtra = "";
388                if (custom || defaultCat || dev || other) {
389                        aggroExtra = "\n\nDoes not affect AI cores or automated ships.";
390                        aiExtra = "\n\nOnly affects automated ships.";
391                }
392                
393                String aggroTitle = "Aggression / behavior";
394                String aggroId = AGGRO_ID;
395                if (!showOfficers) {
396                        aggroTitle = "Behavior";
397                        aggroId = AGGRO_ID_CORES_ONLY;
398                }
399                
400                SimOptionSelectorData aggro = new SimOptionSelectorData(aggroId, aggroTitle, true);
401                if (showOfficers) {
402                        if (custom || defaultCat || civ || dev || other) {
403                                aggro.options.add(new SimOptionData(AGGRO_DEFAULT, "Default (steady)", 
404                                                Global.getSettings().getPersonaltySpec(Personalities.STEADY).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_default"));
405                        } else {
406                                aggro.options.add(new SimOptionData(AGGRO_DEFAULT, "Faction default", 
407                                                "Default aggression level for the selected faction." + aggroExtra, "behavior_default"));
408                        }
409                        aggro.options.add(new SimOptionData(AGGRO_CAUTIOUS, "Cautious",
410                                        Global.getSettings().getPersonaltySpec(Personalities.CAUTIOUS).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_cautious"));
411                        aggro.options.add(new SimOptionData(AGGRO_STEADY, "Steady",
412                                        Global.getSettings().getPersonaltySpec(Personalities.STEADY).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_steady"));
413                        aggro.options.add(new SimOptionData(AGGRO_AGGRESSIVE, "Aggressive", 
414                                        Global.getSettings().getPersonaltySpec(Personalities.AGGRESSIVE).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_aggressive"));
415                        aggro.options.add(new SimOptionData(AGGRO_RECKLESS, "Reckless", 
416                                        Global.getSettings().getPersonaltySpec(Personalities.RECKLESS).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_reckelss"));
417                } else {
418                        aggro.options.add(new SimOptionData(AGGRO_NORMAL, "Normal", "Opposing ships will behave normally.", "behavior_default"));
419                }
420                
421                aggro.options.add(new SimOptionData(AGGRO_DO_NOTHING, "Do nothing", "Opposing ships will not move, use shields, fire weapons, or take any other actions.", "behavior_passive"));
422                if (showOfficers) {
423                        aggro.options.get(aggro.options.size() - 1).extraPad = 10f;
424                }
425                aggro.options.add(new SimOptionData(AGGRO_DEFENSES, "Stationary, defenses only", "Opposing ships will not move, but will use shields/phase cloak/other defenses and defensive ship systems, if any.", "behavior_defensive"));
426                aggro.options.add(new SimOptionData(AGGRO_STATIONARY, "Stationary", "Opposing ships will not move, but will otherwise behave normally.", "behavior_stationary"));
427                aggro.compact = false;
428                result.add(aggro);
429                
430                
431                if (showOfficers) {
432                        if (customFaction) {
433                                SimOptionSelectorData officers = new SimOptionSelectorData(OFFICERS_CUSTOM_ID, "Officers", true);
434                                officers.options.add(new SimOptionData(OFFICERS_CUSTOM_NONE, "None", "No officers on any opposing ships.", "officers_none"));
435                                officers.options.add(new SimOptionData(OFFICERS_CUSTOM_SOME, "Some", "Some officers, up to level 5.", "officers_some"));
436                                officers.options.add(new SimOptionData(OFFICERS_CUSTOM_5, "All ships, level 5", "Level 5 officers on all opposing ships.", "officers_all"));
437                                officers.options.add(new SimOptionData(OFFICERS_CUSTOM_6, "All ships, level 6", "Level 6 officers on all opposing ships.", "officers_high"));
438                                result.add(officers);                   
439                        } else {
440                                SimOptionSelectorData officers = new SimOptionSelectorData(OFFICERS_ID, "Officers", true);
441                                officers.options.add(new SimOptionData(OFFICERS_NONE, "None", "No officers on any opposing ships.", "officers_none"));
442                                officers.options.add(new SimOptionData(OFFICERS_DEFAULT, "Faction default", "Default number and level of officers for the selected faction.", "officers_some"));
443                                officers.options.add(new SimOptionData(OFFICERS_ALL, "All ships", "Maximum level officers on all opposing ships.", "officers_high"));
444                                result.add(officers);
445                        }
446                }
447                
448                if (standardAICores || derelictAICores || omegaAICores || custom || defaultCat || dev || other) {
449                        String coresId = AI_CORES_ID;
450                        if (dev) {
451                                coresId = AI_CORES_DEV_ID;
452                        } else if (omegaAICores) {
453                                coresId = AI_CORES_OMEGA_ID;
454                        } else if (derelictAICores) {
455                                coresId = AI_CORES_DERELICT_ID;
456                        }
457                        SimOptionSelectorData cores = new SimOptionSelectorData(coresId, "AI cores", false);
458                        cores.options.add(new SimOptionData(AI_CORES_NONE, "None", "No AI cores on any opposing ships.", "cores_none"));
459
460                        boolean enableAlpha = coreReqsMet(Commodities.ALPHA_CORE);
461                        boolean enableBeta = coreReqsMet(Commodities.BETA_CORE) || enableAlpha;
462                        boolean enableGamma = coreReqsMet(Commodities.GAMMA_CORE) || enableBeta;
463                        boolean enableMixed = enableAlpha || (derelictAICores && enableGamma);
464
465                        String reqGamma = null;
466                        if (!enableGamma) reqGamma = "Requires a Gamma Core or better in your cargo.";
467                        String reqBeta = null;
468                        if (!enableBeta) reqBeta = "Requires a Beta Core or better in your cargo.";
469                        String reqAlpha = null;
470                        if (!enableAlpha) reqAlpha = "Requires an Alpha Core in your cargo.";
471                        String reqMixed = null;
472                        if (!enableMixed) {
473                                if (derelictAICores) {
474                                        reqMixed = "Requires a Gamma Core or better in your cargo.";
475                                } else {
476                                        reqMixed = "Requires an Alpha Core in your cargo.";
477                                }
478                        }
479                        
480                        String coresCargoNote = "";
481                        String ifPossible = "";
482                        if (isRequireAICoresInCargo()) {
483                                coresCargoNote = " The AI cores used are limited to the total number and type "
484                                                + "of cores in your cargo (and storage, if docked).";
485                                ifPossible = ", if possible";
486                                boolean canUseCores = enableGamma || enableBeta || enableAlpha || enableMixed;
487                                if (canUseCores) {
488                                        enableGamma = enableBeta = enableAlpha = enableMixed = true;
489                                        reqGamma = reqBeta = reqAlpha = reqMixed = null;
490                                } else {
491                                        enableGamma = enableBeta = enableAlpha = enableMixed = false;
492                                        reqGamma = reqBeta = reqAlpha = reqMixed = "No AI cores in cargo (or storage, if docked).";
493                                }
494                        }
495                        
496                        if (standardAICores || derelictAICores || dev) {
497                                cores.options.add(new SimOptionData(AI_CORES_SOME, "Mixed", "A mix of AI cores on some of the opposing ships, based on the number and size of opponents deployed." + coresCargoNote + aiExtra, enableMixed, reqMixed, "cores_mixed"));
498                                cores.options.add(new SimOptionData(AI_CORES_GAMMA, "Gamma cores on all ships", "A gamma core on every opposing ship" + ifPossible + "." + coresCargoNote + aiExtra, enableGamma, reqGamma, "cores_gamma"));
499                                if (standardAICores || dev) {
500                                        cores.options.add(new SimOptionData(AI_CORES_BETA, "Beta cores on all ships", "A beta core on every opposing ship" + ifPossible + "." + coresCargoNote + aiExtra, enableBeta, reqBeta, "cores_beta"));
501                                        cores.options.add(new SimOptionData(AI_CORES_ALPHA, "Alpha cores on all ships", "An alpha core on every opposing ship" + ifPossible + "." + coresCargoNote + aiExtra, enableAlpha, reqAlpha, "cores_alpha"));
502                                }
503                        }
504                        if (dev || omegaAICores) {
505                                cores.options.add(new SimOptionData(AI_CORES_OMEGA, "Omega cores on all ships", "An omega core on every opposing ship." + aiExtra, "cores_omega"));
506                        }
507                        if (!omegaAICores) {
508                                cores.padAfter = 10f;
509                        }
510                        result.add(cores);      
511                }
512                
513                if (integrateCores) {
514                        String iTooltipExtra = "";
515                        if (dev) {
516                                iTooltipExtra = "\n\nDoes not affect omega cores.";
517                        }
518                        SimOptionCheckboxData integrate = new SimOptionCheckboxData(INTEGRATE_CORES_ID, "Integrate AI cores",
519                                        "AI cores will be integrated into opposing ships, increasing each core's level by 1." + iTooltipExtra);
520                        // no longer relevant since aggression/behavior is no longer condensed and is taller
521//                      if (!custom && !dev && !defaultCat && !other) { 
522//                              integrate.padAfter += 8f;
523//                      }
524                        result.add(integrate);
525                }
526
527                
528                SimOptionSelectorData quality = new SimOptionSelectorData(QUALITY_ID, "Ship quality", true);
529                quality.options.add(new SimOptionData(QUALITY_MAX_DMODS, "Maximum d-mods", "Five d-mods on all opposing ships.", "quality_lowest"));
530                quality.options.add(new SimOptionData(QUALITY_SOME_DMODS, "Some d-mods", "Two to four d-mods on all opposing ships.", "quality_low"));
531                quality.options.add(new SimOptionData(QUALITY_NO_DMODS, "No d-mods", "No d-mods on any opposing ships.", "quality_no_dmods"));
532                quality.options.add(new SimOptionData(QUALITY_SOME_SDMODS, "Some s-mods", "One or two s-mods on all opposing ships.", "quality_high"));
533                quality.options.add(new SimOptionData(QUALITY_MANY_SMODS, "Many s-mods", "Two or three s-mods on all opposing ships.", "quality_highest"));
534                result.add(quality);
535                
536                // no longer relevant since aggression/behavior is no longer condensed and is taller
537//              if (!custom && !dev && !defaultCat && !other) {
538//                      quality.padAfter += 11f; // to line it up with the separator line between deployed/reserve
539//              }
540                
541                
542                String rTooltipExtra = "";
543                if (customFaction) {
544                        rTooltipExtra = "\n\nFor the selected category, uses a broader set of weapons than is available to most factions.";
545                }
546                SimOptionCheckboxData loadouts = new SimOptionCheckboxData(RANDOMIZE_VARIANTS_ID, "Randomized loadouts",
547                                "Opposing ships have randomized loadouts when this setting is enabled." + rTooltipExtra);
548                result.add(loadouts);
549                
550                return result;
551        }
552        
553        public boolean showGroupDeploymentWidget(SimCategoryData category) {
554                boolean custom = CUSTOM_CAT_ID.equals(category.id);
555                boolean other = OTHER_CAT_ID.equals(category.id);
556                boolean defaultCat = DEFAULT_CAT_ID.equals(category.id);
557                boolean civ = CIV_CAT_ID.equals(category.id);
558                boolean dev = DEV_CAT_ID.equals(category.id);
559                boolean customFaction = custom || civ || dev || defaultCat;
560                
561                if (civ) return false;
562                
563                return true;
564        }
565        
566        public SimCategoryData getCustomCategory() {
567                SimCategoryData custom = new SimCategoryData();
568                custom.id = CUSTOM_CAT_ID;
569                custom.name = "Custom";
570                custom.custom = true;
571                custom.nonFactionCategory = true;
572                custom.nameColor = Misc.getBasePlayerColor();
573                custom.iconName = Global.getSettings().getSpriteName("simulator", "customPlayerCrest");
574                custom.data = null;
575                custom.faction = createCustomFaction();
576                custom.variants = getVariantIDList(sortVariantList(getVariantList(customOpponents)));
577                return custom;
578        }
579        
580        
581        public List<SimCategoryData> getCategories() {
582                if (!loadedStuff) {
583                        loadCustomOpponents();
584                        loadUnlocksData();
585                        loadedStuff = true;
586                }
587                
588                boolean fullUnlock = isSimFullyUnlocked() || isAllStandardStuffUnlocked();
589                
590                List<SimCategoryData> result = new ArrayList<SimulatorPlugin.SimCategoryData>();
591                
592                Set<String> civilian = new LinkedHashSet<String>();
593                
594                Set<String> other = new LinkedHashSet<String>(unlocksData.variants);
595//              if (other.contains("doom_Strike")) {
596//                      System.out.println("23dfefewf");
597//              }
598//              other.clear();
599                if (Global.getCombatEngine().isInCampaignSim() && INCLUDE_PLAYER_BLUEPRINTS) {
600                        FactionAPI player = Global.getSector().getPlayerFaction();
601                        for (String roleId : getAllRoles()) {
602                                Set<String> variants = player.getVariantsForRole(roleId);
603                                if (variants == null) continue;
604                                other.addAll(variants);
605                        }
606                }
607                
608                
609                for (FactionSpecAPI spec : Global.getSettings().getAllFactionSpecs()) {
610                        if (spec == null || spec.getCustom() == null) continue;
611                        
612                        JSONObject json = spec.getCustom().optJSONObject("simulatorData");
613                        if (json == null) continue;
614                        
615                        boolean show = json.optBoolean("showInSimulator");
616                        show |= isShowDevCategories() && json.optBoolean("showInSimulatorDevModeOnly");
617                        
618                        show &= fullUnlock || unlocksData.factions.contains(spec.getId());
619                        
620                        if (!show) continue;
621                        
622                        boolean includeCiv = json.optBoolean("includeCivShipsWithFaction");
623                        
624                        SimCategoryData data = new SimCategoryData();
625                        data.id = spec.getId();
626                        data.name = Misc.ucFirst(spec.getDisplayName());
627                        data.nameColor = spec.getBaseUIColor();
628                        data.iconName = spec.getCrest();
629                        data.data = spec;
630                        data.variants = getVariants(spec, includeCiv, false, false);
631                        data.maxVariants = getVariants(spec, includeCiv, false, true).size();
632                        data.faction = Global.getSettings().createBaseFaction(spec.getId());
633                        
634                        if (!includeCiv) {
635                                civilian.addAll(getVariants(spec, true, true, false));
636                        }
637                        
638                        if (data.variants == null || data.variants.isEmpty()) continue;
639                        
640                        other.removeAll(data.variants);
641                        
642                        result.add(data);
643                }
644                
645                other.removeAll(civilian);
646                
647                Collections.sort(result, new Comparator<SimCategoryData>() {
648                        public int compare(SimCategoryData o1, SimCategoryData o2) {
649                                return o1.name.compareTo(o2.name);
650                        }
651                });
652                
653                SimCategoryData custom = getCustomCategory();
654                result.add(0, custom);
655                
656                SimCategoryData def = new SimCategoryData();
657                def.id = DEFAULT_CAT_ID;
658                def.name = "Default";
659                def.nonFactionCategory = true;
660                def.nameColor = Misc.getBasePlayerColor();
661                def.iconName = Global.getSettings().getSpriteName("simulator", "defaultPlayerCrest");
662                def.data = null;
663                def.faction = createCustomFaction();
664                def.variants = getVariantIDList(sortVariantList(getVariantList(
665                                new LinkedHashSet<String>(Global.getSettings().getSimOpponents()))));
666                result.add(0, def);
667                
668                other.removeAll(def.variants);
669                
670                if (!other.isEmpty()) {
671                        SimCategoryData otherCat = new SimCategoryData();
672                        otherCat.id = OTHER_CAT_ID;
673                        otherCat.name = "Other";
674                        otherCat.nonFactionCategory = true;
675                        otherCat.nameColor = Misc.getBasePlayerColor();
676                        otherCat.iconName = Global.getSettings().getSpriteName("simulator", "otherPlayerCrest");
677                        otherCat.data = null;
678                        otherCat.faction = createCustomFaction();
679                        otherCat.variants = getVariantIDList(sortVariantList(getVariantList(other)));
680                        otherCat.faction = createCustomFaction();
681                        if (!otherCat.variants.isEmpty()) {
682                                result.add(otherCat);
683                        }
684                }
685                
686                if (!civilian.isEmpty()) {
687                        FactionSpecAPI neutral = Global.getSettings().getFactionSpec(Factions.NEUTRAL);
688                        SimCategoryData civ = new SimCategoryData();
689                        civ.id = CIV_CAT_ID;
690                        civ.name = "Civilian ships";
691                        civ.nonFactionCategory = true;
692                        civ.nameColor = neutral.getBaseUIColor();
693                        civ.iconName = neutral.getCrest();
694                        civ.data = null;
695                        civ.variants = getVariantIDList(sortVariantList(getVariantList(civilian)));
696                        civ.faction = createCustomFaction();
697                        if (!civ.variants.isEmpty()) {
698                                result.add(civ);
699                        }
700                }
701                
702                if (isShowDevCategories()) {
703                        SimCategoryData dev = new SimCategoryData();
704                        dev.id = DEV_CAT_ID;
705                        dev.name = "DevMode stuff";
706                        dev.nonFactionCategory = true;
707                        dev.nameColor = Misc.getBasePlayerColor();
708                        dev.iconName = Global.getSettings().getSpriteName("simulator", "devModeVariantsIcon");
709                        dev.data = null;
710                        dev.faction = createCustomFaction();
711                        dev.variants = getVariantIDList(sortVariantList(getVariantList(
712                                                        new LinkedHashSet<String>(Global.getSettings().getSimOpponentsDev()))));
713                        result.add(1, dev);
714                }
715                
716                return result;
717        }
718
719        public List<String> getVariants(FactionSpecAPI spec, boolean withCiv, boolean onlyCiv, boolean forceFullUnlock) {
720                if (spec == null) return new ArrayList<String>();
721                
722                FactionAPI faction = Global.getSettings().createBaseFaction(spec.getId());
723                
724                boolean fullUnlock = isSimFullyUnlocked() || forceFullUnlock || isAllStandardStuffUnlocked();
725                //fullUnlock = true;
726                
727                Set<String> seen = new LinkedHashSet<String>();
728                List<ShipVariantAPI> variantList = new ArrayList<ShipVariantAPI>();
729                for (String roleId : getAllRoles()) {
730                        Set<String> variants = faction.getVariantsForRole(roleId);
731                        if (variants == null) continue;
732                        for (String variantId : variants) {
733                                if (seen.contains(variantId)) continue;
734                                seen.add(variantId);
735//                              if (variantId.equals("onslaught_Standard")) {
736//                                      System.out.println("3f23few");
737//                              }
738                                if (!fullUnlock && !unlocksData.variants.contains(variantId) && 
739                                                !defaultOpponents.contains(variantId)) {
740                                        continue;
741                                }
742                                
743                                ShipVariantAPI v = Global.getSettings().getVariant(variantId);
744                                if (!isAcceptableSimVariant(v, false)) continue;
745                                if (!v.isStockVariant()) continue;
746                                
747//                              if (v == null || !v.isStockVariant()) continue;
748//                              //if (v.isFighter()) continue;
749//                              
750//                              //if (v.getHullSpec().getHints().contains(ShipTypeHints.HIDE_IN_CODEX)) continue;
751//                              if (v.getHullSpec().hasTag(Tags.NO_SIM) || v.hasTag(Tags.NO_SIM)) continue;
752//                              if (v.getHullSpec().hasTag(Tags.RESTRICTED)) continue;
753                                
754                                boolean civ = v.isCivilian();
755                                if (civ && !withCiv) continue;
756                                if (!civ && onlyCiv) continue;
757                                
758                                variantList.add(v);
759                        }
760                }
761                
762                sortVariantList(variantList);
763                
764                return getVariantIDList(variantList);
765        }
766        
767        
768        public static List<ShipVariantAPI> getVariantList(Set<String> variants) {
769                List<ShipVariantAPI> variantList = new ArrayList<ShipVariantAPI>();
770                for (String id : variants) {
771                        ShipVariantAPI v = Global.getSettings().getVariant(id);
772                        if (v == null || !v.isStockVariant()) continue;
773                        variantList.add(v);
774                }
775                return variantList;
776        }
777        public static List<String> getVariantIDList(List<ShipVariantAPI> variantList) {
778                List<String> variants = new ArrayList<String>();
779                for (ShipVariantAPI v : variantList) {
780                        variants.add(v.getHullVariantId());
781                }
782                return variants;
783        }
784        
785        public static List<ShipVariantAPI> sortVariantList(List<ShipVariantAPI> variantList) {
786                Collections.sort(variantList, new Comparator<ShipVariantAPI>() {
787                        public int compare(ShipVariantAPI v1, ShipVariantAPI v2) {
788                                if (v1.isCivilian() && !v2.isCivilian()) return 1;
789                                if (v2.isCivilian() && !v1.isCivilian()) return -1;
790                                
791                                if (v1.getHullSize().ordinal() < v2.getHullSize().ordinal()) return 1;
792                                if (v1.getHullSize().ordinal() > v2.getHullSize().ordinal()) return -1;
793                                
794                                int diff = (int) (v1.getHullSpec().getSuppliesToRecover() - v2.getHullSpec().getSuppliesToRecover());
795                                if (diff != 0) return (int) Math.signum(-diff);
796                                
797                                return v1.getHullSpec().getHullName().compareTo(v2.getHullSpec().getHullName());
798                        }
799                });
800                return variantList;
801        }
802        
803        
804        public static List<String> getAllRoles() {
805                List<String> result = new ArrayList<String>();
806                result.add(ShipRoles.COMBAT_SMALL);
807                result.add(ShipRoles.COMBAT_MEDIUM);
808                result.add(ShipRoles.COMBAT_LARGE);
809                result.add(ShipRoles.COMBAT_CAPITAL);
810                result.add(ShipRoles.COMBAT_FREIGHTER_SMALL);
811                result.add(ShipRoles.COMBAT_FREIGHTER_MEDIUM);
812                result.add(ShipRoles.COMBAT_FREIGHTER_LARGE);
813                
814                result.add(ShipRoles.CIV_RANDOM);
815                
816                result.add(ShipRoles.PHASE_SMALL);
817                result.add(ShipRoles.PHASE_MEDIUM);
818                result.add(ShipRoles.PHASE_LARGE);
819                        
820                result.add(ShipRoles.PHASE_CAPITAL);
821                        
822                result.add(ShipRoles.CARRIER_SMALL);
823                result.add(ShipRoles.CARRIER_MEDIUM);
824                result.add(ShipRoles.CARRIER_LARGE);
825                result.add(ShipRoles.FREIGHTER_SMALL);
826                result.add(ShipRoles.FREIGHTER_MEDIUM);
827                result.add(ShipRoles.FREIGHTER_LARGE);
828                result.add(ShipRoles.TANKER_SMALL);
829                result.add(ShipRoles.TANKER_MEDIUM);
830                result.add(ShipRoles.TANKER_LARGE);
831                result.add(ShipRoles.PERSONNEL_SMALL);
832                result.add(ShipRoles.PERSONNEL_MEDIUM);
833                result.add(ShipRoles.PERSONNEL_LARGE);
834                result.add(ShipRoles.LINER_SMALL);
835                result.add(ShipRoles.LINER_MEDIUM);
836                result.add(ShipRoles.LINER_LARGE);
837                result.add(ShipRoles.TUG);
838                result.add(ShipRoles.CRIG);
839                result.add(ShipRoles.UTILITY);
840                
841                return result;
842        }
843
844
845
846        public void applySettingsToFleetMembers(List<FleetMemberAPI> members,
847                                                SimCategoryData category, Map<String, String> settings) {
848        
849                boolean custom = CUSTOM_CAT_ID.equals(category.id);
850                boolean defaultCat = DEFAULT_CAT_ID.equals(category.id);
851                boolean civ = CIV_CAT_ID.equals(category.id);
852                boolean dev = DEV_CAT_ID.equals(category.id);
853                boolean customFaction = custom || civ || dev || defaultCat; 
854                
855                FactionAPI faction = category.faction;
856                
857                String officers = settings.get(OFFICERS_ID);
858                if (officers == null) officers = settings.get(OFFICERS_CUSTOM_ID);
859
860                if (officers != null) {
861                        if (!officers.equals(OFFICERS_NONE) && !officers.equals(OFFICERS_CUSTOM_NONE)) {
862                                CampaignFleetAPI fleetNonAuto = Global.getFactory().createEmptyFleet(faction, true);
863                                for (FleetMemberAPI member : members) {
864                                        if (Misc.isAutomated(member)) continue;
865                                        fleetNonAuto.getFleetData().addFleetMember(member);
866                                }
867                                FleetParamsV3 params = new FleetParamsV3();
868                                boolean all = false;
869                                if (officers.equals(OFFICERS_ALL)) {
870                                        all = true;
871                                        params.officerNumberBonus = 1000;
872                                        params.officerLevelBonus = 10;
873                                } else if (officers.equals(OFFICERS_CUSTOM_5)) {
874                                        all = true;
875                                        params.officerNumberBonus = 1000;
876                                        params.officerLevelBonus = 10;
877                                        params.officerLevelLimit = 5;
878                                } else if (officers.equals(OFFICERS_CUSTOM_6)) {
879                                        all = true;
880                                        params.officerNumberBonus = 1000;
881                                        params.officerLevelBonus = 10;
882                                        params.commander = Global.getFactory().createPerson();
883                                        params.commander.getStats().setSkillLevel(Skills.OFFICER_TRAINING, 1);
884                                        params.officerLevelLimit = 6;
885                                }
886                                FleetFactoryV3.addCommanderAndOfficersV2(fleetNonAuto, params, new Random(), true, all);
887                        }
888                }
889                
890                
891                String cores = settings.get(AI_CORES_ID);
892                boolean derelict = false;
893                if (cores == null) {
894                        cores = settings.get(AI_CORES_DERELICT_ID);
895                        if (cores != null) derelict = true;
896                }
897                if (cores == null) cores = settings.get(AI_CORES_OMEGA_ID);
898                if (cores == null) cores = settings.get(AI_CORES_DEV_ID);
899                
900                if (cores != null) {
901                        if (!cores.equals(AI_CORES_NONE)) {
902                                CampaignFleetAPI fleetAuto = Global.getFactory().createEmptyFleet(faction, true);
903                                for (FleetMemberAPI member : members) {
904                                        if (!Misc.isAutomated(member)) continue;
905                                        fleetAuto.getFleetData().addFleetMember(member);
906                                }
907                                FleetParamsV3 params = new FleetParamsV3();
908                                params.doNotIntegrateAICores = true;
909                                boolean all = false;
910                                boolean omega = false;
911                                if (cores.equals(AI_CORES_SOME)) {
912                                        if (derelict) {
913                                                params.aiCores = OfficerQuality.AI_GAMMA;
914                                        } else {
915                                                params.aiCores = OfficerQuality.AI_MIXED;
916                                        }
917                                } else if (cores.equals(AI_CORES_GAMMA)) {
918                                        params.officerNumberBonus = 1000;
919                                        params.aiCores = OfficerQuality.AI_GAMMA;
920                                        all = true;
921                                } else if (cores.equals(AI_CORES_BETA)) {
922                                        params.officerNumberBonus = 1000;
923                                        params.aiCores = OfficerQuality.AI_BETA;
924                                        all = true;
925                                } else if (cores.equals(AI_CORES_ALPHA)) {
926                                        params.officerNumberBonus = 1000;
927                                        params.aiCores = OfficerQuality.AI_ALPHA;
928                                        all = true;
929                                } else if (cores.equals(AI_CORES_OMEGA)) {
930                                        params.officerNumberBonus = 1000;
931                                        params.aiCores = OfficerQuality.AI_OMEGA;
932                                        all = true;
933                                        omega = true;
934                                }
935                                
936                                // pass in derelictMode = false regardless of derelict or not, since if it's true
937                                // RemnantOfficerGeneratorPlugin does some stuff potentially undesired here
938                                // Setting it to AI_GAMMA above does the job, anyway
939                                RemnantOfficerGeneratorPlugin genPlugin = new RemnantOfficerGeneratorPlugin(false, 1f);
940                                if (settings.containsKey(INTEGRATE_CORES_ID) && settings.get(INTEGRATE_CORES_ID).toLowerCase().equals("true")) {
941                                        if (!omega) {
942                                                genPlugin.setForceIntegrateCores(true);
943                                        }
944                                }
945                                genPlugin.setPutCoresOnCivShips(all);
946                                genPlugin.setForceNoCommander(true);
947                                genPlugin.addCommanderAndOfficers(fleetAuto, params, new Random());
948                                
949                                pruneAICoresToAvailable(members);
950                        }
951                }
952                
953                String personality = null;
954                if (settings.containsKey(AGGRO_ID) || settings.containsKey(AGGRO_ID_CORES_ONLY)) {
955                        String aggro = (String) settings.get(AGGRO_ID);
956                        if (aggro == null) aggro = (String) settings.get(AGGRO_ID_CORES_ONLY);
957                        if (aggro.equals(AGGRO_CAUTIOUS)) {
958                                personality = Personalities.CAUTIOUS;
959                        } else if (aggro.equals(AGGRO_STEADY)) {
960                                personality = Personalities.STEADY;
961                        } else if (aggro.equals(AGGRO_AGGRESSIVE)) {
962                                personality = Personalities.AGGRESSIVE;
963                        } else if (aggro.equals(AGGRO_RECKLESS)) {
964                                personality = Personalities.RECKLESS;
965                        } 
966                }
967                
968                if (personality != null) {
969                        for (FleetMemberAPI member : members) {
970                                if (Misc.isAutomated(member)) continue;
971                                
972                                PersonAPI captain = member.getCaptain();
973                                captain.setPersonality(personality);
974                                member.setPersonalityOverride(personality);
975                        }
976                }
977                
978                
979                CampaignFleetAPI fleet = Global.getFactory().createEmptyFleet(faction, true);
980                for (FleetMemberAPI member : members) {
981                        fleet.getFleetData().addFleetMember(member);
982                }
983
984                if (settings.containsKey(RANDOMIZE_VARIANTS_ID) && settings.get(RANDOMIZE_VARIANTS_ID).toLowerCase().equals("true")) {
985                        DefaultFleetInflaterParams params = new DefaultFleetInflaterParams();
986                        params.quality = 2f; // don't add d-mods
987
988                        DefaultFleetInflater inflater = new DefaultFleetInflater(params);
989                        inflater.inflate(fleet);
990                }
991                
992                
993                if (settings.containsKey(QUALITY_ID)) {
994                        for (FleetMemberAPI member : members) {
995                                if (member.getVariant().isStockVariant()) {
996                                        ShipVariantAPI copy = member.getVariant().clone();
997                                        copy.setSource(VariantSource.REFIT);
998                                        member.setVariant(copy, false, false);
999                                }
1000                        }
1001                        
1002                        String quality = (String) settings.get(QUALITY_ID);
1003                        if (quality.equals(QUALITY_NO_DMODS)) {
1004                                // nothing to do
1005                        } else if (quality.equals(QUALITY_MAX_DMODS)) {
1006                                for (FleetMemberAPI member : members) {
1007                                        DModManager.addDMods(member, true, 5, null);
1008                                }
1009                        } else if (quality.equals(QUALITY_SOME_DMODS)) {
1010                                Random random = new Random();
1011                                for (FleetMemberAPI member : members) {
1012                                        int num = 2 + random.nextInt(3);
1013                                        DModManager.addDMods(member, true, num, null);
1014                                }
1015                        } else if (quality.equals(QUALITY_SOME_SDMODS)) {
1016                                Random random = new Random();
1017                                CoreAutofitPlugin plugin = new CoreAutofitPlugin(null);
1018                                for (FleetMemberAPI member : members) {
1019                                        int num = 1 + random.nextInt(2);
1020                                        plugin.addSMods(member, num, this);
1021                                }
1022                        } else if (quality.equals(QUALITY_MANY_SMODS)) {
1023                                Random random = new Random();
1024                                CoreAutofitPlugin plugin = new CoreAutofitPlugin(null);
1025                                for (FleetMemberAPI member : members) {
1026                                        int num = 2 + random.nextInt(2);
1027                                        plugin.addSMods(member, num, this);
1028                                }
1029                        }
1030                }
1031                
1032//              for (FleetMemberAPI member : members) {
1033//                      member.setFlagship(false, false);
1034//              }
1035//              fleet.getFleetData().setFlagship(null);
1036                
1037                // not strictly needed anymore after changing FleetFactoryV3.addCommanderAndOfficersV2()
1038                // to make a "fake" commander
1039                // but just in case, to make sure fleetwide skills don't apply
1040                //makeFleetCommanderNormalOfficer(members);             
1041                fleet.setCommander(Global.getFactory().createPerson());
1042        }
1043
1044        @Override
1045        public void applySettingsToDeployed(List<DeployedFleetMemberAPI> deployed, Map<String, String> settings) {
1046                if (settings.containsKey(AGGRO_ID) || settings.containsKey(AGGRO_ID_CORES_ONLY)) {
1047                        String aggro = (String) settings.get(AGGRO_ID);
1048                        if (aggro == null) aggro = (String) settings.get(AGGRO_ID_CORES_ONLY);
1049                        final String aggro2 = aggro;
1050                        if (aggro.equals(AGGRO_DO_NOTHING)) {
1051                                for (DeployedFleetMemberAPI member : deployed) {
1052                                        final ShipAPI ship = member.getShip();
1053                                        if (ship == null) continue;
1054                                        
1055                                        if (ship.getOwner() == 0) continue;
1056                                        
1057                                        ship.setShipAI(null);
1058                                        ship.setHoldFire(true);
1059                                        ship.getLocation().y -= 2000f;
1060                                        Global.getCombatEngine().addPlugin(new BaseEveryFrameCombatPlugin() {
1061                                                protected float elapsed = 0f;
1062                                                @Override
1063                                                public void advance(float amount, List<InputEventAPI> events) {
1064                                                        elapsed += amount;
1065                                                        if (ship.getTravelDrive() != null) {
1066                                                                ship.getTravelDrive().deactivate();
1067                                                        }
1068                                                        if (elapsed > 0.1f) {
1069                                                                ship.getVelocity().set(0, 0);
1070                                                                elapsed = -10000000f;
1071                                                        }
1072                                                        
1073                                                        ship.giveCommand(ShipCommand.DECELERATE, null, 0);
1074                                                        
1075                                                        if (ship.isHulk()) {
1076                                                                Global.getCombatEngine().removePlugin(this);
1077                                                        }
1078                                                }
1079                                                
1080                                        });
1081                                }
1082                        } else if (aggro.equals(AGGRO_DEFENSES) || aggro.equals(AGGRO_STATIONARY)) {
1083                                final boolean defensesOnly = aggro.equals(AGGRO_DEFENSES);
1084                                for (DeployedFleetMemberAPI member : deployed) {
1085                                        final ShipAPI ship = member.getShip();
1086                                        if (ship == null) continue;
1087                                        
1088                                        if (ship.getOwner() == 0) continue;
1089                                        
1090                                        ship.getLocation().y -= 2000f;
1091                                        Global.getCombatEngine().addPlugin(new BaseEveryFrameCombatPlugin() {
1092                                                protected float elapsed = 0f;
1093                                                @Override
1094                                                public void advance(float amount, List<InputEventAPI> events) {
1095                                                        elapsed += amount;
1096                                                        if (ship.getTravelDrive() != null && ship.getTravelDrive().isActive()) {
1097                                                                ship.getTravelDrive().deactivate();
1098                                                        }
1099                                                        if (elapsed > 0.1f) {
1100                                                                ship.getVelocity().set(0, 0);
1101                                                                elapsed = -10000000f;
1102                                                        }
1103                                                        
1104                                                        List<ShipAPI> all = new ArrayList<>(ship.getChildModulesCopy());
1105                                                        all.add(ship);
1106                                                        
1107                                                        for (ShipAPI curr : all) {
1108                                                                if (aggro2.equals(AGGRO_DEFENSES)) {
1109                                                                        curr.setHoldFire(true);
1110                                                                }
1111                                                                curr.giveCommand(ShipCommand.DECELERATE, null, 0);
1112                                                                
1113                                                                curr.blockCommandForOneFrame(ShipCommand.ACCELERATE);
1114                                                                curr.blockCommandForOneFrame(ShipCommand.ACCELERATE_BACKWARDS);
1115                                                                curr.blockCommandForOneFrame(ShipCommand.STRAFE_LEFT);
1116                                                                curr.blockCommandForOneFrame(ShipCommand.STRAFE_RIGHT);
1117                                                                
1118                                                                if (defensesOnly) {
1119                                                                        curr.blockCommandForOneFrame(ShipCommand.FIRE);
1120                                                                        curr.blockCommandForOneFrame(ShipCommand.VENT_FLUX);
1121                                                                        curr.blockCommandForOneFrame(ShipCommand.PULL_BACK_FIGHTERS);
1122                                                                        curr.blockCommandForOneFrame(ShipCommand.TOGGLE_AUTOFIRE);
1123                                                                        curr.blockCommandForOneFrame(ShipCommand.HOLD_FIRE);
1124                                                                        
1125                                                                        if (curr.getSystem() != null && curr.getSystem().getSpecAPI() != null) {
1126                                                                                ShipSystemSpecAPI spec = curr.getSystem().getSpecAPI();
1127                                                                                if (!spec.hasTag(Tags.SHIP_SYSTEM_DEFENSIVE) ||
1128                                                                                                spec.hasTag(Tags.SHIP_SYSTEM_OFFENSIVE) || 
1129                                                                                                spec.hasTag(Tags.SHIP_SYSTEM_MOVEMENT)) {
1130                                                                                        curr.blockCommandForOneFrame(ShipCommand.USE_SYSTEM);
1131                                                                                }
1132                                                                        }
1133                                                                } else {
1134                                                                        if (curr.getSystem() != null && curr.getSystem().getSpecAPI() != null) {
1135                                                                                ShipSystemSpecAPI spec = curr.getSystem().getSpecAPI();
1136                                                                                if (spec.hasTag(Tags.SHIP_SYSTEM_MOVEMENT)) {
1137                                                                                        curr.blockCommandForOneFrame(ShipCommand.USE_SYSTEM);
1138                                                                                }
1139                                                                        }
1140                                                                }
1141                                                        }
1142                                                        
1143                                                        if (ship.isHulk()) {
1144                                                                Global.getCombatEngine().removePlugin(this);
1145                                                        }
1146                                                }
1147                                                
1148                                        });
1149                                }
1150                        } 
1151                }
1152                
1153        }
1154        
1155        
1156        public static FactionAPI createCustomFaction() {
1157                FactionAPI faction = Global.getSettings().createBaseFaction(Factions.INDEPENDENT);
1158                
1159
1160                FactionDoctrineAPI d = faction.getDoctrine();
1161                d.setAggression(2);
1162                d.setCarriers(2);
1163                d.setPhaseShips(1);
1164                d.setWarships(4);
1165                
1166                d.setAutofitRandomizeProbability(0.25f);
1167                d.setNumShips(3);
1168                d.setOfficerQuality(3);
1169                d.setShipSize(3);
1170                
1171                FactionAPI merc = Global.getSettings().createBaseFaction(Factions.MERCENARY);
1172                
1173                for (String id : merc.getKnownWeapons()) {
1174                        faction.addKnownWeapon(id, false);
1175                }
1176                for (String id : merc.getKnownFighters()) {
1177                        faction.addKnownFighter(id, false);
1178                }
1179                for (String id : merc.getKnownHullMods()) {
1180                        faction.addKnownHullMod(id);
1181                }
1182                
1183                faction.getPriorityWeapons().clear();
1184                faction.getPriorityFighters().clear();
1185                
1186                faction.getHullFrequency().clear();
1187                
1188                return faction;
1189        }
1190        
1191        
1192        public static void makeFleetCommanderNormalOfficer(List<FleetMemberAPI> members) {
1193                int maxLevel = (int) Global.getSettings().getFloat("officerMaxLevel");
1194                for (FleetMemberAPI member : members) {
1195                        PersonAPI captain = member.getCaptain();
1196                        if (!member.isFlagship() && !captain.isDefault()) {
1197                                maxLevel = Math.max(maxLevel, captain.getStats().getLevel());
1198                        }
1199                }
1200                for (FleetMemberAPI member : members) {
1201                        if (member.isFlagship()) {
1202                                PersonAPI captain = member.getCaptain();
1203                                member.setFlagship(false, false);
1204                                captain.getStats().setLevel(Math.min(captain.getStats().getLevel(), maxLevel));
1205                                for (String skillId : Global.getSettings().getSkillIds()) {
1206                                        SkillSpecAPI skill = Global.getSettings().getSkillSpec(skillId);
1207                                        if (skill.isAdmiralSkill()) {
1208                                                captain.getStats().setSkillLevel(skillId, 0);
1209                                        }
1210                                }
1211                                break;
1212                        }
1213                }
1214        }
1215        
1216        
1217        public List<String> generateSelection(SimCategoryData category, int deploymentPoints) {
1218                List<String> result = new ArrayList<String>();
1219                if (category.variants.isEmpty()) return result;
1220                
1221                FactionAPI faction = Global.getSettings().createBaseFaction(category.faction.getId());
1222                
1223                category.faction.getDoctrine().copyToDoctrine(faction.getDoctrine());
1224                
1225                //faction.getDoctrine().setShipSize(5);
1226                //faction.getDoctrine().setCarriers(10);
1227                
1228                faction.clearShipRoleCache();
1229                faction.getRestrictToVariants().clear();
1230                faction.getKnownShips().clear();
1231                
1232                for (String variantId : category.variants) {
1233                        ShipVariantAPI v = Global.getSettings().getVariant(variantId);
1234                        if (v == null || !v.isStockVariant()) continue;
1235                        faction.addKnownShip(v.getHullSpec().getHullId(), false);
1236                        faction.getRestrictToVariants().add(variantId);
1237                }
1238                
1239                FleetParamsV3 params = new FleetParamsV3(
1240                                null,
1241                                Factions.INDEPENDENT,
1242                                2f, // quality - null to determine from producer/source markets and doctrine
1243                                FleetTypes.PATROL_LARGE,
1244                                deploymentPoints * 1f, // combatPts
1245                                0f, // freighterPts 
1246                                0f * 0.5f, // tankerPts
1247                                0f * 0.5f, // transportPts
1248                                0f, // linerPts
1249                                0f, // utilityPts
1250                                1f // qualityMod
1251                                );
1252                params.maxNumShips = 100;
1253                params.factionOverride = faction;
1254                params.ignoreMarketFleetSizeMult = true;
1255                params.withOfficers = false;
1256                params.modeOverride = ShipPickMode.PRIORITY_THEN_ALL;
1257                
1258                CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params);
1259//              CampaignFleetAPI fleet2 = FleetFactoryV3.createFleet(params);
1260//              for (FleetMemberAPI member : fleet2.getFleetData().getMembersListCopy()) {
1261//                      fleet.getFleetData().addFleetMember(member);
1262//              }
1263                pruneFleetDownToDP(fleet, deploymentPoints, new Random());
1264                
1265                for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
1266                        ShipVariantAPI v = member.getVariant();
1267                        if (v != null && v.isStockVariant()) { // && v.getHullSize() == HullSize.FRIGATE) {
1268                                result.add(v.getHullVariantId());
1269                        }
1270                }
1271                
1272//              result.add("onslaught_Standard");
1273//              result.add("onslaught_Standard");
1274                
1275                return result;
1276        }
1277        
1278        
1279        
1280        
1281
1282        public void fitFighterInSlot(int index, AvailableFighter fighter, ShipVariantAPI variant) {}
1283        public void clearFighterSlot(int index, ShipVariantAPI variant) {}
1284        public void fitWeaponInSlot(WeaponSlotAPI slot, AvailableWeapon weapon, ShipVariantAPI variant) {}
1285        public void clearWeaponSlot(WeaponSlotAPI slot, ShipVariantAPI variant) {}
1286        public List<AvailableWeapon> getAvailableWeapons() {return new ArrayList<AvailableWeapon>();}
1287        public List<AvailableFighter> getAvailableFighters() {return new ArrayList<AvailableFighter>();}
1288        public boolean isPriority(WeaponSpecAPI weapon) {return false;}
1289        public boolean isPriority(FighterWingSpecAPI wing) {return false;}
1290        public void syncUIWithVariant(ShipVariantAPI variant) {}
1291        public ShipAPI getShip() {return null;}
1292        public FactionAPI getFaction() {return null;}
1293        public boolean isAllowSlightRandomization() {return false;}
1294        public boolean isPlayerCampaignRefit() {return false;}
1295        public boolean canAddRemoveHullmodInPlayerCampaignRefit(String modId) {return true;}
1296
1297        public List<String> getAvailableHullmods() {
1298                List<String> ids = new ArrayList<String>();
1299                for (HullModSpecAPI mod : Global.getSettings().getAllHullModSpecs()) {
1300                        ids.add(mod.getId());
1301                }
1302                return ids;
1303        }
1304
1305        
1306        public static void pruneFleetDownToDP(CampaignFleetAPI fleet, float targetDP, Random random) {
1307                float currDP = getDP(fleet);
1308                if (currDP > targetDP) {
1309                        fleet.getFleetData().sort();
1310                        
1311                        float fpRem = currDP - targetDP;
1312                        
1313                        while (fpRem > 0) {
1314                                List<FleetMemberAPI> copy = fleet.getFleetData().getMembersListCopy();
1315                                CountingMap<HullSize> counts = new CountingMap<HullSize>();
1316                                for (FleetMemberAPI curr : copy) {
1317                                        counts.add(curr.getHullSpec().getHullSize());
1318                                }
1319                                WeightedRandomPicker<FleetMemberAPI> picker =  new WeightedRandomPicker<FleetMemberAPI>(random);
1320                                for (FleetMemberAPI curr : copy) {
1321                                        float dp = curr.getDeploymentPointsCost();
1322                                        if (dp <= fpRem) {
1323                                                int count = counts.getCount(curr.getHullSpec().getHullSize());
1324                                                float mult = 1f;
1325                                                if (count <= 1) {
1326                                                        mult = 0.0001f;
1327                                                } else {
1328                                                        mult = count;
1329                                                }
1330                                                picker.add(curr, dp * mult);
1331                                        }
1332                                }
1333                                FleetMemberAPI pick = picker.pick();
1334                                if (pick == null) break;
1335                                
1336                                float dp = pick.getDeploymentPointsCost();
1337                                fpRem -= dp;
1338                                fleet.getFleetData().removeFleetMember(pick);
1339                        }
1340                }
1341        }
1342        
1343        public static float getDP(CampaignFleetAPI fleet) {
1344                float total = 0f;
1345                for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
1346                        total += member.getDeploymentPointsCost();
1347                }
1348                return total;
1349        }
1350
1351        public void reportPlayerBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle) {
1352                if (!loadedStuff) {
1353                        loadCustomOpponents();
1354                        loadUnlocksData();
1355                        loadedStuff = true;
1356                }
1357                
1358                //CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
1359                
1360//              unlocksData.factions.clear();
1361//              unlocksData.variants.clear();
1362                //unlocksData.variants.addAll(Global.getSettings().getSimOpponents());
1363                
1364                LinkedHashSet<String> addedFactions = new LinkedHashSet<String>(); 
1365                LinkedHashSet<String> addedVariants = new LinkedHashSet<String>(); 
1366                
1367                for (CampaignFleetAPI fleet : battle.getNonPlayerSideSnapshot()) {
1368                        if (fleet.getFaction() == null ||
1369                                        fleet.getFaction().getFactionSpec() == null ||
1370                                        fleet.getFaction().getFactionSpec().getCustom() == null) continue;
1371                        JSONObject json = fleet.getFaction().getFactionSpec().getCustom().optJSONObject("simulatorData");
1372                        if (json == null) continue;
1373                        boolean show = json.optBoolean("showInSimulator");
1374                        if (!show) continue;
1375                        
1376                        List<FleetMemberAPI> members = Misc.getSnapshotMembersLost(fleet);
1377                        String fid = findBestMatchingFaction(fleet.getFaction().getId(), members);
1378                        
1379                        if (!unlocksData.factions.contains(fid)) {
1380                                unlocksData.factions.add(fid);
1381                                addedFactions.add(fid);
1382                        }
1383
1384                        for (FleetMemberAPI member : members) {
1385//                              if ("LGS Dorus".equals(member.getShipName())) {
1386//                                      System.out.println("ewfwefewfwefe");
1387//                              }
1388                                String vid = getStockVariantId(member);
1389                                
1390//                              if (vid != null && vid.contains("executor")) {
1391//                                      System.out.println("fwfewfe");
1392//                              }
1393                                if (vid != null) {
1394                                        if (!unlocksData.variants.contains(vid)) {
1395                                                unlocksData.variants.add(vid);
1396                                                addedVariants.add(vid);
1397                                        }               
1398                                }
1399                        }
1400                }
1401                
1402                if (!addedVariants.isEmpty()) {
1403                        addedVariants = new LinkedHashSet<String>(getVariantIDList(sortVariantList(getVariantList(
1404                                        new LinkedHashSet<String>(addedVariants)))));
1405                }
1406                
1407                if (!addedFactions.isEmpty() || !addedVariants.isEmpty()) {
1408                        saveUnlocksData();
1409                        
1410                        new SimUpdateIntel(addedFactions, addedVariants);
1411                }
1412        }
1413        
1414        public boolean isAcceptableSimVariant(ShipVariantAPI v, boolean forLearning) {
1415                boolean allowAll = isSimFullyUnlocked() && !forLearning;
1416                if (v == null) return false;
1417                if ((v.getHullSpec().hasTag(Tags.NO_SIM) || v.hasTag(Tags.NO_SIM)) && !allowAll) return false;
1418                if (v.getHullSpec().hasTag(Tags.RESTRICTED) && !allowAll) return false;
1419                if (v.getHullSpec().getHints().contains(ShipTypeHints.STATION)) return false;
1420                return true;
1421        }
1422        
1423        public String getStockVariantId(FleetMemberAPI member) {
1424                ShipVariantAPI v = member.getVariant();
1425                if (!isAcceptableSimVariant(v, true)) return null;
1426                
1427                String vid = null;
1428                if (v.isStockVariant()) {
1429                        vid = v.getHullVariantId();
1430                }
1431                if (vid == null && v.getOriginalVariant() != null) {
1432                        vid = v.getOriginalVariant();
1433                }
1434                return vid;
1435        }
1436        
1437        public String findBestMatchingFaction(String fleetFactionId, List<FleetMemberAPI> members) {
1438                
1439                List<String> roles = getAllRoles();
1440                
1441                FactionAPI best = null;
1442                float bestScore = 0f;
1443                
1444                for (FactionSpecAPI spec : Global.getSettings().getAllFactionSpecs()) {
1445                        if (spec == null || spec.getCustom() == null) continue;
1446                        
1447                        JSONObject json = spec.getCustom().optJSONObject("simulatorData");
1448                        if (json == null) continue;
1449                        
1450                        boolean show = json.optBoolean("showInSimulator");
1451                        if (!show) continue;
1452                        
1453                        FactionAPI faction = Global.getSector().getFaction(spec.getId());
1454                        
1455                        Set<String> allVariants = new LinkedHashSet<String>();
1456                        for (String roleId : roles) {
1457                                allVariants.addAll(faction.getVariantsForRole(roleId));
1458                        }
1459                        
1460                        float matches = 0f;
1461                        float total = 0f;
1462                        for (FleetMemberAPI member : members) {
1463                                String vid = getStockVariantId(member);
1464                                if (vid == null) continue;
1465                                
1466                                if (allVariants.contains(vid)) {
1467                                        matches++;
1468                                }
1469                                total++;
1470                        }
1471                        total = Math.max(total, 1f);
1472                        float score = matches / total;
1473                        if (faction.getId().equals(fleetFactionId)) {
1474                                score *= 1.1f;
1475                        }
1476                        if (score > bestScore) {
1477                                bestScore = score;
1478                                best = faction;
1479                        }
1480                }
1481                
1482                if (best == null || bestScore <= 0) return fleetFactionId;
1483                return best.getId();
1484        }
1485        
1486        public void pruneAICoresToAvailable(List<FleetMemberAPI> members) {
1487                if (!isRequireAICoresInCargo()) return;
1488                
1489                CountingMap<String> availableCores = getAvailableMinusDeployedAICores();
1490                
1491                CountingMap<String> current = new CountingMap<String>();
1492                for (FleetMemberAPI member : members) {
1493                        String coreId = getCoreId(member);
1494                        if (coreId == null) continue;
1495                        
1496                        current.add(coreId, 1);
1497                }
1498                
1499                CountingMap<String> remove = new CountingMap<String>();
1500                remove.putAll(current);
1501                for (String id : availableCores.keySet()) {
1502                        remove.sub(id, availableCores.getCount(id));
1503                }
1504                
1505                if (remove.isEmpty()) return;
1506        
1507                WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>();
1508                for (FleetMemberAPI member : members) {
1509                        String coreId = getCoreId(member);
1510                        if (coreId == null) continue;
1511                        
1512                        if (remove.getCount(coreId) > 0) {
1513                                picker.add(member, member.getDeploymentPointsCost());
1514                        }
1515                }
1516                
1517                while (!remove.isEmpty() && !picker.isEmpty()) {
1518                        FleetMemberAPI member = picker.pickAndRemove();
1519                        
1520                        String coreId = getCoreId(member);
1521                        if (remove.getCount(coreId) > 0) {
1522                                remove.sub(coreId, 1);
1523                                member.setCaptain(Global.getFactory().createPerson());
1524                        }
1525                }
1526        }
1527        
1528        public String getCoreId(FleetMemberAPI member) {
1529                if (member == null) return null;
1530                PersonAPI captain = member.getCaptain();
1531                if (captain == null || captain.isDefault() || !captain.isAICore()) return null;;
1532                return captain.getAICoreId();
1533        }
1534
1535        public CountingMap<String> getAvailableMinusDeployedAICores() {
1536                CountingMap<String> cargoCores = getAvailableAICores();
1537                CountingMap<String> deployedCores = getDeployedAICores();
1538                CountingMap<String> availableCores = new CountingMap<String>();
1539                
1540                availableCores.putAll(cargoCores);
1541                for (String id : deployedCores.keySet()) {
1542                        availableCores.sub(id, deployedCores.getCount(id));
1543                }
1544                return availableCores;
1545        }
1546        
1547        public CountingMap<String> getDeployedAICores() {
1548                // only checking enemy side: in campaign, these settings don't affect own side
1549                CountingMap<String> map = new CountingMap<String>();
1550                for (DeployedFleetMemberAPI member : Global.getCombatEngine().getFleetManager(FleetSide.ENEMY).getDeployedCopyDFM()) {
1551                        if (member.getShip() == null) continue;
1552                        PersonAPI captain = member.getShip().getCaptain();
1553                        if (captain == null || captain.isDefault() || !captain.isAICore()) continue;
1554                        String coreId = captain.getAICoreId();
1555                        if (coreId == null) continue;
1556                        
1557                        map.add(coreId, 1);
1558                }
1559                return map;
1560        }
1561        
1562        public CountingMap<String> getAvailableAICores() {
1563                CountingMap<String> map = new CountingMap<String>();
1564                
1565                CampaignFleetAPI player = Global.getSector().getPlayerFleet();
1566                
1567                CargoAPI cargo = player.getCargo(); 
1568                
1569                map.add(Commodities.ALPHA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.ALPHA_CORE)));
1570                map.add(Commodities.BETA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.BETA_CORE)));
1571                map.add(Commodities.GAMMA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.GAMMA_CORE)));
1572                
1573                
1574                cargo = Misc.getStorageCargo(Global.getSector().getCurrentlyOpenMarket());
1575                if (cargo != null) {
1576                        map.add(Commodities.ALPHA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.ALPHA_CORE)));
1577                        map.add(Commodities.BETA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.BETA_CORE)));
1578                        map.add(Commodities.GAMMA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.GAMMA_CORE)));
1579                }
1580                
1581                return map;
1582        }
1583        
1584        public void appendToTooltip(TooltipMakerAPI info, float initPad, float width, AdvancedSimOption option, Object extra) {
1585                if (isRequireAICoresInCargo()) {
1586                        if (option.getId().equals(AI_CORES_ID) && extra != null) {
1587                                if (extra.equals(AI_CORES_SOME) ||
1588                                                extra.equals(AI_CORES_GAMMA) ||
1589                                                extra.equals(AI_CORES_BETA) ||
1590                                                extra.equals(AI_CORES_ALPHA)) {
1591                                        CountingMap<String> cores = getAvailableAICores();
1592                                        CargoAPI cargo = Global.getFactory().createCargo(true);
1593                                        for (String id : cores.keySet()) {
1594                                                cargo.addCommodity(id, cores.getCount(id));
1595                                        }
1596                                        
1597                                        float opad = 10f;
1598                                        info.addSectionHeading("AI cores in cargo & storage", Alignment.MID, initPad);
1599                                        if (cargo.isEmpty()) {
1600                                                info.addPara(BaseIntelPlugin.INDENT + "None", opad);
1601                                        } else {
1602                                                info.showCargo(cargo, 10, true, opad);
1603                                        }
1604                                        
1605                                        cores = getDeployedAICores();
1606                                        cargo = Global.getFactory().createCargo(true);
1607                                        for (String id : cores.keySet()) {
1608                                                cargo.addCommodity(id, cores.getCount(id));
1609                                        }
1610                                        
1611                                        info.addSectionHeading("Cores already in use in simulation", Alignment.MID, initPad);
1612                                        if (cargo.isEmpty()) {
1613                                                info.addPara(BaseIntelPlugin.INDENT + "None", opad);
1614                                        } else {
1615                                                info.showCargo(cargo, 10, true, opad);
1616                                        }
1617                                        
1618                                        
1619                                        cores = getAvailableMinusDeployedAICores();
1620                                        cargo = Global.getFactory().createCargo(true);
1621                                        for (String id : cores.keySet()) {
1622                                                cargo.addCommodity(id, cores.getCount(id));
1623                                        }
1624                                        
1625                                        info.addSectionHeading("Cores available to deploy", Alignment.MID, initPad);
1626                                        if (cargo.isEmpty()) {
1627                                                info.addPara(BaseIntelPlugin.INDENT + "None", opad);
1628                                        } else {
1629                                                info.showCargo(cargo, 10, true, opad);
1630                                        }
1631                                }
1632                        }
1633                }
1634        }
1635
1636        @Override
1637        public MarketAPI getMarket() {
1638                return null;
1639        }
1640
1641        @Override
1642        public FleetMemberAPI getFleetMember() {
1643                return null;
1644        }
1645
1646}
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661