001package com.fs.starfarer.api.impl.campaign.abilities; 002 003import java.util.ArrayList; 004import java.util.Iterator; 005import java.util.List; 006import java.util.Random; 007 008import org.lwjgl.util.vector.Vector2f; 009 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.campaign.CampaignFleetAPI; 012import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 013import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 014import com.fs.starfarer.api.campaign.LocationAPI; 015import com.fs.starfarer.api.campaign.OrbitalStationAPI; 016import com.fs.starfarer.api.campaign.PlanetAPI; 017import com.fs.starfarer.api.campaign.SectorEntityToken; 018import com.fs.starfarer.api.impl.campaign.ids.Tags; 019import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2; 020import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment; 021import com.fs.starfarer.api.util.FaderUtil; 022import com.fs.starfarer.api.util.IntervalUtil; 023import com.fs.starfarer.api.util.Misc; 024 025public class GraviticScanData { 026 027 public static class GSPing { 028 public float arc; 029 public float angle; 030 public float grav; 031 public FaderUtil fader; 032 public boolean withSound = false; 033 public GSPing(float angle, float arc, float grav, float in, float out) { 034 this.arc = arc; 035 this.angle = angle; 036 this.grav = grav; 037 fader = new FaderUtil(0, in, out, false, true); 038 fader.fadeIn(); 039 } 040 041 public void advance(float days) { 042 fader.advance(days); 043 if (withSound && fader.getBrightness() >= 0.5f) { 044 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle); 045 float dist = 1000f + (1f - Math.min(1f, grav / 200f)) * 1450f; 046 loc.scale(dist); 047 Vector2f.add(loc, Global.getSector().getPlayerFleet().getLocation(), loc); 048 Global.getSoundPlayer().playSound("ui_neutrino_detector_ping", 1, 1, loc, new Vector2f()); 049 withSound = false; 050 } 051 } 052 053 public boolean isDone() { 054 return fader.isFadedOut(); 055 } 056 057 058 } 059 060 private GraviticScanAbility ability; 061 062 063 private int resolution = 360; 064 transient private float [] data; 065 066 067 private List<GSPing> pings = new ArrayList<GSPing>(); 068 //private IntervalUtil noiseInterval = new IntervalUtil(0.01f, 0.02f); 069 070 private IntervalUtil planetInterval = new IntervalUtil(0.01f, 0.01f); 071 private IntervalUtil specialInterval = new IntervalUtil(0.075f, 0.125f); 072 //private IntervalUtil specialInterval = new IntervalUtil(0.15f, 0.25f); 073 074 public GraviticScanData(GraviticScanAbility ability) { 075 this.ability = ability; 076 } 077 078 public void advance(float days) { 079 if (ability.getFleet() == null || ability.getFleet().getContainingLocation() == null) return; 080 081// if (ability.getFleet().isInHyperspace()) { 082// data = null; 083// return; 084// } 085 086 Iterator<GSPing> iter = pings.iterator(); 087 while (iter.hasNext()) { 088 GSPing ping = iter.next(); 089 ping.advance(days); 090 if (ping.isDone()) { 091 iter.remove(); 092 } 093 } 094 095 096// noiseInterval.advance(days); 097// if (noiseInterval.intervalElapsed() && false) { 098// float noiseLevel = getNoiseLevel(); 099// int num = Math.round(noiseLevel * 10); 100// num = 1; 101// for (int i = 0; i < num; i++) { 102// float angle = (float) Math.random() * 360f; 103//// float arc = 5f + 10f * (float) Math.random() + 10 * noiseLevel; 104//// float grav = 5f + 50f * (float) Math.random() * noiseLevel; 105// float arc = 5f + 10f * (float) Math.random(); 106// float grav = 30f + 80f * (float) Math.random(); 107// 108//// float in = 0.02f + 0.02f * (float) Math.random(); 109//// float out = 0.02f + 0.02f * (float) Math.random(); 110// 111// float in = 0.05f + 0.1f * (float) Math.random(); 112// in *= 0.25f; 113// float out = in; 114// 115// GSPing ping = new GSPing(angle, arc, grav, in, out); 116// pings.add(ping); 117// } 118// } 119 120 planetInterval.advance(days); 121 if (planetInterval.intervalElapsed()) { 122 maintainHighSourcePings(); 123 } 124 125 specialInterval.advance(days); 126 if (specialInterval.intervalElapsed()) { 127 doSpecialPings(); 128 } 129 130 131 132 updateData(); 133 134 //System.out.println("Pings: " + pings.size()); 135 } 136 137 138 139 public void updateData() { 140 data = new float[resolution]; 141 142 143 float max = 0f; 144 float incr = 360f / (float) resolution; 145 for (GSPing ping : pings) { 146 147 float b = ping.fader.getBrightness(); 148 if (b <= 0) continue; 149 150 //b = (float) Math.sqrt(b); 151 //b *= b; 152 153 float arc = ping.arc; 154 float mid = ping.angle; 155 float half = (float) Math.ceil(0.5f * arc / incr); 156 for (float i = -half; i <= half; i++) { 157 float curr = mid + incr * i; 158 int index = getIndex(curr); 159 160 float intensity = 1f - Math.abs(i / half); 161 intensity *= intensity; 162 float value = ping.grav * intensity * b; 163 data[index] += value; 164 //float min = Math.min(data[index], value); 165 //data[index] = Math.max(data[index], value); 166 //if (data[index] > max) max = data[index]; 167 } 168 } 169 170 } 171 172 public float getDataAt(float angle) { 173 if (data == null) return 0f; 174 int index = getIndex(angle); 175 return data[index]; 176 } 177 178 public int getIndex(float angle) { 179 angle = Misc.normalizeAngle(angle); 180 int index = (int)Math.floor(resolution * angle/360f); 181 return index; 182 } 183 184 private int initialCount = 0; 185 private List<SectorEntityToken> special = new ArrayList<SectorEntityToken>(); 186 187 188 //private float totalForce; 189 public void doSpecialPings() { 190 CampaignFleetAPI fleet = ability.getFleet(); 191 boolean abyss = Misc.isInAbyss(fleet); 192 //abyss = false; 193 if (fleet.isInHyperspace() && !abyss) return; 194 195 Vector2f loc = fleet.getLocation(); 196 LocationAPI location = fleet.getContainingLocation(); 197 198 float neutrinoLowSkipProb = 0.8f; 199 if (special.isEmpty()) { 200// for (SectorEntityToken entity : location.getAsteroids()) { 201// special.add(entity); 202// } 203 for (Object object : location.getEntities(CustomCampaignEntityAPI.class)) { 204 if (object instanceof SectorEntityToken) { 205 SectorEntityToken entity = (SectorEntityToken) object; 206 207 boolean neutrinoHigh = entity.hasTag(Tags.NEUTRINO_HIGH); 208 if (neutrinoHigh) continue; 209 210 if (abyss && !Misc.isInAbyss(entity)) continue; 211 212 boolean neutrino = entity.hasTag(Tags.NEUTRINO); 213 boolean neutrinoLow = entity.hasTag(Tags.NEUTRINO_LOW); 214 boolean station = entity.hasTag(Tags.STATION); 215 216 217 218 if (!neutrino && !neutrinoLow && !station) continue; 219 if (neutrinoLow && (float) Math.random() < neutrinoLowSkipProb) continue; 220 221 special.add(entity); 222 } 223 } 224// for (Object object : location.getEntities(OrbitalStationAPI.class)) { 225// if (object instanceof SectorEntityToken) { 226// SectorEntityToken entity = (SectorEntityToken) object; 227// special.add(entity); 228// } 229// } 230 for (CampaignFleetAPI curr : location.getFleets()) { 231 if (fleet == curr) continue; 232 233 boolean neutrinoHigh = curr.hasTag(Tags.NEUTRINO_HIGH); 234 if (neutrinoHigh) continue; 235 236 if (abyss && !Misc.isInAbyss(fleet)) continue; 237 238 if ((float) Math.random() < neutrinoLowSkipProb) continue; 239 special.add(curr); 240 } 241 242 initialCount = special.size(); 243 } 244 245 int batch = (int) Math.ceil(initialCount / 1f); 246 for (int i = 0; i < batch; i++) { 247 if (special.isEmpty()) break; 248 249 SectorEntityToken curr = special.remove(0); 250 251 float dist = Misc.getDistance(loc, curr.getLocation()); 252 253 float arc = Misc.computeAngleSpan(curr.getRadius(), dist); 254 arc *= 2f; 255 if (arc < 15) arc = 15; 256 if (arc > 150f) arc = 150f; 257 //arc += 30f; 258 float angle = Misc.getAngleInDegrees(loc, curr.getLocation()); 259 260 float g = getGravity(curr); 261 g *= getRangeGMult(dist); 262 263 float in = 0.05f + 0.1f * (float) Math.random(); 264 in *= 0.25f; 265 float out = in; 266 out *= 2f; 267 GSPing ping = new GSPing(angle, arc, g, in, out); 268 ping.withSound = true; 269 pings.add(ping); 270 } 271 272 273 long seed = (long) (location.getLocation().x * 1300000 + location.getLocation().y * 3700000 + 1213324234234L); 274 Random random = new Random(seed); 275 276 int numFalse = random.nextInt(5); 277 //System.out.println(numFalse); 278 279 for (int i = 0; i < numFalse; i++) { 280 281 boolean constant = random.nextFloat() > 0.25f; 282 if (!constant && (float) Math.random() < neutrinoLowSkipProb) { 283 random.nextFloat(); 284 random.nextFloat(); 285 continue; 286 } 287 288 float arc = 15; 289 float angle = random.nextFloat() * 360f; 290 float in = 0.05f + 0.1f * (float) Math.random(); 291 in *= 0.25f; 292 float out = in; 293 out *= 2f; 294 295 float g = 80 + random.nextFloat() * 60; 296 297 GSPing ping = new GSPing(angle, arc, g, in, out); 298 ping.withSound = true; 299 pings.add(ping); 300 } 301 302 303 } 304 305 public float getRangeGMult(float range) { 306 range -= 3000; 307 if (range < 0) range = 0; 308 309 float max = 15000; 310 if (range > max) range = max; 311 312 313 return 1f - 0.85f * range / max; 314 } 315 316 317 public void maintainSlipstreamPings() { 318 CampaignFleetAPI fleet = ability.getFleet(); 319 Vector2f loc = fleet.getLocation(); 320 LocationAPI location = fleet.getContainingLocation(); 321 322 float range = GraviticScanAbility.SLIPSTREAM_DETECTION_RANGE; 323 324 if (Misc.isInsideSlipstream(fleet) || Misc.isInAbyss(fleet)) return; 325 326 for (CampaignTerrainAPI ter : location.getTerrainCopy()) { 327 if (ter.getPlugin() instanceof SlipstreamTerrainPlugin2) { 328 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) ter.getPlugin(); 329 if (plugin.containsEntity(fleet)) continue; 330 List<SlipstreamSegment> inRange = new ArrayList<SlipstreamSegment>(); 331 List<SlipstreamSegment> near = plugin.getSegmentsNear(loc, range); 332 int skip = 0; 333 for (SlipstreamSegment curr : near) { 334 if (skip > 0) { 335 skip--; 336 continue; 337 } 338 if (curr.bMult <= 0) continue; 339 float dist = Misc.getDistance(loc, curr.loc); 340 if (dist < range) { 341 inRange.add(curr); 342 skip = 5; 343 } 344 } 345 if (!inRange.isEmpty()) { 346 for (SlipstreamSegment curr : inRange) { 347 float dist = Misc.getDistance(loc, curr.loc); 348 349 float arc = Misc.computeAngleSpan(curr.width, dist); 350 arc *= 2f; 351 if (arc > 150f) arc = 150f; 352 if (arc < 20) arc = 20; 353 //arc += 30f; 354 float angle = Misc.getAngleInDegrees(loc, curr.loc); 355 float g = 500f; 356 g *= .1f; 357 g *= getRangeGMult(dist); 358 float in = planetInterval.getIntervalDuration() * 5f; 359 float out = in; 360 GSPing ping = new GSPing(angle, arc, g, in, out); 361 pings.add(ping); 362 } 363 } 364 } 365 } 366 367 } 368 369 370 public void maintainHighSourcePings() { 371 CampaignFleetAPI fleet = ability.getFleet(); 372 Vector2f loc = fleet.getLocation(); 373 LocationAPI location = fleet.getContainingLocation(); 374 375 maintainSlipstreamPings(); 376 377 boolean abyss = Misc.isInAbyss(fleet); 378 if (fleet.isInHyperspace() && !abyss) { 379 return; 380 } 381 382 383// Vector2f netForce = new Vector2f(); 384 385 List<SectorEntityToken> all = new ArrayList<SectorEntityToken>(location.getPlanets()); 386 for (Object object : location.getEntities(CustomCampaignEntityAPI.class)) { 387 if (object instanceof SectorEntityToken) { 388 SectorEntityToken entity = (SectorEntityToken) object; 389 if (abyss && !Misc.isInAbyss(entity)) continue; 390 391 boolean neutrinoHigh = entity.hasTag(Tags.NEUTRINO_HIGH); 392 if (neutrinoHigh) { 393 all.add(entity); 394 } 395 } 396 } 397 for (CampaignFleetAPI curr : location.getFleets()) { 398 if (fleet == curr) continue; 399 if (abyss && !Misc.isInAbyss(fleet)) continue; 400 boolean neutrinoHigh = curr.hasTag(Tags.NEUTRINO_HIGH); 401 if (neutrinoHigh) { 402 all.add(curr); 403 } 404 } 405 406 for (Object object : location.getEntities(OrbitalStationAPI.class)) { 407 if (object instanceof SectorEntityToken) { 408 SectorEntityToken entity = (SectorEntityToken) object; 409 if (abyss && !Misc.isInAbyss(entity)) continue; 410 all.add(entity); 411 } 412 } 413 414 for (Object object : location.getJumpPoints()) { 415 if (object instanceof SectorEntityToken) { 416 SectorEntityToken entity = (SectorEntityToken) object; 417 if (abyss && !Misc.isInAbyss(entity)) continue; 418 all.add(entity); 419 } 420 } 421 422 423 for (SectorEntityToken entity : all) { 424 if (entity instanceof PlanetAPI) { 425 PlanetAPI planet = (PlanetAPI) entity; 426 if (planet.getSpec().isNebulaCenter()) continue; 427 } 428 if (entity.getRadius() <= 0) continue; 429 430 float dist = Misc.getDistance(loc, entity.getLocation()); 431 432 float arc = Misc.computeAngleSpan(entity.getRadius(), dist); 433 arc *= 2f; 434 if (arc > 150f) arc = 150f; 435 if (arc < 20) arc = 20; 436 //arc += 30f; 437 float angle = Misc.getAngleInDegrees(loc, entity.getLocation()); 438 439 float g = getGravity(entity); 440 //g /= dist; 441 442 g *= .1f; 443 if (entity.hasTag(Tags.NEUTRINO_HIGH) || entity instanceof OrbitalStationAPI) { 444 g *= 2f; 445 } 446 447 g *= getRangeGMult(dist); 448 449// Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle); 450// dir.scale(g); 451// Vector2f.add(netForce, dir, netForce); 452// if (Misc.isInArc(90, 30, angle)) { 453// System.out.println("fwefewf"); 454// } 455 float in = planetInterval.getIntervalDuration() * 5f; 456 float out = in; 457 GSPing ping = new GSPing(angle, arc, g, in, out); 458 pings.add(ping); 459 } 460 461// for (String key : objectPings.keySet()) { 462// if (!seen.contains(key)) { 463// GSPing ping = objectPings.get(key); 464// ping.fader.setBounceDown(true); 465// } 466// } 467 468 //totalForce = netForce.length(); 469 //totalForce = maxG; 470 471 //System.out.println("Pings: " + pings.size()); 472 //System.out.println("Noise: " + getNoiseLevel()); 473 //System.out.println("Force: " + totalForce); 474 } 475 476// public float getTotalForce() { 477// return totalForce; 478// } 479// 480// public float getNoiseLevel() { 481// //if (true) return 0f; 482// 483// float minForce = 20f; 484// float noiseOneAt = 150; 485// 486// if (totalForce <= minForce) return 0f; 487// float noise = (totalForce - minForce) / (noiseOneAt - minForce); 488// if (noise > 1) noise = 1; 489// return noise; 490// } 491 492 public float getGravity(SectorEntityToken entity) { 493 float g = entity.getRadius(); 494 495 if (entity instanceof PlanetAPI) { 496 PlanetAPI planet = (PlanetAPI) entity; 497 //if (g < 200) g = 200; 498 499 g *= 2f; 500 501 if (planet.getSpec().isBlackHole()) { 502 g *= 2f; 503 } 504 } 505 506 if (entity instanceof OrbitalStationAPI) { 507 g *= 4f; 508 if (g > 200) g = 200; 509 } 510 511 if (entity instanceof CustomCampaignEntityAPI) { 512 g *= 4f; 513 if (g > 200) g = 200; 514 } 515 516 if (entity instanceof CampaignFleetAPI) { 517 g *= 2f; 518 if (g > 200) g = 200; 519 } 520 521// if (entity.getName().equals("Asteroid")) { 522// g *= 50f; 523// } 524 525 return g; 526 } 527 528} 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555