Starsector API
Loading...
Searching...
No Matches
PulsarBeamTerrainPlugin.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.terrain;
2
3import java.awt.Color;
4import java.util.EnumSet;
5
6import org.lwjgl.util.vector.Vector2f;
7
8import com.fs.starfarer.api.Global;
9import com.fs.starfarer.api.campaign.CampaignEngineLayers;
10import com.fs.starfarer.api.campaign.CampaignFleetAPI;
11import com.fs.starfarer.api.campaign.PlanetAPI;
12import com.fs.starfarer.api.campaign.SectorEntityToken;
13import com.fs.starfarer.api.campaign.TerrainAIFlags;
14import com.fs.starfarer.api.combat.ViewportAPI;
15import com.fs.starfarer.api.fleet.FleetMemberAPI;
16import com.fs.starfarer.api.fleet.FleetMemberViewAPI;
17import com.fs.starfarer.api.graphics.SpriteAPI;
18import com.fs.starfarer.api.impl.campaign.ids.Stats;
19import com.fs.starfarer.api.impl.campaign.terrain.PulsarRenderer.PulsarRendererDelegate;
20import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin.CoronaParams;
21import com.fs.starfarer.api.loading.Description.Type;
22import com.fs.starfarer.api.ui.Alignment;
23import com.fs.starfarer.api.ui.TooltipMakerAPI;
24import com.fs.starfarer.api.util.Misc;
25
26public class PulsarBeamTerrainPlugin extends BaseRingTerrain implements PulsarRendererDelegate {
27
28
29 public static float PULSAR_ARC = 1f / ((float) Math.PI * 2f) * 360f;
30 //public static float PULSAR_ARC = 0.25f / ((float) Math.PI * 2f) * 360f;
31
32 transient protected SpriteAPI flareTexture = null;
33 transient Color color = null;
34
35 transient protected PulsarRenderer flare1, flare2;
36 protected CoronaParams params;
37 protected transient RangeBlockerUtil blocker = null;
38
39 protected float pulsarAngle = (float) Math.random() * 360f;
40 protected float pulsarRotation = -1f * (10f + (float) Math.random() * 10f);
41
42 public void init(String terrainId, SectorEntityToken entity, Object param) {
43 super.init(terrainId, entity, param);
44 params = (CoronaParams) param;
45 name = params.name;
46 if (name == null) {
47 name = "Pulsar Beam";
48 }
49 }
50
51 public String getNameForTooltip() {
52 return "Pulsar Beam";
53 }
54
55 @Override
56 protected Object readResolve() {
57 super.readResolve();
58 //flareTexture = Global.getSettings().getSprite("graphics/fx/beam_weave_fringe.png");
59 flareTexture = Global.getSettings().getSprite("terrain", "pulsar");
60
61 layers = EnumSet.of(CampaignEngineLayers.TERRAIN_7);
62 if (blocker == null) {
63 //blocker = new RangeBlockerUtil(360, super.params.bandWidthInEngine + 1000f);
64 blocker = new RangeBlockerUtil(720 * 2, super.params.bandWidthInEngine + 1000f);
65 }
66
67 flare1 = new PulsarRenderer(this);
68 flare2 = new PulsarRenderer(this);
69 return this;
70 }
71
72 Object writeReplace() {
73 return this;
74 }
75
76 @Override
77 protected boolean shouldPlayLoopOne() {
78 return super.shouldPlayLoopOne() && containsEntity(Global.getSector().getPlayerFleet());
79 }
80
81 @Override
82 protected float getLoopOneVolume() {
83 float intensity = getIntensityAtPoint(Global.getSector().getPlayerFleet().getLocation());
84 return intensity;
85 }
86
87 @Override
88 protected float getExtraSoundRadius() {
89 return 0f;
90// float base = super.getExtraSoundRadius();
91//
92// //float angle = Misc.getAngleInDegrees(params.relatedEntity.getLocation(), Global.getSector().getPlayerFleet().getLocation());
93// float extra = 0f;
94//
95// return base + extra;
96 }
97
98
99 transient private EnumSet<CampaignEngineLayers> layers = EnumSet.of(CampaignEngineLayers.TERRAIN_7);
100 public EnumSet<CampaignEngineLayers> getActiveLayers() {
101 return layers;
102 }
103
104 public CoronaParams getParams() {
105 return params;
106 }
107
108 public void advance(float amount) {
109 super.advance(amount);
110
111 float days = Global.getSector().getClock().convertToDays(amount);
112 pulsarAngle += pulsarRotation * days * 1f;
113 pulsarAngle = Misc.normalizeAngle(pulsarAngle);
114 //pulsarAngle += pulsarRotation * days * 0.5f;
115
116// if (params.relatedEntity instanceof PlanetAPI) {
117// PlanetAPI planet = (PlanetAPI) params.relatedEntity;
118// planet.getSpec().setTilt(pulsarAngle);
119// planet.applySpecChanges();
120// }
121
122 flare1.advance(amount);
123 flare2.advance(amount);
124
127
128 //pulsarAngle += pulsarRotation * days * 10.25f;
129 //pulsarAngle += pulsarRotation * days * 5f;
130
131 if (amount > 0 && blocker != null) {
132 blocker.updateLimits(entity, params.relatedEntity, 0.5f);
133 //blocker.sync();
134 blocker.advance(amount, 100f, 0.5f);
135 }
136
137
138 }
139
140 public void render(CampaignEngineLayers layer, ViewportAPI viewport) {
141 if (blocker != null && !blocker.wasEverUpdated()) {
142 blocker.updateAndSync(entity, params.relatedEntity, 0.1f);
143 }
144
145
146 if (isNearViewport(pulsarAngle, viewport)) {
147 flare1.render(viewport.getAlphaMult());
148 }
149// else {
150// System.out.println("SKIP1");
151// }
152
153 if (isNearViewport(pulsarAngle + 180f, viewport)) {
154 flare2.render(viewport.getAlphaMult());
155 }
156// else {
157// System.out.println("SKIP2");
158// }
159 }
160
161
162 protected boolean isNearViewport(float angle, ViewportAPI viewport) {
163 float wClose = getPulsarInnerWidth();
164 float wFar = getPulsarOuterWidth();
165 float distClose = getPulsarInnerRadius();
166 float distFar = getPulsarOuterRadius();
167
168 float length = distFar - distClose;
169 float incr = (float) Math.ceil((distFar - distClose) / 2000f);
170
171 for (float dist = wClose; dist < distFar; dist += incr) {
172 Vector2f test = Misc.getUnitVectorAtDegreeAngle(angle);
173 test.scale(dist);
174 Vector2f.add(test, entity.getLocation(), test);
175
176 float testDist = wClose + (wFar - wClose) * (dist - distClose) / length;
177 testDist *= 0.5f;
178 if (viewport.isNearViewport(test, testDist + 500f)) {
179 return true;
180 }
181 }
182 return false;
183 }
184
185
186 @Override
187 public float getRenderRange() {
188 return getPulsarOuterRadius() + 1000f;
189 }
190
191 @Override
192 public boolean containsPoint(Vector2f point, float radius) {
193 if (blocker != null && blocker.isAnythingShortened()) {
194 float angle = Misc.getAngleInDegreesStrict(this.entity.getLocation(), point);
195 float dist = Misc.getDistance(this.entity.getLocation(), point);
196 float max = blocker.getCurrMaxAt(angle);
197 if (dist > max) return false;
198 }
199
200 if (!Misc.isInArc(pulsarAngle, PULSAR_ARC, entity.getLocation(), point) &&
201 !Misc.isInArc(pulsarAngle + 180f, PULSAR_ARC, entity.getLocation(), point)) {
202 return false;
203 }
204
205 float dist = Misc.getDistance(this.entity.getLocation(), point);
206 if (dist < getPulsarInnerRadius()) return false;
207
208 return super.containsPoint(point, radius);
209 }
210
211
212
213 @Override
214 public void applyEffect(SectorEntityToken entity, float days) {
215 if (entity instanceof CampaignFleetAPI) {
216 CampaignFleetAPI fleet = (CampaignFleetAPI) entity;
217
218 float intensity = getIntensityAtPoint(fleet.getLocation());
219 if (intensity <= 0) return;
220
221 String buffId = getModId();
222 float buffDur = 0.1f;
223
224 // CR loss
225 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
226 float recoveryRate = member.getStats().getBaseCRRecoveryRatePercentPerDay().getModifiedValue();
227 float lossRate = member.getStats().getBaseCRRecoveryRatePercentPerDay().getBaseValue();
228
229 float resistance = member.getStats().getDynamic().getValue(Stats.CORONA_EFFECT_MULT);
230 //if (inFlare) loss *= 2f;
231 float lossMult = 1f;
232 float adjustedLossMult = (0f + params.crLossMult * intensity * resistance * lossMult * StarCoronaTerrainPlugin.CR_LOSS_MULT_GLOBAL);
233
234 float loss = (-1f * recoveryRate + -1f * lossRate * adjustedLossMult) * days * 0.01f;
235 float curr = member.getRepairTracker().getBaseCR();
236 if (loss > curr) loss = curr;
237
238 if (resistance > 0) {
239 member.getRepairTracker().applyCREvent(loss, "corona", "Pulsar beam effect");
240 }
241
242 float peakFraction = 1f / Math.max(1.3333f, 1f + params.crLossMult * intensity);
243 float peakLost = 1f - peakFraction;
244 peakLost *= resistance;
245
246 float degradationMult = 1f + (params.crLossMult * intensity * resistance) / 2f;
247
248 member.getBuffManager().addBuffOnlyUpdateStat(new PeakPerformanceBuff(buffId + "_1", 1f - peakLost, buffDur));
249 member.getBuffManager().addBuffOnlyUpdateStat(new CRLossPerSecondBuff(buffId + "_2", degradationMult, buffDur));
250 }
251
252 // "wind" effect - adjust velocity
253 float maxFleetBurn = fleet.getFleetData().getBurnLevel();
254 float currFleetBurn = fleet.getCurrBurnLevel();
255
256 float maxWindBurn = params.windBurnLevel;
257
258
259 float currWindBurn = intensity * maxWindBurn;
260 float maxFleetBurnIntoWind = maxFleetBurn - Math.abs(currWindBurn);
261
262 float angle = Misc.getAngleInDegreesStrict(this.entity.getLocation(), fleet.getLocation());
263 Vector2f windDir = Misc.getUnitVectorAtDegreeAngle(angle);
264 if (currWindBurn < 0) {
265 windDir.negate();
266 }
267
268 Vector2f velDir = Misc.normalise(new Vector2f(fleet.getVelocity()));
269 velDir.scale(currFleetBurn);
270
271 float fleetBurnAgainstWind = -1f * Vector2f.dot(windDir, velDir);
272
273 float accelMult = 0.5f;
274 if (fleetBurnAgainstWind > maxFleetBurnIntoWind) {
275 accelMult += 0.75f + 0.25f * (fleetBurnAgainstWind - maxFleetBurnIntoWind);
276 }
277
278 float seconds = days * Global.getSector().getClock().getSecondsPerDay();
279
280 Vector2f vel = fleet.getVelocity();
281 windDir.scale(seconds * fleet.getAcceleration() * accelMult);
282 fleet.setVelocity(vel.x + windDir.x, vel.y + windDir.y);
283
284// if (fleet.getOrbit() != null) {
285// fleet.setOrbit(null);
286// }
287
288 Color glowColor = getPulsarColorForAngle(angle);
289 int alpha = glowColor.getAlpha();
290 if (alpha < 75) {
291 glowColor = Misc.setAlpha(glowColor, 75);
292 }
293 // visual effects - glow, tail
294
295 float durIn = 1f;
296 float durOut = 3f;
297 Misc.normalise(windDir);
298 float sizeNormal = 10f + 25f * intensity;
299 for (FleetMemberViewAPI view : fleet.getViews()) {
300 view.getWindEffectDirX().shift(getModId(), windDir.x * sizeNormal, durIn, durOut, 1f);
301 view.getWindEffectDirY().shift(getModId(), windDir.y * sizeNormal, durIn, durOut, 1f);
302 view.getWindEffectColor().shift(getModId(), glowColor, durIn, durOut, intensity);
303 }
304 }
305 }
306
307
308
309 public float getIntensityAtPoint(Vector2f point) {
310 float maxDist = params.bandWidthInEngine;
311 float minDist = params.relatedEntity.getRadius();
312 float dist = Misc.getDistance(point, params.relatedEntity.getLocation());
313
314 if (dist > maxDist) return 0f;
315
316 float intensity = 1f;
317 if (minDist < maxDist) {
318 intensity = 1f - (dist - minDist) / (maxDist - minDist);
319 //intensity = 0.5f + intensity * 0.5f;
320 if (intensity < 0) intensity = 0;
321 if (intensity > 1) intensity = 1;
322 }
323
324 float angle = Misc.getAngleInDegreesStrict(params.relatedEntity.getLocation(), point);
325 float diff = Misc.getAngleDiff(angle, pulsarAngle);
326 diff = Math.min(diff, Misc.getAngleDiff(angle, pulsarAngle + 180f));
327 float maxDiff = PULSAR_ARC / 2f;
328 if (diff > maxDiff) diff = maxDiff;
329
330 if (diff > maxDiff * 0.5f) {
331 intensity *= 0.25f + 0.75f * (1f - (diff - maxDiff * 0.5f) / (maxDiff * 0.5f));
332 }
333
334 return intensity;
335 }
336
337
338
339 @Override
340 public Color getNameColor() {
341 Color bad = Misc.getNegativeHighlightColor();
342 Color base = super.getNameColor();
343 //bad = Color.red;
344 return Misc.interpolateColor(base, bad, Global.getSector().getCampaignUI().getSharedFader().getBrightness() * 1f);
345 }
346
347 public boolean hasTooltip() {
348 return true;
349 }
350
351 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) {
352 float pad = 10f;
353 float small = 5f;
354 Color gray = Misc.getGrayColor();
355 Color highlight = Misc.getHighlightColor();
356 Color fuel = Global.getSettings().getColor("progressBarFuelColor");
357 Color bad = Misc.getNegativeHighlightColor();
358
359 tooltip.addTitle("Pulsar Beam");
360 tooltip.addPara(Global.getSettings().getDescription(getTerrainId(), Type.TERRAIN).getText1(), pad);
361
362 float nextPad = pad;
363 if (expanded) {
364 tooltip.addSectionHeading("Travel", Alignment.MID, pad);
365 nextPad = small;
366 }
367 tooltip.addPara("Reduces the combat readiness of " +
368 "all ships caught in the pulsar beam at a rapid pace, and blows the fleet off-course.", nextPad);
369 tooltip.addPara("The magnitude of the effect drops off rapidly with distance from the source.", pad);
370
371 if (expanded) {
372 tooltip.addSectionHeading("Combat", Alignment.MID, pad);
373 tooltip.addPara("Reduces the peak performance time of ships and increases the rate of combat readiness degradation in protracted engagements.", small);
374 }
375
376 //tooltip.addPara("Does not stack with other similar terrain effects.", pad);
377 }
378
379 public boolean isTooltipExpandable() {
380 return true;
381 }
382
383 public float getTooltipWidth() {
384 return 350f;
385 }
386
387 public String getTerrainName() {
388 return super.getTerrainName();
389 }
390
391 public String getEffectCategory() {
392 return null; // to ensure multiple coronas overlapping all take effect
393 //return "corona_" + (float) Math.random();
394 }
395
396 public boolean hasAIFlag(Object flag, CampaignFleetAPI fleet) {
397 if (fleet != null && containsEntity(fleet)) {
398 return hasAIFlag(flag);
399 }
400 return false;
401 }
402
403 public boolean hasAIFlag(Object flag) {
404 return flag == TerrainAIFlags.CR_DRAIN ||
405 flag == TerrainAIFlags.BREAK_OTHER_ORBITS ||
406 flag == TerrainAIFlags.EFFECT_DIMINISHED_WITH_RANGE;
407 }
408
409 public float getMaxEffectRadius(Vector2f locFrom) {
410 //float angle = Misc.getAngleInDegrees(params.relatedEntity.getLocation(), locFrom);
411 float maxDist = params.bandWidthInEngine;
412 return maxDist;
413 }
414 public float getMinEffectRadius(Vector2f locFrom) {
415 return 0f;
416 }
417
418 public float getOptimalEffectRadius(Vector2f locFrom) {
419 return params.relatedEntity.getRadius();
420 }
421
422 public boolean canPlayerHoldStationIn() {
423 return false;
424 }
425
426
428 //return null;
429 return blocker;
430 }
431
432 public Vector2f getPulsarCenterLoc() {
433 return params.relatedEntity.getLocation();
434 }
435
436 public Color getPulsarColorForAngle(float angle) {
437 if (color == null) {
438 Color c = Color.white;
439 if (params.relatedEntity instanceof PlanetAPI) {
440 c = ((PlanetAPI)params.relatedEntity).getSpec().getCoronaColor();
441 } else {
442 c = Color.white;
443 }
444 float alpha = 1f;
445 color = Misc.setAlpha(c, (int) (200 * alpha));
446 return color;
447 } else {
448 return color;
449 }
450 }
451
452
453 public float getPulsarInnerRadius() {
454 return params.relatedEntity.getRadius();
455 }
456
457
458 public float getPulsarOuterRadius() {
459 return params.middleRadius + params.bandWidthInEngine * 0.5f;
460 }
461
462 public float getPulsarInnerWidth() {
463 //PULSAR_ARC = 1f / ((float) Math.PI * 2f) * 360f;
464 return PULSAR_ARC / 360f * 2f * (float) Math.PI * getPulsarInnerRadius();
465 //return PULSAR_ARC / 360f * 2f * (float) Math.PI * getPulsarInnerRadius();
466 }
467
468 public float getPulsarOuterWidth() {
469 float r1 = getPulsarInnerRadius();
470 float r2 = getPulsarOuterRadius();
471 return getPulsarInnerWidth() * r2 / r1;
472 }
473
474 public float getPulsarScrollSpeed() {
475 return 50f;
476 }
477
478 public SpriteAPI getPulsarTexture() {
479 return flareTexture;
480 }
481}
482
483
484
485
486
static SettingsAPI getSettings()
Definition Global.java:51
static SectorAPI getSector()
Definition Global.java:59
void render(CampaignEngineLayers layer, ViewportAPI viewport)
void init(String terrainId, SectorEntityToken entity, Object param)
void advance(float amount, float minApproachSpeed, float diffMult)
void updateLimits(SectorEntityToken entity, SectorEntityToken exclude, float diffMult)
void updateAndSync(SectorEntityToken entity, SectorEntityToken exclude, float diffMult)
Description getDescription(String id, Type type)
SpriteAPI getSprite(String filename)