001package com.fs.starfarer.api.impl.campaign.terrain;
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.Global;
010import com.fs.starfarer.api.campaign.CampaignFleetAPI;
011import com.fs.starfarer.api.campaign.LocationAPI;
012import com.fs.starfarer.api.campaign.StarSystemAPI;
013import com.fs.starfarer.api.impl.campaign.enc.EncounterPoint;
014import com.fs.starfarer.api.impl.campaign.enc.EncounterPointProvider;
015import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator.StarSystemType;
016import com.fs.starfarer.api.util.Misc;
017
018public class HyperspaceAbyssPluginImpl extends BaseHyperspaceAbyssPlugin implements EncounterPointProvider {
019
020        public static String EP_TYPE_ABYSSAL = "ep_type_abyssal";
021        
022        public static float ENCOUNTER_NEAR_ABYSSAL_SYSTEM_DIST = 2000f;
023        
024        
025        public static float NASCENT_WELL_DETECTED_RANGE = 1000f;
026        public static float JUMP_POINT_DETECTED_RANGE = 1300f;
027        public static float GAS_GIANT_DETECTED_RANGE = 1600f;
028        public static float STAR_DETECTED_RANGE = 2000f;
029        
030        
031        public static float DEPTH_THRESHOLD_FOR_ENCOUNTER = 0.25f;
032        public static float DEPTH_THRESHOLD_FOR_DWELLER_LIGHT = 3f;
033        public static float DEPTH_THRESHOLD_FOR_ABYSSAL_LIGHT = 1f;
034        public static float DEPTH_THRESHOLD_FOR_ABYSSAL_STELLAR_OBJECT = 1f;
035        public static float DEPTH_THRESHOLD_FOR_ABYSSAL_STAR_SYSTEM = 0.5f;
036        public static float DEPTH_THRESHOLD_FOR_NO_DUST_PARTICLES_IN_COMBAT = 0.5f;
037        public static float DEPTH_THRESHOLD_FOR_FLEETS_FLEEING = 0.5f;
038        
039        
040        public static class AbyssalEPData {
041                /**
042                 * The depth is uncapped. 
043                 */
044                public float depth;
045                public Random random;
046                public StarSystemAPI nearest = null;
047                public float distToNearest = Float.MAX_VALUE;
048                
049                public AbyssalEPData() {
050                        
051                }
052        }
053        
054        
055        public static float PLAYER_DIST_TRAVELLED_TO_REGEN_EPS = 1000f; // units not light-years
056        
057        protected Vector2f playerLocWhenGeneratingEPs = null;
058        protected List<EncounterPoint> encounterPoints = null;
059        protected Random random = new Random();
060        
061        public HyperspaceAbyssPluginImpl() {
062                Global.getSector().getListenerManager().addListener(this);
063        }
064        
065        protected Object readResolve() {
066                if (random == null) {
067                        random = new Random();
068                }
069                return this;
070        }
071
072        public float getAbyssalDepth(Vector2f loc, boolean uncapped) {
073                float w = Global.getSettings().getFloat("sectorWidth");
074                float h = Global.getSettings().getFloat("sectorHeight");
075                
076                // Orion-Perseus Abyss, lower left of the map, plus whatever area outside the map
077                float baseW = 100000f;
078                float baseH = 50000f;
079                
080                float normalizedX = (loc.x + w/2f) / baseW;
081                float normalizedY = (loc.y + h/2f) / baseH;
082                
083                float test = (float) (Math.sqrt(Math.max(0f, normalizedX)) + Math.sqrt(Math.max(0f, normalizedY)));
084//              float test = 1f;
085//              if (normalizedX >= 0 && normalizedY >= 0) {
086//                      test = (float) (Math.sqrt(normalizedX) + Math.sqrt(normalizedY));
087//              } else if (normalizedX < 0 && (loc.y + h/2f) < baseH) {
088//                      test = 0f;
089//              } else if (normalizedY < 0 && (loc.x + w/2f) < baseW) {
090//                      test = 0f;
091//              }
092                
093//              boolean player = Misc.getDistance(Global.getSector().getPlayerFleet().getLocationInHyperspace(), loc) < 5f;
094                
095                float depthBasedOnCorner = 0f;
096                if (test < 1f) {
097                        float threshold = 0.95f;
098//                      if (player) {
099//                              System.out.println("Depth: " + (1f - (test - threshold) / (1f - threshold)));
100//                      }
101                        if (uncapped) {
102                                //return (1f - (test - threshold) / (1f - threshold));
103                                depthBasedOnCorner = (1f - (test - threshold) / (1f - threshold));
104                        } else {
105                                if (test < threshold) depthBasedOnCorner = 1f;
106                                else depthBasedOnCorner = 1f - (test - threshold) / (1f - threshold);
107                        }
108//                      if (test < threshold) return 1f;
109//                      return 1f - (test - threshold) / (1f - threshold);
110                }
111                
112                // outside the map area
113                float left = -w/2f - loc.x; 
114                float below = -h/2f - loc.y; 
115                float right = loc.x - w/2f;
116                float above = loc.y - h/2f;
117                
118                float max = Math.max(left, Math.max(right, Math.max(above, below)));
119                if (max > 0) {
120//                      if (player) {
121//                              System.out.println("Depth: " + max/2000f);
122//                      }
123//                      if (uncapped) return max / 2000f;
124//                      return Math.min(1f, max / 2000f);
125                        if (uncapped) {
126                                return Math.max(depthBasedOnCorner, max / 2000f);
127                        }
128                        return Math.min(1f, Math.max(depthBasedOnCorner, max / 2000f));
129                }
130                
131                // inside the map, outside the Abyss area
132                //return 0f;
133                return depthBasedOnCorner;
134        }
135        
136        /**
137         * @param amount  
138         */
139        public void advance(float amount) {
140                if (!Global.getSector().getListenerManager().hasListener(this)) {
141                        Global.getSector().getListenerManager().addListener(this);
142                }
143
144        }
145        
146        public List<StarSystemAPI> getAbyssalSystems() {
147                List<StarSystemAPI> abyssal = new ArrayList<StarSystemAPI>();
148                for (StarSystemAPI system : Global.getSector().getStarSystems()) {
149                        float depth = Misc.getAbyssalDepth(system.getLocation());
150                        if (depth > DEPTH_THRESHOLD_FOR_ABYSSAL_STAR_SYSTEM) {
151                                abyssal.add(system);
152                        }
153                }
154                return abyssal;
155        }
156        
157        
158
159        public List<EncounterPoint> generateEncounterPoints(LocationAPI where) {
160                if (!where.isHyperspace()) return null;
161                
162                boolean regenerate = encounterPoints == null || playerLocWhenGeneratingEPs == null; 
163                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
164                
165                if (!regenerate) {
166                        Vector2f loc = pf.getLocation();
167                        float dist = Misc.getDistance(loc, playerLocWhenGeneratingEPs);
168                        regenerate = dist > PLAYER_DIST_TRAVELLED_TO_REGEN_EPS;
169                }
170                
171                if (!regenerate) return encounterPoints;
172                
173                List<StarSystemAPI> abyssal = getAbyssalSystems();
174                
175                playerLocWhenGeneratingEPs = new Vector2f(pf.getLocation());
176                encounterPoints = new ArrayList<EncounterPoint>();
177                
178                float startAngle = random.nextFloat() * 360f;
179                for (float angle = startAngle; angle < startAngle + 360f; angle += 30f) {
180                        Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle);
181                        float dist = 3000f;
182                        loc.scale(dist);
183                        Vector2f.add(loc, playerLocWhenGeneratingEPs, loc);
184                        loc = Misc.getPointWithinRadius(loc, 1000f, random);
185                        
186                        float depth = getAbyssalDepth(loc, true);
187                        if (depth < DEPTH_THRESHOLD_FOR_ENCOUNTER) continue;
188
189                        // Can match ids of other points, or have different ids for points that are 
190                        // very close to each other if they're across 1000-boundary 
191                        // this is fine. Just a way to *somewhat* limit repeated spawns in the same location
192                        
193                        String id = "abyssal_" + (int)(loc.x/1000f) + "_" + (int)(loc.y/1000f);
194                        EncounterPoint point = new EncounterPoint(id, where, loc, EP_TYPE_ABYSSAL);
195                        
196                        AbyssalEPData data = new AbyssalEPData();
197                        data.depth = depth;
198                        data.random = Misc.getRandom(random.nextLong(), 7);
199                        
200                        float minDist = Float.MAX_VALUE;
201                        StarSystemAPI nearest = null;
202                        for (StarSystemAPI system : abyssal) {
203                                float distToSystem = Misc.getDistance(system.getLocation(), loc);
204                                float testDist = ENCOUNTER_NEAR_ABYSSAL_SYSTEM_DIST;
205                                if (system.getType() == StarSystemType.DEEP_SPACE) {
206                                        testDist *= 0.5f;
207                                }
208                                if (distToSystem < testDist && distToSystem < minDist) {
209                                        minDist = distToSystem;
210                                        nearest = system;
211                                }
212                        }
213                        if (nearest != null) {
214                                data.nearest = nearest;
215                                data.distToNearest = minDist;
216                        }
217                        
218                        point.custom = data;
219                        encounterPoints.add(point);
220                }
221                
222                return encounterPoints;
223        }
224        
225}
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245