1package com.fs.starfarer.api.impl.campaign.velfield;
3import java.util.ArrayList;
4import java.util.HashSet;
5import java.util.Iterator;
6import java.util.LinkedHashMap;
7import java.util.LinkedHashSet;
10import java.util.Random;
13import org.lwjgl.util.vector.Vector2f;
15import com.fs.starfarer.api.EveryFrameScript;
16import com.fs.starfarer.api.Global;
17import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
18import com.fs.starfarer.api.campaign.JumpPointAPI;
19import com.fs.starfarer.api.campaign.LocationAPI;
20import com.fs.starfarer.api.campaign.NascentGravityWellAPI;
21import com.fs.starfarer.api.campaign.SectorEntityToken;
22import com.fs.starfarer.api.campaign.StarSystemAPI;
23import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
24import com.fs.starfarer.api.impl.campaign.DebugFlags;
25import com.fs.starfarer.api.impl.campaign.ids.Tags;
26import com.fs.starfarer.api.impl.campaign.ids.Terrain;
27import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceAbyssPlugin;
28import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
29import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamBuilder.StreamType;
30import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2;
31import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment;
32import com.fs.starfarer.api.impl.campaign.world.TTBlackSite;
33import com.fs.starfarer.api.util.CollisionGridUtil;
34import com.fs.starfarer.api.util.IntervalUtil;
35import com.fs.starfarer.api.util.Misc;
36import com.fs.starfarer.api.util.WeightedRandomPicker;
61 public static Map<String, Float>
STREAM_CONFIGS =
new LinkedHashMap<String, Float>();
221 for (
int j = 0; j <
HEIGHT; j++) {
222 out = in.substring(j *
WIDTH, (j + 1) *
WIDTH) + out;
232 for (
int j = 0; j <
HEIGHT; j++) {
233 out = out +
new StringBuilder(in.substring(j *
WIDTH, (j + 1) *
WIDTH)).reverse();
242 throw new RuntimeException(
"Length of slipstream config not WIDTHxHEIGHT [" + key +
"]");
245 new StreamConfig(key,
null);
246 }
catch (Throwable t) {
247 throw new RuntimeException(
"Error parsing slipstream config [" + key +
"]", t);
253 public static class StreamData {
259 public int controlX = -1;
260 public int controlY = -1;
261 public int control2X = -1;
262 public int control2Y = -1;
263 public int burnMod = 0;
264 public boolean reverse =
false;
265 public StreamType type = StreamType.NORMAL;
266 public boolean wasUsed =
false;
267 public boolean straight =
false;
268 public boolean priority =
false;
269 public boolean onlyKeepLongestSegment =
false;
270 public boolean randomize =
false;
271 public boolean minorRandomize =
false;
273 public StreamData(String
id) {
276 public Vector2f generateP0(Random
random) {
277 if (p0x < 0)
return null;
278 return generate(p0x, p0y,
random);
280 public Vector2f generateP1(Random
random) {
281 if (p1x < 0)
return null;
282 return generate(p1x, p1y,
random);
284 public Vector2f generateControl(Random
random) {
285 if (controlX < 0)
return null;
286 return generate(controlX, controlY,
random);
288 public Vector2f generateControl2(Random
random) {
289 if (control2X < 0)
return null;
290 return generate(control2X, control2Y,
random);
292 public Vector2f generate(
int cellX,
int cellY, Random
random) {
295 float cellWidth = sw /
WIDTH;
296 float cellHeight = sh /
HEIGHT;
297 Vector2f p =
new Vector2f();
301 float minX = -sw/2f + cellWidth * cellX;
302 float maxX = -sw/2f + cellWidth * (cellX + 1);
303 float minY = -sh/2f + cellHeight * cellY;
304 float maxY = -sh/2f + cellHeight * (cellY + 1);
306 p.x = minX + (maxX - minX) *
random.nextFloat();
307 p.y = minY + (maxY - minY) *
random.nextFloat();
317 public static class StreamConfig {
319 public List<StreamData> streams =
new ArrayList<StreamData>();
321 public StreamConfig(String data, Random
random) {
323 Set<String> ids =
new LinkedHashSet<String>();
324 for (
int i = 0; i < data.length(); i++) {
325 char c = data.charAt(i);
326 if (c ==
'X')
continue;
327 if (Character.isUpperCase(c)) {
332 for (String
id : ids) {
333 StreamData curr =
new StreamData(
id);
334 for (
int i = 0; i < data.length(); i++) {
335 char c = data.charAt(i);
336 if (c ==
'X')
continue;
338 int cellX = i %
WIDTH;
340 if (
id.equals(
"" + c)) {
344 }
else if (curr.p1x < 0) {
348 }
else if (
id.toLowerCase().equals(
"" + c)) {
349 if (curr.controlX < 0) {
350 curr.controlX = cellX;
351 curr.controlY = cellY;
353 curr.control2X = cellX;
354 curr.control2Y = cellY;
358 if (
id.equals(
"" + c)) {
359 for (
int j = i + 1; j < data.length() && j < i + 10; j++) {
360 char c2 = data.charAt(j);
361 if (c2 ==
' ' || Character.isAlphabetic(c2))
break;
364 }
else if (c2 ==
'~') {
365 curr.type = StreamType.NARROW;
366 }
else if (c2 ==
'=') {
367 curr.type = StreamType.WIDE;
368 }
else if (c2 ==
'-') {
369 int burnMod = data.charAt(j + 1) -
'0';
370 if (burnMod == 0) burnMod = 10;
371 curr.burnMod = -1 * burnMod;
372 }
else if (c2 ==
'+') {
373 int burnMod = data.charAt(j + 1) -
'0';
374 if (burnMod == 0) burnMod = 10;
375 curr.burnMod = burnMod;
376 }
else if (c2 ==
'|') {
377 curr.straight =
true;
378 }
else if (c2 ==
'!') {
379 curr.priority =
true;
380 }
else if (c2 ==
'^') {
381 curr.onlyKeepLongestSegment =
true;
382 }
else if (c2 ==
'*') {
383 curr.randomize =
true;
384 }
else if (c2 ==
'?') {
385 curr.minorRandomize =
true;
390 if (curr.p0x >= 0 || curr.p1x >= 0) {
391 if ((curr.randomize || curr.minorRandomize) &&
random !=
null) {
392 if (!curr.reverse && !curr.minorRandomize) {
393 curr.reverse =
random.nextFloat() < 0.5f;
395 if (!curr.straight) {
396 curr.straight =
random.nextFloat() < 0.25f;
398 if (curr.burnMod == 0) {
400 if (curr.minorRandomize) {
401 curr.burnMod =
random.nextInt(6);
403 curr.burnMod =
random.nextInt(11);
406 if (curr.type == StreamType.NORMAL) {
407 float r =
random.nextFloat();
409 curr.type = StreamType.NARROW;
410 }
else if (r < 0.4f) {
411 curr.type = StreamType.WIDE;
422 public static class AddedStream {
423 public CampaignTerrainAPI terrain;
424 public SlipstreamTerrainPlugin2 plugin;
425 public Vector2f from;
427 public Vector2f control;
428 public long timestamp;
429 public AddedStream(SlipstreamTerrainPlugin2 plugin) {
430 this.plugin = plugin;
431 terrain = (CampaignTerrainAPI) plugin.getEntity();
432 from =
new Vector2f(plugin.getSegments().get(0).loc);
433 to =
new Vector2f(plugin.getSegments().get(plugin.getSegments().size() - 1).loc);
434 timestamp = Global.getSector().getClock().getTimestamp();
476 Iterator<AddedStream> iter =
active.iterator();
477 while (iter.hasNext()) {
478 AddedStream curr = iter.next();
479 if (!curr.terrain.isAlive()) {
486 if (month == 6 || month == 12) {
487 for (AddedStream curr :
active) {
488 if (curr.plugin.isDespawning())
continue;
489 float despawnDelay = 0f +
random.nextFloat() * 20f;
490 float timeMinusDelay = 27f - despawnDelay;
491 float despawnDays = timeMinusDelay * 0.5f +
random.nextFloat() * timeMinusDelay * 0.5f;
492 curr.plugin.despawn(despawnDelay, despawnDays,
random);
498 }
else if (month != 6 && month != 12) {
516 String data = picker.
pick();
532 if (
config ==
null)
return;
540 for (StreamData data :
config.streams) {
541 if (data.wasUsed)
continue;
542 if (!data.priority)
continue;
547 for (StreamData data :
config.streams) {
548 if (data.wasUsed)
continue;
553 StreamData data = picker.
pick();
554 if (data ==
null)
return;
556 SlipstreamParams2 params =
new SlipstreamParams2();
557 params.burnLevel = 30 + data.burnMod;
560 params.lineLengthFractionOfSpeed = 0.25f * Math.max(0.25f, Math.min(1f, 30f / (
float) params.burnLevel));
563 Vector2f from = data.generateP0(
random);
564 Vector2f to = data.generateP1(
random);
565 Vector2f control = data.generateControl(
random);
566 Vector2f control2 = data.generateControl2(
random);
567 if (from ==
null || to ==
null)
return;
571 if (month == 12 || month < 6) {
573 if ((!data.reverse && from.x > to.x) || (data.reverse && from.x < to.x)) {
579 if ((!data.reverse && from.x < to.x) || (data.reverse && from.x > to.x)) {
600 if (control2 !=
null) {
604 Vector2f temp = control2;
609 }
else if (control !=
null) {
622 float spawnDays = 1f + 2f *
random.nextFloat();
630 AddedStream added =
new AddedStream(plugin);
640 List<SlipstreamSegment> segments = plugin.
getSegments();
642 Set<SlipstreamSegment> otherStreamCuts =
new HashSet<SlipstreamSegment>();
644 for (SlipstreamSegment curr : segments) {
647 while (iter.hasNext()) {
648 Object obj = iter.next();
658 Vector2f diff = Vector2f.sub(loc, curr.loc,
new Vector2f());
660 float distPerp = Math.abs(Vector2f.dot(curr.normal, diff));
661 float distAlong = Math.abs(Vector2f.dot(curr.dir, diff));
666 float minDistAlong = Math.max(curr.lengthToNext, curr.lengthToPrev);
667 float fadeDistAlong = 500f + minDistAlong;
668 if (distPerp < curr.width / 2f &&
669 distAlong < fadeDistAlong) {
670 if (distAlong < minDistAlong) {
671 curr.fader.forceOut();
674 curr.bMult = Math.min(curr.bMult,
675 (distAlong - minDistAlong) / (fadeDistAlong - minDistAlong));
681 if (otherPlugin == plugin)
continue;
683 for (SlipstreamSegment other : otherPlugin.
getSegmentsNear(curr.loc, curr.width / 2f)) {
685 if (other.bMult <= 0)
continue;
688 float minDist = curr.width / 2f + other.width / 2f;
689 float fadeDist = minDist + 500f;
690 if (dist < fadeDist) {
691 if (dist < minDist) {
692 curr.fader.forceOut();
694 otherStreamCuts.add(curr);
696 curr.bMult = Math.min(curr.bMult,
697 (dist - minDist) / (fadeDist - minDist));
701 }
else if (obj instanceof CustomStreamBlocker) {
702 CustomStreamBlocker blocker = (CustomStreamBlocker) obj;
703 Vector2f loc = blocker.loc;
704 float radius = blocker.radius;
706 Vector2f diff = Vector2f.sub(loc, curr.loc,
new Vector2f());
707 float distPerp = Math.abs(Vector2f.dot(curr.normal, diff));
708 float distAlong = Math.abs(Vector2f.dot(curr.dir, diff));
713 float minDistAlong = Math.max(curr.lengthToNext, curr.lengthToPrev);
714 float fadeDistAlong = 500f + minDistAlong;
715 if (distPerp < curr.width / 2f &&
716 distAlong < fadeDistAlong) {
717 if (distAlong < minDistAlong) {
718 curr.fader.forceOut();
721 curr.bMult = Math.min(curr.bMult,
722 (distAlong - minDistAlong) / (fadeDistAlong - minDistAlong));
725 }
else if (obj instanceof AbyssStreamBlocker) {
726 AbyssStreamBlocker abyss = (AbyssStreamBlocker) obj;
727 if (abyss.containsPoint(curr.loc)) {
728 curr.fader.forceOut();
743 if (onlyKeepLongestBetweenStreams) {
748 for (SlipstreamSegment curr : segments) {
749 if (otherStreamCuts.contains(curr)) {
750 if (currList.size() > longest.size()) {
753 currList =
new ArrayList<SlipstreamSegment>();
755 if (curr.bMult > 0f) {
759 if (currList.size() > longest.size()) {
762 for (SlipstreamSegment curr : segments) {
763 if (!longest.contains(curr)) {
765 curr.fader.forceOut();
774 float minRunLength = minLength;
775 List<SlipstreamSegment> currRun =
new ArrayList<SlipstreamSegment>();
776 for (SlipstreamSegment curr : segments) {
777 if (curr.bMult <= 0f) {
778 float runLength = 0f;
779 for (SlipstreamSegment inRun : currRun) {
780 runLength += inRun.lengthToNext;
782 if (runLength < minRunLength) {
783 for (SlipstreamSegment inRun : currRun) {
784 inRun.fader.forceOut();
796 List<SlipstreamSegment> currRun =
new ArrayList<SlipstreamSegment>();
797 boolean currRunReachedZero =
false;
798 for (SlipstreamSegment curr : segments) {
799 if (curr.bMult < 1f) {
801 if (curr.bMult <= 0f || (curr.fader.getBrightness() == 0 && !curr.fader.isFadingIn())) {
802 currRunReachedZero =
true;
805 if (!currRunReachedZero) {
806 for (SlipstreamSegment inRun : currRun) {
807 inRun.fader.fadeIn();
812 currRunReachedZero =
false;
819 public static class CustomStreamRevealer
extends CustomStreamBlocker {
820 public CustomStreamRevealer(Vector2f loc,
float radius) {
825 public static class CustomStreamBlocker {
828 public CustomStreamBlocker(Vector2f loc,
float radius) {
829 this.loc =
new Vector2f(loc);
830 this.radius = radius;
833 public static class AbyssStreamBlocker {
834 public HyperspaceAbyssPlugin plugin;
835 public AbyssStreamBlocker() {
836 CampaignTerrainAPI terrain = Misc.getHyperspaceTerrain();
837 if (terrain !=
null) {
838 this.plugin = ((HyperspaceTerrainPlugin) terrain.getPlugin()).getAbyssPlugin();
841 public boolean containsPoint(Vector2f loc) {
842 if (plugin ==
null)
return false;
843 return plugin.getAbyssalDepth(loc) > 0;
852 if (
grid !=
null)
return;
856 float minCellSize = 12000f;
857 float cellSize = Math.max(minCellSize, sw * 0.05f);
863 float size = jp.getRadius() * 2f + 100f;
876 CustomStreamBlocker blocker =
new CustomStreamBlocker(well.
getLocation(), size);
882 Vector2f loc = system.getLocation();
884 CustomStreamBlocker blocker =
new CustomStreamBlocker(loc, size);
896 AbyssStreamBlocker orionPersusAbyssBlocker =
new AbyssStreamBlocker();
897 grid.
addObject(orionPersusAbyssBlocker,
new Vector2f(), w, h);
913 int segmentsToSkip = (int) ((cellSize - 2000) / 400f);
914 float checkSize = minCellSize - 2000f;
919 List<SlipstreamSegment> segments = plugin.
getSegments();
921 for (
int i = 0; i < segments.size(); i += segmentsToSkip) {
922 check.add(segments.get(i));
924 if (!check.contains(segments.get(segments.size() - 1))) {
925 check.add(segments.get(segments.size() - 1));
928 for (SlipstreamSegment seg : check) {
static SettingsAPI getSettings()
static SectorAPI getSector()
static void updateSlipstreamBlockers(CollisionGridAPI grid, SlipstreamManager manager)
static void updateSlipstreamConfig(String prevConfig, WeightedRandomPicker< String > nextConfigPicker, SlipstreamManager manager)
static boolean SLIPSTREAM_DEBUG
static final String SLIPSTREAM
float getMaxAngleVarianceForCurve()
float getMaxAngleVariance()
void setMaxAngleVariance(float maxAngleVariance)
void setMaxAngleVarianceForCurve(float maxAngleVarianceForCurve)
void buildToDestination(Vector2f control, Vector2f control2, Vector2f to)
static String mirrorHorz(String in)
static float MAP_HEIGHT_PADDING
CollisionGridUtil getGrid()
static void validateConfigs()
static Map< String, Float > STREAM_CONFIGS
static void mirrorPrevVert(float weight)
static void loadConfigs()
void addStream(int month)
static void fadeOutSectionsShorterThan(List< SlipstreamSegment > segments, float minLength)
static float MAP_WIDTH_PADDING
void checkIntersectionsAndFadeSections(SlipstreamTerrainPlugin2 plugin, boolean onlyKeepLongestBetweenStreams)
transient CollisionGridUtil grid
static void removedFadesThatDoNotReachZero(List< SlipstreamSegment > segments)
static void mirrorPrevHorz(float weight)
void advance(float amount)
List< AddedStream > active
static String mirrorVert(String in)
void spawn(float spawnDays, Random random)
List< SlipstreamSegment > getSegments()
void recomputeEncounterPoints()
List< SlipstreamSegment > getSegmentsNear(Vector2f loc, float range)
static String NASCENT_WELL_KEY
Iterator< Object > getCheckIterator(Vector2f loc, float objWidth, float objHeight)
void addObject(Object object, Vector2f loc, float objWidth, float objHeight)
void advance(float amount)
static float getDistance(SectorEntityToken from, SectorEntityToken to)
static float getSpeedForBurnLevel(float burnLevel)
float getFloat(String key)
float convertToDays(float realSeconds)
CampaignTerrainPlugin getPlugin()
SectorEntityToken addTerrain(String terrainId, Object param)
List< SectorEntityToken > getJumpPoints()
List< CampaignTerrainAPI > getTerrainCopy()
void removeEntity(SectorEntityToken entity)
List< StarSystemAPI > getStarSystems()
CampaignClockAPI getClock()
MemoryAPI getMemoryWithoutUpdate()
LocationAPI getHyperspace()
void setLocation(float x, float y)
SectorEntityToken getOrbitFocus()