001package com.fs.starfarer.api.impl.campaign.enc;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Random;
006
007import org.lwjgl.util.vector.Vector2f;
008
009import com.fs.starfarer.api.EveryFrameScript;
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.CampaignFleetAPI;
012import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
013import com.fs.starfarer.api.campaign.LocationAPI;
014import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
015import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2;
016import com.fs.starfarer.api.util.IntervalUtil;
017import com.fs.starfarer.api.util.Misc;
018import com.fs.starfarer.api.util.TimeoutTracker;
019import com.fs.starfarer.api.util.WeightedRandomPicker;
020
021public class EncounterManager implements EveryFrameScript, EncounterPointProvider {
022
023        public static String EP_TYPE_SLIPSTREAM = "ep_type_slipstream";
024        public static String EP_TYPE_OUTSIDE_SYSTEM = "ep_type_outside_system";
025        
026        public static float MAX_EP_DIST_ADD = 4000;
027        
028        public static List<EPEncounterCreator> CREATORS = new ArrayList<EPEncounterCreator>();
029        static {
030                CREATORS.add(new SlipstreamPirateEPEC());
031                CREATORS.add(new SlipstreamLuddicPathEPEC());
032                CREATORS.add(new SlipstreamMercenaryEPEC());
033                CREATORS.add(new SlipstreamScavengerEPEC());
034                CREATORS.add(new SlipstreamNoEPEC());
035                
036                CREATORS.add(new OutsideSystemRemnantEPEC());
037                CREATORS.add(new OutsideSystemNoEPEC());
038                
039                CREATORS.add(new AbyssalLightEPEC());
040                CREATORS.add(new AbyssalLightDwellerEPEC());
041                CREATORS.add(new AbyssalRogueStellarObjectEPEC());
042                CREATORS.add(new AbyssalRogueStellarObjectDireHintsEPEC());
043                CREATORS.add(new AbyssalNoEPEC());
044        }
045        
046        
047        public static EncounterManager getInstance() {
048                String key = "$encounterManager";
049                EncounterManager manager = (EncounterManager) Global.getSector().getMemoryWithoutUpdate().get(key);
050                if (manager == null) {
051                        for (EveryFrameScript curr : Global.getSector().getScripts()) {
052                                if (curr instanceof EncounterManager) {
053                                        manager = (EncounterManager) curr;
054                                        Global.getSector().getMemoryWithoutUpdate().set(key, manager);
055                                        break;
056                                }
057                        }
058                }
059                return manager;
060        }
061        
062        protected Object readResolve() {
063                return this;
064        }
065        
066        protected Random random = new Random();
067        protected IntervalUtil interval = new IntervalUtil(0.2f, 0.4f);
068        protected TimeoutTracker<String> pointTimeout = new TimeoutTracker<String>();
069        protected TimeoutTracker<String> creatorTimeout = new TimeoutTracker<String>();
070        
071        public EncounterManager() {
072                Global.getSector().getListenerManager().addListener(this);
073        }
074        
075        public void advance(float amount) {
076                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
077                if (pf == null) return;
078                float days = Global.getSector().getClock().convertToDays(amount);
079                //days *= 1000f;
080                pointTimeout.advance(days);
081                creatorTimeout.advance(days);
082                
083                if (interval.getRandom() == null) {
084                        interval.setRandom(random);
085                }
086                
087                interval.advance(days);
088                if (interval.intervalElapsed()) {
089                        
090                        LocationAPI where = Global.getSector().getCurrentLocation();
091                        if (where == null) return;
092                        
093                        float minRange = Global.getSettings().getMaxSensorRange(where);
094                        float maxRange = minRange + MAX_EP_DIST_ADD;
095                        
096                        List<EncounterPoint> points = ListenerUtil.generateEncounterPoints(where);
097                        EncounterPoint closest = null;
098                        float minDist = Float.MAX_VALUE;
099                        
100//                      System.out.println("EncounterManager potential points");
101//                      System.out.println("---------------------------------");
102//                      for (EncounterPoint p : points) {
103//                              System.out.println(p);
104//                      }
105//                      System.out.println("---------------------------------");
106                        
107                        for (EncounterPoint p : points) {
108                                if (pointTimeout.contains(p.id)) {
109                                        continue;
110                                }
111                                float dist = Misc.getDistance(pf.getLocation(), p.loc);
112                                if (dist < minRange || dist > maxRange) continue;
113                                if (dist < minDist) {
114                                        closest = p;
115                                        minDist = dist;
116                                }
117                        }
118                        
119                        //System.out.println("Point picked: " + closest);
120                        
121                        if (closest != null) {
122                                WeightedRandomPicker<EPEncounterCreator> picker = new WeightedRandomPicker<EPEncounterCreator>(random);
123                                for (EPEncounterCreator c : CREATORS) {
124                                        if (creatorTimeout.contains(c.getId())) continue;
125                                        float f = c.getFrequencyForPoint(this, closest);
126                                        if (f > 0f) {
127                                                picker.add(c, f);
128                                        }
129                                }
130                                
131                                EPEncounterCreator c = picker.pick();
132                                if (c != null) {
133                                        c.createEncounter(this, closest);
134                                        
135                                        float ptTO = c.getPointTimeoutMin() + (c.getPointTimeoutMax() - c.getPointTimeoutMin()) * random.nextFloat();
136                                        float cTO = c.getCreatorTimeoutMin() + (c.getCreatorTimeoutMax() - c.getCreatorTimeoutMin()) * random.nextFloat();
137                                        if (ptTO > 0) {
138                                                pointTimeout.set(closest.id, ptTO);
139                                        }
140                                        if (cTO > 0) {
141                                                creatorTimeout.set(c.getId(), cTO);
142                                        }
143                                }
144                        }
145                }
146        }
147        
148        
149
150        public boolean isDone() {
151                return false;
152        }
153
154        public boolean runWhilePaused() {
155                return false;
156        }
157
158        public Random getRandom() {
159                return random;
160        }
161
162        public TimeoutTracker<String> getPointTimeout() {
163                return pointTimeout;
164        }
165
166        public TimeoutTracker<String> getCreatorTimeout() {
167                return creatorTimeout;
168        }
169        
170        
171        public List<EncounterPoint> generateEncounterPoints(LocationAPI where) {
172                if (!where.isHyperspace()) return null;
173                
174                List<EncounterPoint> result = new ArrayList<EncounterPoint>();
175                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
176                float velAngle = Misc.getAngleInDegrees(pf.getVelocity());
177                if (pf.getVelocity().length() < 10f) {
178                        velAngle = pf.getFacing();
179                }
180                for (CampaignTerrainAPI ter : where.getTerrainCopy()) {
181                        if (ter.getPlugin() instanceof SlipstreamTerrainPlugin2) {
182                                SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) ter.getPlugin();
183                                List<Vector2f> points = plugin.getEncounterPoints();
184                                if (points != null) {
185                                        for (Vector2f p : points) {
186                                                float dist = Misc.getDistance(pf.getLocation(), p);
187                                                if (dist > 10000f) continue;
188                                                if (!Misc.isInsideSlipstream(p, 500f)) {
189                                                        float diff = Misc.getAngleDiff(velAngle, Misc.getAngleInDegrees(pf.getLocation(), p));
190                                                        if (diff < 90f) {
191                                                                String id = ter.getId() + "_" + (int)Math.round(p.x) + "_" + (int)Math.round(p.y); 
192                                                                EncounterPoint ep = new EncounterPoint(id, where, p, EP_TYPE_SLIPSTREAM);
193                                                                result.add(ep);
194                                                        }
195                                                }
196                                        }
197                                }
198                        }
199                }
200                return result;
201        }
202        
203}
204
205
206
207
208
209
210