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