1package com.fs.starfarer.api.impl.campaign.velfield;
3import java.util.ArrayList;
4import java.util.EnumSet;
5import java.util.Iterator;
6import java.util.LinkedHashMap;
7import java.util.LinkedHashSet;
10import java.util.Random;
15import org.lwjgl.input.Mouse;
16import org.lwjgl.opengl.GL11;
17import org.lwjgl.opengl.GL14;
18import org.lwjgl.util.vector.Vector2f;
20import com.fs.starfarer.api.Global;
21import com.fs.starfarer.api.campaign.CampaignEngineLayers;
22import com.fs.starfarer.api.campaign.CampaignFleetAPI;
23import com.fs.starfarer.api.campaign.SectorEntityToken;
24import com.fs.starfarer.api.campaign.TerrainAIFlags;
25import com.fs.starfarer.api.combat.ViewportAPI;
26import com.fs.starfarer.api.fleet.FleetMemberViewAPI;
27import com.fs.starfarer.api.graphics.SpriteAPI;
28import com.fs.starfarer.api.impl.campaign.DebugFlags;
29import com.fs.starfarer.api.impl.campaign.abilities.ReversePolarityToggle;
30import com.fs.starfarer.api.impl.campaign.abilities.SustainedBurnAbility;
31import com.fs.starfarer.api.impl.campaign.ghosts.SensorGhost;
32import com.fs.starfarer.api.impl.campaign.ghosts.SensorGhostManager;
33import com.fs.starfarer.api.impl.campaign.ids.Entities;
34import com.fs.starfarer.api.impl.campaign.ids.Stats;
35import com.fs.starfarer.api.impl.campaign.ids.Tags;
36import com.fs.starfarer.api.impl.campaign.terrain.BaseTerrain;
37import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
38import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin.CellState;
39import com.fs.starfarer.api.loading.Description.Type;
40import com.fs.starfarer.api.ui.TooltipMakerAPI;
41import com.fs.starfarer.api.util.FaderUtil;
42import com.fs.starfarer.api.util.FaderUtil.State;
43import com.fs.starfarer.api.util.Misc;
44import com.fs.starfarer.api.util.MutatingVertexUtil;
45import com.fs.starfarer.api.util.WeightedRandomPicker;
52 public static class SlipstreamParams2 {
53 public String enteringSlipstreamTextOverride =
null;
54 public Float enteringSlipstreamTextDurationOverride =
null;
55 public boolean forceNoWindVisualEffectOnFleets =
false;
56 public String spriteKey1 =
"slipstream0";
57 public String spriteKey2 =
"slipstream1";
58 public String spriteKey3 =
"slipstream2";
59 public String edgeKey =
"slipstream_edge";
60 public Color spriteColor = Color.white;
61 public Color windGlowColor =
new Color(0.65f, 0.5f, 1f, 0.75f);
62 public Color edgeColor = Color.white;
63 public float baseWidth = 768f;
64 public float widthForMaxSpeed = 768f;
65 public float edgeWidth = 256f;
66 public float areaPerParticle = 10000;
67 public int maxParticles = 2000;
68 public float minSpeed;
69 public float maxSpeed;
70 public Color minColor =
new Color(0.5f, 0.3f, 0.75f, 0.1f);
71 public Color maxColor =
new Color(0.5f, 0.6f, 1f, 0.3f);
72 public Color mapColor =
new Color(0.5f, 0.6f, 1f, 1f);
74 public float minDur = 2f;
75 public float maxDur = 6f;
76 public float particleFadeInTime = 1f;
77 public float lineLengthFractionOfSpeed = 0.25f;
79 public int burnLevel = 30;
80 public int maxBurnLevelForTextureScroll = 30;
81 public boolean slowDownInWiderSections =
true;
82 public float widthForMaxSpeedMinMult = 0.67f;
83 public float widthForMaxSpeedMaxMult = 1.5f;
84 public float accelerationMult = 1f;
85 public String name =
null;
87 public float texScrollMult0 = 0f;
88 public float texScrollMult1 = -1.13f;
89 public float texScrollMult2 = -0.28f;
91 Object readResolve() {
92 if (accelerationMult <= 0f) {
93 accelerationMult = 1f;
99 public static class SlipstreamSegment {
100 public Vector2f loc =
new Vector2f();
102 public float bMult = 1f;
104 transient public Vector2f locB =
new Vector2f();
105 transient public Vector2f dir =
new Vector2f();
106 transient public float wobbledWidth;
107 transient public int index = 0;
108 transient public Vector2f normal =
new Vector2f();
109 transient public float tx = 0f;
110 transient public float txe1 = 0f;
111 transient public float txe2 = 0f;
112 transient public float totalLength;
113 transient public float lengthToPrev;
114 transient public float lengthToNext;
120 public boolean discovered =
false;
122 public Object readResolve() {
123 float minRadius = 0f;
124 float maxRadius = width * 0.05f;
125 float rate = maxRadius * 0.5f;
126 float angleRate = 50f;
129 locB =
new Vector2f();
134 public static class SlipstreamParticle {
147 Vector2f r =
new Vector2f();
148 r.x = v.x * cos - v.y * sin;
149 r.y = v.x * sin + v.y * cos;
155 protected SlipstreamParams2
params =
new SlipstreamParams2();
162 protected transient List<SlipstreamParticle>
particles =
new ArrayList<SlipstreamParticle>();
167 transient protected List<BoundingBox>
bounds =
new ArrayList<BoundingBox>();
197 int numNoisePoints = 32;
198 while (numNoisePoints <
segments.size()) {
199 numNoisePoints *= 2f;
201 if (numNoisePoints > 512) numNoisePoints = 512;
203 float spikes = 0.67f;
221 boolean allFadedIn =
true;
222 for (SlipstreamSegment seg :
segments) {
223 allFadedIn &= seg.fader.isFadedIn();
225 float t = (seg.index + 1) / size;
227 if (noise <= f || f >= 1f) {
229 seg.fader.setDurationIn(dur);
247 int numNoisePoints = 32;
248 while (numNoisePoints <
segments.size()) {
249 numNoisePoints *= 2f;
251 if (numNoisePoints > 512) numNoisePoints = 512;
253 float spikes = 0.67f;
276 boolean allFaded =
true;
277 for (SlipstreamSegment seg :
segments) {
278 allFaded &= seg.fader.isFadedOut();
280 float t = (seg.index + 1) / size;
282 if (noise <= f || f >= 1f) {
284 seg.fader.setDurationOut(dur);
295 this.needsRecompute =
true;
299 float minSegmentLength = Float.MAX_VALUE;
300 for (SlipstreamSegment curr :
segments) {
301 if (curr.lengthToNext > 0 && minSegmentLength > curr.lengthToNext) {
302 minSegmentLength = curr.lengthToNext;
305 if (minSegmentLength < 50f) minSegmentLength = 50f;
312 for (
int i = 0; i <
segments.size(); i++) {
313 SlipstreamSegment curr =
segments.get(i);
314 while (lengthSoFar < curr.totalLength + curr.lengthToNext) {
330 SlipstreamSegment segment =
segments.get(segIndex);
331 while (distAlongStream < segment.totalLength) {
333 if (segIndex < 0)
return null;
336 while (distAlongStream > segment.totalLength + segment.lengthToNext) {
338 if (segIndex >=
segments.size())
return null;
345 SlipstreamSegment s =
new SlipstreamSegment();
348 s.wobbledWidth = width - params.edgeWidth * 2f * 0.25f;
350 float minRadius = 0f;
351 float maxRadius = s.width * 0.05f;
352 float rate = maxRadius * 0.5f;
353 float angleRate = 50f;
365 this.params = (SlipstreamParams2) pluginParams;
374 Object readResolve() {
375 particles =
new ArrayList<SlipstreamParticle>();
376 bounds =
new ArrayList<BoundingBox>();
384 super.advance(amount);
417 params.maxBurnLevelForTextureScroll));
421 float texelsPerPixel = 1f;
426 float unitsPerOneTexIter = sprite.
getWidth();
427 float texUnitsPerSecondForSpeed = texSpeed / unitsPerOneTexIter * texelsPerPixel;
454 Vector2f avgLoc =
new Vector2f();
455 for (
int i = 0; i <
segments.size(); i++) {
456 SlipstreamSegment curr =
segments.get(i);
458 Vector2f.add(avgLoc, curr.loc, avgLoc);
475 for (
int i = 0; i <
segments.size(); i++) {
476 SlipstreamSegment prev =
null;
477 if (i > 0) prev =
segments.get(i - 1);
478 SlipstreamSegment curr =
segments.get(i);
479 SlipstreamSegment next =
null;
480 SlipstreamSegment next2 =
null;
481 SlipstreamSegment next3 =
null;
492 if (curr.dir ==
null) curr.dir =
new Vector2f();
493 if (curr.normal ==
null) curr.normal =
new Vector2f();
497 curr.dir.set(prev.dir);
500 Vector2f dir = Vector2f.sub(next.loc, curr.loc,
new Vector2f());
505 Vector2f dir = curr.dir;
506 if (prev ==
null || next ==
null) {
507 curr.normal.set(-dir.y, dir.x);
509 Vector2f avg = Vector2f.add(prev.dir, curr.dir,
new Vector2f());
511 curr.normal.set(-avg.y, avg.x);
517 float texLength = 0f;
518 float e1TexLength = 0f;
519 float e2TexLength = 0f;
521 Vector2f dir2 = Vector2f.sub(curr.loc, prev.loc,
new Vector2f());
522 length = dir2.length();
523 texLength = length / sprite.
getWidth();
525 texLength = Math.min(texLength, sprite.
getHeight() / curr.width);
528 Vector2f edgeCurr =
new Vector2f(curr.loc);
529 edgeCurr.x += curr.normal.x * curr.width * 0.5f;
530 edgeCurr.y += curr.normal.y * curr.width * 0.5f;
532 Vector2f edgePrev =
new Vector2f(prev.loc);
533 edgePrev.x += prev.normal.x * prev.width * 0.5f;
534 edgePrev.y += prev.normal.y * prev.width * 0.5f;
536 float length2 = Vector2f.sub(edgeCurr, edgePrev,
new Vector2f()).length();
540 edgeCurr =
new Vector2f(curr.loc);
541 edgeCurr.x -= curr.normal.x * curr.width * 0.5f;
542 edgeCurr.y -= curr.normal.y * curr.width * 0.5f;
544 edgePrev =
new Vector2f(prev.loc);
545 edgePrev.x -= prev.normal.x * prev.width * 0.5f;
546 edgePrev.y -= prev.normal.y * prev.width * 0.5f;
548 length2 = Vector2f.sub(edgeCurr, edgePrev,
new Vector2f()).length();
558 curr.lengthToPrev = length;
564 prev.lengthToNext = length;
567 if (next !=
null && next2 !=
null && next3 !=
null) {
568 Vector2f p0 = curr.loc;
569 Vector2f p1 = next.loc;
570 Vector2f p2 = next2.loc;
571 Vector2f p3 = next3.loc;
576 float adjustment = Math.min(diff, Math.max(diff * 0.25f, diff - 10f));
577 adjustment = diff * 0.5f;
583 p1Adjusted.scale(dist);
584 Vector2f.add(p1Adjusted, p2, p1Adjusted);
585 next.locB = p1Adjusted;
586 }
else if (next !=
null) {
587 next.locB = next.loc;
590 curr.locB =
new Vector2f(curr.loc);
605 List<SlipstreamSegment> section =
new ArrayList<SlipstreamSegment>();
625 for (
int i = 0; i <
segments.size(); i++) {
626 SlipstreamSegment curr =
segments.get(i);
640 viewRadius = Math.max(6000f, viewRadius);
645 for (
int i = 0; i <
segments.size(); i++) {
649 SlipstreamSegment curr =
segments.get(i);
666 for (
int i = 0; i < near.size(); i++) {
667 SlipstreamSegment curr = near.get(i);
670 curr.fader.
getBrightness() * curr.bMult > 0.05f && curr.bMult > 0f) {
672 curr.loc, curr.width * 0.5f +
params.edgeWidth + 100f,
680 float r1 = 0.5f + (float) Math.random() * 1f;
681 float r2 = 0.5f + (float) Math.random() * 1f;
682 curr.wobble1.
advance(amount * r1);
683 curr.wobble2.
advance(amount * r2);
687 Vector2f p1 =
new Vector2f(curr.loc);
688 Vector2f p2 =
new Vector2f(curr.loc);
689 p1.x += curr.normal.x * curr.width * 0.5f;
690 p1.y += curr.normal.y * curr.width * 0.5f;
691 p2.x -= curr.normal.x * curr.width * 0.5f;
692 p2.y -= curr.normal.y * curr.width * 0.5f;
694 p1.x += curr.wobble1.
vector.x;
695 p1.y += curr.wobble1.
vector.y;
696 p2.x += curr.wobble2.
vector.x;
697 p2.y += curr.wobble2.
vector.y;
703 curr.wobbledWidth = d - params.edgeWidth * 2f * 0.25f;
704 if (curr.wobbledWidth < d * 0.5f) curr.wobbledWidth = d * 0.5f;
707 if (curr.index > 0) {
708 SlipstreamSegment prev =
segments.get(curr.index - 1);
709 Vector2f prev1 =
new Vector2f(prev.loc);
710 Vector2f prev2 =
new Vector2f(prev.loc);
711 prev1.x += prev.normal.x * prev.width * 0.5f;
712 prev1.y += prev.normal.y * prev.width * 0.5f;
713 prev2.x -= prev.normal.x * prev.width * 0.5f;
714 prev2.y -= prev.normal.y * prev.width * 0.5f;
716 float wobbleMult = 0.33f;
718 float maxWobbleRadius = Math.min(prev.width, curr.width) * 0.05f;
721 maxWobble1 = Math.min(maxWobbleRadius, maxWobble1);
722 maxWobble2 = Math.min(maxWobbleRadius, maxWobble2);
724 if (curr.index <
segments.size() - 1) {
725 SlipstreamSegment next =
segments.get(curr.index + 1);
726 Vector2f next1 =
new Vector2f(next.loc);
727 Vector2f next2 =
new Vector2f(next.loc);
728 next1.x += next.normal.x * next.width * 0.5f;
729 next1.y += next.normal.y * next.width * 0.5f;
730 next2.x -= next.normal.x * next.width * 0.5f;
731 next2.y -= next.normal.y * next.width * 0.5f;
732 maxWobbleRadius = Math.min(next.width, curr.width) * 0.05f;
735 maxWobble1 = Math.min(maxWobble1, maxWobble1A);
736 maxWobble2 = Math.min(maxWobble2, maxWobble2A);
739 prev.wobble1.
radius.setMax(maxWobble1);
740 prev.wobble2.
radius.setMax(maxWobble2);
741 curr.wobble1.
radius.setMax(maxWobble1);
742 curr.wobble2.
radius.setMax(maxWobble2);
755 boolean useNewSpawnMethod =
true;
758 if (useNewSpawnMethod) {
761 boolean spawnForAllSegments =
false;
766 viewRadius = Math.max(viewRadius, 10000f);
767 if (!inCurrentLocation) {
773 spawnForAllSegments = dist < 2f;
777 if (inCurrentLocation) {
787 List<SlipstreamSegment> near;
788 if (spawnForAllSegments) {
789 near =
new ArrayList<SlipstreamSegment>(
segments);
793 Set<SlipstreamSegment> nearSet =
new LinkedHashSet<SlipstreamSegment>(near);
797 Iterator<SlipstreamParticle> iter =
particles.iterator();
798 while (iter.hasNext()) {
799 SlipstreamParticle p = iter.next();
802 if (!nearSet.contains(seg)) {
807 List<SlipstreamParticle> list = particleMap.get(seg);
810 particleMap.put(seg, list);
817 float totalArea = 0f;
818 int nearParticles = 0;
823 for (
int i = 0; i < near.size(); i++) {
824 SlipstreamSegment curr = near.get(i);
825 if (curr.lengthToNext <= 0)
continue;
827 float area = curr.lengthToNext * curr.width;
828 float desiredParticles = area /
params.areaPerParticle;
829 if (desiredParticles < 1) desiredParticles = 1;
831 float particlesInSegment = 0;
832 List<SlipstreamParticle> list = particleMap.get(curr);
834 particlesInSegment = list.size();
841 if (veryNearSet.contains(curr)) mult = 10f;
843 float w = desiredParticles - particlesInSegment;
846 segmentPicker.
add(curr, w);
850 nearParticles += particlesInSegment;
854 int numParticlesBasedOnArea = (int) (totalArea /
params.areaPerParticle);
855 int actualDesired = numParticlesBasedOnArea;
856 if (numParticlesBasedOnArea < 10) numParticlesBasedOnArea = 10;
857 if (numParticlesBasedOnArea >
params.maxParticles) numParticlesBasedOnArea =
params.maxParticles;
862 int particlesToAdd = numParticlesBasedOnArea - nearParticles;
866 particlesToAdd = Math.min(particlesToAdd,
params.maxParticles -
particles.size());
869 while (added < particlesToAdd) {
871 SlipstreamSegment seg = segmentPicker.
pick();
872 if (seg ==
null)
continue;
874 SlipstreamParticle p =
new SlipstreamParticle();
875 float fLength = (float) Math.random() * 1f;
876 float fWidth = (float) Math.random() * 2f - 1f;
878 float speed = params.minSpeed + (params.maxSpeed -
params.minSpeed) * (
float) Math.random();
879 float dur = params.minDur + (params.maxDur -
params.minDur) * (
float) Math.random();
883 p.dist = seg.totalLength + seg.lengthToNext * fLength;
891 float speedMult = (0.65f + 0.35f * intensity) * wMult;
892 p.speed *= speedMult;
903 float totalArea = 0f;
904 for (
int i = 0; i <
segments.size(); i++) {
905 SlipstreamSegment curr =
segments.get(i);
906 totalArea += curr.lengthToPrev * curr.width;
909 int numParticlesBasedOnArea = (int) (totalArea /
params.areaPerParticle);
910 if (numParticlesBasedOnArea < 10) numParticlesBasedOnArea = 10;
911 if (numParticlesBasedOnArea >
params.maxParticles) numParticlesBasedOnArea =
params.maxParticles;
921 SlipstreamParticle p =
new SlipstreamParticle();
922 float fLength = (float) Math.random() * 1f;
923 float fWidth = (float) Math.random() * 2f - 1f;
925 float speed = params.minSpeed + (params.maxSpeed -
params.minSpeed) * (
float) Math.random();
926 float dur = params.minDur + (params.maxDur -
params.minDur) * (
float) Math.random();
937 float speedMult = (0.65f + 0.35f * intensity) * wMult;
938 p.speed *= speedMult;
949 Iterator<SlipstreamParticle> iter =
particles.iterator();
950 while (iter.hasNext()) {
951 SlipstreamParticle p = iter.next();
952 p.remaining -= amount;
954 if (p.remaining <= 0) {
959 p.dist += p.speed * amount;
965 if (
params.slowDownInWiderSections) {
968 float width = curr.width;
969 if (
segments.size() > curr.index + 1) {
970 SlipstreamSegment next =
segments.get(curr.index + 1);
971 float f = (distAlong - curr.totalLength) / curr.lengthToNext;
975 mult = Math.min(
params.widthForMaxSpeedMaxMult,
976 params.widthForMaxSpeedMinMult + (1f -
params.widthForMaxSpeedMinMult) *
params.widthForMaxSpeed / width);
986 float width = curr.width;
987 if (
segments.size() > curr.index + 1) {
988 SlipstreamSegment next =
segments.get(curr.index + 1);
989 float f = (distAlong - curr.totalLength) / curr.lengthToNext;
1003 float width = curr.wobbledWidth;
1004 if (
segments.size() > curr.index + 1) {
1005 SlipstreamSegment next =
segments.get(curr.index + 1);
1006 float f = (distAlong - curr.totalLength) / curr.lengthToNext;
1012 return curr.wobbledWidth;
1019 yOff = Math.abs(yOff);
1020 float intensity = 1f;
1022 float dropoffAt = 0.5f;
1024 if (yOff > dropoffAt) {
1025 intensity = 1f - 1f * (yOff - dropoffAt) / (1f - dropoffAt);
1033 if (
segments.size() > curr.index + 1) {
1034 SlipstreamSegment next =
segments.get(curr.index + 1);
1035 float f = (distAlong - curr.totalLength) / curr.lengthToNext;
1066 if (bMult <= 0f)
return;
1068 if (
false &&
builder !=
null) {
1085 if (
true &&
false) {
1087 float mx = Mouse.getX();
1088 float my = Mouse.getY();
1091 boolean inside =
false;
1093 box.renderDebug(1f);
1094 inside |= box.pointNeedsDetailedCheck(
new Vector2f(wmx, wmy));
1097 GL11.glDisable(GL11.GL_TEXTURE_2D);
1098 GL11.glEnable(GL11.GL_BLEND);
1099 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1101 GL11.glPointSize(20f);
1102 GL11.glEnable(GL11.GL_POINT_SMOOTH);
1109 GL11.glBegin(GL11.GL_POINTS);
1110 GL11.glVertex2f(wmx, wmy);
1125 Set<SlipstreamSegment> nearSet =
new LinkedHashSet<SlipstreamSegment>(near);
1127 List<List<SlipstreamSegment>> subsections =
new ArrayList<List<SlipstreamSegment>>();
1128 int prevIndex = -10;
1129 List<SlipstreamSegment> subsection =
new ArrayList<SlipstreamSegment>();
1130 for (SlipstreamSegment seg : near) {
1131 if (prevIndex != seg.index - 1) {
1132 if (subsection !=
null && !subsection.isEmpty()) {
1133 subsections.add(subsection);
1135 subsection =
new ArrayList<SlipstreamSegment>();
1137 subsection.add(seg);
1138 prevIndex = seg.index;
1140 if (subsection !=
null && !subsection.isEmpty()) {
1141 subsections.add(subsection);
1160 for (List<SlipstreamSegment> subsection2 : subsections) {
1208 GL11.glDisable(GL11.GL_TEXTURE_2D);
1209 GL11.glEnable(GL11.GL_BLEND);
1210 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1216 GL11.glLineWidth(Math.max(1f, Math.min(2f, 2f/zoom)));
1218 GL11.glEnable(GL11.GL_LINE_SMOOTH);
1231 for (
float offset = -1f;
false && offset <= 1f; offset += 0.1f) {
1233 GL11.glBegin(GL11.GL_LINE_STRIP);
1235 for (
float len = 0; len <
totalLength; len += incr) {
1288// perp = Misc.interpolateVector(next.normal, next2.normal, f);
1290// perp.scale(offset * params.width * 0.5f);
1293 //p = Misc.interpolateVector(pPrev, p, 0.5f);
1294 //GL11.glVertex2f(p.x + perp.x, p.y + perp.y);
1300 GL11.glVertex2f(p.x, p.y);
1305 for (
int i = 0; i <
segments.size() - 3; i+=2) {
1307 SlipstreamSegment prev =
null;
1311 SlipstreamSegment curr =
segments.get(i);
1312 SlipstreamSegment next =
segments.get(i + 1);
1313 SlipstreamSegment next2 =
segments.get(i + 2);
1314 SlipstreamSegment next3 =
segments.get(i + 3);
1320 Vector2f p0 = curr.loc;
1321 Vector2f p1 = next.loc;
1322 Vector2f p2 = next2.loc;
1323 Vector2f p3 = next3.loc;
1341 p0 =
new Vector2f(p0);
1342 p0.x += curr.normal.x * curr.width * 0.5f * offset;
1343 p0.y += curr.normal.y * curr.width * 0.5f * offset;
1345 p2 =
new Vector2f(p2);
1346 p2.x += next2.normal.x * next2.width * 0.5f * offset;
1347 p2.y += next2.normal.y * next2.width * 0.5f * offset;
1349 p1 =
new Vector2f(next.locB);
1350 p1 =
new Vector2f(p1);
1351 p1.x += next.normal.x * next.width * 0.5f * offset;
1352 p1.y += next.normal.y * next.width * 0.5f * offset;
1368 for (
float len = 0; len < curr.lengthToNext + next.lengthToNext; len += incr) {
1369 float t = len / (curr.lengthToNext + next.lengthToNext);
1376 float f = len / curr.lengthToNext;
1381 f = (len - curr.lengthToNext) / next.lengthToNext;
1384 perp.scale(offset * curr.width * 0.5f);
1388 GL11.glVertex2f(p.x, p.y);
1517 if (place !=
null) {
1519 GL11.glPointSize(40f/zoom);
1520 GL11.glEnable(GL11.GL_POINT_SMOOTH);
1521 GL11.glBegin(GL11.GL_POINTS);
1524 GL11.glVertex2f(p.x, p.y);
1528 GL11.glVertex2f(p.x, p.y);
1532 float withinSeg = place[0] - seg.totalLength;
1533 Vector2f p2 =
new Vector2f(seg.normal.y, -seg.normal.x);
1534 p2.scale(withinSeg);
1535 Vector2f.add(p2, seg.loc, p2);
1536 float width = seg.wobbledWidth;
1537 if (
segments.size() > seg.index + 1) {
1538 SlipstreamSegment next =
segments.get(seg.index + 1);
1540 (place[0] - seg.totalLength) / seg.lengthToNext);
1542 p2.x +=
getNormalAt(place[0]).x * place[1] * width * 0.5f;
1543 p2.y +=
getNormalAt(place[0]).y * place[1] * width * 0.5f;
1545 GL11.glVertex2f(p2.x, p2.y);
1575 boolean curvedTrails =
true;
1576 boolean useTex =
false;
1582 GL11.glDisable(GL11.GL_TEXTURE_2D);
1583 GL11.glEnable(GL11.GL_BLEND);
1584 GL11.glLineWidth(Math.max(1f, Math.min(2f, 2f/zoom)));
1586 GL11.glEnable(GL11.GL_LINE_SMOOTH);
1587 GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
1591 if (!curvedTrails) {
1592 GL11.glBegin(GL11.GL_LINES);
1600 GL11.glEnable(GL11.GL_TEXTURE_2D);
1601 GL11.glEnable(GL11.GL_BLEND);
1602 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1608 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
1611 for (SlipstreamParticle p :
particles) {
1613 if (seg ==
null || !nearSet.contains(seg))
continue;
1619 if (p.remaining <= 0.5f) {
1620 a = p.remaining / 0.5f;
1621 }
else if (p.elapsed <
params.particleFadeInTime) {
1622 a = p.elapsed /
params.particleFadeInTime;
1635 float yPos = p.yPos;
1640 GL11.glBegin(GL11.GL_QUAD_STRIP);
1642 if (curr ==
null || !viewport.
isNearViewport(curr, p.speed *
params.lineLengthFractionOfSpeed + 50f)) {
1647 float incr = p.speed * params.lineLengthFractionOfSpeed / iter;
1649 for (
float i = 0; i < iter; i++) {
1650 float min = incr * 1f;
1651 float dist = p.dist - i * incr - min;
1653 if (next ==
null)
break;
1661 float a1 = a * (iter - i) / (iter - 1);
1662 if (i == 0) a1 = 0f;
1665 GL11.glTexCoord2f(0, 0f);
1666 GL11.glVertex2f(curr.x + perp.x * lw, curr.y + perp.y * lw);
1667 GL11.glTexCoord2f(0, 1f);
1668 GL11.glVertex2f(curr.x - perp.x * lw, curr.y - perp.y * lw);
1673 GL11.glBegin(GL11.GL_LINE_STRIP);
1676 if (curr ==
null || !viewport.
isNearViewport(curr, p.speed *
params.lineLengthFractionOfSpeed + 50f)) {
1681 float incr = p.speed * params.lineLengthFractionOfSpeed / iter;
1682 for (
float i = 0; i < iter; i++) {
1684 float min = incr * 0.5f;
1685 Vector2f next =
getPointAt(p.dist - i * incr - min, yPos);
1691 float a1 = a * (iter - i) / (iter - 1);
1693 if (i == 0) a1 = 0f;
1696 GL11.glVertex2f(curr.x, curr.y);
1704 Vector2f start =
getPointAt(p.dist + p.speed *
params.lineLengthFractionOfSpeed * 0.1f, yPos);
1705 if (start ==
null || !viewport.
isNearViewport(start, 500))
continue;
1708 if (mid ==
null)
continue;
1709 Vector2f end =
getPointAt(p.dist - p.speed *
params.lineLengthFractionOfSpeed * 0.9f, yPos);
1710 if (end ==
null)
continue;
1713 GL11.glVertex2f(start.x, start.y);
1715 GL11.glVertex2f(mid.x, mid.y);
1716 GL11.glVertex2f(mid.x, mid.y);
1718 GL11.glVertex2f(end.x, end.y);
1722 if (!curvedTrails) {
1733 if (forMap)
return 1f;
1738 if (playerFleet !=
null) {
1741 bMult = Math.max(0f, 1f - depth);
1749 SpriteAPI edge,
float alpha, List<SlipstreamSegment>
segments,
float extraTX,
boolean forMap) {
1753 if (bMult <= 0f)
return;
1756 GL11.glEnable(GL11.GL_TEXTURE_2D);
1757 GL11.glEnable(GL11.GL_BLEND);
1760 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1767 boolean wireframe =
false;
1770 GL11.glLineWidth(1f);
1771 GL11.glEnable(GL11.GL_LINE_SMOOTH);
1772 GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
1773 GL11.glDisable(GL11.GL_TEXTURE_2D);
1774 GL11.glDisable(GL11.GL_BLEND);
1777 GL11.glEnable(GL11.GL_LINE_SMOOTH);
1779 GL11.glBegin(GL11.GL_LINE_STRIP);
1780 for (SlipstreamSegment curr :
segments) {
1781 GL11.glVertex2f(curr.loc.x, curr.loc.y);
1786 boolean subtract =
false;
1789 GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
1794 GL11.glEnable(GL11.GL_TEXTURE_2D);
1795 GL11.glEnable(GL11.GL_BLEND);
1799 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1805 GL11.glBegin(GL11.GL_QUAD_STRIP);
1806 for (
int i = 0; i <
segments.size(); i++) {
1807 SlipstreamSegment curr =
segments.get(i);
1809 if (i == 0 || i ==
segments.size() - 1) a = 0f;
1811 Vector2f p1 =
new Vector2f(curr.loc);
1812 p1.x += curr.normal.x * curr.width * 0.5f;
1813 p1.y += curr.normal.y * curr.width * 0.5f;
1814 Vector2f p2 =
new Vector2f(curr.loc);
1815 p2.x -= curr.normal.x * curr.width * 0.5f;
1816 p2.y -= curr.normal.y * curr.width * 0.5f;
1819 p1.x += curr.wobble1.
vector.x;
1820 p1.y += curr.wobble1.
vector.y;
1821 p2.x += curr.wobble2.
vector.x;
1822 p2.y += curr.wobble2.
vector.y;
1826 GL11.glTexCoord2f(curr.tx + extraTX, 0f);
1827 GL11.glVertex2f(p1.x, p1.y);
1828 GL11.glTexCoord2f(curr.tx + extraTX, 1f);
1829 GL11.glVertex2f(p2.x, p2.y);
1836 GL11.glBegin(GL11.GL_QUAD_STRIP);
1837 for (
int i = 0; i <
segments.size(); i++) {
1838 SlipstreamSegment curr =
segments.get(i);
1840 if (i == 0 || i ==
segments.size() - 1) a = 0f;
1842 Vector2f p1 =
new Vector2f(curr.loc);
1843 p1.x += curr.normal.x * curr.width * 0.5f;
1844 p1.y += curr.normal.y * curr.width * 0.5f;
1845 Vector2f p2 =
new Vector2f(curr.loc);
1846 p2.x -= curr.normal.x * curr.width * 0.5f;
1847 p2.y -= curr.normal.y * curr.width * 0.5f;
1849 p1.x += curr.wobble1.
vector.x;
1850 p1.y += curr.wobble1.
vector.y;
1851 p2.x += curr.wobble2.
vector.x;
1852 p2.y += curr.wobble2.
vector.y;
1856 GL11.glVertex2f(p1.x, p1.y);
1858 GL11.glVertex2f(p2.x, p2.y);
1864 GL11.glBegin(GL11.GL_QUAD_STRIP);
1865 for (
int i = 0; i <
segments.size(); i++) {
1866 SlipstreamSegment curr =
segments.get(i);
1868 if (i == 0 || i ==
segments.size() - 1) a = 0f;
1870 Vector2f p1 =
new Vector2f(curr.loc);
1871 p1.x += curr.normal.x * curr.width * 0.5f;
1872 p1.y += curr.normal.y * curr.width * 0.5f;
1873 Vector2f p2 =
new Vector2f(curr.loc);
1874 p2.x -= curr.normal.x * curr.width * 0.5f;
1875 p2.y -= curr.normal.y * curr.width * 0.5f;
1877 p1.x += curr.wobble1.
vector.x;
1878 p1.y += curr.wobble1.
vector.y;
1879 p2.x += curr.wobble2.
vector.x;
1880 p2.y += curr.wobble2.
vector.y;
1884 GL11.glVertex2f(p1.x, p1.y);
1886 GL11.glVertex2f(p2.x, p2.y);
1894 float wobbleMult = 0.5f;
1897 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
1899 GL11.glBegin(GL11.GL_QUAD_STRIP);
1901 for (
int i = 0; i <
segments.size(); i++) {
1902 SlipstreamSegment curr =
segments.get(i);
1904 if (i == 0 || i ==
segments.size() - 1) a = 0f;
1917 Vector2f p1 =
new Vector2f(curr.loc);
1918 Vector2f p2 =
new Vector2f(curr.loc);
1919 p1.x += curr.normal.x * curr.width * 0.5f;
1920 p1.y += curr.normal.y * curr.width * 0.5f;
1921 p2.x += curr.normal.x * (curr.width * 0.5f -
params.edgeWidth);
1922 p2.y += curr.normal.y * (curr.width * 0.5f -
params.edgeWidth);
1928 p1.x += curr.wobble1.vector.x * wobbleMult;
1929 p1.y += curr.wobble1.vector.y * wobbleMult;
1930 p2.x += curr.wobble1.vector.x * wobbleMult;
1931 p2.y += curr.wobble1.vector.y * wobbleMult;
1935 GL11.glTexCoord2f(curr.txe1, 1f);
1936 GL11.glVertex2f(p1.x, p1.y);
1937 GL11.glTexCoord2f(curr.txe1, 0f);
1938 GL11.glVertex2f(p2.x, p2.y);
1943 GL11.glBegin(GL11.GL_QUAD_STRIP);
1945 for (
int i = 0; i <
segments.size(); i++) {
1946 SlipstreamSegment curr =
segments.get(i);
1948 if (i == 0 || i ==
segments.size() - 1) a = 0f;
1950 Vector2f p1 =
new Vector2f(curr.loc);
1951 p1.x -= curr.normal.x * curr.width * 0.5f;
1952 p1.y -= curr.normal.y * curr.width * 0.5f;
1953 Vector2f p2 =
new Vector2f(curr.loc);
1954 p2.x -= curr.normal.x * (curr.width * 0.5f -
params.edgeWidth);
1955 p2.y -= curr.normal.y * (curr.width * 0.5f -
params.edgeWidth);
1958 p1.x += curr.wobble2.vector.x * wobbleMult;
1959 p1.y += curr.wobble2.vector.y * wobbleMult;
1960 p2.x += curr.wobble2.vector.x * wobbleMult;
1961 p2.y += curr.wobble2.vector.y * wobbleMult;
1965 GL11.glTexCoord2f(curr.txe2, 1f);
1966 GL11.glVertex2f(p1.x, p1.y);
1967 GL11.glTexCoord2f(curr.txe2, 0f);
1968 GL11.glVertex2f(p2.x, p2.y);
1974 GL14.glBlendEquation(GL14.GL_FUNC_ADD);
1977 if (wireframe) GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
2011 List<SlipstreamSegment> near =
getSegmentsNear(loc, extraRangeForCheck);
2013 for (SlipstreamSegment curr : near) {
2014 SlipstreamSegment next =
null;
2015 if (
segments.size() > curr.index + 1) {
2016 next =
segments.get(curr.index + 1);
2018 next =
new SlipstreamSegment();
2020 next.wobbledWidth = curr.wobbledWidth;
2022 next.normal = curr.normal;
2024 next.loc =
new Vector2f(curr.dir);
2025 next.loc.scale(curr.lengthToPrev);
2026 Vector2f.add(next.loc, curr.loc, next.loc);
2028 next.lengthToPrev = curr.lengthToPrev;
2033 Vector2f p1 = curr.loc;
2034 Vector2f p2 = next.loc;
2036 Vector2f currNormalP1 =
new Vector2f(curr.loc);
2037 Vector2f currNormalP2 =
new Vector2f(curr.normal);
2038 currNormalP2.scale(100f);
2039 Vector2f.add(currNormalP2, currNormalP1, currNormalP2);
2041 Vector2f nextNormalP1 =
new Vector2f(next.loc);
2042 Vector2f nextNormalP2 =
new Vector2f(next.normal);
2043 nextNormalP2.scale(100f);
2044 Vector2f.add(nextNormalP2, nextNormalP1, nextNormalP2);
2047 Vector2f dir =
new Vector2f(curr.dir);
2049 Vector2f p4 = Vector2f.add(p3, dir,
new Vector2f());
2052 if (currNormalP ==
null)
continue;
2054 if (nextNormalP ==
null)
continue;
2056 float u = (p3.x - currNormalP.x) * (nextNormalP.x - currNormalP.x) +
2057 (p3.y - currNormalP.y) * (nextNormalP.y - currNormalP.y);
2058 float denom = Vector2f.sub(nextNormalP, currNormalP,
new Vector2f()).length();
2060 if (denom == 0)
continue;
2063 if (u >= 0 && u <= 1) {
2065 normalAtP3.scale(100f);
2066 Vector2f p3PlusNormal = Vector2f.add(p3, normalAtP3,
new Vector2f());
2069 if (intersect ==
null)
continue;
2071 float distFromLine = Vector2f.sub(intersect, p3,
new Vector2f()).length();
2072 float width =
Misc.
interpolate(curr.wobbledWidth, next.wobbledWidth, u);
2073 width += extraWidthForSegments;
2074 if (distFromLine >= width / 2f && !allowOutsideStream)
return null;
2076 float [] result =
new float[2];
2078 result[0] = curr.totalLength + u * next.lengthToPrev;
2079 result[1] = distFromLine / (width / 2f);
2084 result[1] = -result[1];
2145 if (offset ==
null) {
2158 float distAlong = offset[0];
2159 float yOff = offset[1];
2173 if (intensity <= 0.05f) {
2188 String text =
"Entering slipstream";
2189 if (
params.enteringSlipstreamTextOverride !=
null) {
2190 text =
params.enteringSlipstreamTextOverride;
2193 if (
params.enteringSlipstreamTextDurationOverride !=
null) {
2194 dur =
params.enteringSlipstreamTextDurationOverride;
2196 if (!text.isEmpty()) {
2211 float maxWindBurn = params.burnLevel * 2f;
2212 float currWindBurn = intensity * maxWindBurn;
2213 float maxFleetBurnIntoWind = maxFleetBurn - Math.abs(currWindBurn);
2220 Vector2f p2 =
getPointAt(distAlong + 1f, yOff);
2221 if (reversePolarity) {
2225 if (p1 ==
null || p2 ==
null) {
2235 if (currWindBurn < 0) {
2242 if (baseFleetAccel < 10f) baseFleetAccel = 10f;
2247 fleetTryingToMove &= (
2252 String key =
"$slipstream_moveToYOffset";
2262 if (desired !=
null) {
2275 float windSpeedReduction = 0f;
2276 if (!fleetTryingToMove) {
2277 Vector2f dest =
new Vector2f(windDir);
2280 float currOffset = offset[1];
2282 float sign = Math.signum(diff);
2283 if (reversePolarity) {
2286 float mult = Math.min(Math.abs(diff) * 1f, 1f);
2294 float dot = Vector2f.dot(windDir, moveDir);
2298 accelBasedMult *= accelBasedMult;
2299 if (accelBasedMult > 1f) accelBasedMult = 1f;
2300 if (accelBasedMult < 0.1f) accelBasedMult = 0.1f;
2307 if (burnBonus < 0) burnBonus = 0;
2310 if (windSpeedReduction > 0) {
2312 Math.max(
params.burnLevel * 0.5f * intensity,
params.burnLevel * intensity - windSpeedReduction));
2315 if (reversePolarity) {
2317 maxSpeedWithWind *= polarityMult;
2322 float fleetSpeedAlongWind = Vector2f.dot(windDir, fleet.
getVelocity());
2323 if (fleetSpeedAlongWind >= maxSpeedWithWind) {
2330 velDir.scale(currFleetBurn);
2337 Vector2f windVector =
new Vector2f(windDir);
2338 windVector.scale(windSpeed);
2361 float accelMult = 0.5f + 2f * intensity;
2362 accelMult += 0.25f * 20f * intensity;
2377 float extraAccelMult =
params.accelerationMult;
2378 windDir.scale(seconds * baseFleetAccel * accelMult * extraAccelMult);
2380 if (extraAccelMult > 1f) {
2381 float windAccelAmountThisFrame = windDir.length();
2382 float maxAccelThisFrame = maxSpeedWithWind - fleetSpeedAlongWind;
2384 if (windAccelAmountThisFrame > maxAccelThisFrame) {
2385 float accelThisFrameMult = maxAccelThisFrame / Math.max(1f, windAccelAmountThisFrame);
2386 if (accelThisFrameMult > 1f) accelThisFrameMult = 1f;
2387 windDir.scale(accelThisFrameMult);
2391 fleet.
setVelocity(vel.x + windDir.x, vel.y + windDir.y);
2399 boolean withGlow =
true;
2400 withGlow = !
params.forceNoWindVisualEffectOnFleets;
2403 Color glowColor =
params.windGlowColor;
2408 int alpha = glowColor.getAlpha();
2416 if (reversePolarity) {
2420 if (p1 !=
null && p2 !=
null) {
2438 float sizeNormal = 5f + 10f * intensity;
2440 view.getWindEffectDirX().shift(modId, windDir.x * sizeNormal, durIn, durOut, 1f);
2441 view.getWindEffectDirY().shift(modId, windDir.y * sizeNormal, durIn, durOut, 1f);
2442 view.getWindEffectColor().shift(modId, glowColor, durIn, durOut, 1f);
2455 if (ghost ==
null)
return;
2465 if (offset ==
null) {
2469 float distAlong = offset[0];
2470 float yOff = offset[1];
2477 if (intensity <= 0) {
2484 float maxWindBurn = params.burnLevel * 2f;
2486 float currWindBurn = intensity * maxWindBurn;
2487 float maxFleetBurnIntoWind = maxFleetBurn - Math.abs(currWindBurn);
2491 Vector2f p2 =
getPointAt(distAlong + 1f, yOff);
2492 if (p1 ==
null || p2 ==
null) {
2497 if (currWindBurn < 0) {
2502 if (baseFleetAccel < 10f) baseFleetAccel = 10f;
2504 velDir.scale(currFleetBurn);
2506 float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
2510 Vector2f windVector =
new Vector2f(windDir);
2511 windVector.scale(windSpeed);
2514 Vector2f diff = Vector2f.sub(windVector, vel,
new Vector2f());
2515 float max = diff.length();
2518 if (diff.length() > max) {
2519 diff.scale(max / diff.length());
2521 float accelMult = 0.5f + 2f * intensity;
2522 if (fleetBurnAgainstWind > maxFleetBurnIntoWind) {
2523 accelMult += 0.25f * (fleetBurnAgainstWind - maxFleetBurnIntoWind);
2525 windDir.scale(seconds * baseFleetAccel * accelMult);
2539 if (offset ==
null) {
2543 float distAlong = offset[0];
2544 float yOff = offset[1];
2551 if (intensity <= 0) {
2555 float maxWindBurn = params.burnLevel * 0.5f;
2556 float currWindBurn = intensity * maxWindBurn;
2559 Vector2f p2 =
getPointAt(distAlong + 1f, yOff);
2560 if (p1 ==
null || p2 ==
null) {
2565 if (currWindBurn < 0) {
2570 Vector2f windVector =
new Vector2f(windDir);
2571 windVector.scale(windSpeed);
2574 other.
getVelocity().set(vel.x * f + windVector.x * (1 - f), vel.y * f + windVector.y * (1 - f));
2578 public Vector2f
getPointAt(
float lengthAlongStream,
float offset) {
2582 if (curr ==
null)
return null;
2583 int index = curr.index;
2585 SlipstreamSegment next =
null;
2586 SlipstreamSegment next2 =
null;
2588 if (index >=
segments.size() - 1)
return null;
2590 if (index % 2 == 0) {
2592 if (index >=
segments.size() - 2) {
2593 next2 =
new SlipstreamSegment();
2595 next2.wobbledWidth = next.wobbledWidth;
2597 next2.normal = next.normal;
2599 next2.loc =
new Vector2f(next.dir);
2600 next2.loc.scale(next.lengthToPrev);
2601 Vector2f.add(next2.loc, next.loc, next2.loc);
2603 next2.lengthToPrev = next.lengthToPrev;
2608 if (index % 2 != 0) {
2609 if (index >=
segments.size() - 1)
return null;
2615 float lenForT = lengthAlongStream - curr.totalLength;
2617 float t = lenForT / (curr.lengthToNext + next2.lengthToPrev);
2622 Vector2f p0 =
new Vector2f(curr.loc);
2623 Vector2f p1 =
new Vector2f(next.locB);
2624 Vector2f p2 =
new Vector2f(next2.loc);
2636 p0.x += curr.normal.x * curr.wobbledWidth * 0.5f * offset;
2637 p0.y += curr.normal.y * curr.wobbledWidth * 0.5f * offset;
2639 p2.x += next2.normal.x * next2.wobbledWidth * 0.5f * offset;
2640 p2.y += next2.normal.y * next2.wobbledWidth * 0.5f * offset;
2642 p1.x += next.normal.x * next.wobbledWidth * 0.5f * offset;
2643 p1.y += next.normal.y * next.wobbledWidth * 0.5f * offset;
2653 if (curr ==
null)
return null;
2654 int index = curr.index;
2655 if (index >=
segments.size() - 2)
return null;
2657 SlipstreamSegment next =
segments.get(index + 1);
2658 SlipstreamSegment next2 =
segments.get(index + 2);
2660 if (index % 2 != 0) {
2666 float lenForT = lengthAlongStream - curr.totalLength;
2667 float t = lenForT / (curr.lengthToNext + next.lengthToNext);
2672 Vector2f p0 =
new Vector2f(curr.loc);
2673 Vector2f p1 =
new Vector2f(next.locB);
2674 Vector2f p2 =
new Vector2f(next2.loc);
2676 float edges = params.edgeWidth * 2f * 0.5f;
2677 p0.x += curr.normal.x * (curr.width - edges) * 0.5f * offset;
2678 p0.y += curr.normal.y * (curr.width - edges) * 0.5f * offset;
2680 p2.x += next2.normal.x * (next2.width - edges) * 0.5f * offset;
2681 p2.y += next2.normal.y * (next2.width - edges) * 0.5f * offset;
2683 p1.x += next.normal.x * (next.width - edges) * 0.5f * offset;
2684 p1.y += next.normal.y * (next.width - edges) * 0.5f * offset;
2694 if (curr ==
null)
return null;
2695 int index = curr.index;
2696 if (index >=
segments.size() - 2)
return null;
2698 SlipstreamSegment next =
segments.get(index + 1);
2699 SlipstreamSegment next2 =
segments.get(index + 2);
2701 if (index % 2 != 0) {
2707 float lenForT = lengthAlongStream - curr.totalLength;
2709 float f = lenForT / curr.lengthToNext;
2714 f = (lenForT - curr.lengthToNext) / next.lengthToNext;
2725 if (box.pointNeedsDetailedCheck(loc, range)) {
2728 SlipstreamSegment curr =
segments.get(i);
2730 float r = range + curr.width + Math.max(curr.lengthToPrev, curr.lengthToNext);
2731 if (distSq < r * r) {
2747 return flag == TerrainAIFlags.BREAK_OTHER_ORBITS ||
2769 boolean doDetailedCheck =
false;
2771 doDetailedCheck |= box.pointNeedsDetailedCheck(point, radius);
2773 if (!doDetailedCheck)
return false;
2776 if (coords ==
null)
return false;
2798 tooltip.
addPara(
"Most slipstreams are temporary, and in recent memory their ebb and flow has been "
2799 +
"unusually synchronized with the standard Domain cycle.", opad);
2801 tooltip.
addPara(
"Fleets traveling inside a slipstream use %s less fuel for the distance covered.",
2805 tooltip.
addPara(
"In addition, traveling at burn levels above %s is even more fuel-efficient. "
2806 +
"For example, a fleet traveling at burn %s will consume half as much fuel "
2807 +
"for the distance it covers.",
2811 tooltip.
addPara(
"These fuel use reductions are not reflected by the fuel range indicator on the map.", opad);
2830 return super.getTooltipWidth();
2835 return "Slipstream";
2839 return "Slipstream";
2843 return "slipstream";
2847 public void renderOnRadar(Vector2f radarCenter,
float factor,
float alphaMult) {
2848 GL11.glPushMatrix();
2849 GL11.glTranslatef(-radarCenter.x * factor, -radarCenter.y * factor, 0);
2850 renderOnMap(factor, alphaMult,
true, radarCenter);
2864 public void renderOnMap(
float factor,
float alphaMult,
boolean forRadar, Vector2f radarCenter) {
2868 Set<SlipstreamSegment> nearSet =
new LinkedHashSet<SlipstreamSegment>();
2872 nearSet =
new LinkedHashSet<SlipstreamSegment>(
getSegmentsNear(radarCenter, radius));
2873 for (SlipstreamSegment curr : nearSet) {
2874 curr.discovered =
true;
2876 if (nearSet.isEmpty())
return;
2879 List<SlipstreamSegment> list =
new ArrayList<SlipstreamSegment>();
2880 int incr = Math.min(
segments.size() / 10, 5);
2882 if (incr < 1) incr = 1;
2883 for (
int i = 0; i <
segments.size(); i+=incr) {
2884 SlipstreamSegment curr =
segments.get(i);
2885 if (forRadar && !nearSet.contains(curr))
continue;
2896 List<List<SlipstreamSegment>> subsections =
new ArrayList<List<SlipstreamSegment>>();
2897 int prevIndex = -10;
2898 List<SlipstreamSegment> subsection =
new ArrayList<SlipstreamSegment>();
2899 for (SlipstreamSegment seg : list) {
2900 if (prevIndex != seg.index - 1) {
2901 if (subsection !=
null && !subsection.isEmpty()) {
2902 subsections.add(subsection);
2904 subsection =
new ArrayList<SlipstreamSegment>();
2906 subsection.add(seg);
2907 prevIndex = seg.index;
2909 if (subsection !=
null && !subsection.isEmpty()) {
2910 subsections.add(subsection);
2913 float texOffset = 0f;
2918 if (fader.
getState() == State.IN) {
2920 }
else if (fader.
getState() == State.OUT) {
2928 GL11.glPushMatrix();
2929 GL11.glScalef(factor, factor, 1f);
2931 for (List<SlipstreamSegment> subsection2 : subsections) {
2959 float widthMult = 1f;
2960 float lengthPerArrowMult = 1f;
2961 float minFactor = 0.012f;
2962 if (factor < minFactor) {
2963 widthMult = minFactor / factor;
2964 lengthPerArrowMult = 2f;
2967 float lengthPerArrow = 700f;
2969 lengthPerArrow *= lengthPerArrowMult;
2970 float start =
segments.get(0).totalLength;
2973 start = (float) (Math.floor(start / lengthPerArrow) * lengthPerArrow);
2974 end = (float) (Math.ceil(end/ lengthPerArrow) * lengthPerArrow);
2975 if (end - start < lengthPerArrow)
return;
2978 Color color =
params.mapColor;
2981 GL11.glDisable(GL11.GL_TEXTURE_2D);
2982 GL11.glEnable(GL11.GL_BLEND);
2983 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
2986 GL11.glEnable(GL11.GL_POLYGON_SMOOTH);
2987 GL11.glHint(GL11.GL_POLYGON_SMOOTH_HINT, GL11.GL_NICEST);
2989 float fadeDist = 500f;
2991 GL11.glBegin(GL11.GL_TRIANGLES);
2992 for (
float len = start; len < end; len += lengthPerArrow) {
2993 Vector2f p0 =
getPointAt(len + phase * lengthPerArrow, 0f);
2994 Vector2f p1 =
getPointAt(len + phase * lengthPerArrow + 10f, 0f);
2995 if (p0 ==
null || p1 ==
null)
continue;
2997 float w =
getWidth(len + phase * lengthPerArrow) * widthMult;
2998 float triLength = lengthPerArrow * 0.33f;
2999 triLength = lengthPerArrow * 1f;
3000 triLength = Math.min(lengthPerArrow, (w + lengthPerArrow) / 2f);
3004 if (len + phase * lengthPerArrow - start < fadeDist) {
3005 a *= (len + phase * lengthPerArrow - start) / fadeDist;
3007 if (len + phase * lengthPerArrow > end - fadeDist) {
3008 a *= (end - (len + phase * lengthPerArrow)) / fadeDist;
3010 if (a <= 0f)
continue;
3015 Vector2f t0 =
getPointAt(len + phase * lengthPerArrow + triLength/2f, 0f);
3016 if (t0 ==
null)
continue;
3019 Vector2f perp =
new Vector2f(-dir.y, dir.x);
3021 Vector2f t1 =
new Vector2f(p0);
3022 Vector2f t2 =
new Vector2f(p0);
3023 Vector2f t3 =
new Vector2f(p0);
3024 float backOffset = 0f;
3026 backOffset = triLength * 0.1f;
3027 t3.x -= dir.x * backOffset;
3028 t3.y -= dir.y * backOffset;
3030 t1.x += perp.x * w/2f;
3031 t1.y += perp.y * w/2f;
3032 t1.x -= dir.x * triLength/2f;
3033 t1.y -= dir.y * triLength/2f;
3035 t2.x -= perp.x * w/2f;
3036 t2.y -= perp.y * w/2f;
3037 t2.x -= dir.x * triLength/2f;
3038 t2.y -= dir.y * triLength/2f;
3050 GL11.glVertex2f(t0.x, t0.y);
3052 GL11.glVertex2f(t1.x, t1.y);
3053 GL11.glVertex2f(t3.x, t3.y);
3056 GL11.glVertex2f(t0.x, t0.y);
3058 GL11.glVertex2f(t2.x, t2.y);
3059 GL11.glVertex2f(t3.x, t3.y);
3084 GL11.glEnable(GL11.GL_TEXTURE_2D);
3085 GL11.glEnable(GL11.GL_BLEND);
3086 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
3093 GL11.glBegin(GL11.GL_QUAD_STRIP);
3094 for (
float len = start; len < end; len += incr) {
3097 if (p0 ==
null || p1 ==
null)
continue;
3100 Vector2f perp =
new Vector2f(-dir.y, dir.x);
3103 Vector2f p2 =
new Vector2f(p0);
3104 Vector2f p3 =
new Vector2f(p0);
3105 p2.x += perp.x * w * 0.5f;
3106 p2.y += perp.y * w * 0.5f;
3107 p3.x -= perp.x * w * 0.5f;
3108 p3.y -= perp.y * w * 0.5f;
3111 if (len - start < fadeDist) {
3112 a *= (len - start) / fadeDist;
3114 if (len > end - fadeDist) {
3115 a *= (end - len) / fadeDist;
3119 GL11.glTexCoord2f(0f, 0f);
3120 GL11.glVertex2f(p2.x, p2.y);
3121 GL11.glTexCoord2f(0f, 1f);
3122 GL11.glVertex2f(p3.x, p3.y);
3126 GL11.glDisable(GL11.GL_POLYGON_SMOOTH);
3137 float outerPlaybackRange = (float)
getSpec().
getCustom().optDouble(
"outsideSoundRange", 1000f);
3140 float innerVolume = 0f;
3141 float innerPitch = 1f;
3142 float outerVolume = 0f;
3143 float outerPitch = 1f;
3145 SlipstreamSegment segment =
null;
3146 List<SlipstreamSegment> near =
getSegmentsNear(loc, outerPlaybackRange + 2000f);
3147 float pointProximityOuterVolume = 0f;
3148 for (SlipstreamSegment curr : near) {
3150 float check = curr.wobbledWidth / 2f + outerPlaybackRange;
3152 float volume = 1f - dist / check;
3153 volume *= curr.bMult * curr.fader.getBrightness();
3154 if (volume > pointProximityOuterVolume) {
3155 pointProximityOuterVolume = volume;
3163 outerVolume = pointProximityOuterVolume;
3166 float f = Math.abs(coords[1]);
3170 float minPitch = (float)
getSpec().
getCustom().optDouble(
"minPitch", 0.5f);
3171 float maxPitch = (float)
getSpec().
getCustom().optDouble(
"maxPitch", 1.25f);
3173 innerVolume = 0f + 1f * (Math.min(1f, intensity * 2f) * wMult);
3174 innerPitch = minPitch + (1f - minPitch) * intensity * wMult;
3175 if (innerPitch > maxPitch) innerPitch = maxPitch;
3177 if (intensity >= 0.5f) {
3181 float distFromStream = 0f;
3182 distFromStream =
getWidth(coords[0]) * 0.5f * (f - 1f);
3183 if (distFromStream < outerPlaybackRange) {
3184 float intensity = 1f - distFromStream / outerPlaybackRange;
3185 outerVolume = 0f + 1f * (intensity * wMult);
3189 innerVolume *= fMult;
3190 outerVolume *= fMult;
3191 outerVolume = Math.max(outerVolume, pointProximityOuterVolume);
3193 outerVolume = Math.min(outerVolume, 1f - innerVolume);
3196 if (innerVolume < 0) innerVolume = 0;
3197 if (innerVolume > 1) innerVolume = 1;
3198 if (outerVolume > 1) outerVolume = 1;
3199 if (outerVolume < 0) outerVolume = 0;
3209 float loopFade = 0.5f;
3213 float filterMult = innerVolume;
3214 if (innerVolume > 0f) {
3220 }
else if (outerVolume > 0.5f) {
3221 filterMult = Math.min(1f, (outerVolume - 0.5f) * 4f);
3224 if (innerVolume > 0) {
3229 Math.max(0f, 1f - (1f - gain) * innerVolume),
3230 Math.max(0f, 1f - Math.min(1f - gainHF, innerVolume)));
3235 if (soundId !=
null && innerVolume > 0f) {
3240 if (soundId !=
null && outerVolume > 0f) {
3242 if (segment !=
null) playbackLoc = segment.loc;
3247 float suppressionMult = innerVolume;
3248 suppressionMult = filterMult;
3275 List<List<SlipstreamSegment>> sections =
new ArrayList<List<SlipstreamSegment>>();
3277 boolean currSectionIsBreak =
false;
3278 List<SlipstreamSegment> list =
new ArrayList<SlipstreamSegment>();
3279 for (
int i = 0; i <
segments.size(); i++) {
3280 SlipstreamSegment curr =
segments.get(i);
3281 boolean currSegmentIsBreak = curr.bMult <= 0f;
3282 if (list.isEmpty()) {
3283 currSectionIsBreak = currSegmentIsBreak;
3285 if (currSectionIsBreak == currSegmentIsBreak) {
3288 if (!list.isEmpty()) {
3291 list =
new ArrayList<SlipstreamSegment>();
3296 boolean prevSectionWasLongEnough =
false;
3297 for (List<SlipstreamSegment> section : sections) {
3298 boolean sectionIsBreak = section.get(0).bMult <= 0;
3299 float sectionLength = section.get(section.size() - 1).totalLength - section.get(0).totalLength;
3301 if (sectionIsBreak && prevSectionWasLongEnough && sectionLength >= 1000f) {
3302 Vector2f loc =
new Vector2f(section.get(0).loc);
3303 Vector2f dir =
new Vector2f(section.get(0).dir);
3304 dir.scale(Math.min(1000f, sectionLength * 0.4f));
3305 Vector2f.add(dir, loc, loc);
3309 if (!sectionIsBreak && section.size() >= 10f) {
3310 prevSectionWasLongEnough =
true;
3312 prevSectionWasLongEnough =
false;
3315 if (prevSectionWasLongEnough) {
3316 List<SlipstreamSegment> section = sections.get(sections.size() - 1);
3317 Vector2f loc =
new Vector2f(section.get(section.size() - 1).loc);
3318 Vector2f dir =
new Vector2f(section.get(section.size() - 1).dir);
3320 Vector2f.add(dir, loc, loc);
static SettingsAPI getSettings()
static SoundPlayerAPI getSoundPlayer()
static SectorAPI getSector()
static boolean USE_SLIPSTREAM_VISIBILITY_IN_DEBUG_MODE
static boolean SLIPSTREAM_DEBUG
static String POLARITY_SPEED_MULT
static String POLARITY_WIND_GLOW_COLOR_KEY
static SensorGhost getGhostFor(SectorEntityToken entity)
static final String WRECK
static final String FUEL_USE_NOT_SHOWN_ON_MAP_MULT
void preventOtherTerrainFromAffecting(SectorEntityToken other)
boolean isPreventedFromAffecting(SectorEntityToken other)
void setTileState(Vector2f loc, float radius, CellState state, float waitDur, float signalDur)
static BoundingBox create(List< SlipstreamSegment > segments)
static void normalizeNoise1D(float[] noise)
static void genNoise1D(Random random, float[] noise, int size, float spikes)
static float getInterpNoise(float[] noise, float t)
static float[] initNoise1D(Random random, int size, float spikes)
void renderDebug(float alpha)
String getNameForTooltip()
void renderOnMap(float factor, float alphaMult)
Vector2f getPointAt(float lengthAlongStream, float offset)
List< BoundingBox > getBounds()
void doSoundPlayback(float amount)
boolean shouldCheckFleetsToApplyEffect()
float getAbyssalBMult(boolean forMap)
boolean shouldPlayLoopTwo()
static Vector2f rotateAroundOrigin(Vector2f v, float cos, float sin)
void renderOnMap(float factor, float alphaMult, boolean forRadar, Vector2f radarCenter)
SlipstreamTerrainPlugin2()
Vector2f getNormalAt(float lengthAlongStream)
transient List< SlipstreamParticle > particles
Vector2f getNoWobblePointAt(float lengthAlongStream, float offset)
float playerDesiredYOffset
void advanceSpawn(float amount)
void applyEffectToGhost(SectorEntityToken other, float days)
float getWidthBasedSpeedMult(float distAlong)
void renderOnRadar(Vector2f radarCenter, float factor, float alphaMult)
boolean containsEntity(SectorEntityToken other)
List< SlipstreamSegment > segments
float getWidth(float distAlong)
void addSegment(Vector2f loc, float width)
boolean hasAIFlag(Object flag)
float getWobbledWidth(float distAlong)
void render(CampaignEngineLayers layer, ViewportAPI viewport)
void advanceParticles(float amount)
EnumSet< CampaignEngineLayers > getActiveLayers()
float getIntensity(float yOff)
void spawn(float spawnDays, Random random)
SlipstreamParams2 getParams()
void setDynamic(boolean dynamic)
void applyEffectToEntities(float amount)
boolean containsPoint(Vector2f point, float radius)
void despawn(float despawnDelay, float despawnDays, Random random)
transient int[] lengthToIndexMap
void applyEffectToWreck(SectorEntityToken other, float days)
void advanceDespawn(float amount)
transient int lengthDivisor
void renderSegmentsForMap(List< SlipstreamSegment > segments, float factor, float alphaMult, boolean forRadar, float phase)
float getFaderBrightness(float distAlong)
SlipstreamSegment getSegmentForDist(float distAlongStream)
transient List< BoundingBox > bounds
List< SlipstreamSegment > getSegments()
SlipstreamBuilder getBuilder()
transient SlipstreamBuilder builder
String getEffectCategory()
void init(String terrainId, SectorEntityToken entity, Object pluginParams)
void recomputeEncounterPoints()
static String FUEL_USE_MODIFIER_DESC
boolean isTooltipExpandable()
transient List< Vector2f > encounterPoints
float[] getLengthAndWidthFractionWithinStream(Vector2f loc, float extraRangeForCheck, boolean allowOutsideStream, float extraWidthForSegments)
static float FUEL_USE_MULT
void advanceNearbySegments(float amount)
List< SlipstreamSegment > getSegmentsNear(Vector2f loc, float range)
void setBuilder(SlipstreamBuilder builder)
void applyEffect(SectorEntityToken other, float days)
List< Vector2f > getEncounterPoints()
boolean shouldPlayLoopOne()
void playerNoLongerinSlipstream()
void updateLengthToIndexMap()
void updateBoundingBoxes()
void advance(float amount)
static int MAX_PARTICLES_ADD_PER_FRAME
void createTooltip(TooltipMakerAPI tooltip, boolean expanded)
void renderSegments(SpriteAPI sprite0, SpriteAPI sprite1, SpriteAPI sprite2, SpriteAPI edge, float alpha, List< SlipstreamSegment > segments, float extraTX, boolean forMap)
float[] getLengthAndWidthFractionWithinStream(Vector2f loc)
int playerWasInSlipstreamFramesAgo
void advance(float amount)
static float getDistanceSq(Vector2f v1, Vector2f v2)
static Vector2f getUnitVectorAtDegreeAngle(float degrees)
static Vector2f bezier(Vector2f p0, Vector2f p1, Vector2f p2, float t)
static void setColor(Color color)
static Color setAlpha(Color color, int alpha)
static Vector2f rotateAroundOrigin(Vector2f v, float angle)
static final Vector2f ZERO
static void fadeAndExpire(SectorEntityToken entity)
static Vector2f intersectLines(Vector2f a1, Vector2f a2, Vector2f b1, Vector2f b2)
static float getAngleDiff(float from, float to)
static float getDistance(SectorEntityToken from, SectorEntityToken to)
static boolean isReversePolarity(SectorEntityToken entity)
static Vector2f getUnitVector(Vector2f from, Vector2f to)
static float getClosestTurnDirection(float facing, float desired)
static float getDistanceToPlayerLY(Vector2f locInHyper)
static CampaignTerrainAPI getHyperspaceTerrain()
static Color getHighlightColor()
static float interpolate(float from, float to, float progress)
static Vector2f interpolateVector(Vector2f from, Vector2f to, float progress)
static float getAbyssalDepth(Vector2f loc)
static Color interpolateColor(Color from, Color to, float progress)
static float getSpeedForBurnLevel(float burnLevel)
static float getAngleInDegrees(Vector2f v)
static Vector2f normalise(Vector2f v)
void advance(float amount)
Description getDescription(String id, Type type)
boolean isCampaignSensorsOn()
boolean getBoolean(String key)
float getFloat(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)
float convertToDays(float realSeconds)
float convertToSeconds(float days)
void setVelocity(float x, float y)
List< FleetMemberViewAPI > getViews()
void setMoveDestination(float x, float y)
Vector2f getMoveDestination()
FleetDataAPI getFleetData()
MutableFleetStatsAPI getStats()
CampaignTerrainPlugin getPlugin()
void suppressMusic(float maxLevel)
FaderUtil getSharedFader()
List< CustomCampaignEntityAPI > getCustomEntities()
List< CampaignFleetAPI > getFleets()
CampaignFleetAPI getPlayerFleet()
CampaignClockAPI getClock()
ViewportAPI getViewport()
CampaignUIAPI getCampaignUI()
LocationAPI getContainingLocation()
boolean isInCurrentLocation()
String getCustomEntityType()
Color getIndicatorColor()
void addFloatingText(String text, Color color, float duration)
void setLocation(float x, float y)
boolean hasTag(String tag)
Vector2f getLocationInHyperspace()
MemoryAPI getMemoryWithoutUpdate()
float getFloat(String key)
boolean contains(String key)
void set(String key, Object value)
boolean getBoolean(String key)
boolean isNearViewport(Vector2f loc, float nearDistance)
float convertScreenXToWorldX(float x)
float convertScreenYToWorldY(float y)
DynamicStatsAPI getDynamic()
void addTemporaryModMult(float durInDays, String source, String desc, float value, StatBonus stat)
void setColor(Color color)
SmoothMovementUtil getMovement()
MutableStat getStat(String id)