001package com.fs.starfarer.api.impl.campaign.velfield; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.Iterator; 006import java.util.LinkedHashMap; 007import java.util.LinkedHashSet; 008import java.util.List; 009import java.util.Map; 010import java.util.Random; 011import java.util.Set; 012 013import org.lwjgl.util.vector.Vector2f; 014 015import com.fs.starfarer.api.EveryFrameScript; 016import com.fs.starfarer.api.Global; 017import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 018import com.fs.starfarer.api.campaign.JumpPointAPI; 019import com.fs.starfarer.api.campaign.LocationAPI; 020import com.fs.starfarer.api.campaign.NascentGravityWellAPI; 021import com.fs.starfarer.api.campaign.SectorEntityToken; 022import com.fs.starfarer.api.campaign.StarSystemAPI; 023import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 024import com.fs.starfarer.api.impl.campaign.DebugFlags; 025import com.fs.starfarer.api.impl.campaign.ids.Tags; 026import com.fs.starfarer.api.impl.campaign.ids.Terrain; 027import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceAbyssPlugin; 028import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin; 029import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamBuilder.StreamType; 030import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2; 031import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment; 032import com.fs.starfarer.api.impl.campaign.world.TTBlackSite; 033import com.fs.starfarer.api.util.CollisionGridUtil; 034import com.fs.starfarer.api.util.IntervalUtil; 035import com.fs.starfarer.api.util.Misc; 036import com.fs.starfarer.api.util.WeightedRandomPicker; 037 038public class SlipstreamManager implements EveryFrameScript { 039 040 public static int WIDTH = 21; 041 public static int HEIGHT = 11; 042 public static float MAP_WIDTH_PADDING = 8000; 043 public static float MAP_HEIGHT_PADDING = 5000; 044 /** 045 * 21x11 characters 046 * Capital letter: endpoints 047 * Lowercase letter, optional: control point for 3-point stream (if 2: cubic bezier curve instead of quadratic) 048 * Direction of flow is determined by time of cycle and relative location of endpoints 049 * X is ignored; just marks the center to make it easier to edit 050 * After a capital letter: 051 * < makes the stream go in the opposite-to-standard direction 052 * +-<single digit number> adjusts the stream's burn level; 0 means +10 053 * ~ makes the stream narrower, on average 054 * = makes it wider 055 * | makes it much straighter than usual 056 * ! priority, spawned before other streams (and thus other streams will fade when intersecting it, not this one) 057 * ^ only keep longest segment - but, from intersections with streams ONLY, not with stars/other blockers 058 * * randomize: up to +10 burn, reverse, straightness, wide/narrow 059 * ? randomize: same as above, but no reverse and only up to +5 burn 060 */ 061 public static Map<String, Float> STREAM_CONFIGS = new LinkedHashMap<String, Float>(); 062 063 static { 064 loadConfigs(); 065 } 066 067 public static void loadConfigs() { 068 // begin "finished" 069 STREAM_CONFIGS.put( 070 "aD? B" + 071 "C? EA" + 072 " c " + 073 " " + 074 " " + 075 " X " + 076 " " + 077 " " + 078 " d " + 079 "A!E? C" + 080 "B! Db" 081 , 10f); 082 STREAM_CONFIGS.put( 083 "Ba C D aB" + 084 " " + 085 " " + 086 " " + 087 " " + 088 " CXD " + 089 " e " + 090 " " + 091 " " + 092 " " + 093 "A!bE EbA" 094 , 10f); 095 STREAM_CONFIGS.put( 096 "A! D? F? A" + 097 " " + 098 " " + 099 "a a" + 100 " " + 101 "C! DE X FG C" + 102 " " + 103 "b b" + 104 " " + 105 " " + 106 "B! E? G? B" 107 , 10f); 108 STREAM_CONFIGS.put( 109 "C! D! cA" + 110 " " + 111 " " + 112 " " + 113 "A? aa" + 114 " X c" + 115 "B? bb" + 116 " " + 117 " " + 118 "d C " + 119 "d DB" 120 , 10f); 121 STREAM_CONFIGS.put( 122 "B^! I^? H^? a G" + 123 " " + 124 " i h " + 125 " g" + 126 " A!| H^G^? " + 127 " C^<I X " + 128 " D^ E^?F^? F" + 129 "c AB " + 130 " " + 131 "b d " + 132 "C? D?e a E" 133 , 10f); 134 STREAM_CONFIGS.put( 135 " b a " + 136 "A|~+0 A" + 137 " " + 138 "B|~+0 B" + 139 " c a " + 140 " X " + 141 " " + 142 "C|!+0 d C" + 143 " b " + 144 "D|~+0 D" + 145 " d c " 146 , 10f); 147 148 // highly randomized and mirrored 149 STREAM_CONFIGS.put( 150 "A* B* G" + 151 " H* g " + 152 " h H " + 153 " b " + 154 " C* A G* " + 155 " X F" + 156 " c D " + 157 " B E* e " + 158 " d f " + 159 " " + 160 "D* C F* E" 161 , 5f); 162 String prev = (String) STREAM_CONFIGS.keySet().toArray()[STREAM_CONFIGS.size() - 1]; 163 STREAM_CONFIGS.put(mirrorHorz(prev), 5f); 164 STREAM_CONFIGS.put(mirrorVert(prev), 5f); 165 STREAM_CONFIGS.put(mirrorHorz(mirrorVert(prev)), 5f); 166 167 // end "finished" 168 169 170 // maybe? 171// STREAM_CONFIGS.put( 172// "A|!~+0C D FE A" + 173// "c d B" + 174// " " + 175// " " + 176// " " + 177// " f X e " + 178// " " + 179// " " + 180// " " + 181// "B|!<-5D^ " + 182// " C^ F^E<^ bb" 183// , 10f); 184// STREAM_CONFIGS.put( // also maybe, very similar to another one though 185// "A C D A" + 186// " c d " + 187// " " + 188// " " + 189// " e " + 190// " X " + 191// " b " + 192// "E a E" + 193// " " + 194// " " + 195// "B C D B" 196// , 10f); 197 198 // blank 199// STREAM_CONFIGS.put( 200// " " + 201// " " + 202// " " + 203// " " + 204// " " + 205// " X " + 206// " " + 207// " " + 208// " " + 209// " " + 210// " " 211// , 10f); 212 213 } 214 215 public static void mirrorPrevVert(float weight) { 216 String prev = (String) STREAM_CONFIGS.keySet().toArray()[STREAM_CONFIGS.size() - 1]; 217 STREAM_CONFIGS.put(mirrorVert(prev), weight); 218 } 219 public static String mirrorVert(String in) { 220 String out = ""; 221 for (int j = 0; j < HEIGHT; j++) { 222 out = in.substring(j * WIDTH, (j + 1) * WIDTH) + out; 223 } 224 return out; 225 } 226 public static void mirrorPrevHorz(float weight) { 227 String prev = (String) STREAM_CONFIGS.keySet().toArray()[STREAM_CONFIGS.size() - 1]; 228 STREAM_CONFIGS.put(mirrorHorz(prev), weight); 229 } 230 public static String mirrorHorz(String in) { 231 String out = ""; 232 for (int j = 0; j < HEIGHT; j++) { 233 out = out + new StringBuilder(in.substring(j * WIDTH, (j + 1) * WIDTH)).reverse(); 234 } 235 return out; 236 } 237 238 public static void validateConfigs() { 239 // make sure they all parse 240 for (String key : STREAM_CONFIGS.keySet()) { 241 if (key.length() != WIDTH * HEIGHT) { 242 throw new RuntimeException("Length of slipstream config not WIDTHxHEIGHT [" + key + "]"); 243 } 244 try { 245 new StreamConfig(key, null); 246 } catch (Throwable t) { 247 throw new RuntimeException("Error parsing slipstream config [" + key + "]", t); 248 } 249 } 250 251 } 252 253 public static class StreamData { 254 public String id; 255 public int p0x = -1; 256 public int p0y = -1; 257 public int p1x = -1; 258 public int p1y = -1; 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; 272 273 public StreamData(String id) { 274 this.id = id; 275 } 276 public Vector2f generateP0(Random random) { 277 if (p0x < 0) return null; 278 return generate(p0x, p0y, random); 279 } 280 public Vector2f generateP1(Random random) { 281 if (p1x < 0) return null; 282 return generate(p1x, p1y, random); 283 } 284 public Vector2f generateControl(Random random) { 285 if (controlX < 0) return null; 286 return generate(controlX, controlY, random); 287 } 288 public Vector2f generateControl2(Random random) { 289 if (control2X < 0) return null; 290 return generate(control2X, control2Y, random); 291 } 292 public Vector2f generate(int cellX, int cellY, Random random) { 293 float sw = Global.getSettings().getFloat("sectorWidth") - MAP_WIDTH_PADDING; 294 float sh = Global.getSettings().getFloat("sectorHeight") - MAP_HEIGHT_PADDING; 295 float cellWidth = sw / WIDTH; 296 float cellHeight = sh / HEIGHT; 297 Vector2f p = new Vector2f(); 298// if (cellX == 20) { 299// System.out.println("efwefwef"); 300// } 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); 305 306 p.x = minX + (maxX - minX) * random.nextFloat(); 307 p.y = minY + (maxY - minY) * random.nextFloat(); 308// p.x = minX + (maxX - minX) * 0f; 309// p.y = minY + (maxY - minY) * 0f; 310// p.x = minX + (maxX - minX) * 1f; 311// p.y = minY + (maxY - minY) * 1f; 312 313 return p; 314 } 315 } 316 317 public static class StreamConfig { 318 319 public List<StreamData> streams = new ArrayList<StreamData>(); 320 public String data; 321 public StreamConfig(String data, Random random) { 322 this.data = data; 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)) { 328 ids.add("" + c); 329 } 330 } 331 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; 337 338 int cellX = i % WIDTH; 339 int cellY = HEIGHT - i / WIDTH - 1; 340 if (id.equals("" + c)) { 341 if (curr.p0x < 0) { 342 curr.p0x = cellX; 343 curr.p0y = cellY; 344 } else if (curr.p1x < 0) { 345 curr.p1x = cellX; 346 curr.p1y = cellY; 347 } 348 } else if (id.toLowerCase().equals("" + c)) { 349 if (curr.controlX < 0) { 350 curr.controlX = cellX; 351 curr.controlY = cellY; 352 } else { 353 curr.control2X = cellX; 354 curr.control2Y = cellY; 355 } 356 } 357 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; 362 if (c2 == '<') { 363 curr.reverse = true; 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; 386 } 387 } 388 } 389 } 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; 394 } 395 if (!curr.straight) { 396 curr.straight = random.nextFloat() < 0.25f; 397 } 398 if (curr.burnMod == 0) { 399 //curr.burnMod = (random.nextBoolean() ? 1 : -1) * random.nextInt(6); 400 if (curr.minorRandomize) { 401 curr.burnMod = random.nextInt(6); 402 } else { 403 curr.burnMod = random.nextInt(11); 404 } 405 } 406 if (curr.type == StreamType.NORMAL) { 407 float r = random.nextFloat(); 408 if (r < 0.2f) { 409 curr.type = StreamType.NARROW; 410 } else if (r < 0.4f) { 411 curr.type = StreamType.WIDE; 412 } 413 } 414 } 415 streams.add(curr); 416 } 417 } 418 } 419 } 420 421 422 public static class AddedStream { 423 public CampaignTerrainAPI terrain; 424 public SlipstreamTerrainPlugin2 plugin; 425 public Vector2f from; 426 public Vector2f to; 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(); 435 } 436 } 437 438 439 protected transient CollisionGridUtil grid; 440 441 protected IntervalUtil interval = new IntervalUtil(1f, 2f); 442 protected Random random = new Random(); 443 protected int prevMonth = -1; 444 protected int desiredNumStreams = 0; 445 protected List<AddedStream> active = new ArrayList<SlipstreamManager.AddedStream>(); 446 protected StreamConfig config; 447 protected String prevConfig; 448 449 protected Object readResolve() { 450 if (active == null) { 451 active = new ArrayList<SlipstreamManager.AddedStream>(); 452 } 453 return this; 454 } 455 456 457 public void advance(float amount) { 458 //if (true) return; 459 if (DebugFlags.SLIPSTREAM_DEBUG) { 460 random = Misc.random; 461 } 462 463// int total = 0; 464// for (AddedStream curr : active) { 465// total += curr.plugin.getSegments().size(); 466// } 467// System.out.println("TOTAL SEGMENTS: " + total + ", streams: " + active.size()); 468 469 float days = Global.getSector().getClock().convertToDays(amount); 470 if (DebugFlags.SLIPSTREAM_DEBUG) { 471 days *= 100f; 472 } 473 interval.advance(days); 474 //DebugFlags.SLIPSTREAM_DEBUG = true; 475 if (interval.intervalElapsed()) { 476 Iterator<AddedStream> iter = active.iterator(); 477 while (iter.hasNext()) { 478 AddedStream curr = iter.next(); 479 if (!curr.terrain.isAlive()) { 480 iter.remove(); 481 } 482 } 483 484 int month = Global.getSector().getClock().getMonth(); 485 //month = 6; 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); 493 } 494 if (config != null) { 495 prevConfig = config.data; 496 } 497 config = null; 498 } else if (month != 6 && month != 12) { 499 if (config == null) { 500 if (DebugFlags.SLIPSTREAM_DEBUG) { 501 STREAM_CONFIGS.clear(); 502 loadConfigs(); 503 } 504 505 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random); 506 for (String key : STREAM_CONFIGS.keySet()) { 507 if (prevConfig != null && prevConfig.equals(key) && STREAM_CONFIGS.size() > 1) { 508 continue; 509 } 510 picker.add(key, STREAM_CONFIGS.get(key)); 511 } 512 if (picker.isEmpty() && prevConfig != null) { 513 picker.add(prevConfig, 1f); 514 } 515 ListenerUtil.updateSlipstreamConfig(prevConfig, picker, this); 516 String data = picker.pick(); 517 if (data != null) { 518 config = new StreamConfig(data, random); 519 } 520 } 521 addStream(month); 522 } 523 524 525 prevMonth = month; 526 } 527 grid = null; 528 } 529 530 531 public void addStream(int month) { 532 if (config == null) return; 533 534 //random = new Random(); 535// long seed = 23895464576452L + 4384357483229348234L + 4343253L; 536// seed = 1181783497276652981L ^ seed; 537// Random random = new Random(seed); 538 539 WeightedRandomPicker<StreamData> picker = new WeightedRandomPicker<StreamData>(random); 540 for (StreamData data : config.streams) { 541 if (data.wasUsed) continue; 542 if (!data.priority) continue; 543 picker.add(data); 544 } 545 if (picker.isEmpty()) { 546 // add non-priority if all priority ones are already used 547 for (StreamData data : config.streams) { 548 if (data.wasUsed) continue; 549 picker.add(data); 550 } 551 } 552 553 StreamData data = picker.pick(); 554 if (data == null) return; 555 556 SlipstreamParams2 params = new SlipstreamParams2(); 557 params.burnLevel = 30 + data.burnMod; 558 params.minSpeed = Misc.getSpeedForBurnLevel(params.burnLevel - 5); 559 params.maxSpeed = Misc.getSpeedForBurnLevel(params.burnLevel + 5); 560 params.lineLengthFractionOfSpeed = 0.25f * Math.max(0.25f, Math.min(1f, 30f / (float) params.burnLevel)); 561 562 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; 568 569 // default direction is east in first half of the cycle, west in the second half 570 // months 6 and 12 don't really matter since the slipstreams despawn during those 571 if (month == 12 || month < 6) { 572 //if (!(month == 12 || month < 6)) { 573 if ((!data.reverse && from.x > to.x) || (data.reverse && from.x < to.x)) { 574 Vector2f temp = to; 575 to = from; 576 from = temp; 577 } 578 } else { 579 if ((!data.reverse && from.x < to.x) || (data.reverse && from.x > to.x)) { 580 Vector2f temp = to; 581 to = from; 582 from = temp; 583 } 584 } 585 586 587 LocationAPI hyperspace = Global.getSector().getHyperspace(); 588 CampaignTerrainAPI slipstream = (CampaignTerrainAPI) hyperspace.addTerrain(Terrain.SLIPSTREAM, params); 589 590 slipstream.setLocation(from.x, from.y); 591 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) slipstream.getPlugin(); 592 SlipstreamBuilder builder = new SlipstreamBuilder(slipstream.getLocation(), plugin, data.type, random); 593 594 if (data.straight) { 595 float mult = 0.25f; 596 builder.setMaxAngleVariance(builder.getMaxAngleVariance() * mult); 597 builder.setMaxAngleVarianceForCurve(builder.getMaxAngleVarianceForCurve() * mult); 598 } 599 600 if (control2 != null) { 601 float dist1 = Misc.getDistance(from, control); 602 float dist2 = Misc.getDistance(from, control2); 603 if (dist2 < dist1) { 604 Vector2f temp = control2; 605 control2 = control; 606 control = temp; 607 } 608 builder.buildToDestination(control, control2, to); 609 } else if (control != null) { 610 builder.buildToDestination(control, to); 611 } else { 612 builder.buildToDestination(to); 613 } 614 615 checkIntersectionsAndFadeSections(plugin, data.onlyKeepLongestSegment); 616 617 if (plugin.getSegments().size() < 3) { 618 hyperspace.removeEntity(slipstream); 619 return; 620 } 621 622 float spawnDays = 1f + 2f * random.nextFloat(); 623 if (DebugFlags.SLIPSTREAM_DEBUG) { 624 spawnDays = 0f; 625 } 626 plugin.spawn(spawnDays, random); 627 628 plugin.recomputeEncounterPoints(); 629 630 AddedStream added = new AddedStream(plugin); 631 active.add(added); 632 data.wasUsed = true; 633 } 634 635 public void checkIntersectionsAndFadeSections(SlipstreamTerrainPlugin2 plugin, boolean onlyKeepLongestBetweenStreams) { 636 updateGrid(); 637 638 plugin.recomputeIfNeeded(); 639 640 List<SlipstreamSegment> segments = plugin.getSegments(); 641 642 Set<SlipstreamSegment> otherStreamCuts = new HashSet<SlipstreamSegment>(); 643 644 for (SlipstreamSegment curr : segments) { 645 646 Iterator<Object> iter = grid.getCheckIterator(curr.loc, curr.width / 2f, curr.width / 2f); 647 while (iter.hasNext()) { 648 Object obj = iter.next(); 649 if (obj instanceof JumpPointAPI) { 650 JumpPointAPI jp = (JumpPointAPI) obj; 651 Vector2f loc = jp.getLocation(); 652 float radius = jp.getRadius(); 653 if (jp.getOrbitFocus() != null) { 654 loc = jp.getOrbitFocus().getLocation(); 655 radius = Misc.getDistance(jp.getOrbitFocus(), jp) + jp.getRadius(); 656 } 657 658 Vector2f diff = Vector2f.sub(loc, curr.loc, new Vector2f()); 659 660 float distPerp = Math.abs(Vector2f.dot(curr.normal, diff)); 661 float distAlong = Math.abs(Vector2f.dot(curr.dir, diff)); 662 663 distPerp -= radius; 664 distAlong -= radius; 665 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(); 672 curr.bMult = 0f; 673 } else { 674 curr.bMult = Math.min(curr.bMult, 675 (distAlong - minDistAlong) / (fadeDistAlong - minDistAlong)); 676 } 677 } 678 } else if (obj instanceof CampaignTerrainAPI) { 679 CampaignTerrainAPI terrain = (CampaignTerrainAPI) obj; 680 SlipstreamTerrainPlugin2 otherPlugin = (SlipstreamTerrainPlugin2) terrain.getPlugin(); 681 if (otherPlugin == plugin) continue; 682 683 for (SlipstreamSegment other : otherPlugin.getSegmentsNear(curr.loc, curr.width / 2f)) { 684 //if (other.fader.getBrightness() == 0 || other.bMult <= 0) continue; 685 if (other.bMult <= 0) continue; 686 687 float dist = Misc.getDistance(curr.loc, other.loc); 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(); 693 curr.bMult = 0f; 694 otherStreamCuts.add(curr); 695 } else { 696 curr.bMult = Math.min(curr.bMult, 697 (dist - minDist) / (fadeDist - minDist)); 698 } 699 } 700 } 701 } else if (obj instanceof CustomStreamBlocker) { 702 CustomStreamBlocker blocker = (CustomStreamBlocker) obj; 703 Vector2f loc = blocker.loc; 704 float radius = blocker.radius; 705 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)); 709 710 distPerp -= radius; 711 distAlong -= radius; 712 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(); 719 curr.bMult = 0f; 720 } else { 721 curr.bMult = Math.min(curr.bMult, 722 (distAlong - minDistAlong) / (fadeDistAlong - minDistAlong)); 723 } 724 } 725 } else if (obj instanceof AbyssStreamBlocker) { 726 AbyssStreamBlocker abyss = (AbyssStreamBlocker) obj; 727 if (abyss.containsPoint(curr.loc)) { 728 curr.fader.forceOut(); 729 curr.bMult = 0f; 730 } 731 } 732 } 733 } 734 735 fadeOutSectionsShorterThan(segments, 5000f); 736 737 // unrelated to the above - for proximity-to-something fades that 738 // were unnecessary because there wasn't an actual intersection with that something 739 removedFadesThatDoNotReachZero(segments); 740 741 742 743 if (onlyKeepLongestBetweenStreams) { 744 List<SlipstreamSegment> longest = new ArrayList<SlipstreamTerrainPlugin2.SlipstreamSegment>(); 745 746 List<SlipstreamSegment> currList = new ArrayList<SlipstreamTerrainPlugin2.SlipstreamSegment>(); 747 748 for (SlipstreamSegment curr : segments) { 749 if (otherStreamCuts.contains(curr)) { 750 if (currList.size() > longest.size()) { 751 longest = currList; 752 } 753 currList = new ArrayList<SlipstreamSegment>(); 754 } 755 if (curr.bMult > 0f) { 756 currList.add(curr); 757 } 758 } 759 if (currList.size() > longest.size()) { 760 longest = currList; 761 } 762 for (SlipstreamSegment curr : segments) { 763 if (!longest.contains(curr)) { 764 curr.bMult = 0f; 765 curr.fader.forceOut(); 766 } 767 } 768 769 } 770 } 771 772 773 public static void fadeOutSectionsShorterThan(List<SlipstreamSegment> segments, float minLength) { 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; // counts one more than it should; meh 781 } 782 if (runLength < minRunLength) { 783 for (SlipstreamSegment inRun : currRun) { 784 inRun.fader.forceOut(); 785 inRun.bMult = 0f; 786 } 787 } 788 currRun.clear(); 789 } else { 790 currRun.add(curr); 791 } 792 } 793 } 794 795 public static void removedFadesThatDoNotReachZero(List<SlipstreamSegment> segments) { 796 List<SlipstreamSegment> currRun = new ArrayList<SlipstreamSegment>(); 797 boolean currRunReachedZero = false; 798 for (SlipstreamSegment curr : segments) { 799 if (curr.bMult < 1f) { 800 currRun.add(curr); 801 if (curr.bMult <= 0f || (curr.fader.getBrightness() == 0 && !curr.fader.isFadingIn())) { 802 currRunReachedZero = true; 803 } 804 } else { 805 if (!currRunReachedZero) { 806 for (SlipstreamSegment inRun : currRun) { 807 inRun.fader.fadeIn(); 808 inRun.bMult = 1f; 809 } 810 } 811 currRun.clear(); 812 currRunReachedZero = false; 813 } 814 } 815 } 816 817 818 819 public static class CustomStreamRevealer extends CustomStreamBlocker { 820 public CustomStreamRevealer(Vector2f loc, float radius) { 821 super(loc, radius); 822 } 823 } 824 825 public static class CustomStreamBlocker { 826 public Vector2f loc; 827 public float radius; 828 public CustomStreamBlocker(Vector2f loc, float radius) { 829 this.loc = new Vector2f(loc); 830 this.radius = radius; 831 } 832 } 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(); 839 } 840 } 841 public boolean containsPoint(Vector2f loc) { 842 if (plugin == null) return false; 843 return plugin.getAbyssalDepth(loc) > 0; 844 } 845 } 846 847 public CollisionGridUtil getGrid() { 848 return grid; 849 } 850 851 public void updateGrid() { 852 if (grid != null) return; 853 854 float sw = Global.getSettings().getFloat("sectorWidth"); 855 float sh = Global.getSettings().getFloat("sectorHeight"); 856 float minCellSize = 12000f; 857 float cellSize = Math.max(minCellSize, sw * 0.05f); 858 859 grid = new CollisionGridUtil(-sw/2f, sw/2f, -sh/2f, sh/2f, cellSize); 860 861 LocationAPI hyperspace = Global.getSector().getHyperspace(); 862 for (SectorEntityToken jp : hyperspace.getJumpPoints()) { 863 float size = jp.getRadius() * 2f + 100f; 864 grid.addObject(jp, jp.getLocation(), size * 2f, size * 2f); 865 } 866 867// for (NascentGravityWellAPI well : hyperspace.getGravityWells()) { 868// float size = 1000f + well.getRadius(); 869// CustomStreamBlocker blocker = new CustomStreamBlocker(well.getLocation(), size); 870// grid.addObject(blocker, well.getLocation(), size * 2f, size * 2f); 871// } 872 Object alphaSiteWell = Global.getSector().getMemoryWithoutUpdate().get(TTBlackSite.NASCENT_WELL_KEY); 873 if (alphaSiteWell instanceof NascentGravityWellAPI) { 874 NascentGravityWellAPI well = (NascentGravityWellAPI) alphaSiteWell; 875 float size = 1000f + well.getRadius(); 876 CustomStreamBlocker blocker = new CustomStreamBlocker(well.getLocation(), size); 877 grid.addObject(blocker, well.getLocation(), size * 2f, size * 2f); 878 } 879 880 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 881 if (system.hasTag(Tags.THEME_CORE)) { 882 Vector2f loc = system.getLocation(); 883 float size = 4000f; 884 CustomStreamBlocker blocker = new CustomStreamBlocker(loc, size); 885 grid.addObject(blocker, loc, size * 2f, size * 2f); 886 } 887 } 888 889 { 890 float w = Global.getSettings().getFloat("sectorWidth"); 891 float h = Global.getSettings().getFloat("sectorHeight"); 892// Vector2f loc = new Vector2f(-w/2f, -h/2f); 893// float size = 26000; 894// CustomStreamBlocker orionPersusAbyssBlocker = new CustomStreamBlocker(loc, size); 895// grid.addObject(orionPersusAbyssBlocker, loc, size * 2f, size * 2f); 896 AbyssStreamBlocker orionPersusAbyssBlocker = new AbyssStreamBlocker(); 897 grid.addObject(orionPersusAbyssBlocker, new Vector2f(), w, h); 898 } 899 900// for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 901// if (market.isHidden()) continue; 902// //if (market.hasIndustry(Industries.SPACEPORT)) continue; 903// Industry spaceport = market.getIndustry(Industries.SPACEPORT); 904// if (spaceport == null || !spaceport.isFunctional()) continue; 905// 906// Vector2f loc = market.getLocationInHyperspace(); 907// float size = 5000f; 908// CustomStreamRevealer revealer = new CustomStreamRevealer(loc, size); 909// grid.addObject(revealer, loc, size * 2f, size * 2f); 910// } 911 912 913 int segmentsToSkip = (int) ((cellSize - 2000) / 400f); 914 float checkSize = minCellSize - 2000f; 915 916 for (CampaignTerrainAPI curr : hyperspace.getTerrainCopy()) { 917 if (curr.getPlugin() instanceof SlipstreamTerrainPlugin2) { 918 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) curr.getPlugin(); 919 List<SlipstreamSegment> segments = plugin.getSegments(); 920 List<SlipstreamSegment> check = new ArrayList<SlipstreamTerrainPlugin2.SlipstreamSegment>(); 921 for (int i = 0; i < segments.size(); i += segmentsToSkip) { 922 check.add(segments.get(i)); 923 } 924 if (!check.contains(segments.get(segments.size() - 1))) { 925 check.add(segments.get(segments.size() - 1)); 926 } 927 928 for (SlipstreamSegment seg : check) { 929 grid.addObject(curr, seg.loc, checkSize, checkSize); 930 } 931 } 932 } 933 934 ListenerUtil.updateSlipstreamBlockers(grid, this); 935 } 936 937 938 public boolean isDone() { 939 return false; 940 } 941 942 public boolean runWhilePaused() { 943 return false; 944 } 945 946 947 948}