Starsector API
Loading...
Searching...
No Matches
OrionDeviceStats.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.List;
6
7import org.lwjgl.util.vector.Vector2f;
8
9import com.fs.starfarer.api.Global;
10import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
11import com.fs.starfarer.api.combat.CollisionClass;
12import com.fs.starfarer.api.combat.CombatEngineAPI;
13import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
14import com.fs.starfarer.api.combat.MissileAPI;
15import com.fs.starfarer.api.combat.MutableShipStatsAPI;
16import com.fs.starfarer.api.combat.ShipAPI;
17import com.fs.starfarer.api.combat.ShipCommand;
18import com.fs.starfarer.api.combat.ShipSystemAPI;
19import com.fs.starfarer.api.combat.WeaponAPI;
20import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
21import com.fs.starfarer.api.input.InputEventAPI;
22import com.fs.starfarer.api.loading.WeaponSlotAPI;
23import com.fs.starfarer.api.util.Misc;
24
33
34 public static class PusherPlateImpulse {
35 public float force;
36 public float dur;
37 public float elapsed;
38 }
39 public static class PusherPlateState {
40 public float compression;
41 public float vel;
42 public List<PusherPlateImpulse> impulses = new ArrayList<PusherPlateImpulse>();
43
44 public void addImpulse(float force, float dur) {
45 PusherPlateImpulse ppi = new PusherPlateImpulse();
46 ppi.force = force;
47 ppi.dur = dur;
48 impulses.add(ppi);
49
50 }
51 public void advance(float amount) {
52 List<PusherPlateImpulse> remove = new ArrayList<PusherPlateImpulse>();
53 float totalForce = 0f;
54 for (PusherPlateImpulse curr : impulses) {
55 totalForce += curr.force;
56 curr.elapsed += amount;
57 if (curr.elapsed >= curr.dur) {
58 remove.add(curr);
59 }
60 }
61 impulses.removeAll(remove);
62
63 // assuming k of 1, and a mass of 1
64 float springForce = compression;
65 float netForce = totalForce - springForce;
66
67 vel += netForce * amount;
68 compression += vel * amount;
69
70 if (compression > 1f) {
71 compression = 1f;
72 vel = 0f;
73 }
74 float min = 0f;
75 //min = -0.5f;
76 if (compression < min) {
77 compression = min;
78 vel = 0f;
79 }
80
81 }
82 }
83
84
85 public static class OrionDeviceParams {
86 public float bombFadeInTime = 0.15f;
87 public float bombLiveTime = 0.25f;
88 public float bombSpeed = 50f;
89 public float bombInheritedVelocityFraction = 0.5f;
90
91 public float shapedExplosionOffset = 50f;
92 public float shapedExplosionEndSizeMin = 1f;
93 public float shapedExplosionEndSizeMax = 2f;
94 public Color shapedExplosionColor = new Color(255,125,25,155);
95 public int shapedExplosionNumParticles = 200;
96 public float shapedExplosionMinParticleSize = 80;
97 public float shapedExplosionMaxParticleSize = 100;
98 public float shapedExplosionScatter = 100f;
99 public float shapedExplosionMinParticleVel = 100;
100 public float shapedExplosionMaxParticleVel = 350f;
101 public float shapedExplosionMinParticleDur = 1f;
102 public float shapedExplosionMaxParticleDur = 2f;
103 public float shapedExplosionArc = 90f;
104 public Color jitterColor = new Color(255,125,25,55);
105 public float maxJitterDur = 2f;
106
107 public float pusherPlateMaxOffset = 14f;
108 public float pusherPlateImpulseForce = 10f;
109 public float pusherPlateImpulseDuration = 0.2f;
110
111 public float impactAccel = 5000f;
112 public float impactRateMult = 4f;
113
114 public boolean recolorTowardsEngineColor = false;
115
116 public String bombWeaponId = "od_bomblauncher";
117 }
118
119
120 protected OrionDeviceParams p = new OrionDeviceParams();
121 protected PusherPlateState pusherState = new PusherPlateState();
122
124 p = new OrionDeviceParams();
125 //p.recolorTowardsEngineColor = true;
126 }
127
128
129 protected Color orig = null;
130 protected void recolor(ShipAPI ship) {
131 if (ship == null) return;
132 if (!p.recolorTowardsEngineColor) return;
133
134 if (orig == null) orig = p.shapedExplosionColor;
135
136 Color curr = ship.getEngineController().getFlameColorShifter().getCurr();
137
138 p.shapedExplosionColor = Misc.interpolateColor(orig, curr, 0.75f);
139 p.shapedExplosionColor = Misc.setAlpha(p.shapedExplosionColor, orig.getAlpha());
140 }
141
142 protected boolean wasIdle = false;
143 protected boolean deployedBomb = false;
144 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
145 ShipAPI ship = null;
146 //boolean player = false;
147 if (stats.getEntity() instanceof ShipAPI) {
148 ship = (ShipAPI) stats.getEntity();
149 } else {
150 return;
151 }
152
153 recolor(ship);
154
155 if (effectLevel >= 1 && !deployedBomb) {
156 for (WeaponSlotAPI slot : ship.getHullSpec().getAllWeaponSlotsCopy()) {
157 if (slot.isSystemSlot()) {
158 spawnBomb(ship, slot);
159 }
160 }
161 deployedBomb = true;
162 //pusherPlateOffset = 1f;
163 } else if (state == State.COOLDOWN) {
164 deployedBomb = false;
165 }
166
167
168 float amount = Global.getCombatEngine().getElapsedInLastFrame();
169 pusherState.advance(amount);
170
171 for (WeaponAPI w : ship.getAllWeapons()) {
172 Vector2f offset = new Vector2f(p.pusherPlateMaxOffset, 0f);
173 //pusherPlateOffset = 1f;
174 offset.scale(pusherState.compression);
175 if (w.getSpec().hasTag("pusherplate")) {
176 w.setRenderOffsetForDecorativeBeamWeaponsOnly(offset);
177 }
178 }
179
180 advanceImpl(amount, ship, state, effectLevel);
181 }
182
183 protected void advanceImpl(float amount, ShipAPI ship, State state, float effectLevel) {
184
185 }
186
187
188 public void unapply(MutableShipStatsAPI stats, String id) {
189 }
190
191 public void spawnBomb(ShipAPI source, WeaponSlotAPI slot) {
192 CombatEngineAPI engine = Global.getCombatEngine();
193 Vector2f loc = slot.computePosition(source);
194 float angle = slot.computeMidArcAngle(source);
195
196 if (pusherState.compression > 0) {
197 Vector2f offset = new Vector2f(p.pusherPlateMaxOffset, 0f);
198 offset.scale(pusherState.compression);
199 offset = Misc.rotateAroundOrigin(offset, source.getFacing());
200 Vector2f.add(loc, offset, loc);
201 }
202
203 MissileAPI bomb = (MissileAPI) engine.spawnProjectile(source, null,
204 p.bombWeaponId,
205 loc,
206 angle, source.getVelocity());
207 if (source != null) {
208 Global.getCombatEngine().applyDamageModifiersToSpawnedProjectileWithNullWeapon(
209 source, WeaponType.MISSILE, false, bomb.getDamage());
210 }
211
212 float fadeInTime = p.bombFadeInTime;
213 Vector2f inheritedVel = new Vector2f(source.getVelocity());
214 inheritedVel.scale(p.bombInheritedVelocityFraction);
215 float speed = p.bombSpeed;
216
217 Vector2f vel = Misc.getUnitVectorAtDegreeAngle(angle);
218 vel.scale(speed);
219 Vector2f.add(vel, inheritedVel, vel);
220 bomb.getVelocity().set(vel);
221 bomb.fadeOutThenIn(fadeInTime);
222
223 bomb.setCollisionClass(CollisionClass.NONE);
224 bomb.setEmpResistance(1000);
225 bomb.setEccmChanceOverride(1f);
226
227
228 float liveTime = p.bombLiveTime;
229 bomb.setMaxFlightTime(liveTime);
230
231
232 Global.getCombatEngine().addPlugin(createBombImpactPlugin(source, slot, bomb, loc, angle));
233 }
234
235 protected void notifySpawnedExplosionParticles(Vector2f bombLoc) {
236
237 }
238
239 protected EveryFrameCombatPlugin createBombImpactPlugin(final ShipAPI ship, final WeaponSlotAPI launchSlot,
240 final MissileAPI bomb, final Vector2f launchLoc, final float launchAngle) {
241
242 return new BaseEveryFrameCombatPlugin() {
243 float elapsed = 0f;
244 float impactTime = 0f;
245 float brakingTime = 0f;
246 float forceAngle;
247 float jitterTime = 0f;
248 boolean triggered = false;
249 boolean braking = false;
250 boolean done = false;
251
252 @Override
253 public void advance(float amount, List<InputEventAPI> events) {
254 if (Global.getCombatEngine().isPaused()) return;
255
256 elapsed += amount;
257
258 String impactCounterId = "od_system_counter";
259
260 if (bomb.isFizzling()) {
261 if (!triggered) {
262
263 pusherState.addImpulse(p.pusherPlateImpulseForce, p.pusherPlateImpulseDuration);
264
265 forceAngle = Misc.getAngleInDegrees(bomb.getLocation(), launchSlot.computePosition(ship));
266 float angleToShip = Misc.getAngleInDegrees(bomb.getLocation(), ship.getLocation());
267 if (Misc.getAngleDiff(angleToShip, forceAngle) > 90f) {
268 forceAngle += 180f;
269 }
270
271 float diff = Misc.getAngleDiff(angleToShip, forceAngle);
272 float turnDir = Misc.getClosestTurnDirection(angleToShip, forceAngle);
273 forceAngle = angleToShip + diff * turnDir * 0.2f;
274
275 triggered = true;
276 ship.getMutableStats().getDynamic().getMod(impactCounterId).modifyFlat("od_launch_" + launchLoc, 1f);
277
278 if (Global.getCombatEngine().getViewport().isNearViewport(ship.getLocation(), 800f)) {
279 float angle = forceAngle + 180f;
280
281 int numParticles = p.shapedExplosionNumParticles;
282 float minSize = p.shapedExplosionMinParticleSize;
283 float maxSize = p.shapedExplosionMaxParticleSize;
284 Color pc = p.shapedExplosionColor;
285
286 float minDur = p.shapedExplosionMinParticleDur;
287 float maxDur = p.shapedExplosionMaxParticleDur;
288
289 float arc = p.shapedExplosionArc;
290 float scatter = p.shapedExplosionScatter;
291 float minVel = p.shapedExplosionMinParticleVel;
292 float maxVel = p.shapedExplosionMaxParticleVel;
293
294 float launchOffset = p.shapedExplosionOffset;
295 float endSizeMin = p.shapedExplosionEndSizeMin;
296 float endSizeMax = p.shapedExplosionEndSizeMax;
297
298 Vector2f spawnPoint = Misc.getUnitVectorAtDegreeAngle(forceAngle);
299 spawnPoint.scale(launchOffset);
300 Vector2f.add(bomb.getLocation(), spawnPoint, spawnPoint);
301 for (int i = 0; i < numParticles; i++) {
302 //p.setMaxAge(500 + (int)(Math.random() * 1000f));
303 float angleOffset = (float) Math.random();
304 if (angleOffset > 0.2f) {
305 angleOffset *= angleOffset;
306 }
307 float speedMult = 1f - angleOffset;
308 speedMult = 0.5f + speedMult * 0.5f;
309 angleOffset *= Math.signum((float) Math.random() - 0.5f);
310 angleOffset *= arc/2f;
311 float theta = (float) Math.toRadians(angle + angleOffset);
312 float r = (float) (Math.random() * Math.random() * scatter);
313 float x = (float)Math.cos(theta) * r;
314 float y = (float)Math.sin(theta) * r;
315 Vector2f pLoc = new Vector2f(spawnPoint.x + x, spawnPoint.y + y);
316
317 float speed = minVel + (maxVel - minVel) * (float) Math.random();
318 speed *= speedMult;
319
320 Vector2f pVel = Misc.getUnitVectorAtDegreeAngle((float) Math.toDegrees(theta));
321 pVel.scale(speed);
322
323 float pSize = minSize + (maxSize - minSize) * (float) Math.random();
324 float pDur = minDur + (maxDur - minDur) * (float) Math.random();
325 float endSize = endSizeMin + (endSizeMax - endSizeMin) * (float) Math.random();
326 //Global.getCombatEngine().addSmoothParticle(pLoc, pVel, pSize, 1f, pDur, pc);
327 Global.getCombatEngine().addNebulaParticle(pLoc, pVel, pSize, endSize, 0.1f, 0.5f, pDur, pc);
328 //Global.getCombatEngine().addNebulaSmoothParticle(pLoc, pVel, pSize, endSize, 0.1f, 0.5f, pDur, pc);
329 //Global.getCombatEngine().addSwirlyNebulaParticle(pLoc, pVel, pSize, endSize, 0.1f, 0.5f, pDur, pc, false);
330 }
331 //Global.getCombatEngine().setPaused(true);
332
333 notifySpawnedExplosionParticles(bomb.getLocation());
334 }
335 }
336 }
337
338 boolean multipleImpacts = ship.getMutableStats().getDynamic().getMod(impactCounterId).computeEffective(0) > 1;
339
340 String id = "od_system_mod";
341 ship.getMutableStats().getMaxSpeed().unmodifyFlat(id);
342 //float maxSpeedWithoutBonus = ship.getMaxSpeedWithoutBoost();
343 float maxSpeedWithoutBonus = ship.getMutableStats().getMaxSpeed().getModifiedValue();
344
345// if (!triggered) {
346// ship.giveCommand(ShipCommand.ACCELERATE, null, 0);
347// }
348
349 if (triggered) {
350 jitterTime += amount;
351 float intensity = bomb.getFlightTime() / bomb.getMaxFlightTime();
352 if (intensity > 1f) intensity = 1f;
353 if (triggered) intensity = 1f;
354 if (braking) {
355 intensity = 1f - brakingTime * 2f;
356 if (intensity < 0) intensity = 0;
357 }
358 float alt = 1f - (jitterTime / p.maxJitterDur);
359 if (alt < intensity) {
360 intensity = Math.max(alt, 0f);
361 }
362 Color jc = p.jitterColor;
363 ship.setJitter(this, jc, intensity, 3, 0f, 0f);
364 }
365
366 if (triggered && !braking) {
367 impactTime += amount * p.impactRateMult;
368
369 float mag = (1f - impactTime) * (1f - impactTime);
370
371 //mag = (float) Math.sin(impactTime * Math.PI);
372 //mag *= mag;
373 if (mag > 0) mag = (float) Math.sqrt(mag);
374
375 Vector2f forcePoint = launchSlot.computePosition(ship);
376
377 float dirToCenter = Misc.getAngleInDegrees(forcePoint, ship.getLocation());
378 float angleDiff = Misc.getAngleDiff(forceAngle, dirToCenter);
379// if (angleDiff > 180f) {
380// angleDiff = 360f - angleDiff;
381// }
382
383 float totalAccel = p.impactAccel;
384 float portionAppliedToAngularVelocity = angleDiff * 1f / 90f;
385 if (portionAppliedToAngularVelocity > 1f) portionAppliedToAngularVelocity = 1f;
386
387 Vector2f acc = Misc.getUnitVectorAtDegreeAngle(forceAngle);
388 acc.scale(totalAccel * (1f - portionAppliedToAngularVelocity * 0.2f));
389 acc.scale(mag * amount);
390
391 Vector2f.add(ship.getVelocity(), acc, ship.getVelocity());
392
393 float angVelChange = portionAppliedToAngularVelocity * ship.getMaxTurnRate() * 0.25f;
394 angVelChange *= mag;
395 angVelChange *= Misc.getClosestTurnDirection(forceAngle, dirToCenter);
396 ship.setAngularVelocity(ship.getAngularVelocity() + angVelChange * amount);
397
398 float maxSpeedBoost = 1000f * Math.max(0f, (1f - portionAppliedToAngularVelocity) * 0.5f);
399
400 if (maxSpeedBoost > 0) {
401 ship.getMutableStats().getMaxSpeed().modifyFlat(id, maxSpeedBoost);
402 } else {
403 ship.getMutableStats().getMaxSpeed().unmodifyFlat(id);
404 }
405 ship.getMutableStats().getDeceleration().modifyFlat(id, 1f * Math.max(maxSpeedWithoutBonus, ship.getVelocity().length() - maxSpeedWithoutBonus));
406 //ship.getMutableStats().getTurnAcceleration().modifyFlat(id, 100f * (1f - mag));
407 //ship.giveCommand(ShipCommand.ACCELERATE, null, 0);
408 ship.blockCommandForOneFrame(ShipCommand.ACCELERATE);
409 ship.blockCommandForOneFrame(ShipCommand.ACCELERATE_BACKWARDS);
410 ship.giveCommand(ShipCommand.DECELERATE, null, 0);
411 //ship.getEngineController().forceShowAccelerating();
412 if (impactTime >= 1f) {
413 braking = true;
414 //ship.getMutableStats().getTurnAcceleration().unmodify(id);
415 ship.getMutableStats().getMaxSpeed().unmodify(id);
416 }
417 }
418
419 if (braking) {
420 if (!multipleImpacts) {
421 ship.getMutableStats().getDeceleration().modifyFlat(id, 2f * Math.max(maxSpeedWithoutBonus, ship.getVelocity().length() - maxSpeedWithoutBonus));
422 //ship.giveCommand(ShipCommand.ACCELERATE, null, 0);
423 ship.blockCommandForOneFrame(ShipCommand.ACCELERATE);
424 ship.blockCommandForOneFrame(ShipCommand.ACCELERATE_BACKWARDS);
425 ship.giveCommand(ShipCommand.DECELERATE, null, 0);
426 //ship.getEngineController().forceShowAccelerating();
427 }
428 brakingTime += amount;
429 float threshold = 3f;
430 if (multipleImpacts) threshold = 0.1f;
431 if (brakingTime >= threshold || ship.getVelocity().length() <= maxSpeedWithoutBonus) {
432 done = true;
433 }
434 }
435
436
437 if ((!triggered && elapsed > 1f) || done) {
438 Global.getCombatEngine().removePlugin(this);
439
440 ship.getMutableStats().getDeceleration().unmodify(id);
441 //ship.getMutableStats().getTurnAcceleration().unmodify(id);
442 ship.getMutableStats().getMaxSpeed().unmodify(id);
443
444 ship.getMutableStats().getDynamic().getMod(impactCounterId).unmodifyFlat("od_launch_" + launchLoc);
445 }
446 }
447 };
448 }
449
450
451 @Override
452 public boolean isUsable(ShipSystemAPI system, ShipAPI ship) {
453 if (ship.getEngineController().isFlamedOut() || ship.getEngineController().isFlamingOut()) {
454 return false;
455 }
456 return super.isUsable(system, ship);
457 }
458
459
460
461}
462
463
464
465
466
467
468
469
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
void spawnBomb(ShipAPI source, WeaponSlotAPI slot)
void unapply(MutableShipStatsAPI stats, String id)
boolean isUsable(ShipSystemAPI system, ShipAPI ship)
void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
void advanceImpl(float amount, ShipAPI ship, State state, float effectLevel)
EveryFrameCombatPlugin createBombImpactPlugin(final ShipAPI ship, final WeaponSlotAPI launchSlot, final MissileAPI bomb, final Vector2f launchLoc, final float launchAngle)