Starsector API
Loading...
Searching...
No Matches
RiftCascadeEffect.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.awt.Color;
4
5import org.lwjgl.util.vector.Vector2f;
6
7import com.fs.starfarer.api.Global;
8import com.fs.starfarer.api.combat.BeamAPI;
9import com.fs.starfarer.api.combat.BeamEffectPlugin;
10import com.fs.starfarer.api.combat.BoundsAPI;
11import com.fs.starfarer.api.combat.BoundsAPI.SegmentAPI;
12import com.fs.starfarer.api.combat.CombatEngineAPI;
13import com.fs.starfarer.api.combat.CombatEntityAPI;
14import com.fs.starfarer.api.combat.EmpArcEntityAPI;
15import com.fs.starfarer.api.combat.MissileAPI;
16import com.fs.starfarer.api.combat.ShipAPI;
17import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
18import com.fs.starfarer.api.loading.MissileSpecAPI;
19import com.fs.starfarer.api.util.IntervalUtil;
20import com.fs.starfarer.api.util.Misc;
21
31public class RiftCascadeEffect implements BeamEffectPlugin { //WithReset {
32
33 public static Color STANDARD_RIFT_COLOR = new Color(100,60,255,255);
34 public static Color EXPLOSION_UNDERCOLOR = new Color(100, 0, 25, 100);
35 public static Color NEGATIVE_SOURCE_COLOR = new Color(200,255,200,25);
36
37 public static String RIFTCASCADE_MINELAYER = "riftcascade_minelayer";
38
39 public static int MAX_RIFTS = 5;
40 public static float UNUSED_RANGE_PER_SPAWN = 200;
41 public static float SPAWN_SPACING = 175;
42 public static float SPAWN_INTERVAL = 0.1f;
43
44
45
46 protected Vector2f arcFrom = null;
47 protected Vector2f prevMineLoc = null;
48
49 protected boolean doneSpawningMines = false;
50 protected float spawned = 0;
51 protected int numToSpawn = 0;
52 protected float untilNextSpawn = 0;
53 protected float spawnDir = 0;
54
55 protected IntervalUtil tracker = new IntervalUtil(0.1f, 0.2f);
56
57 // re-instantiated when beam fires so this doesn't matter
58// public void reset() {
59// doneSpawningMines = false;
60// spawned = 0;
61// untilNextSpawn = 0;
62// arcFrom = null;
63// prevMineLoc = null;
64// numToSpawn = 0;
65// spawnDir = 0;
66// }
67
69 //System.out.println("23rwerefewfwe");
70 }
71
72
73 public void advance(float amount, CombatEngineAPI engine, BeamAPI beam) {
74 tracker.advance(amount);
75 if (tracker.intervalElapsed()) {
76 spawnNegativeParticles(engine, beam);
77 }
78
79 if (beam.getBrightness() < 1f) return;
80
81
82 if (doneSpawningMines) return;
83
84 if (numToSpawn <= 0 && beam.getDamageTarget() != null) {
85 float range = beam.getWeapon().getRange();
86 float length = beam.getLengthPrevFrame();
87 //float perSpawn = range / NUM_SPAWNS;
88 numToSpawn = (int) ((range - length) / UNUSED_RANGE_PER_SPAWN) + 1;
89 if (numToSpawn > MAX_RIFTS) {
91 }
92 untilNextSpawn = 0f;
93 }
94 //numToSpawn = 5;
95
96// if (beam.getBrightness() >= 1f) {
97// canSpawn = true;
98// }
99 //untilNextSpawn = 0f;
100
101 untilNextSpawn -= amount;
102 if (untilNextSpawn > 0) return;
103// if (!canSpawn || beam.getBrightness() >= 1f) return;
104
105 float perSpawn = SPAWN_SPACING;
106
107 ShipAPI ship = beam.getSource();
108
109 boolean spawnedMine = false;
110 if (beam.getLength() > beam.getWeapon().getRange() - 10f) {
111 float angle = Misc.getAngleInDegrees(beam.getFrom(), beam.getRayEndPrevFrame());
112 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle);
113 loc.scale(beam.getLength());
114 Vector2f.add(loc, beam.getFrom(), loc);
115
116 spawnMine(ship, loc);
117 spawnedMine = true;
118 } else if (beam.getDamageTarget() != null) {
119 Vector2f arcTo = getNextArcLoc(engine, beam, perSpawn);
120 float thickness = beam.getWidth();
121 //thickness = 20;
122 float dist = Misc.getDistance(arcFrom, arcTo);
123 if (dist < SPAWN_SPACING * 2f) {
124 EmpArcEntityAPI arc = engine.spawnEmpArcVisual(arcFrom, null, arcTo, null, thickness, beam.getFringeColor(), Color.white);
125 arc.setCoreWidthOverride(Math.max(20f, thickness * 0.67f));
126 //Global.getSoundPlayer().playSound("tachyon_lance_emp_impact", 1f, 1f, arc.getLocation(), arc.getVelocity());
127 }
128 spawnMine(ship, arcTo);
129 spawnedMine = true;
130 arcFrom = arcTo;
131 }
132
134 if (spawnedMine) {
135 spawned++;
136 if (spawned >= numToSpawn) {
137 doneSpawningMines = true;
138 }
139 }
140 }
141
142 public void spawnNegativeParticles(CombatEngineAPI engine, BeamAPI beam) {
143 float length = beam.getLengthPrevFrame();
144 if (length <= 10f) return;
145
146 //NEGATIVE_SOURCE_COLOR = new Color(200,255,200,25);
147
148 Vector2f from = beam.getFrom();
149 Vector2f to = beam.getRayEndPrevFrame();
150
151 ShipAPI ship = beam.getSource();
152
153 float angle = Misc.getAngleInDegrees(from, to);
154 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle);
155// Vector2f perp1 = new Vector2f(-dir.y, dir.x);
156// Vector2f perp2 = new Vector2f(dir.y, -dir.x);
157
158 //Color color = new Color(150,255,150,25);
159 Color color = NEGATIVE_SOURCE_COLOR;
160 //color = Misc.setAlpha(color, 50);
161
162 float sizeMult = 1f;
163 sizeMult = 0.67f;
164
165 for (int i = 0; i < 3; i++) {
166 float rampUp = 0.25f + 0.25f * (float) Math.random();
167 float dur = 1f + 1f * (float) Math.random();
168 //dur *= 2f;
169 float size = 200f + 50f * (float) Math.random();
170 size *= sizeMult;
171 //size *= 0.5f;
172 //Vector2f loc = Misc.getPointWithinRadius(from, size * 0.5f);
173 //Vector2f loc = Misc.getPointAtRadius(from, size * 0.33f);
174 Vector2f loc = Misc.getPointAtRadius(beam.getWeapon().getLocation(), size * 0.33f);
175 engine.addNegativeParticle(loc, ship.getVelocity(), size, rampUp / dur, dur, color);
176 //engine.addNegativeNebulaParticle(loc, ship.getVelocity(), size, 2f, rampUp, 0f, dur, color);
177 }
178
179 if (true) return;
180
181 // particles along the beam
182 float spawnOtherParticleRange = 100;
183 if (length > spawnOtherParticleRange * 2f && (float) Math.random() < 0.25f) {
184 //color = new Color(150,255,150,255);
185 color = new Color(150,255,150,75);
186 int numToSpawn = (int) ((length - spawnOtherParticleRange) / 200f + 1);
187 numToSpawn = 1;
188 for (int i = 0; i < numToSpawn; i++) {
189 float distAlongBeam = spawnOtherParticleRange + (length - spawnOtherParticleRange * 2f) * (float) Math.random();
190 float groupSpeed = 100f + (float) Math.random() * 100f;
191 for (int j = 0; j < 7; j++) {
192 float rampUp = 0.25f + 0.25f * (float) Math.random();
193 float dur = 1f + 1f * (float) Math.random();
194 float size = 50f + 50f * (float) Math.random();
195 Vector2f loc = new Vector2f(dir);
196 float sign = Math.signum((float) Math.random() - 0.5f);
197 loc.scale(distAlongBeam + sign * (float) Math.random() * size * 0.5f);
198 Vector2f.add(loc, from, loc);
199
200// Vector2f off = new Vector2f(perp1);
201// if ((float) Math.random() < 0.5f) off = new Vector2f(perp2);
202//
203// off.scale(size * 0.1f);
204 //Vector2f.add(loc, off, loc);
205
206 loc = Misc.getPointWithinRadius(loc, size * 0.25f);
207
208 float dist = Misc.getDistance(loc, to);
209 Vector2f vel = new Vector2f(dir);
210 if ((float) Math.random() < 0.5f) {
211 vel.negate();
212 dist = Misc.getDistance(loc, from);
213 }
214
215 float speed = groupSpeed;
216 float maxSpeed = dist / dur;
217 if (speed > maxSpeed) speed = maxSpeed;
218 vel.scale(speed);
219 Vector2f.add(vel, ship.getVelocity(), vel);
220
221 engine.addNegativeParticle(loc, vel, size, rampUp, dur, color);
222 }
223 }
224 }
225 }
226
227
228 public Vector2f getNextArcLoc(CombatEngineAPI engine, BeamAPI beam, float perSpawn) {
229 CombatEntityAPI target = beam.getDamageTarget();
230 float radiusOverride = -1f;
231 if (target instanceof ShipAPI) {
232 ShipAPI ship = (ShipAPI) target;
233 if (ship.getParentStation() != null && ship.getStationSlot() != null) {
234 //radiusOverride = Misc.getDistance(ship.getLocation(), ship.getParentStation().getLocation());
235 //radiusOverride += ship.getCollisionRadius();
236 radiusOverride = Misc.getDistance(beam.getRayEndPrevFrame(), ship.getParentStation().getLocation()) + 0f;
237 target = ship.getParentStation();
238 }
239 }
240
241 if (arcFrom == null) {
242 arcFrom = new Vector2f(beam.getRayEndPrevFrame());
243// Vector2f loc = Misc.getUnitVectorAtDegreeAngle(beamAngle);
244// loc.scale(beam.getLengthPrevFrame());
245// //loc.scale(200f);
246// Vector2f.add(loc, beam.getFrom(), loc);
247// arcFrom = loc;
248
249 float beamAngle = Misc.getAngleInDegrees(beam.getFrom(), beam.getRayEndPrevFrame());
250 float beamSourceToTarget = Misc.getAngleInDegrees(beam.getFrom(), target.getLocation());
251
252 // this is the direction we'll rotate - from the target's center - so that it's spawning mines around the side
253 // closer to the beam's straight line
254 spawnDir = Misc.getClosestTurnDirection(beamAngle, beamSourceToTarget);
255 if (spawnDir == 0) spawnDir = 1;
256
257 boolean computeNextLoc = false;
258 if (prevMineLoc != null) {
259 float dist = Misc.getDistance(arcFrom, prevMineLoc);
260 if (dist < perSpawn) {
261 perSpawn -= dist;
262 computeNextLoc = true;
263 }
264 }
265 if (!computeNextLoc) {
266 return arcFrom;
267 }
268 }
269
270// target = Global.getCombatEngine().getPlayerShip();
271// target.getLocation().y += 750f;
272
273 Vector2f targetLoc = target.getLocation();
274 float targetRadius = target.getCollisionRadius();
275 if (radiusOverride >= 0) {
276 targetRadius = radiusOverride;
277 }
278
279// if (target instanceof ShipAPI) {
280// ShipAPI ship = (ShipAPI) target;
281// targetLoc = ship.getShieldCenterEvenIfNoShield();
282// targetRadius = ship.getShieldRadiusEvenIfNoShield();
283// }
284
285 boolean hitShield = target.getShield() != null && target.getShield().isWithinArc(beam.getRayEndPrevFrame());
286 if (hitShield) perSpawn *= 0.67f;
287// float beamAngle = Misc.getAngleInDegrees(beam.getFrom(), beam.getRayEndPrevFrame());
288// float beamSourceToTarget = Misc.getAngleInDegrees(beam.getFrom(), targetLoc);
289//
290// // this is the direction we'll rotate - from the target's center - so that it's spawning mines around the side
291// // closer to the beam's straight line
292// float dir = Misc.getClosestTurnDirection(beamAngle, beamSourceToTarget);
293
294 float prevAngle = Misc.getAngleInDegrees(targetLoc, arcFrom);
295 float anglePerSegment = 360f * perSpawn / (3.14f * 2f * targetRadius);
296 if (anglePerSegment > 90f) anglePerSegment = 90f;
297 float angle = prevAngle + anglePerSegment * spawnDir;
298
299
300 Vector2f arcTo = Misc.getUnitVectorAtDegreeAngle(angle);
301 arcTo.scale(targetRadius);
302 Vector2f.add(targetLoc, arcTo, arcTo);
303
304 float actualRadius = Global.getSettings().getTargetingRadius(arcTo, target, hitShield);
305 if (radiusOverride >= 0) {
306 actualRadius = radiusOverride;
307 }
308 if (!hitShield) {
309 //actualRadius *= 1f + 0.1f * (float) Math.random();
310 actualRadius += 30f + 50f * (float) Math.random();
311 } else {
312// actualRadius = target.getShield().getRadius();
313// actualRadius += 20f + 20f * (float) Math.random();
314 actualRadius += 30f + 50f * (float) Math.random();
315 }
316
317// float angleDiff = Misc.getAngleDiff(beamSourceToTarget + 180f, angle);
318// if (angleDiff > 150f) {
319// actualRadius += perSpawn * (180f - angleDiff) / 30f;
320// }
321
322 arcTo = Misc.getUnitVectorAtDegreeAngle(angle);
323 arcTo.scale(actualRadius);
324 Vector2f.add(targetLoc, arcTo, arcTo);
325
326
327 // now we've got an arcTo location somewhere roughly circular; try to cleave more closely to the hull
328 // if the target is a ship
329 if (target instanceof ShipAPI && !hitShield) {
330 ShipAPI ship = (ShipAPI) target;
331 BoundsAPI bounds = ship.getExactBounds();
332 if (bounds != null) {
333 Vector2f best = null;
334 float bestDist = Float.MAX_VALUE;
335 for (SegmentAPI segment : bounds.getSegments()) {
336 float test = Misc.getDistance(segment.getP1(), arcTo);
337 if (test < bestDist) {
338 bestDist = test;
339 best = segment.getP1();
340 }
341 }
342 if (best != null) {
343 Object o = Global.getSettings().getWeaponSpec(RIFTCASCADE_MINELAYER).getProjectileSpec();
344 if (o instanceof MissileSpecAPI) {
345 MissileSpecAPI spec = (MissileSpecAPI) o;
346 float explosionRadius = (float) spec.getBehaviorJSON().optJSONObject("explosionSpec").optDouble("coreRadius");
347 float sizeMult = getSizeMult();
348 explosionRadius *= sizeMult;
349
350 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(best, arcTo));
351 dir.scale(explosionRadius * 0.9f);
352 Vector2f.add(best, dir, dir);
353 arcTo = dir;
354
355// dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(prevMineLoc, arcTo));
356// dir.scale(perSpawn);
357// Vector2f.add(prevMineLoc, dir, dir);
358 }
359 }
360 }
361
362 }
363
364 return arcTo;
365 }
366
367 public float getSizeMult() {
368 float sizeMult = 1f - spawned / (float) Math.max(1, numToSpawn - 1);
369 sizeMult = 0.75f + (1f - sizeMult) * 0.5f;
370 //sizeMult = 0.5f + 0.5f * sizeMult;
371 return sizeMult;
372 }
373
374 public void spawnMine(ShipAPI source, Vector2f mineLoc) {
375 CombatEngineAPI engine = Global.getCombatEngine();
376
377 MissileAPI mine = (MissileAPI) engine.spawnProjectile(source, null,
379 mineLoc,
380 (float) Math.random() * 360f, null);
381
382 // "spawned" does not include this mine
383 float sizeMult = getSizeMult();
384 mine.setCustomData(RiftCascadeMineExplosion.SIZE_MULT_KEY, sizeMult);
385
386 if (source != null) {
387 Global.getCombatEngine().applyDamageModifiersToSpawnedProjectileWithNullWeapon(
388 source, WeaponType.ENERGY, false, mine.getDamage());
389 }
390
391 mine.getDamage().getModifier().modifyMult("mine_sizeMult", sizeMult);
392
393
394 float fadeInTime = 0.05f;
395 mine.getVelocity().scale(0);
396 mine.fadeOutThenIn(fadeInTime);
397
398 //Global.getCombatEngine().addPlugin(createMissileJitterPlugin(mine, fadeInTime));
399
400 //mine.setFlightTime((float) Math.random());
401 float liveTime = 0f;
402 //liveTime = 0.01f;
403 mine.setFlightTime(mine.getMaxFlightTime() - liveTime);
404 mine.addDamagedAlready(source);
405 mine.setNoMineFFConcerns(true);
406
407 prevMineLoc = mineLoc;
408 }
409
410}
411
412
413
414
415
static SettingsAPI getSettings()
Definition Global.java:51
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
void spawnNegativeParticles(CombatEngineAPI engine, BeamAPI beam)
void advance(float amount, CombatEngineAPI engine, BeamAPI beam)
void spawnMine(ShipAPI source, Vector2f mineLoc)
Vector2f getNextArcLoc(CombatEngineAPI engine, BeamAPI beam, float perSpawn)
float getTargetingRadius(Vector2f from, CombatEntityAPI target, boolean considerShield)
WeaponSpecAPI getWeaponSpec(String weaponId)