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