Starsector API
Loading...
Searching...
No Matches
EnergyLashSystemScript.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat.threat;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import java.awt.Color;
7
8import org.lwjgl.util.vector.Vector2f;
9
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
12import com.fs.starfarer.api.combat.CombatEngineAPI;
13import com.fs.starfarer.api.combat.DamageType;
14import com.fs.starfarer.api.combat.EmpArcEntityAPI;
15import com.fs.starfarer.api.combat.EmpArcEntityAPI.EmpArcParams;
16import com.fs.starfarer.api.combat.MutableShipStatsAPI;
17import com.fs.starfarer.api.combat.ShipAPI;
18import com.fs.starfarer.api.combat.ShipAPI.HullSize;
19import com.fs.starfarer.api.combat.ShipSystemAPI;
20import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState;
21import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags;
22import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
23import com.fs.starfarer.api.impl.combat.BaseShipSystemScript;
24import com.fs.starfarer.api.input.InputEventAPI;
25import com.fs.starfarer.api.loading.WeaponSlotAPI;
26import com.fs.starfarer.api.util.Misc;
27import com.fs.starfarer.api.util.Misc.FindShipFilter;
28
30
31 public static float MAX_LASH_RANGE = 1500f;
32
33 public static float DAMAGE = 0;
34 public static float EMP_DAMAGE = 1500;
35
36 public static float MIN_COOLDOWN = 2f;
37 public static float MAX_COOLDOWN = 10f;
38 public static float COOLDOWN_DP_MULT = 0.33f;
39
40 public static float MIN_HIT_ENEMY_COOLDOWN = 2f;
41 public static float MAX_HIT_ENEMY_COOLDOWN = 5f;
42 public static float HIT_PHASE_ENEMY_COOLDOWN_MULT = 2f;
43
44 public static float SWARM_TIMEOUT = 10f;
45
46 public static float PHASE_OVERLOAD_DUR = 1f;
47
48
49
50 public static class DelayedCombatActionPlugin extends BaseEveryFrameCombatPlugin {
51 float elapsed = 0f;
52 float delay;
53 Runnable r;
54
55 public DelayedCombatActionPlugin(float delay, Runnable r) {
56 this.delay = delay;
57 this.r = r;
58 }
59
60 @Override
61 public void advance(float amount, List<InputEventAPI> events) {
62 if (Global.getCombatEngine().isPaused()) return;
63
64 elapsed += amount;
65 if (elapsed < delay) return;
66
67 r.run();
68
70 engine.removePlugin(this);
71 }
72 }
73
74
75
77 protected List<WeaponSlotAPI> slots;
78 protected boolean readyToFire = true;
80 protected float cooldownToSet = -1f;
81
82 protected void findSlots(ShipAPI ship) {
83 if (slots != null) return;
84 slots = new ArrayList<>();
85 for (WeaponSlotAPI slot : ship.getHullSpec().getAllWeaponSlotsCopy()) {
86 if (slot.isSystemSlot()) {
87 slots.add(slot);
88 if (slot.getSlotSize() == WeaponSize.MEDIUM) {
89 mainSlot = slot;
90 }
91 }
92 }
93 }
94
95 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
96 ShipAPI ship = null;
97 //boolean player = false;
98 if (stats.getEntity() instanceof ShipAPI) {
99 ship = (ShipAPI) stats.getEntity();
100 //player = ship == Global.getCombatEngine().getPlayerShip();
101 } else {
102 return;
103 }
104
106
107 if ((state == State.COOLDOWN || state == State.IDLE) && cooldownToSet >= 0f) {
110 cooldownToSet = -1f;
111
112 }
113
114 if (state == State.IDLE || state == State.COOLDOWN || effectLevel <= 0f) {
115 readyToFire = true;
116 }
117
118 if (state == State.IN || state == State.OUT) {
119 float jitterLevel = effectLevel;
120
121 float maxRangeBonus = 150f;
122 //float jitterRangeBonus = jitterLevel * maxRangeBonus;
123 float jitterRangeBonus = (1f - effectLevel * effectLevel) * maxRangeBonus;
124
125 float brightness = 0f;
126 float threshold = 0.1f;
127 if (effectLevel < threshold) {
128 brightness = effectLevel / threshold;
129 } else {
130 brightness = 1f - (effectLevel - threshold) / (1f - threshold);
131 }
132 if (brightness < 0) brightness = 0;
133 if (brightness > 1) brightness = 1;
134 if (state == State.OUT) {
135 jitterRangeBonus = 0f;
136 brightness = effectLevel * effectLevel;
137 }
139 //color = VoltaicDischargeOnFireEffect.EMP_FRINGE_COLOR_BRIGHT;
140 //ship.setJitterUnder(this, color, jitterLevel, 21, 0f, 3f + jitterRangeBonus);
141 //ship.setJitter(this, JITTER_COLOR, jitterLevel, 4, 0f, 0 + jitterRangeBonus * 0.67f);
142 //ship.setJitter(this, color, jitterLevel, 1, 0f, 3f);
143 ship.setJitter(this, color, jitterLevel, 5, 0f, 3f + jitterRangeBonus);
144 }
145
146 if (effectLevel == 1 && readyToFire) {
147 ShipAPI target = findTarget(ship);
148 readyToFire = false;
149 if (target != null) {
151 findSlots(ship);
152
153 Vector2f slotLoc = mainSlot.computePosition(ship);
154
155 EmpArcParams params = new EmpArcParams();
156 params.segmentLengthMult = 8f;
157 params.zigZagReductionFactor = 0.15f;
158 params.fadeOutDist = 500f;
159 params.minFadeOutMult = 2f;
160 params.flickerRateMult = 0.7f;
161
162 //params.movementDurMax = 0.1f;
163// params.movementDurMin = 0.25f;
164// params.movementDurMax = 0.25f;
165
166
167 if (ship.getOwner() == target.getOwner()) {
168 //params.flickerRateMult = 0.6f;
169 params.flickerRateMult = 0.3f;
170
172 if (ThreatSwarmAI.isAttackSwarm(target)) {
174 }
175 float emp = 0;
176 float dam = 0;
177 EmpArcEntityAPI arc = (EmpArcEntityAPI)engine.spawnEmpArcPierceShields(ship, slotLoc, ship, target,
179 dam,
180 emp, // emp
181 100000f, // max range
182 "energy_lash_friendly_impact",
183 100f, // thickness
184 //new Color(100,165,255,255),
185 color,
186 new Color(255,255,255,255),
187 params
188 );
189 arc.setTargetToShipCenter(slotLoc, target);
190 arc.setCoreWidthOverride(50f);
191
192 arc.setSingleFlickerMode(true);
193 //arc.setFadedOutAtStart(true);
194 Global.getSoundPlayer().playSound("energy_lash_fire", 1f, 1f, ship.getLocation(), ship.getVelocity());
195 } else {
196 params.flickerRateMult = 0.4f;
197
198 int numArcs = slots.size();
199 //numArcs = 1;
200
201 float emp = EMP_DAMAGE;
202 float dam = DAMAGE;
203
204 for (int i = 0; i < numArcs; i++) {
205 float delay = 0.03f * i;
206 //delay = 0f;
207
208// EmpArcParams params2 = new EmpArcParams();
209// params2.segmentLengthMult = 8f;
210// params2.zigZagReductionFactor = 0.15f;
211// params2.fadeOutDist = 500f;
212// params2.minFadeOutMult = 2f;
213// params2.flickerRateMult = 0.8f - i * 0.1f;
214// params2.flickerRateMult = 0.8f;
215
216 int index = i;
217 ShipAPI ship2 = ship;
218 Runnable r = new Runnable() {
219 @Override
220 public void run() {
221 Vector2f slotLoc = slots.get(index).computePosition(ship2);
223 Color core = new Color(255,255,255,255);
224 if (target.isPhased()) {
227 }
228 //color = Misc.interpolateColor(color, new Color(255,0,255), 0.25f);
229 EmpArcEntityAPI arc = (EmpArcEntityAPI)engine.spawnEmpArc(ship2, slotLoc, ship2, target,
231 dam,
232 emp, // emp
233 100000f, // max range
234 "energy_lash_enemy_impact",
235 60f, // thickness
236 //new Color(100,165,255,255),
237 color,
238 core,
239 params
240 );
241 arc.setCoreWidthOverride(40f);
242 arc.setSingleFlickerMode(true);
243 }
244 };
245 if (delay <= 0f) {
246 r.run();
247 } else {
248 Global.getCombatEngine().addPlugin(new DelayedCombatActionPlugin(delay, r));
249 }
250
251 Global.getSoundPlayer().playSound("energy_lash_fire_at_enemy", 1f, 1f, ship.getLocation(), ship.getVelocity());
252
253// arc.setFadedOutAtStart(true);
254// arc.setRenderGlowAtStart(false);
255 }
256 }
257
258 applyEffectToTarget(ship, target);
259 }
260 }
261 }
262
263
264
265
266 protected void applyEffectToTarget(ShipAPI ship, ShipAPI target) {
267 boolean isSwarm = ThreatSwarmAI.isAttackSwarm(target);
268 if (!isSwarm) {
269 if (target == null || target.getSystem() == null || target.isHulk()) return;
270 }
271 if (ship == null || ship.getSystem() == null || ship.isHulk()) return;
272
273 if (ship.getOwner() == target.getOwner()) {
274 if (target.getSystem() != null && target.getSystem().getScript() instanceof EnergyLashActivatedSystem) {
276 script.hitWithEnergyLash(ship, target);
277 } else if (isSwarm) {
280 }
281
282 float cooldown = target.getHullSpec().getSuppliesToRecover();
283 //float cooldown = target.getMutableStats().getSuppliesToRecover().getBaseValue();
284 //cooldown = (int)Math.round(target.getMutableStats().getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).computeEffective(cooldown));
285
286 cooldown = MIN_COOLDOWN + cooldown * COOLDOWN_DP_MULT;
287 if (cooldown > MAX_COOLDOWN) cooldown = MAX_COOLDOWN;
288 if (target.isFighter()) cooldown = MIN_COOLDOWN;
289// ship.getSystem().setCooldown(cooldown);
290// ship.getSystem().setCooldownRemaining(cooldown);
291 cooldownToSet = cooldown;
292 } else {
293 boolean hitPhase = false;
294 if (target.isPhased()) {
297 if (target.getFluxTracker().showFloaty() ||
299 target == Global.getCombatEngine().getPlayerShip()) {
301 target.getFluxTracker().showOverloadFloatyIfNeeded("Phase Field Disruption!",
303 }
304
306 @Override
307 public void advance(float amount, List<InputEventAPI> events) {
308 if (!target.getFluxTracker().isOverloadedOrVenting()) {
309 target.resetOverloadColor();
311 }
312 }
313 });
314
315 hitPhase = true;
316 }
317
318 float cooldown = MIN_HIT_ENEMY_COOLDOWN +
319 (MAX_HIT_ENEMY_COOLDOWN - MIN_HIT_ENEMY_COOLDOWN) * (float) Math.random();
320 if (hitPhase) {
322 }
323 if (cooldown > MAX_COOLDOWN) cooldown = MAX_COOLDOWN;
324// ship.getSystem().setCooldown(cooldown);
325// ship.getSystem().setCooldownRemaining(cooldown);
326 cooldownToSet = cooldown;
327 }
328
329// ship.getSystem().setCooldown(0.2f);
330// ship.getSystem().setCooldownRemaining(0.2f);
331 }
332
333 public void unapply(MutableShipStatsAPI stats, String id) {
334 }
335
336 public StatusData getStatusData(int index, State state, float effectLevel) {
337 return null;
338 }
339
340 @Override
341 public String getInfoText(ShipSystemAPI system, ShipAPI ship) {
342 if (system.isOutOfAmmo()) return null;
343 if (system.getState() != SystemState.IDLE) return null;
344
345 ShipAPI target = findTarget(ship);
346 if (target != null && target != ship) {
347 return "READY";
348 }
349 if ((target == null || target == ship) && ship.getShipTarget() != null) {
350 return "OUT OF RANGE";
351 }
352 return "NO TARGET";
353 }
354
355 public boolean isInRange(ShipAPI ship, ShipAPI target) {
356 float range = getRange(ship);
357 float dist = Misc.getDistance(ship.getLocation(), target.getLocation());
358 float radSum = ship.getCollisionRadius() + target.getCollisionRadius();
359 return dist <= range + radSum;
360 }
361
362 public boolean isValidLashTarget(ShipAPI ship, ShipAPI other) {
363 if (other == null) return false;
364 if (other.isHulk() || other.getOwner() == 100) return false;
365 if (other.isShuttlePod()) return false;
367 if (other.isFighter() && other.getOwner() == ship.getOwner()) {
369 }
370
371 if (other.isFighter()) return false;
372 if (other.getOwner() == ship.getOwner()) {
373 if (other.getSystem() == null) return false;
374 if (!(other.getSystem().getScript() instanceof EnergyLashActivatedSystem)) return false;
375 if (other.getSystem().getCooldownRemaining() > 0) return false;
376 if (other.getSystem().isActive()) return false;
377 if (other.getFluxTracker().isOverloadedOrVenting()) return false;
378 }
379 return true;
380 //return !other.isFighter();
381 }
382
383
384 protected ShipAPI findTarget(ShipAPI ship) {
385 float range = getRange(ship);
386 boolean player = ship == Global.getCombatEngine().getPlayerShip();
387 ShipAPI target = ship.getShipTarget();
388
389 float extraRange = 0f;
390 if (ship.getShipAI() != null && ship.getAIFlags().hasFlag(AIFlags.CUSTOM1)){
391 target = (ShipAPI) ship.getAIFlags().getCustom(AIFlags.CUSTOM1);
392 extraRange += 500f;
393 }
394
395
396 if (target != null) {
397 float dist = Misc.getDistance(ship.getLocation(), target.getLocation());
398 float radSum = ship.getCollisionRadius() + target.getCollisionRadius();
399 if (dist > range + radSum + extraRange) target = null;
400 } else {
401 FindShipFilter filter = s -> isValidLashTarget(ship, s);
402
403 if (target == null || target.getOwner() == ship.getOwner()) {
404 if (player) {
405 target = Misc.findClosestShipTo(ship, ship.getMouseTarget(), HullSize.FIGHTER, range, true, false, filter);
406 } else {
407 Object test = ship.getAIFlags().getCustom(AIFlags.MANEUVER_TARGET);
408 if (test instanceof ShipAPI) {
409 target = (ShipAPI) test;
410 float dist = Misc.getDistance(ship.getLocation(), target.getLocation());
411 float radSum = ship.getCollisionRadius() + target.getCollisionRadius();
412 if (dist > range + radSum) target = null;
413 }
414 }
415 }
416 if (target == null) {
417 target = Misc.findClosestShipTo(ship, ship.getLocation(), HullSize.FIGHTER, range, true, false, filter);
418 }
419 }
420
421 return target;
422 }
423
424 @Override
425 public boolean isUsable(ShipSystemAPI system, ShipAPI ship) {
426 ShipAPI target = findTarget(ship);
427 return target != null && target != ship;
428 //return super.isUsable(system, ship);
429 }
430
431 public static float getRange(ShipAPI ship) {
432 if (ship == null) return MAX_LASH_RANGE;
434 }
435
436}
437
438
439
440
441
442
443
444
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:49
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
float computeEffective(float baseValue)
void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
StatusData getStatusData(int index, State state, float effectLevel)
static ShipAPI findClosestShipTo(ShipAPI ship, Vector2f locFromForSorting, HullSize smallestToNote, float maxRange, boolean considerShipRadius, boolean allowHulks, FindShipFilter filter)
Definition Misc.java:2526
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)
EmpArcEntityAPI spawnEmpArcPierceShields(ShipAPI damageSource, Vector2f point, CombatEntityAPI pointAnchor, CombatEntityAPI empTargetEntity, DamageType damageType, float damAmount, float empDamAmount, float maxRange, String impactSoundId, float thickness, Color fringe, Color core)
void removePlugin(EveryFrameCombatPlugin plugin)
void addPlugin(EveryFrameCombatPlugin plugin)
EmpArcEntityAPI spawnEmpArc(ShipAPI damageSource, Vector2f point, CombatEntityAPI pointAnchor, CombatEntityAPI empTargetEntity, DamageType damageType, float damAmount, float empDamAmount, float maxRange, String impactSoundId, float thickness, Color fringe, Color core)
void setTargetToShipCenter(Vector2f sourceSlotPos, ShipAPI ship)
void setCoreWidthOverride(float coreWidthOverride)
void beginOverloadWithTotalBaseDuration(float totalDur)
MutableShipStatsAPI getMutableStats()
void setJitter(Object source, Color color, float intensity, int copies, float range)
List< WeaponSlotAPI > getAllWeaponSlotsCopy()
void setCooldownRemaining(float remaining)
Vector2f computePosition(CombatEntityAPI ship)