Starsector API
Loading...
Searching...
No Matches
AbyssalLightEntityPlugin.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign;
2
3import java.awt.Color;
4
5import org.lwjgl.util.vector.Vector2f;
6
7import com.fs.starfarer.api.EveryFrameScript;
8import com.fs.starfarer.api.Global;
9import com.fs.starfarer.api.SoundAPI;
10import com.fs.starfarer.api.campaign.CampaignEngineLayers;
11import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
12import com.fs.starfarer.api.campaign.CampaignFleetAPI;
13import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
14import com.fs.starfarer.api.campaign.CustomEntitySpecAPI;
15import com.fs.starfarer.api.campaign.SectorEntityToken;
16import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel;
17import com.fs.starfarer.api.combat.ViewportAPI;
18import com.fs.starfarer.api.graphics.SpriteAPI;
19import com.fs.starfarer.api.impl.campaign.abilities.InterdictionPulseAbility;
20import com.fs.starfarer.api.impl.campaign.enc.EncounterManager;
21import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
22import com.fs.starfarer.api.impl.campaign.ids.Tags;
23import com.fs.starfarer.api.impl.campaign.intel.events.ht.HTPoints;
24import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
25import com.fs.starfarer.api.util.FaderUtil;
26import com.fs.starfarer.api.util.FlickerUtilV2;
27import com.fs.starfarer.api.util.Misc;
28
30
31 public static enum DespawnType {
32 FADE_OUT,
33 EXPAND,
34 }
35
36 public static float SENSOR_BURST_TRIGGER_RANGE = 500f;
37 public static float DWELLER_TRIGGER_RANGE = 200f;
38
39 public static float MIN_DURATION = 20f;
40 public static float MAX_DURATION = 30f;
41
42 public static float MIN_SIZE = 600f;
43 public static float MAX_SIZE = 1200f;
44
45 public static float DESPAWN_POOF_THRESHOLD = 0.7f;
46
47 public static float GLOW_FREQUENCY = 0.2f; // on/off cycles per second
48
49 public static float PLAYER_PROXIMITY_MIN_BRIGHTNESS_AT = 2000f;
50 public static float PLAYER_PROXIMITY_MAX_BRIGHTNESS_AT = 300f;
51 public static float PLAYER_PROXIMITY_MIN_BRIGHTNESS = 0.25f;
52
53
54 public static class DwellerEncounterScript implements EveryFrameScript {
55 protected float elapsed = 0f;
56 protected String trigger;
57 protected boolean done = false;
58 protected float triggerDelay;
59 protected SectorEntityToken light;
60 protected CampaignFleetAPI fleet;
61 public DwellerEncounterScript(String trigger, CampaignFleetAPI fleet, SectorEntityToken light) {
62 this.trigger = trigger;
63 this.fleet = fleet;
64 this.light = light;
65 triggerDelay = 1f + (float) Math.random() * 1f;
66
67 }
68 public boolean isDone() {
69 return done;
70 }
71 public boolean runWhilePaused() {
72 return false;
73 }
74 public void advance(float amount) {
75 if (done) return;
76
78 return;
79 }
80
81 elapsed += amount;
82
83 if (fleet == null || elapsed > 3f || !light.isAlive() || light.hasTag(Tags.FADING_OUT_AND_EXPIRING) ||
85 done = true;
86 return;
87 }
88
89 float dist = Misc.getDistance(fleet, light) - fleet.getRadius() - light.getRadius();
90 if (dist > DWELLER_TRIGGER_RANGE) {
91 done = true;
92 return;
93 }
94
95
96 if (elapsed > triggerDelay) {
97 if (fleet.isPlayerFleet()) {
98 Misc.showRuleDialog(light, trigger);
99 } else {
100 fleet.despawn(FleetDespawnReason.DESTROYED_BY_BATTLE, light);
101 if (light.getCustomPlugin() instanceof AbyssalLightEntityPlugin) {
103 plugin.despawn(DespawnType.FADE_OUT);
104 }
105 }
106 done = true;
107 return;
108 }
109 }
110 }
111 public static class AbyssalLightSoundStopper implements EveryFrameScript {
112 protected transient SoundAPI sound = null;
113 protected float elapsed = 0f;
114 public AbyssalLightSoundStopper(SoundAPI sound) {
115 this.sound = sound;
116 }
117 public boolean isDone() {
118 return sound == null;
119 }
120 public boolean runWhilePaused() {
121 return true;
122 }
123 public void advance(float amount) {
124 elapsed += amount;
125 if (sound == null) return;
126 if (!sound.isPlaying() && elapsed > 1f) {
127 sound = null;
128 return;
129 }
130
131 if (Global.getSector().isPaused() && sound != null) {
132 sound.stop();
133 sound = null;
134 }
135 }
136 }
137 public static class AbyssalLightParams {
138 public Color color = new Color(200,200,255,255);
139 public float frequencyMultMin = 0.5f;
140 public float frequencyMultMax = 1.5f;
141 public float frequencyChangeMult = 1f;
142 public float size;
143 public float detectedRange;
144 public float bonusMult;
145 public float durationDays;
146 public int pointsOverride = 0;
147
148 public AbyssalLightParams() {
149 this(MIN_SIZE, MAX_SIZE);
150 }
151 public AbyssalLightParams(float minSize, float maxSize) {
152 frequencyMultMin = 0.5f;
153 frequencyMultMax = 1.5f;
154
155 float sizeRange = maxSize - minSize;
156 float avgSize = MIN_SIZE + (MAX_SIZE - MIN_SIZE) * 0.5f;
157
158
159 size = minSize + (float) Math.random() * sizeRange;
160 detectedRange = (size * 3f) / HyperspaceTerrainPlugin.ABYSS_SENSOR_RANGE_MULT;
161
162 bonusMult = size / avgSize;
163
164 durationDays = MIN_DURATION + (MAX_DURATION - MIN_DURATION) * (float) Math.random();
165 }
166
167 public int getTopographyPoints() {
168 if (pointsOverride > 0) return pointsOverride;
169
170 return Math.round(HTPoints.ABYSSAL_LIGHT_MIN +
171 (HTPoints.ABYSSAL_LIGHT_AVG - HTPoints.ABYSSAL_LIGHT_MIN) * bonusMult);
172 }
173 }
174
175
176 transient private SpriteAPI glow;
177
178
179 protected float phase = 0f;
180 protected float frequencyMult = 1f;
182 protected FaderUtil fader = new FaderUtil(0f, 0.5f, 0.5f);
183 protected AbyssalLightParams params;
184 protected float untilFrequencyChange = 0f;
185 protected float untilSoundPlayed = 0f;
186 protected float flickerDur = 0f;
187 protected float abilityResponseFlickerTimeout = 0f;
188 protected DespawnType despawnType = null;
189 protected boolean playedDespawnSound = false;
190
191 public void init(SectorEntityToken entity, Object pluginParams) {
192 super.init(entity, pluginParams);
193 params = (AbyssalLightParams) pluginParams;
194
195 float radiusMult = params.bonusMult;
196 if (radiusMult > 1f) {
197 radiusMult = 1f + (radiusMult - 1f) * 0.33f;
198 } else if (radiusMult < 1f) {
199 radiusMult = 1f - (1f - radiusMult) * 0.33f;
200 }
201 ((CustomCampaignEntityAPI) entity).setRadius(entity.getRadius() * radiusMult);
203 entity.setDiscoverable(false);
204 //entity.setDiscoveryXP(spec.getDiscoveryXP());
205
206 entity.getDetectedRangeMod().modifyFlat("gen", params.detectedRange);
210
211 updateFrequency(0f);
213 readResolve();
214 }
215
216 public boolean isDespawning() {
218 }
219
220 public void despawn(DespawnType type) {
221 if (isDespawning()) return;
222
223 float dur = 1f;
224 if (type == DespawnType.EXPAND) dur = 2f;
226 despawnType = type;
227
228 if (type == DespawnType.EXPAND && entity.isInCurrentLocation()) {
229 VisibilityLevel level = entity.getVisibilityLevelToPlayerFleet();
230 if (level == VisibilityLevel.COMPOSITION_DETAILS || level == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS) {
231 Global.getSoundPlayer().playSound("abyssal_light_expand_despawn_windup", 1f, 1f, entity.getLocation(), Misc.ZERO);
232 }
233 }
234 }
235
236 public void updateSoundDelayAndPlaySound(float amount) {
237 untilSoundPlayed -= amount;
238 if (untilSoundPlayed <= 0f) {
239 //untilSoundPlayed = 0.5f + (float) Math.random() * 20f;
240 untilSoundPlayed = 5f + (float) Math.random() * 30f;
241
242 // don't play sound when called from the constructor with amount == 0f
243 if (amount > 0 && entity.isInCurrentLocation()) {
244 VisibilityLevel level = entity.getVisibilityLevelToPlayerFleet();
245 if (level == VisibilityLevel.COMPOSITION_DETAILS || level == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS) {
246 float volume = 1f;
247 if (params.bonusMult < 1f) {
248 volume = 1f - (1f - params.bonusMult) * 0.6f;
249 }
250 if (volume > 1f) volume = 1f;
251 if (volume < 0f) volume = 0f;
252
253 float pitchMult = 1f;
254 if (isDweller()) {
255 pitchMult = 0.5f;
256 }
257
258 SoundAPI sound = Global.getSoundPlayer().playSound("abyssal_light_random_sound", 1f * pitchMult, volume, entity.getLocation(), Misc.ZERO);
259 Global.getSector().addScript(new AbyssalLightSoundStopper(sound));
260 //flickerDur = 3f + 2f * (float) Math.random();
261 flickerDur = 0.5f + 0.5f * (float) Math.random();
262
263// too random
264// if (isDweller()) {
265// CampaignFleetAPI fleet = Global.getSector().getPlayerFleet();
266// if (fleet != null && abilityResponseFlickerTimeout <= 0f) {
267// flickerDur = 0.5f + 0.5f * (float) Math.random();
268// abilityResponseFlickerTimeout = 3f;
269// float dist = Misc.getDistance(fleet, entity);
270// if (dist < DWELLER_TRIGGER_RANGE + fleet.getRadius() + entity.getRadius()) {
271// Global.getSector().addScript(new DwellerEncounterScript("DwellerAttackAfterAbilityUse", entity));
272// }
273// }
274// }
275 }
276 }
277 }
278 }
279 public void updateFrequency(float amount) {
280 untilFrequencyChange -= amount * params.frequencyChangeMult;
281 if (untilFrequencyChange <= 0f) {
282 untilFrequencyChange = 1f + (float) Math.random() * 3f;
283
284 frequencyMult = params.frequencyMultMin *
285 (float) Math.random() * (params.frequencyMultMax - params.frequencyMultMin);
286 }
287 }
288
289 Object readResolve() {
290 glow = Global.getSettings().getSprite("campaignEntities", "abyssal_light_glow");
291 return this;
292 }
293
294 public AbyssalLightParams getParams() {
295 return params;
296 }
297
298 public boolean isDweller() {
299 return entity != null && entity.hasTag(Tags.DWELLER_LIGHT);
300 }
301
302 public void advance(float amount) {
303 if (isDespawning()) {
304 if (!playedDespawnSound &&
305 (getDespawnProgress() > DESPAWN_POOF_THRESHOLD || despawnType == DespawnType.FADE_OUT)) {
306 playedDespawnSound = true;
308 VisibilityLevel level = entity.getVisibilityLevelToPlayerFleet();
309 if (level == VisibilityLevel.COMPOSITION_DETAILS || level == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS) {
310 String soundId = "abyssal_light_despawn_disrupted";
311 if (despawnType == DespawnType.FADE_OUT) {
312 soundId = "abyssal_light_despawn_expired";
313 }
315 }
316 }
317 }
318
319 return;
320 }
321
322 //boolean dweller = isDespawning();
323
324 float days = Misc.getDays(amount);
325 params.durationDays -= days;
326 if (params.durationDays <= 0f || Misc.getAbyssalDepth(entity) < 1f) {
327 despawn(DespawnType.FADE_OUT);
328 return;
329 }
330
331 updateFrequency(amount);
333
334 phase += amount * GLOW_FREQUENCY * frequencyMult;
335 while (phase > 1) phase --;
336
337 if (flickerDur > 0) {
338 flickerDur -= amount;
339 if (flickerDur < 0) flickerDur = 0;
340 }
344 //System.out.println("Timeout: " + abilityResponseFlickerTimeout);
345 }
346
347 flicker.advance(amount * 10f);
348 fader.advance(amount);
349
351 if (entity.getContainingLocation() != null) {
353 float range = InterdictionPulseAbility.getRange(fleet);
354 float dist = Misc.getDistance(fleet.getLocation(), entity.getLocation());
355 if (dist > range) continue;
356
357
358 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.JUST_DID_INTERDICTION_PULSE)) {
359 if (isDweller()) {
361 flickerDur = 0.5f + 0.5f * (float) Math.random();
363 if (dist < DWELLER_TRIGGER_RANGE + fleet.getRadius() + entity.getRadius()) {
364 Global.getSector().addScript(new DwellerEncounterScript("DwellerAttackAfterAbilityUse", fleet, entity));
365 }
366 }
367 } else {
369
370 float brightness = getProximityBasedBrightnessFactor(fleet, entity.getLocation());
371 bonus.addBurnBonus(params.bonusMult * brightness);
372
373 if (fleet.isPlayerFleet()) {
374 bonus.addTopographyPoints(params.getTopographyPoints());
375 }
376
377 despawn(DespawnType.EXPAND);
378 }
379 }
380 }
381 }
382 if (isDespawning()) return;
383 }
384
386 if (entity.getContainingLocation() != null) {
388 float range = SENSOR_BURST_TRIGGER_RANGE + fleet.getRadius() + entity.getRadius();
389 float dist = Misc.getDistance(fleet.getLocation(), entity.getLocation());
390 if (dist > range) continue;
391
392
393 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.JUST_DID_SENSOR_BURST)) {
394 if (isDweller()) {
396 flickerDur = 0.5f + 0.5f * (float) Math.random();
398 if (dist < DWELLER_TRIGGER_RANGE + fleet.getRadius() + entity.getRadius()) {
399 Global.getSector().addScript(new DwellerEncounterScript("DwellerAttackAfterAbilityUse", fleet, entity));
400 }
401 }
402 } else {
404
405 float brightness = getProximityBasedBrightnessFactor(fleet, entity.getLocation());
406 bonus.addSensorBonus(params.bonusMult * brightness);
407
408 if (fleet.isPlayerFleet()) {
409 bonus.addTopographyPoints(params.getTopographyPoints());
410
411 // can cause overlapping gravity wells etc
412 //EncounterManager.getInstance().getPointTimeout().clear();
414 }
415
416 despawn(DespawnType.EXPAND);
417 }
418 }
419 }
420 }
421 if (isDespawning()) return;
422 }
423 }
424
425 public float getFlickerBasedMult() {
426 if (flickerDur <= 0f) return 1f;
427
428 float mult = 1f;
429 if (flickerDur < 1f) mult = flickerDur;
430
431 if (isDweller()) {
432 mult *= 0.75f;
433 } else {
434 mult *= 0.25f;
435 }
436
437 float f = 1f - flicker.getBrightness() * mult;
438 return f;
439 }
440
441 public float getGlowAlpha() {
442 float glowAlpha = 0f;
443 if (phase < 0.5f) glowAlpha = phase * 2f;
444 if (phase >= 0.5f) glowAlpha = (1f - (phase - 0.5f) * 2f);
445 glowAlpha = 0.75f + glowAlpha * 0.25f;
446
447 if (glowAlpha < 0) glowAlpha = 0;
448 if (glowAlpha > 1) glowAlpha = 1;
449 return glowAlpha;
450 }
451
452
453
454 public float getRenderRange() {
455 return entity.getRadius() + params.size * 0.75f;
456 }
457
458 public float getDespawnProgress() {
459 if (!isDespawning()) return 0f;
461 return 1 - f;
462 }
463
464 public void render(CampaignEngineLayers layer, ViewportAPI viewport) {
465 float alphaMult = viewport.getAlphaMult();
466
467 if (!isDespawning() || despawnType == DespawnType.FADE_OUT) {
468 alphaMult *= entity.getSensorFaderBrightness();
469 }
470
471 if (alphaMult <= 0) return;
472
473 float f = getDespawnProgress();
474 if (despawnType == DespawnType.FADE_OUT) {
475 f = 0f;
476 }
477
478 if (f > DESPAWN_POOF_THRESHOLD) {
479 float fadeOut = (1f - f) / (1f - DESPAWN_POOF_THRESHOLD);
480 alphaMult *= fadeOut;
481 }
482
483 VisibilityLevel level = entity.getVisibilityLevelToPlayerFleet();
484 if (level == VisibilityLevel.COMPOSITION_DETAILS || level == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS) {
485 fader.fadeIn();
486 } else {
487 fader.fadeOut();
488 }
489
490 alphaMult *= fader.getBrightness();
491
493 alphaMult *= b;
494
495
496// if (Misc.getDistanceToPlayerLY(entity) > 0.1f) {
497// Misc.fadeAndExpire(entity);
498// return;
499// }
500// if (!Global.getSector().isPaused()) {
501// System.out.println("Alpha: " + alphaMult);
502// }
503
505 if (spec == null) return;
506
507 float w = spec.getSpriteWidth();
508 float h = spec.getSpriteHeight();
509
510 Vector2f loc = entity.getLocation();
511
512 float glowAlpha = getGlowAlpha();
513
514 glow.setColor(params.color);
515
516 w = params.size;
517 h = params.size;
518
519 float scale = 0.25f + alphaMult * 0.75f;
520 if (f > DESPAWN_POOF_THRESHOLD) {
521 scale *= 1.5f;
522 }
523
524 float fringeScale = 1f;
525 //fringeScale = 1.5f;
526
527 glow.setAdditiveBlend();
528
529 if (layer == CampaignEngineLayers.TERRAIN_8) {
530 //float flicker = getFlickerBasedMult();
531 glow.setAlphaMult(alphaMult * glowAlpha * 0.5f);
532 glow.setSize(w * scale * fringeScale, h * scale * fringeScale);
533
534 glow.renderAtCenter(loc.x, loc.y);
535 }
536
537 if (layer == CampaignEngineLayers.STATIONS) {
538 if (f > 0f) {
539 float extra = 1f + f * 1.33f;
540 if (f > DESPAWN_POOF_THRESHOLD) extra = 0f;
541 scale *= extra;
542 }
544 for (int i = 0; i < 5; i++) {
545 if (i != 0) flicker = 1f;
546
547 w *= 0.3f;
548 h *= 0.3f;
549 glow.setSize(w * scale, h * scale);
550 glow.setAlphaMult(alphaMult * glowAlpha * 0.67f * flicker);
551 glow.renderAtCenter(loc.x, loc.y);
552 }
553 }
554 }
555
556
560 public static float getPlayerProximityBasedBrightnessFactor(Vector2f loc) {
562 if (player == null) {
564 }
565 return getProximityBasedBrightnessFactor(player, loc);
566 }
567
568 public static float getProximityBasedBrightnessFactor(CampaignFleetAPI from, Vector2f loc) {
569
570 float dist = Misc.getDistance(from.getLocation(), loc) - from.getRadius();
571
573 return 1f;
574 //return Math.max(PLAYER_PROXIMITY_MIN_BRIGHTNESS, dist / PLAYER_PROXIMITY_MAX_BRIGHTNESS_AT);
575 }
577
579 if (f < 0f) f = 0f;
580
582
583 return b;
584 }
585
586
587// @Override
588// public void createMapTooltip(TooltipMakerAPI tooltip, boolean expanded) {
589// String post = "";
590// Color color = entity.getFaction().getBaseUIColor();
591// Color postColor = color;
592// if (entity.getMemoryWithoutUpdate().getBoolean(RemnantSystemType.DESTROYED.getBeaconFlag())) {
593// post = " - Low";
594// postColor = Misc.getPositiveHighlightColor();
595// } else if (entity.getMemoryWithoutUpdate().getBoolean(RemnantSystemType.SUPPRESSED.getBeaconFlag())) {
596// post = " - Medium";
597// postColor = Misc.getHighlightColor();
598// } else if (entity.getMemoryWithoutUpdate().getBoolean(RemnantSystemType.RESURGENT.getBeaconFlag())) {
599// post = " - High";
600// postColor = Misc.getNegativeHighlightColor();
601// }
602//
603// tooltip.addPara(entity.getName() + post, 0f, color, postColor, post.replaceFirst(" - ", ""));
604// }
605//
606// @Override
607// public boolean hasCustomMapTooltip() {
608// return true;
609// }
610//
611// @Override
612// public void appendToCampaignTooltip(TooltipMakerAPI tooltip, VisibilityLevel level) {
613// if (level == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS ||
614// level == VisibilityLevel.COMPOSITION_DETAILS) {
615//
616// String post = "";
617// Color color = Misc.getTextColor();
618// Color postColor = color;
619// if (entity.getMemoryWithoutUpdate().getBoolean(RemnantSystemType.DESTROYED.getBeaconFlag())) {
620// post = "low";
621// postColor = Misc.getPositiveHighlightColor();
622// } else if (entity.getMemoryWithoutUpdate().getBoolean(RemnantSystemType.SUPPRESSED.getBeaconFlag())) {
623// post = "medium";
624// postColor = Misc.getHighlightColor();
625// } else if (entity.getMemoryWithoutUpdate().getBoolean(RemnantSystemType.RESURGENT.getBeaconFlag())) {
626// post = "high";
627// postColor = Misc.getNegativeHighlightColor();
628// }
629// if (!post.isEmpty()) {
630// tooltip.setParaFontDefault();
631// tooltip.addPara(BaseIntelPlugin.BULLET + "Danger level: " + post, 10f, color, postColor, post);
632// }
633// }
634//
635// }
636}
637
638
639
640
641
642
643
644
645
static SettingsAPI getSettings()
Definition Global.java:57
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:49
static SectorAPI getSector()
Definition Global.java:65
void modifyFlat(String source, float value)
static AbyssalLightBonus get(CampaignFleetAPI fleet)
static float getPlayerProximityBasedBrightnessFactor(SectorEntityToken entity)
static float getProximityBasedBrightnessFactor(CampaignFleetAPI from, Vector2f loc)
void init(SectorEntityToken entity, Object pluginParams)
void render(CampaignEngineLayers layer, ViewportAPI viewport)
static final String GLOBAL_INTERDICTION_PULSE_JUST_USED_IN_CURRENT_LOCATION
Definition MemFlags.java:53
static final String GLOBAL_SENSOR_BURST_JUST_USED_IN_CURRENT_LOCATION
Definition MemFlags.java:49
static final String FADING_OUT_AND_EXPIRING
Definition Tags.java:341
static final Vector2f ZERO
Definition Misc.java:249
static void fadeAndExpire(SectorEntityToken entity)
Definition Misc.java:3133
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static float getDays(float amount)
Definition Misc.java:4663
static float getAbyssalDepth(Vector2f loc)
Definition Misc.java:2330
static boolean showRuleDialog(SectorEntityToken entity, String initialTrigger)
Definition Misc.java:1103
SpriteAPI getSprite(String filename)
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)
List< CampaignFleetAPI > getFleets()
void addScript(EveryFrameScript script)
CustomCampaignEntityPlugin getCustomPlugin()
void setDetectionRangeDetailsOverrideMult(Float detectionRangeDetailsOverrideMult)
void setExtendedDetectedAtRange(Float extendedDetectedAtRange)
void setDiscoverable(Boolean discoverable)
void set(String key, Object value)