001package com.fs.starfarer.api.impl.campaign.velfield;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.Iterator;
006import java.util.LinkedHashMap;
007import java.util.LinkedHashSet;
008import java.util.List;
009import java.util.Map;
010import java.util.Set;
011
012import org.lwjgl.input.Mouse;
013import org.lwjgl.opengl.GL11;
014import org.lwjgl.opengl.GL14;
015import org.lwjgl.util.vector.Vector2f;
016
017import com.fs.starfarer.api.Global;
018import com.fs.starfarer.api.campaign.CampaignEngineLayers;
019import com.fs.starfarer.api.campaign.CampaignFleetAPI;
020import com.fs.starfarer.api.campaign.SectorEntityToken;
021import com.fs.starfarer.api.combat.ViewportAPI;
022import com.fs.starfarer.api.fleet.FleetMemberViewAPI;
023import com.fs.starfarer.api.graphics.SpriteAPI;
024import com.fs.starfarer.api.impl.campaign.BaseCustomEntityPlugin;
025import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
026import com.fs.starfarer.api.util.FaderUtil;
027import com.fs.starfarer.api.util.Misc;
028import com.fs.starfarer.api.util.MutatingVertexUtil;
029import com.fs.starfarer.api.util.WeightedRandomPicker;
030
031public class SlipstreamEntityPlugin2 extends BaseCustomEntityPlugin {
032        
033        public static class SlipstreamSegment {
034                public Vector2f locB = new Vector2f();
035                public Vector2f loc = new Vector2f();
036                public Vector2f dir = new Vector2f();
037                public float width;
038                
039                transient public float wobbledWidth;
040                transient public int index = 0;
041                transient public Vector2f normal = new Vector2f();
042                transient public float tx = 0f;
043                transient public float txe1 = 0f;
044                transient public float txe2 = 0f;
045                transient public float totalLength;
046                transient public float lengthToPrev;
047                transient public float lengthToNext;
048                
049                public MutatingVertexUtil wobble1;
050                public MutatingVertexUtil wobble2;
051                public FaderUtil fader = new FaderUtil(0f, 1f, 1f);
052        }
053        
054        public static class SlipstreamParticle {
055                float speed;
056                float dist;
057                float yPos;
058                Color color;
059                float remaining;
060                float elapsed;
061        }
062        
063        public static class SlipstreamParams2 {
064                public String spriteKey1 = "slipstream";
065                public String edgeKey = "slipstream_edge3";
066                public Color spriteColor = new Color(0.3f, 0.5f, 1f, 1f);
067                public Color windGlowColor = new Color(0.3f, 0.5f, 1f, 1f);
068                public Color edgeColor = Color.white;
069                public float edgeWidth = 256;
070                //public float width;
071                public float areaPerParticle = 2875;
072                public int maxParticles = 2000;
073                //public int numParticles;
074                public float minSpeed;
075                public float maxSpeed;
076                public int burnLevel = 30;
077                public Color minColor;
078                public Color maxColor;
079                public float particleFadeInTime = 1f;
080                public float minDur = 0f;
081                public float maxDur = 4f;
082                public float lineLengthFractionOfSpeed = 0.5f;
083                public boolean slowDownInWiderSections = false;
084                public float widthForMaxSpeed = 1f;
085                public float widthForMaxSpeedMinMult = 0.5f;
086                public float widthForMaxSpeedMaxMult = 1.5f;
087        }
088        
089        public static int MAX_PARTICLES_ADD_PER_FRAME = 250;
090        
091        public static float RAD_PER_DEG = 0.01745329251f;
092        public static Vector2f rotateAroundOrigin(Vector2f v, float cos, float sin) {
093                Vector2f r = new Vector2f();
094                r.x = v.x * cos - v.y * sin;
095                r.y = v.x * sin + v.y * cos;
096                return r;
097        }
098        
099
100        
101        protected SlipstreamParams2 params = new SlipstreamParams2();
102        
103        protected List<SlipstreamSegment> segments = new ArrayList<SlipstreamEntityPlugin2.SlipstreamSegment>();
104        protected float totalLength = 0f;
105        
106        protected transient List<SlipstreamParticle> particles = new ArrayList<SlipstreamParticle>();
107        protected transient int [] lengthToIndexMap;
108        protected transient int lengthDivisor;
109        
110        protected boolean needsRecompute = true;
111        protected List<BoundingBox> bounds = new ArrayList<BoundingBox>();
112        protected int segmentsPerBox;
113        
114        public SlipstreamEntityPlugin2() {
115        }
116        
117        public void setNeedsRecompute() {
118                this.needsRecompute = true;
119        }
120
121        public void updateLengthToIndexMap() {
122                float minSegmentLength = Float.MAX_VALUE;
123                for (SlipstreamSegment curr : segments) {
124                        if (curr.lengthToNext > 0 && minSegmentLength > curr.lengthToNext) {
125                                minSegmentLength = curr.lengthToNext;
126                        }
127                }
128                if (minSegmentLength < 50f) minSegmentLength = 50f;
129                
130                lengthDivisor = (int) (minSegmentLength - 1f); 
131                int numIndices = (int) (totalLength / lengthDivisor);
132                lengthToIndexMap = new int [numIndices];
133                
134                int lengthSoFar = 0;
135                for (int i = 0; i < segments.size(); i++) {
136                        SlipstreamSegment curr = segments.get(i);
137                        while (lengthSoFar < curr.totalLength + curr.lengthToNext) {
138                                int lengthIndex = lengthSoFar / lengthDivisor;
139                                if (lengthIndex < lengthToIndexMap.length) {
140                                        lengthToIndexMap[lengthIndex] = i;
141                                }
142                                lengthSoFar += lengthDivisor;
143                        }
144                }
145        }
146        
147        public SlipstreamSegment getSegmentForDist(float distAlongStream) {
148                if (lengthToIndexMap == null) return null;
149                int mapIndex = (int) (distAlongStream / lengthDivisor);
150                if (mapIndex < 0 || mapIndex >= lengthToIndexMap.length) return null;
151                //System.out.println("Index: " + mapIndex + ", dist: " + distAlongStream);
152                int segIndex = lengthToIndexMap[mapIndex];
153                SlipstreamSegment segment = segments.get(segIndex);
154                while (distAlongStream < segment.totalLength) {
155                        segIndex--;
156                        if (segIndex < 0) return null;
157                        segment = segments.get(segIndex);
158                }
159                while (distAlongStream > segment.totalLength + segment.lengthToNext) {
160                        segIndex++;
161                        if (segIndex >= segments.size()) return null;
162                        segment = segments.get(segIndex);
163                }
164                return segment;
165        }
166        
167        public void addSegment(Vector2f loc, float width) {
168                SlipstreamSegment s = new SlipstreamSegment();
169                s.loc.set(loc);
170                s.width = width;
171                
172                float minRadius = 0f;
173                float maxRadius = s.width * 0.05f;
174                float rate = maxRadius * 0.5f;
175                float angleRate = 50f;
176                s.wobble1 = new MutatingVertexUtil(minRadius, maxRadius, rate, angleRate); 
177                s.wobble2 = new MutatingVertexUtil(minRadius, maxRadius, rate, angleRate); 
178                
179                s.fader.fadeIn();
180                
181                segments.add(s);
182                setNeedsRecompute();
183        }
184        
185        public void init(SectorEntityToken entity, Object pluginParams) {
186                super.init(entity, pluginParams);
187                this.params = (SlipstreamParams2) pluginParams;
188                fader.fadeIn();
189                readResolve();
190        }
191        
192        public float getRenderRange() {
193                return totalLength * 0.6f + 1000f;
194                //return totalLength + 1000f;
195        }
196
197        Object readResolve() {
198                if (particles == null) {
199                        particles = new ArrayList<SlipstreamParticle>();
200                }
201                return this;
202        }
203        
204        public void advance(float amount) {
205                if (!entity.isInCurrentLocation()) return;
206                
207                applyEffectToFleets(amount);
208                
209                fader.advance(amount);
210
211                
212//              entity.getLocation().x += Misc.getSpeedForBurnLevel(params.burnLevel) * amount;
213//              entity.setFacing(0f);
214//              entity.getLocation().set(Global.getSector().getPlayerFleet().getLocation().x + 500, 
215//                              Global.getSector().getPlayerFleet().getLocation().y + 1000f);
216//              entity.getLocation().set(Global.getSector().getPlayerFleet().getLocation());
217                
218                params.minColor = new Color(0.5f, 0.3f, 0.75f, 0.85f);
219                params.maxColor = new Color(0.5f, 0.6f, 1f, 1f);
220                params.spriteColor = new Color(0.3f, 0.5f, 1f, 1f);
221                params.minDur = 1f;
222                params.maxDur = 4f;
223                params.minSpeed = 700f;
224                params.maxSpeed = 1500f;
225                //params.lineLengthFractionOfSpeed = 0.5f;
226                //params.lineLengthFractionOfSpeed = 1f;
227                //params.lineLengthFractionOfSpeed = 0.15f;
228                params.burnLevel = 30;
229                //params.burnLevel = 1000;
230                //params.particleFadeInTime = 0.01f;
231                params.minSpeed = Misc.getSpeedForBurnLevel(params.burnLevel - 5);
232                params.maxSpeed = Misc.getSpeedForBurnLevel(params.burnLevel + 5);
233                params.lineLengthFractionOfSpeed = 0.25f * Math.max(0.25f, Math.min(1f, 30f / (float) params.burnLevel));
234                //params.burnLevel = 200;
235                //params.numParticles = 2000;
236                //params.numParticles = 1;
237                params.minColor = new Color(0.5f, 0.3f, 0.75f, 0.1f);
238                params.maxColor = new Color(0.5f, 0.6f, 1f, 0.5f);
239                
240                
241                float width = 512;
242                params.widthForMaxSpeed = width;
243                params.slowDownInWiderSections = true;
244                params.widthForMaxSpeedMinMult = 0.5f;
245                
246                params.edgeWidth = 256f;
247                params.spriteColor = new Color(0.3f, 0.5f, 1f, 0.5f);
248                params.edgeColor = new Color(0.3f, 0.5f, 1f, 0.75f);
249                params.edgeColor = Color.white;
250                
251                //params.edgeWidth = 128f;
252                //params.minDur = 2f;
253                //params.maxDur = 2f;
254                params.minDur = 2f;
255                params.maxDur = 6f;
256                
257                params.areaPerParticle = 10000;
258                //params.areaPerParticle = 10f;
259                //params.maxParticles = 100000;
260                //MAX_PARTICLES_ADD_PER_FRAME = 2000;
261                
262                
263//              params.minSpeed = Misc.getSpeedForBurnLevel(16f);
264//              params.maxSpeed = Misc.getSpeedForBurnLevel(16f);
265//              params.numParticles = 0;
266                
267                //segments.clear();
268//              if (segments.isEmpty()) {
269//                      float currX = entity.getLocation().x + 200f;
270//                      addSegment(new Vector2f(currX, entity.getLocation().y), 1600f);
271//                      currX += 200f;
272//                      addSegment(new Vector2f(currX, entity.getLocation().y), 1200f);
273//                      currX += 300f;
274//                      addSegment(new Vector2f(currX, entity.getLocation().y), 800f);
275//                      currX += 400f;
276//                      addSegment(new Vector2f(currX, entity.getLocation().y), 700f);
277//                      currX += 500f;
278//                      addSegment(new Vector2f(currX, entity.getLocation().y), 600f);
279//                      currX += 600f;
280//                      addSegment(new Vector2f(currX, entity.getLocation().y), 512f);
281//              }
282                if (segments.isEmpty()) {
283                        float spacing = 200f;
284//                      for (int i = 0; i < 100; i++) {
285//                              addSegment(new Vector2f(entity.getLocation().x + i * spacing, entity.getLocation().y), params.width);
286//                      }
287//                      float x = segments.get(segments.size() - 1).loc.x;
288//                      float y = segments.get(segments.size() - 1).loc.y;
289//                      addSegment(new Vector2f(x + 200f, y - 100f), params.width);
290//                      addSegment(new Vector2f(x + 400f, y - 200f), params.width);
291//                      addSegment(new Vector2f(x + 600f, y - 300f), params.width);
292//                      addSegment(new Vector2f(x + 800f, y - 400f), params.width);
293//                      addSegment(new Vector2f(x + 1000f, y - 500f), params.width);
294//                      addSegment(new Vector2f(x + 1000f, y - 600f), params.width);
295//                      addSegment(new Vector2f(x + 800f, y - 700f), params.width);
296//                      addSegment(new Vector2f(x + 600f, y - 800f), params.width);
297//                      addSegment(new Vector2f(x + 400f, y - 900f), params.width);
298//                      addSegment(new Vector2f(x + 200f, y - 1000f), params.width);
299//                      for (int i = 100; i >= 0; i--) {
300//                              addSegment(new Vector2f(entity.getLocation().x + i * spacing, entity.getLocation().y - 1100), params.width);
301//                      }
302                        
303                        int iter = 1000;
304                        for (int i = 0; i < iter; i++) {
305                                float yOff = (float) Math.sin(i * 0.05f);
306                                addSegment(new Vector2f(entity.getLocation().x + i * spacing,
307                                //addSegment(new Vector2f(entity.getLocation().x + i * (spacing + (50 - i) * 5),
308                                                entity.getLocation().y + yOff * 2000f), 
309                                                //width);
310                                                //width * (0.7f + (float) Math.random() * 0.7f));
311                                                width + i * 10f);
312                        }
313//                      float spacing = 1000f;
314//                      for (int i = 0; i < 500; i++) {
315//                              float yOff = 0f;
316//                              addSegment(new Vector2f(entity.getLocation().x + i * spacing,
317//                              //addSegment(new Vector2f(entity.getLocation().x + i * (spacing + (50 - i) * 5),
318//                                              entity.getLocation().y + yOff * 2000f), 
319//                                              //width);
320//                                              //width * (0.7f + (float) Math.random() * 0.7f));
321//                                              width + 500f + i * 2f);
322//                      }
323                }
324                
325                recomputeIfNeeded();
326                advanceNearbySegments(amount);
327        
328                addParticles();
329                advanceParticles(amount);
330        }
331        
332        
333        public void recomputeIfNeeded() {
334                if (!needsRecompute) return;
335                recompute();
336        }
337        
338        public void recompute() {
339                needsRecompute = false;
340                
341                // compute average location, set segment indices
342                Vector2f avgLoc = new Vector2f();
343                for (int i = 0; i < segments.size(); i++) {
344                        SlipstreamSegment curr = segments.get(i);
345                        curr.index = i;
346                        Vector2f.add(avgLoc, curr.loc, avgLoc);
347                }
348
349                if (segments.size() > 0) {
350                        avgLoc.scale(1f / segments.size());
351                        entity.setLocation(avgLoc.x, avgLoc.y);
352                }
353
354                
355                SpriteAPI sprite = Global.getSettings().getSprite("misc", params.spriteKey1);
356                SpriteAPI edge = Global.getSettings().getSprite("misc", params.edgeKey);
357                
358                // compute texture coordinates etc
359                float tx = 0f;
360                float txe1 = 0f;
361                float txe2 = 0f;
362                float totalLength = 0f;
363                for (int i = 0; i < segments.size(); i++) {
364                        SlipstreamSegment prev = null;
365                        if (i > 0) prev = segments.get(i - 1);
366                        SlipstreamSegment curr = segments.get(i);
367                        SlipstreamSegment next = null;
368                        SlipstreamSegment next2 = null;
369                        SlipstreamSegment next3 = null;
370                        if (i < segments.size() - 1) {
371                                next = segments.get(i + 1);
372                        }
373                        if (i < segments.size() - 2) {
374                                next2 = segments.get(i + 2);
375                        }
376                        if (i < segments.size() - 3) {
377                                next3 = segments.get(i + 3);
378                        }
379
380                        if (next == null) {
381                                if (prev != null) {
382                                        curr.dir.set(prev.dir);
383                                }
384                        } else {
385                                Vector2f dir = Vector2f.sub(next.loc, curr.loc, new Vector2f());
386                                dir = Misc.normalise(dir);
387                                curr.dir = dir;
388                        }
389
390                        Vector2f dir = curr.dir;
391                        Vector2f normal = new Vector2f(-dir.y, dir.x);
392                        curr.normal.set(normal);
393
394                        float length = 0f;
395                        float texLength = 0f;
396                        float e1TexLength = 0f;
397                        float e2TexLength = 0f;
398                        if (prev != null) {
399                                Vector2f dir2 = Vector2f.sub(curr.loc, prev.loc, new Vector2f());
400                                length = dir2.length();
401                                texLength = length / sprite.getWidth();
402                                texLength = Math.min(texLength, sprite.getHeight() / curr.width);
403
404                                Vector2f edgeCurr = new Vector2f(curr.loc);
405                                edgeCurr.x += curr.normal.x * curr.width * 0.5f;
406                                edgeCurr.y += curr.normal.y * curr.width * 0.5f;
407
408                                Vector2f edgePrev = new Vector2f(prev.loc);
409                                edgePrev.x += prev.normal.x * prev.width * 0.5f;
410                                edgePrev.y += prev.normal.y * prev.width * 0.5f;
411
412                                float length2 = Vector2f.sub(edgeCurr, edgePrev, new Vector2f()).length();
413                                e1TexLength = length2 / edge.getWidth() * edge.getHeight() / params.edgeWidth;
414
415
416                                edgeCurr = new Vector2f(curr.loc);
417                                edgeCurr.x -= curr.normal.x * curr.width * 0.5f;
418                                edgeCurr.y -= curr.normal.y * curr.width * 0.5f;
419
420                                edgePrev = new Vector2f(prev.loc);
421                                edgePrev.x -= prev.normal.x * prev.width * 0.5f;
422                                edgePrev.y -= prev.normal.y * prev.width * 0.5f;
423
424                                length2 = Vector2f.sub(edgeCurr, edgePrev, new Vector2f()).length();
425                                e2TexLength = length2 / edge.getWidth() * edge.getHeight() / params.edgeWidth;
426                        }
427
428                        tx += texLength;
429                        txe1 += e1TexLength;
430                        txe2 += e2TexLength;
431                        curr.tx = tx;
432                        curr.txe1 = txe1;
433                        curr.txe2 = txe2;
434                        curr.lengthToPrev = length;
435
436                        totalLength += length;
437                        curr.totalLength = totalLength;
438                        //curr.lengthToNext = Misc.getDistance(curr.loc, next.loc);
439                        if (prev != null) {
440                                prev.lengthToNext = length;
441                        }
442
443                        if (next != null && next2 != null && next3 != null) {
444                                Vector2f p0 = curr.loc;
445                                Vector2f p1 = next.loc;
446                                Vector2f p2 = next2.loc;
447                                Vector2f p3 = next3.loc;
448
449                                float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
450                                float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
451                                float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
452                                float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
453                                adjustment = diff * 0.5f;
454                                //adjustment = diff * 0.25f;
455                                float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
456                                //angle = Misc.getAngleInDegrees(p3, p2);
457                                float dist = Misc.getDistance(p2, p1);
458                                Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
459                                p1Adjusted.scale(dist);
460                                Vector2f.add(p1Adjusted, p2, p1Adjusted);
461                                next.locB = p1Adjusted;
462                        } else if (next != null) {
463                                next.locB = next.loc;
464                        }
465                        if (prev == null) {
466                                curr.locB = new Vector2f(curr.loc);
467                        }
468                }
469                this.totalLength = totalLength;
470                
471                updateLengthToIndexMap();
472                updateBoundingBoxes();
473        }
474        
475        protected void updateBoundingBoxes() {
476                segmentsPerBox = (int) Math.sqrt(segments.size()) + 1;
477                if (segmentsPerBox < 20) segmentsPerBox = 20;
478                
479                bounds.clear();
480                for (int i = 0; i < segments.size(); i+= segmentsPerBox) {
481                        List<SlipstreamSegment> section = new ArrayList<SlipstreamSegment>();
482                        for (int j = i; j < i + segmentsPerBox && j < segments.size(); j++) {
483                                section.add(segments.get(j));
484                        }
485                        if (i + segmentsPerBox < segments.size()) {
486                                section.add(segments.get(i + segmentsPerBox));
487                        }
488                        //BoundingBox box = BoundingBox.create(section);
489                        //bounds.add(box);
490                }
491        }
492        
493        protected void advanceNearbySegments(float amount) {
494                CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
495                if (pf == null || !entity.isInCurrentLocation()) {
496                        return;
497                }
498                
499                if (segments.size() > 0) {
500                        segments.get(0).fader.forceOut();
501                        segments.get(segments.size() - 1).fader.fadeOut();
502                }
503                
504                ViewportAPI viewport = Global.getSector().getViewport();
505                float viewRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
506                viewRadius = Math.max(6000f, viewRadius);
507                viewRadius += 1000f;
508                List<SlipstreamSegment> near = getSegmentsNear(viewport.getCenter(), viewRadius);
509                
510                // advance fader and wobble, compute wobbledWidth
511                for (int i = 0; i < near.size(); i++) {
512                        SlipstreamSegment curr = near.get(i);
513                        
514                        curr.fader.advance(amount);
515                        
516                        float r1 = 0.5f + (float) Math.random() * 1f;
517                        float r2 = 0.5f + (float) Math.random() * 1f;
518                        curr.wobble1.advance(amount * r1);
519                        curr.wobble2.advance(amount * r2);
520//                      curr.wobble1.vector.set(0, 0);
521//                      curr.wobble2.vector.set(0, 0);
522                        
523                        Vector2f p1 = new Vector2f(curr.loc);
524                        Vector2f p2 = new Vector2f(curr.loc);
525                        p1.x += curr.normal.x * curr.width * 0.5f;
526                        p1.y += curr.normal.y * curr.width * 0.5f;
527                        p2.x -= curr.normal.x * curr.width * 0.5f;
528                        p2.y -= curr.normal.y * curr.width * 0.5f;
529                        
530                        p1.x += curr.wobble1.vector.x;
531                        p1.y += curr.wobble1.vector.y;
532                        p2.x += curr.wobble2.vector.x;
533                        p2.y += curr.wobble2.vector.y;
534                        
535                        //curr.wobbledWidth = Misc.getDistance(p1, p2);
536                        float d = Misc.getDistance(p1, p2);
537                        curr.wobbledWidth = d - params.edgeWidth * 2f * 0.5f;
538                        if (curr.wobbledWidth < d * 0.5f) curr.wobbledWidth = d * 0.5f;
539                        //curr.wobbledWidth = curr.width;
540                        
541                        if (curr.index > 0) {
542                                SlipstreamSegment prev = segments.get(curr.index - 1);
543                                Vector2f prev1 = new Vector2f(prev.loc);
544                                Vector2f prev2 = new Vector2f(prev.loc);
545                                prev1.x += curr.normal.x * curr.width * 0.5f;
546                                prev1.y += curr.normal.y * curr.width * 0.5f;
547                                prev2.x -= curr.normal.x * curr.width * 0.5f;
548                                prev2.y -= curr.normal.y * curr.width * 0.5f;
549                                
550                                float maxWobbleRadius = Math.min(prev.width, curr.width) * 0.05f;
551                                float maxWobble1 = Misc.getDistance(p1, prev1) * 0.33f;
552                                float maxWobble2 = Misc.getDistance(p2, prev2) * 0.33f;
553                                maxWobble1 = Math.min(maxWobbleRadius, maxWobble1);
554                                maxWobble2 = Math.min(maxWobbleRadius, maxWobble2);
555                                prev.wobble1.radius.setMax(maxWobble1);
556                                prev.wobble2.radius.setMax(maxWobble2);
557                                curr.wobble1.radius.setMax(maxWobble1);
558                                curr.wobble2.radius.setMax(maxWobble2);
559                        }
560                }
561        }
562        
563        
564        
565        public void addParticles() {
566                if (Global.getSector().getPlayerFleet() == null) {
567                        particles.clear();
568                        return;
569                }
570                
571                boolean useNewSpawnMethod = true;
572                //useNewSpawnMethod = false;
573                
574                if (useNewSpawnMethod) {
575                        boolean inCurrentLocation = entity.isInCurrentLocation();
576                        boolean inHyperspace = entity.isInHyperspace();
577                        boolean spawnForAllSegments = false;
578                        ViewportAPI viewport = Global.getSector().getViewport();
579                        Vector2f locFrom = viewport.getCenter();
580                        float viewRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
581                        viewRadius += 2000f;
582                        viewRadius = Math.max(viewRadius, 10000f);
583                        if (!inCurrentLocation) {
584                                if (inHyperspace) {
585                                        viewRadius = 5000f;
586                                        locFrom = Global.getSector().getPlayerFleet().getLocationInHyperspace();
587                                } else {
588                                        float dist = Misc.getDistanceToPlayerLY(entity);
589                                        spawnForAllSegments = dist < 2f;
590                                }
591                        }
592                        Set<SlipstreamSegment> veryNearSet = new LinkedHashSet<SlipstreamEntityPlugin2.SlipstreamSegment>();
593                        if (inCurrentLocation) {
594                                float veryNearRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
595                                viewRadius += 500f;
596                                veryNearSet = new LinkedHashSet<SlipstreamEntityPlugin2.SlipstreamSegment>(
597                                                        getSegmentsNear(viewport.getCenter(), veryNearRadius));
598                        }
599                        
600        //              viewRadius *= 0.5f;
601        //              viewRadius = 500f;
602                        
603                        List<SlipstreamSegment> near;
604                        if (spawnForAllSegments) {
605                                 near = new ArrayList<SlipstreamSegment>(segments);
606                        } else {
607                                near = getSegmentsNear(locFrom, viewRadius);
608                        }
609                        Set<SlipstreamSegment> nearSet = new LinkedHashSet<SlipstreamSegment>(near);
610                        
611                        Map<SlipstreamSegment, List<SlipstreamParticle>> particleMap = new LinkedHashMap<SlipstreamEntityPlugin2.SlipstreamSegment, List<SlipstreamParticle>>();
612                        //for (SlipstreamParticle p : particles) {
613                        Iterator<SlipstreamParticle> iter = particles.iterator();
614                        while (iter.hasNext()) {
615                                SlipstreamParticle p = iter.next();
616                                SlipstreamSegment seg = getSegmentForDist(p.dist);
617                                if (seg != null) {
618                                        if (!nearSet.contains(seg)) {
619                                                iter.remove();
620                                                continue;
621                                        }
622                                        
623                                        List<SlipstreamParticle> list = particleMap.get(seg);
624                                        if (list == null) {
625                                                list = new ArrayList<SlipstreamEntityPlugin2.SlipstreamParticle>();
626                                                particleMap.put(seg, list);
627                                        }
628                                        list.add(p);
629                                }
630                        }
631                        
632                        
633                        float totalArea = 0f;
634                        int nearParticles = 0;
635                        WeightedRandomPicker<SlipstreamSegment> segmentPicker = new WeightedRandomPicker<SlipstreamEntityPlugin2.SlipstreamSegment>();
636                        
637                        // figure out how many particles to add total, and also which segments to add them
638                        // to to achieve a relatively even distribution
639                        for (int i = 0; i < near.size(); i++) {
640                                SlipstreamSegment curr = near.get(i);
641                                if (curr.lengthToNext <= 0) continue; // last segment, can't have particles in it since the stream is over
642        
643                                float area = curr.lengthToNext * curr.width;
644                                float desiredParticles =  area / params.areaPerParticle;
645                                if (desiredParticles < 1) desiredParticles = 1;
646                                
647                                float particlesInSegment = 0; 
648                                List<SlipstreamParticle> list = particleMap.get(curr);
649                                if (list != null) {
650                                        particlesInSegment = list.size();
651                                }
652                                
653                                float mult = 1f;
654                                // spawn more particles in visible/nearly visible areas
655                                // better to have less visible particles when the player zooms out while paused
656                                // than to have less visible particles when zoomed in
657                                if (veryNearSet.contains(curr)) mult = 10f;
658                                
659                                float w = desiredParticles - particlesInSegment;
660                                w *= mult;
661                                if (w < 5f) w = 5f;
662                                segmentPicker.add(curr, w);
663                                //segmentPicker.add(curr, 1f);
664                                
665                                totalArea += area;
666                                nearParticles += particlesInSegment;
667                        }
668                        
669                        
670                        int numParticlesBasedOnArea = (int) (totalArea / params.areaPerParticle);
671                        int actualDesired = numParticlesBasedOnArea;
672                        if (numParticlesBasedOnArea < 10) numParticlesBasedOnArea = 10;
673                        if (numParticlesBasedOnArea > params.maxParticles) numParticlesBasedOnArea = params.maxParticles;
674                        //System.out.println("Area: " + totalArea/params.numParticles);
675                        //numParticlesBasedOnArea = 20000;
676                        
677                        
678                        int particlesToAdd = numParticlesBasedOnArea - nearParticles;
679                        if (particlesToAdd > MAX_PARTICLES_ADD_PER_FRAME) {
680                                particlesToAdd = MAX_PARTICLES_ADD_PER_FRAME;
681                        }
682                        particlesToAdd = Math.min(particlesToAdd, params.maxParticles - particles.size());
683                        
684                        int added = 0;
685                        while (added < particlesToAdd) {
686                                added++;
687                                SlipstreamSegment seg = segmentPicker.pick();
688                                if (seg == null) continue;
689                                
690                                SlipstreamParticle p = new SlipstreamParticle();
691                                float fLength = (float) Math.random() * 1f;
692                                float fWidth = (float) Math.random() * 2f - 1f;
693                                
694                                float speed = params.minSpeed + (params.maxSpeed - params.minSpeed) * (float) Math.random();
695                                float dur = params.minDur + (params.maxDur - params.minDur) * (float) Math.random(); 
696                                
697                                p.yPos = fWidth;
698                                //p.dist = totalLength * fLength;
699                                p.dist = seg.totalLength + seg.lengthToNext * fLength;
700                                p.speed = speed;
701                                
702                                float intensity = getIntensity(p.yPos);
703                                float wMult = getWidthBasedSpeedMult(p.dist);
704        //                      if (wMult <= 0) {
705        //                              getWidthBasedSpeedMult(p.dist);
706        //                      }
707                                float speedMult = (0.65f + 0.35f * intensity) * wMult;
708                                p.speed *= speedMult;
709                                
710                                p.remaining = dur;
711                                p.color = getRandomColor();
712                                
713                                particles.add(p);
714                        }
715                        
716                        //System.out.println("Particles: " + particles.size() + " desired based on area: " + actualDesired);
717                        
718                } else {
719                        float totalArea = 0f;
720                        for (int i = 0; i < segments.size(); i++) {
721                                SlipstreamSegment curr = segments.get(i);
722                                totalArea += curr.lengthToPrev * curr.width;
723                        }
724                        
725                        int numParticlesBasedOnArea = (int) (totalArea / params.areaPerParticle);
726                        if (numParticlesBasedOnArea < 10) numParticlesBasedOnArea = 10;
727                        if (numParticlesBasedOnArea > params.maxParticles) numParticlesBasedOnArea = params.maxParticles;
728                        //System.out.println("Area: " + totalArea/params.numParticles);
729                        //numParticlesBasedOnArea = 20000;
730                
731                
732                        int added = 0;
733                        //while (particles.size() < params.numParticles && added < MAX_PARTICLES_ADD_PER_FRAME) {
734                        while (particles.size() < numParticlesBasedOnArea && added < MAX_PARTICLES_ADD_PER_FRAME) {
735                                added++;
736                                
737                                SlipstreamParticle p = new SlipstreamParticle();
738                                float fLength = (float) Math.random() * 1f;
739                                float fWidth = (float) Math.random() * 2f - 1f;
740                                
741                                float speed = params.minSpeed + (params.maxSpeed - params.minSpeed) * (float) Math.random();
742                                float dur = params.minDur + (params.maxDur - params.minDur) * (float) Math.random(); 
743                                
744                                p.yPos = fWidth;
745                                p.dist = totalLength * fLength;
746                                p.speed = speed;
747                                
748                                float intensity = getIntensity(p.yPos);
749                                float wMult = getWidthBasedSpeedMult(p.dist);
750        //                      if (wMult <= 0) {
751        //                              getWidthBasedSpeedMult(p.dist);
752        //                      }
753                                float speedMult = (0.65f + 0.35f * intensity) * wMult;
754                                p.speed *= speedMult;
755                                
756                                p.remaining = dur;
757                                p.color = getRandomColor();
758                                
759                                particles.add(p);
760                        }
761                }
762        }
763        
764        public void advanceParticles(float amount) {
765                Iterator<SlipstreamParticle> iter = particles.iterator();
766                while (iter.hasNext()) {
767                        SlipstreamParticle p = iter.next();
768                        p.remaining -= amount;
769                        p.elapsed += amount;
770                        if (p.remaining <= 0) {
771                                iter.remove();
772                                continue;
773                        }
774
775                        p.dist += p.speed * amount;
776                }
777        }
778        
779        public float getWidthBasedSpeedMult(float distAlong) {
780                float mult = 1f;
781                if (params.slowDownInWiderSections) {
782                        SlipstreamSegment curr = getSegmentForDist(distAlong);
783                        if (curr != null) {
784                                float width = curr.width;
785                                if (segments.size() > curr.index + 1) {
786                                        SlipstreamSegment next = segments.get(curr.index + 1);
787                                        float f = (distAlong - curr.totalLength) / curr.lengthToNext;
788                                        if (f < 0) f = 0;
789                                        if (f > 1) f = 1;
790                                        width = Misc.interpolate(width, next.width, f);
791                                        mult = Math.min(params.widthForMaxSpeedMaxMult,
792                                                        params.widthForMaxSpeedMinMult + (1f - params.widthForMaxSpeedMinMult) * params.widthForMaxSpeed / width);
793                                }
794                        }
795                }
796                return mult;
797        }
798        public float getIntensity(float yOff) {
799                yOff = Math.abs(yOff);
800                float intensity = 1f;
801                if (yOff > 0.5f) {
802                        intensity = 1f - 1f * (yOff - 0.5f) / 0.5f;
803                }
804                return intensity;
805        }
806        
807        public float getFaderBrightness(float distAlong) {
808                SlipstreamSegment curr = getSegmentForDist(distAlong);
809                if (curr != null) {
810                        if (segments.size() > curr.index + 1) {
811                                SlipstreamSegment next = segments.get(curr.index + 1);
812                                float f = (distAlong - curr.totalLength) / curr.lengthToNext;
813                                if (f < 0) f = 0;
814                                if (f > 1) f = 1;
815                                return Misc.interpolate(curr.fader.getBrightness(), next.fader.getBrightness(), f);
816                        } else {
817                                return 0f;
818                        }
819                }
820                return 0f;
821        }
822
823        public void render(CampaignEngineLayers layer, ViewportAPI viewport) {
824                recomputeIfNeeded();
825                if (lengthToIndexMap == null) return;
826                
827                
828                if (true && false) {
829                        //BoundingBox box = BoundingBox.create(segments);
830                        float mx = Mouse.getX();
831                        float my = Mouse.getY();
832                        float wmx = Global.getSector().getViewport().convertScreenXToWorldX(mx);
833                        float wmy = Global.getSector().getViewport().convertScreenYToWorldY(my);
834                        boolean inside = false;
835                        for (BoundingBox box : bounds) {
836                                box.renderDebug(1f);
837                                inside |= box.pointNeedsDetailedCheck(new Vector2f(wmx, wmy));
838                        }
839                        
840                        GL11.glDisable(GL11.GL_TEXTURE_2D);
841                        GL11.glEnable(GL11.GL_BLEND);
842                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
843                
844                        GL11.glPointSize(20f);
845                        GL11.glEnable(GL11.GL_POINT_SMOOTH);
846                        if (inside) {
847                                Misc.setColor(Color.green);
848                        } else {
849                                Misc.setColor(Color.gray);
850                        }
851                        
852                        GL11.glBegin(GL11.GL_POINTS);
853                        GL11.glVertex2f(wmx, wmy);
854                        GL11.glEnd();
855                        //return;
856                }
857                
858                
859                
860                
861                float viewRadius = new Vector2f(viewport.getVisibleWidth() * 0.5f, viewport.getVisibleHeight() * 0.5f).length();
862                viewRadius += 500f;
863                
864//              viewRadius *= 0.5f;
865//              viewRadius = 500f;
866                
867                List<SlipstreamSegment> near = getSegmentsNear(viewport.getCenter(), viewRadius);
868                Set<SlipstreamSegment> nearSet = new LinkedHashSet<SlipstreamSegment>(near);
869                
870                List<List<SlipstreamSegment>> subsections = new ArrayList<List<SlipstreamSegment>>();
871                int prevIndex = -10;
872                List<SlipstreamSegment> subsection = new ArrayList<SlipstreamSegment>();
873                for (SlipstreamSegment seg : near) {
874                        if (prevIndex != seg.index -1) {
875                                if (subsection != null && !subsection.isEmpty()) {
876                                        subsections.add(subsection);
877                                }
878                                subsection = new ArrayList<SlipstreamSegment>();
879                        }
880                        subsection.add(seg);
881                        prevIndex = seg.index;
882                }
883                if (subsection != null && !subsection.isEmpty()) {
884                        subsections.add(subsection);
885                }
886                
887                SpriteAPI sprite = Global.getSettings().getSprite("misc", params.spriteKey1);
888                //sprite.setAdditiveBlend();
889                sprite.setNormalBlend();
890                sprite.setColor(params.spriteColor);
891                
892                SpriteAPI edge = Global.getSettings().getSprite("misc", params.edgeKey);
893                edge.setNormalBlend();
894                edge.setColor(params.edgeColor);
895                
896                //sprite.setColor(Misc.setAlpha(params.spriteColor1, 255));
897                //sprite.setColor(Color.blue);
898                for (List<SlipstreamSegment> subsection2 : subsections) {
899                        renderSegments(sprite, edge, viewport.getAlphaMult(), subsection2);
900                }
901                
902                //sprite.setColor(Color.red);
903                //renderLayer(sprite, texProgress2, viewport.getAlphaMult());
904                //sprite.setColor(Color.green);
905                //renderLayer(sprite, texProgress3, viewport.getAlphaMult());
906                
907//              int state = 0;
908//              for (int i = 0; i < segments.size() - 4; i += 2) {
909//                      //GL11.glBegin(GL11.GL_POINTS);
910//                      SlipstreamSegment prev = null;
911//                      if (i > 0) {
912//                              prev = segments.get(i - 1);
913//                      }
914//                      SlipstreamSegment curr = segments.get(i);
915//                      SlipstreamSegment next = segments.get(i + 1);   
916//                      SlipstreamSegment next2 = segments.get(i + 2);
917//                      SlipstreamSegment next3 = segments.get(i + 3);
918//                      Vector2f p0 = curr.loc;
919//                      Vector2f p1 = next.loc;
920//                      Vector2f p2 = next2.loc;
921//                      Vector2f p3 = next3.loc;
922//                      
923//                      if (state == 0) {
924//                              state = 1;
925//                              float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
926//                              float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
927//                              float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
928//                              float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * diff * 0.5f + 180f;
929//                              angle = Misc.getAngleInDegrees(p3, p2);
930//                              float dist = Misc.getDistance(p2, p1);
931//                              Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
932//                              p1Adjusted.scale(dist);
933//                              Vector2f.add(p1Adjusted, p2, p1Adjusted);
934//                              curr.locB.set(p1Adjusted);
935//                      } else if (state == 1) {
936//                              curr.locB.set(curr.loc);
937//                      } else if (state == 2) {
938//                              
939//                      }
940//              }
941                
942                
943                
944                
945                
946                GL11.glDisable(GL11.GL_TEXTURE_2D);
947                GL11.glEnable(GL11.GL_BLEND);
948                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
949                
950                float zoom = Global.getSector().getViewport().getViewMult(); 
951
952                //GL11.glLineWidth(2f);
953                //GL11.glLineWidth(Math.max(1f, 2f/zoom));
954                GL11.glLineWidth(Math.max(1f, Math.min(2f, 2f/zoom)));
955                //GL11.glLineWidth(1.5f);
956                GL11.glEnable(GL11.GL_LINE_SMOOTH);
957                
958                Misc.setColor(new Color(1f, 1f, 1f, 0.5f));
959                Misc.setColor(Color.white);
960                //GL11.glLineWidth(1f);
961                
962//              for (SlipstreamSegment seg : segments) {
963//                      if (seg.totalLength <= 0f && segments.indexOf(seg) > 1) {
964//                              System.out.println("efwefwefwefe");
965//                      }
966//              }
967                
968                // draw bezier lines for debug
969                for (float offset = -1f; false && offset <= 1f; offset += 0.1f) {
970                //for (float offset = 0f; offset <= 0f; offset += 0.1f) {
971                        GL11.glBegin(GL11.GL_LINE_STRIP);
972                        float incr = 10f;
973                        for (float len = 0; len < totalLength; len += incr) {
974//                              if (len > 10000f) {
975//                                      System.out.println("ewfwefew");
976//                              }
977                                /*
978                                SlipstreamSegment curr = getSegmentForDist(len);
979                                if (curr == null) continue;
980                                int index = curr.index;
981                                if (index >= segments.size() - 2) continue;
982                                SlipstreamSegment next = segments.get(index + 1);
983                                SlipstreamSegment next2 = segments.get(index + 2);
984                                
985                                if (index % 2 != 0) {
986                                        curr = segments.get(index - 1);
987                                        next = segments.get(index);
988                                        next2 = segments.get(index + 1);
989                                }
990                                
991                                float lenForT = len - curr.totalLength;
992                                float t = lenForT / (curr.lengthToNext + next.lengthToNext);
993                                
994                                //Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
995                                Vector2f p0 = curr.loc;
996                                Vector2f p1 = next.loc;
997                                Vector2f p2 = next2.loc;
998                                
999                                p0 = new Vector2f(p0);
1000                                p0.x += curr.normal.x * params.width * 0.5f * offset;
1001                                p0.y += curr.normal.y * params.width * 0.5f * offset;
1002                                
1003                                p2 = new Vector2f(p2);
1004                                p2.x += next2.normal.x * params.width * 0.5f * offset;
1005                                p2.y += next2.normal.y * params.width * 0.5f * offset;
1006                                
1007                                p1 = new Vector2f(next.locB);
1008                                p1 = new Vector2f(p1);
1009                                p1.x += next.normal.x * params.width * 0.5f * offset;
1010                                p1.y += next.normal.y * params.width * 0.5f * offset;
1011                                
1012                                Vector2f p = Misc.bezier(p0, p1, p2, t);
1013                                
1014//                              float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1015//                              Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1016                                //curr.lengthToNext + next.lengthToNext
1017//                              float f = lenForT / curr.lengthToNext;
1018//                              Vector2f perp;
1019//                              if (f < 1f) {
1020//                                      perp = Misc.interpolateVector(curr.normal, next.normal, f);
1021//                              } else {
1022//                                      f = (lenForT - curr.lengthToNext) / next.lengthToNext;
1023////                                    if (f > 1f) {
1024////                                            System.out.println("wefwefe " + index);
1025////                                    }
1026//                                      perp = Misc.interpolateVector(next.normal, next2.normal, f);
1027//                              }
1028//                              perp.scale(offset * params.width * 0.5f);
1029                                //perp.set(0, 0);
1030                                
1031                                //p = Misc.interpolateVector(pPrev, p, 0.5f);
1032                                //GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1033                                 * 
1034                                 */
1035                                
1036                                Vector2f p = getPointAt(len, offset);
1037                                if (p != null) {
1038                                        GL11.glVertex2f(p.x, p.y);
1039                                }
1040                        }
1041                        if (false) {
1042                                Misc.setColor(Color.red);
1043                                for (int i = 0; i < segments.size() - 3; i+=2) {
1044                                        //GL11.glBegin(GL11.GL_POINTS);
1045                                        SlipstreamSegment prev = null;
1046                                        if (i > 0) {
1047                                                prev = segments.get(i - 1);
1048                                        }
1049                                        SlipstreamSegment curr = segments.get(i);
1050                                        SlipstreamSegment next = segments.get(i + 1);   
1051                                        SlipstreamSegment next2 = segments.get(i + 2);
1052                                        SlipstreamSegment next3 = segments.get(i + 3);
1053                                        
1054                //                      GL11.glVertex2f(curr.loc.x, curr.loc.y);
1055                //                      GL11.glVertex2f(next.loc.x, next.loc.y);
1056                //                      GL11.glVertex2f(next2.loc.x, next2.loc.y);
1057                                        
1058                                        Vector2f p0 = curr.loc;
1059                                        Vector2f p1 = next.loc;
1060                                        Vector2f p2 = next2.loc;
1061                                        Vector2f p3 = next3.loc;
1062                                        
1063        //                              float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1064        //                              float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1065        //                              float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1066        //                              float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
1067        //                              adjustment = diff * 0.5f;
1068        //                              //adjustment = diff * 0.25f;
1069        //                              float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
1070        //                              //angle = Misc.getAngleInDegrees(p3, p2);
1071        //                              float dist = Misc.getDistance(p2, p1);
1072        //                              Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1073        //                              p1Adjusted.scale(dist);
1074        //                              Vector2f.add(p1Adjusted, p2, p1Adjusted);
1075                                        
1076                                        //GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1077                                        //GL11.glVertex2f(p1.x, p1.y);
1078                                        
1079                                        p0 = new Vector2f(p0);
1080                                        p0.x += curr.normal.x * curr.width * 0.5f * offset;
1081                                        p0.y += curr.normal.y * curr.width * 0.5f * offset;
1082                                        
1083                                        p2 = new Vector2f(p2);
1084                                        p2.x += next2.normal.x * next2.width * 0.5f * offset;
1085                                        p2.y += next2.normal.y * next2.width * 0.5f * offset;
1086                                        
1087                                        p1 = new Vector2f(next.locB);
1088                                        p1 = new Vector2f(p1);
1089                                        p1.x += next.normal.x * next.width * 0.5f * offset;
1090                                        p1.y += next.normal.y * next.width * 0.5f * offset;
1091                                        
1092        //                              p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1093        //                              p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1094        //                              diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1095        //                              adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
1096        //                              adjustment = diff * 0.5f;
1097        //                              //adjustment = diff * 0.25f;
1098        //                              angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
1099        //                              //angle = Misc.getAngleInDegrees(p3, p2);
1100        //                              dist = Misc.getDistance(p2, p1);
1101        //                              p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1102        //                              p1Adjusted.scale(dist);
1103        //                              Vector2f.add(p1Adjusted, p2, p1Adjusted);
1104                                        
1105                                        incr = 10f;
1106                                        for (float len = 0; len < curr.lengthToNext + next.lengthToNext; len += incr) {
1107                                                float t = len / (curr.lengthToNext + next.lengthToNext);
1108                                                //Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1109                                                Vector2f p = Misc.bezier(p0, p1, p2, t);
1110                                                
1111                //                              float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1112                //                              Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1113                                                
1114                                                float f = len / curr.lengthToNext;
1115                                                Vector2f perp;
1116                                                if (f < 1f) {
1117                                                        perp = Misc.interpolateVector(curr.normal, next.normal, f);
1118                                                } else {
1119                                                        f = (len - curr.lengthToNext) / next.lengthToNext;
1120                                                        perp = Misc.interpolateVector(next.normal, next2.normal, f);
1121                                                }
1122                                                perp.scale(offset * curr.width * 0.5f);
1123                                                perp.set(0, 0);
1124                                                
1125                                                //p = Misc.interpolateVector(pPrev, p, 0.5f);
1126                                                GL11.glVertex2f(p.x, p.y);
1127                                                //GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1128                                                //GL11.glVertex2f(pPrev.x, pPrev.y);
1129                                        }
1130                                        //if (i == 4) break;
1131                                }
1132                        }
1133                        GL11.glEnd();
1134                }
1135                
1136//              GL11.glBegin(GL11.GL_LINES);
1137//              for (int i = 0; i < segments.size() - 4; i+=2) {
1138//                      //GL11.glBegin(GL11.GL_POINTS);
1139//                      SlipstreamSegment prev = null;
1140//                      if (i > 0) {
1141//                              prev = segments.get(i - 1);
1142//                      }
1143//                      SlipstreamSegment curr = segments.get(i);
1144//                      SlipstreamSegment next = segments.get(i + 1);   
1145//                      SlipstreamSegment next2 = segments.get(i + 2);
1146//                      SlipstreamSegment next3 = segments.get(i + 3);
1147//                      
1148////                    GL11.glVertex2f(curr.loc.x, curr.loc.y);
1149////                    GL11.glVertex2f(next.loc.x, next.loc.y);
1150////                    GL11.glVertex2f(next2.loc.x, next2.loc.y);
1151//                      
1152//                      Vector2f p0 = curr.loc;
1153//                      Vector2f p1 = next.loc;
1154//                      Vector2f p2 = next2.loc;
1155//                      Vector2f p3 = next3.loc;
1156//                      
1157//                      float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1158//                      float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1159//                      float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1160//                      float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
1161//                      adjustment = diff * 0.5f;
1162//                      //adjustment = diff * 0.25f;
1163//                      float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * adjustment * 1f + 180f;
1164//                      //angle = Misc.getAngleInDegrees(p3, p2);
1165//                      float dist = Misc.getDistance(p2, p1);
1166//                      Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1167//                      p1Adjusted.scale(dist);
1168//                      Vector2f.add(p1Adjusted, p2, p1Adjusted);
1169//                      
1170//                      //skip = diff < 30f;
1171//                      skip = false;
1172//                      if (skip) p1Adjusted.set(p1);
1173//                      skip = !skip;
1174//                      
1175//                      prevAdjustedP1 = p1Adjusted;
1176//                      //GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1177//                      //GL11.glVertex2f(p1.x, p1.y);
1178//                      
1179//                      float incr = 10f;
1180//                      Misc.setColor(new Color(1f, 0.5f, 0f, 1f));
1181//                      for (float len = 0; len < curr.lengthToNext + next.lengthToNext; len += incr) {
1182//                              float t = len / (curr.lengthToNext + next.lengthToNext);
1183//                              //Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1184//                              Vector2f p = Misc.bezier(curr.loc, p1Adjusted, next2.loc, t);
1185////                            float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1186////                            Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1187//                              
1188//                              float f = len / curr.lengthToNext;
1189//                              Vector2f perp;
1190//                              if (f < 1f) {
1191//                                      perp = Misc.interpolateVector(curr.normal, next.normal, f);
1192//                              } else {
1193//                                      f = (len - curr.lengthToNext) / next.lengthToNext;
1194//                                      perp = Misc.interpolateVector(next.normal, next2.normal, f);
1195//                              }
1196//                              
1197//                              
1198//                              perp.scale(1f * params.width * 0.5f);
1199//                              
1200//                              //p = Misc.interpolateVector(pPrev, p, 0.5f);
1201//                              //GL11.glVertex2f(p.x, p.y);
1202//                              GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1203//                              GL11.glVertex2f(p.x - perp.x, p.y - perp.y);
1204//                              //GL11.glVertex2f(pPrev.x, pPrev.y);
1205//                      }
1206//                      //if (i == 4) break;
1207//              }
1208//              GL11.glEnd();
1209                
1210//              GL11.glPointSize(10);
1211//              GL11.glBegin(GL11.GL_POINTS);
1212//              for (int i = 0; i < segments.size() - 4; i+=2) {
1213//                      if (i % 4 == 0) {
1214//                              Misc.setColor(Color.red);
1215//                      } else {
1216//                              Misc.setColor(Color.green);
1217//                      }
1218//                      //GL11.glBegin(GL11.GL_POINTS);
1219//                      //SlipstreamSegment prev = segments.get(i);
1220//                      SlipstreamSegment curr = segments.get(i);
1221//                      SlipstreamSegment next = segments.get(i + 1);   
1222//                      SlipstreamSegment next2 = segments.get(i + 2);
1223//                      SlipstreamSegment next3 = segments.get(i + 3);
1224//                      
1225////                    GL11.glVertex2f(curr.loc.x, curr.loc.y);
1226////                    GL11.glVertex2f(next.loc.x, next.loc.y);
1227////                    GL11.glVertex2f(next2.loc.x, next2.loc.y);
1228//                      
1229//                      Vector2f p0 = curr.loc;
1230//                      Vector2f p1 = next.loc;
1231//                      Vector2f p2 = next2.loc;
1232//                      Vector2f p3 = next3.loc;
1233//                      
1234//                      float p1ToP2 = Misc.getAngleInDegrees(p1, p2);
1235//                      float p2ToP3 = Misc.getAngleInDegrees(p2, p3);
1236//                      float diff = Misc.getAngleDiff(p1ToP2, p2ToP3);
1237//                      float angle = p1ToP2 + Misc.getClosestTurnDirection(p1ToP2, p2ToP3) * diff * 1f + 180f;
1238//                      //angle = Misc.getAngleInDegrees(p3, p2);
1239//                      float dist = Misc.getDistance(p2, p1);
1240//                      Vector2f p1Adjusted = Misc.getUnitVectorAtDegreeAngle(angle);
1241//                      p1Adjusted.scale(dist);
1242//                      Vector2f.add(p1Adjusted, p2, p1Adjusted);
1243//                      prevAdjustedP1 = p1Adjusted;
1244//                      //GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1245//                      //GL11.glVertex2f(p1.x, p1.y);
1246//                      
1247//                      GL11.glVertex2f(p0.x, p0.y);
1248//                      GL11.glVertex2f(p1Adjusted.x, p1Adjusted.y);
1249//                      GL11.glVertex2f(p2.x, p2.y);
1250//              }
1251//              GL11.glEnd();
1252                
1253                if (false) {
1254                        float[] place = getLengthAndWidthFractionWithinStream(Global.getSector().getPlayerFleet().getLocation());
1255                        if (place != null) {
1256                                Misc.setColor(Color.red);
1257                                GL11.glPointSize(40f/zoom);
1258                                GL11.glEnable(GL11.GL_POINT_SMOOTH);
1259                                GL11.glBegin(GL11.GL_POINTS);
1260                                Vector2f p = getPointAt(place[0], place[1]);
1261                                
1262                                GL11.glVertex2f(p.x, p.y);
1263                                
1264                                Misc.setColor(Color.blue);
1265                                p = Global.getSector().getPlayerFleet().getLocation();
1266                                GL11.glVertex2f(p.x, p.y);
1267                                
1268                                SlipstreamSegment seg = getSegmentForDist(place[0]);
1269                                if (seg != null) {
1270                                        float withinSeg = place[0] - seg.totalLength;
1271                                        Vector2f p2 = new Vector2f(seg.normal.y, -seg.normal.x);
1272                                        p2.scale(withinSeg);
1273                                        Vector2f.add(p2, seg.loc, p2);
1274                                        float width = seg.wobbledWidth;
1275                                        if (segments.size() > seg.index + 1) {
1276                                                SlipstreamSegment next = segments.get(seg.index + 1);
1277                                                width = Misc.interpolate(seg.wobbledWidth, next.wobbledWidth, 
1278                                                                (place[0] - seg.totalLength) / seg.lengthToNext);
1279                                        }
1280                                        p2.x += getNormalAt(place[0]).x * place[1] * width * 0.5f;
1281                                        p2.y += getNormalAt(place[0]).y * place[1] * width * 0.5f;
1282                                        Misc.setColor(Color.green);
1283                                        GL11.glVertex2f(p2.x, p2.y);
1284                                }
1285                                GL11.glEnd();
1286                        }
1287                }
1288                
1289//              GL11.glBegin(GL11.GL_LINE_STRIP);
1290//              for (int i = 1; i < segments.size() - 2; i++) {
1291//                      SlipstreamSegment prev = segments.get(i);
1292//                      SlipstreamSegment curr = segments.get(i);
1293//                      SlipstreamSegment next = segments.get(i + 1);   
1294//                      SlipstreamSegment next2 = segments.get(i + 2);
1295//                      
1296//                      float incr = 5f;
1297//                      for (float len = 0; len < curr.lengthToNext; len += incr) {
1298//                              float t = len / (curr.lengthToNext + next.lengthToNext);
1299//                              Vector2f p = Misc.bezier(curr.loc, next.loc, next2.loc, t);
1300//                              
1301//                              float tPrev = (prev.lengthToNext + len) / (prev.lengthToNext + curr.lengthToNext);
1302//                              Vector2f pPrev = Misc.bezier(prev.loc, curr.loc, next.loc, tPrev);
1303//                              
1304//                              //p = Misc.interpolateVector(pPrev, p, 0.5f);
1305//                              //GL11.glVertex2f(p.x, p.y);
1306//                              GL11.glVertex2f(pPrev.x, pPrev.y);
1307//                      }
1308//                      if (i == 4) break;
1309//              }
1310//              GL11.glEnd();
1311                
1312                boolean curvedTrails = true;
1313                boolean useTex = false;
1314                //if (zoom > 1.25f) useTex = false;
1315                //useTex = false;
1316                //System.out.println("USETEX = " + useTex);
1317                if (!useTex) {
1318                        GL11.glDisable(GL11.GL_TEXTURE_2D);
1319                        GL11.glEnable(GL11.GL_BLEND);
1320                        GL11.glLineWidth(Math.max(1f, Math.min(2f, 2f/zoom)));
1321                        //GL11.glLineWidth(25f);
1322                        GL11.glEnable(GL11.GL_LINE_SMOOTH);
1323                        GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
1324                }
1325                
1326                //curvedTrails = false;
1327                if (!curvedTrails) {
1328                        GL11.glBegin(GL11.GL_LINES);
1329                }
1330//              GL11.glEnable(GL11.GL_POINT_SMOOTH);
1331//              GL11.glPointSize(10f);
1332//              GL11.glBegin(GL11.GL_POINTS);
1333                //int index = 0;
1334                
1335                if (useTex) {
1336                        GL11.glEnable(GL11.GL_TEXTURE_2D);
1337                        GL11.glEnable(GL11.GL_BLEND);
1338                        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1339                        
1340                        SpriteAPI line = Global.getSettings().getSprite("graphics/hud/line4x4.png");
1341                        //line = Global.getSettings().getSprite("graphics/hud/line32x32.png");
1342                        line.bindTexture();
1343                }
1344                
1345                for (SlipstreamParticle p : particles) {
1346                        SlipstreamSegment seg = getSegmentForDist(p.dist);
1347                        if (seg == null || !nearSet.contains(seg)) continue; 
1348                        
1349//                      index++;
1350//                      if (index > 1) break;
1351                        //if (true) break;
1352                        float a = viewport.getAlphaMult();
1353                        if (p.remaining <= 0.5f) {
1354                                a = p.remaining / 0.5f;
1355                        } else if (p.elapsed < params.particleFadeInTime) {
1356                                a = p.elapsed / params.particleFadeInTime;
1357                        }
1358                        
1359                        a *= getFaderBrightness(p.dist);
1360                        
1361                        //a *= 0.5f;
1362                        //a *= 0.1f;
1363                        
1364                        //a = 1f;
1365                        
1366//                      SlipstreamSegment seg = getSegmentForDist(p.dist);
1367//                      if (seg == null) continue;
1368                        float yPos = p.yPos;
1369                        //yPos = 0f;
1370                        
1371                        if (curvedTrails) {
1372                                if (useTex) {
1373                                        GL11.glBegin(GL11.GL_QUAD_STRIP);
1374                                        Vector2f curr = getPointAt(p.dist, yPos);
1375                                        if (curr == null || !viewport.isNearViewport(curr, p.speed * params.lineLengthFractionOfSpeed + 50f)) {
1376                                                GL11.glEnd();
1377                                                continue;
1378                                        }
1379                                        float iter = 5f;
1380                                        float incr = p.speed * params.lineLengthFractionOfSpeed / iter;
1381                                        float lw = 1f;
1382                                        for (float i = 0; i < iter; i++) {
1383                                                float min = incr * 1f;
1384                                                float dist = p.dist - i * incr - min;
1385                                                Vector2f next = getPointAt(dist, yPos);
1386                                                if (next == null) break;
1387                                                
1388                                                Vector2f perp = getNormalAt(dist);
1389                                                if (perp == null) {
1390                                                        GL11.glEnd();
1391                                                        break;
1392                                                }
1393                                                
1394                                                float a1 = a * (iter - i) / (iter - 1);
1395                                                if (i == 0) a1 = 0f;
1396                                                
1397                                                Misc.setColor(p.color, a1);
1398                                                GL11.glTexCoord2f(0, 0f);
1399                                                GL11.glVertex2f(curr.x + perp.x * lw, curr.y + perp.y * lw);
1400                                                GL11.glTexCoord2f(0, 1f);
1401                                                GL11.glVertex2f(curr.x - perp.x * lw, curr.y - perp.y * lw);
1402                                                curr = next;
1403                                        }
1404                                        GL11.glEnd();
1405                                } else {
1406                                        GL11.glBegin(GL11.GL_LINE_STRIP);
1407                                        //GL11.glBegin(GL11.GL_LINES);
1408                                        Vector2f curr = getPointAt(p.dist, yPos);
1409                                        if (curr == null || !viewport.isNearViewport(curr, p.speed * params.lineLengthFractionOfSpeed + 50f)) {
1410                                                GL11.glEnd();
1411                                                continue;
1412                                        }
1413                                        float iter = 5f;
1414                                        float incr = p.speed * params.lineLengthFractionOfSpeed / iter;
1415                                        for (float i = 0; i < iter; i++) {
1416                                                
1417                                                float min = incr * 0.5f;
1418                                                Vector2f next = getPointAt(p.dist - i * incr - min, yPos);
1419                                                if (next == null) {
1420                                                        GL11.glEnd();
1421                                                        break;
1422                                                }
1423                                                
1424                                                float a1 = a * (iter - i) / (iter - 1);
1425                                                //float a2 = a * (iter - i - 1) / (iter - 1);
1426                                                if (i == 0) a1 = 0f;
1427                                                
1428                                                Misc.setColor(p.color, a1);
1429                                                GL11.glVertex2f(curr.x, curr.y);
1430                                                //Misc.setColor(p.color, a2);
1431                                                //GL11.glVertex2f(next.x, next.y);
1432                                                curr = next;
1433                                        }
1434                                        GL11.glEnd();
1435                                }
1436                        } else {
1437                                Vector2f start = getPointAt(p.dist + p.speed * params.lineLengthFractionOfSpeed * 0.1f, yPos);
1438                                if (start == null || !viewport.isNearViewport(start, 500)) continue;
1439                                
1440                                Vector2f mid = getPointAt(p.dist, yPos);
1441                                if (mid == null) continue;
1442                                Vector2f end = getPointAt(p.dist - p.speed * params.lineLengthFractionOfSpeed * 0.9f, yPos);
1443                                if (end == null) continue;
1444                                
1445                                Misc.setColor(p.color, 0f);
1446                                GL11.glVertex2f(start.x, start.y);
1447                                Misc.setColor(p.color, a);
1448                                GL11.glVertex2f(mid.x, mid.y);
1449                                GL11.glVertex2f(mid.x, mid.y);
1450                                Misc.setColor(p.color, 0f);
1451                                GL11.glVertex2f(end.x, end.y);
1452                        }
1453//                      
1454                }
1455                if (!curvedTrails) {
1456                        GL11.glEnd();
1457                }
1458        }
1459        
1460        
1461        protected FaderUtil fader = new FaderUtil(0f, 0.5f, 0.5f);
1462        
1463        public void renderSegments(SpriteAPI sprite, SpriteAPI edge, float alpha, List<SlipstreamSegment> segments) {
1464                //if (true) return;
1465                GL11.glEnable(GL11.GL_TEXTURE_2D);
1466                sprite.bindTexture();
1467                GL11.glEnable(GL11.GL_BLEND);
1468                //GL11.glDisable(GL11.GL_BLEND);
1469                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1470                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1471                Color color = sprite.getColor();
1472                //color = Misc.interpolateColor(color, Color.black, 0.5f);
1473                //color = Color.black;
1474                //color = Misc.scaleColorOnly(color, 0.3f);
1475                //color = Misc.setAlpha(color, 100);
1476                
1477                boolean wireframe = false;
1478                //wireframe = true;
1479                if (wireframe) {
1480                        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
1481                        GL11.glDisable(GL11.GL_TEXTURE_2D);
1482                        GL11.glDisable(GL11.GL_BLEND);
1483                }
1484                
1485                boolean subtract = false;
1486                //subtract = true;
1487                if (subtract) {
1488                        GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
1489                }
1490                
1491                // "channel"
1492                Color c = Color.black;
1493                //c = Misc.setAlpha(c, 255);
1494                GL11.glDisable(GL11.GL_TEXTURE_2D);
1495                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1496                
1497                int alphaInDeep = 255;
1498                int alphaOutside = 0;
1499                HyperspaceTerrainPlugin plugin = (HyperspaceTerrainPlugin) Misc.getHyperspaceTerrain().getPlugin();
1500                
1501                GL11.glBegin(GL11.GL_QUAD_STRIP);
1502                
1503                for (int i = 0; i < segments.size(); i++) {
1504                        SlipstreamSegment curr = segments.get(i);
1505                        float a = curr.fader.getBrightness();
1506                        
1507                        if (entity.isInHyperspace() && plugin.isInClouds(curr.loc, 100f)) {
1508                                c = Misc.setAlpha(c, alphaInDeep);
1509                        } else {
1510                                c = Misc.setAlpha(c, alphaOutside);
1511                        }
1512                        
1513                        Vector2f p1 = new Vector2f(curr.loc);
1514                        p1.x += curr.normal.x * curr.width * 0.5f;
1515                        p1.y += curr.normal.y * curr.width * 0.5f;
1516                        Vector2f p2 = new Vector2f(curr.loc);
1517                        p2.x += curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1518                        p2.y += curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1519                        
1520                        p1.x += curr.wobble1.vector.x;
1521                        p1.y += curr.wobble1.vector.y;
1522                        
1523                        Misc.setColor(c, alpha * 0f * a);
1524                        GL11.glVertex2f(p1.x, p1.y);
1525                        Misc.setColor(c, alpha * 1f * a);
1526                        GL11.glVertex2f(p2.x, p2.y);
1527                }
1528                GL11.glEnd();
1529                
1530                //edge2.bindTexture();
1531                GL11.glBegin(GL11.GL_QUAD_STRIP);
1532                
1533                for (int i = 0; i < segments.size(); i++) {
1534                        SlipstreamSegment curr = segments.get(i);
1535                        float a = curr.fader.getBrightness();
1536                        
1537                        if (entity.isInHyperspace() && plugin.isInClouds(curr.loc, 100f)) {
1538                                c = Misc.setAlpha(c, alphaInDeep);
1539                        } else {
1540                                c = Misc.setAlpha(c, alphaOutside);
1541                        }
1542                        
1543                        Vector2f p1 = new Vector2f(curr.loc);
1544                        p1.x -= curr.normal.x * curr.width * 0.5f;
1545                        p1.y -= curr.normal.y * curr.width * 0.5f;
1546                        Vector2f p2 = new Vector2f(curr.loc);
1547                        p2.x -= curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1548                        p2.y -= curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1549                        
1550                        p1.x += curr.wobble2.vector.x;
1551                        p1.y += curr.wobble2.vector.y;
1552                        
1553                        Misc.setColor(c, alpha * 0f * a);
1554                        GL11.glVertex2f(p1.x, p1.y);
1555                        Misc.setColor(c, alpha * 1f * a);
1556                        GL11.glVertex2f(p2.x, p2.y);
1557                }
1558                GL11.glEnd();
1559                
1560                GL11.glBegin(GL11.GL_QUAD_STRIP);
1561                
1562                for (int i = 0; i < segments.size(); i++) {
1563                        SlipstreamSegment curr = segments.get(i);
1564                        float a = curr.fader.getBrightness();
1565                        
1566                        if (entity.isInHyperspace() && plugin.isInClouds(curr.loc, 100f)) {
1567                                c = Misc.setAlpha(c, alphaInDeep);
1568                        } else {
1569                                c = Misc.setAlpha(c, alphaOutside);
1570                        }
1571                        
1572                        Vector2f p1 = new Vector2f(curr.loc);
1573                        p1.x += curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1574                        p1.y += curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1575                        Vector2f p2 = new Vector2f(curr.loc);
1576                        p2.x -= curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1577                        p2.y -= curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1578                        
1579                        Misc.setColor(c, alpha * 1f * a);
1580                        GL11.glVertex2f(p1.x, p1.y);
1581                        GL11.glVertex2f(p2.x, p2.y);
1582                }
1583                GL11.glEnd();
1584                // end "channel"
1585                
1586                
1587                // main background
1588                if (!wireframe) {
1589                        GL11.glEnable(GL11.GL_TEXTURE_2D);
1590                        GL11.glEnable(GL11.GL_BLEND);
1591                }
1592                //GL11.glDisable(GL11.GL_BLEND);
1593                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1594                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1595                
1596                GL11.glBegin(GL11.GL_QUAD_STRIP);
1597                for (int i = 0; i < segments.size(); i++) {
1598                        SlipstreamSegment curr = segments.get(i);
1599                        float a = curr.fader.getBrightness();
1600                        
1601                        Vector2f p1 = new Vector2f(curr.loc);
1602                        p1.x += curr.normal.x * curr.width * 0.5f;
1603                        p1.y += curr.normal.y * curr.width * 0.5f;
1604                        Vector2f p2 = new Vector2f(curr.loc);
1605                        p2.x -= curr.normal.x * curr.width * 0.5f;
1606                        p2.y -= curr.normal.y * curr.width * 0.5f;
1607                        
1608                        p1.x += curr.wobble1.vector.x;
1609                        p1.y += curr.wobble1.vector.y;
1610                        p2.x += curr.wobble2.vector.x;
1611                        p2.y += curr.wobble2.vector.y;
1612                        
1613                        Misc.setColor(color, alpha * 1f * a);
1614                        GL11.glTexCoord2f(curr.tx, 0f);
1615                        GL11.glVertex2f(p1.x, p1.y);
1616                        GL11.glTexCoord2f(curr.tx, 1f);
1617                        GL11.glVertex2f(p2.x, p2.y);
1618                }
1619                GL11.glEnd();
1620                
1621                
1622                // edges
1623                color = edge.getColor();
1624                float wobbleMult = 0.5f;
1625                edge.bindTexture();
1626                GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1627                //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1628                
1629                GL11.glBegin(GL11.GL_QUAD_STRIP);
1630                
1631                for (int i = 0; i < segments.size(); i++) {
1632                        SlipstreamSegment curr = segments.get(i);
1633                        float a = curr.fader.getBrightness();
1634                        
1635                        Vector2f p1 = new Vector2f(curr.loc);
1636                        p1.x += curr.normal.x * curr.width * 0.5f;
1637                        p1.y += curr.normal.y * curr.width * 0.5f;
1638                        Vector2f p2 = new Vector2f(curr.loc);
1639                        p2.x += curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1640                        p2.y += curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1641                        
1642                        p1.x += curr.wobble1.vector.x * wobbleMult;
1643                        p1.y += curr.wobble1.vector.y * wobbleMult;
1644                        p2.x += curr.wobble1.vector.x * wobbleMult;
1645                        p2.y += curr.wobble1.vector.y * wobbleMult;
1646                        
1647                        Misc.setColor(color, alpha * 1f * a);
1648                        GL11.glTexCoord2f(curr.txe1, 1f);
1649                        GL11.glVertex2f(p1.x, p1.y);
1650                        GL11.glTexCoord2f(curr.txe1, 0f);
1651                        GL11.glVertex2f(p2.x, p2.y);
1652                }
1653                GL11.glEnd();
1654                
1655                //edge2.bindTexture();
1656                GL11.glBegin(GL11.GL_QUAD_STRIP);
1657                
1658                for (int i = 0; i < segments.size(); i++) {
1659                        SlipstreamSegment curr = segments.get(i);
1660                        float a = curr.fader.getBrightness();
1661                        
1662                        Vector2f p1 = new Vector2f(curr.loc);
1663                        p1.x -= curr.normal.x * curr.width * 0.5f;
1664                        p1.y -= curr.normal.y * curr.width * 0.5f;
1665                        Vector2f p2 = new Vector2f(curr.loc);
1666                        p2.x -= curr.normal.x * (curr.width * 0.5f - params.edgeWidth);
1667                        p2.y -= curr.normal.y * (curr.width * 0.5f - params.edgeWidth);
1668                        
1669                        p1.x += curr.wobble2.vector.x * wobbleMult;
1670                        p1.y += curr.wobble2.vector.y * wobbleMult;
1671                        p2.x += curr.wobble2.vector.x * wobbleMult;
1672                        p2.y += curr.wobble2.vector.y * wobbleMult;
1673                        
1674                        Misc.setColor(color, alpha * 1f * a);
1675                        GL11.glTexCoord2f(curr.txe2, 1f);
1676                        GL11.glVertex2f(p1.x, p1.y);
1677                        GL11.glTexCoord2f(curr.txe2, 0f);
1678                        GL11.glVertex2f(p2.x, p2.y);
1679                }
1680                GL11.glEnd();
1681                
1682                
1683                if (subtract) {
1684                        GL14.glBlendEquation(GL14.GL_FUNC_ADD);
1685                }
1686                
1687                if (wireframe) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
1688        }
1689
1690        
1691        
1692        
1693        
1694        public Color getRandomColor() {
1695                return Misc.interpolateColor(params.minColor, params.maxColor, (float) Math.random());
1696        }
1697        
1698        
1699        /**
1700         * result[0] = actual distance along the length of the slipstream 
1701         * result[1] = offset along the width of the slipstream, 
1702         *                              0 = on center, 1 = on edge along normal, -1 = on edge along negative of normal
1703         * null if outside stream
1704         * Assumes rectangular, non-tapered stream
1705         * @param loc
1706         * @return
1707         */
1708        public float [] getLengthAndWidthFractionWithinStream(Vector2f loc) {
1709                float dist = Misc.getDistance(loc, entity.getLocation());
1710                if (dist > getRenderRange()) return null;
1711                
1712                List<SlipstreamSegment> near = getSegmentsNear(loc, 0f);
1713                
1714                for (SlipstreamSegment curr : near) {
1715                        SlipstreamSegment next = null;
1716                        if (segments.size() > curr.index + 1) {
1717                                next = segments.get(curr.index + 1);
1718                        } else {
1719                                next = new SlipstreamSegment();
1720                                //next2.width = next.width;
1721                                next.wobbledWidth = curr.wobbledWidth;
1722                                
1723                                next.normal = curr.normal;
1724                                //next2.dir = next.dir;
1725                                next.loc = new Vector2f(curr.dir);
1726                                next.loc.scale(curr.lengthToPrev);
1727                                Vector2f.add(next.loc, curr.loc, next.loc);
1728                                //next2.locB = next2.loc;
1729                                next.lengthToPrev = curr.lengthToPrev;
1730                                //continue;
1731                        }
1732                                
1733                        Vector2f p3 = loc;
1734                        Vector2f p1 = curr.loc;
1735                        Vector2f p2 = next.loc;
1736                        
1737                        Vector2f currNormalP1 = new Vector2f(curr.loc);
1738                        Vector2f currNormalP2 = new Vector2f(curr.normal);
1739                        currNormalP2.scale(100f);
1740                        Vector2f.add(currNormalP2, currNormalP1, currNormalP2);
1741                        
1742                        Vector2f nextNormalP1 = new Vector2f(next.loc);
1743                        Vector2f nextNormalP2 = new Vector2f(next.normal);
1744                        nextNormalP2.scale(100f);
1745                        Vector2f.add(nextNormalP2, nextNormalP1, nextNormalP2);
1746                        
1747                        //Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
1748                        Vector2f dir = new Vector2f(curr.dir);
1749                        dir.scale(100f);
1750                        Vector2f p4 = Vector2f.add(p3, dir, new Vector2f());
1751                        
1752                        Vector2f currNormalP = Misc.intersectLines(currNormalP1, currNormalP2, p3, p4);
1753                        if (currNormalP == null) continue;
1754                        Vector2f nextNormalP = Misc.intersectLines(nextNormalP1, nextNormalP2, p3, p4);
1755                        if (nextNormalP == null) continue;
1756                        
1757                        float u = (p3.x - currNormalP.x) * (nextNormalP.x - currNormalP.x) + 
1758                                                        (p3.y - currNormalP.y) * (nextNormalP.y - currNormalP.y);
1759                        float denom = Vector2f.sub(nextNormalP, currNormalP, new Vector2f()).length();
1760                        denom *= denom;
1761                        if (denom == 0) continue;
1762                        u /= denom;
1763                        
1764                        if (u >= 0 && u <= 1) { // p3 is between the two points on the normals
1765                                Vector2f normalAtP3 = Misc.interpolateVector(curr.normal, next.normal, u);
1766                                normalAtP3.scale(100f);
1767                                Vector2f p3PlusNormal = Vector2f.add(p3, normalAtP3, new Vector2f());
1768                                
1769                                Vector2f intersect = Misc.intersectLines(p1, p2, p3, p3PlusNormal);
1770                                if (intersect == null) continue;
1771                                
1772                                float distFromLine = Vector2f.sub(intersect, p3, new Vector2f()).length();
1773                                float width = Misc.interpolate(curr.wobbledWidth, next.wobbledWidth, u);
1774                                if (distFromLine >= width / 2f) return null;
1775                                
1776                                float [] result = new float[2];
1777                                //result[0] = curr.totalLength + u * curr.lengthToNext;
1778                                result[0] = curr.totalLength + u * next.lengthToPrev;
1779                                result[1] = distFromLine / (width / 2f);
1780                                
1781                                float currToLoc = Misc.getAngleInDegrees(p1, p3);
1782                                float segDir = Misc.getAngleInDegrees(p1, p2);
1783                                if (Misc.getClosestTurnDirection(segDir, currToLoc) < 0) {
1784                                        result[1] = -result[1]; 
1785                                }
1786                                
1787                                return result;
1788                        
1789//                      float u = (p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y);
1790//                      float denom = Vector2f.sub(p2, p1, new Vector2f()).length();
1791//                      denom *= denom;
1792//                      if (denom == 0) continue;
1793//                      u /= denom;
1794//                      
1795////                    if (u < 0 && u > -0.03f) u = 0f;
1796////                    if (u > 1 && u < 1.03f) u = 1f;
1797//                      
1798//                      if (u >= 0 && u <= 1) { // intersection is between p1 and p2
1799//                              Vector2f intersect = new Vector2f();
1800//                              intersect.x = p1.x + u * (p2.x - p1.x);
1801//                              intersect.y = p1.y + u * (p2.y - p1.y);
1802//                              float distFromLine = Vector2f.sub(intersect, p3, new Vector2f()).length();
1803//
1804//                              float width = curr.wobbledWidth;
1805//                              if (next != null) {
1806//                                      width = Misc.interpolate(curr.wobbledWidth, next.wobbledWidth, u);
1807//                              }
1808//                              
1809//                              if (distFromLine >= width / 2f) return null;
1810//                              
1811//                              float [] result = new float[2];
1812//                              result[0] = curr.totalLength + u * curr.lengthToNext;
1813//                              result[1] = distFromLine / (width / 2f);
1814//                              
1815//                              float currToLoc = Misc.getAngleInDegrees(p1, p3);
1816//                              float segDir = Misc.getAngleInDegrees(p1, p2);
1817//                              if (Misc.getClosestTurnDirection(segDir, currToLoc) < 0) {
1818//                                      result[1] = -result[1]; 
1819//                              }
1820//                              
1821//                              return result;
1822                        }
1823                        
1824                }
1825                
1826//              Vector2f p3 = new Vector2f(loc);
1827//              Vector2f p1 = new Vector2f(entity.getLocation());
1828//              Vector2f p2 = Misc.getUnitVectorAtDegreeAngle(entity.getFacing() + 180f);
1829//              //p2.scale(params.length);
1830//              Vector2f.add(p2, p1, p2);
1831//              
1832//              float u = (p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y);
1833//              float denom = Vector2f.sub(p2, p1, new Vector2f()).length();
1834//              denom *= denom;
1835//              if (denom == 0) return null;
1836//              u /= denom;
1837//              
1838//              if (u >= 0 && u <= 1) { // intersection is between p1 and p2
1839//                      Vector2f intersect = new Vector2f();
1840//                      intersect.x = p1.x + u * (p2.x - p1.x);
1841//                      intersect.y = p1.y + u * (p2.y - p1.y);
1842//                      float distFromLine = Vector2f.sub(intersect, p3, new Vector2f()).length();
1843//                      //float distAlongLine = u * params.length;
1844//                      if (distFromLine >= params.width/2f) return null;
1845//                      
1846//                      float [] result = new float[2];
1847//                      result[0] = u;
1848//                      result[1] = distFromLine / (params.width / 2f);
1849//                      return result;
1850//              }
1851                return null;
1852        }
1853        
1854        public void applyEffectToFleets(float amount) {
1855                float days = Global.getSector().getClock().convertToDays(amount);
1856                for (CampaignFleetAPI fleet : entity.getContainingLocation().getFleets()) {
1857                        applyEffect(fleet, days);
1858                }
1859        }
1860        
1861        //protected boolean playerWasInSlipstream = false;
1862        protected int playerWasInSlipstreamFramesAgo = 1000;
1863        public void applyEffect(SectorEntityToken other, float days) {
1864                if (other instanceof CampaignFleetAPI) {
1865                        CampaignFleetAPI fleet = (CampaignFleetAPI) other;
1866                        
1867//                      if (fleet.isPlayerFleet()) {
1868//                              if (getLengthAndWidthFractionWithinStream(fleet.getLocation()) == null) {
1869//                                      System.out.println("wefwefwefe");
1870//                              }
1871//                              System.out.println("efwefwef");
1872//                      }
1873                        
1874                        float [] offset = getLengthAndWidthFractionWithinStream(fleet.getLocation());
1875                        if (offset == null) {
1876                                if (fleet.isPlayerFleet()) {
1877                                        playerWasInSlipstreamFramesAgo++;
1878                                        if (playerWasInSlipstreamFramesAgo > 1000) {
1879                                                playerWasInSlipstreamFramesAgo = 1000;
1880                                        }
1881                                }
1882                                return;
1883                        }
1884                        
1885//                      if (fleet.isPlayerFleet()) {
1886//                              System.out.println("Location in stream: " + offset[0] + ", " + offset[1]);
1887//                      }
1888                        
1889                        //params.burnLevel = 10;
1890                        
1891                        float distAlong = offset[0];
1892                        float yOff = offset[1];
1893                        
1894//                      float intensity = 1f;
1895//                      if (Math.abs(yOff) > 0.5f) {
1896//                              intensity *= (1f - Math.abs(yOff)) / 0.5f;
1897//                      }
1898                        float intensity = getIntensity(yOff);
1899                        float wMult = getWidthBasedSpeedMult(distAlong);
1900                        //System.out.println("wMult: " + wMult);
1901                        intensity *= wMult;
1902                        intensity *= getFaderBrightness(distAlong);
1903                        //intensity *= intensity;
1904                        //System.out.println(intensity);
1905                        
1906                        if (intensity <= 0) {
1907                                if (fleet.isPlayerFleet()) {
1908                                        playerWasInSlipstreamFramesAgo++;
1909                                        if (playerWasInSlipstreamFramesAgo > 1000) {
1910                                                playerWasInSlipstreamFramesAgo = 1000;
1911                                        }
1912                                }
1913                                return;
1914                        }
1915                        
1916                        if (fleet.isPlayerFleet()) {
1917                                //if (!playerWasInSlipstream) {
1918                                //      playerWasInSlipstream = true;
1919                                if (playerWasInSlipstreamFramesAgo > 5) {
1920                                        fleet.addFloatingText("Entering slipstream", Misc.setAlpha(fleet.getIndicatorColor(), 255), 0.5f);
1921                                }
1922                                playerWasInSlipstreamFramesAgo = 0;
1923                        }
1924                        
1925                        //System.out.println("Intensity: " + intensity);
1926
1927                        // "wind" effect - adjust velocity
1928                        float maxFleetBurn = fleet.getFleetData().getBurnLevel();
1929                        float currFleetBurn = fleet.getCurrBurnLevel();
1930                        
1931                        float maxWindBurn = params.burnLevel * 2f;
1932                        
1933                        float currWindBurn = intensity * maxWindBurn;
1934                        float maxFleetBurnIntoWind = maxFleetBurn - Math.abs(currWindBurn);
1935                        float seconds = days * Global.getSector().getClock().getSecondsPerDay();
1936                        
1937//                      float angle = Misc.getAngleInDegreesStrict(this.entity.getLocation(), fleet.getLocation()) + 180f;
1938//                      Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(angle);
1939                        Vector2f p1 = getPointAt(distAlong, yOff);
1940                        Vector2f p2 = getPointAt(distAlong + 1f, yOff);
1941                        if (p1 == null || p2 == null) {
1942                                if (fleet.isPlayerFleet()) {
1943                                        playerWasInSlipstreamFramesAgo++;
1944                                        if (playerWasInSlipstreamFramesAgo > 1000) {
1945                                                playerWasInSlipstreamFramesAgo = 1000;
1946                                        }
1947                                }
1948                                return;
1949                        }
1950                        
1951                        //Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(entity.getFacing());
1952                        Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
1953                        if (currWindBurn < 0) {
1954                                windDir.negate();
1955                        }
1956                        Vector2f velDir = Misc.normalise(new Vector2f(fleet.getVelocity()));
1957                        //float baseFleetAccel = Misc.getSpeedForBurnLevel(fleet.getFleetData().getMinBurnLevel());
1958                        float baseFleetAccel = fleet.getTravelSpeed();
1959                        if (baseFleetAccel < 10f) baseFleetAccel = 10f;
1960                        
1961                        boolean fleetTryingToMove = fleet.getMoveDestination() !=  null && 
1962                                        Misc.getDistance(fleet.getLocation(), fleet.getMoveDestination()) > fleet.getRadius() + 10f;
1963                        if (fleet.isPlayerFleet()) {
1964                                fleetTryingToMove &= (
1965                                                Global.getSector().getCampaignUI().isPlayerFleetFollowingMouse() ||
1966                                                fleet.wasSlowMoving());
1967                        }
1968                        float windSpeedReduction = 0f;
1969                        if (!fleetTryingToMove) {
1970                                Vector2f dest = new Vector2f(windDir);
1971                                dest.scale(1000f);
1972                                Vector2f.add(dest, fleet.getLocation(), dest);
1973                                fleet.setMoveDestination(dest.x, dest.y);
1974                        } else {
1975                                Vector2f moveDir = Misc.getUnitVectorAtDegreeAngle(
1976                                                                Misc.getAngleInDegrees(fleet.getLocation(), fleet.getMoveDestination()));
1977                                float dot = Vector2f.dot(windDir, moveDir);
1978                                if (fleet.wasSlowMoving()) dot = -1f;
1979                                if (dot < 0) {
1980                                        float accelBasedMult = fleet.getAcceleration() / baseFleetAccel;
1981                                        accelBasedMult *= accelBasedMult;
1982                                        if (accelBasedMult > 1f) accelBasedMult = 1f;
1983                                        if (accelBasedMult < 0.1f) accelBasedMult = 0.1f;
1984                                        windSpeedReduction = -dot * fleet.getFleetData().getBurnLevel() * accelBasedMult;
1985                                }
1986                        }
1987                        
1988                        //float burnBonus = fleet.getFleetData().getBurnLevel() - fleet.getFleetData().getMinBurnLevelUnmodified();
1989                        float burnBonus = fleet.getFleetData().getBurnLevel() - fleet.getFleetData().getMinBurnLevel();
1990                        if (burnBonus < 0) burnBonus = 0;
1991                        //float maxSpeedWithWind = Misc.getSpeedForBurnLevel(params.burnLevel + burnBonus);
1992                        float maxSpeedWithWind = Misc.getSpeedForBurnLevel((params.burnLevel * intensity) + burnBonus);
1993                        if (windSpeedReduction > 0) {
1994                                maxSpeedWithWind = Misc.getSpeedForBurnLevel(
1995                                                        Math.max(params.burnLevel  * 0.5f * intensity, params.burnLevel * intensity - windSpeedReduction));
1996                        }
1997                        
1998                        float fleetSpeedAlongWind = Vector2f.dot(windDir, fleet.getVelocity());
1999                        if (fleetSpeedAlongWind >= maxSpeedWithWind) {
2000//                              float dotPlayerAndWindVel = Vector2f.dot(windDir, velDir);
2001//                              if (dotPlayerAndWindVel > 0.98f) {
2002                                        return;
2003                                //}
2004                        }
2005                        
2006                        velDir.scale(currFleetBurn);
2007                        
2008                        float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
2009                        
2010                        
2011                        float windSpeed = Misc.getSpeedForBurnLevel(currWindBurn);
2012                        //float fleetSpeed = fleet.getTravelSpeed();
2013                        Vector2f windVector = new Vector2f(windDir);
2014                        windVector.scale(windSpeed);
2015                        
2016                        Vector2f vel = fleet.getVelocity();
2017                        Vector2f diff = Vector2f.sub(windVector, vel, new Vector2f());
2018                        //windDir.scale(seconds * fleet.getAcceleration());
2019                        float max = diff.length();
2020                        diff = Misc.normalise(diff);
2021                        //diff.scale(Math.max(windSpeed * seconds, fleet.getAcceleration() * 1f * seconds));
2022                        diff.scale(fleet.getAcceleration() * 3f * seconds);
2023                        //diff.scale(fleet.getTravelSpeed() * 5f * seconds);
2024                        //diff.scale(accelMult);
2025                        if (diff.length() > max) {
2026                                diff.scale(max / diff.length());
2027                        }
2028                        //System.out.println("Applying diff: " + diff);
2029                        //fleet.setVelocity(vel.x + diff.x, vel.y + diff.y);
2030                        
2031                        
2032//                      Vector2f velDir = Misc.normalise(new Vector2f(fleet.getVelocity()));
2033//                      velDir.scale(currFleetBurn);
2034//                      
2035//                      float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
2036//                      
2037                        float accelMult = 0.5f;
2038                        if (fleetBurnAgainstWind > maxFleetBurnIntoWind) {
2039                                accelMult += 0.75f + 0.25f * (fleetBurnAgainstWind - maxFleetBurnIntoWind);
2040                        }
2041                        
2042                        
2043                        //Vector2f vel = fleet.getVelocity();
2044                        //windDir.scale(seconds * fleet.getAcceleration() * accelMult);
2045                        //float baseFleetAccel = Math.max(fleet.getTravelSpeed(), fleet.getAcceleration());
2046                        
2047                        windDir.scale(seconds * baseFleetAccel * accelMult);
2048                        fleet.setVelocity(vel.x + windDir.x, vel.y + windDir.y);
2049                        
2050                        
2051                        boolean withGlow = true;
2052                        //withGlow = false;
2053                        if (withGlow) {
2054                                Color glowColor = params.windGlowColor;
2055                                int alpha = glowColor.getAlpha();
2056                                if (alpha < 75) {
2057                                        glowColor = Misc.setAlpha(glowColor, 75);
2058                                }
2059                                // visual effects - glow, tail
2060                                
2061                                p1 = getNoWobblePointAt(distAlong, yOff);
2062                                p2 = getNoWobblePointAt(distAlong + 100f, yOff);
2063                                if (p1 != null && p2 != null) {
2064                                        windDir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(p1, p2));
2065                                        
2066//                                      float fleetSpeedAlongWind = Vector2f.dot(windDir, fleet.getVelocity());
2067//                                      //float fleetSpeed = fleet.getVelocity().length();
2068//                                      
2069//                                      windSpeed = Misc.getSpeedForBurnLevel(params.burnLevel);
2070//                                      float matchingWindFraction = fleetSpeedAlongWind/windSpeed;
2071//                                      float effectMag = 1f - matchingWindFraction;
2072//                                      if (effectMag < 0f)  effectMag = 0f;
2073                                        //if (effectMag < 0.25f) effectMag = 0.25f;
2074                                        //effectMag = 0.5f;
2075                                        
2076                                        String modId = "slipstream_" + entity.getId();
2077                                        float durIn = 1f;
2078                                        float durOut = 3f;
2079                                        //durIn = 0.5f;
2080                                        //float sizeNormal = (15f + 30f * effectMag * effectMag) * (intensity);
2081                                        float sizeNormal = 5f + 10f * intensity;
2082                                        for (FleetMemberViewAPI view : fleet.getViews()) {
2083                                                view.getWindEffectDirX().shift(modId, windDir.x * sizeNormal, durIn, durOut, 1f);
2084                                                view.getWindEffectDirY().shift(modId, windDir.y * sizeNormal, durIn, durOut, 1f);
2085                                                view.getWindEffectColor().shift(modId, glowColor, durIn, durOut, 1f);
2086                                        }
2087                                }
2088                        }
2089                }
2090        }
2091        
2092        
2093        public Vector2f getPointAt(float lengthAlongStream, float offset) {
2094                SlipstreamSegment curr = getSegmentForDist(lengthAlongStream);
2095                if (curr == null) return null;
2096                int index = curr.index;
2097                
2098                SlipstreamSegment next = null;
2099                SlipstreamSegment next2 = null;
2100
2101                if (index >= segments.size() - 1) return null;
2102                
2103                if (index % 2 == 0) {
2104                        next = segments.get(index + 1);
2105                        if (index >= segments.size() - 2) {
2106                                next2 = new SlipstreamSegment();
2107                                //next2.width = next.width;
2108                                next2.wobbledWidth = next.wobbledWidth;
2109                                
2110                                next2.normal = next.normal;
2111                                //next2.dir = next.dir;
2112                                next2.loc = new Vector2f(next.dir);
2113                                next2.loc.scale(next.lengthToPrev);
2114                                Vector2f.add(next2.loc, next.loc, next2.loc);
2115                                //next2.locB = next2.loc;
2116                                next2.lengthToPrev = next.lengthToPrev;
2117                        } else {
2118                                next2 = segments.get(index + 2);
2119                        }
2120                }
2121                if (index % 2 != 0) {
2122                        if (index >= segments.size() - 1) return null;
2123                        curr = segments.get(index - 1);
2124                        next = segments.get(index);
2125                        next2 = segments.get(index + 1);
2126                }
2127                
2128                float lenForT = lengthAlongStream - curr.totalLength;
2129                //float t = lenForT / (curr.lengthToNext + next.lengthToNext);
2130                float t = lenForT / (curr.lengthToNext + next2.lengthToPrev);
2131//              if (t < 0) {
2132//                      System.out.println("wefwefe");
2133//              }
2134                
2135                Vector2f p0 = new Vector2f(curr.loc);
2136                Vector2f p1 = new Vector2f(next.locB);
2137                Vector2f p2 = new Vector2f(next2.loc);
2138                
2139//              offset *= 0.7f;
2140//              p0.x += curr.normal.x * curr.width * 0.5f * offset;
2141//              p0.y += curr.normal.y * curr.width * 0.5f * offset;
2142//              
2143//              p2.x += next2.normal.x * next2.width * 0.5f * offset;
2144//              p2.y += next2.normal.y * next2.width * 0.5f * offset;
2145//              
2146//              p1.x += next.normal.x * next.width * 0.5f * offset;
2147//              p1.y += next.normal.y * next.width * 0.5f * offset;
2148                
2149                p0.x += curr.normal.x * curr.wobbledWidth * 0.5f * offset;
2150                p0.y += curr.normal.y * curr.wobbledWidth * 0.5f * offset;
2151                
2152                p2.x += next2.normal.x * next2.wobbledWidth * 0.5f * offset;
2153                p2.y += next2.normal.y * next2.wobbledWidth * 0.5f * offset;
2154                
2155                p1.x += next.normal.x * next.wobbledWidth * 0.5f * offset;
2156                p1.y += next.normal.y * next.wobbledWidth * 0.5f * offset;
2157                
2158                //System.out.println("T: " + t);
2159                Vector2f p = Misc.bezier(p0, p1, p2, t);
2160                
2161                return p;
2162        }
2163        
2164        public Vector2f getNoWobblePointAt(float lengthAlongStream, float offset) {
2165                SlipstreamSegment curr = getSegmentForDist(lengthAlongStream);
2166                if (curr == null) return null;
2167                int index = curr.index;
2168                if (index >= segments.size() - 2) return null;
2169                
2170                SlipstreamSegment next = segments.get(index + 1);
2171                SlipstreamSegment next2 = segments.get(index + 2);
2172                
2173                if (index % 2 != 0) {
2174                        curr = segments.get(index - 1);
2175                        next = segments.get(index);
2176                        next2 = segments.get(index + 1);
2177                }
2178                
2179                float lenForT = lengthAlongStream - curr.totalLength;
2180                float t = lenForT / (curr.lengthToNext + next.lengthToNext);
2181//              if (t < 0) {
2182//                      System.out.println("wefwefe");
2183//              }
2184                
2185                Vector2f p0 = new Vector2f(curr.loc);
2186                Vector2f p1 = new Vector2f(next.locB);
2187                Vector2f p2 = new Vector2f(next2.loc);
2188                
2189                float edges = params.edgeWidth * 2f * 0.5f;
2190                p0.x += curr.normal.x * (curr.width - edges) * 0.5f * offset;
2191                p0.y += curr.normal.y * (curr.width - edges) * 0.5f * offset;
2192                
2193                p2.x += next2.normal.x * (next2.width - edges) * 0.5f * offset;
2194                p2.y += next2.normal.y * (next2.width - edges) * 0.5f * offset;
2195                
2196                p1.x += next.normal.x * (next.width - edges) * 0.5f * offset;
2197                p1.y += next.normal.y * (next.width - edges) * 0.5f * offset;
2198                
2199                Vector2f p = Misc.bezier(p0, p1, p2, t);
2200                
2201                return p;
2202        }
2203        
2204        
2205        public Vector2f getNormalAt(float lengthAlongStream) {
2206                SlipstreamSegment curr = getSegmentForDist(lengthAlongStream);
2207                if (curr == null) return null;
2208                int index = curr.index;
2209                if (index >= segments.size() - 2) return null;
2210                
2211                SlipstreamSegment next = segments.get(index + 1);
2212                SlipstreamSegment next2 = segments.get(index + 2);
2213                
2214                if (index % 2 != 0) {
2215                        curr = segments.get(index - 1);
2216                        next = segments.get(index);
2217                        next2 = segments.get(index + 1);
2218                }
2219                
2220                float lenForT = lengthAlongStream - curr.totalLength;
2221                
2222                float f = lenForT / curr.lengthToNext;
2223                Vector2f perp;
2224                if (f < 1f) {
2225                        perp = Misc.interpolateVector(curr.normal, next.normal, f);
2226                } else {
2227                        f = (lenForT - curr.lengthToNext) / next.lengthToNext;
2228                        perp = Misc.interpolateVector(next.normal, next2.normal, f);
2229                }
2230                return perp;
2231        }
2232        
2233        public List<SlipstreamSegment> getSegmentsNear(Vector2f loc, float range) {
2234                //List<SlipstreamSegment> potential = new ArrayList<SlipstreamEntityPlugin2.SlipstreamSegment>();
2235                List<SlipstreamSegment> result = new ArrayList<SlipstreamEntityPlugin2.SlipstreamSegment>();
2236                int boxIndex = 0;
2237                for (BoundingBox box : bounds) {
2238                        if (box.pointNeedsDetailedCheck(loc, range)) {
2239                                int min = boxIndex * segmentsPerBox;
2240                                for (int i = min; i < min + segmentsPerBox && i < segments.size(); i++) {
2241                                        SlipstreamSegment curr = segments.get(i);
2242                                        float distSq = Misc.getDistanceSq(curr.loc, loc);
2243                                        float r = range + curr.width + Math.max(curr.lengthToPrev, curr.lengthToNext);
2244                                        if (distSq < r * r) {
2245                                                result.add(curr);
2246                                        }
2247                                }
2248                        }
2249                        boxIndex++;
2250                }
2251                
2252//              for (SlipstreamSegment curr : potential) {
2253//                      float distSq = Misc.getDistanceSq(curr.loc, loc);
2254//                      float r = range + curr.width + Math.max(curr.lengthToPrev, curr.lengthToNext);
2255//                      if (distSq < r * r) {
2256//                              result.add(curr);
2257//                      }
2258//              }
2259//              List<SlipstreamSegment> result = new ArrayList<SlipstreamEntityPlugin2.SlipstreamSegment>();
2260//              for (SlipstreamSegment curr : segments) {
2261//                      float distSq = Misc.getDistanceSq(curr.loc, loc);
2262//                      float r = range + curr.width + Math.max(curr.lengthToPrev, curr.lengthToNext);
2263//                      if (distSq < r * r) {
2264//                              result.add(curr);
2265//                      }
2266//              }
2267                return result;
2268        }
2269}
2270
2271
2272
2273
2274