001package com.fs.starfarer.api.impl.campaign.velfield;
002
003import java.util.ArrayList;
004import java.util.EnumSet;
005import java.util.Iterator;
006import java.util.LinkedHashMap;
007import java.util.LinkedHashSet;
008import java.util.List;
009import java.util.Map;
010import java.util.Random;
011import java.util.Set;
012
013import java.awt.Color;
014
015import org.lwjgl.input.Mouse;
016import org.lwjgl.opengl.GL11;
017import org.lwjgl.opengl.GL14;
018import org.lwjgl.util.vector.Vector2f;
019
020import com.fs.starfarer.api.Global;
021import com.fs.starfarer.api.campaign.CampaignEngineLayers;
022import com.fs.starfarer.api.campaign.CampaignFleetAPI;
023import com.fs.starfarer.api.campaign.SectorEntityToken;
024import com.fs.starfarer.api.campaign.TerrainAIFlags;
025import com.fs.starfarer.api.combat.ViewportAPI;
026import com.fs.starfarer.api.fleet.FleetMemberViewAPI;
027import com.fs.starfarer.api.graphics.SpriteAPI;
028import com.fs.starfarer.api.impl.campaign.DebugFlags;
029import com.fs.starfarer.api.impl.campaign.abilities.ReversePolarityToggle;
030import com.fs.starfarer.api.impl.campaign.abilities.SustainedBurnAbility;
031import com.fs.starfarer.api.impl.campaign.ghosts.SensorGhost;
032import com.fs.starfarer.api.impl.campaign.ghosts.SensorGhostManager;
033import com.fs.starfarer.api.impl.campaign.ids.Entities;
034import com.fs.starfarer.api.impl.campaign.ids.Stats;
035import com.fs.starfarer.api.impl.campaign.ids.Tags;
036import com.fs.starfarer.api.impl.campaign.terrain.BaseTerrain;
037import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
038import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin.CellState;
039import com.fs.starfarer.api.loading.Description.Type;
040import com.fs.starfarer.api.ui.TooltipMakerAPI;
041import com.fs.starfarer.api.util.FaderUtil;
042import com.fs.starfarer.api.util.FaderUtil.State;
043import com.fs.starfarer.api.util.Misc;
044import com.fs.starfarer.api.util.MutatingVertexUtil;
045import com.fs.starfarer.api.util.WeightedRandomPicker;
046
047public class SlipstreamTerrainPlugin2 extends BaseTerrain {
048        
049        public static float FUEL_USE_MULT = 0.5f;
050        public static String FUEL_USE_MODIFIER_DESC = "Inside slipstream";
051        
052        public static class SlipstreamParams2 {
053                public String enteringSlipstreamTextOverride = null;
054                public Float enteringSlipstreamTextDurationOverride = null;
055                public boolean forceNoWindVisualEffectOnFleets = false;
056                public String spriteKey1 = "slipstream0";
057                public String spriteKey2 = "slipstream1";
058                public String spriteKey3 = "slipstream2";
059                public String edgeKey = "slipstream_edge";
060                public Color spriteColor = Color.white;
061                public Color windGlowColor = new Color(0.65f, 0.5f, 1f, 0.75f);
062                public Color edgeColor = Color.white;
063                public float baseWidth = 768f;
064                public float widthForMaxSpeed = 768f;
065                public float edgeWidth = 256f;
066                public float areaPerParticle = 10000;
067                public int maxParticles = 2000;
068                public float minSpeed;
069                public float maxSpeed;
070                public Color minColor = new Color(0.5f, 0.3f, 0.75f, 0.1f);
071                public Color maxColor = new Color(0.5f, 0.6f, 1f, 0.3f);
072                public Color mapColor = new Color(0.5f, 0.6f, 1f, 1f);
073                //public Color maxColor = new Color(0.5f, 0.6f, 1f, 0.5f);
074                public float minDur = 2f;
075                public float maxDur = 6f;
076                public float particleFadeInTime = 1f;
077                public float lineLengthFractionOfSpeed = 0.25f;
078                
079                public int burnLevel = 30;
080                public int maxBurnLevelForTextureScroll = 30;
081                public boolean slowDownInWiderSections = true;
082                public float widthForMaxSpeedMinMult = 0.67f;
083                public float widthForMaxSpeedMaxMult = 1.5f;
084                public float accelerationMult = 1f;
085                public String name = null;
086                
087                public float texScrollMult0 = 0f;
088                public float texScrollMult1 = -1.13f;
089                public float texScrollMult2 = -0.28f;
090                
091                Object readResolve() {
092                        if (accelerationMult <= 0f) {
093                                accelerationMult = 1f;
094                        }
095                        return this;
096                }
097        }
098        
099        public static class SlipstreamSegment {
100                public Vector2f loc = new Vector2f();
101                public float width;
102                public float bMult = 1f;
103                
104                transient public Vector2f locB = new Vector2f();
105                transient public Vector2f dir = new Vector2f();
106                transient public float wobbledWidth;
107                transient public int index = 0;
108                transient public Vector2f normal = new Vector2f();
109                transient public float tx = 0f;
110                transient public float txe1 = 0f;
111                transient public float txe2 = 0f;
112                transient public float totalLength;
113                transient public float lengthToPrev;
114                transient public float lengthToNext;
115                
116                transient public MutatingVertexUtil wobble1;
117                transient public MutatingVertexUtil wobble2;
118                public FaderUtil fader = new FaderUtil(0f, 1f, 1f);
119                
120                public boolean discovered = false;
121                
122                public Object readResolve() {
123                        float minRadius = 0f;
124                        float maxRadius = width * 0.05f;
125                        float rate = maxRadius * 0.5f;
126                        float angleRate = 50f;
127                        wobble1 = new MutatingVertexUtil(minRadius, maxRadius, rate, angleRate); 
128                        wobble2 = new MutatingVertexUtil(minRadius, maxRadius, rate, angleRate);
129                        locB = new Vector2f();
130                        return this;
131                }
132        }
133        
134        public static class SlipstreamParticle {
135                float speed;
136                float dist;
137                float yPos;
138                Color color;
139                float remaining;
140                float elapsed;
141        }
142        
143        public static int MAX_PARTICLES_ADD_PER_FRAME = 250;
144        
145        public static float RAD_PER_DEG = 0.01745329251f;
146        public static Vector2f rotateAroundOrigin(Vector2f v, float cos, float sin) {
147                Vector2f r = new Vector2f();
148                r.x = v.x * cos - v.y * sin;
149                r.y = v.x * sin + v.y * cos;
150                return r;
151        }
152        
153
154        
155        protected SlipstreamParams2 params = new SlipstreamParams2();
156        
157        protected List<SlipstreamSegment> segments = new ArrayList<SlipstreamTerrainPlugin2.SlipstreamSegment>();
158        protected float totalLength = 0f;
159        
160        protected transient List<Vector2f> encounterPoints = new ArrayList<Vector2f>();
161        
162        protected transient List<SlipstreamParticle> particles = new ArrayList<SlipstreamParticle>();
163        protected transient int [] lengthToIndexMap;
164        protected transient int lengthDivisor;
165        
166        protected boolean needsRecompute = true;
167        transient protected List<BoundingBox> bounds = new ArrayList<BoundingBox>();
168        protected int segmentsPerBox;
169        
170        protected float texProgress0 = 0f;
171        protected float texProgress1 = 0f;
172        protected float texProgress2 = 0f;
173        //protected transient float mapArrowProgress = 0f;
174        
175        protected float [] despawnNoise = null;
176        protected float despawnDelay = 0f;
177        protected float despawnDays = 0f;
178        protected float despawnElapsed = 0f;
179        
180        protected float [] spawnNoise = null;
181        protected float spawnDays = 0f;
182        protected float spawnElapsed = 0f;
183        protected boolean dynamic = false;
184        
185        public SlipstreamTerrainPlugin2() {
186        }
187        
188        public boolean isDespawning() {
189                return entity.hasTag(Tags.FADING_OUT_AND_EXPIRING) || despawnNoise != null;
190        }
191        
192        public void spawn(float spawnDays, Random random) {
193                //if (true) return;
194                this.spawnDays = spawnDays;
195                spawnElapsed = 0f;
196                
197                int numNoisePoints = 32;
198                while (numNoisePoints < segments.size()) {
199                        numNoisePoints *= 2f;
200                }
201                if (numNoisePoints > 512) numNoisePoints = 512;
202                
203                float spikes = 0.67f;
204                spawnNoise = SlipstreamBuilder.initNoise1D(random, numNoisePoints, spikes); 
205                spawnNoise[0] = 0.5f;
206                spawnNoise[spawnNoise.length - 1] = 0.5f;
207                SlipstreamBuilder.genNoise1D(random, spawnNoise, numNoisePoints, spikes);
208                SlipstreamBuilder.normalizeNoise1D(spawnNoise);
209        }
210        
211        protected void advanceSpawn(float amount) {
212                if (spawnNoise == null) return;
213                if (despawnElapsed > 0 || despawnDays > 0) return;
214                
215                float days = Global.getSector().getClock().convertToDays(amount);
216                
217                spawnElapsed += days;
218                float f = spawnElapsed / Math.max(0.1f, spawnDays);
219                
220                float size = segments.size();
221                boolean allFadedIn = true;
222                for (SlipstreamSegment seg : segments) {
223                        allFadedIn &= seg.fader.isFadedIn();
224                        
225                        float t = (seg.index + 1) / size; 
226                        float noise = SlipstreamBuilder.getInterpNoise(spawnNoise, t);
227                        if (noise <= f || f >= 1f) {
228                                float dur = Math.max(1f, Global.getSector().getClock().convertToSeconds((spawnDays - spawnElapsed) / 2f));
229                                seg.fader.setDurationIn(dur);
230                                seg.fader.fadeIn();
231                        } else {
232                                seg.fader.fadeOut();
233                        }
234                }
235                if (allFadedIn) {
236                        spawnNoise = null;
237                        spawnElapsed = 0;
238                        spawnDays = 0;
239                }
240        }
241        
242        public void despawn(float despawnDelay, float despawnDays, Random random) {
243                this.despawnDays = despawnDays;
244                this.despawnDelay = despawnDelay;
245                despawnElapsed = 0f;
246                
247                int numNoisePoints = 32;
248                while (numNoisePoints < segments.size()) {
249                        numNoisePoints *= 2f;
250                }
251                if (numNoisePoints > 512) numNoisePoints = 512;
252                
253                float spikes = 0.67f;
254                despawnNoise = SlipstreamBuilder.initNoise1D(random, numNoisePoints, spikes); 
255                despawnNoise[0] = 0.5f;
256                despawnNoise[despawnNoise.length - 1] = 0.5f;
257                SlipstreamBuilder.genNoise1D(random, despawnNoise, numNoisePoints, spikes);
258                SlipstreamBuilder.normalizeNoise1D(despawnNoise);
259        }
260        
261        protected void advanceDespawn(float amount) {
262//              if (isDespawning()) {
263//                      System.out.println("3f23f23f32");
264//              }
265                if (despawnNoise == null) return;
266                if (entity.hasTag(Tags.FADING_OUT_AND_EXPIRING)) return;
267                
268                float days = Global.getSector().getClock().convertToDays(amount);
269                despawnDelay -= days;
270                if (despawnDelay > 0) return;
271                
272                despawnElapsed += days;
273                float f = despawnElapsed / Math.max(0.1f, despawnDays);
274                
275                float size = segments.size();
276                boolean allFaded = true;
277                for (SlipstreamSegment seg : segments) {
278                        allFaded &= seg.fader.isFadedOut();
279                        
280                        float t = (seg.index + 1) / size; 
281                        float noise = SlipstreamBuilder.getInterpNoise(despawnNoise, t);
282                        if (noise <= f || f >= 1f) {
283                                float dur = Math.max(1f, Global.getSector().getClock().convertToSeconds((despawnDays - despawnElapsed) / 2f));
284                                seg.fader.setDurationOut(dur);
285                                seg.fader.fadeOut();
286                        }
287                }
288                if (allFaded) {
289                        Misc.fadeAndExpire(entity);
290                        despawnNoise = null;
291                }
292        }
293        
294        public void setNeedsRecompute() {
295                this.needsRecompute = true;
296        }
297
298        public void updateLengthToIndexMap() {
299                float minSegmentLength = Float.MAX_VALUE;
300                for (SlipstreamSegment curr : segments) {
301                        if (curr.lengthToNext > 0 && minSegmentLength > curr.lengthToNext) {
302                                minSegmentLength = curr.lengthToNext;
303                        }
304                }
305                if (minSegmentLength < 50f) minSegmentLength = 50f;
306                
307                lengthDivisor = (int) (minSegmentLength - 1f); 
308                int numIndices = (int) (totalLength / lengthDivisor);
309                lengthToIndexMap = new int [numIndices];
310                
311                int lengthSoFar = 0;
312                for (int i = 0; i < segments.size(); i++) {
313                        SlipstreamSegment curr = segments.get(i);
314                        while (lengthSoFar < curr.totalLength + curr.lengthToNext) {
315                                int lengthIndex = lengthSoFar / lengthDivisor;
316                                if (lengthIndex < lengthToIndexMap.length) {
317                                        lengthToIndexMap[lengthIndex] = i;
318                                }
319                                lengthSoFar += lengthDivisor;
320                        }
321                }
322        }
323        
324        public SlipstreamSegment getSegmentForDist(float distAlongStream) {
325                if (lengthToIndexMap == null) return null;
326                int mapIndex = (int) (distAlongStream / lengthDivisor);
327                if (mapIndex < 0 || mapIndex >= lengthToIndexMap.length) return null;
328                //System.out.println("Index: " + mapIndex + ", dist: " + distAlongStream);
329                int segIndex = lengthToIndexMap[mapIndex];
330                SlipstreamSegment segment = segments.get(segIndex);
331                while (distAlongStream < segment.totalLength) {
332                        segIndex--;
333                        if (segIndex < 0) return null;
334                        segment = segments.get(segIndex);
335                }
336                while (distAlongStream > segment.totalLength + segment.lengthToNext) {
337                        segIndex++;
338                        if (segIndex >= segments.size()) return null;
339                        segment = segments.get(segIndex);
340                }
341                return segment;
342        }
343        
344        public void addSegment(Vector2f loc, float width) {
345                SlipstreamSegment s = new SlipstreamSegment();
346                s.loc.set(loc);
347                s.width = width;
348                s.wobbledWidth = width - params.edgeWidth * 2f * 0.25f;
349                
350                float minRadius = 0f;
351                float maxRadius = s.width * 0.05f;
352                float rate = maxRadius * 0.5f;
353                float angleRate = 50f;
354                s.wobble1 = new MutatingVertexUtil(minRadius, maxRadius, rate, angleRate); 
355                s.wobble2 = new MutatingVertexUtil(minRadius, maxRadius, rate, angleRate); 
356                
357                s.fader.fadeIn();
358                
359                segments.add(s);
360                setNeedsRecompute();
361        }
362        
363        public void init(String terrainId, SectorEntityToken entity, Object pluginParams) {
364                super.init(terrainId, entity, pluginParams);
365                this.params = (SlipstreamParams2) pluginParams;
366                readResolve();
367        }
368        
369        public float getRenderRange() {
370                return totalLength * 0.6f + 1000f;
371                //return totalLength + 1000f;
372        }
373
374        Object readResolve() {
375                particles = new ArrayList<SlipstreamParticle>();
376                bounds = new ArrayList<BoundingBox>();
377                
378                needsRecompute = true;
379                layers = EnumSet.of(CampaignEngineLayers.TERRAIN_SLIPSTREAM);
380                return this;
381        }
382        
383        public void advance(float amount) {
384                super.advance(amount);
385                if (amount <= 0) {
386                        return; // happens during game load
387                }
388                if (entity.isInCurrentLocation()) {
389                        applyEffectToEntities(amount);
390                        doSoundPlayback(amount);
391                }
392                
393//              for (SlipstreamSegment seg : getSegments()) {
394//                      seg.fader.fadeOut();
395//                      seg.fader.fadeIn();
396//              }
397//              float countWithLowBMult = 0f;
398//              for (SlipstreamSegment seg : getSegments()) {
399//                      if (seg.bMult < 1f) {
400//                              countWithLowBMult++;
401//                      }
402//              }
403//              System.out.println("WITH LOW bMult from STP2: " + countWithLowBMult);
404                
405                
406                recomputeIfNeeded();
407                advanceNearbySegments(amount);
408        
409                addParticles();
410                advanceParticles(amount);
411                
412                advanceSpawn(amount);
413                advanceDespawn(amount);
414                
415                if (entity.isInCurrentLocation()) {
416                        float texSpeed = Misc.getSpeedForBurnLevel(Math.min(params.burnLevel * 0.5f, 
417                                                                                                                                params.maxBurnLevelForTextureScroll));
418                        
419                        SpriteAPI sprite = Global.getSettings().getSprite("misc", params.spriteKey1);
420                        
421                        float texelsPerPixel = 1f;
422                        if (segments.size() > 1) {
423                                texelsPerPixel = (segments.get(1).tx * sprite.getWidth()) / Math.max(1f, segments.get(1).lengthToPrev);
424                        }
425                        
426                        float unitsPerOneTexIter = sprite.getWidth();
427                        float texUnitsPerSecondForSpeed = texSpeed / unitsPerOneTexIter * texelsPerPixel;
428                        texProgress0 -= texUnitsPerSecondForSpeed * amount * params.texScrollMult0;
429                        texProgress1 += texUnitsPerSecondForSpeed * amount * params.texScrollMult1;
430                        texProgress2 += texUnitsPerSecondForSpeed * amount * params.texScrollMult2;
431                        if (texProgress0 > 100000) texProgress0 -= 100000f;
432                        if (texProgress1 > 100000) texProgress1 -= 100000f;
433                        if (texProgress2 > 100000) texProgress2 -= 100000f;
434                }
435        }
436        
437        public boolean isDynamic() {
438                return dynamic;
439        }
440
441        public void setDynamic(boolean dynamic) {
442                this.dynamic = dynamic;
443        }
444
445        public void recomputeIfNeeded() {
446                if (!needsRecompute) return;
447                recompute();
448        }
449        
450        public void recompute() {
451                needsRecompute = false;
452                
453                // compute average location, set segment indices
454                Vector2f avgLoc = new Vector2f();
455                for (int i = 0; i < segments.size(); i++) {
456                        SlipstreamSegment curr = segments.get(i);
457                        curr.index = i;
458                        Vector2f.add(avgLoc, curr.loc, avgLoc);
459                }
460
461                if (segments.size() > 0) {
462                        avgLoc.scale(1f / segments.size());
463                        entity.setLocation(avgLoc.x, avgLoc.y);
464                }
465
466                
467                SpriteAPI sprite = Global.getSettings().getSprite("misc", params.spriteKey1);
468                SpriteAPI edge = Global.getSettings().getSprite("misc", params.edgeKey);
469                
470                // compute texture coordinates etc
471                float tx = 0f;
472                float txe1 = 0f;
473                float txe2 = 0f;
474                float totalLength = 0f;
475                for (int i = 0; i < segments.size(); i++) {
476                        SlipstreamSegment prev = null;
477                        if (i > 0) prev = segments.get(i - 1);
478                        SlipstreamSegment curr = segments.get(i);
479                        SlipstreamSegment next = null;
480                        SlipstreamSegment next2 = null;
481                        SlipstreamSegment next3 = null;
482                        if (i < segments.size() - 1) {
483                                next = segments.get(i + 1);
484                        }
485                        if (i < segments.size() - 2) {
486                                next2 = segments.get(i + 2);
487                        }
488                        if (i < segments.size() - 3) {
489                                next3 = segments.get(i + 3);
490                        }
491
492                        if (curr.dir == null) curr.dir = new Vector2f();
493                        if (curr.normal == null) curr.normal = new Vector2f();
494                        
495                        if (next == null) {
496                                if (prev != null) {
497                                        curr.dir.set(prev.dir);
498                                }
499                        } else {
500                                Vector2f dir = Vector2f.sub(next.loc, curr.loc, new Vector2f());
501                                dir = Misc.normalise(dir);
502                                curr.dir = dir;
503                        }
504
505                        Vector2f dir = curr.dir;
506                        if (prev == null || next == null) {
507                                curr.normal.set(-dir.y, dir.x);
508                        } else {
509                                Vector2f avg = Vector2f.add(prev.dir, curr.dir, new Vector2f());
510                                avg.scale(0.5f);
511                                curr.normal.set(-avg.y, avg.x);
512                        }
513//                      Vector2f normal = new Vector2f(-dir.y, dir.x);
514//                      curr.normal.set(normal);
515                        
516                        float length = 0f;
517                        float texLength = 0f;
518                        float e1TexLength = 0f;
519                        float e2TexLength = 0f;
520                        if (prev != null) {
521                                Vector2f dir2 = Vector2f.sub(curr.loc, prev.loc, new Vector2f());
522                                length = dir2.length();
523                                texLength = length / sprite.getWidth();
524                                if (!dynamic) {
525                                        texLength = Math.min(texLength, sprite.getHeight() / curr.width);
526                                }
527
528                                Vector2f edgeCurr = new Vector2f(curr.loc);
529                                edgeCurr.x += curr.normal.x * curr.width * 0.5f;
530                                edgeCurr.y += curr.normal.y * curr.width * 0.5f;
531
532                                Vector2f edgePrev = new Vector2f(prev.loc);
533                                edgePrev.x += prev.normal.x * prev.width * 0.5f;
534                                edgePrev.y += prev.normal.y * prev.width * 0.5f;
535
536                                float length2 = Vector2f.sub(edgeCurr, edgePrev, new Vector2f()).length();
537                                e1TexLength = length2 / edge.getWidth() * edge.getHeight() / params.edgeWidth;
538
539
540                                edgeCurr = new Vector2f(curr.loc);
541                                edgeCurr.x -= curr.normal.x * curr.width * 0.5f;
542                                edgeCurr.y -= curr.normal.y * curr.width * 0.5f;
543
544                                edgePrev = new Vector2f(prev.loc);
545                                edgePrev.x -= prev.normal.x * prev.width * 0.5f;
546                                edgePrev.y -= prev.normal.y * prev.width * 0.5f;
547
548                                length2 = Vector2f.sub(edgeCurr, edgePrev, new Vector2f()).length();
549                                e2TexLength = length2 / edge.getWidth() * edge.getHeight() / params.edgeWidth;
550                        }
551
552                        tx += texLength;
553                        txe1 += e1TexLength;
554                        txe2 += e2TexLength;
555                        curr.tx = tx;
556                        curr.txe1 = txe1;
557                        curr.txe2 = txe2;
558                        curr.lengthToPrev = length;
559
560                        totalLength += length;
561                        curr.totalLength = totalLength;
562                        //curr.lengthToNext = Misc.getDistance(curr.loc, next.loc);
563                        if (prev != null) {
564                                prev.lengthToNext = length;
565                        }
566
567                        if (next != null && next2 != null && next3 != null) {
568                                Vector2f p0 = curr.loc;
569                                Vector2f p1 = next.loc;
570                                Vector2f p2 = next2.loc;
571                                Vector2f p3 = next3.loc;
572
573                                float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
574                                float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
575                                float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
576                                float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
577                                adjustment = diff * 0.5f;
578                                //adjustment = diff * 0.25f;
579                                float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
580                                //angle = Misc.getAngleInDegrees(p3, p2);
581                                float dist = Misc.getDistance(p2, p1);
582                                Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
583                                p1Adjusted.scale(dist);
584                                Vector2f.add(p1Adjusted, p2, p1Adjusted);
585                                next.locB = p1Adjusted;
586                        } else if (next != null) {
587                                next.locB = next.loc;
588                        }
589                        if (prev == null) {
590                                curr.locB = new Vector2f(curr.loc);
591                        }
592                }
593                this.totalLength = totalLength;
594                
595                updateLengthToIndexMap();
596                updateBoundingBoxes();
597        }
598        
599        protected void updateBoundingBoxes() {
600                segmentsPerBox = (int) Math.sqrt(segments.size()) + 1;
601                if (segmentsPerBox < 20) segmentsPerBox = 20;
602                
603                bounds.clear();
604                for (int i = 0; i < segments.size(); i+= segmentsPerBox) {
605                        List<SlipstreamSegment> section = new ArrayList<SlipstreamSegment>();
606                        for (int j = i; j < i + segmentsPerBox && j < segments.size(); j++) {
607                                section.add(segments.get(j));
608                        }
609                        if (i + segmentsPerBox < segments.size()) {
610                                section.add(segments.get(i + segmentsPerBox));
611                        }
612                        BoundingBox box = BoundingBox.create(section);
613                        bounds.add(box);
614                }
615        }
616        
617        protected void advanceNearbySegments(float amount) {
618//              if (isDespawning()) {
619//                      System.out.println("3f23f23f32");
620//              }
621                
622                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
623                if (pf == null || !entity.isInCurrentLocation()) {
624                        if (spawnNoise != null || despawnNoise != null) {
625                                for (int i = 0; i < segments.size(); i++) {
626                                        SlipstreamSegment curr = segments.get(i);
627                                        curr.fader.advance(amount);
628                                }
629                        }
630                        return;
631                }
632                
633//              if (segments.size() > 0) {
634//                      segments.get(0).fader.forceOut();
635//                      segments.get(segments.size() - 1).fader.fadeOut();
636//              }
637                
638                ViewportAPI viewport = Global.getSector().getViewport();
639                float viewRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
640                viewRadius = Math.max(6000f, viewRadius);
641                viewRadius += 1000f;
642                List<SlipstreamSegment> near = getSegmentsNear(viewport.getCenter(), viewRadius);
643                
644                // advance faders for all segments, not just nearby, since it's not just aesthetic
645                for (int i = 0; i < segments.size(); i++) {
646//                      if (i == 6) {
647//                              System.out.println("wefwefwef");
648//                      }
649                        SlipstreamSegment curr = segments.get(i);
650                        curr.fader.advance(amount);
651                }
652                
653                HyperspaceTerrainPlugin plugin = (HyperspaceTerrainPlugin) Misc.getHyperspaceTerrain().getPlugin();
654
655//              float [] c = getLengthAndWidthFractionWithinStream(pf.getLocation());
656//              if (c != null) {
657//                      if (getSegmentForDist(c[0]) != null) {
658//                              System.out.println("efwefwefew");
659//                              for (int i = 0; i < segments.size() && i < 20; i++) {
660//                                      System.out.println("bMult: " + segments.get(i).bMult);
661//                              }
662//                      }
663//              }
664                        
665                // advance wobble, compute wobbledWidth
666                for (int i = 0; i < near.size(); i++) {
667                        SlipstreamSegment curr = near.get(i);
668                        
669                        if (entity.isInHyperspace() && !curr.fader.isFadedOut() && 
670                                        curr.fader.getBrightness() * curr.bMult > 0.05f && curr.bMult > 0f) {
671                                plugin.setTileState(
672                                                curr.loc, curr.width * 0.5f + params.edgeWidth + 100f, 
673                                                CellState.OFF,
674                                                1f - curr.fader.getBrightness(), -1f);
675                                //plugin.turnOffStorms(curr.loc, curr.width * 0.5f + params.edgeWidth + 200f);
676                        }
677                        
678                        //curr.fader.advance(amount);
679                        
680                        float r1 = 0.5f + (float) Math.random() * 1f;
681                        float r2 = 0.5f + (float) Math.random() * 1f;
682                        curr.wobble1.advance(amount * r1);
683                        curr.wobble2.advance(amount * r2);
684//                      curr.wobble1.vector.set(0, 0);
685//                      curr.wobble2.vector.set(0, 0);
686                        
687                        Vector2f p1 = new Vector2f(curr.loc);
688                        Vector2f p2 = new Vector2f(curr.loc);
689                        p1.x += curr.normal.x * curr.width * 0.5f;
690                        p1.y += curr.normal.y * curr.width * 0.5f;
691                        p2.x -= curr.normal.x * curr.width * 0.5f;
692                        p2.y -= curr.normal.y * curr.width * 0.5f;
693                        
694                        p1.x += curr.wobble1.vector.x;
695                        p1.y += curr.wobble1.vector.y;
696                        p2.x += curr.wobble2.vector.x;
697                        p2.y += curr.wobble2.vector.y;
698                        
699                        //particles.clear();
700                        //curr.wobbledWidth = Misc.getDistance(p1, p2);
701                        float d = Misc.getDistance(p1, p2);
702                        //curr.wobbledWidth = d - params.edgeWidth * 2f * 0.5f;
703                        curr.wobbledWidth = d - params.edgeWidth * 2f * 0.25f;
704                        if (curr.wobbledWidth < d * 0.5f) curr.wobbledWidth = d * 0.5f;
705                        //curr.wobbledWidth = curr.width;
706                        
707                        if (curr.index > 0) {
708                                SlipstreamSegment prev = segments.get(curr.index - 1);
709                                Vector2f prev1 = new Vector2f(prev.loc);
710                                Vector2f prev2 = new Vector2f(prev.loc);
711                                prev1.x += prev.normal.x * prev.width * 0.5f;
712                                prev1.y += prev.normal.y * prev.width * 0.5f;
713                                prev2.x -= prev.normal.x * prev.width * 0.5f;
714                                prev2.y -= prev.normal.y * prev.width * 0.5f;
715                                
716                                float wobbleMult = 0.33f;
717                                wobbleMult = 0.4f;
718                                float maxWobbleRadius = Math.min(prev.width, curr.width) * 0.05f;
719                                float maxWobble1 = Misc.getDistance(p1, prev1) * wobbleMult;
720                                float maxWobble2 = Misc.getDistance(p2, prev2) * wobbleMult;
721                                maxWobble1 = Math.min(maxWobbleRadius, maxWobble1);
722                                maxWobble2 = Math.min(maxWobbleRadius, maxWobble2);
723                                
724                                if (curr.index < segments.size() - 1) {
725                                        SlipstreamSegment next = segments.get(curr.index + 1);
726                                        Vector2f next1 = new Vector2f(next.loc);
727                                        Vector2f next2 = new Vector2f(next.loc);
728                                        next1.x += next.normal.x * next.width * 0.5f;
729                                        next1.y += next.normal.y * next.width * 0.5f;
730                                        next2.x -= next.normal.x * next.width * 0.5f;
731                                        next2.y -= next.normal.y * next.width * 0.5f;
732                                        maxWobbleRadius = Math.min(next.width, curr.width) * 0.05f;
733                                        float maxWobble1A = Misc.getDistance(p1, next1) * wobbleMult;
734                                        float maxWobble2A = Misc.getDistance(p2, next2) * wobbleMult;
735                                        maxWobble1 = Math.min(maxWobble1, maxWobble1A);
736                                        maxWobble2 = Math.min(maxWobble2, maxWobble2A);
737                                }
738                                
739                                prev.wobble1.radius.setMax(maxWobble1);
740                                prev.wobble2.radius.setMax(maxWobble2);
741                                curr.wobble1.radius.setMax(maxWobble1);
742                                curr.wobble2.radius.setMax(maxWobble2);
743                        }
744                }
745        }
746        
747        
748        
749        public void addParticles() {
750                if (Global.getSector().getPlayerFleet() == null) {
751                        particles.clear();
752                        return;
753                }
754                
755                boolean useNewSpawnMethod = true;
756                //useNewSpawnMethod = false;
757                
758                if (useNewSpawnMethod) {
759                        boolean inCurrentLocation = entity.isInCurrentLocation();
760                        boolean inHyperspace = entity.isInHyperspace();
761                        boolean spawnForAllSegments = false;
762                        ViewportAPI viewport = Global.getSector().getViewport();
763                        Vector2f locFrom = viewport.getCenter();
764                        float viewRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
765                        viewRadius += 2000f;
766                        viewRadius = Math.max(viewRadius, 10000f);
767                        if (!inCurrentLocation) {
768                                if (inHyperspace) {
769                                        viewRadius = 5000f;
770                                        locFrom = Global.getSector().getPlayerFleet().getLocationInHyperspace();
771                                } else {
772                                        float dist = Misc.getDistanceToPlayerLY(entity);
773                                        spawnForAllSegments = dist < 2f;
774                                }
775                        }
776                        Set<SlipstreamSegment> veryNearSet = new LinkedHashSet<SlipstreamTerrainPlugin2.SlipstreamSegment>();
777                        if (inCurrentLocation) {
778                                float veryNearRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
779                                viewRadius += 500f;
780                                veryNearSet = new LinkedHashSet<SlipstreamTerrainPlugin2.SlipstreamSegment>(
781                                                        getSegmentsNear(viewport.getCenter(), veryNearRadius));
782                        }
783                        
784        //              viewRadius *= 0.5f;
785        //              viewRadius = 500f;
786                        
787                        List<SlipstreamSegment> near;
788                        if (spawnForAllSegments) {
789                                 near = new ArrayList<SlipstreamSegment>(segments);
790                        } else {
791                                near = getSegmentsNear(locFrom, viewRadius);
792                        }
793                        Set<SlipstreamSegment> nearSet = new LinkedHashSet<SlipstreamSegment>(near);
794                        
795                        Map<SlipstreamSegment, List<SlipstreamParticle>> particleMap = new LinkedHashMap<SlipstreamTerrainPlugin2.SlipstreamSegment, List<SlipstreamParticle>>();
796                        //for (SlipstreamParticle p : particles) {
797                        Iterator<SlipstreamParticle> iter = particles.iterator();
798                        while (iter.hasNext()) {
799                                SlipstreamParticle p = iter.next();
800                                SlipstreamSegment seg = getSegmentForDist(p.dist);
801                                if (seg != null) {
802                                        if (!nearSet.contains(seg)) {
803                                                iter.remove();
804                                                continue;
805                                        }
806                                        
807                                        List<SlipstreamParticle> list = particleMap.get(seg);
808                                        if (list == null) {
809                                                list = new ArrayList<SlipstreamTerrainPlugin2.SlipstreamParticle>();
810                                                particleMap.put(seg, list);
811                                        }
812                                        list.add(p);
813                                }
814                        }
815                        
816                        
817                        float totalArea = 0f;
818                        int nearParticles = 0;
819                        WeightedRandomPicker<SlipstreamSegment> segmentPicker = new WeightedRandomPicker<SlipstreamTerrainPlugin2.SlipstreamSegment>();
820                        
821                        // figure out how many particles to add total, and also which segments to add them
822                        // to to achieve a relatively even distribution
823                        for (int i = 0; i < near.size(); i++) {
824                                SlipstreamSegment curr = near.get(i);
825                                if (curr.lengthToNext <= 0) continue; // last segment, can't have particles in it since the stream is over
826        
827                                float area = curr.lengthToNext * curr.width;
828                                float desiredParticles =  area / params.areaPerParticle;
829                                if (desiredParticles < 1) desiredParticles = 1;
830                                
831                                float particlesInSegment = 0; 
832                                List<SlipstreamParticle> list = particleMap.get(curr);
833                                if (list != null) {
834                                        particlesInSegment = list.size();
835                                }
836                                
837                                float mult = 1f;
838                                // spawn more particles in visible/nearly visible areas
839                                // better to have less visible particles when the player zooms out while paused
840                                // than to have less visible particles when zoomed in
841                                if (veryNearSet.contains(curr)) mult = 10f;
842                                
843                                float w = desiredParticles - particlesInSegment;
844                                w *= mult;
845                                if (w < 5f) w = 5f;
846                                segmentPicker.add(curr, w);
847                                //segmentPicker.add(curr, 1f);
848                                
849                                totalArea += area;
850                                nearParticles += particlesInSegment;
851                        }
852                        
853                        
854                        int numParticlesBasedOnArea = (int) (totalArea / params.areaPerParticle);
855                        int actualDesired = numParticlesBasedOnArea;
856                        if (numParticlesBasedOnArea < 10) numParticlesBasedOnArea = 10;
857                        if (numParticlesBasedOnArea > params.maxParticles) numParticlesBasedOnArea = params.maxParticles;
858                        //System.out.println("Area: " + totalArea/params.numParticles);
859                        //numParticlesBasedOnArea = 20000;
860                        
861                        
862                        int particlesToAdd = numParticlesBasedOnArea - nearParticles;
863                        if (particlesToAdd > MAX_PARTICLES_ADD_PER_FRAME) {
864                                particlesToAdd = MAX_PARTICLES_ADD_PER_FRAME;
865                        }
866                        particlesToAdd = Math.min(particlesToAdd, params.maxParticles - particles.size());
867                        
868                        int added = 0;
869                        while (added < particlesToAdd) {
870                                added++;
871                                SlipstreamSegment seg = segmentPicker.pick();
872                                if (seg == null) continue;
873                                
874                                SlipstreamParticle p = new SlipstreamParticle();
875                                float fLength = (float) Math.random() * 1f;
876                                float fWidth = (float) Math.random() * 2f - 1f;
877                                
878                                float speed = params.minSpeed + (params.maxSpeed - params.minSpeed) * (float) Math.random();
879                                float dur = params.minDur + (params.maxDur - params.minDur) * (float) Math.random(); 
880                                
881                                p.yPos = fWidth;
882                                //p.dist = totalLength * fLength;
883                                p.dist = seg.totalLength + seg.lengthToNext * fLength;
884                                p.speed = speed;
885                                
886                                float intensity = getIntensity(p.yPos);
887                                float wMult = getWidthBasedSpeedMult(p.dist);
888        //                      if (wMult <= 0) {
889        //                              getWidthBasedSpeedMult(p.dist);
890        //                      }
891                                float speedMult = (0.65f + 0.35f * intensity) * wMult;
892                                p.speed *= speedMult;
893                                
894                                p.remaining = dur;
895                                p.color = getRandomColor();
896                                
897                                particles.add(p);
898                        }
899                        
900                        //System.out.println("Particles: " + particles.size() + " desired based on area: " + actualDesired);
901                        
902                } else {
903                        float totalArea = 0f;
904                        for (int i = 0; i < segments.size(); i++) {
905                                SlipstreamSegment curr = segments.get(i);
906                                totalArea += curr.lengthToPrev * curr.width;
907                        }
908                        
909                        int numParticlesBasedOnArea = (int) (totalArea / params.areaPerParticle);
910                        if (numParticlesBasedOnArea < 10) numParticlesBasedOnArea = 10;
911                        if (numParticlesBasedOnArea > params.maxParticles) numParticlesBasedOnArea = params.maxParticles;
912                        //System.out.println("Area: " + totalArea/params.numParticles);
913                        //numParticlesBasedOnArea = 20000;
914                
915                
916                        int added = 0;
917                        //while (particles.size() < params.numParticles && added < MAX_PARTICLES_ADD_PER_FRAME) {
918                        while (particles.size() < numParticlesBasedOnArea && added < MAX_PARTICLES_ADD_PER_FRAME) {
919                                added++;
920                                
921                                SlipstreamParticle p = new SlipstreamParticle();
922                                float fLength = (float) Math.random() * 1f;
923                                float fWidth = (float) Math.random() * 2f - 1f;
924                                
925                                float speed = params.minSpeed + (params.maxSpeed - params.minSpeed) * (float) Math.random();
926                                float dur = params.minDur + (params.maxDur - params.minDur) * (float) Math.random(); 
927                                
928                                p.yPos = fWidth;
929                                p.dist = totalLength * fLength;
930                                p.speed = speed;
931                                
932                                float intensity = getIntensity(p.yPos);
933                                float wMult = getWidthBasedSpeedMult(p.dist);
934        //                      if (wMult <= 0) {
935        //                              getWidthBasedSpeedMult(p.dist);
936        //                      }
937                                float speedMult = (0.65f + 0.35f * intensity) * wMult;
938                                p.speed *= speedMult;
939                                
940                                p.remaining = dur;
941                                p.color = getRandomColor();
942                                
943                                particles.add(p);
944                        }
945                }
946        }
947        
948        public void advanceParticles(float amount) {
949                Iterator<SlipstreamParticle> iter = particles.iterator();
950                while (iter.hasNext()) {
951                        SlipstreamParticle p = iter.next();
952                        p.remaining -= amount;
953                        p.elapsed += amount;
954                        if (p.remaining <= 0) {
955                                iter.remove();
956                                continue;
957                        }
958
959                        p.dist += p.speed * amount;
960                }
961        }
962        
963        public float getWidthBasedSpeedMult(float distAlong) {
964                float mult = 1f;
965                if (params.slowDownInWiderSections) {
966                        SlipstreamSegment curr = getSegmentForDist(distAlong);
967                        if (curr != null) {
968                                float width = curr.width;
969                                if (segments.size() > curr.index + 1) {
970                                        SlipstreamSegment next = segments.get(curr.index + 1);
971                                        float f = (distAlong - curr.totalLength) / curr.lengthToNext;
972                                        if (f < 0) f = 0;
973                                        if (f > 1) f = 1;
974                                        width = Misc.interpolate(width, next.width, f);
975                                        mult = Math.min(params.widthForMaxSpeedMaxMult,
976                                                        params.widthForMaxSpeedMinMult + (1f - params.widthForMaxSpeedMinMult) * params.widthForMaxSpeed / width);
977                                }
978                        }
979                }
980                return mult;
981        }
982        
983        public float getWidth(float distAlong) {
984                SlipstreamSegment curr = getSegmentForDist(distAlong);
985                if (curr != null) {
986                        float width = curr.width;
987                        if (segments.size() > curr.index + 1) {
988                                SlipstreamSegment next = segments.get(curr.index + 1);
989                                float f = (distAlong - curr.totalLength) / curr.lengthToNext;
990                                if (f < 0) f = 0;
991                                if (f > 1) f = 1;
992                                width = Misc.interpolate(width, next.width, f);
993                                return width;
994                        } else {
995                                return curr.width;
996                        }
997                }
998                return 0f;
999        }
1000        public float getWobbledWidth(float distAlong) {
1001                SlipstreamSegment curr = getSegmentForDist(distAlong);
1002                if (curr != null) {
1003                        float width = curr.wobbledWidth;
1004                        if (segments.size() > curr.index + 1) {
1005                                SlipstreamSegment next = segments.get(curr.index + 1);
1006                                float f = (distAlong - curr.totalLength) / curr.lengthToNext;
1007                                if (f < 0) f = 0;
1008                                if (f > 1) f = 1;
1009                                width = Misc.interpolate(width, next.wobbledWidth, f);
1010                                return width;
1011                        } else {
1012                                return curr.wobbledWidth;
1013                        }
1014                }
1015                return 0f;
1016        }
1017        
1018        public float getIntensity(float yOff) {
1019                yOff = Math.abs(yOff);
1020                float intensity = 1f;
1021                
1022                float dropoffAt = 0.5f;
1023                dropoffAt = 0.33f;
1024                if (yOff > dropoffAt) {
1025                        intensity = 1f - 1f * (yOff - dropoffAt) / (1f - dropoffAt);
1026                }
1027                return intensity;
1028        }
1029        
1030        public float getFaderBrightness(float distAlong) {
1031                SlipstreamSegment curr = getSegmentForDist(distAlong);
1032                if (curr != null) {
1033                        if (segments.size() > curr.index + 1) {
1034                                SlipstreamSegment next = segments.get(curr.index + 1);
1035                                float f = (distAlong - curr.totalLength) / curr.lengthToNext;
1036                                if (f < 0) f = 0;
1037                                if (f > 1) f = 1;
1038                                return Misc.interpolate(curr.fader.getBrightness() * curr.bMult,
1039                                                                                next.fader.getBrightness() * next.bMult, f);
1040                        } else {
1041                                return 0f;
1042                        }
1043                }
1044                return 0f;
1045        }
1046        
1047        protected transient SlipstreamBuilder builder = null;
1048        public SlipstreamBuilder getBuilder() {
1049                return builder;
1050        }
1051
1052        public void setBuilder(SlipstreamBuilder builder) {
1053                this.builder = builder;
1054        }
1055        
1056        public SlipstreamParams2 getParams() {
1057                return params;
1058        }
1059
1060        public void render(CampaignEngineLayers layer, ViewportAPI viewport) {
1061                //if (true) return;
1062                recomputeIfNeeded();
1063                if (lengthToIndexMap == null) return;
1064                
1065                float bMult = getAbyssalBMult(false);
1066                if (bMult <= 0f) return;
1067                
1068                if (false && builder != null) {
1069//                      CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
1070//                      Vector2f loc = new Vector2f(pf.getLocation());
1071//                      Vector2f loc = new Vector2f(segments.get(0).loc);
1072//                      loc.x -= 500f;
1073//                      loc.y -= 300f;
1074//                      long seed = 23895464576452L + 4384357483229348234L;
1075//                      seed = 1181783497276652981L ^ seed;
1076//                      Random random = new Random(seed);
1077                        
1078//                      SlipstreamBuilder builder = new SlipstreamBuilder(loc, params, random);
1079//                      builder.buildTest();
1080                        builder.renderDebug(1f);
1081                        return;
1082                }
1083                
1084                
1085                if (true && false) {
1086                        //BoundingBox box = BoundingBox.create(segments);
1087                        float mx = Mouse.getX();
1088                        float my = Mouse.getY();
1089                        float wmx = Global.getSector().getViewport().convertScreenXToWorldX(mx);
1090                        float wmy = Global.getSector().getViewport().convertScreenYToWorldY(my);
1091                        boolean inside = false;
1092                        for (BoundingBox box : bounds) {
1093                                box.renderDebug(1f);
1094                                inside |= box.pointNeedsDetailedCheck(new Vector2f(wmx, wmy));
1095                        }
1096                        
1097                        GL11.glDisable(GL11.GL_TEXTURE_2D);
1098                        GL11.glEnable(GL11.GL_BLEND);
1099                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1100                
1101                        GL11.glPointSize(20f);
1102                        GL11.glEnable(GL11.GL_POINT_SMOOTH);
1103                        if (inside) {
1104                                Misc.setColor(Color.green);
1105                        } else {
1106                                Misc.setColor(Color.gray);
1107                        }
1108                        
1109                        GL11.glBegin(GL11.GL_POINTS);
1110                        GL11.glVertex2f(wmx, wmy);
1111                        GL11.glEnd();
1112                        //return;
1113                }
1114                
1115                
1116                
1117                
1118                float viewRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
1119                viewRadius += 500f;
1120                
1121//              viewRadius *= 0.5f;
1122//              viewRadius = 500f;
1123                
1124                List<SlipstreamSegment> near = getSegmentsNear(viewport.getCenter(), viewRadius);
1125                Set<SlipstreamSegment> nearSet = new LinkedHashSet<SlipstreamSegment>(near);
1126                
1127                List<List<SlipstreamSegment>> subsections = new ArrayList<List<SlipstreamSegment>>();
1128                int prevIndex = -10;
1129                List<SlipstreamSegment> subsection = new ArrayList<SlipstreamSegment>();
1130                for (SlipstreamSegment seg : near) {
1131                        if (prevIndex != seg.index - 1) {
1132                                if (subsection != null && !subsection.isEmpty()) {
1133                                        subsections.add(subsection);
1134                                }
1135                                subsection = new ArrayList<SlipstreamSegment>();
1136                        }
1137                        subsection.add(seg);
1138                        prevIndex = seg.index;
1139                }
1140                if (subsection != null && !subsection.isEmpty()) {
1141                        subsections.add(subsection);
1142                }
1143                
1144                SpriteAPI sprite0 = Global.getSettings().getSprite("misc", params.spriteKey1);
1145                sprite0.setNormalBlend();
1146                sprite0.setColor(params.spriteColor);
1147                SpriteAPI sprite1 = Global.getSettings().getSprite("misc", params.spriteKey2);
1148                sprite1.setNormalBlend();
1149                sprite1.setColor(params.spriteColor);
1150                SpriteAPI sprite2 = Global.getSettings().getSprite("misc", params.spriteKey3);
1151                sprite2.setNormalBlend();
1152                sprite2.setColor(params.spriteColor);
1153                
1154                SpriteAPI edge = Global.getSettings().getSprite("misc", params.edgeKey);
1155                edge.setNormalBlend();
1156                edge.setColor(params.edgeColor);
1157                
1158                //sprite.setColor(Misc.setAlpha(params.spriteColor1, 255));
1159                //sprite.setColor(Color.blue);
1160                for (List<SlipstreamSegment> subsection2 : subsections) {
1161                        renderSegments(sprite0, sprite1, sprite2, edge, viewport.getAlphaMult(), subsection2, 0f, false);
1162                }
1163                
1164                //sprite.setColor(Color.red);
1165                //renderLayer(sprite, texProgress2, viewport.getAlphaMult());
1166                //sprite.setColor(Color.green);
1167                //renderLayer(sprite, texProgress3, viewport.getAlphaMult());
1168                
1169//              int state = 0;
1170//              for (int i = 0; i < segments.size() - 4; i += 2) {
1171//                      //GL11.glBegin(GL11.GL_POINTS);
1172//                      SlipstreamSegment prev = null;
1173//                      if (i > 0) {
1174//                              prev = segments.get(i - 1);
1175//                      }
1176//                      SlipstreamSegment curr = segments.get(i);
1177//                      SlipstreamSegment next = segments.get(i + 1);   
1178//                      SlipstreamSegment next2 = segments.get(i + 2);
1179//                      SlipstreamSegment next3 = segments.get(i + 3);
1180//                      Vector2f p0 = curr.loc;
1181//                      Vector2f p1 = next.loc;
1182//                      Vector2f p2 = next2.loc;
1183//                      Vector2f p3 = next3.loc;
1184//                      
1185//                      if (state == 0) {
1186//                              state = 1;
1187//                              float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1188//                              float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1189//                              float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1190//                              float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * diff * 0.5f + 180f;
1191//                              angle = Misc.getAngleInDegrees(p3, p2);
1192//                              float dist = Misc.getDistance(p2, p1);
1193//                              Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1194//                              p1Adjusted.scale(dist);
1195//                              Vector2f.add(p1Adjusted, p2, p1Adjusted);
1196//                              curr.locB.set(p1Adjusted);
1197//                      } else if (state == 1) {
1198//                              curr.locB.set(curr.loc);
1199//                      } else if (state == 2) {
1200//                              
1201//                      }
1202//              }
1203                
1204                
1205                
1206                
1207                
1208                GL11.glDisable(GL11.GL_TEXTURE_2D);
1209                GL11.glEnable(GL11.GL_BLEND);
1210                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1211                
1212                float zoom = Global.getSector().getViewport().getViewMult(); 
1213
1214                //GL11.glLineWidth(2f);
1215                //GL11.glLineWidth(Math.max(1f, 2f/zoom));
1216                GL11.glLineWidth(Math.max(1f, Math.min(2f, 2f/zoom)));
1217                //GL11.glLineWidth(1.5f);
1218                GL11.glEnable(GL11.GL_LINE_SMOOTH);
1219                
1220                Misc.setColor(new Color(1f, 1f, 1f, 0.5f));
1221                Misc.setColor(Color.white);
1222                //GL11.glLineWidth(1f);
1223                
1224//              for (SlipstreamSegment seg : segments) {
1225//                      if (seg.totalLength <= 0f && segments.indexOf(seg) > 1) {
1226//                              System.out.println("efwefwefwefe");
1227//                      }
1228//              }
1229                
1230                // draw bezier lines for debug
1231                for (float offset = -1f; false && offset <= 1f; offset += 0.1f) {
1232                //for (float offset = 0f; offset <= 0f; offset += 0.1f) {
1233                        GL11.glBegin(GL11.GL_LINE_STRIP);
1234                        float incr = 10f;
1235                        for (float len = 0; len < totalLength; len += incr) {
1236//                              if (len > 10000f) {
1237//                                      System.out.println("ewfwefew");
1238//                              }
1239                                /*
1240                                SlipstreamSegment curr = getSegmentForDist(len);
1241                                if (curr == null) continue;
1242                                int index = curr.index;
1243                                if (index >= segments.size() - 2) continue;
1244                                SlipstreamSegment next = segments.get(index + 1);
1245                                SlipstreamSegment next2 = segments.get(index + 2);
1246                                
1247                                if (index % 2 != 0) {
1248                                        curr = segments.get(index - 1);
1249                                        next = segments.get(index);
1250                                        next2 = segments.get(index + 1);
1251                                }
1252                                
1253                                float lenForT = len - curr.totalLength;
1254                                float t = lenForT / (curr.lengthToNext + next.lengthToNext);
1255                                
1256                                //Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1257                                Vector2f p0 = curr.loc;
1258                                Vector2f p1 = next.loc;
1259                                Vector2f p2 = next2.loc;
1260                                
1261                                p0 = new Vector2f(p0);
1262                                p0.x += curr.normal.x * params.width * 0.5f * offset;
1263                                p0.y += curr.normal.y * params.width * 0.5f * offset;
1264                                
1265                                p2 = new Vector2f(p2);
1266                                p2.x += next2.normal.x * params.width * 0.5f * offset;
1267                                p2.y += next2.normal.y * params.width * 0.5f * offset;
1268                                
1269                                p1 = new Vector2f(next.locB);
1270                                p1 = new Vector2f(p1);
1271                                p1.x += next.normal.x * params.width * 0.5f * offset;
1272                                p1.y += next.normal.y * params.width * 0.5f * offset;
1273                                
1274                                Vector2f p = Misc.bezier(p0, p1, p2, t);
1275                                
1276//                              float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1277//                              Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1278                                //curr.lengthToNext + next.lengthToNext
1279//                              float f = lenForT / curr.lengthToNext;
1280//                              Vector2f perp;
1281//                              if (f < 1f) {
1282//                                      perp = Misc.interpolateVector(curr.normal, next.normal, f);
1283//                              } else {
1284//                                      f = (lenForT - curr.lengthToNext) / next.lengthToNext;
1285////                                    if (f > 1f) {
1286////                                            System.out.println("wefwefe " + index);
1287////                                    }
1288//                                      perp = Misc.interpolateVector(next.normal, next2.normal, f);
1289//                              }
1290//                              perp.scale(offset * params.width * 0.5f);
1291                                //perp.set(0, 0);
1292                                
1293                                //p = Misc.interpolateVector(pPrev, p, 0.5f);
1294                                //GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1295                                 * 
1296                                 */
1297                                
1298                                Vector2f p = getPointAt(len, offset);
1299                                if (p != null) {
1300                                        GL11.glVertex2f(p.x, p.y);
1301                                }
1302                        }
1303                        if (false) {
1304                                Misc.setColor(Color.red);
1305                                for (int i = 0; i < segments.size() - 3; i+=2) {
1306                                        //GL11.glBegin(GL11.GL_POINTS);
1307                                        SlipstreamSegment prev = null;
1308                                        if (i > 0) {
1309                                                prev = segments.get(i - 1);
1310                                        }
1311                                        SlipstreamSegment curr = segments.get(i);
1312                                        SlipstreamSegment next = segments.get(i + 1);   
1313                                        SlipstreamSegment next2 = segments.get(i + 2);
1314                                        SlipstreamSegment next3 = segments.get(i + 3);
1315                                        
1316                //                      GL11.glVertex2f(curr.loc.x, curr.loc.y);
1317                //                      GL11.glVertex2f(next.loc.x, next.loc.y);
1318                //                      GL11.glVertex2f(next2.loc.x, next2.loc.y);
1319                                        
1320                                        Vector2f p0 = curr.loc;
1321                                        Vector2f p1 = next.loc;
1322                                        Vector2f p2 = next2.loc;
1323                                        Vector2f p3 = next3.loc;
1324                                        
1325        //                              float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1326        //                              float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1327        //                              float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1328        //                              float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
1329        //                              adjustment = diff * 0.5f;
1330        //                              //adjustment = diff * 0.25f;
1331        //                              float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
1332        //                              //angle = Misc.getAngleInDegrees(p3, p2);
1333        //                              float dist = Misc.getDistance(p2, p1);
1334        //                              Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1335        //                              p1Adjusted.scale(dist);
1336        //                              Vector2f.add(p1Adjusted, p2, p1Adjusted);
1337                                        
1338                                        //GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1339                                        //GL11.glVertex2f(p1.x, p1.y);
1340                                        
1341                                        p0 = new Vector2f(p0);
1342                                        p0.x += curr.normal.x * curr.width * 0.5f * offset;
1343                                        p0.y += curr.normal.y * curr.width * 0.5f * offset;
1344                                        
1345                                        p2 = new Vector2f(p2);
1346                                        p2.x += next2.normal.x * next2.width * 0.5f * offset;
1347                                        p2.y += next2.normal.y * next2.width * 0.5f * offset;
1348                                        
1349                                        p1 = new Vector2f(next.locB);
1350                                        p1 = new Vector2f(p1);
1351                                        p1.x += next.normal.x * next.width * 0.5f * offset;
1352                                        p1.y += next.normal.y * next.width * 0.5f * offset;
1353                                        
1354        //                              p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1355        //                              p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1356        //                              diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1357        //                              adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
1358        //                              adjustment = diff * 0.5f;
1359        //                              //adjustment = diff * 0.25f;
1360        //                              angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
1361        //                              //angle = Misc.getAngleInDegrees(p3, p2);
1362        //                              dist = Misc.getDistance(p2, p1);
1363        //                              p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1364        //                              p1Adjusted.scale(dist);
1365        //                              Vector2f.add(p1Adjusted, p2, p1Adjusted);
1366                                        
1367                                        incr = 10f;
1368                                        for (float len = 0; len < curr.lengthToNext + next.lengthToNext; len += incr) {
1369                                                float t = len / (curr.lengthToNext + next.lengthToNext);
1370                                                //Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1371                                                Vector2f p = Misc.bezier(p0, p1, p2, t);
1372                                                
1373                //                              float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1374                //                              Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1375                                                
1376                                                float f = len / curr.lengthToNext;
1377                                                Vector2f perp;
1378                                                if (f < 1f) {
1379                                                        perp = Misc.interpolateVector(curr.normal, next.normal, f);
1380                                                } else {
1381                                                        f = (len - curr.lengthToNext) / next.lengthToNext;
1382                                                        perp = Misc.interpolateVector(next.normal, next2.normal, f);
1383                                                }
1384                                                perp.scale(offset * curr.width * 0.5f);
1385                                                perp.set(0, 0);
1386                                                
1387                                                //p = Misc.interpolateVector(pPrev, p, 0.5f);
1388                                                GL11.glVertex2f(p.x, p.y);
1389                                                //GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1390                                                //GL11.glVertex2f(pPrev.x, pPrev.y);
1391                                        }
1392                                        //if (i == 4) break;
1393                                }
1394                        }
1395                        GL11.glEnd();
1396                }
1397                
1398//              GL11.glBegin(GL11.GL_LINES);
1399//              for (int i = 0; i < segments.size() - 4; i+=2) {
1400//                      //GL11.glBegin(GL11.GL_POINTS);
1401//                      SlipstreamSegment prev = null;
1402//                      if (i > 0) {
1403//                              prev = segments.get(i - 1);
1404//                      }
1405//                      SlipstreamSegment curr = segments.get(i);
1406//                      SlipstreamSegment next = segments.get(i + 1);   
1407//                      SlipstreamSegment next2 = segments.get(i + 2);
1408//                      SlipstreamSegment next3 = segments.get(i + 3);
1409//                      
1410////                    GL11.glVertex2f(curr.loc.x, curr.loc.y);
1411////                    GL11.glVertex2f(next.loc.x, next.loc.y);
1412////                    GL11.glVertex2f(next2.loc.x, next2.loc.y);
1413//                      
1414//                      Vector2f p0 = curr.loc;
1415//                      Vector2f p1 = next.loc;
1416//                      Vector2f p2 = next2.loc;
1417//                      Vector2f p3 = next3.loc;
1418//                      
1419//                      float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1420//                      float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1421//                      float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1422//                      float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
1423//                      adjustment = diff * 0.5f;
1424//                      //adjustment = diff * 0.25f;
1425//                      float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
1426//                      //angle = Misc.getAngleInDegrees(p3, p2);
1427//                      float dist = Misc.getDistance(p2, p1);
1428//                      Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1429//                      p1Adjusted.scale(dist);
1430//                      Vector2f.add(p1Adjusted, p2, p1Adjusted);
1431//                      
1432//                      //skip = diff < 30f;
1433//                      skip = false;
1434//                      if (skip) p1Adjusted.set(p1);
1435//                      skip = !skip;
1436//                      
1437//                      prevAdjustedP1 = p1Adjusted;
1438//                      //GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1439//                      //GL11.glVertex2f(p1.x, p1.y);
1440//                      
1441//                      float incr = 10f;
1442//                      Misc.setColor(new Color(1f, 0.5f, 0f, 1f));
1443//                      for (float len = 0; len < curr.lengthToNext + next.lengthToNext; len += incr) {
1444//                              float t = len / (curr.lengthToNext + next.lengthToNext);
1445//                              //Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1446//                              Vector2f p = Misc.bezier(curr.loc, p1Adjusted, next2.loc, t);
1447////                            float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1448////                            Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1449//                              
1450//                              float f = len / curr.lengthToNext;
1451//                              Vector2f perp;
1452//                              if (f < 1f) {
1453//                                      perp = Misc.interpolateVector(curr.normal, next.normal, f);
1454//                              } else {
1455//                                      f = (len - curr.lengthToNext) / next.lengthToNext;
1456//                                      perp = Misc.interpolateVector(next.normal, next2.normal, f);
1457//                              }
1458//                              
1459//                              
1460//                              perp.scale(1f * params.width * 0.5f);
1461//                              
1462//                              //p = Misc.interpolateVector(pPrev, p, 0.5f);
1463//                              //GL11.glVertex2f(p.x, p.y);
1464//                              GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1465//                              GL11.glVertex2f(p.x - perp.x, p.y - perp.y);
1466//                              //GL11.glVertex2f(pPrev.x, pPrev.y);
1467//                      }
1468//                      //if (i == 4) break;
1469//              }
1470//              GL11.glEnd();
1471                
1472//              GL11.glPointSize(10);
1473//              GL11.glBegin(GL11.GL_POINTS);
1474//              for (int i = 0; i < segments.size() - 4; i+=2) {
1475//                      if (i % 4 == 0) {
1476//                              Misc.setColor(Color.red);
1477//                      } else {
1478//                              Misc.setColor(Color.green);
1479//                      }
1480//                      //GL11.glBegin(GL11.GL_POINTS);
1481//                      //SlipstreamSegment prev = segments.get(i);
1482//                      SlipstreamSegment curr = segments.get(i);
1483//                      SlipstreamSegment next = segments.get(i + 1);   
1484//                      SlipstreamSegment next2 = segments.get(i + 2);
1485//                      SlipstreamSegment next3 = segments.get(i + 3);
1486//                      
1487////                    GL11.glVertex2f(curr.loc.x, curr.loc.y);
1488////                    GL11.glVertex2f(next.loc.x, next.loc.y);
1489////                    GL11.glVertex2f(next2.loc.x, next2.loc.y);
1490//                      
1491//                      Vector2f p0 = curr.loc;
1492//                      Vector2f p1 = next.loc;
1493//                      Vector2f p2 = next2.loc;
1494//                      Vector2f p3 = next3.loc;
1495//                      
1496//                      float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1497//                      float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1498//                      float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1499//                      float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * diff * 1f + 180f;
1500//                      //angle = Misc.getAngleInDegrees(p3, p2);
1501//                      float dist = Misc.getDistance(p2, p1);
1502//                      Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1503//                      p1Adjusted.scale(dist);
1504//                      Vector2f.add(p1Adjusted, p2, p1Adjusted);
1505//                      prevAdjustedP1 = p1Adjusted;
1506//                      //GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1507//                      //GL11.glVertex2f(p1.x, p1.y);
1508//                      
1509//                      GL11.glVertex2f(p0.x, p0.y);
1510//                      GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1511//                      GL11.glVertex2f(p2.x, p2.y);
1512//              }
1513//              GL11.glEnd();
1514                
1515                if (false) {
1516                        float[] place = getLengthAndWidthFractionWithinStream(Global.getSector().getPlayerFleet().getLocation());
1517                        if (place != null) {
1518                                Misc.setColor(Color.red);
1519                                GL11.glPointSize(40f/zoom);
1520                                GL11.glEnable(GL11.GL_POINT_SMOOTH);
1521                                GL11.glBegin(GL11.GL_POINTS);
1522                                Vector2f p = getPointAt(place[0], place[1]);
1523                                
1524                                GL11.glVertex2f(p.x, p.y);
1525                                
1526                                Misc.setColor(Color.blue);
1527                                p = Global.getSector().getPlayerFleet().getLocation();
1528                                GL11.glVertex2f(p.x, p.y);
1529                                
1530                                SlipstreamSegment seg = getSegmentForDist(place[0]);
1531                                if (seg != null) {
1532                                        float withinSeg = place[0] - seg.totalLength;
1533                                        Vector2f p2 = new Vector2f(seg.normal.y, -seg.normal.x);
1534                                        p2.scale(withinSeg);
1535                                        Vector2f.add(p2, seg.loc, p2);
1536                                        float width = seg.wobbledWidth;
1537                                        if (segments.size() > seg.index + 1) {
1538                                                SlipstreamSegment next = segments.get(seg.index + 1);
1539                                                width = Misc.interpolate(seg.wobbledWidth, next.wobbledWidth, 
1540                                                                (place[0] - seg.totalLength) / seg.lengthToNext);
1541                                        }
1542                                        p2.x += getNormalAt(place[0]).x * place[1] * width * 0.5f;
1543                                        p2.y += getNormalAt(place[0]).y * place[1] * width * 0.5f;
1544                                        Misc.setColor(Color.green);
1545                                        GL11.glVertex2f(p2.x, p2.y);
1546                                }
1547                                GL11.glEnd();
1548                        }
1549                }
1550                
1551//              GL11.glBegin(GL11.GL_LINE_STRIP);
1552//              for (int i = 1; i < segments.size() - 2; i++) {
1553//                      SlipstreamSegment prev = segments.get(i);
1554//                      SlipstreamSegment curr = segments.get(i);
1555//                      SlipstreamSegment next = segments.get(i + 1);   
1556//                      SlipstreamSegment next2 = segments.get(i + 2);
1557//                      
1558//                      float incr = 5f;
1559//                      for (float len = 0; len < curr.lengthToNext; len += incr) {
1560//                              float t = len / (curr.lengthToNext + next.lengthToNext);
1561//                              Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1562//                              
1563//                              float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1564//                              Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1565//                              
1566//                              //p = Misc.interpolateVector(pPrev, p, 0.5f);
1567//                              //GL11.glVertex2f(p.x, p.y);
1568//                              GL11.glVertex2f(pPrev.x, pPrev.y);
1569//                      }
1570//                      if (i == 4) break;
1571//              }
1572//              GL11.glEnd();
1573                
1574                //if (true) return;
1575                boolean curvedTrails = true;
1576                boolean useTex = false;
1577                useTex = !Global.getSettings().getBoolean("slipstreamUseGLLines");
1578                //if (zoom > 1.25f) useTex = false;
1579                //useTex = true;
1580                //System.out.println("USETEX = " + useTex);
1581                if (!useTex) {
1582                        GL11.glDisable(GL11.GL_TEXTURE_2D);
1583                        GL11.glEnable(GL11.GL_BLEND);
1584                        GL11.glLineWidth(Math.max(1f, Math.min(2f, 2f/zoom)));
1585                        //GL11.glLineWidth(25f);
1586                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1587                        GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
1588                }
1589                
1590                //curvedTrails = false;
1591                if (!curvedTrails) {
1592                        GL11.glBegin(GL11.GL_LINES);
1593                }
1594//              GL11.glEnable(GL11.GL_POINT_SMOOTH);
1595//              GL11.glPointSize(10f);
1596//              GL11.glBegin(GL11.GL_POINTS);
1597                //int index = 0;
1598                
1599                if (useTex) {
1600                        GL11.glEnable(GL11.GL_TEXTURE_2D);
1601                        GL11.glEnable(GL11.GL_BLEND);
1602                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1603                        
1604                        SpriteAPI line = Global.getSettings().getSprite("graphics/hud/line4x4.png");
1605                        //line = Global.getSettings().getSprite("graphics/hud/line32x32.png");
1606                        line.bindTexture();
1607                } else {
1608                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1609                }
1610                
1611                for (SlipstreamParticle p : particles) {
1612                        SlipstreamSegment seg = getSegmentForDist(p.dist);
1613                        if (seg == null || !nearSet.contains(seg)) continue; 
1614                        
1615//                      index++;
1616//                      if (index > 1) break;
1617                        //if (true) break;
1618                        float a = viewport.getAlphaMult();
1619                        if (p.remaining <= 0.5f) {
1620                                a = p.remaining / 0.5f;
1621                        } else if (p.elapsed < params.particleFadeInTime) {
1622                                a = p.elapsed / params.particleFadeInTime;
1623                        }
1624                        
1625                        a *= getFaderBrightness(p.dist);
1626                        a *= bMult;
1627                        
1628                        //a *= 0.5f;
1629                        //a *= 0.1f;
1630                        
1631                        //a = 1f;
1632                        
1633//                      SlipstreamSegment seg = getSegmentForDist(p.dist);
1634//                      if (seg == null) continue;
1635                        float yPos = p.yPos;
1636                        //yPos = 0f;
1637                        
1638                        if (curvedTrails) {
1639                                if (useTex) {
1640                                        GL11.glBegin(GL11.GL_QUAD_STRIP);
1641                                        Vector2f curr = getPointAt(p.dist, yPos);
1642                                        if (curr == null || !viewport.isNearViewport(curr, p.speed * params.lineLengthFractionOfSpeed + 50f)) {
1643                                                GL11.glEnd();
1644                                                continue;
1645                                        }
1646                                        float iter = 5f;
1647                                        float incr = p.speed * params.lineLengthFractionOfSpeed / iter;
1648                                        float lw = 1f;
1649                                        for (float i = 0; i < iter; i++) {
1650                                                float min = incr * 1f;
1651                                                float dist = p.dist - i * incr - min;
1652                                                Vector2f next = getPointAt(dist, yPos);
1653                                                if (next == null) break;
1654                                                
1655                                                Vector2f perp = getNormalAt(dist);
1656                                                if (perp == null) {
1657                                                        GL11.glEnd();
1658                                                        break;
1659                                                }
1660                                                
1661                                                float a1 = a * (iter - i) / (iter - 1);
1662                                                if (i == 0) a1 = 0f;
1663                                                
1664                                                Misc.setColor(p.color, a1);
1665                                                GL11.glTexCoord2f(0, 0f);
1666                                                GL11.glVertex2f(curr.x + perp.x * lw, curr.y + perp.y * lw);
1667                                                GL11.glTexCoord2f(0, 1f);
1668                                                GL11.glVertex2f(curr.x - perp.x * lw, curr.y - perp.y * lw);
1669                                                curr = next;
1670                                        }
1671                                        GL11.glEnd();
1672                                } else {
1673                                        GL11.glBegin(GL11.GL_LINE_STRIP);
1674                                        //GL11.glBegin(GL11.GL_LINES);
1675                                        Vector2f curr = getPointAt(p.dist, yPos);
1676                                        if (curr == null || !viewport.isNearViewport(curr, p.speed * params.lineLengthFractionOfSpeed + 50f)) {
1677                                                GL11.glEnd();
1678                                                continue;
1679                                        }
1680                                        float iter = 5f;
1681                                        float incr = p.speed * params.lineLengthFractionOfSpeed / iter;
1682                                        for (float i = 0; i < iter; i++) {
1683                                                
1684                                                float min = incr * 0.5f;
1685                                                Vector2f next = getPointAt(p.dist - i * incr - min, yPos);
1686                                                if (next == null) {
1687                                                        GL11.glEnd();
1688                                                        break;
1689                                                }
1690                                                
1691                                                float a1 = a * (iter - i) / (iter - 1);
1692                                                //float a2 = a * (iter - i - 1) / (iter - 1);
1693                                                if (i == 0) a1 = 0f;
1694                                                
1695                                                Misc.setColor(p.color, a1);
1696                                                GL11.glVertex2f(curr.x, curr.y);
1697                                                //Misc.setColor(p.color, a2);
1698                                                //GL11.glVertex2f(next.x, next.y);
1699                                                curr = next;
1700                                        }
1701                                        GL11.glEnd();
1702                                }
1703                        } else {
1704                                Vector2f start = getPointAt(p.dist + p.speed * params.lineLengthFractionOfSpeed * 0.1f, yPos);
1705                                if (start == null || !viewport.isNearViewport(start, 500)) continue;
1706                                
1707                                Vector2f mid = getPointAt(p.dist, yPos);
1708                                if (mid == null) continue;
1709                                Vector2f end = getPointAt(p.dist - p.speed * params.lineLengthFractionOfSpeed * 0.9f, yPos);
1710                                if (end == null) continue;
1711                                
1712                                Misc.setColor(p.color, 0f);
1713                                GL11.glVertex2f(start.x, start.y);
1714                                Misc.setColor(p.color, a);
1715                                GL11.glVertex2f(mid.x, mid.y);
1716                                GL11.glVertex2f(mid.x, mid.y);
1717                                Misc.setColor(p.color, 0f);
1718                                GL11.glVertex2f(end.x, end.y);
1719                        }
1720//                      
1721                }
1722                if (!curvedTrails) {
1723                        GL11.glEnd();
1724                }
1725        }
1726        
1727        
1728//      public void renderSegments(SpriteAPI sprite, SpriteAPI edge, float alpha, List<SlipstreamSegment> segments) {
1729//              renderSegments(sprite, edge, alpha, segments, 0f);
1730//      }
1731        
1732        public float getAbyssalBMult(boolean forMap) {
1733                if (forMap) return 1f;
1734                if (entity.hasTag(Tags.SLIPSTREAM_VISIBLE_IN_ABYSS)) return 1f;
1735                float bMult = 1f;
1736                if (!forMap) {
1737                        CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
1738                        if (playerFleet != null) {
1739                                float depth = Misc.getAbyssalDepth(playerFleet);
1740                                if (depth > 0) {
1741                                        bMult = Math.max(0f, 1f - depth);
1742                                }
1743                        }
1744                }
1745                return bMult;
1746        }
1747        
1748        public void renderSegments(SpriteAPI sprite0, SpriteAPI sprite1, SpriteAPI sprite2, 
1749                                                           SpriteAPI edge, float alpha, List<SlipstreamSegment> segments, float extraTX, boolean forMap) {
1750                //if (true) return;
1751                
1752                float bMult = getAbyssalBMult(forMap);
1753                if (bMult <= 0f) return;
1754                
1755                
1756                GL11.glEnable(GL11.GL_TEXTURE_2D);
1757                GL11.glEnable(GL11.GL_BLEND);
1758                //GL11.glDisable(GL11.GL_BLEND);
1759                //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1760                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1761
1762                //color = Misc.interpolateColor(color, Color.black, 0.5f);
1763                //color = Color.black;
1764                //color = Misc.scaleColorOnly(color, 0.25f);
1765                //color = Misc.setAlpha(color, 100);
1766
1767                boolean wireframe = false;
1768                //wireframe = true;
1769                if (wireframe) {
1770                        GL11.glLineWidth(1f);
1771                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1772                        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
1773                        GL11.glDisable(GL11.GL_TEXTURE_2D);
1774                        GL11.glDisable(GL11.GL_BLEND);
1775                        
1776                        Misc.setColor(Color.yellow);
1777                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1778                        //GL11.glLineWidth(3f);
1779                        GL11.glBegin(GL11.GL_LINE_STRIP);
1780                        for (SlipstreamSegment curr : segments) {
1781                                GL11.glVertex2f(curr.loc.x, curr.loc.y);
1782                        }
1783                        GL11.glEnd();
1784                }
1785                
1786                boolean subtract = false;
1787                //subtract = true;
1788                if (subtract) {
1789                        GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
1790                }
1791                
1792                // main background
1793                if (!wireframe) {
1794                        GL11.glEnable(GL11.GL_TEXTURE_2D);
1795                        GL11.glEnable(GL11.GL_BLEND);
1796                }
1797                //GL11.glDisable(GL11.GL_BLEND);
1798                //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1799                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1800                
1801                if (!forMap) extraTX = texProgress0;
1802                
1803                sprite0.bindTexture();
1804                Color color = sprite0.getColor();
1805                GL11.glBegin(GL11.GL_QUAD_STRIP);
1806                for (int i = 0; i < segments.size(); i++) {
1807                        SlipstreamSegment curr = segments.get(i);
1808                        float a = curr.fader.getBrightness() * curr.bMult * bMult;
1809                        if (i == 0 || i == segments.size() - 1) a = 0f;
1810                        
1811                        Vector2f p1 = new Vector2f(curr.loc);
1812                        p1.x += curr.normal.x * curr.width * 0.5f;
1813                        p1.y += curr.normal.y * curr.width * 0.5f;
1814                        Vector2f p2 = new Vector2f(curr.loc);
1815                        p2.x -= curr.normal.x * curr.width * 0.5f;
1816                        p2.y -= curr.normal.y * curr.width * 0.5f;
1817                        
1818                        if (!forMap) {
1819                                p1.x += curr.wobble1.vector.x;
1820                                p1.y += curr.wobble1.vector.y;
1821                                p2.x += curr.wobble2.vector.x;
1822                                p2.y += curr.wobble2.vector.y;
1823                        }
1824                        
1825                        Misc.setColor(color, alpha * 1f * a);
1826                        GL11.glTexCoord2f(curr.tx + extraTX, 0f);
1827                        GL11.glVertex2f(p1.x, p1.y);
1828                        GL11.glTexCoord2f(curr.tx + extraTX, 1f);
1829                        GL11.glVertex2f(p2.x, p2.y);
1830                }
1831                GL11.glEnd();
1832                
1833                if (!forMap) {
1834                        sprite1.bindTexture();
1835                        color = sprite1.getColor();
1836                        GL11.glBegin(GL11.GL_QUAD_STRIP);
1837                        for (int i = 0; i < segments.size(); i++) {
1838                                SlipstreamSegment curr = segments.get(i);
1839                                float a = curr.fader.getBrightness() * curr.bMult * bMult;
1840                                if (i == 0 || i == segments.size() - 1) a = 0f;
1841                                
1842                                Vector2f p1 = new Vector2f(curr.loc);
1843                                p1.x += curr.normal.x * curr.width * 0.5f;
1844                                p1.y += curr.normal.y * curr.width * 0.5f;
1845                                Vector2f p2 = new Vector2f(curr.loc);
1846                                p2.x -= curr.normal.x * curr.width * 0.5f;
1847                                p2.y -= curr.normal.y * curr.width * 0.5f;
1848                                
1849                                p1.x += curr.wobble1.vector.x;
1850                                p1.y += curr.wobble1.vector.y;
1851                                p2.x += curr.wobble2.vector.x;
1852                                p2.y += curr.wobble2.vector.y;
1853                                
1854                                Misc.setColor(color, alpha * 1f * a);
1855                                GL11.glTexCoord2f(curr.tx + texProgress1, 0f);
1856                                GL11.glVertex2f(p1.x, p1.y);
1857                                GL11.glTexCoord2f(curr.tx + texProgress1, 1f);
1858                                GL11.glVertex2f(p2.x, p2.y);
1859                        }
1860                        GL11.glEnd();
1861                        
1862                        sprite2.bindTexture();
1863                        color = sprite2.getColor();
1864                        GL11.glBegin(GL11.GL_QUAD_STRIP);
1865                        for (int i = 0; i < segments.size(); i++) {
1866                                SlipstreamSegment curr = segments.get(i);
1867                                float a = curr.fader.getBrightness() * curr.bMult * bMult;
1868                                if (i == 0 || i == segments.size() - 1) a = 0f;
1869
1870                                Vector2f p1 = new Vector2f(curr.loc);
1871                                p1.x += curr.normal.x * curr.width * 0.5f;
1872                                p1.y += curr.normal.y * curr.width * 0.5f;
1873                                Vector2f p2 = new Vector2f(curr.loc);
1874                                p2.x -= curr.normal.x * curr.width * 0.5f;
1875                                p2.y -= curr.normal.y * curr.width * 0.5f;
1876                                
1877                                p1.x += curr.wobble1.vector.x;
1878                                p1.y += curr.wobble1.vector.y;
1879                                p2.x += curr.wobble2.vector.x;
1880                                p2.y += curr.wobble2.vector.y;
1881                                
1882                                Misc.setColor(color, alpha * 1f * a);
1883                                GL11.glTexCoord2f(curr.tx + texProgress2, 0f);
1884                                GL11.glVertex2f(p1.x, p1.y);
1885                                GL11.glTexCoord2f(curr.tx + texProgress2, 1f);
1886                                GL11.glVertex2f(p2.x, p2.y);
1887                        }
1888                        GL11.glEnd();
1889                }
1890                
1891                
1892                // edges
1893                color = edge.getColor();
1894                float wobbleMult = 0.5f;
1895                edge.bindTexture();
1896                //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1897                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1898                
1899                GL11.glBegin(GL11.GL_QUAD_STRIP);
1900                
1901                for (int i = 0; i < segments.size(); i++) {
1902                        SlipstreamSegment curr = segments.get(i);
1903                        float a = curr.fader.getBrightness() * curr.bMult * bMult;
1904                        if (i == 0 || i == segments.size() - 1) a = 0f;
1905                        
1906//                      float width = getWidth(curr.totalLength);
1907//                      float wobbled = getWobbledWidth(curr.totalLength);
1908//                      float yOff = width / wobbled; 
1909//                      Vector2f p1 = getPointAt(curr.totalLength, yOff);
1910//                      Vector2f p2 = getPointAt(curr.totalLength, yOff);
1911//                      if (p1 == null) {
1912//                              System.out.println("efwefwefew");
1913//                              p1 = getPointAt(curr.totalLength, yOff);
1914//                      }
1915//                      p2 = new Vector2f(p1);
1916                        
1917                        Vector2f p1 = new Vector2f(curr.loc);
1918                        Vector2f p2 = new Vector2f(curr.loc);
1919                        p1.x += curr.normal.x * curr.width * 0.5f;
1920                        p1.y += curr.normal.y * curr.width * 0.5f;
1921                        p2.x += curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1922                        p2.y += curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1923                        
1924//                      p2.x += curr.normal.x * -params.edgeWidth;
1925//                      p2.y += curr.normal.y * -params.edgeWidth;
1926                        
1927                        if (!forMap) {
1928                                p1.x += curr.wobble1.vector.x * wobbleMult;
1929                                p1.y += curr.wobble1.vector.y * wobbleMult;
1930                                p2.x += curr.wobble1.vector.x * wobbleMult;
1931                                p2.y += curr.wobble1.vector.y * wobbleMult;
1932                        }
1933                        
1934                        Misc.setColor(color, alpha * 1f * a);
1935                        GL11.glTexCoord2f(curr.txe1, 1f);
1936                        GL11.glVertex2f(p1.x, p1.y);
1937                        GL11.glTexCoord2f(curr.txe1, 0f);
1938                        GL11.glVertex2f(p2.x, p2.y);
1939                }
1940                GL11.glEnd();
1941                
1942                //edge2.bindTexture();
1943                GL11.glBegin(GL11.GL_QUAD_STRIP);
1944                
1945                for (int i = 0; i < segments.size(); i++) {
1946                        SlipstreamSegment curr = segments.get(i);
1947                        float a = curr.fader.getBrightness() * curr.bMult * bMult;
1948                        if (i == 0 || i == segments.size() - 1) a = 0f;
1949                        
1950                        Vector2f p1 = new Vector2f(curr.loc);
1951                        p1.x -= curr.normal.x * curr.width * 0.5f;
1952                        p1.y -= curr.normal.y * curr.width * 0.5f;
1953                        Vector2f p2 = new Vector2f(curr.loc);
1954                        p2.x -= curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1955                        p2.y -= curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1956                        
1957                        if (!forMap) {
1958                                p1.x += curr.wobble2.vector.x * wobbleMult;
1959                                p1.y += curr.wobble2.vector.y * wobbleMult;
1960                                p2.x += curr.wobble2.vector.x * wobbleMult;
1961                                p2.y += curr.wobble2.vector.y * wobbleMult;
1962                        }
1963                        
1964                        Misc.setColor(color, alpha * 1f * a);
1965                        GL11.glTexCoord2f(curr.txe2, 1f);
1966                        GL11.glVertex2f(p1.x, p1.y);
1967                        GL11.glTexCoord2f(curr.txe2, 0f);
1968                        GL11.glVertex2f(p2.x, p2.y);
1969                }
1970                GL11.glEnd();
1971                
1972                
1973                if (subtract) {
1974                        GL14.glBlendEquation(GL14.GL_FUNC_ADD);
1975                }
1976                
1977                if (wireframe) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
1978        }
1979
1980        
1981        
1982        
1983        
1984        public Color getRandomColor() {
1985                return Misc.interpolateColor(params.minColor, params.maxColor, (float) Math.random());
1986        }
1987        
1988        public float getTotalLength() {
1989                return totalLength;
1990        }
1991        
1992
1993        /**
1994         * result[0] = actual distance along the length of the slipstream 
1995         * result[1] = offset along the width of the slipstream, 
1996         *                              0 = on center, 1 = on edge along normal, -1 = on edge along negative of normal
1997         * null if outside stream
1998         * Assumes rectangular, non-tapered stream
1999         * @param loc
2000         * @return
2001         */
2002        public float [] getLengthAndWidthFractionWithinStream(Vector2f loc) {
2003                return getLengthAndWidthFractionWithinStream(loc, 0f, false, 0f);
2004        }
2005        public float [] getLengthAndWidthFractionWithinStream(Vector2f loc, float extraRangeForCheck, boolean allowOutsideStream, float extraWidthForSegments) {
2006                recomputeIfNeeded();
2007                
2008                float dist = Misc.getDistance(loc, entity.getLocation());
2009                if (dist > getRenderRange()) return null;
2010                
2011                List<SlipstreamSegment> near = getSegmentsNear(loc, extraRangeForCheck);
2012                
2013                for (SlipstreamSegment curr : near) {
2014                        SlipstreamSegment next = null;
2015                        if (segments.size() > curr.index + 1) {
2016                                next = segments.get(curr.index + 1);
2017                        } else {
2018                                next = new SlipstreamSegment();
2019                                //next2.width = next.width;
2020                                next.wobbledWidth = curr.wobbledWidth;
2021                                
2022                                next.normal = curr.normal;
2023                                //next2.dir = next.dir;
2024                                next.loc = new Vector2f(curr.dir);
2025                                next.loc.scale(curr.lengthToPrev);
2026                                Vector2f.add(next.loc, curr.loc, next.loc);
2027                                //next2.locB = next2.loc;
2028                                next.lengthToPrev = curr.lengthToPrev;
2029                                //continue;
2030                        }
2031                                
2032                        Vector2f p3 = loc;
2033                        Vector2f p1 = curr.loc;
2034                        Vector2f p2 = next.loc;
2035                        
2036                        Vector2f currNormalP1 = new Vector2f(curr.loc);
2037                        Vector2f currNormalP2 = new Vector2f(curr.normal);
2038                        currNormalP2.scale(100f);
2039                        Vector2f.add(currNormalP2, currNormalP1, currNormalP2);
2040                        
2041                        Vector2f nextNormalP1 = new Vector2f(next.loc);
2042                        Vector2f nextNormalP2 = new Vector2f(next.normal);
2043                        nextNormalP2.scale(100f);
2044                        Vector2f.add(nextNormalP2, nextNormalP1, nextNormalP2);
2045                        
2046                        //Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
2047                        Vector2f dir = new Vector2f(curr.dir);
2048                        dir.scale(100f);
2049                        Vector2f p4 = Vector2f.add(p3, dir, new Vector2f());
2050                        
2051                        Vector2f currNormalP = Misc.intersectLines(currNormalP1, currNormalP2, p3, p4);
2052                        if (currNormalP == null) continue;
2053                        Vector2f nextNormalP = Misc.intersectLines(nextNormalP1, nextNormalP2, p3, p4);
2054                        if (nextNormalP == null) continue;
2055                        
2056                        float u = (p3.x - currNormalP.x) * (nextNormalP.x - currNormalP.x) + 
2057                                                        (p3.y - currNormalP.y) * (nextNormalP.y - currNormalP.y);
2058                        float denom = Vector2f.sub(nextNormalP, currNormalP, new Vector2f()).length();
2059                        denom *= denom;
2060                        if (denom == 0) continue;
2061                        u /= denom;
2062                        
2063                        if (u >= 0 && u <= 1) { // p3 is between the two points on the normals
2064                                Vector2f normalAtP3 = Misc.interpolateVector(curr.normal, next.normal, u);
2065                                normalAtP3.scale(100f);
2066                                Vector2f p3PlusNormal = Vector2f.add(p3, normalAtP3, new Vector2f());
2067                                
2068                                Vector2f intersect = Misc.intersectLines(p1, p2, p3, p3PlusNormal);
2069                                if (intersect == null) continue;
2070                                
2071                                float distFromLine = Vector2f.sub(intersect, p3, new Vector2f()).length();
2072                                float width = Misc.interpolate(curr.wobbledWidth, next.wobbledWidth, u);
2073                                width += extraWidthForSegments;
2074                                if (distFromLine >= width / 2f && !allowOutsideStream) return null;
2075                                
2076                                float [] result = new float[2];
2077                                //result[0] = curr.totalLength + u * curr.lengthToNext;
2078                                result[0] = curr.totalLength + u * next.lengthToPrev;
2079                                result[1] = distFromLine / (width / 2f);
2080                                
2081                                float currToLoc = Misc.getAngleInDegrees(p1, p3);
2082                                float segDir = Misc.getAngleInDegrees(p1, p2);
2083                                if (Misc.getClosestTurnDirection(segDir, currToLoc) < 0) {
2084                                        result[1] = -result[1]; 
2085                                }
2086                                
2087                                return result;
2088                        }
2089                }
2090                return null;
2091        }
2092        
2093        public void applyEffectToEntities(float amount) {
2094                if (entity.getContainingLocation() == null) return;
2095                
2096                float days = Global.getSector().getClock().convertToDays(amount);
2097                for (CampaignFleetAPI fleet : entity.getContainingLocation().getFleets()) {
2098                        if (isPreventedFromAffecting(fleet)) continue;
2099                        applyEffect(fleet, days);
2100                }
2101                //for (SectorEntityToken entity : entity.getContainingLocation().getEntitiesWithTag(Tags.GHOST)) {
2102                for (SectorEntityToken entity : entity.getContainingLocation().getCustomEntities()) {
2103                        if (entity.hasTag(Tags.GHOST)) {
2104                                if (isPreventedFromAffecting(entity)) continue;
2105                                applyEffectToGhost(entity, days);
2106                        } else if (Entities.WRECK.equals(entity.getCustomEntityType())) {
2107                                if (isPreventedFromAffecting(entity)) continue;
2108                                applyEffectToWreck(entity, days);
2109                        }
2110                        
2111                }
2112        }
2113        
2114        //protected boolean playerWasInSlipstream = false;
2115        protected int playerWasInSlipstreamFramesAgo = 1000;
2116        protected float playerDesiredYOffset = 1000;
2117        protected void playerNoLongerinSlipstream() {
2118                playerWasInSlipstreamFramesAgo++;
2119                if (playerWasInSlipstreamFramesAgo > 1000) {
2120                        playerWasInSlipstreamFramesAgo = 1000;
2121                }
2122                playerDesiredYOffset = 1000;
2123        }
2124        public void applyEffect(SectorEntityToken other, float days) {
2125                if (other.hasTag(Tags.UNAFFECTED_BY_SLIPSTREAM)) return;
2126                
2127                if (!containsPoint(other.getLocation(), 0f)) {
2128                        if (other.isPlayerFleet()) {
2129                                playerNoLongerinSlipstream();
2130                        }
2131                        return;
2132                }
2133                
2134                if (other instanceof CampaignFleetAPI) {
2135                        CampaignFleetAPI fleet = (CampaignFleetAPI) other;
2136                        
2137//                      if (fleet.isPlayerFleet()) {
2138//                              if (getLengthAndWidthFractionWithinStream(fleet.getLocation()) == null) {
2139//                                      System.out.println("wefwefwefe");
2140//                              }
2141//                              System.out.println("efwefwef");
2142//                      }
2143                        
2144                        float [] offset = getLengthAndWidthFractionWithinStream(fleet.getLocation());
2145                        if (offset == null) {
2146                                if (fleet.isPlayerFleet()) {
2147                                        playerNoLongerinSlipstream();
2148                                }
2149                                return;
2150                        }
2151                        
2152//                      if (fleet.isPlayerFleet()) {
2153//                              System.out.println("Location in stream: " + offset[0] + ", " + offset[1]);
2154//                      }
2155                        
2156                        //params.burnLevel = 10;
2157                        
2158                        float distAlong = offset[0];
2159                        float yOff = offset[1];
2160                        
2161//                      float intensity = 1f;
2162//                      if (Math.abs(yOff) > 0.5f) {
2163//                              intensity *= (1f - Math.abs(yOff)) / 0.5f;
2164//                      }
2165                        float intensity = getIntensity(yOff);
2166                        float wMult = getWidthBasedSpeedMult(distAlong);
2167                        //System.out.println("wMult: " + wMult);
2168                        intensity *= wMult;
2169                        intensity *= getFaderBrightness(distAlong);
2170                        //intensity *= intensity;
2171                        //System.out.println(intensity);
2172                        
2173                        if (intensity <= 0.05f) {
2174                                if (fleet.isPlayerFleet()) {
2175                                        playerNoLongerinSlipstream();
2176                                }
2177                                return;
2178                        }
2179                        
2180                        preventOtherTerrainFromAffecting(fleet);
2181                        fleet.getMemoryWithoutUpdate().set(SustainedBurnAbility.SB_NO_STOP, true, 0.1f);
2182                        fleet.getMemoryWithoutUpdate().set(SustainedBurnAbility.SB_NO_SLOW, true, 0.1f);
2183                        
2184                        if (fleet.isPlayerFleet()) {
2185                                //if (!playerWasInSlipstream) {
2186                                //      playerWasInSlipstream = true;
2187                                if (playerWasInSlipstreamFramesAgo > 5) {
2188                                        String text = "Entering slipstream";
2189                                        if (params.enteringSlipstreamTextOverride != null) {
2190                                                text = params.enteringSlipstreamTextOverride;
2191                                        }
2192                                        float dur = 0.5f;
2193                                        if (params.enteringSlipstreamTextDurationOverride != null) {
2194                                                dur = params.enteringSlipstreamTextDurationOverride;
2195                                        }
2196                                        if (!text.isEmpty()) {
2197                                                fleet.addFloatingText(text, Misc.setAlpha(fleet.getIndicatorColor(), 255), dur);
2198                                        }
2199                                }
2200                                playerWasInSlipstreamFramesAgo = 0;
2201                        }
2202                        
2203                        //System.out.println("Intensity: " + intensity);
2204
2205                        // "wind" effect - adjust velocity
2206                        float maxFleetBurn = fleet.getFleetData().getBurnLevel();
2207                        float currFleetBurn = fleet.getCurrBurnLevel();
2208                        
2209                        boolean reversePolarity = Misc.isReversePolarity(fleet);
2210                        
2211                        float maxWindBurn = params.burnLevel * 2f;
2212                        float currWindBurn = intensity * maxWindBurn;
2213                        float maxFleetBurnIntoWind = maxFleetBurn - Math.abs(currWindBurn);
2214                        float seconds = days * Global.getSector().getClock().getSecondsPerDay();
2215                        
2216                        
2217//                      float angle = Misc.getAngleInDegreesStrict(this.entity.getLocation(), fleet.getLocation()) + 180f;
2218//                      Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(angle);
2219                        Vector2f p1 = getPointAt(distAlong, yOff);
2220                        Vector2f p2 = getPointAt(distAlong + 1f, yOff);
2221                        if (reversePolarity) {
2222                                p1 = getPointAt(distAlong, yOff);
2223                                p2 = getPointAt(distAlong - 1f, yOff);
2224                        }
2225                        if (p1 == null || p2 == null) {
2226                                if (fleet.isPlayerFleet()) {
2227                                        playerNoLongerinSlipstream();
2228                                }
2229                                return;
2230                        }
2231                        
2232                        
2233                        //Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(entity.getFacing());
2234                        Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
2235                        if (currWindBurn < 0) {
2236                                windDir.negate();
2237                        }
2238                        
2239                        Vector2f velDir = Misc.normalise(new Vector2f(fleet.getVelocity()));
2240                        //float baseFleetAccel = Misc.getSpeedForBurnLevel(fleet.getFleetData().getMinBurnLevel());
2241                        float baseFleetAccel = fleet.getTravelSpeed();
2242                        if (baseFleetAccel < 10f) baseFleetAccel = 10f;
2243                        
2244                        boolean fleetTryingToMove = fleet.getMoveDestination() != null && 
2245                                        Misc.getDistance(fleet.getLocation(), fleet.getMoveDestination()) > fleet.getRadius() + 10f;
2246                        if (fleet.isPlayerFleet()) {
2247                                fleetTryingToMove &= (
2248                                                Global.getSector().getCampaignUI().isPlayerFleetFollowingMouse() ||
2249                                                fleet.wasSlowMoving()
2250                                                );
2251                                
2252                                String key = "$slipstream_moveToYOffset";
2253                                if (fleetTryingToMove && fleet.getMoveDestination() != null) {
2254//                                      float mx = Mouse.getX();
2255//                                      float my = Mouse.getY();
2256                                        // this accounts for screen scaling
2257                                        float mx = Global.getSettings().getMouseX();
2258                                        float my = Global.getSettings().getMouseY();
2259                                        float wmx = Global.getSector().getViewport().convertScreenXToWorldX(mx);
2260                                        float wmy = Global.getSector().getViewport().convertScreenYToWorldY(my);
2261                                        float [] desired = getLengthAndWidthFractionWithinStream(new Vector2f(wmx, wmy));
2262                                        if (desired != null) {
2263                                                playerDesiredYOffset = desired[1];
2264                                                fleet.getMemoryWithoutUpdate().set(key, true, 0.2f);
2265                                        } else {
2266                                                playerDesiredYOffset = 1000f;
2267                                        }
2268                                }
2269                                if (!fleet.getMemoryWithoutUpdate().getBoolean(key)) {
2270                                        playerDesiredYOffset = 1000f;
2271                                }
2272                        }
2273                        
2274                        //System.out.println("PDY: " + playerDesiredYOffset);
2275                        float windSpeedReduction = 0f;
2276                        if (!fleetTryingToMove) {
2277                                Vector2f dest = new Vector2f(windDir);
2278                                dest.scale(1000f);
2279                                if (playerDesiredYOffset <= 1f && playerDesiredYOffset >= -1f) {
2280                                        float currOffset = offset[1];
2281                                        float diff = playerDesiredYOffset - currOffset;
2282                                        float sign = Math.signum(diff);
2283                                        if (reversePolarity) {
2284                                                sign = -sign;
2285                                        }
2286                                        float mult = Math.min(Math.abs(diff) * 1f, 1f);
2287                                        dest = Misc.rotateAroundOrigin(dest, Math.min(60f, 60f * mult * 1f) * sign);
2288                                }
2289                                Vector2f.add(dest, fleet.getLocation(), dest);
2290                                fleet.setMoveDestination(dest.x, dest.y);
2291                        } else {
2292                                Vector2f moveDir = Misc.getUnitVectorAtDegreeAngle(
2293                                                                Misc.getAngleInDegrees(fleet.getLocation(), fleet.getMoveDestination()));
2294                                float dot = Vector2f.dot(windDir, moveDir);
2295                                if (fleet.wasSlowMoving()) dot = -1f;
2296                                if (dot < 0) {
2297                                        float accelBasedMult = fleet.getAcceleration() / baseFleetAccel;
2298                                        accelBasedMult *= accelBasedMult;
2299                                        if (accelBasedMult > 1f) accelBasedMult = 1f;
2300                                        if (accelBasedMult < 0.1f) accelBasedMult = 0.1f;
2301                                        windSpeedReduction = -dot * fleet.getFleetData().getBurnLevel() * accelBasedMult;
2302                                }
2303                        }
2304                        
2305                        //float burnBonus = fleet.getFleetData().getBurnLevel() - fleet.getFleetData().getMinBurnLevelUnmodified();
2306                        float burnBonus = fleet.getFleetData().getBurnLevel() - fleet.getFleetData().getMinBurnLevel();
2307                        if (burnBonus < 0) burnBonus = 0;
2308                        //float maxSpeedWithWind = Misc.getSpeedForBurnLevel(params.burnLevel + burnBonus);
2309                        float maxSpeedWithWind = Misc.getSpeedForBurnLevel((params.burnLevel * intensity) + burnBonus);
2310                        if (windSpeedReduction > 0) {
2311                                maxSpeedWithWind = Misc.getSpeedForBurnLevel(
2312                                                        Math.max(params.burnLevel  * 0.5f * intensity, params.burnLevel * intensity - windSpeedReduction));
2313                        }
2314                        
2315                        if (reversePolarity) {
2316                                float polarityMult = fleet.getMemoryWithoutUpdate().getFloat(ReversePolarityToggle.POLARITY_SPEED_MULT);
2317                                maxSpeedWithWind *= polarityMult;
2318                                //System.out.println("MSWW: " + maxSpeedWithWind + ", mult: " + polarityMult);
2319                                //maxSpeedWithWind *= ReversePolarity.SLIPSTREAM_SPEED_MULT;
2320                        }
2321                        
2322                        float fleetSpeedAlongWind = Vector2f.dot(windDir, fleet.getVelocity());
2323                        if (fleetSpeedAlongWind >= maxSpeedWithWind) {
2324//                              float dotPlayerAndWindVel = Vector2f.dot(windDir, velDir);
2325//                              if (dotPlayerAndWindVel > 0.98f) {
2326                                        return;
2327                                //}
2328                        }
2329                        
2330                        velDir.scale(currFleetBurn);
2331                        
2332                        //float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
2333                        
2334                        
2335                        float windSpeed = Misc.getSpeedForBurnLevel(currWindBurn);
2336                        //float fleetSpeed = fleet.getTravelSpeed();
2337                        Vector2f windVector = new Vector2f(windDir);
2338                        windVector.scale(windSpeed);
2339                        
2340                        Vector2f vel = fleet.getVelocity();
2341//                      Vector2f diff = Vector2f.sub(windVector, vel, new Vector2f());
2342//                      //windDir.scale(seconds * fleet.getAcceleration());
2343//                      float max = diff.length();
2344//                      diff = Misc.normalise(diff);
2345//                      //diff.scale(Math.max(windSpeed * seconds, fleet.getAcceleration() * 1f * seconds));
2346//                      diff.scale(fleet.getAcceleration() * 3f * seconds);
2347//                      //diff.scale(fleet.getTravelSpeed() * 5f * seconds);
2348//                      //diff.scale(accelMult);
2349//                      if (diff.length() > max) {
2350//                              diff.scale(max / diff.length());
2351//                      }
2352                        //System.out.println("Applying diff: " + diff);
2353                        //fleet.setVelocity(vel.x + diff.x, vel.y + diff.y);
2354                        
2355                        
2356//                      Vector2f velDir = Misc.normalise(new Vector2f(fleet.getVelocity()));
2357//                      velDir.scale(currFleetBurn);
2358//                      
2359//                      float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
2360                        //System.out.println("fleetBurnAgainstWind: " + fleetBurnAgainstWind);
2361                        float accelMult = 0.5f + 2f * intensity;
2362                        accelMult += 0.25f * 20f * intensity;
2363//                      if (fleetBurnAgainstWind > maxFleetBurnIntoWind) {
2364//                              //accelMult += 0.75f + 0.25f * (fleetBurnAgainstWind - maxFleetBurnIntoWind);
2365//                              accelMult += 0.25f * (fleetBurnAgainstWind - maxFleetBurnIntoWind);
2366//                      } else {
2367//                      }
2368                        
2369                        //if (fleetTryingToMove) accelMult *= 0.15f;
2370                        //if (accelMult < 2f) accelMult = 2f;
2371                        //wefwefwefew
2372                        
2373                        //Vector2f vel = fleet.getVelocity();
2374                        //windDir.scale(seconds * fleet.getAcceleration() * accelMult);
2375                        //float baseFleetAccel = Math.max(fleet.getTravelSpeed(), fleet.getAcceleration());
2376
2377                        float extraAccelMult = params.accelerationMult;
2378                        windDir.scale(seconds * baseFleetAccel * accelMult * extraAccelMult);
2379                        
2380                        if (extraAccelMult > 1f) {
2381                                float windAccelAmountThisFrame = windDir.length();
2382                                float maxAccelThisFrame = maxSpeedWithWind - fleetSpeedAlongWind;
2383                                
2384                                if (windAccelAmountThisFrame > maxAccelThisFrame) {
2385                                        float accelThisFrameMult = maxAccelThisFrame / Math.max(1f, windAccelAmountThisFrame);
2386                                        if (accelThisFrameMult > 1f) accelThisFrameMult = 1f;
2387                                        windDir.scale(accelThisFrameMult);
2388                                }
2389                        }
2390                        
2391                        fleet.setVelocity(vel.x + windDir.x, vel.y + windDir.y);
2392                        
2393                        fleet.getStats().addTemporaryModMult(0.1f, getModId() + "_1",
2394                                        FUEL_USE_MODIFIER_DESC, FUEL_USE_MULT, 
2395                                        fleet.getStats().getDynamic().getStat(Stats.FUEL_USE_NOT_SHOWN_ON_MAP_MULT));
2396                                        //fleet.getStats().getFuelUseHyperMult());
2397                        
2398                        
2399                        boolean withGlow = true;
2400                        withGlow = !params.forceNoWindVisualEffectOnFleets;
2401                        //withGlow = false;
2402                        if (withGlow) {
2403                                Color glowColor = params.windGlowColor;
2404                                if (fleet.getMemoryWithoutUpdate().contains(ReversePolarityToggle.POLARITY_WIND_GLOW_COLOR_KEY)) {
2405                                        glowColor = (Color) fleet.getMemoryWithoutUpdate().get(ReversePolarityToggle.POLARITY_WIND_GLOW_COLOR_KEY);
2406                                }
2407                                
2408                                int alpha = glowColor.getAlpha();
2409                                if (alpha < 75) {
2410                                        glowColor = Misc.setAlpha(glowColor, 75);
2411                                }
2412                                // visual effects - glow, tail
2413                                
2414                                p1 = getNoWobblePointAt(distAlong, yOff);
2415                                p2 = getNoWobblePointAt(distAlong + 100f, yOff);
2416                                if (reversePolarity) {
2417                                        p1 = getNoWobblePointAt(distAlong, yOff);
2418                                        p2 = getNoWobblePointAt(distAlong - 100f, yOff);
2419                                }
2420                                if (p1 != null && p2 != null) {
2421                                        windDir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
2422                                        
2423//                                      float fleetSpeedAlongWind = Vector2f.dot(windDir, fleet.getVelocity());
2424//                                      //float fleetSpeed = fleet.getVelocity().length();
2425//                                      
2426//                                      windSpeed = Misc.getSpeedForBurnLevel(params.burnLevel);
2427//                                      float matchingWindFraction = fleetSpeedAlongWind/windSpeed;
2428//                                      float effectMag = 1f - matchingWindFraction;
2429//                                      if (effectMag < 0f)  effectMag = 0f;
2430                                        //if (effectMag < 0.25f) effectMag = 0.25f;
2431                                        //effectMag = 0.5f;
2432                                        
2433                                        String modId = "slipstream_" + entity.getId();
2434                                        float durIn = 1f;
2435                                        float durOut = 3f;
2436                                        //durIn = 0.5f;
2437                                        //float sizeNormal = (15f + 30f * effectMag * effectMag) * (intensity);
2438                                        float sizeNormal = 5f + 10f * intensity;
2439                                        for (FleetMemberViewAPI view : fleet.getViews()) {
2440                                                view.getWindEffectDirX().shift(modId, windDir.x * sizeNormal, durIn, durOut, 1f);
2441                                                view.getWindEffectDirY().shift(modId, windDir.y * sizeNormal, durIn, durOut, 1f);
2442                                                view.getWindEffectColor().shift(modId, glowColor, durIn, durOut, 1f);
2443                                        }
2444                                }
2445                        }
2446                }
2447        }
2448        
2449        
2450        public void applyEffectToGhost(SectorEntityToken other, float days) {
2451//              /if (true) return;
2452                //if (!(other.getCustomPlugin() instanceof SensorGhost)) return;
2453                
2454                SensorGhost ghost = SensorGhostManager.getGhostFor(other);
2455                if (ghost == null) return;
2456                
2457                if (other.hasTag(Tags.UNAFFECTED_BY_SLIPSTREAM)) return;
2458                
2459                //SensorGhost ghost = (SensorGhost) other.getCustomPlugin();
2460                if (!containsPoint(other.getLocation(), 0f)) {
2461                        return;
2462                }
2463                
2464                float [] offset = getLengthAndWidthFractionWithinStream(other.getLocation());
2465                if (offset == null) {
2466                        return;
2467                }
2468                        
2469                float distAlong = offset[0];
2470                float yOff = offset[1];
2471                        
2472                float intensity = getIntensity(yOff);
2473                float wMult = getWidthBasedSpeedMult(distAlong);
2474                intensity *= wMult;
2475                intensity *= getFaderBrightness(distAlong);
2476                        
2477                if (intensity <= 0) {
2478                        return;
2479                }
2480                        
2481                float maxFleetBurn = ghost.getMaxBurn();
2482                float currFleetBurn = ghost.getCurrBurn();
2483                        
2484                float maxWindBurn = params.burnLevel * 2f;
2485                        
2486                float currWindBurn = intensity * maxWindBurn;
2487                float maxFleetBurnIntoWind = maxFleetBurn - Math.abs(currWindBurn);
2488                float seconds = days * Global.getSector().getClock().getSecondsPerDay();
2489                        
2490                Vector2f p1 = getPointAt(distAlong, yOff);
2491                Vector2f p2 = getPointAt(distAlong + 1f, yOff);
2492                if (p1 == null || p2 == null) {
2493                        return;
2494                }
2495                        
2496                Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
2497                if (currWindBurn < 0) {
2498                        windDir.negate();
2499                }
2500                Vector2f velDir = Misc.normalise(new Vector2f(other.getVelocity()));
2501                float baseFleetAccel = ghost.getAcceleration();
2502                if (baseFleetAccel < 10f) baseFleetAccel = 10f;
2503                        
2504                velDir.scale(currFleetBurn);
2505                        
2506                float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
2507                        
2508                        
2509                float windSpeed = Misc.getSpeedForBurnLevel(currWindBurn);
2510                Vector2f windVector = new Vector2f(windDir);
2511                windVector.scale(windSpeed);
2512                        
2513                Vector2f vel = other.getVelocity();
2514                Vector2f diff = Vector2f.sub(windVector, vel, new Vector2f());
2515                float max = diff.length();
2516                diff = Misc.normalise(diff);
2517                diff.scale(ghost.getAcceleration() * 3f * seconds);
2518                if (diff.length() > max) {
2519                        diff.scale(max / diff.length());
2520                }
2521                float accelMult = 0.5f + 2f * intensity;
2522                if (fleetBurnAgainstWind > maxFleetBurnIntoWind) {
2523                        accelMult += 0.25f * (fleetBurnAgainstWind - maxFleetBurnIntoWind);
2524                }
2525                windDir.scale(seconds * baseFleetAccel * accelMult);
2526                
2527                ghost.getMovement().getVelocity().set(vel.x + windDir.x, vel.y + windDir.y);
2528        }
2529        
2530        
2531        public void applyEffectToWreck(SectorEntityToken other, float days) {
2532                if (other.hasTag(Tags.UNAFFECTED_BY_SLIPSTREAM)) return;
2533                
2534                if (!containsPoint(other.getLocation(), 0f)) {
2535                        return;
2536                }
2537                
2538                float [] offset = getLengthAndWidthFractionWithinStream(other.getLocation());
2539                if (offset == null) {
2540                        return;
2541                }
2542                        
2543                float distAlong = offset[0];
2544                float yOff = offset[1];
2545                        
2546                float intensity = getIntensity(yOff);
2547                float wMult = getWidthBasedSpeedMult(distAlong);
2548                intensity *= wMult;
2549                intensity *= getFaderBrightness(distAlong);
2550                        
2551                if (intensity <= 0) {
2552                        return;
2553                }
2554                        
2555                float maxWindBurn = params.burnLevel * 0.5f;
2556                float currWindBurn = intensity * maxWindBurn;
2557                        
2558                Vector2f p1 = getPointAt(distAlong, yOff);
2559                Vector2f p2 = getPointAt(distAlong + 1f, yOff);
2560                if (p1 == null || p2 == null) {
2561                        return;
2562                }
2563                        
2564                Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
2565                if (currWindBurn < 0) {
2566                        windDir.negate();
2567                }
2568                
2569                float windSpeed = Misc.getSpeedForBurnLevel(currWindBurn);
2570                Vector2f windVector = new Vector2f(windDir);
2571                windVector.scale(windSpeed);
2572                Vector2f vel = other.getVelocity();
2573                float f = 0.95f;
2574                other.getVelocity().set(vel.x * f + windVector.x * (1 - f), vel.y * f + windVector.y * (1 - f));
2575        }
2576        
2577        
2578        public Vector2f getPointAt(float lengthAlongStream, float offset) {
2579                recomputeIfNeeded();
2580                
2581                SlipstreamSegment curr = getSegmentForDist(lengthAlongStream);
2582                if (curr == null) return null;
2583                int index = curr.index;
2584                
2585                SlipstreamSegment next = null;
2586                SlipstreamSegment next2 = null;
2587
2588                if (index >= segments.size() - 1) return null;
2589                
2590                if (index % 2 == 0) {
2591                        next = segments.get(index + 1);
2592                        if (index >= segments.size() - 2) {
2593                                next2 = new SlipstreamSegment();
2594                                //next2.width = next.width;
2595                                next2.wobbledWidth = next.wobbledWidth;
2596                                
2597                                next2.normal = next.normal;
2598                                //next2.dir = next.dir;
2599                                next2.loc = new Vector2f(next.dir);
2600                                next2.loc.scale(next.lengthToPrev);
2601                                Vector2f.add(next2.loc, next.loc, next2.loc);
2602                                //next2.locB = next2.loc;
2603                                next2.lengthToPrev = next.lengthToPrev;
2604                        } else {
2605                                next2 = segments.get(index + 2);
2606                        }
2607                }
2608                if (index % 2 != 0) {
2609                        if (index >= segments.size() - 1) return null;
2610                        curr = segments.get(index - 1);
2611                        next = segments.get(index);
2612                        next2 = segments.get(index + 1);
2613                }
2614                
2615                float lenForT = lengthAlongStream - curr.totalLength;
2616                //float t = lenForT / (curr.lengthToNext + next.lengthToNext);
2617                float t = lenForT / (curr.lengthToNext + next2.lengthToPrev);
2618//              if (t < 0) {
2619//                      System.out.println("wefwefe");
2620//              }
2621                
2622                Vector2f p0 = new Vector2f(curr.loc);
2623                Vector2f p1 = new Vector2f(next.locB);
2624                Vector2f p2 = new Vector2f(next2.loc);
2625                
2626//              offset *= 0.7f;
2627//              p0.x += curr.normal.x * curr.width * 0.5f * offset;
2628//              p0.y += curr.normal.y * curr.width * 0.5f * offset;
2629//              
2630//              p2.x += next2.normal.x * next2.width * 0.5f * offset;
2631//              p2.y += next2.normal.y * next2.width * 0.5f * offset;
2632//              
2633//              p1.x += next.normal.x * next.width * 0.5f * offset;
2634//              p1.y += next.normal.y * next.width * 0.5f * offset;
2635                
2636                p0.x += curr.normal.x * curr.wobbledWidth * 0.5f * offset;
2637                p0.y += curr.normal.y * curr.wobbledWidth * 0.5f * offset;
2638                
2639                p2.x += next2.normal.x * next2.wobbledWidth * 0.5f * offset;
2640                p2.y += next2.normal.y * next2.wobbledWidth * 0.5f * offset;
2641                
2642                p1.x += next.normal.x * next.wobbledWidth * 0.5f * offset;
2643                p1.y += next.normal.y * next.wobbledWidth * 0.5f * offset;
2644                
2645                //System.out.println("T: " + t);
2646                Vector2f p = Misc.bezier(p0, p1, p2, t);
2647                
2648                return p;
2649        }
2650        
2651        public Vector2f getNoWobblePointAt(float lengthAlongStream, float offset) {
2652                SlipstreamSegment curr = getSegmentForDist(lengthAlongStream);
2653                if (curr == null) return null;
2654                int index = curr.index;
2655                if (index >= segments.size() - 2) return null;
2656                
2657                SlipstreamSegment next = segments.get(index + 1);
2658                SlipstreamSegment next2 = segments.get(index + 2);
2659                
2660                if (index % 2 != 0) {
2661                        curr = segments.get(index - 1);
2662                        next = segments.get(index);
2663                        next2 = segments.get(index + 1);
2664                }
2665                
2666                float lenForT = lengthAlongStream - curr.totalLength;
2667                float t = lenForT / (curr.lengthToNext + next.lengthToNext);
2668//              if (t < 0) {
2669//                      System.out.println("wefwefe");
2670//              }
2671                
2672                Vector2f p0 = new Vector2f(curr.loc);
2673                Vector2f p1 = new Vector2f(next.locB);
2674                Vector2f p2 = new Vector2f(next2.loc);
2675                
2676                float edges = params.edgeWidth * 2f * 0.5f;
2677                p0.x += curr.normal.x * (curr.width - edges) * 0.5f * offset;
2678                p0.y += curr.normal.y * (curr.width - edges) * 0.5f * offset;
2679                
2680                p2.x += next2.normal.x * (next2.width - edges) * 0.5f * offset;
2681                p2.y += next2.normal.y * (next2.width - edges) * 0.5f * offset;
2682                
2683                p1.x += next.normal.x * (next.width - edges) * 0.5f * offset;
2684                p1.y += next.normal.y * (next.width - edges) * 0.5f * offset;
2685                
2686                Vector2f p = Misc.bezier(p0, p1, p2, t);
2687                
2688                return p;
2689        }
2690        
2691        
2692        public Vector2f getNormalAt(float lengthAlongStream) {
2693                SlipstreamSegment curr = getSegmentForDist(lengthAlongStream);
2694                if (curr == null) return null;
2695                int index = curr.index;
2696                if (index >= segments.size() - 2) return null;
2697                
2698                SlipstreamSegment next = segments.get(index + 1);
2699                SlipstreamSegment next2 = segments.get(index + 2);
2700                
2701                if (index % 2 != 0) {
2702                        curr = segments.get(index - 1);
2703                        next = segments.get(index);
2704                        next2 = segments.get(index + 1);
2705                }
2706                
2707                float lenForT = lengthAlongStream - curr.totalLength;
2708                
2709                float f = lenForT / curr.lengthToNext;
2710                Vector2f perp;
2711                if (f < 1f) {
2712                        perp = Misc.interpolateVector(curr.normal, next.normal, f);
2713                } else {
2714                        f = (lenForT - curr.lengthToNext) / next.lengthToNext;
2715                        perp = Misc.interpolateVector(next.normal, next2.normal, f);
2716                }
2717                return perp;
2718        }
2719        
2720        public List<SlipstreamSegment> getSegmentsNear(Vector2f loc, float range) {
2721                //List<SlipstreamSegment> potential = new ArrayList<SlipstreamEntityPlugin2.SlipstreamSegment>();
2722                List<SlipstreamSegment> result = new ArrayList<SlipstreamTerrainPlugin2.SlipstreamSegment>();
2723                int boxIndex = 0;
2724                for (BoundingBox box : bounds) {
2725                        if (box.pointNeedsDetailedCheck(loc, range)) {
2726                                int min = boxIndex * segmentsPerBox;
2727                                for (int i = min; i < min + segmentsPerBox && i < segments.size(); i++) {
2728                                        SlipstreamSegment curr = segments.get(i);
2729                                        float distSq = Misc.getDistanceSq(curr.loc, loc);
2730                                        float r = range + curr.width + Math.max(curr.lengthToPrev, curr.lengthToNext);
2731                                        if (distSq < r * r) {
2732                                                result.add(curr);
2733                                        }
2734                                }
2735                        }
2736                        boxIndex++;
2737                }
2738                return result;
2739        }
2740
2741        @Override
2742        protected boolean shouldCheckFleetsToApplyEffect() {
2743                return false; // handled directly in advance(); also does sensor ghosts etc
2744        }
2745        
2746        public boolean hasAIFlag(Object flag) {
2747                return flag == TerrainAIFlags.BREAK_OTHER_ORBITS ||
2748                                flag == TerrainAIFlags.MOVES_FLEETS;
2749        }
2750        
2751        @Override
2752        public boolean containsEntity(SectorEntityToken other) {
2753                //if (true) return false;
2754                if (other.getContainingLocation() != this.entity.getContainingLocation()) return false;
2755                return other != null && containsPoint(other.getLocation(), 0f) && !isPreventedFromAffecting(other);
2756        }
2757
2758        /* 
2759         * The way this check works - using getLengthAndWidthFractionWithinStream() - means it can't
2760         * work with a radius > 0 - or, rather, the passed in radius value is ignored.
2761         * Update: can sort of fake it by pretending segments have extra width. Will not catch cases where something is
2762         * at the end/start of a stream, though, and might miss cases where the "wider" segments overlap
2763         * (non-Javadoc)
2764         * @see com.fs.starfarer.api.impl.campaign.terrain.BaseTerrain#containsPoint(org.lwjgl.util.vector.Vector2f, float)
2765         */
2766        @Override
2767        public boolean containsPoint(Vector2f point, float radius) {
2768                //if (true) return false;
2769                boolean doDetailedCheck = false;
2770                for (BoundingBox box : bounds) {
2771                        doDetailedCheck |= box.pointNeedsDetailedCheck(point, radius);
2772                }
2773                if (!doDetailedCheck) return false;
2774                
2775                float [] coords = getLengthAndWidthFractionWithinStream(point, 0f, false, radius);
2776                if (coords == null) return false;
2777                
2778                float b = getFaderBrightness(coords[0]);
2779                
2780                return b > 0;
2781        }
2782        
2783        public List<BoundingBox> getBounds() {
2784                return bounds;
2785        }
2786
2787        transient private EnumSet<CampaignEngineLayers> layers = EnumSet.of(CampaignEngineLayers.TERRAIN_7);
2788        public EnumSet<CampaignEngineLayers> getActiveLayers() {
2789                return layers;
2790        }
2791        
2792        public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) {
2793                float opad = 10f;
2794                
2795                tooltip.addTitle(getNameForTooltip());
2796                tooltip.addPara(Global.getSettings().getDescription(getTerrainId(), Type.TERRAIN).getText1(), opad);
2797                
2798                tooltip.addPara("Most slipstreams are temporary, and in recent memory their ebb and flow has been "
2799                                + "unusually synchronized with the standard Domain cycle.", opad);
2800                
2801                tooltip.addPara("Fleets traveling inside a slipstream use %s less fuel for the distance covered.",
2802                                opad, Misc.getHighlightColor(),
2803                                "" + (int)Math.round((1f - FUEL_USE_MULT) * 100f) + "%");
2804                
2805                tooltip.addPara("In addition, traveling at burn levels above %s is even more fuel-efficient. "
2806                                + "For example, a fleet traveling at burn %s will consume half as much fuel "
2807                                + "for the distance it covers.",
2808                                opad,
2809                                Misc.getHighlightColor(), "20", "40", "half");
2810                
2811                tooltip.addPara("These fuel use reductions are not reflected by the fuel range indicator on the map.", opad);
2812                
2813//              tooltip.addPara("Fleets traveling at burn levels above %s become even more fuel-efficient. "
2814//                              + "For example, a fleet traveling at burn %s will consume %s less fuel for the "
2815//                              + "distance it covers.",
2816//                              opad,
2817//                              Misc.getHighlightColor(), "20", "40", "50%");
2818        }
2819        
2820        @Override
2821        public boolean hasTooltip() {
2822                return true;
2823        }
2824
2825        public boolean isTooltipExpandable() {
2826                return false;
2827        }
2828        
2829        public float getTooltipWidth() {
2830                return super.getTooltipWidth();
2831        }
2832        
2833        public String getTerrainName() {
2834                if (params.name != null) return params.name;
2835                return "Slipstream";
2836        }
2837        
2838        public String getNameForTooltip() {
2839                return "Slipstream";
2840        }
2841        
2842        public String getEffectCategory() {
2843                return "slipstream";
2844        }
2845
2846        @Override
2847        public void renderOnRadar(Vector2f radarCenter, float factor, float alphaMult) {
2848                GL11.glPushMatrix();
2849                GL11.glTranslatef(-radarCenter.x * factor, -radarCenter.y * factor, 0);
2850                renderOnMap(factor, alphaMult, true, radarCenter);
2851                GL11.glPopMatrix();
2852        }
2853
2854        public List<SlipstreamSegment> getSegments() {
2855                return segments;
2856        }
2857        
2858        
2859        @Override
2860        public void renderOnMap(float factor, float alphaMult) {
2861                renderOnMap(factor, alphaMult, false, null);
2862        }
2863
2864        public void renderOnMap(float factor, float alphaMult, boolean forRadar, Vector2f radarCenter) {
2865                recomputeIfNeeded();
2866                //if (true) return;
2867                
2868                Set<SlipstreamSegment> nearSet = new LinkedHashSet<SlipstreamSegment>();
2869                if (forRadar) {
2870                        //float radius = Global.getSettings().getFloat("campaignRadarRadius") + 1000f;
2871                        float radius = Global.getSettings().getFloat("campaignRadarRadius");
2872                        nearSet = new LinkedHashSet<SlipstreamSegment>(getSegmentsNear(radarCenter, radius));
2873                        for (SlipstreamSegment curr : nearSet) {
2874                                curr.discovered = true;
2875                        }
2876                        if (nearSet.isEmpty()) return;
2877                }
2878                
2879                List<SlipstreamSegment> list = new ArrayList<SlipstreamSegment>();
2880                int incr = Math.min(segments.size() / 10, 5);
2881                incr = 1;
2882                if (incr < 1) incr = 1;
2883                for (int i = 0; i < segments.size(); i+=incr) {
2884                        SlipstreamSegment curr = segments.get(i);
2885                        if (forRadar && !nearSet.contains(curr)) continue;
2886                        if (!forRadar && !curr.discovered && Global.getSettings().isCampaignSensorsOn() &&
2887                                        (!DebugFlags.SLIPSTREAM_DEBUG || DebugFlags.USE_SLIPSTREAM_VISIBILITY_IN_DEBUG_MODE)) continue;
2888                        //if (!forRadar && !curr.discovered && !Global.getSettings().isDevMode()) continue;
2889                        //if (!forRadar && !curr.discovered) continue;
2890                        list.add(curr);
2891                        if (i + incr >= segments.size() && i + 1 < segments.size()) {
2892                                list.add(segments.get(segments.size() - 1));
2893                        }
2894                }
2895                
2896                List<List<SlipstreamSegment>> subsections = new ArrayList<List<SlipstreamSegment>>();
2897                int prevIndex = -10;
2898                List<SlipstreamSegment> subsection = new ArrayList<SlipstreamSegment>();
2899                for (SlipstreamSegment seg : list) {
2900                        if (prevIndex != seg.index - 1) {
2901                                if (subsection != null && !subsection.isEmpty()) {
2902                                        subsections.add(subsection);
2903                                }
2904                                subsection = new ArrayList<SlipstreamSegment>();
2905                        }
2906                        subsection.add(seg);
2907                        prevIndex = seg.index;
2908                }
2909                if (subsection != null && !subsection.isEmpty()) {
2910                        subsections.add(subsection);
2911                }
2912                
2913                float texOffset = 0f;
2914                FaderUtil fader = Global.getSector().getCampaignUI().getSharedFader();
2915                float b = fader.getBrightness();
2916                b *= 0.5f;
2917                //b *= 2f;
2918                if (fader.getState() == State.IN) {
2919                        texOffset = b;
2920                } else if (fader.getState() == State.OUT) {
2921                        texOffset = 1f - b;
2922                }
2923                //texOffset = mapArrowProgress;
2924                //texOffset = -texOffset;
2925                //texOffset *= 0.5f;
2926                //texOffset = 0f;
2927                
2928                GL11.glPushMatrix();
2929                GL11.glScalef(factor, factor, 1f);
2930                //renderSegments(sprite, null, null, edge, alphaMult, list, texOffset % 1, true);
2931                for (List<SlipstreamSegment> subsection2 : subsections) {
2932                        renderSegmentsForMap(subsection2, factor, alphaMult, forRadar, texOffset % 1);
2933                }
2934                
2935                // debug: rendering encounter points 
2936//              if (true) {
2937//                      GL11.glDisable(GL11.GL_TEXTURE_2D);
2938//                      GL11.glEnable(GL11.GL_BLEND);
2939//                      GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
2940//              
2941//                      GL11.glPointSize(20f);
2942//                      GL11.glEnable(GL11.GL_POINT_SMOOTH);
2943//                      GL11.glBegin(GL11.GL_POINTS);
2944//                      Misc.setColor(Color.yellow);
2945//                      for (Vector2f p : getEncounterPoints()) {
2946//                              GL11.glVertex2f(p.x, p.y);
2947//                      }
2948//                      GL11.glEnd();
2949//              }
2950                
2951                GL11.glPopMatrix();
2952        }
2953        
2954        protected void renderSegmentsForMap(List<SlipstreamSegment> segments, float factor, float alphaMult, boolean forRadar, float phase) {
2955                //if (true) return;
2956                if (segments.isEmpty()) return;
2957                
2958                //System.out.println(factor);
2959                float widthMult = 1f;
2960                float lengthPerArrowMult = 1f;
2961                float minFactor = 0.012f;
2962                if (factor < minFactor) {
2963                        widthMult = minFactor / factor;
2964                        lengthPerArrowMult = 2f;
2965                }
2966                
2967                float lengthPerArrow = 700f;
2968                //lengthPerArrow *= sizeMult;
2969                lengthPerArrow *= lengthPerArrowMult;
2970                float start = segments.get(0).totalLength;
2971                float end = segments.get(segments.size() - 1).totalLength;
2972                
2973                start = (float) (Math.floor(start / lengthPerArrow) * lengthPerArrow);
2974                end = (float) (Math.ceil(end/ lengthPerArrow) * lengthPerArrow);
2975                if (end - start < lengthPerArrow) return;
2976                
2977                //Color color = Misc.setAlpha(params.maxColor, 255);
2978                Color color = params.mapColor;
2979                Color orig = color;
2980                
2981                GL11.glDisable(GL11.GL_TEXTURE_2D);
2982                GL11.glEnable(GL11.GL_BLEND);
2983                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
2984                //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
2985                
2986                GL11.glEnable(GL11.GL_POLYGON_SMOOTH);
2987                GL11.glHint(GL11.GL_POLYGON_SMOOTH_HINT, GL11.GL_NICEST);
2988                
2989                float fadeDist = 500f;
2990                //phase = 0f;
2991                GL11.glBegin(GL11.GL_TRIANGLES);
2992                for (float len = start; len < end; len += lengthPerArrow) {
2993                        Vector2f p0 = getPointAt(len + phase * lengthPerArrow, 0f);
2994                        Vector2f p1 = getPointAt(len + phase * lengthPerArrow + 10f, 0f);
2995                        if (p0 == null || p1 == null) continue;
2996                        
2997                        float w = getWidth(len + phase * lengthPerArrow) * widthMult;
2998                        float triLength = lengthPerArrow * 0.33f;
2999                        triLength = lengthPerArrow * 1f;
3000                        triLength = Math.min(lengthPerArrow, (w + lengthPerArrow) / 2f);
3001                        
3002                        
3003                        float a = getFaderBrightness(len + phase * lengthPerArrow + triLength/2f);
3004                        if (len + phase * lengthPerArrow - start < fadeDist) {
3005                                a *= (len + phase * lengthPerArrow - start) / fadeDist;
3006                        }
3007                        if (len + phase * lengthPerArrow > end - fadeDist) {
3008                                a *= (end - (len + phase * lengthPerArrow)) / fadeDist;
3009                        }
3010                        if (a <= 0f) continue;
3011                        
3012//                      Vector2f t0 = new Vector2f(p0);
3013//                      t0.x += dir.x * triLength/2f;
3014//                      t0.y += dir.y * triLength/2f;
3015                        Vector2f t0 = getPointAt(len + phase * lengthPerArrow + triLength/2f, 0f);
3016                        if (t0 == null) continue;
3017                        
3018                        Vector2f dir = Misc.getUnitVector(p0, t0);
3019                        Vector2f perp = new Vector2f(-dir.y, dir.x);
3020                        
3021                        Vector2f t1 = new Vector2f(p0);
3022                        Vector2f t2 = new Vector2f(p0);
3023                        Vector2f t3 = new Vector2f(p0);
3024                        float backOffset = 0f;
3025                        //offset = triLength * -0.1f;
3026                        backOffset = triLength * 0.1f;
3027                        t3.x -= dir.x * backOffset;
3028                        t3.y -= dir.y * backOffset;
3029                        
3030                        t1.x += perp.x * w/2f;
3031                        t1.y += perp.y * w/2f;
3032                        t1.x -= dir.x * triLength/2f;
3033                        t1.y -= dir.y * triLength/2f;
3034                        
3035                        t2.x -= perp.x * w/2f;
3036                        t2.y -= perp.y * w/2f;
3037                        t2.x -= dir.x * triLength/2f;
3038                        t2.y -= dir.y * triLength/2f;
3039                        
3040//                      float f = (len - start) / (end - start);
3041//                      f = 1f - f;
3042//                      f *= 4f;
3043//                      f += phase * 1f;
3044//                      f = ((float) Math.sin(f * Math.PI * 2f) + 1f) * 0.5f;
3045//                      color = Misc.interpolateColor(orig, Color.white, f * 0.67f);
3046                        
3047                        
3048                        
3049                        Misc.setColor(color, alphaMult * 1f * a);
3050                        GL11.glVertex2f(t0.x, t0.y);
3051                        Misc.setColor(color, alphaMult * 0f * a);
3052                        GL11.glVertex2f(t1.x, t1.y);
3053                        GL11.glVertex2f(t3.x, t3.y);
3054                        
3055                        Misc.setColor(color, alphaMult * 1f * a);
3056                        GL11.glVertex2f(t0.x, t0.y);
3057                        Misc.setColor(color, alphaMult * 0f * a);
3058                        GL11.glVertex2f(t2.x, t2.y);
3059                        GL11.glVertex2f(t3.x, t3.y);
3060                        
3061                        //float a2 = alphaMult * 0.5f;
3062//                      float a2 = (float) (Math.pow(alphaMult, 0.33f) * 0.5f);
3063//                      for (float off = -1f; off <= 1f; off += 1f) {
3064//                              float off2 = off / factor;
3065//                              off2 *= 0.5f;
3066//                              Misc.setColor(color, a2 * 1f * a);
3067//                              GL11.glVertex2f(t0.x + off2, t0.y + off2);
3068//                              Misc.setColor(color, a2 * 0f * a);
3069//                              GL11.glVertex2f(t1.x + off2, t1.y + off2);
3070//                              GL11.glVertex2f(t3.x + off2, t3.y + off2);
3071//                              
3072//                              Misc.setColor(color, a2 * 1f * a);
3073//                              GL11.glVertex2f(t0.x + off2, t0.y + off2);
3074//                              Misc.setColor(color, a2 * 0f * a);
3075//                              GL11.glVertex2f(t2.x + off2, t2.y + off2);
3076//                              GL11.glVertex2f(t3.x + off2, t3.y + off2);
3077//                      }
3078                        
3079                        color = orig;
3080                        
3081                }
3082                GL11.glEnd();
3083                
3084                GL11.glEnable(GL11.GL_TEXTURE_2D);
3085                GL11.glEnable(GL11.GL_BLEND);
3086                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
3087                //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
3088                SpriteAPI line = Global.getSettings().getSprite("graphics/hud/line4x4.png");
3089                line.bindTexture();
3090                
3091                float incr = 100f;
3092                float lineW = 50f;
3093                GL11.glBegin(GL11.GL_QUAD_STRIP);
3094                for (float len = start; len < end; len += incr) {
3095                        Vector2f p0 = getPointAt(len, 0f);
3096                        Vector2f p1 = getPointAt(len + 10f, 0f);
3097                        if (p0 == null || p1 == null) continue;
3098                        
3099                        Vector2f dir = Misc.getUnitVector(p0, p1);
3100                        Vector2f perp = new Vector2f(-dir.y, dir.x);
3101                        float w = lineW;
3102                        
3103                        Vector2f p2 = new Vector2f(p0);
3104                        Vector2f p3 = new Vector2f(p0);
3105                        p2.x += perp.x * w * 0.5f;
3106                        p2.y += perp.y * w * 0.5f;
3107                        p3.x -= perp.x * w * 0.5f;
3108                        p3.y -= perp.y * w * 0.5f;
3109                        
3110                        float a = getFaderBrightness(len);
3111                        if (len - start < fadeDist) {
3112                                a *= (len - start) / fadeDist;
3113                        }
3114                        if (len > end - fadeDist) {
3115                                a *= (end - len) / fadeDist;
3116                        }
3117                        
3118                        Misc.setColor(color, alphaMult * a * 0.5f);
3119                        GL11.glTexCoord2f(0f, 0f);
3120                        GL11.glVertex2f(p2.x, p2.y);
3121                        GL11.glTexCoord2f(0f, 1f);
3122                        GL11.glVertex2f(p3.x, p3.y);
3123                }
3124                GL11.glEnd();
3125                
3126                GL11.glDisable(GL11.GL_POLYGON_SMOOTH);
3127        }
3128        
3129        
3130        protected void doSoundPlayback(float amount) {
3131                //if (true) return;
3132                
3133                CampaignFleetAPI fleet = Global.getSector().getPlayerFleet();
3134                if (fleet != null && entity.isInCurrentLocation()) {
3135                        Vector2f loc = fleet.getLocation();
3136                        
3137                        float outerPlaybackRange = (float) getSpec().getCustom().optDouble("outsideSoundRange", 1000f);
3138                        float [] coords = getLengthAndWidthFractionWithinStream(loc, outerPlaybackRange + 2000f, true, 0f);
3139                        
3140                        float innerVolume = 0f;
3141                        float innerPitch = 1f;
3142                        float outerVolume = 0f;
3143                        float outerPitch = 1f;
3144                        
3145                        SlipstreamSegment segment = null;
3146                        List<SlipstreamSegment> near = getSegmentsNear(loc, outerPlaybackRange + 2000f);
3147                        float pointProximityOuterVolume = 0f;
3148                        for (SlipstreamSegment curr : near) {
3149                                float dist = Misc.getDistance(loc, curr.loc);
3150                                float check = curr.wobbledWidth / 2f + outerPlaybackRange;
3151                                if (dist < check) {
3152                                        float volume = 1f - dist / check;
3153                                        volume *= curr.bMult * curr.fader.getBrightness();
3154                                        if (volume > pointProximityOuterVolume) {
3155                                                pointProximityOuterVolume = volume;
3156                                                segment = curr;
3157                                        }
3158                                }
3159                        }
3160                        
3161                        float fMult = coords == null ? 0f : getFaderBrightness(coords[0]);
3162                        if (fMult <= 0f) {
3163                                outerVolume = pointProximityOuterVolume;
3164                        } else {
3165                                float wMult = getWidthBasedSpeedMult(coords[0]);
3166                                float f = Math.abs(coords[1]);
3167                                
3168                                if (f <= 1f) {
3169                                        float intensity = getIntensity(f);
3170                                        float minPitch = (float) getSpec().getCustom().optDouble("minPitch", 0.5f);
3171                                        float maxPitch = (float) getSpec().getCustom().optDouble("maxPitch", 1.25f);
3172                                        //innerVolume = 0f + 1f * (intensity * wMult);
3173                                        innerVolume = 0f + 1f * (Math.min(1f, intensity * 2f) * wMult);
3174                                        innerPitch = minPitch + (1f - minPitch) * intensity * wMult;
3175                                        if (innerPitch > maxPitch) innerPitch = maxPitch;
3176                                        outerVolume = 1f;
3177                                        if (intensity >= 0.5f) {
3178                                                outerVolume = 0f;
3179                                        }
3180                                } else {
3181                                        float distFromStream = 0f;
3182                                        distFromStream = getWidth(coords[0]) * 0.5f * (f - 1f);
3183                                        if (distFromStream < outerPlaybackRange) {
3184                                                float intensity = 1f - distFromStream / outerPlaybackRange;
3185                                                outerVolume = 0f + 1f * (intensity * wMult);
3186                                        }
3187                                }
3188                                
3189                                innerVolume *= fMult;
3190                                outerVolume *= fMult;
3191                                outerVolume = Math.max(outerVolume, pointProximityOuterVolume);
3192                        }
3193                        outerVolume = Math.min(outerVolume, 1f - innerVolume);
3194                        //outerVolume = Math.min(outerVolume, 1f - Math.max(0f, innerVolume - 0.25f) * (1f / .75f));
3195                        
3196                        if (innerVolume < 0) innerVolume = 0;
3197                        if (innerVolume > 1) innerVolume = 1;
3198                        if (outerVolume > 1) outerVolume = 1;
3199                        if (outerVolume < 0) outerVolume = 0;
3200
3201//                      if (innerVolume > 0) {
3202//                              outerVolume = 0;
3203//                              innerVolume = 1;
3204//                      }
3205//                      if (innerVolume != 0 || outerVolume != 0) {
3206//                              System.out.println("inner: " + innerVolume + ", outer: " + outerVolume);
3207//                      }
3208
3209                        float loopFade = 0.5f;
3210                        //loopFade = 5f;
3211                        String soundId = getSpec().getLoopOne();
3212                        
3213                        float filterMult = innerVolume;
3214                        if (innerVolume > 0f) {
3215                                filterMult = 1f;
3216//                      } else if (outerVolume > 0f) {
3217//                              filterMult = Math.min(1f, outerVolume * 2f);
3218//                              if (filterMult < 0.5f) filterMult *= filterMult;
3219//                      }
3220                        } else if (outerVolume > 0.5f) {
3221                                filterMult = Math.min(1f, (outerVolume - 0.5f) * 4f);
3222                        }
3223                        //System.out.println("Filter: " + filterMult);
3224                        if (innerVolume > 0) {
3225                                float gain = (float) getSpec().getCustom().optDouble("gain", 0.75f);
3226                                float gainHF = (float) getSpec().getCustom().optDouble("gainHF", 0.5f);
3227                                
3228                                Global.getSoundPlayer().applyLowPassFilter(
3229                                                Math.max(0f, 1f - (1f - gain) * innerVolume),
3230                                                Math.max(0f, 1f - Math.min(1f - gainHF, innerVolume)));
3231//                                              Math.max(0f, 1f - 0.25f * innerVolume),
3232//                                              Math.max(0f, 1f - Math.min(0.5f, innerVolume)));
3233                        }
3234                        
3235                        if (soundId != null && innerVolume > 0f) {
3236                                Global.getSoundPlayer().playLoop(soundId, fleet, innerPitch,
3237                                                getLoopOneVolume() * innerVolume, fleet.getLocation(), Misc.ZERO, loopFade, loopFade);
3238                        }
3239                        soundId = getSpec().getLoopTwo();
3240                        if (soundId != null && outerVolume > 0f) {
3241                                Vector2f playbackLoc = fleet.getLocation();
3242                                if (segment != null) playbackLoc = segment.loc;
3243                                Global.getSoundPlayer().playLoop(soundId, fleet, outerPitch,
3244                                                getLoopTwoVolume() * outerVolume, playbackLoc, Misc.ZERO, loopFade, loopFade);
3245                        }
3246                        
3247                        float suppressionMult = innerVolume;
3248                        suppressionMult = filterMult;
3249                        Global.getSector().getCampaignUI().suppressMusic(getSpec().getMusicSuppression() * suppressionMult);
3250                }
3251        }
3252        
3253        
3254        @Override
3255        protected boolean shouldPlayLoopOne() {
3256                return false;
3257        }
3258
3259        @Override
3260        protected boolean shouldPlayLoopTwo() {
3261                return false;
3262        }
3263
3264        public List<Vector2f> getEncounterPoints() {
3265                if (encounterPoints == null) {
3266                        encounterPoints = new ArrayList<Vector2f>();
3267                        recomputeEncounterPoints();
3268                }
3269                return encounterPoints;
3270        }
3271        
3272        public void recomputeEncounterPoints() {
3273                encounterPoints = new ArrayList<Vector2f>();
3274                
3275                List<List<SlipstreamSegment>> sections = new ArrayList<List<SlipstreamSegment>>();
3276                
3277                boolean currSectionIsBreak = false;
3278                List<SlipstreamSegment> list = new ArrayList<SlipstreamSegment>();
3279                for (int i = 0; i < segments.size(); i++) {
3280                        SlipstreamSegment curr = segments.get(i);
3281                        boolean currSegmentIsBreak = curr.bMult <= 0f;
3282                        if (list.isEmpty()) {
3283                                currSectionIsBreak = currSegmentIsBreak;
3284                        }
3285                        if (currSectionIsBreak == currSegmentIsBreak) {
3286                                list.add(curr);
3287                        } else {
3288                                if (!list.isEmpty()) {
3289                                        sections.add(list);
3290                                }
3291                                list = new ArrayList<SlipstreamSegment>();
3292                                i--;
3293                        }
3294                }
3295                
3296                boolean prevSectionWasLongEnough = false;
3297                for (List<SlipstreamSegment> section : sections) {
3298                        boolean sectionIsBreak = section.get(0).bMult <= 0;
3299                        float sectionLength = section.get(section.size() - 1).totalLength - section.get(0).totalLength;
3300                        //if (sectionIsBreak && prevSectionWasLongEnough && section.size() > 5f) {
3301                        if (sectionIsBreak && prevSectionWasLongEnough && sectionLength >= 1000f) {// && sectionLength < 4000f) {
3302                                Vector2f loc = new Vector2f(section.get(0).loc);
3303                                Vector2f dir = new Vector2f(section.get(0).dir);
3304                                dir.scale(Math.min(1000f, sectionLength * 0.4f));
3305                                Vector2f.add(dir, loc, loc);
3306                                encounterPoints.add(loc);
3307                        }
3308                        
3309                        if (!sectionIsBreak && section.size() >= 10f) {
3310                                prevSectionWasLongEnough = true;
3311                        } else {
3312                                prevSectionWasLongEnough = false;
3313                        }
3314                }
3315                if (prevSectionWasLongEnough) {
3316                        List<SlipstreamSegment> section = sections.get(sections.size() - 1);
3317                        Vector2f loc = new Vector2f(section.get(section.size() - 1).loc);
3318                        Vector2f dir = new Vector2f(section.get(section.size() - 1).dir);
3319                        dir.scale(1000f);
3320                        Vector2f.add(dir, loc, loc);
3321                        encounterPoints.add(loc);
3322                }
3323        }
3324        
3325        
3326}
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336