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