Starsector API
Loading...
Searching...
No Matches
GraviticScanAbility.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.abilities;
2
3import java.util.EnumSet;
4
5import java.awt.Color;
6
7import org.lwjgl.opengl.GL11;
8import org.lwjgl.util.vector.Vector2f;
9
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.campaign.CampaignEngineLayers;
12import com.fs.starfarer.api.campaign.CampaignFleetAPI;
13import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
14import com.fs.starfarer.api.combat.ViewportAPI;
15import com.fs.starfarer.api.graphics.SpriteAPI;
16import com.fs.starfarer.api.impl.campaign.ids.Commodities;
17import com.fs.starfarer.api.ui.Alignment;
18import com.fs.starfarer.api.ui.LabelAPI;
19import com.fs.starfarer.api.ui.TooltipMakerAPI;
20import com.fs.starfarer.api.util.Misc;
21
23
24 public static float SLIPSTREAM_DETECTION_RANGE = 20000f;
25
26 public static String COMMODITY_ID = Commodities.VOLATILES;
27 public static float COMMODITY_PER_DAY = 1f;
28
29 public static float DETECTABILITY_PERCENT = 50f;
30
31
32 @Override
33 protected String getActivationText() {
34 if (COMMODITY_ID != null && getFleet() != null && getFleet().getCargo().getCommodityQuantity(COMMODITY_ID) <= 0 &&
35 (!Global.getSettings().isDevMode() || Global.getSettings().getBoolean("playtestingMode"))) {
36 return null;
37 }
38 return "Neutrino detector activated";
39 }
40
41 @Override
42 protected String getDeactivationText() {
43 return null;
44 }
45
46
47 @Override
48 protected void activateImpl() {
49
50 }
51
52 @Override
53 public boolean showProgressIndicator() {
54 return false;
55 }
56
57 @Override
58 public boolean showActiveIndicator() {
59 return isActive();
60 }
61
62
63 @Override
64 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) {
65 Color bad = Misc.getNegativeHighlightColor();
66 Color gray = Misc.getGrayColor();
67 Color highlight = Misc.getHighlightColor();
68
69 String status = " (off)";
70 if (turnedOn) {
71 status = " (on)";
72 }
73
75 LabelAPI title = tooltip.addTitle(spec.getName() + status);
76 title.highlightLast(status);
77 title.setHighlightColor(gray);
78 } else {
79 tooltip.addSpacer(-10f);
80 }
81
82 float pad = 10f;
83
84
85 tooltip.addPara("Reconfigures the fleet's drive field to act as a neutrino detector, " +
86 "allowing detection of human-made artifacts - and occasionally fleets - at extreme ranges. ", pad);
87
88 tooltip.addSectionHeading("Normal space", Alignment.MID, pad);
89 tooltip.addPara("High-emission sources such as stars, planets, jump-points, or space stations produce constant streams. " +
90 "Average sources produce periodic bursts. Low-emission sources produce occasional bursts.", pad);
91
92 tooltip.addPara("Notoriously unreliable and almost guaranteed to produce numerous false readings.", pad);
93
94
95 if (COMMODITY_ID != null) {
96 String unit = "unit";
97 if (COMMODITY_PER_DAY != 1) unit = "units";
99 unit += " of " + spec.getName().toLowerCase();
100
101 tooltip.addPara("Increases the range at which the fleet can be detected by %s and consumes %s " + unit + " per day.",
102 pad, highlight,
103 "" + (int)DETECTABILITY_PERCENT + "%",
105 );
106 } else {
107 tooltip.addPara("Increases the range at which the fleet can be detected by %s.",
108 pad, highlight,
109 "" + (int)DETECTABILITY_PERCENT + "%"
110 );
111 }
112
113 int maxRange = (int) Math.round(SLIPSTREAM_DETECTION_RANGE / Misc.getUnitsPerLightYear());
114 tooltip.addSectionHeading("Hyperspace", Alignment.MID, pad);
115 tooltip.addPara("Reliably detects the presence of slipstreams out to a range of %s light-years. "
116 + "Except for abyssal areas, the background noise levels are such that it is unable to detect any other neutrino sources. "
117 + "When the fleet is traversing a slipstream, the detector is overwhelmed and shuts down.",
118 pad, highlight, "" + maxRange);
120 tooltip.addPara("Cannot activate while inside slipstream.", bad, pad);
121 }
122// if (getFleet() != null && getFleet().isInHyperspace()) {
123// tooltip.addPara("Can not function in hyperspace.", bad, pad);
124// } else {
125// tooltip.addPara("Can not function in hyperspace.", pad);
126// }
127
128 //tooltip.addPara("Disables the transponder when activated.", pad);
129 addIncompatibleToTooltip(tooltip, expanded);
130 }
131
132 public boolean hasTooltip() {
133 return true;
134 }
135
136 @Override
137 public EnumSet<CampaignEngineLayers> getActiveLayers() {
138 return EnumSet.of(CampaignEngineLayers.ABOVE);
139 }
140
141
142 @Override
143 public void advance(float amount) {
144 super.advance(amount);
145
146 if (data != null && !isActive() && getProgressFraction() <= 0f) {
147 data = null;
148 }
149 }
150
151
152 protected float phaseAngle;
153 protected GraviticScanData data = null;
154 @Override
155 protected void applyEffect(float amount, float level) {
156 CampaignFleetAPI fleet = getFleet();
157 if (fleet == null) return;
158
159 //if (level < 1) level = 0;
160
162
163 float days = Global.getSector().getClock().convertToDays(amount);
164 phaseAngle += days * 360f * 10f;
166
167 if (data == null) {
168 data = new GraviticScanData(this);
169 }
170 data.advance(days);
171
172 if (COMMODITY_ID != null) {
173 float cost = days * COMMODITY_PER_DAY;
174 if (fleet.getCargo().getCommodityQuantity(COMMODITY_ID) > 0 || (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode"))) {
176 } else {
178 fleet.addFloatingText("Out of " + spec.getName().toLowerCase(), Misc.setAlpha(entity.getIndicatorColor(), 255), 0.5f);
179 deactivate();
180 }
181 }
182
183 if (Misc.isInsideSlipstream(fleet)) {
184 deactivate();
185 }
186// if (fleet.isInHyperspace()) {
187// deactivate();
188// }
189 }
190
194
195 @Override
196 public boolean isUsable() {
197 CampaignFleetAPI fleet = getFleet();
198 if (fleet == null) return false;
199
200 return !Misc.isInsideSlipstream(fleet);
201 //return isActive() || !fleet.isInHyperspace();
202 }
203
204
205 @Override
206 protected void deactivateImpl() {
207 cleanupImpl();
208 }
209
210 @Override
211 protected void cleanupImpl() {
212 CampaignFleetAPI fleet = getFleet();
213 if (fleet == null) return;
214
216 //data = null;
217 }
218
219
220
221
222 public float getRingRadius() {
223 return getFleet().getRadius() + 75f;
224 //return getFleet().getRadius() + 25f;
225 }
226
227 transient protected SpriteAPI texture;
228 @Override
229 public void render(CampaignEngineLayers layer, ViewportAPI viewport) {
230
231 if (data == null) return;
232
233 float level = getProgressFraction();
234 if (level <= 0) return;
235 if (getFleet() == null) return;
236 if (!getFleet().isPlayerFleet()) return;
237
238 float alphaMult = viewport.getAlphaMult() * level;
239
240// float x = getFleet().getLocation().x;
241// float y = getFleet().getLocation().y;
242//
243// GL11.glPushMatrix();
244// GL11.glTranslatef(x, y, 0);
245//
246// GL11.glDisable(GL11.GL_TEXTURE_2D);
247// Misc.renderQuad(30, 30, 100, 100, Color.green, alphaMult * level);
248//
249//
250// GL11.glPopMatrix();
251
252
253 //float noiseLevel = data.getNoiseLevel();
254
255 float bandWidthInTexture = 256;
256 float bandIndex;
257
258 float radStart = getRingRadius();
259 float radEnd = radStart + 75f;
260
261 float circ = (float) (Math.PI * 2f * (radStart + radEnd) / 2f);
262 //float pixelsPerSegment = 10f;
263 float pixelsPerSegment = circ / 360f;
264 //float pixelsPerSegment = circ / 720;
265 float segments = Math.round(circ / pixelsPerSegment);
266
267// segments = 360;
268// pixelsPerSegment = circ / segments;
269 //pixelsPerSegment = 10f;
270
271 float startRad = (float) Math.toRadians(0);
272 float endRad = (float) Math.toRadians(360f);
273 float spanRad = Math.abs(endRad - startRad);
274 float anglePerSegment = spanRad / segments;
275
276 Vector2f loc = getFleet().getLocation();
277 float x = loc.x;
278 float y = loc.y;
279
280
281 GL11.glPushMatrix();
282 GL11.glTranslatef(x, y, 0);
283
284 //float zoom = viewport.getViewMult();
285 //GL11.glScalef(zoom, zoom, 1);
286
287 GL11.glEnable(GL11.GL_TEXTURE_2D);
288
289 if (texture == null) texture = Global.getSettings().getSprite("abilities", "neutrino_detector");
291
292 GL11.glEnable(GL11.GL_BLEND);
293 //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
294 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
295
296 boolean outlineMode = false;
297 //outlineMode = true;
298 if (outlineMode) {
299 GL11.glDisable(GL11.GL_TEXTURE_2D);
300 GL11.glDisable(GL11.GL_BLEND);
301 GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
302 //GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
303 }
304
305 float thickness = (radEnd - radStart) * 1f;
306 float radius = radStart;
307
308 float texProgress = 0f;
309 float texHeight = texture.getTextureHeight();
310 float imageHeight = texture.getHeight();
311 float texPerSegment = pixelsPerSegment * texHeight / imageHeight * bandWidthInTexture / thickness;
312
313 texPerSegment *= 1f;
314
315 float totalTex = Math.max(1f, Math.round(texPerSegment * segments));
316 texPerSegment = totalTex / segments;
317
318 float texWidth = texture.getTextureWidth();
319 float imageWidth = texture.getWidth();
320
321
322
323 Color color = new Color(25,215,255,255);
324 //Color color = new Color(255,25,255,155);
325
326
327 for (int iter = 0; iter < 2; iter++) {
328 if (iter == 0) {
329 bandIndex = 1;
330 } else {
331 //color = new Color(255,215,25,255);
332 //color = new Color(25,255,215,255);
333 bandIndex = 0;
334 texProgress = segments/2f * texPerSegment;
335 //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
336 }
337 if (iter == 1) {
338 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
339 }
340 //bandIndex = 1;
341
342 float leftTX = (float) bandIndex * texWidth * bandWidthInTexture / imageWidth;
343 float rightTX = (float) (bandIndex + 1f) * texWidth * bandWidthInTexture / imageWidth - 0.001f;
344
345 GL11.glBegin(GL11.GL_QUAD_STRIP);
346 for (float i = 0; i < segments + 1; i++) {
347
348 float segIndex = i % (int) segments;
349
350 //float phaseAngleRad = (float) Math.toRadians(phaseAngle + segIndex * 10) + (segIndex * anglePerSegment * 10f);
351 float phaseAngleRad;
352 if (iter == 0) {
353 phaseAngleRad = (float) Math.toRadians(phaseAngle) + (segIndex * anglePerSegment * 29f);
354 } else { //if (iter == 1) {
355 phaseAngleRad = (float) Math.toRadians(-phaseAngle) + (segIndex * anglePerSegment * 17f);
356 }
357
358
359 float angle = (float) Math.toDegrees(segIndex * anglePerSegment);
360 //if (iter == 1) angle += 180;
361
362
363 float pulseSin = (float) Math.sin(phaseAngleRad);
364 float pulseMax = thickness * 0.5f;
365
366 pulseMax = thickness * 0.2f;
367 pulseMax = 10f;
368
369 //pulseMax *= 0.25f + 0.75f * noiseLevel;
370
371 float pulseAmount = pulseSin * pulseMax;
372 //float pulseInner = pulseAmount * 0.1f;
373 float pulseInner = pulseAmount * 0.1f;
374
375 float r = radius;
376
377// float thicknessMult = delegate.getAuroraThicknessMult(angle);
378// float thicknessFlat = delegate.getAuroraThicknessFlat(angle);
379
380 float theta = anglePerSegment * segIndex;;
381 float cos = (float) Math.cos(theta);
382 float sin = (float) Math.sin(theta);
383
384 float rInner = r - pulseInner;
385 //if (rInner < r * 0.9f) rInner = r * 0.9f;
386
387 //float rOuter = (r + thickness * thicknessMult - pulseAmount + thicknessFlat);
388 float rOuter = r + thickness - pulseAmount;
389
390
391 //rOuter += noiseLevel * 25f;
392
393 float grav = data.getDataAt(angle);
394 //if (grav > 500) System.out.println(grav);
395 //if (grav > 300) grav = 300;
396 if (grav > 750) grav = 750;
397 grav *= 250f / 750f;
398 grav *= level;
399 //grav *= 0.5f;
400 //rInner -= grav * 0.25f;
401
402 //rInner -= grav * 0.1f;
403 rOuter += grav;
404// rInner -= grav * 3f;
405// rOuter -= grav * 3f;
406 //System.out.println(grav);
407
408 float alpha = alphaMult;
409 alpha *= 0.25f + Math.min(grav / 100, 0.75f);
410 //alpha *= 0.75f;
411
412//
413//
414//
415// phaseAngleWarp = (float) Math.toRadians(phaseAngle - 180 * iter) + (segIndex * anglePerSegment * 1f);
416// float warpSin = (float) Math.sin(phaseAngleWarp);
417// rInner += thickness * 0.5f * warpSin;
418// rOuter += thickness * 0.5f * warpSin;
419
420
421
422 float x1 = cos * rInner;
423 float y1 = sin * rInner;
424 float x2 = cos * rOuter;
425 float y2 = sin * rOuter;
426
427 x2 += (float) (Math.cos(phaseAngleRad) * pixelsPerSegment * 0.33f);
428 y2 += (float) (Math.sin(phaseAngleRad) * pixelsPerSegment * 0.33f);
429
430
431 GL11.glColor4ub((byte)color.getRed(),
432 (byte)color.getGreen(),
433 (byte)color.getBlue(),
434 (byte)((float) color.getAlpha() * alphaMult * alpha));
435
436 GL11.glTexCoord2f(leftTX, texProgress);
437 GL11.glVertex2f(x1, y1);
438 GL11.glTexCoord2f(rightTX, texProgress);
439 GL11.glVertex2f(x2, y2);
440
441 texProgress += texPerSegment * 1f;
442 }
443 GL11.glEnd();
444
445 //GL11.glRotatef(180, 0, 0, 1);
446 }
447 GL11.glPopMatrix();
448
449 if (outlineMode) {
450 GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
451 }
452 }
453
454
455
456
457
458
459
460}
461
462
463
464
465
static SettingsAPI getSettings()
Definition Global.java:57
static boolean CODEX_TOOLTIP_MODE
Definition Global.java:15
static SectorAPI getSector()
Definition Global.java:65
void modifyPercent(String source, float value)
void addIncompatibleToTooltip(TooltipMakerAPI tooltip, boolean expanded)
void createTooltip(TooltipMakerAPI tooltip, boolean expanded)
void render(CampaignEngineLayers layer, ViewportAPI viewport)
static boolean isInsideSlipstream(Vector2f loc, float radius)
Definition Misc.java:6408
static Color getNegativeHighlightColor()
Definition Misc.java:802
static Color setAlpha(Color color, int alpha)
Definition Misc.java:1316
static Color getGrayColor()
Definition Misc.java:826
static float getUnitsPerLightYear()
Definition Misc.java:846
static Color getHighlightColor()
Definition Misc.java:792
static float normalizeAngle(float angleDeg)
Definition Misc.java:1142
static String getRoundedValueMaxOneAfterDecimal(float value)
Definition Misc.java:673
boolean getBoolean(String key)
CommoditySpecAPI getCommoditySpec(String commodityId)
SpriteAPI getSprite(String filename)
void removeCommodity(String id, float quantity)
void addFloatingText(String text, Color color, float duration)
void setHighlightColor(Color color)
void highlightLast(String substring)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
UIComponentAPI addSpacer(float height)
LabelAPI addSectionHeading(String str, Alignment align, float pad)