001package com.fs.starfarer.api.impl.campaign.missions.hub;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.Collection;
006import java.util.HashSet;
007import java.util.LinkedHashSet;
008import java.util.List;
009import java.util.Set;
010
011import org.lwjgl.util.vector.Vector2f;
012
013import com.fs.starfarer.api.Global;
014import com.fs.starfarer.api.campaign.AsteroidAPI;
015import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
016import com.fs.starfarer.api.campaign.LocationAPI;
017import com.fs.starfarer.api.campaign.PlanetAPI;
018import com.fs.starfarer.api.campaign.SectorEntityToken;
019import com.fs.starfarer.api.campaign.StarSystemAPI;
020import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
021import com.fs.starfarer.api.campaign.econ.Industry;
022import com.fs.starfarer.api.campaign.econ.MarketAPI;
023import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel;
024import com.fs.starfarer.api.campaign.econ.MarketConditionAPI;
025import com.fs.starfarer.api.characters.PersonAPI;
026import com.fs.starfarer.api.impl.campaign.DebugFlags;
027import com.fs.starfarer.api.impl.campaign.econ.CommodityIconCounts;
028import com.fs.starfarer.api.impl.campaign.ids.Conditions;
029import com.fs.starfarer.api.impl.campaign.ids.Factions;
030import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
031import com.fs.starfarer.api.impl.campaign.ids.Tags;
032import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD;
033import com.fs.starfarer.api.impl.campaign.terrain.BaseRingTerrain;
034import com.fs.starfarer.api.impl.campaign.terrain.BaseTiledTerrain;
035import com.fs.starfarer.api.util.Misc;
036import com.fs.starfarer.api.util.WeightedRandomPicker;
037
038/**
039 * Hub missions should generally extend this class. The methods/classes defined in this class could easily
040 * be in BaseHubMission instead; this class exists purely for organizational purposes.
041 * 
042 * @author Alex Mosolov
043 *
044 * Copyright 2019 Fractal Softworks, LLC
045 */
046public abstract class HubMissionWithSearch extends HubMissionWithTriggers {
047
048//      /**
049//       * "Center of mass".
050//       * @return
051//       */
052//      public Vector2f getApproximateLocationOfOtherMissions() {
053//              Vector2f com = new Vector2f();
054//              
055//              PersonAPI person = getPerson();
056//              MarketAPI market = null;
057//              if (person != null) market = person.getMarket();
058//              
059//              Vector2f playerLoc = Global.getSector().getPlayerFleet().getLocationInHyperspace();
060//              float thresholdDistForBonusAngle = 8000f;
061//              
062//              float count = 0f;
063//              for (BaseHubMission curr : BaseMissionHub.getCreatedMissionsList(person, market)) {
064//                      if (curr.getStartingStage() == null) continue;
065//                      SectorEntityToken loc = curr.getMapLocation(null, curr.getStartingStage());
066//                      if (loc == null) continue;
067//                      
068//                      // closer locations are weighed a lot less since they can really swing the angle a lot
069//                      float f = 1f;
070//                      float dist = Misc.getDistance(playerLoc, loc.getLocationInHyperspace());
071//                      if (dist < thresholdDistForBonusAngle) {
072//                              f = dist / thresholdDistForBonusAngle;
073//                              f *= f;
074//                      }
075//                      
076//                      // rather, actually, just skip the closer locations
077//                      if (f < 1f) continue;
078//                      
079//                      Vector2f add = new Vector2f(loc.getLocationInHyperspace());
080//                      add.scale(f);
081//                      Vector2f.add(com, add, com);
082//                      count += f;
083//              }
084//              if (count > 0) {
085//                      com.scale(1f / count);
086//              }
087//              return com;
088//      }
089        
090        public boolean matchesSetMissionAngle(Vector2f other, float allowedArc, 
091                                                                                                          float allowedArcBonusIfClose) {
092                Vector2f playerLoc = Global.getSector().getPlayerFleet().getLocationInHyperspace();
093                
094                PersonAPI person = getPerson();
095                MarketAPI market = null;
096                if (person != null) market = person.getMarket();
097                
098                float missionAngle = BaseMissionHub.getMissionAngle(person, market);
099                
100                float angleToOther = Misc.getAngleInDegrees(playerLoc, other); 
101                float distToOther = Misc.getDistance(playerLoc, other); 
102                
103                float angleDiff = Misc.getAngleDiff(missionAngle, angleToOther);
104                
105                float maxAllowedAngleDiff = allowedArc / 2f;
106                float thresholdDistForBonusAngle = 5000f;
107                if (distToOther < thresholdDistForBonusAngle) {
108                        float f = (1f - distToOther / thresholdDistForBonusAngle);
109                        //f *= f;
110                        maxAllowedAngleDiff += f * allowedArcBonusIfClose / 2f;
111                }
112                return angleDiff <= maxAllowedAngleDiff;
113        }
114        
115        public static float DEFAULT_MISSION_ARC = 60f;
116        public static float DEFAULT_MISSION_ARC_BONUS = 90f;
117        public static float DEFAULT_MISSION_MARKET_ARC = 150f;
118        public static float DEFAULT_MISSION_MARKET_ARC_BONUS = 210f;
119        
120        public static class SystemInDirectionOfOtherMissionsReq implements StarSystemRequirement {
121                protected HubMissionWithSearch mission;
122                protected float arcMult;
123                public SystemInDirectionOfOtherMissionsReq(HubMissionWithSearch mission, float arcMult) {
124                        this.mission = mission;
125                        this.arcMult = arcMult;
126                }
127                public boolean systemMatchesRequirement(StarSystemAPI system) {
128                        return mission.matchesSetMissionAngle(system.getLocation(), 
129                                        DEFAULT_MISSION_ARC * arcMult, 90f);
130                }
131        }
132        
133        public static class MarketInDirectionOfOtherMissionsReq implements MarketRequirement {
134                protected HubMissionWithSearch mission;
135                protected float arcMult;
136                public MarketInDirectionOfOtherMissionsReq(HubMissionWithSearch mission, float arcMult) {
137                        this.mission = mission;
138                        this.arcMult = arcMult;
139                }
140                public boolean marketMatchesRequirement(MarketAPI market) {
141                        return mission.matchesSetMissionAngle(market.getLocationInHyperspace(), 
142                                                DEFAULT_MISSION_MARKET_ARC * arcMult, DEFAULT_MISSION_MARKET_ARC_BONUS);
143                }
144        }
145        
146        public static class PlanetInDirectionOfOtherMissionsReq implements PlanetRequirement {
147                protected HubMissionWithSearch mission;
148                protected float arcMult;
149                public PlanetInDirectionOfOtherMissionsReq(HubMissionWithSearch mission, float arcMult) {
150                        this.mission = mission;
151                        this.arcMult = arcMult;
152                }
153                public boolean planetMatchesRequirement(PlanetAPI planet) {
154//                      if (planet.getStarSystem().getNameWithNoType().toLowerCase().equals("megaron")) {
155//                              System.out.println("ewfwefwefwe");
156//                      }
157                        return mission.matchesSetMissionAngle(planet.getLocationInHyperspace(), 
158                                        DEFAULT_MISSION_ARC * arcMult, DEFAULT_MISSION_ARC_BONUS);
159                }
160        }
161        
162        public static class EntityInDirectionOfOtherMissionsReq implements EntityRequirement {
163                protected HubMissionWithSearch mission;
164                protected float arcMult;
165                public EntityInDirectionOfOtherMissionsReq(HubMissionWithSearch mission, float arcMult) {
166                        this.mission = mission;
167                        this.arcMult = arcMult;
168                }
169                public boolean entityMatchesRequirement(SectorEntityToken entity) {
170                        return mission.matchesSetMissionAngle(entity.getLocationInHyperspace(),
171                                        DEFAULT_MISSION_ARC * arcMult, DEFAULT_MISSION_ARC_BONUS);
172                }
173        }
174        
175        public static class TerrainInDirectionOfOtherMissionsReq implements TerrainRequirement {
176                protected HubMissionWithSearch mission;
177                protected float arcMult;
178                public TerrainInDirectionOfOtherMissionsReq(HubMissionWithSearch mission, float arcMult) {
179                        this.mission = mission;
180                        this.arcMult = arcMult;
181                }
182                public boolean terrainMatchesRequirement(CampaignTerrainAPI terrain) {
183                        return mission.matchesSetMissionAngle(terrain.getLocationInHyperspace(),
184                                        DEFAULT_MISSION_ARC * arcMult, DEFAULT_MISSION_ARC_BONUS);
185                }
186        }
187        
188
189        public static class SystemInDirection implements StarSystemRequirement {
190                protected float dir;
191                protected float arc;
192                protected HubMissionWithSearch mission;
193                
194                public SystemInDirection(HubMissionWithSearch mission, float dir, float arc) {
195                        this.mission = mission;
196                        this.dir = dir;
197                        this.arc = arc;
198                }
199                public boolean systemMatchesRequirement(StarSystemAPI system) {
200                        if (mission.getPerson() == null) return false;
201                        if (mission.getPerson().getMarket() == null) return false;
202                        Vector2f from = mission.getPerson().getMarket().getLocationInHyperspace();
203                        return Misc.isInArc(dir, arc, from, system.getLocation());
204                }
205        }
206        
207        public static class SystemInDirectionFrom implements StarSystemRequirement {
208                protected Vector2f from;
209                protected float dir;
210                protected float arc;
211                
212                public SystemInDirectionFrom(Vector2f from, float dir, float arc) {
213                        this.from = from;
214                        this.dir = dir;
215                        this.arc = arc;
216                }
217                public boolean systemMatchesRequirement(StarSystemAPI system) {
218                        return Misc.isInArc(dir, arc, from, system.getLocation());
219                }
220        }
221        
222        public static class MarketIsReq implements MarketRequirement {
223                boolean negate;
224                MarketAPI param;
225                public MarketIsReq(MarketAPI param, boolean negate) {
226                        this.param = param;
227                        this.negate = negate;
228                }
229                public boolean marketMatchesRequirement(MarketAPI market) {
230                        boolean result = market == param;
231                        if (negate) result = !result;
232                        return result;
233                }
234        }
235        public static class MarketFactionReq implements MarketRequirement {
236                boolean negate;
237                Set<String> factions;
238                public MarketFactionReq(boolean negate, String ... factions) {
239                        this.factions = new LinkedHashSet<String>();
240                        this.factions.addAll(Arrays.asList(factions));
241                        this.negate = negate;
242                }
243                public boolean marketMatchesRequirement(MarketAPI market) {
244                        boolean result = factions.contains(market.getFactionId());
245                        if (negate) result = !result;
246                        return result;
247                }
248        }
249        
250        public static class MarketTacticalBombardableReq implements MarketRequirement {
251                boolean negate;
252                public MarketTacticalBombardableReq(boolean negate) {
253                        this.negate = negate;
254                }
255                public boolean marketMatchesRequirement(MarketAPI market) {
256                        boolean result = !MarketCMD.getTacticalBombardmentTargets(market).isEmpty();
257                        if (negate) result = !result;
258                        return result;
259                }
260        }
261        
262        public static class MarketFactionHostileReq implements MarketRequirement {
263                boolean negate;
264                String faction;
265                public MarketFactionHostileReq(boolean negate, String faction) {
266                        this.faction = faction;
267                        this.negate = negate;
268                }
269                public boolean marketMatchesRequirement(MarketAPI market) {
270                        boolean result = market.getFaction().isHostileTo(faction);
271                        if (negate) result = !result;
272                        return result;
273                }
274        }
275//      public static class MarketFactionCustomFlagReq implements MarketRequirement {
276//              boolean negate;
277//              String flag;
278//              public MarketFactionCustomFlagReq(boolean negate, String flag) {
279//                      this.flag = flag;
280//                      this.negate = negate;
281//              }
282//              public boolean marketMatchesRequirement(MarketAPI market) {
283//                      boolean result = market.getFaction().getCustomBoolean(flag);
284//                      if (negate) result = !result;
285//                      return result;
286//              }
287//      }
288        
289        public static class MarketMilitaryReq implements MarketRequirement {
290                public boolean marketMatchesRequirement(MarketAPI market) {
291                        return Misc.isMilitary(market);
292                }
293        }
294        public static class MarketNotMilitaryReq implements MarketRequirement {
295                public boolean marketMatchesRequirement(MarketAPI market) {
296                        return !Misc.isMilitary(market);
297                }
298        }
299        
300        public static class MarketHiddenReq implements MarketRequirement {
301                public boolean marketMatchesRequirement(MarketAPI market) {
302                        return market.isHidden();
303                }
304        }
305        public static class MarketNotHiddenReq implements MarketRequirement {
306                public boolean marketMatchesRequirement(MarketAPI market) {
307                        return !market.isHidden();
308                        //return !market.isHidden() && !market.isInvalidMissionTarget();
309                }
310        }
311        public static class MarketNotInHyperReq implements MarketRequirement {
312                public boolean marketMatchesRequirement(MarketAPI market) {
313                        return !market.isInHyperspace();
314                }
315        }
316        
317        public static class MarketMemoryFlagReq implements MarketRequirement {
318                String key;
319                Object value;
320                public MarketMemoryFlagReq(String key, Object value) {
321                        this.key = key;
322                        this.value = value;
323                }
324                public boolean marketMatchesRequirement(MarketAPI market) {
325                        Object val = market.getMemoryWithoutUpdate().get(key);
326                        return val != null && val.equals(value);
327                }
328        }
329        
330        public static class MarketLocationReq implements MarketRequirement {
331                boolean negate;
332                Set<LocationAPI> locations;
333                public MarketLocationReq(boolean negate, LocationAPI ... locations) {
334                        this.locations = new LinkedHashSet<LocationAPI>();
335                        this.locations.addAll(Arrays.asList(locations));
336                        this.negate = negate;
337                }
338                public boolean marketMatchesRequirement(MarketAPI market) {
339                        boolean result = locations.contains(market.getContainingLocation());
340                        if (negate) result = !result;
341                        return result;
342                }
343        }
344        public static class MarketFactionCustomReq extends StringCollectionReqs implements MarketRequirement {
345                public MarketFactionCustomReq(ReqMode mode, String ... custom) {
346                        super(mode, custom);
347                }
348                public boolean marketMatchesRequirement(MarketAPI market) {
349                        Set<String> set = new HashSet<String>();
350                        for (String custom : tags) {
351                                if (market.getFaction().getCustomBoolean(custom)) {
352                                        set.add(custom);
353                                }
354                        }
355                        return matchesRequirements(set);
356                }
357        }
358        
359        public static interface GenericRequirement {
360                
361        }
362        public static interface StarSystemRequirement extends GenericRequirement {
363                public boolean systemMatchesRequirement(StarSystemAPI system);
364        }
365        public static interface PlanetRequirement extends GenericRequirement {
366                public boolean planetMatchesRequirement(PlanetAPI planet);
367        }
368        public static interface EntityRequirement extends GenericRequirement {
369                public boolean entityMatchesRequirement(SectorEntityToken entity);
370        }
371        public static interface MarketRequirement extends GenericRequirement {
372                public boolean marketMatchesRequirement(MarketAPI market);
373        }
374        public static interface CommodityRequirement extends GenericRequirement {
375                public boolean commodityMatchesRequirement(CommodityOnMarketAPI com);
376        }
377        public static interface TerrainRequirement extends GenericRequirement {
378                public boolean terrainMatchesRequirement(CampaignTerrainAPI terrain);
379        }
380        
381        public static boolean matchesReq(GenericRequirement req, Object param) {
382                if (req instanceof StarSystemRequirement) {
383                        return ((StarSystemRequirement) req).systemMatchesRequirement((StarSystemAPI) param);
384                }
385                if (req instanceof PlanetRequirement) {
386                        return ((PlanetRequirement) req).planetMatchesRequirement((PlanetAPI) param);
387                }
388                if (req instanceof EntityRequirement) {
389                        return ((EntityRequirement) req).entityMatchesRequirement((SectorEntityToken) param);
390                }
391                if (req instanceof MarketRequirement) {
392                        if (((MarketAPI)param).isInvalidMissionTarget()) return false;
393                        return ((MarketRequirement) req).marketMatchesRequirement((MarketAPI) param);
394                }
395                if (req instanceof CommodityRequirement) {
396                        return ((CommodityRequirement) req).commodityMatchesRequirement((CommodityOnMarketAPI) param);
397                }
398                if (req instanceof TerrainRequirement) {
399                        return ((TerrainRequirement) req).terrainMatchesRequirement((CampaignTerrainAPI) param);
400                }
401                return false;
402        }
403        
404        public static class TerrainTypeReq extends StringCollectionReqs implements TerrainRequirement {
405                public TerrainTypeReq(ReqMode mode, String[] types) {
406                        super(mode, types);
407                }
408                public boolean terrainMatchesRequirement(CampaignTerrainAPI terrain) {
409                        Set<String> set = new HashSet<String>();
410                        set.add(terrain.getType());
411                        return matchesRequirements(set);
412                }
413        }
414        public static class TerrainHasSpecialNameReq implements TerrainRequirement {
415                public TerrainHasSpecialNameReq() {
416                }
417                public boolean terrainMatchesRequirement(CampaignTerrainAPI terrain) {
418                        String n1 = terrain.getPlugin().getTerrainName();
419                        String n2 = terrain.getPlugin().getNameForTooltip();
420                        if (n1 == null) return false;
421                        return (!n1.equals(n2));
422                }
423        }
424        public static class EntityUndiscoveredReq implements EntityRequirement {
425                boolean negate;
426                public EntityUndiscoveredReq(boolean negate) {
427                        this.negate = negate;
428                }
429                public boolean entityMatchesRequirement(SectorEntityToken entity) {
430                        boolean result = entity.isDiscoverable();
431                        if (negate) result = !result;
432                        return result;
433                }
434        }
435
436        public static class EntityTypeReq implements EntityRequirement {
437                private Set<String> set;
438                public EntityTypeReq(String[] types) {
439                        set = new LinkedHashSet<String>();
440                        for (String type : types) {
441                                set.add(type);
442                        }
443                }
444                public boolean entityMatchesRequirement(SectorEntityToken entity) {
445                        return set.contains(entity.getCustomEntityType());
446                }
447        }
448        
449        public static class EntityMemoryReq implements EntityRequirement {
450                private List<String> flags = new ArrayList<String>();
451                public EntityMemoryReq(String ... flags) {
452                        for (String flag : flags) {
453                                this.flags.add(flag);
454                        }
455                }
456                public boolean entityMatchesRequirement(SectorEntityToken entity) {
457                        for (String flag : flags) {
458                                if (!entity.getMemoryWithoutUpdate().getBoolean(flag)) return false;
459                        }
460                        return true;
461                }
462        }
463        
464        public static class SystemOnFringeOfSectorReq implements StarSystemRequirement {
465                protected float rangeLY = Global.getSettings().getFloat("sectorHeight") / Misc.getUnitsPerLightYear() * 
466                                                                                NON_FRINGE_PORTION_OF_HEIGHT * 0.5f;
467                protected boolean negate = false;
468                public SystemOnFringeOfSectorReq() {
469                        this(false);
470                }
471                public SystemOnFringeOfSectorReq(boolean negate) {
472                        this.negate = negate;
473                }
474                public boolean systemMatchesRequirement(StarSystemAPI system) {
475                        boolean result = Misc.getDistanceLY(new Vector2f(), system.getLocation()) > rangeLY;
476                        if (negate) result = !result;
477                        return result;
478                }
479        }
480        public static class SystemInInnerSectorReq implements StarSystemRequirement {
481                protected float rangeLY = Global.getSettings().getFloat("sectorHeight") / Misc.getUnitsPerLightYear() * 
482                                                                                INNER_SECTOR_PORTION_OF_HEIGHT * 0.5f;
483                protected boolean negate = false;
484                public SystemInInnerSectorReq() {
485                        this(false);
486                }
487                public SystemInInnerSectorReq(boolean negate) {
488                        this.negate = negate;
489                }
490                public boolean systemMatchesRequirement(StarSystemAPI system) {
491                        boolean result = Misc.getDistanceLY(new Vector2f(), system.getLocation()) < rangeLY;
492                        if (negate) result = !result;
493                        return result;
494                }
495        }
496        
497        public static class SystemWithinRangeReq implements StarSystemRequirement {
498                private Vector2f loc;
499                private float minRangeLY;
500                private float maxRangeLY;
501                public SystemWithinRangeReq(Vector2f loc, float minRangeLY, float maxRangeLY) {
502                        this.loc = loc;
503                        this.minRangeLY = minRangeLY;
504                        this.maxRangeLY = maxRangeLY;
505                }
506                public boolean systemMatchesRequirement(StarSystemAPI system) {
507                        float dist = Misc.getDistanceLY(loc, system.getLocation());
508                        return dist >= minRangeLY && dist <= maxRangeLY;
509                }
510        }
511        
512        public static class SystemHasBaseReq implements StarSystemRequirement {
513                private String factionId;
514                public SystemHasBaseReq(String factionId) {
515                        this.factionId = factionId;
516                }
517                public boolean systemMatchesRequirement(StarSystemAPI system) {
518                        for (MarketAPI market : Misc.getMarketsInLocation(system)) {
519                                if (!factionId.equals(market.getFactionId())) continue;
520                                if (market.getMemoryWithoutUpdate().getBoolean(MemFlags.HIDDEN_BASE_MEM_FLAG)) {
521                                        return true;
522                                }
523                        }
524                        return false;
525                }
526        }
527        
528        public static class SystemHasColonyReq implements StarSystemRequirement {
529                private String factionId;
530                private int minSize;
531                public SystemHasColonyReq(String factionId, int minSize) {
532                        this.factionId = factionId;
533                        this.minSize = minSize;
534                }
535                public boolean systemMatchesRequirement(StarSystemAPI system) {
536                        for (MarketAPI market : Misc.getMarketsInLocation(system)) {
537                                if (!factionId.equals(market.getFactionId())) continue;
538                                if (market.getSize() < minSize) continue;
539                                if (market.getMemoryWithoutUpdate().getBoolean(MemFlags.HIDDEN_BASE_MEM_FLAG)) {
540                                        continue;
541                                }
542                                return true;
543                        }
544                        return false;
545                }
546        }
547        
548        public static class MultipleStarSystemRequirements implements StarSystemRequirement {
549                protected StarSystemRequirement [] reqs;
550                protected ReqMode mode;
551                public MultipleStarSystemRequirements(ReqMode mode, StarSystemRequirement ... reqs) {
552                        this.mode = mode;
553                        this.reqs = reqs;
554                }
555                public boolean systemMatchesRequirement(StarSystemAPI system) {
556                        switch (mode) {
557                        case ALL:
558                                for (StarSystemRequirement req : reqs) if (!req.systemMatchesRequirement(system)) return false;
559                                return true;
560                        case ANY:
561                                for (StarSystemRequirement req : reqs) if (req.systemMatchesRequirement(system)) return true;
562                                return false;
563                        case NOT_ALL:
564                                for (StarSystemRequirement req : reqs) if (!req.systemMatchesRequirement(system)) return true;
565                                return false;
566                        case NOT_ANY:
567                                for (StarSystemRequirement req : reqs) if (req.systemMatchesRequirement(system)) return false;
568                                return true;
569                        }
570                        return false;
571                }
572        }
573        public static class MultiplePlanetRequirements implements PlanetRequirement {
574                protected PlanetRequirement [] reqs;
575                protected ReqMode mode;
576                public MultiplePlanetRequirements(ReqMode mode, PlanetRequirement ... reqs) {
577                        this.mode = mode;
578                        this.reqs = reqs;
579                }
580                public boolean planetMatchesRequirement(PlanetAPI planet) {
581                        switch (mode) {
582                        case ALL:
583                                for (PlanetRequirement req : reqs) if (!req.planetMatchesRequirement(planet)) return false;
584                                return true;
585                        case ANY:
586                                for (PlanetRequirement req : reqs) if (req.planetMatchesRequirement(planet)) return true;
587                                return false;
588                        case NOT_ALL:
589                                for (PlanetRequirement req : reqs) if (!req.planetMatchesRequirement(planet)) return true;
590                                return false;
591                        case NOT_ANY:
592                                for (PlanetRequirement req : reqs) if (req.planetMatchesRequirement(planet)) return false;
593                                return true;
594                        }
595                        return false;
596                }
597        }
598        public static class MultipleEntityRequirements implements EntityRequirement {
599                protected EntityRequirement [] reqs;
600                protected ReqMode mode;
601                public MultipleEntityRequirements(ReqMode mode, EntityRequirement ... reqs) {
602                        this.mode = mode;
603                        this.reqs = reqs;
604                }
605                public boolean entityMatchesRequirement(SectorEntityToken entity) {
606                        switch (mode) {
607                        case ALL:
608                                for (EntityRequirement req : reqs) if (!req.entityMatchesRequirement(entity)) return false;
609                                return true;
610                        case ANY:
611                                for (EntityRequirement req : reqs) if (req.entityMatchesRequirement(entity)) return true;
612                                return false;
613                        case NOT_ALL:
614                                for (EntityRequirement req : reqs) if (!req.entityMatchesRequirement(entity)) return true;
615                                return false;
616                        case NOT_ANY:
617                                for (EntityRequirement req : reqs) if (req.entityMatchesRequirement(entity)) return false;
618                                return true;
619                        }
620                        return false;
621                }
622        }
623        
624        public static class MultipleMarketRequirements implements MarketRequirement {
625                protected MarketRequirement [] reqs;
626                protected ReqMode mode;
627                public MultipleMarketRequirements(ReqMode mode, MarketRequirement ... reqs) {
628                        this.mode = mode;
629                        this.reqs = reqs;
630                }
631                public boolean marketMatchesRequirement(MarketAPI market) {
632                        switch (mode) {
633                        case ALL:
634                                for (MarketRequirement req : reqs) if (!req.marketMatchesRequirement(market)) return false;
635                                return true;
636                        case ANY:
637                                for (MarketRequirement req : reqs) if (req.marketMatchesRequirement(market)) return true;
638                                return false;
639                        case NOT_ALL:
640                                for (MarketRequirement req : reqs) if (!req.marketMatchesRequirement(market)) return true;
641                                return false;
642                        case NOT_ANY:
643                                for (MarketRequirement req : reqs) if (req.marketMatchesRequirement(market)) return false;
644                                return true;
645                        }
646                        return false;
647                }
648        }
649        
650        public static class MultipleCommodityRequirements implements CommodityRequirement {
651                protected CommodityRequirement [] reqs;
652                protected ReqMode mode;
653                public MultipleCommodityRequirements(ReqMode mode, CommodityRequirement ... reqs) {
654                        this.mode = mode;
655                        this.reqs = reqs;
656                }
657                public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
658                        switch (mode) {
659                        case ALL:
660                                for (CommodityRequirement req : reqs) if (!req.commodityMatchesRequirement(com)) return false;
661                                return true;
662                        case ANY:
663                                for (CommodityRequirement req : reqs) if (req.commodityMatchesRequirement(com)) return true;
664                                return false;
665                        case NOT_ALL:
666                                for (CommodityRequirement req : reqs) if (!req.commodityMatchesRequirement(com)) return true;
667                                return false;
668                        case NOT_ANY:
669                                for (CommodityRequirement req : reqs) if (req.commodityMatchesRequirement(com)) return false;
670                                return true;
671                        }
672                        return false;
673                }
674        }
675        
676        
677        public static class PlanetOrbitIsNotNearJumpPoint implements PlanetRequirement {
678                protected float checkDist;
679                public PlanetOrbitIsNotNearJumpPoint(float checkDist) {
680                        this.checkDist = checkDist;
681                }
682                public boolean planetMatchesRequirement(PlanetAPI planet) {
683                        StarSystemAPI system = planet.getStarSystem();
684                        if (system == null) return true;
685                        
686                        float planetDist = planet.getCircularOrbitRadius();
687                        if (planet.getOrbit() == null) {
688                                planetDist = planet.getLocation().length();
689                        }
690                        
691                        for (SectorEntityToken jp : system.getJumpPoints()) {
692                                float dist = 0;
693                                if (jp.getOrbitFocus() == planet.getOrbitFocus()) {
694                                        float jpDist = jp.getCircularOrbitRadius();
695                                        if (jp.getOrbitFocus() == null) {
696                                                jpDist = jp.getLocation().length();
697                                        }
698                                        dist = Math.abs(planetDist - jpDist);
699                                        if (jp.getOrbitFocus() == null && planet.getOrbitFocus() == null) {
700                                                dist = Misc.getDistance(planet, jp);
701                                        }
702                                } else if (jp.getOrbitFocus() == null && planet.getOrbitFocus() != null) {
703                                        float jpDist = Misc.getDistance(jp, planet.getOrbitFocus());
704                                        dist = Math.abs(planetDist - jpDist);
705                                        if (jp.getOrbitFocus() == null && planet.getOrbitFocus() == null) {
706                                                dist = Misc.getDistance(planet, jp);
707                                        }
708                                } else if (jp.getOrbitFocus() != null && planet.getOrbitFocus() == null) {
709                                        float jpDist = jp.getCircularOrbitRadius();
710                                        float pDist2 = Misc.getDistance(jp.getOrbitFocus(), jp);
711                                        dist = Math.abs(pDist2 - jpDist);
712                                        if (jp.getOrbitFocus() == null && planet.getOrbitFocus() == null) {
713                                                dist = Misc.getDistance(planet, jp);
714                                        }
715                                } else if (jp.getOrbitFocus() != null && planet.getOrbitFocus() != null) {
716                                        // orbiting different centers
717                                        float jpDist = jp.getCircularOrbitRadius();
718                                        dist = Misc.getDistance(jp.getOrbitFocus(), planet.getOrbitFocus());
719                                        dist -= planetDist + jpDist;
720                                }
721                                if (dist < checkDist) return false;
722                        }
723                        return true;
724                }
725        }
726        
727        public static class PlanetIsPopulatedReq implements PlanetRequirement {
728                protected boolean negate = false;
729                public PlanetIsPopulatedReq() {
730                        this(false);
731                }
732                public PlanetIsPopulatedReq(boolean negate) {
733                        this.negate = negate;
734                }
735                public boolean planetMatchesRequirement(PlanetAPI planet) {
736                        boolean result = planet.getMarket() != null && !planet.getMarket().isPlanetConditionMarketOnly();
737                        if (negate) result = !result;
738                        return result;
739                }
740        }
741        public static class PlanetUnexploredRuinsReq implements PlanetRequirement {
742                protected boolean negate = false;
743                public PlanetUnexploredRuinsReq() {
744                        this(false);
745                }
746                public PlanetUnexploredRuinsReq(boolean negate) {
747                        this.negate = negate;
748                }
749                public boolean planetMatchesRequirement(PlanetAPI planet) {
750                        MarketAPI market = planet.getMarket();
751                        boolean result = market != null && market.isPlanetConditionMarketOnly() && 
752                                                !market.getMemoryWithoutUpdate().getBoolean("$ruinsExplored");
753                        if (negate) result = !result;
754                        return result;
755                }
756        }
757        public static class PlanetFullySurveyedReq implements PlanetRequirement {
758                protected boolean negate = false;
759                public PlanetFullySurveyedReq() {
760                        this(false);
761                }
762                public PlanetFullySurveyedReq(boolean negate) {
763                        this.negate = negate;
764                }
765                public boolean planetMatchesRequirement(PlanetAPI planet) {
766                        boolean result = planet.getMarket() != null && planet.getMarket().getSurveyLevel() == SurveyLevel.FULL;
767                        if (negate) result = !result;
768                        return result;
769                }
770        }
771        public static class PlanetUnsurveyedReq implements PlanetRequirement {
772                protected boolean negate = false;
773                public PlanetUnsurveyedReq() {
774                        this(false);
775                }
776                public PlanetUnsurveyedReq(boolean negate) {
777                        this.negate = negate;
778                }
779                public boolean planetMatchesRequirement(PlanetAPI planet) {
780                        boolean result = planet.getMarket() != null && planet.getMarket().getSurveyLevel() == SurveyLevel.NONE;
781                        if (negate) result = !result;
782                        return result;
783                }
784        }
785        public static class PlanetIsGasGiantReq implements PlanetRequirement {
786                protected boolean negate = false;
787                public PlanetIsGasGiantReq() {
788                        this(false);
789                }
790                public PlanetIsGasGiantReq(boolean negate) {
791                        this.negate = negate;
792                }
793                public boolean planetMatchesRequirement(PlanetAPI planet) {
794                        boolean result = planet.isGasGiant();
795                        if (negate) result = !result;
796                        return result;
797                }
798        }
799        public static class SystemHasPulsarReq implements StarSystemRequirement {
800                protected boolean negate = false;
801                public SystemHasPulsarReq() {
802                        this(false);
803                }
804                public SystemHasPulsarReq(boolean negate) {
805                        this.negate = negate;
806                }
807                public boolean systemMatchesRequirement(StarSystemAPI system) {
808                        boolean result = Misc.hasPulsar(system);
809                        if (negate) result = !result;
810                        return result;
811                }
812        }
813        
814        public static class SystemHasAtLeastJumpPointsReq implements StarSystemRequirement {
815                protected int min = 0;
816                public SystemHasAtLeastJumpPointsReq(int min) {
817                        this.min = min;
818                }
819                public boolean systemMatchesRequirement(StarSystemAPI system) {
820                        return system.getJumpPoints().size() >= min;
821                }
822        }
823        
824        public static class SystemIsNebulaReq implements StarSystemRequirement {
825                protected boolean negate = false;
826                public SystemIsNebulaReq() {
827                        this(false);
828                }
829                public SystemIsNebulaReq(boolean negate) {
830                        this.negate = negate;
831                }
832                public boolean systemMatchesRequirement(StarSystemAPI system) {
833                        boolean result = system.isNebula();
834                        if (negate) result = !result;
835                        return result;
836                }
837        }
838        public static class SystemIsBlackHoleReq implements StarSystemRequirement {
839                protected boolean negate = false;
840                public SystemIsBlackHoleReq() {
841                        this(false);
842                }
843                public SystemIsBlackHoleReq(boolean negate) {
844                        this.negate = negate;
845                }
846                public boolean systemMatchesRequirement(StarSystemAPI system) {
847                        boolean result = system.getStar() != null && system.getStar().getSpec().isBlackHole();
848                        if (negate) result = !result;
849                        return result;
850                }
851        }
852        public static class StarSystemUnexploredReq implements StarSystemRequirement {
853                protected boolean negate = false;
854                public StarSystemUnexploredReq() {
855                        this(false);
856                }
857                public StarSystemUnexploredReq(boolean negate) {
858                        this.negate = negate;
859                }
860                public boolean systemMatchesRequirement(StarSystemAPI system) {
861//                      if (system.getNameWithNoType().equals("Stella Vitae")) {
862//                              System.out.println("wefwefwef");
863//                      }
864                        boolean result = !system.isEnteredByPlayer();
865                        if (DebugFlags.ALLOW_VIEW_UNEXPLORED_SYSTEM_MAP) {
866                                result = system.getDaysSinceLastPlayerVisit() >= 100000f;
867                        }
868                        if (negate) result = !result;
869                        return result;
870                }
871        }
872        
873        public static class StarSystemDaysSincePlayerVisitReq implements StarSystemRequirement {
874                protected float days;
875                public StarSystemDaysSincePlayerVisitReq(float days) {
876                        this.days = days;
877                }
878                public boolean systemMatchesRequirement(StarSystemAPI system) {
879                        boolean result = system.getDaysSinceLastPlayerVisit() >= days;
880                        return result;
881                }
882        }
883        public static class StarSystemHasNumPlanetsReq implements StarSystemRequirement {
884                protected int num;
885                public StarSystemHasNumPlanetsReq(int num) {
886                        this.num = num;
887                }
888                public boolean systemMatchesRequirement(StarSystemAPI system) {
889                        int stars = 0;
890                        if (system.getStar() != null) stars++;
891                        if (system.getSecondary() != null) stars++;
892                        if (system.getTertiary() != null) stars++;
893                        return system.getPlanets().size() - stars >= num;
894                }
895        }
896        
897        public static class StarSystemHasNumTerrainReq implements StarSystemRequirement {
898                protected int num;
899                public StarSystemHasNumTerrainReq(int num) {
900                        this.num = num;
901                }
902                public boolean systemMatchesRequirement(StarSystemAPI system) {
903                        return system.getTerrainCopy().size() >= num;
904                }
905        }
906        
907        public static class StarSystemHasNumPlanetsAndTerrainReq implements StarSystemRequirement {
908                protected int num;
909                public StarSystemHasNumPlanetsAndTerrainReq(int num) {
910                        this.num = num;
911                }
912                public boolean systemMatchesRequirement(StarSystemAPI system) {
913                        int stars = 0;
914                        if (system.getStar() != null) stars++;
915                        if (system.getSecondary() != null) stars++;
916                        if (system.getTertiary() != null) stars++;
917                        return system.getPlanets().size() - stars + system.getTerrainCopy().size() >= num;
918                }
919        }
920        
921        public static class StringCollectionReqs {
922                public ReqMode mode;
923                List<String> tags = new ArrayList<String>();
924                public StringCollectionReqs(ReqMode mode, String ... tags) {
925                        this.mode = mode;
926                        for (String tag : tags) {
927                                this.tags.add(tag);
928                        }
929                }
930                public boolean matchesRequirements(Collection<String> set) {
931                        switch (mode) {
932                        case ALL:
933                                for (String tag : tags) if (!set.contains(tag)) return false;
934                                return true;
935                        case ANY:
936                                for (String tag : tags) if (set.contains(tag)) return true;
937                                return false;
938                        case NOT_ALL:
939                                for (String tag : tags) if (!set.contains(tag)) return true;
940                                return false;
941                        case NOT_ANY:
942                                for (String tag : tags) if (set.contains(tag)) return false;
943                                return true;
944                        }
945                        return false;
946                }
947        }
948        
949        public static class RequiredSystemTags extends StringCollectionReqs implements StarSystemRequirement {
950                public RequiredSystemTags(ReqMode mode, String[] tags) {
951                        super(mode, tags);
952                }
953                public boolean systemMatchesRequirement(StarSystemAPI system) {
954                        return matchesRequirements(system.getTags());
955                }
956        }
957        public static class RequiredTerrainTags extends StringCollectionReqs implements TerrainRequirement {
958                public RequiredTerrainTags(ReqMode mode, String[] tags) {
959                        super(mode, tags);
960                }
961                public boolean terrainMatchesRequirement(CampaignTerrainAPI terrain) {
962                        return matchesRequirements(terrain.getTags());
963                }
964        }
965        public static class RequiredPlanetTags extends StringCollectionReqs implements PlanetRequirement {
966                public RequiredPlanetTags(ReqMode mode, String[] tags) {
967                        super(mode, tags);
968                }
969                public boolean planetMatchesRequirement(PlanetAPI planet) {
970                        return matchesRequirements(planet.getTags());
971                }
972        }
973        public static class RequiredEntityTags extends StringCollectionReqs implements EntityRequirement {
974                public RequiredEntityTags(ReqMode mode, String[] tags) {
975                        super(mode, tags);
976                }
977                public boolean entityMatchesRequirement(SectorEntityToken entity) {
978                        return matchesRequirements(entity.getTags());
979                }
980        }
981        public static class RequiredPlanetConditions extends StringCollectionReqs implements PlanetRequirement {
982                public RequiredPlanetConditions(ReqMode mode, String[] tags) {
983                        super(mode, tags);
984                }
985                public boolean planetMatchesRequirement(PlanetAPI planet) {
986                        List<String> set = new ArrayList<String>();
987                        if (planet.getMarket() != null) {
988                                for (MarketConditionAPI mc : planet.getMarket().getConditions()) {
989                                        set.add(mc.getId());
990                                }
991                        }
992                        return matchesRequirements(set);
993                }
994        }
995        
996        public static class RequiredCommodityTags extends StringCollectionReqs implements CommodityRequirement {
997                public RequiredCommodityTags(ReqMode mode, String[] tags) {
998                        super(mode, tags);
999                }
1000                public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
1001                        List<String> set = new ArrayList<String>();
1002                        set.addAll(com.getCommodity().getTags());
1003                        return matchesRequirements(set);
1004                }
1005        }
1006        
1007        public static class RequiredMarketConditions extends StringCollectionReqs implements MarketRequirement {
1008                public RequiredMarketConditions(ReqMode mode, String[] tags) {
1009                        super(mode, tags);
1010                }
1011                public boolean marketMatchesRequirement(MarketAPI market) {
1012                        List<String> set = new ArrayList<String>();
1013                        for (MarketConditionAPI mc : market.getConditions()) {
1014                                set.add(mc.getId());
1015                        }
1016                        return matchesRequirements(set);
1017                }
1018        }
1019        
1020        public static class RequiredMarketIndustries extends StringCollectionReqs implements MarketRequirement {
1021                public RequiredMarketIndustries(ReqMode mode, String[] tags) {
1022                        super(mode, tags);
1023                }
1024                public boolean marketMatchesRequirement(MarketAPI market) {
1025                        List<String> set = new ArrayList<String>();
1026                        for (Industry ind : market.getIndustries()) {
1027                                set.add(ind.getId());
1028                        }
1029                        return matchesRequirements(set);
1030                }
1031        }
1032        
1033        
1034        public static class SearchData {
1035                public List<GenericRequirement> systemReqs = new ArrayList<GenericRequirement>();
1036                public List<GenericRequirement> systemPrefs = new ArrayList<GenericRequirement>();
1037                public List<PlanetRequirement> planetReqs = new ArrayList<PlanetRequirement>();
1038                public List<PlanetRequirement> planetPrefs = new ArrayList<PlanetRequirement>();
1039                public List<EntityRequirement> entityReqs = new ArrayList<EntityRequirement>();
1040                public List<EntityRequirement> entityPrefs = new ArrayList<EntityRequirement>();
1041                public List<MarketRequirement> marketReqs = new ArrayList<MarketRequirement>();
1042                public List<MarketRequirement> marketPrefs = new ArrayList<MarketRequirement>();
1043                public List<TerrainRequirement> terrainReqs = new ArrayList<TerrainRequirement>();
1044                public List<TerrainRequirement> terrainPrefs = new ArrayList<TerrainRequirement>();
1045                public List<CommodityRequirement> commodityReqs = new ArrayList<CommodityRequirement>();
1046                public List<CommodityRequirement> commodityPrefs = new ArrayList<CommodityRequirement>();
1047                
1048                List<StarSystemAPI> matchingSystems = null;
1049                List<StarSystemAPI> preferredSystems = null;
1050                
1051                List<MarketAPI> matchingMarkets = null;
1052                List<MarketAPI> preferredMarkets = null;
1053        }
1054        
1055        protected transient SearchData search = new SearchData();
1056        
1057        public SearchData getSearch() {
1058                return search;
1059        }
1060        public void resetSearch() {
1061                search = new SearchData();
1062        }
1063        
1064        public void requireSystemInterestingAndNotCore() {
1065                requireSystemTags(ReqMode.NOT_ANY, Tags.THEME_CORE);
1066                preferSystemTags(ReqMode.ANY, Tags.THEME_INTERESTING, Tags.THEME_INTERESTING_MINOR);
1067        }
1068        public void requireSystemInterestingAndNotUnsafeOrCore() {
1069                requireSystemTags(ReqMode.NOT_ANY, Tags.THEME_UNSAFE, Tags.THEME_CORE);
1070                preferSystemTags(ReqMode.ANY, Tags.THEME_INTERESTING, Tags.THEME_INTERESTING_MINOR);
1071        }
1072        public void preferSystemInteresting() {
1073                preferSystemTags(ReqMode.ANY, Tags.THEME_INTERESTING, Tags.THEME_INTERESTING_MINOR);
1074        }
1075        
1076        public void preferSystemInDirectionOfOtherMissions() {
1077                search.systemPrefs.add(new SystemInDirectionOfOtherMissionsReq(this, 1f));
1078        }
1079        
1080        public void requireSystemInDirection(float dir, float arc) {
1081                search.systemReqs.add(new SystemInDirection(this, dir, arc));
1082        }
1083        
1084        public void preferSystemInDirection(float dir, float arc) {
1085                search.systemPrefs.add(new SystemInDirection(this, dir, arc));
1086        }
1087        
1088        public void requireSystemInDirectionFrom(Vector2f from, float dir, float arc) {
1089                search.systemReqs.add(new SystemInDirectionFrom(from, dir, arc));
1090        }
1091        
1092        public void preferSystemInDirectionFrom(Vector2f from, float dir, float arc) {
1093                search.systemPrefs.add(new SystemInDirectionFrom(from, dir, arc));
1094        }
1095        
1096        /**
1097         * Shouldn't use "preferSystem" for these because the systems are picked BEFORE planets are checked so 
1098         * e.g. we may pick 20 systems that "match", find that none of them have planets that match, and fall
1099         * back to the full set of systems. Using the preferPlanets method, we'll look at all-direction systems
1100         * and filter them out at the planet level. 
1101         */
1102        public void preferPlanetInDirectionOfOtherMissions() {
1103                search.planetPrefs.add(new PlanetInDirectionOfOtherMissionsReq(this, 1f));
1104        }
1105        
1106        public void preferEntityInDirectionOfOtherMissions() {
1107                search.entityPrefs.add(new EntityInDirectionOfOtherMissionsReq(this, 1f));
1108        }
1109        
1110        public void preferTerrainInDirectionOfOtherMissions() {
1111                search.terrainPrefs.add(new TerrainInDirectionOfOtherMissionsReq(this, 1f));
1112        }
1113        
1114        public void preferMarketInDirectionOfOtherMissions() {
1115                search.marketPrefs.add(new MarketInDirectionOfOtherMissionsReq(this, 1f));
1116        }
1117        
1118        public void requireSystemTags(ReqMode mode, String ... tags) {
1119                search.systemReqs.add(new RequiredSystemTags(mode, tags));
1120        }
1121        public void preferSystemTags(ReqMode mode, String ... tags) {
1122                search.systemPrefs.add(new RequiredSystemTags(mode, tags));
1123        }
1124        public void requireSystemHasBase(String factionId) {
1125                search.systemReqs.add(new SystemHasBaseReq(factionId));
1126        }
1127        public void preferSystemHasBase(String factionId) {
1128                search.systemPrefs.add(new SystemHasBaseReq(factionId));
1129        }
1130        public void requireSystemHasColony(String factionId, int minSize) {
1131                search.systemReqs.add(new SystemHasColonyReq(factionId, minSize));
1132        }
1133        public void preferSystemHasColony(String factionId, int minSize) {
1134                search.systemPrefs.add(new SystemHasColonyReq(factionId, minSize));
1135        }
1136        
1137        public void requireSystemHasAtLeastNumJumpPoints(int min) {
1138                search.systemReqs.add(new SystemHasAtLeastJumpPointsReq(min));
1139        }
1140        public void preferSystemHasAtLeastNumJumpPoints(int min) {
1141                search.systemPrefs.add(new SystemHasAtLeastJumpPointsReq(min));
1142        }
1143        
1144        public void requireSystemUnexplored() {
1145                search.systemReqs.add(new StarSystemUnexploredReq());
1146        }
1147        public void preferSystemUnexplored() {
1148                search.systemPrefs.add(new StarSystemUnexploredReq());
1149                preferSystemNotEnteredByPlayerFor(365f); // fallback for when everything is explored
1150        }
1151        
1152        public void requireSystemNotEnteredByPlayerFor(float days) {
1153                search.systemReqs.add(new StarSystemDaysSincePlayerVisitReq(days));
1154        }
1155        public void preferSystemNotEnteredByPlayerFor(float days) {
1156                search.systemPrefs.add(new StarSystemDaysSincePlayerVisitReq(days));
1157        }
1158        
1159        public void requireSystemExplored() {
1160                search.systemReqs.add(new StarSystemUnexploredReq(true));
1161        }
1162        public void preferSystemExplored() {
1163                search.systemPrefs.add(new StarSystemUnexploredReq(true));
1164        }
1165        public void requireSystemHasNumPlanets(int num) {
1166                search.systemReqs.add(new StarSystemHasNumPlanetsReq(num));
1167        }
1168        public void preferSystemHasNumPlanets(int num) {
1169                search.systemPrefs.add(new StarSystemHasNumPlanetsReq(num));
1170        }
1171        
1172        public void requireSystemHasNumTerrain(int num) {
1173                search.systemReqs.add(new StarSystemHasNumTerrainReq(num));
1174        }
1175        public void preferSystemHasNumTerrain(int num) {
1176                search.systemPrefs.add(new StarSystemHasNumTerrainReq(num));
1177        }
1178        
1179        public void requireSystemHasNumPlanetsAndTerrain(int num) {
1180                search.systemReqs.add(new StarSystemHasNumPlanetsAndTerrainReq(num));
1181        }
1182        public void preferSystemHasNumPlanetsAndTerrain(int num) {
1183                search.systemPrefs.add(new StarSystemHasNumTerrainReq(num));
1184        }
1185        
1186        public void requireSystemIsDense() {
1187                requireSystemHasNumPlanets(3);
1188                requireSystemHasNumTerrain(3);
1189                requireSystemHasNumPlanetsAndTerrain(10);
1190        }
1191        public void preferSystemIsDense() {
1192                preferSystemHasNumPlanets(3);
1193                preferSystemHasNumTerrain(3);
1194                preferSystemHasNumPlanetsAndTerrain(10);
1195        }
1196        
1197        public void requireSystemBlackHole() {
1198                search.systemReqs.add(new SystemIsBlackHoleReq());
1199        }
1200        public void requireSystemNebula() {
1201                search.systemReqs.add(new SystemIsNebulaReq());
1202        }
1203        public void requireSystemHasPulsar() {
1204                search.systemReqs.add(new SystemHasPulsarReq());
1205        }
1206        public void preferSystemBlackHole() {
1207                search.systemPrefs.add(new SystemIsBlackHoleReq());
1208        }
1209        public void preferSystemNebula() {
1210                search.systemPrefs.add(new SystemIsNebulaReq());
1211        }
1212        public void preferSystemHasPulsar() {
1213                search.systemPrefs.add(new SystemHasPulsarReq());
1214        }
1215        
1216        public void requireSystemBlackHoleOrPulsarOrNebula() {
1217                search.systemReqs.add(new MultipleStarSystemRequirements(ReqMode.ANY, 
1218                                new SystemIsBlackHoleReq(), new SystemHasPulsarReq(), new SystemIsNebulaReq()));
1219        }
1220        public void preferSystemBlackHoleOrPulsarOrNebula() {
1221                search.systemPrefs.add(new MultipleStarSystemRequirements(ReqMode.ANY, 
1222                                new SystemIsBlackHoleReq(), new SystemHasPulsarReq(), new SystemIsNebulaReq()));
1223        }
1224        
1225        public void requireSystemBlackHoleOrNebula() {
1226                search.systemReqs.add(new MultipleStarSystemRequirements(ReqMode.ANY, 
1227                                new SystemIsBlackHoleReq(), new SystemIsNebulaReq()));
1228        }
1229        public void preferSystemBlackHoleOrNebula() {
1230                search.systemPrefs.add(new MultipleStarSystemRequirements(ReqMode.ANY, 
1231                                new SystemIsBlackHoleReq(), new SystemIsNebulaReq()));
1232        }
1233        
1234        public void requireSystemNotBlackHole() {
1235                search.systemReqs.add(new SystemIsBlackHoleReq(true));
1236        }
1237        public void requireSystemNotNebula() {
1238                search.systemReqs.add(new SystemIsNebulaReq(true));
1239        }
1240        public void requireSystemNotHasPulsar() {
1241                search.systemReqs.add(new SystemHasPulsarReq(true));
1242        }
1243        public void requireSystemNotAlreadyUsedForStory() {
1244                requireSystemTags(ReqMode.NOT_ANY, Tags.SYSTEM_ALREADY_USED_FOR_STORY);
1245        }
1246        
1247        /**
1248         * To avoid re-using the same system for different story things.
1249         * @param stage
1250         * @param system
1251         */
1252        public void setSystemWasUsedForStory(Object stage, StarSystemAPI system) {
1253                beginStageTrigger(stage);
1254                triggerAddTagAfterDelay(0f, system, Tags.SYSTEM_ALREADY_USED_FOR_STORY);
1255                endTrigger();
1256        }
1257        public void preferSystemNotBlackHole() {
1258                search.systemPrefs.add(new SystemIsBlackHoleReq(true));
1259        }
1260        public void preferSystemNotNebula() {
1261                search.systemPrefs.add(new SystemIsNebulaReq(true));
1262        }
1263        public void preferSystemNotPulsar() {
1264                search.systemPrefs.add(new SystemHasPulsarReq(true));
1265        }
1266        
1267        public void requireSystemHasSafeStars() {
1268                requireSystemNotBlackHole();
1269                requireSystemNotHasPulsar();
1270        }
1271        
1272        public static float INNER_SECTOR_PORTION_OF_HEIGHT = 0.7f;
1273        public static float NON_FRINGE_PORTION_OF_HEIGHT = 0.7f;
1274        public void requireSystemInInnerSector() {
1275                search.systemReqs.add(new SystemInInnerSectorReq());
1276        }
1277        public void preferSystemInInnerSector() {
1278                search.systemPrefs.add(new SystemInInnerSectorReq());
1279        }
1280        public void requireSystemOnFringeOfSector() {
1281                search.systemReqs.add(new SystemOnFringeOfSectorReq());
1282        }
1283        public void preferSystemOnFringeOfSector() {
1284                search.systemPrefs.add(new SystemOnFringeOfSectorReq());
1285        }
1286        
1287        public void requireSystemWithinRangeOf(Vector2f location, float rangeLY) {
1288                search.systemReqs.add(new SystemWithinRangeReq(location, 0, rangeLY));
1289        }
1290        public void preferSystemWithinRangeOf(Vector2f location, float rangeLY) {
1291                search.systemPrefs.add(new SystemWithinRangeReq(location, 0, rangeLY));
1292        }
1293        
1294        public void requireSystemOutsideRangeOf(Vector2f location, float rangeLY) {
1295                search.systemReqs.add(new SystemWithinRangeReq(location, rangeLY, 1000000f));
1296        }
1297        public void preferSystemOutsideRangeOf(Vector2f location, float rangeLY) {
1298                search.systemPrefs.add(new SystemWithinRangeReq(location, rangeLY, 1000000));
1299        }
1300        
1301        public void requireSystemWithinRangeOf(Vector2f location, float minRangeLY, float maxRangeLY) {
1302                search.systemReqs.add(new SystemWithinRangeReq(location, minRangeLY, maxRangeLY));
1303        }
1304        public void preferSystemWithinRangeOf(Vector2f location, float minRangeLY, float maxRangeLY) {
1305                search.systemPrefs.add(new SystemWithinRangeReq(location, minRangeLY, maxRangeLY));
1306        }
1307        
1308        public void requirePlanetNotStar() {
1309                requirePlanetTags(ReqMode.NOT_ANY, Tags.STAR);
1310        }
1311        public void requirePlanetIsStar() {
1312                requirePlanetTags(ReqMode.ALL, Tags.STAR);
1313        }
1314        
1315        public void requirePlanetNotGasGiant() {
1316                search.planetReqs.add(new PlanetIsGasGiantReq(true));
1317        }
1318        public void preferPlanetNonGasGiant() {
1319                search.planetPrefs.add(new PlanetIsGasGiantReq(true));
1320        }
1321        
1322        public void requirePlanetNotNearJumpPoint(float minDist) {
1323                search.planetReqs.add(new PlanetOrbitIsNotNearJumpPoint(minDist));
1324        }
1325        public void preferPlanetNotNearJumpPoint(float minDist) {
1326                search.planetPrefs.add(new PlanetOrbitIsNotNearJumpPoint(minDist));
1327        }
1328        
1329        public void requirePlanetIsGasGiant() {
1330                search.planetReqs.add(new PlanetIsGasGiantReq());
1331        }
1332        public void preferPlanetIsGasGiant() {
1333                search.planetPrefs.add(new PlanetIsGasGiantReq());
1334        }
1335        
1336        public void requirePlanetPopulated() {
1337                search.planetReqs.add(new PlanetIsPopulatedReq());
1338        }
1339        public void preferPlanetPopulated() {
1340                search.planetPrefs.add(new PlanetIsPopulatedReq());
1341        }
1342        public void requirePlanetUnpopulated() {
1343                search.planetReqs.add(new PlanetIsPopulatedReq(true));
1344        }
1345        public void preferPlanetUnpopulated() {
1346                search.planetPrefs.add(new PlanetIsPopulatedReq(true));
1347        }
1348        
1349        public void requirePlanetTags(ReqMode mode, String ... tags) {
1350                search.planetReqs.add(new RequiredPlanetTags(mode, tags));
1351        }
1352        public void preferPlanetTags(ReqMode mode, String ... tags) {
1353                search.planetPrefs.add(new RequiredPlanetTags(mode, tags));
1354        }
1355        
1356        public void requirePlanetConditions(ReqMode mode, String ... tags) {
1357                search.planetReqs.add(new RequiredPlanetConditions(mode, tags));
1358        }
1359        public void preferPlanetConditions(ReqMode mode, String ... conditions) {
1360                search.planetPrefs.add(new RequiredPlanetConditions(mode, conditions));
1361        }
1362        
1363        public void requirePlanetNotFullySurveyed() {
1364                search.planetReqs.add(new PlanetFullySurveyedReq(true));
1365        }
1366        public void preferPlanetNotFullySurveyed() {
1367                search.planetPrefs.add(new PlanetFullySurveyedReq(true));
1368        }
1369        public void requirePlanetFullySurveyed() {
1370                search.planetReqs.add(new PlanetFullySurveyedReq());
1371        }
1372        public void preferPlanetFullySurveyed() {
1373                search.planetPrefs.add(new PlanetFullySurveyedReq());
1374        }
1375        public void preferPlanetUnsurveyed() {
1376                search.planetPrefs.add(new PlanetUnsurveyedReq());
1377        }
1378        public void requirePlanetUnsurveyed() {
1379                search.planetReqs.add(new PlanetUnsurveyedReq());
1380        }
1381        
1382        public void requirePlanetWithRuins() {
1383                requirePlanetConditions(ReqMode.ANY, Conditions.RUINS_SCATTERED, Conditions.RUINS_WIDESPREAD,
1384                                                                Conditions.RUINS_EXTENSIVE, Conditions.RUINS_VAST);
1385        }
1386        public void preferPlanetWithRuins() {
1387                preferPlanetConditions(ReqMode.ANY, Conditions.RUINS_SCATTERED, Conditions.RUINS_WIDESPREAD,
1388                                Conditions.RUINS_EXTENSIVE, Conditions.RUINS_VAST);
1389        }
1390        public void requirePlanetWithoutRuins() {
1391                requirePlanetConditions(ReqMode.NOT_ANY, Conditions.RUINS_SCATTERED, Conditions.RUINS_WIDESPREAD,
1392                                Conditions.RUINS_EXTENSIVE, Conditions.RUINS_VAST);
1393        }
1394        public void preferPlanetWithoutRuins() {
1395                preferPlanetConditions(ReqMode.NOT_ANY, Conditions.RUINS_SCATTERED, Conditions.RUINS_WIDESPREAD,
1396                                Conditions.RUINS_EXTENSIVE, Conditions.RUINS_VAST);
1397        }
1398        
1399        public void requirePlanetUnexploredRuins() {
1400                search.planetReqs.add(new PlanetUnexploredRuinsReq());
1401        }
1402        public void preferPlanetUnexploredRuins() {
1403                search.planetPrefs.add(new PlanetUnexploredRuinsReq());
1404        }
1405        
1406        
1407        public void requireEntityTags(ReqMode mode, String ... tags) {
1408                search.entityReqs.add(new RequiredEntityTags(mode, tags));
1409        }
1410        public void preferEntityTags(ReqMode mode, String ... tags) {
1411                search.entityPrefs.add(new RequiredEntityTags(mode, tags));
1412        }
1413        
1414        public void requireEntityType(String ... types) {
1415                search.entityReqs.add(new EntityTypeReq(types));
1416        }
1417        public void preferEntityType(String ... types) {
1418                search.entityPrefs.add(new EntityTypeReq(types));
1419        }
1420        
1421        public void requireEntityMemoryFlags(String ... flags) {
1422                search.entityReqs.add(new EntityMemoryReq(flags));
1423        }
1424        public void preferEntityMemoryFlags(String ... flags) {
1425                search.entityPrefs.add(new EntityMemoryReq(flags));
1426        }
1427        
1428        public void requireEntityUndiscovered() {
1429                search.entityReqs.add(new EntityUndiscoveredReq(false));
1430        }
1431        public void preferEntityUndiscovered() {
1432                search.entityPrefs.add(new EntityUndiscoveredReq(false));
1433        }
1434        
1435        public void requireEntityNot(final SectorEntityToken entity) {
1436                search.entityReqs.add(new EntityRequirement() {
1437                        public boolean entityMatchesRequirement(SectorEntityToken param) {
1438                                return entity != param;
1439                        }
1440                });
1441        }
1442        public void requirePlanetNot(final PlanetAPI planet) {
1443                search.planetReqs.add(new PlanetRequirement() {
1444                        public boolean planetMatchesRequirement(PlanetAPI param) {
1445                                return planet != param;
1446                        }
1447                });
1448        }
1449        public void requireSystemNot(final StarSystemAPI system) {
1450                if (system == null) return;
1451                search.systemReqs.add(new StarSystemRequirement() {
1452                        public boolean systemMatchesRequirement(StarSystemAPI param) {
1453                                return system != param;
1454                        }
1455                });
1456        }
1457        
1458        public void requireSystemIs(final StarSystemAPI system) {
1459                search.systemReqs.add(new StarSystemRequirement() {
1460                        public boolean systemMatchesRequirement(StarSystemAPI param) {
1461                                return system == param;
1462                        }
1463                });
1464        }
1465        
1466        public void requireSystem(StarSystemRequirement req) {
1467                search.systemReqs.add(req);
1468        }
1469        public void preferSystem(StarSystemRequirement req) {
1470                search.systemPrefs.add(req);
1471        }
1472        
1473        
1474        @SuppressWarnings("unchecked")
1475        protected void findMatching(List reqs, List prefs, List params,
1476                                                                List matches, List preferred) {
1477                if (reqs.isEmpty() && prefs.isEmpty()) {
1478                        matches.addAll(params);
1479                        return;
1480                }
1481                
1482                OUTER: for (Object param : params) {
1483                        for (Object req : reqs) {
1484                                if (!matchesReq((GenericRequirement) req, param)) continue OUTER;
1485                        }
1486                        matches.add(param);
1487                }
1488        
1489                // preferred: check prefs requirements in order, skip any that would produce
1490                // an empty result so e.g. if A, B, and C are required, then:
1491                // 1) Retain everything that meets A, say this is not an empty set
1492                // 2) If A && B is an empty set, skip B
1493                // 3) Then retain everything that meets C - so, A && C is the final set
1494                // Note that if the order is [B, A, C], the result would be different - B && C,
1495                // or just C (if nothing met B)
1496                List matchingPrefs = new ArrayList(matches);
1497                boolean foundAny = false;
1498                for (Object req : prefs) {
1499                        List retain = new ArrayList();
1500                        for (Object curr : matchingPrefs) {
1501                                if (matchesReq((GenericRequirement) req, curr)) {
1502                                        retain.add(curr);
1503                                }
1504                        }
1505                        if (retain.isEmpty()) continue;
1506                        foundAny = true;
1507                        matchingPrefs.retainAll(retain);
1508                }
1509                if (foundAny) {
1510                        preferred.addAll(matchingPrefs);
1511                }
1512        
1513//              OUTER: for (Object param : params) {
1514//                      for (Object req : prefs) {
1515//                              if (!matchesReq((GenericRequirement) req, param)) continue OUTER;
1516//                      }
1517//                      preferred.add(param);
1518//              }
1519        }
1520        @SuppressWarnings("unchecked")
1521        public Object pickFromMatching(List matches, List preferred) {
1522                WeightedRandomPicker pref = new WeightedRandomPicker(genRandom);
1523                WeightedRandomPicker other = new WeightedRandomPicker(genRandom);
1524                pref.addAll(preferred);
1525                other.addAll(matches);
1526                
1527                if (!pref.isEmpty()) {
1528                        return pref.pick();
1529                }
1530                
1531                return other.pick();
1532        }
1533        
1534        protected void findMatchingSystems() {
1535                requireSystemTags(ReqMode.NOT_ANY, Tags.THEME_HIDDEN);
1536                search.matchingSystems = new ArrayList<StarSystemAPI>();
1537                search.preferredSystems = new ArrayList<StarSystemAPI>();
1538                findMatching(search.systemReqs, search.systemPrefs, Global.getSector().getStarSystems(),
1539                                         search.matchingSystems, search.preferredSystems);
1540        }
1541        
1542        public StarSystemAPI pickSystem() {
1543                return pickSystem(true);
1544        }
1545        public StarSystemAPI pickSystem(boolean resetSearch) {
1546                findMatchingSystems();
1547                StarSystemAPI system = (StarSystemAPI) pickFromMatching(search.matchingSystems, search.preferredSystems);
1548                if (resetSearch) resetSearch();
1549                return system;
1550        }
1551        
1552        protected transient boolean makeSystemPreferencesMoreImportant = false;
1553        public void searchMakeSystemPreferencesMoreImportant(boolean value) {
1554                makeSystemPreferencesMoreImportant = value;
1555        }
1556        
1557        public PlanetAPI pickPlanet() {
1558                return pickPlanet(true);
1559        }
1560        public PlanetAPI pickPlanet(boolean resetSearch) {
1561                findMatchingSystems();
1562                
1563                List<PlanetAPI> inPreferredSystems = new ArrayList<PlanetAPI>();
1564                List<PlanetAPI> inMatchingSystems = new ArrayList<PlanetAPI>();
1565                for (StarSystemAPI system : search.matchingSystems) {
1566                        for (PlanetAPI planet : system.getPlanets()) {
1567                                if (planet.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1568                                inMatchingSystems.add(planet);
1569                        }
1570                        //inMatchingSystems.addAll(system.getPlanets());
1571                }
1572                for (StarSystemAPI system : search.preferredSystems) {
1573                        for (PlanetAPI planet : system.getPlanets()) {
1574                                if (planet.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1575                                inPreferredSystems.add(planet);
1576                        }
1577                        //inPreferredSystems.addAll(system.getPlanets());
1578                }
1579                        
1580                List<PlanetAPI> matchesInPref = new ArrayList<PlanetAPI>();
1581                List<PlanetAPI> preferredInPref = new ArrayList<PlanetAPI>();
1582                findMatching(search.planetReqs, search.planetPrefs, inPreferredSystems, matchesInPref, preferredInPref);
1583                if (!preferredInPref.isEmpty()) {
1584                        if (resetSearch) resetSearch();
1585                        return (PlanetAPI) pickOneObject(preferredInPref);
1586                }
1587                List<PlanetAPI> matchesInMatches = new ArrayList<PlanetAPI>();
1588                List<PlanetAPI> preferredInMatches = new ArrayList<PlanetAPI>();
1589                findMatching(search.planetReqs, search.planetPrefs, inMatchingSystems, matchesInMatches, preferredInMatches);
1590                if (makeSystemPreferencesMoreImportant) {
1591                        if (!matchesInPref.isEmpty()) {
1592                                if (resetSearch) resetSearch();
1593                                return (PlanetAPI) pickOneObject(matchesInPref);
1594                        }
1595                        if (!preferredInMatches.isEmpty()) {
1596                                if (resetSearch) resetSearch();
1597                                return (PlanetAPI) pickOneObject(preferredInMatches);
1598                        }
1599                } else {
1600                        if (!preferredInMatches.isEmpty()) {
1601                                if (resetSearch) resetSearch();
1602                                return (PlanetAPI) pickOneObject(preferredInMatches);
1603                        }
1604                        if (!matchesInPref.isEmpty()) {
1605                                if (resetSearch) resetSearch();
1606                                return (PlanetAPI) pickOneObject(matchesInPref);
1607                        }
1608                }
1609                
1610                if (resetSearch) resetSearch();
1611                return (PlanetAPI) pickOneObject(matchesInMatches);
1612                
1613//              WeightedRandomPicker<StarSystemAPI> pref = new WeightedRandomPicker<StarSystemAPI>(genRandom);
1614//              WeightedRandomPicker<StarSystemAPI> other = new WeightedRandomPicker<StarSystemAPI>(genRandom);
1615//              pref.addAll(search.preferredSystems);
1616//              other.addAll(search.matchingSystems);
1617//              
1618//              WeightedRandomPicker<PlanetAPI> allMatches = new WeightedRandomPicker<PlanetAPI>(genRandom);
1619//              while (!pref.isEmpty() || !other.isEmpty()) {
1620//                      StarSystemAPI pick = pref.pickAndRemove();
1621//                      if (pick == null) pick = other.pickAndRemove();
1622//                      if (pick == null) break;
1623//                      
1624//                      WeightedRandomPicker<PlanetAPI> matches = new WeightedRandomPicker<PlanetAPI>(genRandom);
1625//                      
1626//                      List<PlanetAPI> planets = new ArrayList<PlanetAPI>(pick.getPlanets());
1627//                      WeightedRandomPicker<PlanetAPI> preferred = new WeightedRandomPicker<PlanetAPI>(genRandom);
1628//                      OUTER: for (PlanetAPI planet : planets) {
1629//                              for (PlanetRequirement req : search.planetReqs) {
1630//                                      if (!req.planetMatchesRequirement(planet)) continue OUTER;
1631//                              }
1632//                              allMatches.add(planet);
1633//                              matches.add(planet);
1634//                      }
1635//                      
1636////                    if (planet.getName().equals("Mucalinda")) {
1637////                            System.out.println("32f323223");
1638////                    }
1639////                    if (curr.getName().equals("Megaron")) {
1640////                            System.out.println("32f323223");
1641////                    }
1642//                      
1643//                      List<PlanetAPI> matchingPrefs = new ArrayList<PlanetAPI>(matches.getItems());
1644//                      boolean foundAny = false;
1645//                      for (PlanetRequirement req : search.planetPrefs) {
1646//                              List<PlanetAPI> retain = new ArrayList<PlanetAPI>();
1647//                              for (PlanetAPI curr : matchingPrefs) {
1648//                                      if (req.planetMatchesRequirement(curr)) {
1649//                                              retain.add(curr);
1650//                                      }
1651//                              }
1652//                              if (retain.isEmpty()) continue;
1653//                              foundAny = true;
1654//                              matchingPrefs.retainAll(retain);
1655//                      }
1656//                      if (foundAny) {
1657//                              preferred.addAll(matchingPrefs);
1658//                      }
1659//                      
1660////                    OUTER: for (PlanetAPI planet : matches.getItems()) {
1661////                            for (PlanetRequirement req : search.planetPrefs) {
1662////                                    if (!req.planetMatchesRequirement(planet)) continue OUTER;
1663////                            }
1664////                            preferred.add(planet);
1665////                    }
1666//                      
1667//                      if (!preferred.isEmpty()) {
1668//                              if (resetSearch) resetSearch();
1669//                              return preferred.pick();
1670//                      }
1671//              }
1672//              
1673//              if (resetSearch) resetSearch();
1674//              return allMatches.pick();
1675        }
1676        
1677        public SectorEntityToken pickEntity() {
1678                return pickEntity(true);
1679        }
1680        public SectorEntityToken pickEntity(boolean resetSearch) {
1681                findMatchingSystems();
1682                
1683                List<SectorEntityToken> inPreferredSystems = new ArrayList<SectorEntityToken>();
1684                List<SectorEntityToken> inMatchingSystems = new ArrayList<SectorEntityToken>();
1685                for (StarSystemAPI system : search.matchingSystems) {
1686                        List<SectorEntityToken> entities = new ArrayList<SectorEntityToken>(system.getAllEntities());
1687                        for (SectorEntityToken entity : entities) {
1688                                if (entity instanceof AsteroidAPI) continue;
1689                                if (entity.hasTag(Tags.EXPIRES)) continue;
1690                                if (entity.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1691                                //if (!(entity.getCustomPlugin() instanceof DerelictShipEntityPlugin)) continue;
1692                                inMatchingSystems.add(entity);
1693                        }
1694                }
1695                for (StarSystemAPI system : search.preferredSystems) {
1696                        List<SectorEntityToken> entities = new ArrayList<SectorEntityToken>(system.getAllEntities());
1697                        for (SectorEntityToken entity : entities) {
1698                                if (entity instanceof AsteroidAPI) continue;
1699                                if (entity.hasTag(Tags.EXPIRES)) continue;
1700                                if (entity.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
1701                                inPreferredSystems.add(entity);
1702                        }
1703                }
1704                        
1705                List<SectorEntityToken> matchesInPref = new ArrayList<SectorEntityToken>();
1706                List<SectorEntityToken> preferredInPref = new ArrayList<SectorEntityToken>();
1707                findMatching(search.entityReqs, search.entityPrefs, inPreferredSystems, matchesInPref, preferredInPref);
1708                if (!preferredInPref.isEmpty()) {
1709                        if (resetSearch) resetSearch();
1710                        return (SectorEntityToken) pickOneObject(preferredInPref);
1711                }
1712                List<SectorEntityToken> matchesInMatches = new ArrayList<SectorEntityToken>();
1713                List<SectorEntityToken> preferredInMatches = new ArrayList<SectorEntityToken>();
1714                findMatching(search.entityReqs, search.entityPrefs, inMatchingSystems, matchesInMatches, preferredInMatches);
1715                if (makeSystemPreferencesMoreImportant) {
1716                        if (!matchesInPref.isEmpty()) {
1717                                if (resetSearch) resetSearch();
1718                                return (SectorEntityToken) pickOneObject(matchesInPref);
1719                        }
1720                        if (!preferredInMatches.isEmpty()) {
1721                                if (resetSearch) resetSearch();
1722                                return (SectorEntityToken) pickOneObject(preferredInMatches);
1723                        }
1724                } else {
1725                        if (!preferredInMatches.isEmpty()) {
1726                                if (resetSearch) resetSearch();
1727                                return (SectorEntityToken) pickOneObject(preferredInMatches);
1728                        }
1729                        if (!matchesInPref.isEmpty()) {
1730                                if (resetSearch) resetSearch();
1731                                return (SectorEntityToken) pickOneObject(matchesInPref);
1732                        }
1733                }
1734                
1735                if (resetSearch) resetSearch();
1736                return (SectorEntityToken) pickOneObject(matchesInMatches);
1737                
1738                
1739//              WeightedRandomPicker<StarSystemAPI> pref = new WeightedRandomPicker<StarSystemAPI>(genRandom);
1740//              WeightedRandomPicker<StarSystemAPI> other = new WeightedRandomPicker<StarSystemAPI>(genRandom);
1741//              pref.addAll(search.preferredSystems);
1742//              other.addAll(search.matchingSystems);
1743//              
1744//              WeightedRandomPicker<SectorEntityToken> allMatches = new WeightedRandomPicker<SectorEntityToken>(genRandom);
1745//              while (!pref.isEmpty() || !other.isEmpty()) {
1746//                      StarSystemAPI pick = pref.pickAndRemove();
1747//                      if (pick == null) pick = other.pickAndRemove();
1748//                      if (pick == null) break;
1749//                      
1750//                      WeightedRandomPicker<SectorEntityToken> matches = new WeightedRandomPicker<SectorEntityToken>(genRandom);
1751//                      
1752//                      List<SectorEntityToken> entities = new ArrayList<SectorEntityToken>(pick.getAllEntities());
1753//                      WeightedRandomPicker<SectorEntityToken> preferred = new WeightedRandomPicker<SectorEntityToken>(genRandom);
1754//                      OUTER: for (SectorEntityToken entity : entities) {
1755//                              if (entity instanceof AsteroidAPI) continue;
1756//                              if (entity.hasTag(Tags.EXPIRES)) continue;
1757//                              for (EntityRequirement req : search.entityReqs) {
1758//                                      if (!req.entityMatchesRequirement(entity)) continue OUTER;
1759//                              }
1760//                              allMatches.add(entity);
1761//                              matches.add(entity);
1762//                      }
1763//                      
1764//                      
1765//                      List<SectorEntityToken> matchingPrefs = new ArrayList<SectorEntityToken>(matches.getItems());
1766//                      boolean foundAny = false;
1767//                      for (EntityRequirement req : search.entityPrefs) {
1768//                              List<SectorEntityToken> retain = new ArrayList<SectorEntityToken>();
1769//                              for (SectorEntityToken curr : matchingPrefs) {
1770//                                      if (req.entityMatchesRequirement(curr)) {
1771//                                              retain.add(curr);
1772//                                      }
1773//                              }
1774//                              if (retain.isEmpty()) continue;
1775//                              foundAny = true;
1776//                              matchingPrefs.retainAll(retain);
1777//                      }
1778//                      if (foundAny) {
1779//                              preferred.addAll(matchingPrefs);
1780//                      }
1781//                      
1782////                    OUTER: for (SectorEntityToken entity : matches.getItems()) {
1783////                            for (EntityRequirement req : search.entityPrefs) {
1784////                                    if (!req.entityMatchesRequirement(entity)) continue OUTER;
1785////                            }
1786////                            preferred.add(entity);
1787////                    }
1788//                      
1789//                      if (!preferred.isEmpty()) {
1790//                              if (resetSearch) resetSearch();
1791//                              return preferred.pick();
1792//                      }
1793//              }
1794//              
1795//              if (resetSearch) resetSearch();
1796//              return allMatches.pick();
1797        }
1798        
1799
1800        
1801        protected void findMatchingMarkets() {
1802                List<MarketAPI> markets = new ArrayList<MarketAPI>();;
1803                for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
1804                        if (market.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) {
1805                                continue;
1806                        }
1807                        if (market.getPlanetEntity() != null && market.getPlanetEntity().hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) {
1808                                continue;
1809                        }
1810                        markets.add(market);
1811                }
1812//              System.out.println("BEGIN");
1813//              for (MarketAPI curr : markets) {
1814//                      System.out.println(curr.getName());
1815//              }
1816//              System.out.println("END");
1817//              findMatchingSystems();
1818//              if (!(search.systemPrefs.isEmpty() && search.systemReqs.isEmpty())) {
1819//                      Set<StarSystemAPI> systems = new HashSet<StarSystemAPI>(search.matchingSystems);
1820//                      Iterator<MarketAPI> iter = markets.iterator();
1821//                      while (iter.hasNext()) {
1822//                              MarketAPI curr = iter.next();
1823//                              if (!systems.contains(curr.getStarSystem())) {
1824//                                      iter.remove();
1825//                              }
1826//                      }
1827//              }
1828                
1829                search.matchingMarkets = new ArrayList<MarketAPI>();
1830                search.preferredMarkets = new ArrayList<MarketAPI>();
1831                findMatching(search.marketReqs, search.marketPrefs, markets,
1832                                                        search.matchingMarkets, search.preferredMarkets);
1833        }
1834        
1835        public MarketAPI pickMarket() {
1836                return pickMarket(true);
1837        }
1838        public MarketAPI pickMarket(boolean resetSearch) {
1839                findMatchingMarkets();
1840                MarketAPI market = (MarketAPI) pickFromMatching(search.matchingMarkets, search.preferredMarkets);
1841                if (resetSearch) resetSearch();
1842                
1843                //if (true) return Global.getSector().getEconomy().getMarket("nomios");
1844                
1845                return market;
1846        }
1847        
1848        public CommodityOnMarketAPI pickCommodity() {
1849                return pickCommodity(true);
1850        }
1851        
1852        public CommodityOnMarketAPI pickCommodity(boolean resetSearch) {
1853                findMatchingMarkets();
1854                
1855                WeightedRandomPicker<CommodityOnMarketAPI> pref = new WeightedRandomPicker<CommodityOnMarketAPI>(genRandom);
1856                WeightedRandomPicker<CommodityOnMarketAPI> other = new WeightedRandomPicker<CommodityOnMarketAPI>(genRandom);
1857                
1858                for (MarketAPI market : search.matchingMarkets) {
1859                        SKIP: for (CommodityOnMarketAPI com : market.getAllCommodities()) {
1860                                if (com.isMeta()) continue;
1861                                if (com.isNonEcon()) continue;
1862                                
1863                                for (CommodityRequirement req : search.commodityReqs) {
1864                                        if (!req.commodityMatchesRequirement(com)) continue SKIP;
1865                                }
1866                                other.add(com);
1867                        }
1868                }
1869                
1870                List<CommodityOnMarketAPI> matchingPrefs = new ArrayList<CommodityOnMarketAPI>(other.getItems());
1871                boolean foundAny = false;
1872                for (CommodityRequirement req : search.commodityPrefs) {
1873                        List<CommodityOnMarketAPI> retain = new ArrayList<CommodityOnMarketAPI>();
1874                        for (CommodityOnMarketAPI com : matchingPrefs) {
1875                                if (req.commodityMatchesRequirement(com)) {
1876                                        retain.add(com);
1877                                }
1878                        }
1879                        if (retain.isEmpty()) continue;
1880                        foundAny = true;
1881                        matchingPrefs.retainAll(retain);
1882                }
1883                if (foundAny) {
1884                        pref.addAll(matchingPrefs);
1885                }
1886
1887                
1888                CommodityOnMarketAPI result = pref.pick();
1889                if (result == null) result = other.pick();
1890                if (resetSearch) resetSearch();
1891                
1892                return result;
1893        }
1894
1895        
1896//      public void requireMarketFactionCustom(String flag) {
1897//              search.marketReqs.add(new MarketFactionCustomFlagReq(false, flag));
1898//      }
1899//      public void preferMarketFactionCustom(String flag) {
1900//              search.marketPrefs.add(new MarketFactionCustomFlagReq(false, flag));
1901//      }
1902//      public void requireMarketFactionNotCustom(String flag) {
1903//              search.marketReqs.add(new MarketFactionCustomFlagReq(true, flag));
1904//      }
1905//      public void preferMarketFactionNotCustom(String flag) {
1906//              search.marketPrefs.add(new MarketFactionCustomFlagReq(true, flag));
1907//      }
1908        
1909        public void requireMarketTacticallyBombardable() {
1910                search.marketReqs.add(new MarketTacticalBombardableReq(false));
1911        }
1912        public void requireMarketNotTacticallyBombardable() {
1913                search.marketReqs.add(new MarketTacticalBombardableReq(true));
1914        }
1915        public void preferMarketTacticallyBombardable() {
1916                search.marketPrefs.add(new MarketTacticalBombardableReq(false));
1917        }
1918        public void preferMarketNotTacticallyBombardable() {
1919                search.marketPrefs.add(new MarketTacticalBombardableReq(true));
1920        }
1921        public void requireMarketMilitary() {
1922                search.marketReqs.add(new MarketMilitaryReq());
1923        }
1924        public void preferMarketMilitary() {
1925                search.marketPrefs.add(new MarketMilitaryReq());
1926        }
1927        public void requireMarketNotMilitary() {
1928                search.marketReqs.add(new MarketNotMilitaryReq());
1929        }
1930        public void preferMarketNotMilitary() {
1931                search.marketPrefs.add(new MarketNotMilitaryReq());
1932        }
1933        
1934        public void requireMarketMemoryFlag(String key, Object value) {
1935                search.marketReqs.add(new MarketMemoryFlagReq(key, value));
1936        }
1937        public void preferMarketMemoryFlag(String key, Object value) {
1938                search.marketPrefs.add(new MarketMemoryFlagReq(key, value));
1939        }
1940        
1941        public void requireMarketHidden() {
1942                search.marketReqs.add(new MarketHiddenReq());
1943        }
1944        public void preferMarketHidden() {
1945                search.marketPrefs.add(new MarketHiddenReq());
1946        }
1947        public void requireMarketNotHidden() {
1948                search.marketReqs.add(new MarketNotHiddenReq());
1949        }
1950        public void preferMarketNotHidden() {
1951                search.marketPrefs.add(new MarketNotHiddenReq());
1952        }
1953        public void requireMarketNotInHyperspace() {
1954                search.marketReqs.add(new MarketNotInHyperReq());
1955        }
1956        public void preferMarketNotInHyperspace() {
1957                search.marketPrefs.add(new MarketNotInHyperReq());
1958        }
1959        
1960        
1961        public void requireMarketIs(String id) {
1962                search.marketReqs.add(new MarketIsReq(Global.getSector().getEconomy().getMarket(id), false));
1963        }
1964        public void requireMarketIs(final MarketAPI param) {
1965                search.marketReqs.add(new MarketIsReq(param, false));
1966        }
1967        public void preferMarketIs(final MarketAPI param) {
1968                search.marketPrefs.add(new MarketIsReq(param, false));
1969        }
1970        public void requireMarketIsNot(final MarketAPI param) {
1971                search.marketReqs.add(new MarketIsReq(param, true));
1972        }
1973        public void preferMarketIsNot(final MarketAPI param) {
1974                search.marketPrefs.add(new MarketIsReq(param, true));
1975        }
1976        
1977        public void requireMarketFaction(String ... factions) {
1978                search.marketReqs.add(new MarketFactionReq(false, factions));
1979        }
1980        public void preferMarketFaction(String ... factions) {
1981                search.marketPrefs.add(new MarketFactionReq(false, factions));
1982        }
1983        public void requireMarketFactionNot(String ... factions) {
1984                search.marketReqs.add(new MarketFactionReq(true, factions));
1985        }
1986        public void preferMarketFactionNot(String ... factions) {
1987                search.marketPrefs.add(new MarketFactionReq(true, factions));
1988        }
1989        
1990        public void requireMarketFactionNotPlayer() {
1991                requireMarketFactionNot(Factions.PLAYER);
1992        }
1993        
1994        public void requireMarketFactionHostileTo(String faction) {
1995                search.marketReqs.add(new MarketFactionHostileReq(false, faction));
1996        }
1997        public void preferMarketFactionHostileTo(String faction) {
1998                search.marketPrefs.add(new MarketFactionHostileReq(false, faction));
1999        }
2000        public void requireMarketFactionNotHostileTo(String faction) {
2001                search.marketReqs.add(new MarketFactionHostileReq(true, faction));
2002        }
2003        public void preferMarketFactionNotHostileTo(String faction) {
2004                search.marketPrefs.add(new MarketFactionHostileReq(true, faction));
2005        }
2006        
2007        protected LocationAPI[] convertLocations(String ... locations) {
2008                List<LocationAPI> result = new ArrayList<LocationAPI>();
2009                for (String s : locations) {
2010                        if ("hyperspace".equals(s)) {
2011                                result.add(Global.getSector().getHyperspace());
2012                        } else {
2013                                StarSystemAPI system = Global.getSector().getStarSystem(s);
2014//                              if (Global.getSettings().isDevMode() && system == null) {
2015//                                      throw new RuntimeException("Star system named [" + s + "] not found");
2016//                              }
2017                                if (system != null) {
2018                                        result.add(system);
2019                                }
2020                        }
2021                }
2022                return result.toArray(new LocationAPI[0]);
2023        }
2024        public void requireMarketLocation(String ...locations) {
2025                requireMarketLocation(convertLocations(locations));
2026        }
2027        public void preferMarketLocation(String ... locations) {
2028                preferMarketLocation(convertLocations(locations));
2029        }
2030        public void requireMarketLocationNot(String ... locations) {
2031                requireMarketLocationNot(convertLocations(locations));
2032        }
2033        public void preferMarketLocationNot(String ... locations) {
2034                preferMarketLocationNot(convertLocations(locations));
2035        }
2036        public void requireMarketLocation(LocationAPI ... locations) {
2037                search.marketReqs.add(new MarketLocationReq(false, locations));
2038        }
2039        public void preferMarketLocation(LocationAPI ... locations) {
2040                search.marketPrefs.add(new MarketLocationReq(false, locations));
2041        }
2042        public void requireMarketLocationNot(LocationAPI ... locations) {
2043                search.marketReqs.add(new MarketLocationReq(true, locations));
2044        }
2045        public void preferMarketLocationNot(LocationAPI ... locations) {
2046                search.marketPrefs.add(new MarketLocationReq(true, locations));
2047        }
2048        
2049        public void requireMarketFactionCustom(ReqMode mode, String ... custom) {
2050                search.marketReqs.add(new MarketFactionCustomReq(mode, custom));
2051        }
2052        public void preferMarketFactionCustom(ReqMode mode, String ... custom) {
2053                search.marketPrefs.add(new MarketFactionCustomReq(mode, custom));
2054        }
2055        
2056        public void requireMarketSizeAtLeast(final int size) {
2057                search.marketReqs.add(new MarketRequirement() {
2058                        public boolean marketMatchesRequirement(MarketAPI market) {
2059                                return market.getSize() >= size;
2060                        }
2061                });
2062        }
2063        public void preferMarketSizeAtLeast(final int size) {
2064                search.marketPrefs.add(new MarketRequirement() {
2065                        public boolean marketMatchesRequirement(MarketAPI market) {
2066                                return market.getSize() >= size;
2067                        }
2068                });
2069        }
2070        
2071        public void requireMarketSizeAtMost(final int size) {
2072                search.marketReqs.add(new MarketRequirement() {
2073                        public boolean marketMatchesRequirement(MarketAPI market) {
2074                                return market.getSize() <= size;
2075                        }
2076                });
2077        }
2078        public void preferMarketSizeAtMost(final int size) {
2079                search.marketPrefs.add(new MarketRequirement() {
2080                        public boolean marketMatchesRequirement(MarketAPI market) {
2081                                return market.getSize() <= size;
2082                        }
2083                });
2084        }
2085        
2086        public void requireMarketStabilityAtLeast(final int stability) {
2087                search.marketReqs.add(new MarketRequirement() {
2088                        public boolean marketMatchesRequirement(MarketAPI market) {
2089                                return market.getStabilityValue() >= stability;
2090                        }
2091                });
2092        }
2093        public void preferMarketStabilityAtLeast(final int stability) {
2094                search.marketPrefs.add(new MarketRequirement() {
2095                        public boolean marketMatchesRequirement(MarketAPI market) {
2096                                return market.getStabilityValue() >= stability;
2097                        }
2098                });
2099        }
2100        public void requireMarketStabilityAtMost(final int stability) {
2101                search.marketReqs.add(new MarketRequirement() {
2102                        public boolean marketMatchesRequirement(MarketAPI market) {
2103                                return market.getStabilityValue() <= stability;
2104                        }
2105                });
2106        }
2107        public void preferMarketStabilityAtMost(final int stability) {
2108                search.marketPrefs.add(new MarketRequirement() {
2109                        public boolean marketMatchesRequirement(MarketAPI market) {
2110                                return market.getStabilityValue() <= stability;
2111                        }
2112                });
2113        }
2114        
2115        public void requireMarketConditions(ReqMode mode, String ... conditions) {
2116                search.marketReqs.add(new RequiredMarketConditions(mode, conditions));
2117        }
2118        public void preferMarketConditions(ReqMode mode, String ... conditions) {
2119                search.marketPrefs.add(new RequiredMarketConditions(mode, conditions));
2120        }
2121        
2122        public void requireMarketIndustries(ReqMode mode, String ... industries) {
2123                search.marketReqs.add(new RequiredMarketIndustries(mode, industries));
2124        }
2125        public void preferMarketIndustries(ReqMode mode, String ... industries) {
2126                search.marketPrefs.add(new RequiredMarketIndustries(mode, industries));
2127        }
2128        
2129        public void requireMarketIsMilitary() {
2130                search.marketReqs.add(new MarketRequirement() {
2131                        public boolean marketMatchesRequirement(MarketAPI market) {
2132                                return Misc.isMilitary(market);
2133                        }
2134                });
2135        }
2136        public void preferMarketIsMilitary() {
2137                search.marketPrefs.add(new MarketRequirement() {
2138                        public boolean marketMatchesRequirement(MarketAPI market) {
2139                                return Misc.isMilitary(market);
2140                        }
2141                });
2142        }
2143        
2144        public void requireMarketHasSpaceport() {
2145                search.marketReqs.add(new MarketRequirement() {
2146                        public boolean marketMatchesRequirement(MarketAPI market) {
2147                                return market.hasSpaceport();
2148                        }
2149                });
2150        }
2151        public void preferMarketHasSpaceport() {
2152                search.marketPrefs.add(new MarketRequirement() {
2153                        public boolean marketMatchesRequirement(MarketAPI market) {
2154                                return market.hasSpaceport();
2155                        }
2156                });
2157        }
2158        
2159        public void requireMarketNotHasSpaceport() {
2160                search.marketReqs.add(new MarketRequirement() {
2161                        public boolean marketMatchesRequirement(MarketAPI market) {
2162                                return !market.hasSpaceport();
2163                        }
2164                });
2165        }
2166        public void preferMarketNotHasSpaceport() {
2167                search.marketPrefs.add(new MarketRequirement() {
2168                        public boolean marketMatchesRequirement(MarketAPI market) {
2169                                return !market.hasSpaceport();
2170                        }
2171                });
2172        }
2173        
2174        public void requireCommodityIsNotPersonnel() {
2175                search.commodityReqs.add(new CommodityRequirement() {
2176                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2177                                return !com.isPersonnel();
2178                        }
2179                });
2180        }
2181        public void preferCommodityIsNotPersonnel() {
2182                search.commodityPrefs.add(new CommodityRequirement() {
2183                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2184                                return !com.isPersonnel();
2185                        }
2186                });
2187        }
2188        
2189        public void requireCommodityLegal() {
2190                search.commodityReqs.add(new CommodityRequirement() {
2191                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2192                                return !com.isIllegal();
2193                        }
2194                });
2195        }
2196        public void preferCommodityLegal() {
2197                search.commodityPrefs.add(new CommodityRequirement() {
2198                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2199                                return !com.isIllegal();
2200                        }
2201                });
2202        }
2203        public void requireCommodityIllegal() {
2204                search.commodityReqs.add(new CommodityRequirement() {
2205                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2206                                return com.isIllegal();
2207                        }
2208                });
2209        }
2210        public void preferCommodityIllegal() {
2211                search.commodityPrefs.add(new CommodityRequirement() {
2212                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2213                                return com.isIllegal();
2214                        }
2215                });
2216        }
2217        
2218        public void requireCommodityIs(final String id) {
2219                search.commodityReqs.add(new CommodityRequirement() {
2220                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2221                                return com.getId().equals(id);
2222                        }
2223                });
2224        }
2225        public void preferCommodityIs(final String id) {
2226                search.commodityPrefs.add(new CommodityRequirement() {
2227                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2228                                return com.getId().equals(id);
2229                        }
2230                });
2231        }
2232        
2233        public void requireCommodityTags(ReqMode mode, String ... tags) {
2234                search.commodityReqs.add(new RequiredCommodityTags(mode, tags));
2235        }
2236        public void preferCommodityTags(ReqMode mode, String ... tags) {
2237                search.commodityPrefs.add(new RequiredCommodityTags(mode, tags));
2238        }
2239        
2240        public void requireCommodityAvailableAtLeast(final int qty) {
2241                search.commodityReqs.add(new CommodityRequirement() {
2242                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2243                                return com.getAvailable() >= qty;
2244                        }
2245                });
2246        }
2247        public void preferCommodityAvailableAtLeast(final int qty) {
2248                search.commodityPrefs.add(new CommodityRequirement() {
2249                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2250                                return com.getAvailable() >= qty;
2251                        }
2252                });
2253        }
2254        public void requireCommodityAvailableAtMost(final int qty) {
2255                search.commodityReqs.add(new CommodityRequirement() {
2256                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2257                                return com.getAvailable() <= qty;
2258                        }
2259                });
2260        }
2261        public void preferCommodityAvailableAtMost(final int qty) {
2262                search.commodityPrefs.add(new CommodityRequirement() {
2263                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2264                                return com.getAvailable() <= qty;
2265                        }
2266                });
2267        }
2268        
2269        public void requireCommodityDemandAtLeast(final int qty) {
2270                search.commodityReqs.add(new CommodityRequirement() {
2271                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2272                                return com.getMaxDemand() >= qty;
2273                        }
2274                });
2275        }
2276        public void preferCommodityDemandAtLeast(final int qty) {
2277                search.commodityPrefs.add(new CommodityRequirement() {
2278                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2279                                return com.getMaxDemand() >= qty;
2280                        }
2281                });
2282        }
2283        public void requireCommodityDemandAtMost(final int qty) {
2284                search.commodityReqs.add(new CommodityRequirement() {
2285                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2286                                return com.getMaxDemand() <= qty;
2287                        }
2288                });
2289        }
2290        public void preferCommodityDemandAtMost(final int qty) {
2291                search.commodityPrefs.add(new CommodityRequirement() {
2292                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2293                                return com.getMaxDemand() <= qty;
2294                        }
2295                });
2296        }
2297        
2298        public void requireCommodityProductionAtLeast(final int qty) {
2299                search.commodityReqs.add(new CommodityRequirement() {
2300                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2301                                return com.getMaxSupply() >= qty;
2302                        }
2303                });
2304        }
2305        public void preferCommodityProductionAtLeast(final int qty) {
2306                search.commodityPrefs.add(new CommodityRequirement() {
2307                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2308                                return com.getMaxSupply() >= qty;
2309                        }
2310                });
2311        }
2312        public void requireCommodityProductionAtMost(final int qty) {
2313                search.commodityReqs.add(new CommodityRequirement() {
2314                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2315                                return com.getMaxSupply() <= qty;
2316                        }
2317                });
2318        }
2319        public void preferCommodityProductionAtMost(final int qty) {
2320                search.commodityPrefs.add(new CommodityRequirement() {
2321                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2322                                return com.getMaxSupply() <= qty;
2323                        }
2324                });
2325        }
2326        
2327        public void requireCommoditySurplusAtLeast(final int qty) {
2328                search.commodityReqs.add(new CommodityRequirement() {
2329                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2330                                CommodityIconCounts counts = new CommodityIconCounts(com);
2331                                return counts.extra >= qty;
2332                        }
2333                });
2334        }
2335        public void preferCommoditySurplusAtLeast(final int qty) {
2336                search.commodityPrefs.add(new CommodityRequirement() {
2337                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2338                                CommodityIconCounts counts = new CommodityIconCounts(com);
2339                                return counts.extra >= qty;
2340                        }
2341                });
2342        }
2343        public void requireCommoditySurplusAtMost(final int qty) {
2344                search.commodityReqs.add(new CommodityRequirement() {
2345                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2346                                CommodityIconCounts counts = new CommodityIconCounts(com);
2347                                return counts.extra <= qty;
2348                        }
2349                });
2350        }
2351        public void preferCommoditySurplusAtMost(final int qty) {
2352                search.commodityPrefs.add(new CommodityRequirement() {
2353                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2354                                CommodityIconCounts counts = new CommodityIconCounts(com);
2355                                return counts.extra <= qty;
2356                        }
2357                });
2358        }
2359        
2360        public void requireCommodityDeficitAtLeast(final int qty) {
2361                search.commodityReqs.add(new CommodityRequirement() {
2362                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2363                                CommodityIconCounts counts = new CommodityIconCounts(com);
2364                                return counts.deficit >= qty;
2365                        }
2366                });
2367        }
2368        public void preferCommodityDeficitAtLeast(final int qty) {
2369                search.commodityPrefs.add(new CommodityRequirement() {
2370                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2371                                CommodityIconCounts counts = new CommodityIconCounts(com);
2372                                return counts.deficit >= qty;
2373                        }
2374                });
2375        }
2376        public void requireCommodityDeficitAtMost(final int qty) {
2377                search.commodityReqs.add(new CommodityRequirement() {
2378                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2379                                CommodityIconCounts counts = new CommodityIconCounts(com);
2380                                return counts.deficit <= qty;
2381                        }
2382                });
2383        }
2384        public void preferCommodityDeficitAtMost(final int qty) {
2385                search.commodityPrefs.add(new CommodityRequirement() {
2386                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2387                                CommodityIconCounts counts = new CommodityIconCounts(com);
2388                                return counts.deficit <= qty;
2389                        }
2390                });
2391        }
2392        
2393        public void requireCommodityBasePriceAtLeast(final float price) {
2394                search.commodityReqs.add(new CommodityRequirement() {
2395                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2396                                return com.getCommodity().getBasePrice() >= price;
2397                        }
2398                });
2399        }
2400        public void preferCommodityBasePriceAtLeast(final float price) {
2401                search.commodityPrefs.add(new CommodityRequirement() {
2402                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2403                                return com.getCommodity().getBasePrice() >= price;
2404                        }
2405                });
2406        }
2407        public void requireCommodityBasePriceAtMost(final float price) {
2408                search.commodityReqs.add(new CommodityRequirement() {
2409                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2410                                return com.getCommodity().getBasePrice() <= price;
2411                        }
2412                });
2413        }
2414        public void preferCommodityBasePriceAtMost(final float price) {
2415                search.commodityPrefs.add(new CommodityRequirement() {
2416                        public boolean commodityMatchesRequirement(CommodityOnMarketAPI com) {
2417                                return com.getCommodity().getBasePrice() <= price;
2418                        }
2419                });
2420        }
2421
2422        public void requireTerrainType(ReqMode mode, String ... types) {
2423                search.terrainReqs.add(new TerrainTypeReq(mode, types));
2424        }
2425        public void preferTerrainType(ReqMode mode, String ... types) {
2426                search.terrainPrefs.add(new TerrainTypeReq(mode, types));
2427        }
2428        
2429        public void requireTerrainTags(ReqMode mode, String ... tags) {
2430                search.terrainReqs.add(new RequiredTerrainTags(mode, tags));
2431        }
2432        public void preferTerrainTags(ReqMode mode, String ... tags) {
2433                search.terrainPrefs.add(new RequiredTerrainTags(mode, tags));
2434        }
2435        public void requireTerrainHasSpecialName() {
2436                search.terrainReqs.add(new TerrainHasSpecialNameReq());
2437        }
2438        public void preferTerrainHasSpecialName() {
2439                search.terrainPrefs.add(new TerrainHasSpecialNameReq());
2440        }
2441        
2442        public CampaignTerrainAPI pickTerrain() {
2443                return pickTerrain(true);
2444        }
2445        public CampaignTerrainAPI pickTerrain(boolean resetSearch) {
2446                findMatchingSystems();
2447                
2448                List<CampaignTerrainAPI> inPreferredSystems = new ArrayList<CampaignTerrainAPI>();
2449                List<CampaignTerrainAPI> inMatchingSystems = new ArrayList<CampaignTerrainAPI>();
2450                for (StarSystemAPI system : search.matchingSystems) {
2451                        List<CampaignTerrainAPI> terrainList = new ArrayList<CampaignTerrainAPI>(system.getTerrainCopy());
2452                        for (CampaignTerrainAPI terrain : terrainList) {
2453                                if (terrain.hasTag(Tags.EXPIRES)) continue;
2454                                
2455                                // exclude system-wide nebulas
2456                                if (terrain.getPlugin() instanceof BaseTiledTerrain) {
2457                                        BaseTiledTerrain btt = (BaseTiledTerrain) terrain.getPlugin();
2458                                        if (btt.getTiles() != null && btt.getTiles().length > 50) continue;
2459                                }
2460                                // exclude large rings
2461                                if (terrain.getPlugin() instanceof BaseRingTerrain) {
2462                                        BaseRingTerrain rtp = (BaseRingTerrain) terrain.getPlugin();
2463                                        if (rtp.getRingParams() != null && rtp.getRingParams().middleRadius > 5000f) continue;
2464                                }
2465                                inMatchingSystems.add(terrain);
2466                        }
2467                }
2468                for (StarSystemAPI system : search.preferredSystems) {
2469                        List<CampaignTerrainAPI> terrainList = new ArrayList<CampaignTerrainAPI>(system.getTerrainCopy());
2470                        for (CampaignTerrainAPI terrain : terrainList) {
2471                                if (terrain.hasTag(Tags.EXPIRES)) continue;
2472                                
2473                                // exclude system-wide nebulas
2474                                if (terrain.getPlugin() instanceof BaseTiledTerrain) {
2475                                        BaseTiledTerrain btt = (BaseTiledTerrain) terrain.getPlugin();
2476                                        if (btt.getTiles() != null && btt.getTiles().length > 50) continue;
2477                                }
2478                                // exclude large rings
2479                                if (terrain.getPlugin() instanceof BaseRingTerrain) {
2480                                        BaseRingTerrain rtp = (BaseRingTerrain) terrain.getPlugin();
2481                                        if (rtp.getRingParams() != null && rtp.getRingParams().middleRadius > 5000f) continue;
2482                                }
2483                                inMatchingSystems.add(terrain);
2484                        }
2485                }
2486                        
2487                List<CampaignTerrainAPI> matchesInPref = new ArrayList<CampaignTerrainAPI>();
2488                List<CampaignTerrainAPI> preferredInPref = new ArrayList<CampaignTerrainAPI>();
2489                findMatching(search.terrainReqs, search.terrainPrefs, inPreferredSystems, matchesInPref, preferredInPref);
2490                if (!preferredInPref.isEmpty()) {
2491                        if (resetSearch) resetSearch();
2492                        return (CampaignTerrainAPI) pickOneObject(preferredInPref);
2493                }
2494                List<CampaignTerrainAPI> matchesInMatches = new ArrayList<CampaignTerrainAPI>();
2495                List<CampaignTerrainAPI> preferredInMatches = new ArrayList<CampaignTerrainAPI>();
2496                findMatching(search.terrainReqs, search.terrainPrefs, inMatchingSystems, matchesInMatches, preferredInMatches);
2497                if (makeSystemPreferencesMoreImportant) {
2498                        if (!matchesInPref.isEmpty()) {
2499                                if (resetSearch) resetSearch();
2500                                return (CampaignTerrainAPI) pickOneObject(matchesInPref);
2501                        }
2502                        if (!preferredInMatches.isEmpty()) {
2503                                if (resetSearch) resetSearch();
2504                                return (CampaignTerrainAPI) pickOneObject(preferredInMatches);
2505                        }
2506                } else {
2507                        if (!preferredInMatches.isEmpty()) {
2508                                if (resetSearch) resetSearch();
2509                                return (CampaignTerrainAPI) pickOneObject(preferredInMatches);
2510                        }
2511                        if (!matchesInPref.isEmpty()) {
2512                                if (resetSearch) resetSearch();
2513                                return (CampaignTerrainAPI) pickOneObject(matchesInPref);
2514                        }
2515                }
2516                
2517                if (resetSearch) resetSearch();
2518                return (CampaignTerrainAPI) pickOneObject(matchesInMatches);
2519                
2520                
2521                
2522                
2523//              WeightedRandomPicker<StarSystemAPI> pref = new WeightedRandomPicker<StarSystemAPI>(genRandom);
2524//              WeightedRandomPicker<StarSystemAPI> other = new WeightedRandomPicker<StarSystemAPI>(genRandom);
2525//              pref.addAll(search.preferredSystems);
2526//              other.addAll(search.matchingSystems);
2527//              
2528//              WeightedRandomPicker<CampaignTerrainAPI> allMatches = new WeightedRandomPicker<CampaignTerrainAPI>(genRandom);
2529//              while (!pref.isEmpty() || !other.isEmpty()) {
2530//                      StarSystemAPI pick = pref.pickAndRemove();
2531//                      if (pick == null) pick = other.pickAndRemove();
2532//                      if (pick == null) break;
2533//                      
2534//                      WeightedRandomPicker<CampaignTerrainAPI> matches = new WeightedRandomPicker<CampaignTerrainAPI>(genRandom);
2535//                      
2536//                      List<CampaignTerrainAPI> terrainList = new ArrayList<CampaignTerrainAPI>(pick.getTerrainCopy());
2537//                      WeightedRandomPicker<CampaignTerrainAPI> preferred = new WeightedRandomPicker<CampaignTerrainAPI>(genRandom);
2538//                      OUTER: for (CampaignTerrainAPI terrain : terrainList) {
2539//                              if (terrain.hasTag(Tags.EXPIRES)) continue;
2540//                              for (TerrainRequirement req : search.terrainReqs) {
2541//                                      if (!req.terrainMatchesRequirement(terrain)) continue OUTER;
2542//                              }
2543//                              allMatches.add(terrain);
2544//                              matches.add(terrain);
2545//                      }
2546//                      
2547//                      
2548//                      List<CampaignTerrainAPI> matchingPrefs = new ArrayList<CampaignTerrainAPI>(matches.getItems());
2549//                      boolean foundAny = false;
2550//                      for (TerrainRequirement req : search.terrainPrefs) {
2551//                              List<CampaignTerrainAPI> retain = new ArrayList<CampaignTerrainAPI>();
2552//                              for (CampaignTerrainAPI curr : matchingPrefs) {
2553//                                      if (curr.hasTag(Tags.EXPIRES)) continue;
2554//                                      if (req.terrainMatchesRequirement(curr)) {
2555//                                              retain.add(curr);
2556//                                      }
2557//                              }
2558//                              if (retain.isEmpty()) continue;
2559//                              foundAny = true;
2560//                              matchingPrefs.retainAll(retain);
2561//                      }
2562//                      if (foundAny) {
2563//                              preferred.addAll(matchingPrefs);
2564//                      }
2565//                      
2566//                      if (!preferred.isEmpty()) {
2567//                              if (resetSearch) resetSearch();
2568//                              return preferred.pick();
2569//                      }
2570//              }
2571//              
2572//              if (resetSearch) resetSearch();
2573//              return allMatches.pick();
2574        }
2575}
2576
2577
2578
2579
2580