001package com.fs.starfarer.api.impl.campaign.abilities;
002
003import java.util.ArrayList;
004import java.util.Iterator;
005import java.util.List;
006import java.util.Random;
007
008import org.lwjgl.util.vector.Vector2f;
009
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.CampaignFleetAPI;
012import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
013import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
014import com.fs.starfarer.api.campaign.LocationAPI;
015import com.fs.starfarer.api.campaign.OrbitalStationAPI;
016import com.fs.starfarer.api.campaign.PlanetAPI;
017import com.fs.starfarer.api.campaign.SectorEntityToken;
018import com.fs.starfarer.api.impl.campaign.ids.Tags;
019import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2;
020import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment;
021import com.fs.starfarer.api.util.FaderUtil;
022import com.fs.starfarer.api.util.IntervalUtil;
023import com.fs.starfarer.api.util.Misc;
024
025public class GraviticScanData {
026
027        public static class GSPing {
028                public float arc;
029                public float angle;
030                public float grav;
031                public FaderUtil fader;
032                public boolean withSound = false;
033                public GSPing(float angle, float arc, float grav, float in, float out) {
034                        this.arc = arc;
035                        this.angle = angle;
036                        this.grav = grav;
037                        fader = new FaderUtil(0, in, out, false, true);
038                        fader.fadeIn();
039                }
040                
041                public void advance(float days) {
042                        fader.advance(days);
043                        if (withSound && fader.getBrightness() >= 0.5f) {
044                                Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle);
045                                float dist = 1000f + (1f - Math.min(1f, grav / 200f)) * 1450f;
046                                loc.scale(dist);
047                                Vector2f.add(loc, Global.getSector().getPlayerFleet().getLocation(), loc);
048                                Global.getSoundPlayer().playSound("ui_neutrino_detector_ping", 1, 1, loc, new Vector2f());
049                                withSound = false;
050                        }
051                }
052                
053                public boolean isDone() {
054                        return fader.isFadedOut();
055                }
056                
057                
058        }
059        
060        private GraviticScanAbility ability;
061        
062        
063        private int resolution = 360;
064        transient private float [] data;
065        
066        
067        private List<GSPing> pings = new ArrayList<GSPing>();
068        //private IntervalUtil noiseInterval = new IntervalUtil(0.01f, 0.02f);
069        
070        private IntervalUtil planetInterval = new IntervalUtil(0.01f, 0.01f);
071        private IntervalUtil specialInterval = new IntervalUtil(0.075f, 0.125f);
072        //private IntervalUtil specialInterval = new IntervalUtil(0.15f, 0.25f);
073        
074        public GraviticScanData(GraviticScanAbility ability) {
075                this.ability = ability;
076        }
077
078        public void advance(float days) {
079                if (ability.getFleet() == null || ability.getFleet().getContainingLocation() == null) return;
080                
081//              if (ability.getFleet().isInHyperspace()) {
082//                      data = null;
083//                      return;
084//              }
085                
086                Iterator<GSPing> iter = pings.iterator();
087                while (iter.hasNext()) {
088                        GSPing ping = iter.next();
089                        ping.advance(days);
090                        if (ping.isDone()) {
091                                iter.remove();
092                        }
093                }
094                
095                
096//              noiseInterval.advance(days);
097//              if (noiseInterval.intervalElapsed() && false) {
098//                      float noiseLevel = getNoiseLevel();
099//                      int num = Math.round(noiseLevel * 10);
100//                      num = 1;
101//                      for (int i = 0; i < num; i++) {
102//                              float angle = (float) Math.random() * 360f;
103////                            float arc = 5f + 10f * (float) Math.random() + 10 * noiseLevel;
104////                            float grav = 5f + 50f * (float) Math.random() * noiseLevel;
105//                              float arc = 5f + 10f * (float) Math.random();
106//                              float grav = 30f + 80f * (float) Math.random();
107//                              
108////                            float in = 0.02f + 0.02f * (float) Math.random();
109////                            float out = 0.02f + 0.02f * (float) Math.random();
110//                              
111//                              float in = 0.05f + 0.1f * (float) Math.random();
112//                              in *= 0.25f;
113//                              float out = in;
114//                              
115//                              GSPing ping = new GSPing(angle, arc, grav, in, out);
116//                              pings.add(ping);
117//                      }
118//              }
119                
120                planetInterval.advance(days);
121                if (planetInterval.intervalElapsed()) {
122                        maintainHighSourcePings();
123                }
124                
125                specialInterval.advance(days);
126                if (specialInterval.intervalElapsed()) {
127                        doSpecialPings();
128                }
129                
130                
131                
132                updateData();
133                
134                //System.out.println("Pings: " + pings.size());
135        }
136        
137        
138        
139        public void updateData() {
140                data = new float[resolution];
141                
142        
143                float max = 0f;
144                float incr = 360f / (float) resolution;
145                for (GSPing ping : pings) {
146                        
147                        float b = ping.fader.getBrightness();
148                        if (b <= 0) continue;
149                        
150                        //b = (float) Math.sqrt(b);
151                        //b *= b;
152                        
153                        float arc = ping.arc;
154                        float mid = ping.angle;
155                        float half = (float) Math.ceil(0.5f * arc / incr);
156                        for (float i = -half; i <= half; i++) {
157                                float curr = mid + incr * i;
158                                int index = getIndex(curr);
159                                
160                                float intensity = 1f - Math.abs(i / half);
161                                intensity *= intensity;
162                                float value = ping.grav * intensity * b;
163                                data[index] += value;
164                                //float min = Math.min(data[index], value);
165                                //data[index] = Math.max(data[index], value);
166                                //if (data[index] > max) max = data[index];
167                        }
168                }
169                
170        }
171        
172        public float getDataAt(float angle) {
173                if (data == null) return 0f;
174                int index = getIndex(angle);
175                return data[index];
176        }
177        
178        public int getIndex(float angle) {
179                angle = Misc.normalizeAngle(angle);
180                int index = (int)Math.floor(resolution * angle/360f);
181                return index;
182        }
183        
184        private int initialCount = 0;
185        private List<SectorEntityToken> special = new ArrayList<SectorEntityToken>();
186
187
188        //private float totalForce;
189        public void doSpecialPings() {
190                CampaignFleetAPI fleet = ability.getFleet();
191                boolean abyss = Misc.isInAbyss(fleet);
192                //abyss = false;
193                if (fleet.isInHyperspace() && !abyss) return;
194                
195                Vector2f loc = fleet.getLocation();
196                LocationAPI location = fleet.getContainingLocation();
197                
198                float neutrinoLowSkipProb = 0.8f;
199                if (special.isEmpty()) {
200//                      for (SectorEntityToken entity : location.getAsteroids()) {
201//                              special.add(entity);
202//                      }
203                        for (Object object : location.getEntities(CustomCampaignEntityAPI.class)) {
204                                if (object instanceof SectorEntityToken) {
205                                        SectorEntityToken entity = (SectorEntityToken) object;
206                                        
207                                        boolean neutrinoHigh = entity.hasTag(Tags.NEUTRINO_HIGH);
208                                        if (neutrinoHigh) continue;
209                                        
210                                        if (abyss && !Misc.isInAbyss(entity)) continue;
211                                        
212                                        boolean neutrino = entity.hasTag(Tags.NEUTRINO);
213                                        boolean neutrinoLow = entity.hasTag(Tags.NEUTRINO_LOW);
214                                        boolean station = entity.hasTag(Tags.STATION);
215                                        
216                                        
217                                        
218                                        if (!neutrino && !neutrinoLow && !station) continue;
219                                        if (neutrinoLow && (float) Math.random() < neutrinoLowSkipProb) continue;
220                                        
221                                        special.add(entity);
222                                }
223                        }
224//                      for (Object object : location.getEntities(OrbitalStationAPI.class)) {
225//                              if (object instanceof SectorEntityToken) {
226//                                      SectorEntityToken entity = (SectorEntityToken) object;
227//                                      special.add(entity);
228//                              }
229//                      }
230                        for (CampaignFleetAPI curr : location.getFleets()) {
231                                if (fleet == curr) continue;
232                                
233                                boolean neutrinoHigh = curr.hasTag(Tags.NEUTRINO_HIGH);
234                                if (neutrinoHigh) continue;
235                                
236                                if (abyss && !Misc.isInAbyss(fleet)) continue;
237                                
238                                if ((float) Math.random() < neutrinoLowSkipProb) continue;
239                                special.add(curr);
240                        }
241                        
242                        initialCount = special.size();
243                }
244                
245                int batch = (int) Math.ceil(initialCount / 1f);
246                for (int i = 0; i < batch; i++) {
247                        if (special.isEmpty()) break;
248                        
249                        SectorEntityToken curr = special.remove(0);
250                        
251                        float dist = Misc.getDistance(loc, curr.getLocation());
252                        
253                        float arc = Misc.computeAngleSpan(curr.getRadius(), dist);
254                        arc *= 2f;
255                        if (arc < 15) arc = 15;
256                        if (arc > 150f) arc = 150f;
257                        //arc += 30f;
258                        float angle = Misc.getAngleInDegrees(loc, curr.getLocation());
259                        
260                        float g = getGravity(curr);
261                        g *= getRangeGMult(dist);
262
263                        float in = 0.05f + 0.1f * (float) Math.random();
264                        in *= 0.25f;
265                        float out = in;
266                        out *= 2f;
267                        GSPing ping = new GSPing(angle, arc, g, in, out);
268                        ping.withSound = true;
269                        pings.add(ping);
270                }
271                
272                
273                long seed = (long) (location.getLocation().x * 1300000 + location.getLocation().y * 3700000 + 1213324234234L);
274                Random random = new Random(seed);
275                
276                int numFalse = random.nextInt(5);
277                //System.out.println(numFalse);
278                
279                for (int i = 0; i < numFalse; i++) {
280                        
281                        boolean constant = random.nextFloat() > 0.25f;
282                        if (!constant && (float) Math.random() < neutrinoLowSkipProb) {
283                                random.nextFloat();
284                                random.nextFloat();
285                                continue;
286                        }
287                        
288                        float arc = 15;
289                        float angle = random.nextFloat() * 360f;
290                        float in = 0.05f + 0.1f * (float) Math.random();
291                        in *= 0.25f;
292                        float out = in;
293                        out *= 2f;
294                        
295                        float g = 80 + random.nextFloat() * 60;
296                        
297                        GSPing ping = new GSPing(angle, arc, g, in, out);
298                        ping.withSound = true;
299                        pings.add(ping);
300                }
301                        
302                
303        }
304        
305        public float getRangeGMult(float range) {
306                range -= 3000;
307                if (range < 0) range = 0;
308                
309                float max = 15000;
310                if (range > max) range = max;
311                
312                
313                return 1f - 0.85f * range / max;
314        }
315        
316        
317        public void maintainSlipstreamPings() {
318                CampaignFleetAPI fleet = ability.getFleet();
319                Vector2f loc = fleet.getLocation();
320                LocationAPI location = fleet.getContainingLocation();
321                
322                float range = GraviticScanAbility.SLIPSTREAM_DETECTION_RANGE;
323
324                if (Misc.isInsideSlipstream(fleet) || Misc.isInAbyss(fleet)) return;
325                
326                for (CampaignTerrainAPI ter : location.getTerrainCopy()) {
327                        if (ter.getPlugin() instanceof SlipstreamTerrainPlugin2) {
328                                SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) ter.getPlugin();
329                                if (plugin.containsEntity(fleet)) continue;
330                                List<SlipstreamSegment> inRange = new ArrayList<SlipstreamSegment>();
331                                List<SlipstreamSegment> near = plugin.getSegmentsNear(loc, range);
332                                int skip = 0;
333                                for (SlipstreamSegment curr : near) {
334                                        if (skip > 0) {
335                                                skip--;
336                                                continue;
337                                        }
338                                        if (curr.bMult <= 0) continue;
339                                        float dist = Misc.getDistance(loc, curr.loc);
340                                        if (dist < range) {
341                                                inRange.add(curr);
342                                                skip = 5;
343                                        }
344                                }
345                                if (!inRange.isEmpty()) {
346                                        for (SlipstreamSegment curr : inRange) {
347                                                float dist = Misc.getDistance(loc, curr.loc);
348                                                
349                                                float arc = Misc.computeAngleSpan(curr.width, dist);
350                                                arc *= 2f;
351                                                if (arc > 150f) arc = 150f;
352                                                if (arc < 20) arc = 20;
353                                                //arc += 30f;
354                                                float angle = Misc.getAngleInDegrees(loc, curr.loc);
355                                                float g = 500f;
356                                                g *= .1f;
357                                                g *= getRangeGMult(dist);
358                                                float in = planetInterval.getIntervalDuration() * 5f;
359                                                float out = in;
360                                                GSPing ping = new GSPing(angle, arc, g, in, out);
361                                                pings.add(ping);
362                                        }
363                                }
364                        }
365                }
366                
367        }
368        
369        
370        public void maintainHighSourcePings() {
371                CampaignFleetAPI fleet = ability.getFleet();
372                Vector2f loc = fleet.getLocation();
373                LocationAPI location = fleet.getContainingLocation();
374                
375                maintainSlipstreamPings();
376                
377                boolean abyss = Misc.isInAbyss(fleet);
378                if (fleet.isInHyperspace() && !abyss) {
379                        return;
380                }
381                
382                
383//              Vector2f netForce = new Vector2f();
384
385                List<SectorEntityToken> all = new ArrayList<SectorEntityToken>(location.getPlanets());
386                for (Object object : location.getEntities(CustomCampaignEntityAPI.class)) {
387                        if (object instanceof SectorEntityToken) {
388                                SectorEntityToken entity = (SectorEntityToken) object;
389                                if (abyss && !Misc.isInAbyss(entity)) continue;
390                                
391                                boolean neutrinoHigh = entity.hasTag(Tags.NEUTRINO_HIGH);
392                                if (neutrinoHigh) {
393                                        all.add(entity);
394                                }
395                        }
396                }
397                for (CampaignFleetAPI curr : location.getFleets()) {
398                        if (fleet == curr) continue;
399                        if (abyss && !Misc.isInAbyss(fleet)) continue;
400                        boolean neutrinoHigh = curr.hasTag(Tags.NEUTRINO_HIGH);
401                        if (neutrinoHigh) {
402                                all.add(curr);
403                        }
404                }
405                
406                for (Object object : location.getEntities(OrbitalStationAPI.class)) {
407                        if (object instanceof SectorEntityToken) {
408                                SectorEntityToken entity = (SectorEntityToken) object;
409                                if (abyss && !Misc.isInAbyss(entity)) continue;
410                                all.add(entity);
411                        }
412                }
413                
414                for (Object object : location.getJumpPoints()) {
415                        if (object instanceof SectorEntityToken) {
416                                SectorEntityToken entity = (SectorEntityToken) object;
417                                if (abyss && !Misc.isInAbyss(entity)) continue;
418                                all.add(entity);
419                        }
420                }
421                
422                
423                for (SectorEntityToken entity : all) {
424                        if (entity instanceof PlanetAPI) {
425                                PlanetAPI planet = (PlanetAPI) entity;
426                                if (planet.getSpec().isNebulaCenter()) continue;
427                        }
428                        if (entity.getRadius() <= 0) continue;
429                        
430                        float dist = Misc.getDistance(loc, entity.getLocation());
431                        
432                        float arc = Misc.computeAngleSpan(entity.getRadius(), dist);
433                        arc *= 2f;
434                        if (arc > 150f) arc = 150f;
435                        if (arc < 20) arc = 20;
436                        //arc += 30f;
437                        float angle = Misc.getAngleInDegrees(loc, entity.getLocation());
438                        
439                        float g = getGravity(entity);
440                        //g /= dist;
441                        
442                        g *= .1f;
443                        if (entity.hasTag(Tags.NEUTRINO_HIGH) || entity instanceof OrbitalStationAPI) {
444                                g *= 2f;
445                        }
446                        
447                        g *= getRangeGMult(dist);
448                        
449//                      Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle);
450//                      dir.scale(g);
451//                      Vector2f.add(netForce, dir, netForce);
452//                      if (Misc.isInArc(90, 30, angle)) {
453//                              System.out.println("fwefewf");
454//                      }
455                        float in = planetInterval.getIntervalDuration() * 5f;
456                        float out = in;
457                        GSPing ping = new GSPing(angle, arc, g, in, out);
458                        pings.add(ping);
459                }
460                
461//              for (String key : objectPings.keySet()) {
462//                      if (!seen.contains(key)) {
463//                              GSPing ping = objectPings.get(key);
464//                              ping.fader.setBounceDown(true);
465//                      }
466//              }
467                
468                //totalForce = netForce.length();
469                //totalForce = maxG;
470                
471                //System.out.println("Pings: " + pings.size());
472                //System.out.println("Noise: " + getNoiseLevel());
473                //System.out.println("Force: " + totalForce);
474        }
475        
476//      public float getTotalForce() {
477//              return totalForce;
478//      }
479//      
480//      public float getNoiseLevel() {
481//              //if (true) return 0f;
482//              
483//              float minForce = 20f;
484//              float noiseOneAt = 150;
485//              
486//              if (totalForce <= minForce) return 0f;
487//              float noise = (totalForce - minForce) / (noiseOneAt - minForce);
488//              if (noise > 1) noise = 1;
489//              return noise;
490//      }
491
492        public float getGravity(SectorEntityToken entity) {
493                float g = entity.getRadius();
494                
495                if (entity instanceof PlanetAPI) {
496                        PlanetAPI planet = (PlanetAPI) entity;
497                        //if (g < 200) g = 200;
498                        
499                        g *= 2f;
500                        
501                        if (planet.getSpec().isBlackHole()) {
502                                g *= 2f;
503                        }
504                }
505                
506                if (entity instanceof OrbitalStationAPI) {
507                        g *= 4f;
508                        if (g > 200) g = 200;
509                }
510                
511                if (entity instanceof CustomCampaignEntityAPI) {
512                        g *= 4f;
513                        if (g > 200) g = 200;
514                }
515                
516                if (entity instanceof CampaignFleetAPI) {
517                        g *= 2f;
518                        if (g > 200) g = 200;
519                }
520                
521//              if (entity.getName().equals("Asteroid")) {
522//                      g *= 50f;
523//              }
524                
525                return g;
526        }
527        
528}
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555