Starsector API
Loading...
Searching...
No Matches
DEMScript.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat.dem;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.List;
6
7import org.json.JSONArray;
8import org.json.JSONException;
9import org.json.JSONObject;
10import org.lwjgl.util.vector.Vector2f;
11
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
14import com.fs.starfarer.api.combat.CollisionClass;
15import com.fs.starfarer.api.combat.CombatEngineLayers;
16import com.fs.starfarer.api.combat.CombatEntityAPI;
17import com.fs.starfarer.api.combat.DamageType;
18import com.fs.starfarer.api.combat.GuidedMissileAI;
19import com.fs.starfarer.api.combat.MissileAIPlugin;
20import com.fs.starfarer.api.combat.MissileAPI;
21import com.fs.starfarer.api.combat.ShipAPI;
22import com.fs.starfarer.api.combat.ShipCommand;
23import com.fs.starfarer.api.combat.ShipHullSpecAPI;
24import com.fs.starfarer.api.combat.ShipVariantAPI;
25import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags;
26import com.fs.starfarer.api.combat.WeaponAPI;
27import com.fs.starfarer.api.input.InputEventAPI;
28import com.fs.starfarer.api.loading.WeaponGroupSpec;
29import com.fs.starfarer.api.loading.WeaponGroupType;
30import com.fs.starfarer.api.util.Misc;
31
35public class DEMScript extends BaseEveryFrameCombatPlugin implements MissileAIPlugin {
36
37 public static enum State {
38 WAIT,
39 TURN_TO_TARGET,
40 SIGNAL,
41 FIRE,
42 DONE,
43 }
44
45 protected State state = State.WAIT;
46 protected MissileAPI missile;
47 protected ShipAPI ship;
48 protected WeaponAPI weapon;
49 protected CombatEntityAPI fireTarget;
50 protected ShipAPI demDrone;
51
52 //protected Vector2f targetingLaserFireOffset = new Vector2f();
53 protected List<Vector2f> targetingLaserFireOffset = new ArrayList<Vector2f>();
54 protected List<Vector2f> targetingLaserSweepAngles = new ArrayList<Vector2f>();
55 protected List<Vector2f> payloadSweepAngles = new ArrayList<Vector2f>();
56 protected List<Float> payloadSweepPhaseShift = new ArrayList<Float>();
57 protected float minDelayBeforeTriggering = 0f;
58 protected boolean useTriggerAngle = false;
59 protected float triggerAngle = 0f;
60 protected float allowedDriftFraction = 0f;
61 protected float triggerDistance = 0f;
62 protected float turnRateBoost = 0f;
63 protected float turnRateMultOnSignal = 1f;
64 protected float targetingLaserArc = 0f;
65 protected float targetingTime = 1f;
66 protected float firingTime = 1f;
67 protected String targetingLaserId;
68 protected String payloadWeaponId;
69 protected float preferredMinFireDistance;
70 protected float preferredMaxFireDistance;
71 protected float targetingLaserRange;
72 protected float payloadSweepRateMult;
73 protected boolean bombPumped;
74 protected boolean fadeOutEngineWhenFiring;
75 protected boolean destroyMissleWhenDoneFiring;
76 protected boolean randomStrafe;
79 protected boolean snapFacingToTargetIfCloseEnough = true;
80 protected Color destroyedExplosionColor;
81
82 protected float elapsedWaiting = 0f;
83 protected float elapsedTargeting = 0f;
84 protected float elapsedFiring = 0f;
85 //protected DamagingProjectileAPI explosion;
86 protected int explosionDelayFrames = 0;
87 protected float strafeDur = 0f;
88 protected float strafeDir = 0f;
89 protected boolean exploded = false;
90
91 protected ShapedExplosionParams p;
92
93 public DEMScript(MissileAPI missile, ShipAPI ship, WeaponAPI weapon) {
94 this.missile = missile;
95 this.ship = ship;
96 this.weapon = weapon;
97
98 JSONObject json = missile.getSpec().getBehaviorJSON();
99 //minDelayBeforeTriggering = (float) json.optDouble("minDelayBeforeTriggering", 1f);
100 minDelayBeforeTriggering = getValue(json, "minDelayBeforeTriggering", 1f);
101 allowedDriftFraction = (float) json.optDouble("allowedDriftFraction", 0.33f);
102 //triggerDistance = (float) json.optDouble("triggerDistance", 500f);
103 //preferredMinFireDistance = (float) json.optDouble("preferredMinFireDistance", 0f);
104 triggerDistance = getValue(json, "triggerDistance", 500f);
105
106 try {
107 if (json.optBoolean("withShapedExplosion")) {
108 p = new ShapedExplosionParams();
109 p.load(json);;
110 }
111 } catch (Exception e) {
112 throw new RuntimeException(e);
113 }
114
115 snapFacingToTargetIfCloseEnough = json.optBoolean("snapFacingToTargetIfCloseEnough", false);
116
117 if (json.has("triggerAngle")) {
118 useTriggerAngle = true;
119 triggerAngle = getValue(json, "triggerAngle", 0f);
120 }
121
122 preferredMaxFireDistance = getValue(json, "preferredMaxFireDistance", triggerDistance);
123 preferredMinFireDistance = getValue(json, "preferredMinFireDistance", 0f);
124 if (json.has("targetingLaserRange")) {
125 targetingLaserRange = (float) json.optDouble("targetingLaserRange", 600f);
126 } else {
128 }
129 turnRateBoost = (float) json.optDouble("turnRateBoost", 100f);
130 turnRateMultOnSignal = (float) json.optDouble("turnRateMultOnSignal", 1f);
131 //targetingTime = (float) json.optDouble("targetingTime", 1f);
132 targetingTime = getValue(json, "targetingTime", 1f);
133 firingTime = (float) json.optDouble("firingTime", 1.25f);
134 targetingLaserId = json.optString("targetingLaserId", null);
135 payloadWeaponId = json.optString("payloadWeaponId", null);
136 targetingLaserArc = (float) json.optDouble("targetingLaserArc", 10f);
137 payloadSweepRateMult = (float) json.optDouble("payloadSweepRateMult", 1f);
138 bombPumped = json.optBoolean("bombPumped", false);
139 fadeOutEngineWhenFiring = json.optBoolean("fadeOutEngineWhenFiring", false);
140 destroyMissleWhenDoneFiring = json.optBoolean("destroyMissleWhenDoneFiring", false);
141 randomStrafe = json.optBoolean("randomStrafe", false);
142 randomPayloadSweepPhaseShift = json.optBoolean("randomPayloadSweepPhaseShift", false);
143 payloadCenterSweepOnOriginalOffset = json.optBoolean("payloadCenterSweepOnOriginalOffset", false);
144 if (json.has("destroyedExplosionColor")) {
145 try {
146 destroyedExplosionColor = Misc.optColor(json, "destroyedExplosionColor", null);
147 } catch (Exception e) {
148 throw new RuntimeException(e);
149 }
150 }
151
152 JSONArray arr = json.optJSONArray("targetingLaserFireOffset");
153 if (arr != null) {
154 for (int i = 0; i < arr.length(); i += 2) {
155 Vector2f v = new Vector2f((float) arr.optDouble(i), (float) arr.optDouble(i + 1));
157 }
158 }
159 arr = json.optJSONArray("targetingLaserSweepAngles");
160 if (arr != null) {
161 for (int i = 0; i < arr.length(); i += 2) {
162 Vector2f v = new Vector2f((float) arr.optDouble(i), (float) arr.optDouble(i + 1));
164 }
165 }
166 arr = json.optJSONArray("payloadSweepAngles");
167 if (arr != null) {
168 for (int i = 0; i < arr.length(); i += 2) {
169 Vector2f v = new Vector2f((float) arr.optDouble(i), (float) arr.optDouble(i + 1));
170
172 float orig = Global.getSettings().getWeaponSpec(payloadWeaponId).getTurretAngleOffsets().get(i/2);
173 v.x += orig;
174 v.y += orig;
175 }
176
177 payloadSweepAngles.add(v);
178 }
179 }
181 for (int i = 0; i < payloadSweepAngles.size(); i++) {
182 payloadSweepPhaseShift.add((float) Math.random());
183 }
184 }
185 float maxSpeed = Math.max(50f, missile.getMaxSpeed());
186 float etaMod = -1f * triggerDistance / maxSpeed;
187 missile.setEtaModifier(etaMod);
188 }
189
190 public static float getValue(JSONObject json, String key, float defaultValue) {
191 JSONArray arr = json.optJSONArray(key);
192 if (arr != null) {
193 Vector2f v = new Vector2f((float) arr.optDouble(0), (float) arr.optDouble(1));
194 return v.x + (v.y - v.x) * (float) Math.random();
195 }
196 return (float) json.optDouble(key, defaultValue);
197 }
198
199
200 @Override
201 public void advance(float amount, List<InputEventAPI> events) {
202 if (Global.getCombatEngine().isPaused()) return;
203
204 // so that the AI doesn't treat fizzled missiles as a threat due to the drone still being there
205 if (missile.isFizzling()) {
206 if (demDrone != null) {
207 Global.getCombatEngine().removeEntity(demDrone);
208 }
209 }
210
211 boolean doCleanup = state == State.DONE ||
212 (!bombPumped || state.ordinal() < State.FIRE.ordinal()) &&
213 (missile.isExpired() || missile.didDamage() ||
214 !Global.getCombatEngine().isEntityInPlay(missile));
215 if (doCleanup) {
216 if (demDrone != null) {
217 Global.getCombatEngine().removeEntity(demDrone);
218 }
219 Global.getCombatEngine().removePlugin(this);
220 return;
221 }
222
223 if (state == State.WAIT && missile.isArmed() && !missile.isFizzling() && !missile.isFading()) {
224 CombatEntityAPI target = null;
225 if (missile.getAI() instanceof GuidedMissileAI) {
226 GuidedMissileAI ai = (GuidedMissileAI) missile.getAI();
227 target = ai.getTarget();
228 }
229 elapsedWaiting += amount;
230
231 if (useTriggerAngle && target != null) {
232 Vector2f from = target.getLocation();
233 if (target instanceof ShipAPI) {
234 from = ((ShipAPI) target).getShieldCenterEvenIfNoShield();
235 }
236 float toMissile = Misc.getAngleInDegrees(from, missile.getLocation());
237 //float diff = Misc.getAngleDiff(target.getFacing(), toMissile);
238 //float toShip = Misc.getAngleInDegrees(from, ship.getLocation());
239 float toShip = Misc.getAngleInDegrees(from, missile.getSpawnLocation());
240 float diff = Misc.getAngleDiff(toShip, toMissile);
241 if (diff >= triggerAngle) {
243 }
244 }
245
246 if (target != null && elapsedWaiting >= minDelayBeforeTriggering) {
247 float dist = Misc.getDistance(target.getLocation(), missile.getLocation());
248 dist -= Global.getSettings().getTargetingRadius(missile.getLocation(), target, false);
249
250 if (dist < triggerDistance) {
251 missile.setMaxFlightTime(10000f);
252 state = State.TURN_TO_TARGET;
253 fireTarget = target;
254
255 // turn off the normal missile AI; this script is taking over
256 missile.setMissileAI(this);
257
258 missile.getEngineStats().getMaxTurnRate().modifyFlat("dem", turnRateBoost);
259 missile.getEngineStats().getTurnAcceleration().modifyFlat("dem", turnRateBoost * 2f);
260
261 ShipHullSpecAPI spec = Global.getSettings().getHullSpec("dem_drone");
262 ShipVariantAPI v = Global.getSettings().createEmptyVariant("dem_drone", spec);
263 v.addWeapon("WS 000", targetingLaserId);
264 WeaponGroupSpec g = new WeaponGroupSpec(WeaponGroupType.LINKED);
265 g.addSlot("WS 000");
266 v.addWeaponGroup(g);
267 v.addWeapon("WS 001", payloadWeaponId);
268 g = new WeaponGroupSpec(WeaponGroupType.LINKED);
269 g.addSlot("WS 001");
270 v.addWeaponGroup(g);
271
272 demDrone = Global.getCombatEngine().createFXDrone(v);
273 demDrone.setLayer(CombatEngineLayers.ABOVE_SHIPS_AND_MISSILES_LAYER);
274 demDrone.setOwner(ship.getOriginalOwner());
275 demDrone.getMutableStats().getBeamWeaponRangeBonus().modifyFlat("dem", targetingLaserRange);
276 demDrone.getMutableStats().getHullDamageTakenMult().modifyMult("dem", 0f); // so it's non-targetable
277 demDrone.setDrone(true);
278 demDrone.getAIFlags().setFlag(AIFlags.DRONE_MOTHERSHIP, 100000f, ship);
279 demDrone.getMutableStats().getEnergyWeaponDamageMult().applyMods(ship.getMutableStats().getMissileWeaponDamageMult());
280 demDrone.getMutableStats().getMissileWeaponDamageMult().applyMods(ship.getMutableStats().getMissileWeaponDamageMult());
281 demDrone.getMutableStats().getBallisticWeaponDamageMult().applyMods(ship.getMutableStats().getMissileWeaponDamageMult());
282 demDrone.setCollisionClass(CollisionClass.NONE);
283 demDrone.giveCommand(ShipCommand.SELECT_GROUP, null, 0);
284 Global.getCombatEngine().addEntity(demDrone);
285
286 if (targetingLaserFireOffset.size() > 0) {
287 WeaponAPI tLaser = demDrone.getWeaponGroupsCopy().get(0).getWeaponsCopy().get(0);
288 tLaser.ensureClonedSpec();
289 tLaser.getSpec().getTurretFireOffsets().clear();
290 tLaser.getSpec().getTurretFireOffsets().addAll(targetingLaserFireOffset);
291 }
292 }
293 }
294 } else if (state == State.TURN_TO_TARGET) {
295 float angle = Misc.getAngleInDegrees(missile.getLocation(), fireTarget.getLocation());
296
297 if (Misc.isInArc(missile.getFacing(), targetingLaserArc, angle)) {
298 missile.getEngineStats().getMaxTurnRate().modifyMult("dem_mult", turnRateMultOnSignal);
299 //missile.getEngineStats().getTurnAcceleration().modifyMult("dem_mult", turnRateMultOnSignal);
300
301 state = State.SIGNAL;
302 }
303 } else if (state == State.SIGNAL) {
304
305 if (targetingLaserSweepAngles.size() > 0) {
306 float progress = elapsedTargeting / targetingTime;
307 WeaponAPI tLaser = demDrone.getWeaponGroupsCopy().get(0).getWeaponsCopy().get(0);
308 tLaser.ensureClonedSpec();
309 tLaser.getSpec().getTurretAngleOffsets().clear();
310 for (Vector2f curr : targetingLaserSweepAngles) {
311 float angle = 0f;
312 if (progress < 0.5f) {
313 angle = curr.x + (curr.y - curr.x) * progress * 2f;
314 } else {
315 angle = curr.x + (curr.y - curr.x) * (1f - progress) * 2f;
316 }
317 tLaser.getSpec().getTurretAngleOffsets().add(angle);
318 }
319 }
320
321 if (targetingLaserRange > 0 && targetingTime > 0) {
322 demDrone.giveCommand(ShipCommand.FIRE, fireTarget.getLocation(), 0);
323 }
324
325 elapsedTargeting += amount;
327 state = State.FIRE;
328 demDrone.giveCommand(ShipCommand.SELECT_GROUP, null, 1);
329
330 if (!bombPumped) {
331 //missile.flameOut();
332 missile.setFlightTime(0f);
333 missile.setMaxFlightTime(firingTime);
334 missile.setNoFlameoutOnFizzling(true);
335 missile.setNoGlowTime(0f);
336 missile.setFizzleTime(0.5f);
337 missile.setFadeTime(0.5f);
338 missile.setEtaModifier(0f);
339 }
340 }
341 } else if (state == State.FIRE) {
342 //Global.getCombatEngine().setPaused(true);
343
344 if (bombPumped && !exploded && explosionDelayFrames >= 1) {
345 missile.explode();
346 Global.getCombatEngine().removeEntity(missile);
347
348// ShapedExplosionParams p = new ShapedExplosionParams();
349// p.shapedExplosionNumParticles = 50;
350// p.shapedExplosionMinParticleDur = 0.7f;
351// p.shapedExplosionMaxParticleDur = 1.1f;
352// p.shapedExplosionMinParticleSize = 50f;
353// p.shapedExplosionMaxParticleSize = 70f;
354// p.shapedExplosionColor = new Color(255,40,40,155);
355// p.shapedExplosionArc = 45f;
356// p.shapedExplosionMinParticleVel = 50f;
357// p.shapedExplosionMaxParticleVel = 250f;
358//
361//
362// float speedMult = 1f;
363// p.shapedExplosionMinParticleVel *= speedMult;
364// p.shapedExplosionMaxParticleVel *= speedMult;
365// p.shapedExplosionMinParticleDur /= speedMult;
366// p.shapedExplosionMaxParticleDur /= speedMult;
367 if (p != null) {
368 spawnShapedExplosion(missile.getLocation(), missile.getFacing(), p);
369 }
370 exploded = true;
371 }
373
375 float progress = elapsedFiring / firingTime;
376 progress *= 2f;
377 if (progress > 1f) progress = 1f;
378 missile.getEngineController().fadeToOtherColor(this, Misc.zeroColor, Misc.zeroColor, progress, 1f);
379 }
380
381 if (payloadSweepAngles.size() > 0) {
382 WeaponAPI payload = demDrone.getWeaponGroupsCopy().get(1).getWeaponsCopy().get(0);
383 payload.ensureClonedSpec();
384 payload.getSpec().getTurretAngleOffsets().clear();
385 int index = 0;
386 for (Vector2f curr : payloadSweepAngles) {
387 float angle = 0f;
388 float progress = elapsedFiring / firingTime;
389 if (progress < 0.5f) {
390 angle = curr.x + (curr.y - curr.x) * progress * 2f;
391 } else {
392 angle = curr.x + (curr.y - curr.x) * (1f - progress) * 2f;
393 }
395 progress += payloadSweepPhaseShift.get(index);
396 progress = (float) Math.sin(progress * Math.PI * payloadSweepRateMult);
397 progress = Math.abs(progress);
398
399 angle = curr.x + (curr.y - curr.x) * progress;
400 }
401
402
403 payload.getSpec().getTurretAngleOffsets().add(angle);
404 index++;
405 }
406 }
407
408
409 // use payload's normal range as defined in weapon_data.csv
410 demDrone.getMutableStats().getBeamWeaponRangeBonus().unmodifyFlat("dem");
411 demDrone.giveCommand(ShipCommand.FIRE, fireTarget.getLocation(), 0);
412
413 elapsedFiring += amount;
414 if (elapsedFiring >= firingTime) {
415 missile.setNoGlowTime(10f);
416 state = State.DONE;
417
419 missile.getVelocity().set(0, 0);
420 if (destroyedExplosionColor != null) {
421 missile.setDestroyedExplosionColorOverride(destroyedExplosionColor);
422 }
423 Global.getCombatEngine().applyDamage(missile, missile.getLocation(), 100000f, DamageType.ENERGY, 0f, false, false, demDrone, false);
424 } else {
425 missile.setFizzleTime(1f);
426 missile.setArmedWhileFizzling(false);
427 }
428 }
429 }
430
431 doMissileControl(amount);
432 updateDroneState(amount);
433
434 }
435
436 protected void updateDroneState(float amount) {
437 if (demDrone != null) {
438 //System.out.println("FIRE FACING: " + missile.getFacing());
439 //if (explosion == null) {
440 demDrone.setOwner(missile.getOwner());
441 demDrone.getLocation().set(missile.getLocation());
442 demDrone.setFacing(missile.getFacing());
443 demDrone.getVelocity().set(missile.getVelocity());
444 demDrone.setAngularVelocity(missile.getAngularVelocity());
445 //demDrone.getMouseTarget().set(fireTarget.getLocation());
446 //}
447 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(missile.getFacing());
448 dir.scale(1000f);
449 Vector2f.add(dir, missile.getLocation(), dir);
450 demDrone.getMouseTarget().set(dir);
451
452 //demDrone.getMutableStats().getWeaponTurnRateBonus().modifyMult("dem", 0f);
453
454 WeaponAPI tLaser = demDrone.getWeaponGroupsCopy().get(0).getWeaponsCopy().get(0);
455 WeaponAPI payload = demDrone.getWeaponGroupsCopy().get(1).getWeaponsCopy().get(0);
456 tLaser.setFacing(missile.getFacing());
457 payload.setFacing(missile.getFacing());
458 tLaser.setKeepBeamTargetWhileChargingDown(true);
459 payload.setKeepBeamTargetWhileChargingDown(true);
460 tLaser.setScaleBeamGlowBasedOnDamageEffectiveness(false);
461 if (firingTime <= 2f) {
462 payload.setScaleBeamGlowBasedOnDamageEffectiveness(false);
463 }
464 tLaser.updateBeamFromPoints();
465 payload.updateBeamFromPoints();
466 }
467 }
468
469 protected void doMissileControl(float amount) {
470 if (state == State.TURN_TO_TARGET || state == State.SIGNAL ||
471 (state == State.FIRE && !bombPumped && !fadeOutEngineWhenFiring)) {
472
473 float dist = Misc.getDistance(fireTarget.getLocation(), missile.getLocation());
474 dist -= Global.getSettings().getTargetingRadius(missile.getLocation(), fireTarget, false);
475 if (dist < preferredMinFireDistance) {
476 missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);
477 } else if (dist > preferredMaxFireDistance) {
478 missile.giveCommand(ShipCommand.ACCELERATE);
479 } else if (missile.getVelocity().length() > missile.getMaxSpeed() * allowedDriftFraction) {
480 missile.giveCommand(ShipCommand.DECELERATE);
481 }
482 float dir = Misc.getAngleInDegrees(missile.getLocation(), fireTarget.getLocation());
483 float diff = Misc.getAngleDiff(missile.getFacing(), dir);
484 float rate = missile.getMaxTurnRate() * amount;
485// float turnDir1 = Misc.getClosestTurnDirection(missile.getFacing(), dir);
486// boolean turningTowardsDesiredFacing = Math.signum(turnDir1) == Math.signum(missile.getAngularVelocity());
487 boolean turningTowardsDesiredFacing = true;
488 //snapFacingToTargetIfCloseEnough = true;
489 boolean phased = fireTarget instanceof ShipAPI && ((ShipAPI)fireTarget).isPhased();
490 if (!phased) {
491 if (diff <= rate * 0.25f && turningTowardsDesiredFacing && snapFacingToTargetIfCloseEnough) {
492 missile.setFacing(dir);
493 } else {
494 Misc.turnTowardsPointV2(missile, fireTarget.getLocation(), 0f);
495 }
496 }
497
498 if (randomStrafe) {
499 if (strafeDur <= 0) {
500 float r = (float) Math.random();
501
502 if (strafeDir == 0) {
503 if (r < 0.4f) {
504 strafeDir = 1f;
505 } else if (r < 0.8f) {
506 strafeDir = -1f;
507 } else {
508 strafeDir = 0f;
509 }
510 } else {
511 if (r < 0.8f) {
513 } else {
514 strafeDir = 0f;
515 }
516 }
517
518 strafeDur = 0.5f + (float) Math.random() * 0.5f;
519 //strafeDur *= 0.5f;
520 }
521
522 Vector2f driftDir = Misc.getUnitVectorAtDegreeAngle(missile.getFacing() + 90f);
523 if (strafeDir == 1f) driftDir.negate();
524
525 float distToShip = Misc.getDistance(ship.getLocation(), missile.getLocation());
526 float shipToFireTarget = Misc.getDistance(ship.getLocation(), fireTarget.getLocation());
527 float extra = 0f;
528 if (dist > shipToFireTarget) extra = dist - shipToFireTarget;
529 if (distToShip < ship.getCollisionRadius() * 1f + extra) {
530 float away = Misc.getAngleInDegrees(ship.getLocation(), missile.getLocation());
531 float turnDir = Misc.getClosestTurnDirection(away, missile.getFacing());
532 strafeDir = turnDir;
533 }
534
535 float maxDrift = missile.getMaxSpeed() * allowedDriftFraction;
536 float speedInDir = Vector2f.dot(driftDir, missile.getVelocity());
537
538 if (speedInDir < maxDrift) {
539 if (strafeDir == 1f) {
540 missile.giveCommand(ShipCommand.STRAFE_RIGHT);
541 } else if (strafeDir == -1f) {
542 missile.giveCommand(ShipCommand.STRAFE_LEFT);
543 }
544 }
545
546 strafeDur -= amount;
547 }
548 }
549 }
550
551 public void advance(float amount) {
552 // MissileAIPlugin.advance()
553 // unused, but just want the missile to have a non-null AI
554 }
555
556
557
558 public static class ShapedExplosionParams {
559 public float shapedExplosionEndSizeMin = 1f;
560 public float shapedExplosionEndSizeMax = 2f;
561 public Color shapedExplosionColor = new Color(255,150,130,155);
562 public int shapedExplosionNumParticles = 200;
563 public float shapedExplosionMinParticleSize = 80;
564 public float shapedExplosionMaxParticleSize = 100;
565 public float shapedExplosionScatter = 100f;
566 public float shapedExplosionMinParticleVel = 100;
567 public float shapedExplosionMaxParticleVel = 350f;
568 public float shapedExplosionMinParticleDur = 1f;
569 public float shapedExplosionMaxParticleDur = 2f;
570 public float shapedExplosionArc = 90f;
571
572 public void load(JSONObject json) throws JSONException {
573 shapedExplosionEndSizeMin = (float)json.optDouble("shapedExplosionEndSizeMin");
574 shapedExplosionEndSizeMax = (float)json.optDouble("shapedExplosionEndSizeMax");
575 shapedExplosionNumParticles = json.optInt("shapedExplosionNumParticles");
576 shapedExplosionMinParticleSize = (float)json.optDouble("shapedExplosionMinParticleSize");
577 shapedExplosionMaxParticleSize = (float)json.optDouble("shapedExplosionMaxParticleSize");
578 shapedExplosionScatter = (float)json.optDouble("shapedExplosionScatter");
579 shapedExplosionMinParticleVel = (float)json.optDouble("shapedExplosionMinParticleVel");
580 shapedExplosionMaxParticleVel = (float)json.optDouble("shapedExplosionMaxParticleVel");
581 shapedExplosionMinParticleDur = (float)json.optDouble("shapedExplosionMinParticleDur");
582 shapedExplosionMaxParticleDur = (float)json.optDouble("shapedExplosionMaxParticleDur");
583 shapedExplosionArc = (float)json.optDouble("shapedExplosionArc");
584 shapedExplosionColor = Misc.optColor(json, "shapedExplosionColor", null);
585 }
586 }
587
588 public void spawnShapedExplosion(Vector2f loc, float angle, ShapedExplosionParams p) {
589
590 if (Global.getCombatEngine().getViewport().isNearViewport(ship.getLocation(), 800f)) {
591 int numParticles = p.shapedExplosionNumParticles;
592 float minSize = p.shapedExplosionMinParticleSize;
593 float maxSize = p.shapedExplosionMaxParticleSize;
594 Color pc = p.shapedExplosionColor;
595
596 float minDur = p.shapedExplosionMinParticleDur;
597 float maxDur = p.shapedExplosionMaxParticleDur;
598
599 float arc = p.shapedExplosionArc;
600 float scatter = p.shapedExplosionScatter;
601 float minVel = p.shapedExplosionMinParticleVel;
602 float maxVel = p.shapedExplosionMaxParticleVel;
603
604 float endSizeMin = p.shapedExplosionEndSizeMin;
605 float endSizeMax = p.shapedExplosionEndSizeMax;
606
607 Vector2f spawnPoint = new Vector2f(loc);
608 for (int i = 0; i < numParticles; i++) {
609 //p.setMaxAge(500 + (int)(Math.random() * 1000f));
610 float angleOffset = (float) Math.random();
611 if (angleOffset > 0.2f) {
612 angleOffset *= angleOffset;
613 }
614 float speedMult = 1f - angleOffset;
615 speedMult = 0.5f + speedMult * 0.5f;
616 angleOffset *= Math.signum((float) Math.random() - 0.5f);
617 angleOffset *= arc/2f;
618 float theta = (float) Math.toRadians(angle + angleOffset);
619 float r = (float) (Math.random() * Math.random() * scatter);
620 float x = (float)Math.cos(theta) * r;
621 float y = (float)Math.sin(theta) * r;
622 Vector2f pLoc = new Vector2f(spawnPoint.x + x, spawnPoint.y + y);
623
624 float speed = minVel + (maxVel - minVel) * (float) Math.random();
625 speed *= speedMult;
626
627 Vector2f pVel = Misc.getUnitVectorAtDegreeAngle((float) Math.toDegrees(theta));
628 pVel.scale(speed);
629
630 float pSize = minSize + (maxSize - minSize) * (float) Math.random();
631 float pDur = minDur + (maxDur - minDur) * (float) Math.random();
632 float endSize = endSizeMin + (endSizeMax - endSizeMin) * (float) Math.random();
633 //Global.getCombatEngine().addSmoothParticle(pLoc, pVel, pSize, 1f, pDur, pc);
634 Global.getCombatEngine().addNebulaParticle(pLoc, pVel, pSize, endSize, 0.1f, 0.5f, pDur, pc);
635 //Global.getCombatEngine().addNebulaSmoothParticle(pLoc, pVel, pSize, endSize, 0.1f, 0.5f, pDur, pc);
636 //Global.getCombatEngine().addSwirlyNebulaParticle(pLoc, pVel, pSize, endSize, 0.1f, 0.5f, pDur, pc, false);
637 }
638 }
639 }
640}
641
642
643
644
645
646
647
648
static SettingsAPI getSettings()
Definition Global.java:51
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
static float getValue(JSONObject json, String key, float defaultValue)
void spawnShapedExplosion(Vector2f loc, float angle, ShapedExplosionParams p)
void advance(float amount, List< InputEventAPI > events)
DEMScript(MissileAPI missile, ShipAPI ship, WeaponAPI weapon)
float getTargetingRadius(Vector2f from, CombatEntityAPI target, boolean considerShield)
ShipHullSpecAPI getHullSpec(String hullId)
ShipVariantAPI createEmptyVariant(String hullVariantId, ShipHullSpecAPI hullSpec)
WeaponSpecAPI getWeaponSpec(String weaponId)