Starsector API
Loading...
Searching...
No Matches
DroneStrikeStats.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.List;
7
8import java.awt.Color;
9
10import org.lwjgl.util.vector.Vector2f;
11
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin;
14import com.fs.starfarer.api.combat.CollisionClass;
15import com.fs.starfarer.api.combat.CombatEngineAPI;
16import com.fs.starfarer.api.combat.DamageType;
17import com.fs.starfarer.api.combat.EmpArcEntityAPI;
18import com.fs.starfarer.api.combat.FighterLaunchBayAPI;
19import com.fs.starfarer.api.combat.GuidedMissileAI;
20import com.fs.starfarer.api.combat.MissileAPI;
21import com.fs.starfarer.api.combat.MutableShipStatsAPI;
22import com.fs.starfarer.api.combat.ShipAPI;
23import com.fs.starfarer.api.combat.ShipAPI.HullSize;
24import com.fs.starfarer.api.combat.ShipSystemAPI;
25import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState;
26import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags;
27import com.fs.starfarer.api.combat.WeaponAPI;
28import com.fs.starfarer.api.util.Misc;
29
31
32 public static class DroneMissileScript extends BaseCombatLayeredRenderingPlugin {
33 protected ShipAPI drone;
34 protected MissileAPI missile;
35 protected boolean done;
36
37 public DroneMissileScript(ShipAPI drone, MissileAPI missile) {
38 super();
39 this.drone = drone;
40 this.missile = missile;
41 missile.setNoFlameoutOnFizzling(true);
42 //missile.setFlightTime(missile.getMaxFlightTime() - 1f);
43 }
44
45 @Override
46 public void advance(float amount) {
47 super.advance(amount);
48
49 if (done) return;
50
52
53 missile.setEccmChanceOverride(1f);
54 missile.setOwner(drone.getOriginalOwner());
55
56 drone.getLocation().set(missile.getLocation());
57 drone.getVelocity().set(missile.getVelocity());
59 drone.setFacing(missile.getFacing());
60 drone.getEngineController().fadeToOtherColor(this, new Color(0,0,0,0), new Color(0,0,0,0), 1f, 1f);
61
62
63 float dist = Misc.getDistance(missile.getLocation(), missile.getStart());
64 float jitterFraction = dist / missile.getMaxRange();
65 jitterFraction = Math.max(jitterFraction, missile.getFlightTime() / missile.getMaxFlightTime());
66
67 missile.setSpriteAlphaOverride(0f);
68 float jitterMax = 1f + 10f * jitterFraction;
69 drone.setJitter(this, new Color(255,100,50, (int)(25 + 50 * jitterFraction)), 1f, 10, 1f, jitterMax);
70
71
72// if (true && !done && missile.getFlightTime() > 1f) {
73// Vector2f damageFrom = new Vector2f(drone.getLocation());
74// damageFrom = Misc.getPointWithinRadius(damageFrom, 20);
75// engine.applyDamage(drone, damageFrom, 1000000f, DamageType.ENERGY, 0, true, false, drone, false);
76// }
77
78 boolean droneDestroyed = drone.isHulk() || drone.getHitpoints() <= 0;
79 if (missile.isFizzling() || (missile.getHitpoints() <= 0 && !missile.didDamage()) || droneDestroyed) {
80 drone.getVelocity().set(0, 0);
81 missile.getVelocity().set(0, 0);
82
83 if (!droneDestroyed) {
84 Vector2f damageFrom = new Vector2f(drone.getLocation());
85 damageFrom = Misc.getPointWithinRadius(damageFrom, 20);
86 engine.applyDamage(drone, damageFrom, 1000000f, DamageType.ENERGY, 0, true, false, drone, false);
87 }
88 missile.interruptContrail();
89 engine.removeEntity(drone);
90 engine.removeEntity(missile);
91
92 missile.explode();
93
94 done = true;
95 return;
96 }
97 if (missile.didDamage()) {
98 drone.getVelocity().set(0, 0);
99 missile.getVelocity().set(0, 0);
100
101 Vector2f damageFrom = new Vector2f(drone.getLocation());
102 damageFrom = Misc.getPointWithinRadius(damageFrom, 20);
103 engine.applyDamage(drone, damageFrom, 1000000f, DamageType.ENERGY, 0, true, false, drone, false);
104 missile.interruptContrail();
105 engine.removeEntity(drone);
106 engine.removeEntity(missile);
107 done = true;
108 return;
109 }
110
111 }
112
113 @Override
114 public boolean isExpired() {
115 return done;
116 }
117
118
119 }
120
121
122
123 protected String getWeaponId() {
124 return "terminator_missile";
125 }
126 protected int getNumToFire() {
127 return 1;
128 }
129
130 protected WeaponAPI weapon;
131 protected boolean fired = false;
132
133 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
134 ShipAPI ship = null;
135 //boolean player = false;
136 if (stats.getEntity() instanceof ShipAPI) {
137 ship = (ShipAPI) stats.getEntity();
138 //player = ship == Global.getCombatEngine().getPlayerShip();
139 } else {
140 return;
141 }
142
143 if (weapon == null) {
145 }
146
147 for (ShipAPI drone : getDrones(ship)) {
148 drone.setExplosionScale(0.67f);
149 drone.setExplosionVelocityOverride(new Vector2f());
150 drone.setExplosionFlashColorOverride(new Color(255, 100, 50, 255));
151 }
152
153 if (effectLevel > 0 && !fired) {
154 if (!getDrones(ship).isEmpty()) {
155 ShipAPI target = findTarget(ship);
156 convertDrones(ship, target);
157 }
158 } else if (state == State.IDLE){
159 fired = false;
160 }
161 }
162
163 public void convertDrones(ShipAPI ship, final ShipAPI target) {
165 fired = true;
166 forceNextTarget = null;
167 int num = 0;
168
169 List<ShipAPI> drones = getDrones(ship);
170 if (target != null) {
171 Collections.sort(drones, new Comparator<ShipAPI>() {
172 public int compare(ShipAPI o1, ShipAPI o2) {
173 float d1 = Misc.getDistance(o1.getLocation(), target.getLocation());
174 float d2 = Misc.getDistance(o2.getLocation(), target.getLocation());
175 return (int)Math.signum(d1 - d2);
176 }
177 });
178 } else {
179 Collections.shuffle(drones);
180 }
181
182 for (ShipAPI drone : drones) {
183 if (num < getNumToFire()) {
184 MissileAPI missile = (MissileAPI) engine.spawnProjectile(
185 ship, weapon, getWeaponId(),
186 new Vector2f(drone.getLocation()), drone.getFacing(), new Vector2f(drone.getVelocity()));
187 if (target != null && missile.getAI() instanceof GuidedMissileAI) {
188 GuidedMissileAI ai = (GuidedMissileAI) missile.getAI();
189 ai.setTarget(target);
190 }
191 //missile.setHitpoints(missile.getHitpoints() * drone.getHullLevel());
192 missile.setEmpResistance(10000);
193
194 float base = missile.getMaxRange();
195 float max = getMaxRange(ship);
196 missile.setMaxRange(max);
197 missile.setMaxFlightTime(missile.getMaxFlightTime() * max/base);
198
199 drone.getWing().removeMember(drone);
200 drone.setWing(null);
201 drone.setExplosionFlashColorOverride(new Color(255, 100, 50, 255));
202 engine.addLayeredRenderingPlugin(new DroneMissileScript(drone, missile));
203
204// engine.removeEntity(drone);
205// drone.getVelocity().set(0, 0);
206// drone.setHulk(true);
207// drone.setHitpoints(-1f);
208
209 //float thickness = 16f;
210// EmpArcParams params = new EmpArcParams();
211// params.segmentLengthMult = 4f;
212// //params.glowSizeMult = 0.5f;
213// params.brightSpotFadeFraction = 0.33f;
214// params.brightSpotFullFraction = 1f;
216// params.flickerRateMult = 0.7f;
217
218
219 float thickness = 26f;
220 float coreWidthMult = 0.67f;
221 EmpArcEntityAPI arc = engine.spawnEmpArcVisual(ship.getLocation(), ship,
222 missile.getLocation(), missile, thickness, new Color(255,100,100,255), Color.white, null);
223 arc.setCoreWidthOverride(thickness * coreWidthMult);
225 } else {
226 if (drone.getShipAI() != null) {
227 drone.getShipAI().cancelCurrentManeuver();
228 }
229 }
230 num++;
231 }
232 }
233
234
235 public void unapply(MutableShipStatsAPI stats, String id) {
236 // never called
237 }
238
239 protected ShipAPI forceNextTarget = null;
240 protected ShipAPI findTarget(ShipAPI ship) {
241 if (getDrones(ship).isEmpty()) {
242 return null;
243 }
244
245 if (forceNextTarget != null && forceNextTarget.isAlive()) {
246 return forceNextTarget;
247 }
248
249 float range = getMaxRange(ship);
250 boolean player = ship == Global.getCombatEngine().getPlayerShip();
251 ShipAPI target = ship.getShipTarget();
252
253 // If not the player:
254 // The AI sets forceNextTarget, so if we're here, that target got destroyed in the last frame
255 // or it's using a different AI
256 // so, find *something* as a failsafe
257
258 if (!player) {
259 Object test = ship.getAIFlags().getCustom(AIFlags.MANEUVER_TARGET);
260 if (test instanceof ShipAPI) {
261 target = (ShipAPI) test;
262 float dist = Misc.getDistance(ship.getLocation(), target.getLocation());
263 float radSum = ship.getCollisionRadius() + target.getCollisionRadius();
264 if (dist > range + radSum) target = null;
265 }
266 if (target == null) {
267 target = Misc.findClosestShipEnemyOf(ship, ship.getMouseTarget(), HullSize.FRIGATE, range, true);
268 }
269 return target;
270 }
271
272 // Player ship
273
274 if (target != null) return target; // was set with R, so, respect that
275
276 // otherwise, find the nearest thing to the mouse cursor, regardless of if it's in range
277
278 target = Misc.findClosestShipEnemyOf(ship, ship.getMouseTarget(), HullSize.FIGHTER, Float.MAX_VALUE, true);
279 if (target != null && target.isFighter()) {
280 ShipAPI nearbyShip = Misc.findClosestShipEnemyOf(ship, target.getLocation(), HullSize.FRIGATE, 100, false);
281 if (nearbyShip != null) target = nearbyShip;
282 }
283 if (target == null) {
284 target = Misc.findClosestShipEnemyOf(ship, ship.getLocation(), HullSize.FIGHTER, range, true);
285 }
286
287 return target;
288 }
289
290
291 public StatusData getStatusData(int index, State state, float effectLevel) {
292 return null;
293 }
294
295 public List<ShipAPI> getDrones(ShipAPI ship) {
296 List<ShipAPI> result = new ArrayList<ShipAPI>();
297 for (FighterLaunchBayAPI bay : ship.getLaunchBaysCopy()) {
298 if (bay.getWing() == null) continue;
299 for (ShipAPI drone : bay.getWing().getWingMembers()) {
300 result.add(drone);
301 }
302 }
303 return result;
304 }
305
306 @Override
307 public String getInfoText(ShipSystemAPI system, ShipAPI ship) {
308 if (system.isOutOfAmmo()) return null;
309 if (system.getState() != SystemState.IDLE) return null;
310
311 if (getDrones(ship).isEmpty()) {
312 return "NO DRONES";
313 }
314
315 float range = getMaxRange(ship);
316
317 ShipAPI target = findTarget(ship);
318 if (target == null) {
319 if (ship.getMouseTarget() != null) {
320 float dist = Misc.getDistance(ship.getLocation(), ship.getMouseTarget());
321 float radSum = ship.getCollisionRadius();
322 if (dist + radSum > range) {
323 return "OUT OF RANGE";
324 }
325 }
326 return "NO TARGET";
327 }
328
329 float dist = Misc.getDistance(ship.getLocation(), target.getLocation());
330 float radSum = ship.getCollisionRadius() + target.getCollisionRadius();
331 if (dist > range + radSum) {
332 return "OUT OF RANGE";
333 }
334
335 return "READY";
336 }
337
338
339 @Override
340 public boolean isUsable(ShipSystemAPI system, ShipAPI ship) {
341 if (ship != null && ship.getSystem() != null && ship.getSystem().getState() != SystemState.IDLE) {
342 return true; // preventing out-of-ammo click when launching last drone
343 }
344 return !getDrones(ship).isEmpty();
345// if (true) return true;
346// ShipAPI target = findTarget(ship);
347// return target != null && target != ship;
348 }
349
350 public float getMaxRange(ShipAPI ship) {
351 if (weapon == null) {
353 }
354 //return weapon.getRange();
356 }
357 public boolean dronesUsefulAsPD() {
358 return true;
359 }
361 return false;
362 }
363 public int getMaxDrones() {
364 return 2;
365 }
366 public float getMissileSpeed() {
367 return weapon.getProjectileSpeed();
368 }
370 this.forceNextTarget = forceNextTarget;
371 }
373 return forceNextTarget;
374 }
375}
376
377
378
379
380
381
382
383
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
float computeEffective(float baseValue)
boolean isUsable(ShipSystemAPI system, ShipAPI ship)
void unapply(MutableShipStatsAPI stats, String id)
String getInfoText(ShipSystemAPI system, ShipAPI ship)
void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
void convertDrones(ShipAPI ship, final ShipAPI target)
StatusData getStatusData(int index, State state, float effectLevel)
static ShipAPI findClosestShipEnemyOf(ShipAPI ship, Vector2f locFromForSorting, HullSize smallestToNote, float maxRange, boolean considerShipRadius)
Definition Misc.java:2497
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static Vector2f getPointWithinRadius(Vector2f from, float r)
Definition Misc.java:711
EmpArcEntityAPI spawnEmpArcVisual(Vector2f from, CombatEntityAPI fromAnchor, Vector2f to, CombatEntityAPI toAnchor, float thickness, Color fringe, Color core)
CombatEntityAPI addLayeredRenderingPlugin(CombatLayeredRenderingPlugin plugin)
void applyDamage(CombatEntityAPI entity, Vector2f point, float damageAmount, DamageType damageType, float empAmount, boolean bypassShields, boolean dealsSoftFlux, Object source, boolean playSound)
void removeEntity(CombatEntityAPI entity)
CombatEntityAPI spawnProjectile(ShipAPI ship, WeaponAPI weapon, String weaponId, Vector2f point, float angle, Vector2f shipVelocity)
WeaponAPI createFakeWeapon(ShipAPI ship, String weaponId)
void setCollisionClass(CollisionClass collisionClass)
void setCoreWidthOverride(float coreWidthOverride)
void setTarget(CombatEntityAPI target)
void setNoFlameoutOnFizzling(boolean noFlameoutOnFizzling)
void setSpriteAlphaOverride(float spriteAlphaOverride)
void setEccmChanceOverride(float eccmChanceOverride)
void setEmpResistance(int empResistance)
DamagingProjectileAPI explode()
void setMaxFlightTime(float maxFlightTime)
MutableShipStatsAPI getMutableStats()
List< FighterLaunchBayAPI > getLaunchBaysCopy()
void setJitter(Object source, Color color, float intensity, int copies, float range)
ShipEngineControllerAPI getEngineController()
void fadeToOtherColor(Object key, Color other, Color contrailColor, float effectLevel, float maxBlend)