001package com.fs.starfarer.api.impl.campaign.procgen.themes;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.List;
006import java.util.Random;
007
008import org.lwjgl.util.vector.Vector2f;
009
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
012import com.fs.starfarer.api.campaign.FactionAPI;
013import com.fs.starfarer.api.campaign.PlanetAPI;
014import com.fs.starfarer.api.campaign.SectorEntityToken;
015import com.fs.starfarer.api.campaign.StarSystemAPI;
016import com.fs.starfarer.api.campaign.econ.MarketAPI;
017import com.fs.starfarer.api.characters.PersonAPI;
018import com.fs.starfarer.api.combat.ShipAPI.HullSize;
019import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints;
020import com.fs.starfarer.api.combat.ShipVariantAPI;
021import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin;
022import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictShipData;
023import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictType;
024import com.fs.starfarer.api.impl.campaign.econ.impl.TechMining;
025import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent;
026import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent.SkillPickPreference;
027import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
028import com.fs.starfarer.api.impl.campaign.ids.Commodities;
029import com.fs.starfarer.api.impl.campaign.ids.Entities;
030import com.fs.starfarer.api.impl.campaign.ids.Factions;
031import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
032import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
033import com.fs.starfarer.api.impl.campaign.ids.Tags;
034import com.fs.starfarer.api.impl.campaign.intel.events.ht.HTPoints;
035import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
036import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.AddedEntity;
037import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.StarSystemData;
038import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BlueprintSpecial.BlueprintSpecialData;
039import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BreadcrumbSpecial.BreadcrumbSpecialData;
040import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.CargoManifestSpecial.CargoManifestSpecialData;
041import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.CryopodOfficerGen;
042import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.CryopodOfficerGen.CryopodOfficerTemplate;
043import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.PerShipData;
044import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipCondition;
045import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipRecoverySpecialData;
046import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.SleeperPodsSpecial.SleeperPodsSpecialData;
047import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.SleeperPodsSpecial.SleeperSpecialType;
048import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.SurveyDataSpecial.SurveyDataSpecialData;
049import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.SurveyDataSpecial.SurveyDataSpecialType;
050import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.TopographicDataSpecial.TopographicDataSpecialData;
051import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.TransmitterTrapSpecial.TransmitterTrapSpecialData;
052import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin;
053import com.fs.starfarer.api.util.Misc;
054import com.fs.starfarer.api.util.WeightedRandomPicker;
055
056public class SalvageSpecialAssigner {
057
058        public static int STANDARD_PODS_OFFICER_LEVEL = Global.getSettings().getInt("standardSleeperPodsOfficerLevel");
059        public static int EXCEPTIONAL_PODS_OFFICER_LEVEL = Global.getSettings().getInt("exceptionalSleeperPodsOfficerLevel");
060        public static int EXCEPTIONAL_PODS_OFFICER_ELITE_SKILLS = Global.getSettings().getInt("exceptionalSleeperPodsOfficerEliteSkills");
061        public static int MAX_EXCEPTIONAL_PODS_OFFICERS = Global.getSettings().getInt("maxExceptionalSleeperPodsOfficers");
062        public static float PROB_EXCEPTIONAL_PODS_OFFICER = Global.getSettings().getFloat("probSleeperPodsOfficerIsExceptional");
063        public static float PROB_UNEXCEPTIONAL_USE_TEMPLATE = Global.getSettings().getFloat("probSleeperPodsUnexceptionalOfficerUseTemplate");
064        
065        public static int MAX_NORMAL_OFFICER_LEVEL = Global.getSettings().getInt("officerMaxLevel");
066        
067        public static class SpecialCreationContext {
068                public String themeId;
069                public boolean onNewGame = true;
070                public List<SectorEntityToken> all = new ArrayList<SectorEntityToken>();
071                public SpecialCreationContext() {
072                }
073                
074        }
075        
076        public static interface SpecialCreator {
077                Object createSpecial(SectorEntityToken entity, SpecialCreationContext context);
078        }
079        
080        public static void assignSpecialForBattleWreck(SectorEntityToken entity) {
081                Random random = StarSystemGenerator.random;
082                WeightedRandomPicker<SpecialCreator> picker = new WeightedRandomPicker<SpecialCreator>(random);
083                
084                picker.add(new NothingSpecialCreator(), 10f);
085                picker.add(new ShipRecoverySpecialCreator(random, 0, 0, false, null, null), 10f);
086                SpecialCreator pick = picker.pick();
087                
088                SpecialCreationContext context = new SpecialCreationContext();
089                
090                Object specialData = pick.createSpecial(entity, context);
091                if (specialData != null) {
092                        Misc.setSalvageSpecial(entity, specialData);
093                }
094        }
095        
096        public static void assignSpecialForDistressDerelict(SectorEntityToken entity) {
097                Random random = new Random();
098                
099                WeightedRandomPicker<SpecialCreator> picker = new WeightedRandomPicker<SpecialCreator>(random);
100                
101                DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin();
102                PerShipData shipData = plugin.getData().ship;
103                ShipVariantAPI variant = shipData.variant;
104                if (variant == null && shipData.variantId != null) {
105                        variant = Global.getSettings().getVariant(shipData.variantId);
106                }
107                float p = variant.getHullSpec().getMaxCrew();
108                float c = variant.getHullSpec().getCargo();
109                
110                WeightedRandomPicker<String> recoverableShipFactions = getNearbyFactions(random, entity);
111                if (entity.getContainingLocation().hasTag(Tags.THEME_REMNANT)) {
112                        recoverableShipFactions = Misc.createStringPicker(random,
113                                        Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f);
114                }
115                WeightedRandomPicker<String> officerFactions = recoverableShipFactions;
116                WeightedRandomPicker<String> valuableCargo = getValuableCargo(random);
117                
118                picker.add(new NothingSpecialCreator(), 10f);
119                picker.add(new ShipRecoverySpecialCreator(random, 0, 0, false, null, null), 30f);
120                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, p * 0.125f, p * 0.25f, null), 2f);
121                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, p * 0.25f, p * 0.5f, null), 20f);
122                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 1, officerFactions), 2f);
123                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, EXCEPTIONAL_PODS_OFFICER_LEVEL, officerFactions), 15f);
124                if (!Global.getSector().isInSectorGen() && CryopodOfficerGen.canAddMoreExceptional()) {
125                        // This won't increment the "spawned exceptional officer" count since the min
126                        // level is set to exceptional, and SleeperPodsSpecialCreator won't roll
127                        // exceptional officers on its own when not isInSectorGen().
128                        // Instead, the count will be incremented when the pods are opened.
129                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER,
130                                        EXCEPTIONAL_PODS_OFFICER_LEVEL, EXCEPTIONAL_PODS_OFFICER_LEVEL, officerFactions), 30f);
131                }
132                
133                picker.add(new CargoManifestSpecialCreator(random, valuableCargo, c * 0.25f, c * 0.5f), 10f);
134                picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 2f);
135                picker.add(new BlueprintSpecialCreator(random), 1f);
136                picker.add(new TopographicDataSpecialCreator(random, HTPoints.LOW_MIN, HTPoints.LOW_MAX), 1f);
137                
138                SpecialCreator pick = picker.pick();
139                SpecialCreationContext context = new SpecialCreationContext();
140                
141                Object specialData = pick.createSpecial(entity, context);
142                if (specialData != null) {
143                        Misc.setSalvageSpecial(entity, specialData);
144                }
145        }
146        
147        public static void assignSpecialForDebrisField(SectorEntityToken entity) {
148                Random random = StarSystemGenerator.random;
149                WeightedRandomPicker<SpecialCreator> picker = new WeightedRandomPicker<SpecialCreator>(random);
150                
151                WeightedRandomPicker<String> recoverableShipFactions = getNearbyFactions(random, entity);
152                WeightedRandomPicker<String> trapFactions = Misc.createStringPicker(random, Factions.PIRATES, 10f);
153                WeightedRandomPicker<String> valuableCargo = getValuableCargo(random);
154                
155                picker.add(new NothingSpecialCreator(), 60f);
156                picker.add(new ShipRecoverySpecialCreator(random, 1, 3, true, null, recoverableShipFactions), 10f);
157                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 10, 20, null), 2f);
158                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, 20, 40, null), 6f);
159                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, 1, 5, null), 3f);
160                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, EXCEPTIONAL_PODS_OFFICER_LEVEL, recoverableShipFactions), 1f);
161                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 1, recoverableShipFactions), 0.2f);
162                picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 50), 10f);
163                picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 4f);
164                picker.add(new BreadcrumbSpecialCreator(random, null), 10f);
165                picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 25), 10f);
166                picker.add(new TopographicDataSpecialCreator(random, HTPoints.LOW_MIN, HTPoints.LOW_MAX), 5f);
167                
168                SpecialCreator pick = picker.pick();
169                
170                SpecialCreationContext context = new SpecialCreationContext();
171                
172                Object specialData = pick.createSpecial(entity, context);
173                if (specialData != null) {
174                        Misc.setSalvageSpecial(entity, specialData);
175                }
176        }
177        
178        public static WeightedRandomPicker<String> getValuableCargo(Random random) {
179                WeightedRandomPicker<String> valuableCargo = Misc.createStringPicker(random,
180                                Commodities.VOLATILES, 10f, Commodities.RARE_METALS, 10f, Commodities.RARE_ORE, 10f,
181                                Commodities.HEAVY_MACHINERY, 10f,
182                                Commodities.HAND_WEAPONS, 10f, Commodities.ORGANS, 10f, Commodities.DRUGS, 10f,
183                                Commodities.LUXURY_GOODS, 10f, Commodities.LOBSTER, 10f);
184                return valuableCargo;
185        }
186        
187        public static WeightedRandomPicker<String> getIndustryCargo(Random random) {
188                WeightedRandomPicker<String> industryCargo = Misc.createStringPicker(random,
189                                Commodities.VOLATILES, 10f, Commodities.RARE_METALS, 10f, Commodities.RARE_ORE, 10f,
190                                Commodities.HEAVY_MACHINERY, 10f, Commodities.ORE, 10f);
191                return industryCargo;
192        }
193        
194        public static WeightedRandomPicker<String> getHabCargo(Random random) {
195                WeightedRandomPicker<String> habCargo = Misc.createStringPicker(random,
196                                Commodities.HAND_WEAPONS, 10f, Commodities.ORGANS, 10f, Commodities.DRUGS, 10f,
197                                Commodities.LUXURY_GOODS, 10f, Commodities.LOBSTER, 10f);
198                return habCargo;
199        }
200        
201        public static WeightedRandomPicker<String> getNearbyFactions(Random random, SectorEntityToken entity) {
202                WeightedRandomPicker<String> picker = Misc.createStringPicker(random, Factions.INDEPENDENT, 10f);
203                for (MarketAPI market : Misc.getNearbyMarkets(entity.getLocationInHyperspace(), 10f)) {
204                        picker.add(market.getFactionId(), market.getSize());
205                }
206                return picker;
207        }
208        
209        public static WeightedRandomPicker<String> getNearbyFactions(Random random, SectorEntityToken entity,
210                                                                                                                                float rangeLY,
211                                                                                                                                float indWeight, float pirateWeight) {
212                if (random == null) random = new Random();
213                return getNearbyFactions(random, entity.getLocationInHyperspace(), rangeLY, indWeight, pirateWeight);
214        }
215        public static WeightedRandomPicker<String> getNearbyFactions(Random random, Vector2f locationInHyper,
216                                                                                                                float rangeLY,
217                                                                                                                float indWeight, float pirateWeight) {
218                if (random == null) random = new Random();
219                WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random);
220                if (indWeight > 0) {
221                        picker.add(Factions.INDEPENDENT, indWeight);
222                        picker.add(Factions.MERCENARY, indWeight * 0.1f);
223                }
224                if (pirateWeight > 0) picker.add(Factions.PIRATES, pirateWeight);
225                for (MarketAPI market : Misc.getNearbyMarkets(locationInHyper, rangeLY)) {
226                        picker.add(market.getFactionId(), market.getSize());
227                }
228                return picker;
229        }
230        
231        public static SpecialCreator pickSpecialFor(SectorEntityToken entity, SpecialCreationContext context) {
232                
233                Random random = StarSystemGenerator.random;
234                if (randomOverride != null) {
235                        random = randomOverride;
236                }
237                
238                WeightedRandomPicker<SpecialCreator> picker = new WeightedRandomPicker<SpecialCreator>(random);
239                
240                //System.out.println("Random: " + random.nextLong());
241
242                String type = entity.getCustomEntityType();
243                
244                WeightedRandomPicker<String> recoverableShipFactions = getNearbyFactions(random, entity);
245                
246                if (entity.getContainingLocation().hasTag(Tags.THEME_REMNANT)) {
247                        recoverableShipFactions = Misc.createStringPicker(random,
248                                        Factions.TRITACHYON, 10f, Factions.HEGEMONY, 7f, Factions.INDEPENDENT, 3f);
249                }
250                
251                int maxPodsOfficerLevel = EXCEPTIONAL_PODS_OFFICER_LEVEL;
252                // limited to MAX_EXCEPTIONAL_PODS_OFFICERS anyway, so it's fine to generate after new game sectorgen if possible
253//              if (context != null && !context.onNewGame) {
254//                      maxPodsOfficerLevel = MAX_NORMAL_OFFICER_LEVEL;
255//              }
256                
257                WeightedRandomPicker<String> remnantsFaction = Misc.createStringPicker(random, Factions.REMNANTS, 10f);
258                WeightedRandomPicker<String> piratesFaction = Misc.createStringPicker(random, Factions.PIRATES, 10f);
259                
260                
261                WeightedRandomPicker<String> trapFactions = piratesFaction;
262                if (entity.getContainingLocation().hasTag(Tags.THEME_REMNANT_SUPPRESSED) ||
263                                entity.getContainingLocation().hasTag(Tags.THEME_REMNANT_RESURGENT)) {
264                        trapFactions = remnantsFaction;
265                }
266                
267                
268//              WeightedRandomPicker<String> officerFactions = Misc.createStringPicker(random,
269//                                      Factions.PIRATES, 10f, Factions.HEGEMONY, 5f, Factions.INDEPENDENT, 10f,
270//                                      Factions.TRITACHYON, 5f, Factions.LUDDIC_CHURCH, 5f, Factions.PERSEAN, 10f,
271//                                      Factions.DIKTAT, 5f);
272//              
273//              if (entity.getContainingLocation().hasTag(Tags.THEME_REMNANT)) {
274//                      officerFactions.add(Factions.TRITACHYON, 10f);
275//                      officerFactions.add(Factions.HEGEMONY, 5f);
276//              }
277                
278                WeightedRandomPicker<String> officerFactions = recoverableShipFactions;
279                
280                
281                
282                WeightedRandomPicker<String> valuableCargo = getValuableCargo(random);
283                WeightedRandomPicker<String> industryCargo = getIndustryCargo(random);
284                
285                WeightedRandomPicker<String> habCargo = getHabCargo(random);
286                
287                
288                // ruins on a planet
289                if (entity instanceof PlanetAPI) {
290                        
291                        float sizeMult = TechMining.getTechMiningRuinSizeModifier(entity.getMarket());
292                        
293                        picker.add(new NothingSpecialCreator(), 30f);
294                        picker.add(new ShipRecoverySpecialCreator(random, 1, 2, false, DerelictType.CIVILIAN, recoverableShipFactions), 5f);
295                        picker.add(new ShipRecoverySpecialCreator(random, 1, 3, false, DerelictType.SMALL, recoverableShipFactions), 10f);
296                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.MEDIUM, recoverableShipFactions), 3f);
297                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.LARGE, recoverableShipFactions), 1f);
298                        if (sizeMult > 0) {
299                                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 100 * sizeMult, 200 * sizeMult, null), 2f);
300                                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, 500 * sizeMult, 1000 * sizeMult, null), 6f);
301                                picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, 50 * sizeMult, 500 * sizeMult, null), 3f);
302                        }
303                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
304                        // min/max doesn't matter for ADMIN
305                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 1, officerFactions), 5f);
306                        
307                        picker.add(new CargoManifestSpecialCreator(random, industryCargo, 500 * sizeMult, 2500 * sizeMult), 15f);
308                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 500 * sizeMult, 2500 * sizeMult), 15f);
309                        
310                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_LARGE, trapFactions, 
311                                        (int)(10 + 30 * sizeMult), (int)(10 + 30 * sizeMult)), 10f);
312                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.LOW_MIN, HTPoints.HIGH_MAX), 3f);
313                        
314                        // text for these is not set up to handle the "planet" case
315                        //picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.AUTO_PICK), 20f);
316                        //picker.add(new BreadcrumbSpecialCreator(random, context.all), 10f);
317                }
318                
319                
320                // derelict ship
321                if (entity.getCustomPlugin() instanceof DerelictShipEntityPlugin || Entities.WRECK.equals(type)) {
322                        DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin();
323                        
324                        PerShipData shipData = plugin.getData().ship;
325                        ShipVariantAPI variant = shipData.variant;
326                        if (variant == null && shipData.variantId != null) {
327                                variant = Global.getSettings().getVariant(shipData.variantId);
328                        }
329                        float p = variant.getHullSpec().getMaxCrew();
330                        float c = variant.getHullSpec().getCargo();
331                        
332                        picker.add(new NothingSpecialCreator(), 40f);
333                        picker.add(new ShipRecoverySpecialCreator(random, 0, 0, false, null, null), 30f);
334                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, p * 0.125f, p * 0.25f, null), 2f);
335                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, p * 0.25f, p * 0.5f, null), 7f);
336                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, p * 0.1f, p * 0.2f, null), 3f);
337                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 10f);
338                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 1, officerFactions), 0.2f);
339                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, c * 0.25f, c * 0.5f), 10f);
340                        picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 4f);
341                        picker.add(new BreadcrumbSpecialCreator(random, context.all), 10f);
342                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.LOW_MIN, HTPoints.LOW_MAX), 5f);
343                        
344                        if (entity.getOrbit() != null) {
345                                picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 25), 10f);
346                        }
347                        
348                        if (!entity.hasTag(Tags.EXPIRES)) {
349                                picker.add(new BlueprintSpecialCreator(random), 1f);
350                        }
351                }
352                
353                // debris field
354                boolean debris = entity instanceof CampaignTerrainAPI &&
355                                                ((CampaignTerrainAPI)entity).getPlugin() instanceof DebrisFieldTerrainPlugin;
356                if (debris) {
357                        picker.add(new NothingSpecialCreator(), 60f);
358                        picker.add(new ShipRecoverySpecialCreator(random, 1, 3, true, null, recoverableShipFactions), 10f);
359                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 10, 30, null), 2f);
360                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, 10, 50, null), 6f);
361                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, 1, 5, null), 3f);
362                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
363                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 0.2f);
364                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 50), 10f);
365                        picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 4f);
366                        picker.add(new BreadcrumbSpecialCreator(random, context.all), 10f);
367                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 25), 10f);
368                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.LOW_MIN, HTPoints.LOW_MAX), 1f);
369                }
370                
371                if (Entities.STATION_MINING_REMNANT.equals(type) || Entities.STATION_MINING.equals(type)) {
372                        picker.add(new NothingSpecialCreator(), 30f);
373                        picker.add(new ShipRecoverySpecialCreator(random, 1, 3, false, DerelictType.CIVILIAN, recoverableShipFactions), 10f);
374                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 10, 20, null), 1f);
375                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, 100, 200, null), 6f);
376                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, 5, 50, null), 3f);
377                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
378                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 3f);
379                        picker.add(new CargoManifestSpecialCreator(random, industryCargo, 50, 250), 30f);
380                        picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 8f);
381                        picker.add(new BreadcrumbSpecialCreator(random, context.all), 10f);
382                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_MEDIUM, trapFactions, 10, 16), 10f);
383                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.MEDIUM_MIN, HTPoints.MEDIUM_MAX), 1f);
384                }
385                
386                if (Entities.STATION_RESEARCH_REMNANT.equals(type) || Entities.STATION_RESEARCH.equals(type)) {
387                        picker.add(new NothingSpecialCreator(), 30f);
388                        picker.add(new ShipRecoverySpecialCreator(random, 1, 3, false, DerelictType.CIVILIAN, recoverableShipFactions), 10f);
389                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 50, 100, null), 2f);
390                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, 100, 200, null), 6f);
391                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, 5, 50, null), 3f);
392                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
393                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 3f);
394                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 30), 10f);
395                        picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 4f);
396                        picker.add(new BreadcrumbSpecialCreator(random, context.all), 10f);
397                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_MEDIUM, trapFactions, 10, 16), 20f);
398                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.HIGH_MIN, HTPoints.HIGH_MAX), 10f);
399                }
400                
401                if (Entities.ORBITAL_HABITAT_REMNANT.equals(type) || Entities.ORBITAL_HABITAT.equals(type)) {
402                        picker.add(new NothingSpecialCreator(), 40f);
403                        picker.add(new ShipRecoverySpecialCreator(random, 1, 3, false, DerelictType.CIVILIAN, recoverableShipFactions), 20f);
404                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 50, 100, null), 6f);
405                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.CREW, 100, 200, null), 20f);
406                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ORGANS, 5, 50, null), 5f);
407                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 10f);
408                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 5f);
409                        picker.add(new CargoManifestSpecialCreator(random, habCargo, 10, 30), 10f);
410                        picker.add(new SurveyDataSpecialCreator(random, SurveyDataSpecialType.PLANET_SURVEY_DATA), 2f);
411                        picker.add(new BreadcrumbSpecialCreator(random, context.all), 10f);
412                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_MEDIUM, trapFactions, 10, 16), 10f);
413                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.MEDIUM_MIN, HTPoints.MEDIUM_MAX), 2f);
414                }
415                
416                
417                List<String> weapons = Arrays.asList(Entities.WEAPONS_CACHE, Entities.WEAPONS_CACHE_HIGH, Entities.WEAPONS_CACHE_LOW, Entities.WEAPONS_CACHE_REMNANT);
418                if (weapons.contains(type)) {
419                        picker.add(new NothingSpecialCreator(), 30f);
420                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.SMALL, recoverableShipFactions), 10f);
421                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.MARINES, 50, 100, null), 1f);
422                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 2f);
423                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 30), 10f);
424                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 8), 10f);
425                }
426                
427                List<String> weaponsSmall = Arrays.asList(Entities.WEAPONS_CACHE_SMALL, Entities.WEAPONS_CACHE_SMALL_HIGH,
428                                                                Entities.WEAPONS_CACHE_SMALL_LOW, Entities.WEAPONS_CACHE_SMALL_REMNANT);
429                if (weaponsSmall.contains(type)) {
430                        picker.add(new NothingSpecialCreator(), 30f);
431                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.SMALL, recoverableShipFactions), 10f);
432                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
433                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 30), 10f);
434                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 8), 10f);
435                }               
436                
437                
438                List<String> supplies = Arrays.asList(Entities.SUPPLY_CACHE);
439                if (supplies.contains(type)) {
440                        picker.add(new NothingSpecialCreator(), 30f);
441                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.SMALL, recoverableShipFactions), 10f);
442                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
443                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 0.2f);
444                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 30), 10f);
445                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 8), 10f);
446                }
447                
448                List<String> suppliesSmall = Arrays.asList(Entities.SUPPLY_CACHE_SMALL);
449                if (suppliesSmall.contains(type)) {
450                        picker.add(new NothingSpecialCreator(), 30f);
451                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.SMALL, recoverableShipFactions), 10f);
452                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
453                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 0.2f);
454                        picker.add(new CargoManifestSpecialCreator(random, valuableCargo, 10, 30), 10f);
455                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 8), 10f);
456                }
457                
458                
459                List<String> equipment = Arrays.asList(Entities.EQUIPMENT_CACHE);
460                if (equipment.contains(type)) {
461                        picker.add(new NothingSpecialCreator(), 30f);
462                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.SMALL, recoverableShipFactions), 10f);
463                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
464                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 0.2f);
465                        picker.add(new CargoManifestSpecialCreator(random, industryCargo, 10, 30), 10f);
466                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 8), 10f);
467                        picker.add(new TopographicDataSpecialCreator(random, HTPoints.LOW_MIN, HTPoints.LOW_MAX), 1f);
468                }
469                
470                List<String> equipmentSmall = Arrays.asList(Entities.EQUIPMENT_CACHE_SMALL);
471                if (equipmentSmall.contains(type)) {
472                        picker.add(new NothingSpecialCreator(), 30f);
473                        picker.add(new ShipRecoverySpecialCreator(random, 1, 1, false, DerelictType.SMALL, recoverableShipFactions), 10f);
474                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.OFFICER, STANDARD_PODS_OFFICER_LEVEL, maxPodsOfficerLevel, officerFactions), 1f);
475                        picker.add(new SleeperPodsSpecialCreator(random, SleeperSpecialType.ADMIN, 1, 5, officerFactions), 0.2f);
476                        picker.add(new CargoManifestSpecialCreator(random, industryCargo, 10, 30), 10f);
477                        picker.add(new TransmitterTrapSpecialCreator(random, 0.5f, FleetTypes.PATROL_SMALL, trapFactions, 4, 8), 10f);
478                }
479                
480                
481                return picker.pick();
482        }
483        
484        
485        public static void assignSpecials(SectorEntityToken entity) {
486                assignSpecials(entity, true);
487        }
488        
489        protected static Random randomOverride = null;
490        public static void assignSpecials(SectorEntityToken entity, boolean onNewGame, Random random) {
491                randomOverride = random;
492                assignSpecials(entity, onNewGame);
493                randomOverride = null;
494        }
495        public static void assignSpecials(SectorEntityToken entity, boolean onNewGame) {
496                SpecialCreationContext context = new SpecialCreationContext();
497                context.onNewGame = onNewGame;
498                
499                SpecialCreator creator = pickSpecialFor(entity, context);
500                if (creator == null) return;
501                
502                Object specialData = creator.createSpecial(entity, context);
503                if (specialData != null) {
504                        Misc.setSalvageSpecial(entity, specialData);
505                        
506                        if (DerelictThemeGenerator.DEBUG) {
507                                String id = entity.getCustomEntityType();
508                                if (id == null) id = entity.getClass().getSimpleName();
509                                System.out.println("Assigned " + specialData.getClass().getSimpleName() + " to " + id);
510                        }
511                }
512        }
513        
514        
515        public static void assignSpecials(List<StarSystemData> systemData, SpecialCreationContext context) {
516                
517                context.all.clear();
518                for (StarSystemData data : systemData) {
519                        for (AddedEntity added : data.generated) {
520                                context.all.add(added.entity);
521                        }
522                        for (PlanetAPI planet : data.system.getPlanets()) {
523                                if (planet.getMarket() != null && 
524                                                planet.getMarket().isPlanetConditionMarketOnly() && 
525                                                Misc.hasRuins(planet.getMarket())) {
526                                        context.all.add(planet);
527                                }
528                        }
529                }
530                
531                if (DerelictThemeGenerator.DEBUG) {
532                        System.out.println("\n\n\nAssigning salvage specials");
533                }
534                
535                for (SectorEntityToken entity : context.all) {
536                        SpecialCreator creator = pickSpecialFor(entity, context);
537                        if (creator == null) continue;
538                        
539                        Object specialData = creator.createSpecial(entity, context);
540                        if (specialData != null) {
541                                Misc.setSalvageSpecial(entity, specialData);
542                                
543                                if (DerelictThemeGenerator.DEBUG) {
544                                        String id = entity.getCustomEntityType();
545                                        if (id == null) id = entity.getClass().getSimpleName();
546                                        System.out.println("Assigned " + specialData.getClass().getSimpleName() + " to " + id);
547                                }
548                        }
549                        
550                        //System.out.println("" + StarSystemGenerator.random.nextLong());
551                }
552                
553                if (DerelictThemeGenerator.DEBUG) {
554                        System.out.println("Finished assigning salvage specials\n\n\n\n");
555                }
556                
557        }
558        
559        
560        
561        
562
563        public static class NothingSpecialCreator implements SpecialCreator {
564                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
565                        return null;
566                }
567        }
568        
569        public static class ShipRecoverySpecialCreator implements SpecialCreator {
570                private int min, max;
571                private WeightedRandomPicker<String> factionPicker;
572                private Random random;
573                private boolean badCondition;
574                private DerelictType type;
575                public ShipRecoverySpecialCreator(Random random, int min, int max, boolean badCondition,
576                                                                                  DerelictType type,
577                                                                                  WeightedRandomPicker<String> factionPicker) {
578                        this.badCondition = badCondition;
579                        this.type = type;
580                        if (random == null) random = new Random();
581                        this.random = random;
582                        this.min = min;
583                        this.max = max;
584                        this.factionPicker = factionPicker;
585                }
586
587                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
588                        
589                        if (entity.getCustomPlugin() instanceof DerelictShipEntityPlugin) {
590                                DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin();
591                                
592                                ShipRecoverySpecialData data = new ShipRecoverySpecialData(null);
593                                data.addShip(plugin.getData().ship.clone());
594                                return data;
595                        }
596                        
597//                      boolean debris = entity instanceof CampaignTerrainAPI && 
598//                              ((CampaignTerrainAPI)entity).getPlugin() instanceof DebrisFieldTerrainPlugin;
599                        String desc = null;
600                        if (entity instanceof PlanetAPI) {
601                                desc = "found in a sealed hangar bay";
602                        }
603                        
604                        ShipRecoverySpecialData data = new ShipRecoverySpecialData(desc);
605                        int num = min + random.nextInt(max - min + 1);
606                        for (int i = 0; i < num; i++) {
607                                String factionId = factionPicker.pick();
608                                ShipCondition condition = DerelictShipEntityPlugin.pickDerelictCondition(random);
609                                if (badCondition) {
610                                        condition = DerelictShipEntityPlugin.pickBadCondition(random);
611                                }
612                                DerelictShipData dsd = DerelictShipEntityPlugin.createRandom(factionId, type, random, 0f);
613                                if (dsd == null || dsd.ship == null) continue;
614                                
615                                dsd.ship.condition = condition;
616                                data.addShip(dsd.ship);
617                        }
618                        
619                        if (data.ships == null || data.ships.isEmpty()) return null;
620                        
621                        return data;
622                }
623                
624        }
625        
626        
627        public static class SleeperPodsSpecialCreator implements SpecialCreator {
628                private SleeperSpecialType type;
629                private int min, max;
630                private WeightedRandomPicker<String> officerFactions;
631                private Random random;
632                
633                public SleeperPodsSpecialCreator(Random random, SleeperSpecialType type, float min, float max, 
634                                                                                 WeightedRandomPicker<String> officerFactions) {
635                        if (min < 1) min = 1;
636                        if (max < 1) max = 1;
637                        if (min > max) min = max;
638                        this.random = random;
639                        this.type = type;
640                        this.min = (int) min;
641                        this.max = (int) max;
642                        this.officerFactions = officerFactions;
643                        
644//                      if (type == SleeperSpecialType.OFFICER && max == 15) {
645//                              System.out.println("ewfwefwe");
646//                      }
647                }
648
649
650                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
651                        SleeperPodsSpecialData data = new SleeperPodsSpecialData(type, null);
652                        
653                        if (type == SleeperSpecialType.OFFICER) {
654//                              if (entity.getContainingLocation().getName().startsWith("Dalar's")) {
655//                                      System.out.println("wefwefwe");
656//                              }
657                                String factionId = officerFactions.pick();
658                                FactionAPI faction = Global.getSector().getFaction(factionId);
659                                //int level = min + random.nextInt(max - min + 1);
660                                
661                                //String key = "$SleeperPodsSpecialCreator_exceptionalCount";
662                                //int numAlreadyCreated = Global.getSector().getMemoryWithoutUpdate().getInt(key);
663                                int numAlreadyCreated = CryopodOfficerGen.getNumExceptionalCreated();
664                                
665                                int level = min;
666                                if (context != null && context.onNewGame && 
667                                                numAlreadyCreated < MAX_EXCEPTIONAL_PODS_OFFICERS && 
668                                                random.nextFloat() < PROB_EXCEPTIONAL_PODS_OFFICER) {
669                                        level = EXCEPTIONAL_PODS_OFFICER_LEVEL;
670//                                      numAlreadyCreated++;
671//                                      Global.getSector().getMemoryWithoutUpdate().set(key, numAlreadyCreated);
672                                        CryopodOfficerGen.incrNumExceptionalCreated();
673                                }
674                                
675//                              SkillPickPreference pref = SkillPickPreference.GENERIC;
676//                              float f = random.nextFloat();
677//                              if (f < 0.05f) {
678//                                      pref = SkillPickPreference.ANY;
679//                              } else if (f < 0.1f) {
680//                                      pref = SkillPickPreference.PHASE;
681//                              } else if (f < 0.25f) {
682//                                      pref = SkillPickPreference.CARRIER;
683//                              }
684                                SkillPickPreference pref = SkillPickPreference.ANY;
685                                
686                                //pref = SkillPickPreference.CARRIER;
687                                
688                                
689//                              WeightedRandomPicker<Integer> numElite = new WeightedRandomPicker<Integer>(random);
690//                              numElite.add(0, 20f);
691//                              numElite.add(1, 10f);
692//                              numElite.add(2, level);
693//                              if (level >= EXCEPTIONAL_PODS_OFFICER_LEVEL - 1) {
694//                                      numElite.add(3, level);
695//                              }
696//                              if (level >= EXCEPTIONAL_PODS_OFFICER_LEVEL) {
697//                                      numElite.add(4, level);
698//                              }
699//                              int eliteSkillNumOverride = numElite.pick();
700                                int eliteSkillNumOverride = 1;
701                                if (level == EXCEPTIONAL_PODS_OFFICER_LEVEL) {
702                                        eliteSkillNumOverride = EXCEPTIONAL_PODS_OFFICER_ELITE_SKILLS;
703                                }
704                                
705                                PersonAPI officer = OfficerManagerEvent.createOfficer(faction, level, pref, true, null, true, 
706                                                                                                                                          true, eliteSkillNumOverride, random);
707                                if (level == EXCEPTIONAL_PODS_OFFICER_LEVEL) {
708                                        CryopodOfficerTemplate template = CryopodOfficerGen.TEMPLATES_EXCEPTIONAL.pick(random);
709                                        if (template != null) {
710                                                officer = template.create(faction, random);
711                                        }
712                                } else if (random.nextFloat() < PROB_UNEXCEPTIONAL_USE_TEMPLATE) {
713                                        CryopodOfficerTemplate template = CryopodOfficerGen.TEMPLATES_NORMAL.pick(random);
714                                        if (template != null) {
715                                                officer = template.create(faction, random);
716                                        }
717                                }
718                                
719                                if (level == EXCEPTIONAL_PODS_OFFICER_LEVEL) {
720                                        officer.getMemoryWithoutUpdate().set(MemFlags.EXCEPTIONAL_SLEEPER_POD_OFFICER, true);
721                                }
722                                data.officer = officer;
723                                data.min = 1;
724                                data.max = 1;
725                        } else if (type == SleeperSpecialType.ADMIN) {
726                                //System.out.println("ADMIN: " + entity.getContainingLocation().getName() + ", " + entity.getName());
727                                String factionId = officerFactions.pick();
728                                FactionAPI faction = Global.getSector().getFaction(factionId);
729                                
730                                WeightedRandomPicker<Integer> tierPicker = new WeightedRandomPicker<Integer>(random);
731                                //tierPicker.add(0, 0);
732                                tierPicker.add(1, 50);
733                                tierPicker.add(2, 50);
734                                
735                                int tier = tierPicker.pick();
736                                
737                                PersonAPI officer = OfficerManagerEvent.createAdmin(faction, tier, random);
738                                data.officer = officer;
739                                data.min = 1;
740                                data.max = 1;
741                        } else {
742                                data.min = min;
743                                data.max = max;
744                        }
745                        
746                        return data;
747                }
748                
749        }
750        
751        
752        public static class SurveyDataSpecialCreator implements SpecialCreator {
753                private Random random;
754                private SurveyDataSpecialType type;
755                
756                public SurveyDataSpecialCreator(Random random, SurveyDataSpecialType type) {
757                        this.random = random;
758                        this.type = type;
759                }
760
761
762                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
763                        SurveyDataSpecialData data = new SurveyDataSpecialData(type);
764                        return data;
765                }
766                
767        }
768        
769        public static class BlueprintSpecialCreator implements SpecialCreator {
770                private Random random;
771                
772                public BlueprintSpecialCreator(Random random) {
773                        this.random = random;
774                }
775                
776                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
777                        return new BlueprintSpecialData();
778                }
779        }
780        
781        
782        public static class TopographicDataSpecialCreator implements SpecialCreator {
783                private Random random;
784                private int min;
785                private int max;
786                
787                public TopographicDataSpecialCreator(Random random, int min, int max) {
788                        if (min < 1) min = 1;
789                        if (max < 1) max = 1;
790                        this.random = random;
791                        this.min = (int) min;
792                        this.max = (int) max;
793                }
794                
795                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
796                        int points = min + random.nextInt(max - min + 1);
797                        TopographicDataSpecialData data = new TopographicDataSpecialData(points);
798                        return data;
799                }
800        }
801        public static class CargoManifestSpecialCreator implements SpecialCreator {
802                private Random random;
803                private SurveyDataSpecialType type;
804                private WeightedRandomPicker<String> cargoPicker;
805                private int min;
806                private int max;
807                
808                public CargoManifestSpecialCreator(Random random, WeightedRandomPicker<String> cargoPicker, float min, float max) {
809                        if (min < 1) min = 1;
810                        if (max < 1) max = 1;
811                        this.random = random;
812                        this.cargoPicker = cargoPicker;
813                        this.min = (int) min;
814                        this.max = (int) max;
815                }
816                
817                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
818                        CargoManifestSpecialData data = new CargoManifestSpecialData(cargoPicker.pick(), min, max);
819                        return data;
820                }
821        }
822        
823        public static class TransmitterTrapSpecialCreator implements SpecialCreator {
824                private Random random;
825                private WeightedRandomPicker<String> factionPicker;
826                private int minPts;
827                private int maxPts;
828                private final float chance;
829                private final String fleetType;
830                
831                public TransmitterTrapSpecialCreator(Random random, float chance, String fleetType, 
832                                                                        WeightedRandomPicker<String> factionPicker, int min, int max) {
833                        this.random = random;
834                        this.chance = chance;
835                        this.fleetType = fleetType;
836                        this.factionPicker = factionPicker;
837                        this.minPts = min;
838                        this.maxPts = max;
839                }
840                
841                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
842                        
843                        
844                        TransmitterTrapSpecialData data = new TransmitterTrapSpecialData();
845                        data.prob = chance;
846                        String factionId = factionPicker.pick();
847
848                        data.nearbyFleetFaction = factionId;
849                        data.useAllFleetsInRange = true;
850                        
851                        if (fleetType != null) {
852                                int combatPoints = minPts + random.nextInt(maxPts - minPts + 1);
853                                combatPoints *= 5;
854                                
855                                FleetParamsV3 params = new FleetParamsV3(
856                                                null,
857                                                entity.getLocationInHyperspace(),
858                                                factionId,
859                                                null, 
860                                                fleetType,
861                                                combatPoints, // combatPts
862                                                0f, // freighterPts 
863                                                0f, // tankerPts
864                                                0f, // transportPts
865                                                0f, // linerPts
866                                                0f, // utilityPts
867                                                0f // qualityMod
868                                                );                              
869                                data.params = params;
870                        }
871                        
872                        return data;
873                }
874        }
875        
876        
877        public static class BreadcrumbSpecialCreator implements SpecialCreator {
878                private Random random;
879                private List<SectorEntityToken> all;
880                
881                public BreadcrumbSpecialCreator(Random random, List<SectorEntityToken> all) {
882                        this.random = random;
883                        this.all = all;
884                }
885                
886                public Object createSpecial(SectorEntityToken entity, SpecialCreationContext context) {
887                        WeightedRandomPicker<SectorEntityToken> picker = new WeightedRandomPicker<SectorEntityToken>(random);
888                        
889//                      boolean debris = entity instanceof CampaignTerrainAPI &&
890//                                              ((CampaignTerrainAPI)entity).getPlugin() instanceof DebrisFieldTerrainPlugin;
891                        
892                        if (all != null) {
893                                for (SectorEntityToken other : all) {
894                                        if (other == entity) continue;
895                                        // only breadcrumb to larger ships
896                                        if (!isLargeShipOrNonShip(other)) continue;
897                                        picker.add(other);
898                                }
899                        }
900                        
901                        List<StarSystemAPI> systems = Misc.getNearbyStarSystems(entity, 10f);
902                        for (StarSystemAPI system : systems) {
903                                for (SectorEntityToken other : system.getEntitiesWithTag(Tags.SALVAGEABLE)) {
904                                        if (!other.hasSensorProfile() && !other.isDiscoverable()) continue;
905                                        if (other == entity) continue;
906                                        // only breadcrumb to larger ships
907                                        if (!isLargeShipOrNonShip(other)) continue;
908                                        if (other.hasTag(Tags.EXPIRES)) continue;
909                                        if (other.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
910                                        if (other.getContainingLocation() != null && other.getContainingLocation().hasTag(Tags.THEME_HIDDEN)) continue;
911                                        
912                                        if (other.getMemoryWithoutUpdate() != null && other.getMemoryWithoutUpdate().getBoolean("$ttWeaponsCache")) continue;
913                                        picker.add(other);
914                                }
915                        }
916                        
917                        
918                        SectorEntityToken target = picker.pick();
919                        if (target == null) return null;
920                        
921//                      if (target instanceof PlanetAPI) {
922//                              SurveyDataSpecialData data = new SurveyDataSpecialData(SurveyDataSpecialType.PLANET_SURVEY_DATA);
923//                              data.entityId = target.getId();
924//                              return data;
925//                      }
926                        BreadcrumbSpecialData data = new BreadcrumbSpecialData(target.getId());
927                        return data;
928                }
929                
930                public static boolean isLargeShipOrNonShip(SectorEntityToken other) {
931                        if (other.getCustomPlugin() instanceof DerelictShipEntityPlugin) {
932                                DerelictShipEntityPlugin dsep = (DerelictShipEntityPlugin) other.getCustomPlugin();
933                                ShipVariantAPI variant = dsep.getData().ship.variant;
934                                if (variant == null && dsep.getData().ship.variantId != null) {
935                                        variant = Global.getSettings().getVariant(dsep.getData().ship.variantId);
936                                }
937                                if (variant != null) {
938                                        if (variant.getHullSize() == HullSize.FRIGATE) return false;
939                                        if (variant.getHullSize() == HullSize.DESTROYER) return false;
940                                        if (variant.getHullSize() == HullSize.CRUISER &&
941                                                        variant.getHullSpec().getHints().contains(ShipTypeHints.CIVILIAN)) return false;
942                                }
943                        }
944                        return true;
945                }
946        }
947        
948}
949
950
951
952
953