Starsector API
Loading...
Searching...
No Matches
ShroudedVortexAI.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat.dweller;
2
3import java.util.ArrayList;
4import java.util.Iterator;
5import java.util.List;
6
7import org.lwjgl.util.vector.Vector2f;
8
9import com.fs.starfarer.api.Global;
10import com.fs.starfarer.api.combat.CollisionClass;
11import com.fs.starfarer.api.combat.CombatEngineAPI;
12import com.fs.starfarer.api.combat.DamageType;
13import com.fs.starfarer.api.combat.ShipAIConfig;
14import com.fs.starfarer.api.combat.ShipAIPlugin;
15import com.fs.starfarer.api.combat.ShipAPI;
16import com.fs.starfarer.api.combat.ShipCommand;
17import com.fs.starfarer.api.combat.ShipwideAIFlags;
18import com.fs.starfarer.api.combat.WeaponGroupAPI;
19import com.fs.starfarer.api.util.IntervalUtil;
20import com.fs.starfarer.api.util.Misc;
21import com.fs.starfarer.api.util.WeightedRandomPicker;
22
23public class ShroudedVortexAI implements ShipAIPlugin {
24
25 public static class FlockingData {
26 public Vector2f loc;
27 public Vector2f vel;
28 public float minA;
29 public float maxA;
30 public float minR;
31 public float maxR;
32 public float repelAtAngleDist;
33 public float minC;
34 public float maxC;
35 public float attractWeight;
36 public float repelWeight;
37 public float cohesionWeight;
38 public float facing;
39 }
40
41
45 public static float HULL_FRACTION_LOST_PER_SECOND = 0.05f;
46
47 public static String VORTEX_FLOCKING = "vortex_flocking";
48 public static float ATTRACTOR_RANGE_MAX = 2000f;
49 public static float COHESION_RANGE_MIN = 150f;
50 public static float COHESION_RANGE_MAX = 300f;
51 public static float REPEL_RANGE_MIN = 0f;
52 public static float REPEL_RANGE_MAX = 400f;
53
55 protected ShipAPI ship;
56 protected boolean exploded = false;
57 protected Vector2f prevVel = null;
58
59 protected IntervalUtil updateInterval = new IntervalUtil(0.5f, 1.5f);
60 protected IntervalUtil headingInterval = new IntervalUtil(0.5f, 1.5f);
61
62 protected ShipAPI target = null;
63 protected float timeOnTarget = 0f;
64 protected float numCollisions = 0f;
65
66 protected List<FlockingData> flockingData = new ArrayList<>();
67 protected float desiredHeading = 0f;
68
70 this.ship = ship;
71
73
75 }
76
77 protected void doInitialSetup() {
79 }
80
81 protected void toggleOn(int groupNum) {
82 List<WeaponGroupAPI> groups = ship.getWeaponGroupsCopy();
83 if (groups.size() <= groupNum) return;
84 groups.get(groupNum).toggleOn();
85 }
86 protected void toggleOff(int groupNum) {
87 List<WeaponGroupAPI> groups = ship.getWeaponGroupsCopy();
88 if (groups.size() <= groupNum) return;
89 groups.get(groupNum).toggleOff();
90 }
91
92
93 @Override
94 public void advance(float amount) {
95 //if (true) return;
96
97 if (target != null) {
98 timeOnTarget += amount;
99 }
100
101 updateInterval.advance(amount);
102 if (updateInterval.intervalElapsed()) {
103 ShipAPI prev = target;
104 target = findTarget();
105 if (prev != target) {
106 timeOnTarget = 0f;
107 }
108
110 }
111
112 headingInterval.advance(amount * 5f);
113 if (headingInterval.intervalElapsed()) {
115 }
116
117// String id = getClass().getSimpleName();
118// if (timeOnTarget > 3f) {
119// ship.getMutableStats().getAcceleration().modifyMult(id, 0.24f);
120// ship.getMutableStats().getDeceleration().modifyMult(id, 0.24f);
121// } else {
122// ship.getMutableStats().getAcceleration().unmodifyMult(id);
123// ship.getMutableStats().getDeceleration().unmodifyMult(id);
124// }
125
126 if (prevVel != null) {
127 float delta = Vector2f.sub(prevVel, ship.getVelocity(), new Vector2f()).length();
128 // likely collision, stop the ship to make it feel heavier
129 if (delta > ship.getMaxSpeedWithoutBoost() * 0.25f) {
130 ship.getVelocity().scale(0.1f);
132 }
133 }
134 prevVel = new Vector2f(ship.getVelocity());
135
136
138
140 if (shroud != null) {
141 ShipAPI sourceShip = (ShipAPI) shroud.custom1;
142 if (sourceShip != null) {
143 float dist = Misc.getDistance(ship.getLocation(), sourceShip.getLocation());
144 if (dist > (ship.getCollisionRadius() + sourceShip.getCollisionRadius() * 0.75f)) {
146 } else {
148 }
149 }
150 }
151
152
153 float damage = ship.getMaxHitpoints() * HULL_FRACTION_LOST_PER_SECOND * (1f + numCollisions) * amount;
155 if (ship.getHitpoints() <= 0f) {
156 // like other damage, this will trigger the explosion visuals/damage in ShroudedVortexShipCreator
157 engine.applyDamage(ship, ship.getLocation(), 10000f, DamageType.ENERGY, 0f, true, false, ship, false);
158 }
159
161 }
162
163 protected void giveMovementCommands() {
165
166 //ship.giveCommand(ShipCommand.DECELERATE, null, 0);
169 } else {
171 }
172
173 float heading = Misc.getAngleInDegrees(ship.getVelocity());
174 if (target != null) {
175 float speed = (ship.getVelocity().length() + ship.getMaxSpeed()) * 0.5f;
176 Vector2f point = engine.getAimPointWithLeadForAutofire(ship, 1f, target, speed);
177 heading = Misc.getAngleInDegrees(ship.getLocation(), point);
178 }
179
180 //engine.headInDirectionWithoutTurning(ship, heading, 10000);
181
183 }
184
185
186 @Override
188 return flags;
189 }
190
191
193 float range = 5000f;
194 float goodRange = ship.getHullLevel() / HULL_FRACTION_LOST_PER_SECOND * ship.getMaxSpeed() * 0.75f;
195 Vector2f from = ship.getLocation();
196
198 Iterator<Object> iter = engine.getShipGrid().getCheckIterator(from,
199 range * 2f, range * 2f);
200 int owner = ship.getOwner();
201// ShipAPI best = null;
202// float maxScore = -100000f;
203
204 float currAngle = Misc.getAngleInDegrees(ship.getVelocity());
205
208
209 while (iter.hasNext()) {
210 Object o = iter.next();
211 if (!(o instanceof ShipAPI)) continue;
212
213 ShipAPI other = (ShipAPI) o;
214 if (other == ship) continue;
215 if (other.getOwner() == owner) continue;
216 if (other.isHulk()) continue;
217 if (other.isPhased()) continue;
218 if (!engine.isAwareOf(owner, other))
219
220 if (other.getCollisionClass() == CollisionClass.NONE) continue;
221
222
223 float dist = Misc.getDistance(from, other.getLocation());
224 if (dist > range) continue;
225
226 float score = 1f;
227 if (other.isFrigate()) {
228 score = 11f;
229 } else if (other.isDestroyer()) {
230 score = 15f;
231 } else if (other.isCruiser() || other.isCapital()) {
232 score = 25f;
233 }
234
235
236 float angleToOther = Misc.getAngleInDegrees(ship.getLocation(), other.getLocation());
237 float angleDiff = Misc.getAngleDiff(currAngle, angleToOther);
238
239 float f = angleDiff / 90f;
240 if (f > 1f) f = 1f;
241
242 float minus = 5f * dist / 5000f;
243 if (minus > 5f) minus = 3f;
244
245 score -= minus;
246
247 score -= f * 5f;
248
249 if (dist > goodRange) {
250 lessGood.add(other, score);
251 } else {
252 good.add(other, score);
253 }
254// if (dist > goodRange) {
255// score -= 100f;
256// }
257//
258// if (score > maxScore) {
259// maxScore = score;
260// best = other;
261// }
262 }
263
264 if (target != null) {
265 if (good.getItems().contains(target)) return target;
266 if (lessGood.getItems().contains(target) && good.isEmpty()) return target;
267 }
268
269 if (!good.isEmpty()) {
270 return good.pick();
271 }
272
273 return lessGood.pick();
274 }
275
276
277
278
279 protected void updateFlockingData() {
280 flockingData.clear();
281
283
284 int owner = ship.getOriginalOwner();
285 Vector2f loc = ship.getLocation();
286 float radius = ship.getCollisionRadius() * 1f;
287
288 if (target != null) {
290 FlockingData data = new FlockingData();
291 data.facing = target.getFacing();
292 data.loc = target.getLocation();
293 data.vel = target.getVelocity();
294 data.attractWeight = 100f;
295 if (dist - ship.getCollisionRadius() - target.getCollisionRadius() < 500f) {
296 data.attractWeight *= 10f;
297 }
298 data.repelWeight = 0f;
299 data.minA = 0f;
300 data.maxA = 1000000f;
301 data.minR = 0f;
302 data.maxR = 0f;
303 data.repelAtAngleDist = 0f;
304 flockingData.add(data);
305 }
306
307 for (ShipAPI curr : engine.getShips()) {
308 if (curr == ship) continue;
309 if (curr.getOwner() != owner) continue;
310 if (curr.isHulk() || curr.getOwner() == 100) continue;
311
312 float currRadius = curr.getCollisionRadius();
313
314 if (curr.hasTag(VORTEX_FLOCKING)) {
315 FlockingData data = new FlockingData();
316 data.facing = Misc.getAngleInDegrees(curr.getVelocity());
317 data.loc = curr.getLocation();
318 data.vel = curr.getVelocity();
319 data.repelWeight = 100f;
320 data.cohesionWeight = 1f;
321 data.attractWeight = 3f;
322
323 data.minA = 0f + radius + currRadius;
324 data.maxA = ATTRACTOR_RANGE_MAX + radius + currRadius;
325
326 data.minR = REPEL_RANGE_MIN + radius + currRadius;
327 data.maxR = REPEL_RANGE_MAX + radius + currRadius;
328
329 data.minC = COHESION_RANGE_MIN + radius + currRadius;
330 data.maxC = COHESION_RANGE_MAX + radius + currRadius;
331
332 flockingData.add(data);
333 } else if (!curr.isFighter()) {
334 FlockingData data = new FlockingData();
335 data.facing = Misc.getAngleInDegrees(curr.getVelocity());
336 data.loc = curr.getLocation();
337 data.vel = curr.getVelocity();
338 data.attractWeight = 0f;
339 data.cohesionWeight = 0f;
340 data.repelWeight = 100f;
341
342 data.minA = 0f;
343 data.maxA = 0f;
344
345 data.minR = REPEL_RANGE_MIN * 0.5f + radius + currRadius;
346 data.maxR = REPEL_RANGE_MAX * 0.5f + radius + currRadius;
347
348 data.minC = 0f;
349 data.maxC = 0f;
350
351 flockingData.add(data);
352 }
353 }
354 }
355
356 protected void computeDesiredHeading() {
357
358 Vector2f loc = ship.getLocation();
359 Vector2f vel = ship.getVelocity();
360 float facing = ship.getFacing();
361
362 Vector2f total = new Vector2f();
363
364 for (FlockingData curr : flockingData) {
365 float dist = Misc.getDistance(curr.loc, loc);
366 if (curr.maxR > 0 && dist < curr.maxR) {
367 float repelWeight = curr.repelWeight;
368 if (dist > curr.minR && curr.maxR > curr.minR) {
369 repelWeight = (dist - curr.minR) / (curr.maxR - curr.minR);
370 if (repelWeight > 1f) repelWeight = 1f;
371 repelWeight = 1f - repelWeight;
372 repelWeight *= curr.repelWeight;
373 }
374
375 Vector2f dir = Misc.getUnitVector(curr.loc, loc);
376
377 float distIntoRepel = curr.maxR - dist;
378 float repelAdjustmentAngle = 0f;
379 if (distIntoRepel < curr.repelAtAngleDist && curr.repelAtAngleDist > 0) {
380 float repelMult = (1f - distIntoRepel / curr.repelAtAngleDist);
381 repelAdjustmentAngle = 90f * repelMult;
382 repelWeight *= (1f - repelMult);
383
384 float repelAngle = Misc.getAngleInDegrees(dir);
385 float turnDir = Misc.getClosestTurnDirection(dir, vel);
386 repelAdjustmentAngle *= turnDir;
387 dir = Misc.getUnitVectorAtDegreeAngle(repelAngle + repelAdjustmentAngle);
388 }
389
390 dir.scale(repelWeight);
391 Vector2f.add(total, dir, total);
392 }
393
394 if (curr.maxA > 0 && dist < curr.maxA) {
395 float attractWeight = curr.attractWeight;
396 if (dist > curr.minA && curr.maxA > curr.minA) {
397 attractWeight = (dist - curr.minA) / (curr.maxA - curr.minA);
398 if (attractWeight > 1f) attractWeight = 1f;
399 attractWeight = 1f - attractWeight;
400 attractWeight *= curr.attractWeight;
401 }
402
403 Vector2f dir = Misc.getUnitVector(loc, curr.loc);
404 dir.scale(attractWeight);
405 Vector2f.add(total, dir, total);
406 }
407
408 if (curr.maxC > 0 && dist < curr.maxC) {
409 float cohesionWeight = curr.cohesionWeight;
410 if (dist > curr.minC && curr.maxC > curr.minC) {
411 cohesionWeight = (dist - curr.minC) / (curr.maxC - curr.minC);
412 if (cohesionWeight > 1f) cohesionWeight = 1f;
413 cohesionWeight = 1f - cohesionWeight;
414 cohesionWeight *= curr.cohesionWeight;
415 }
416
417 Vector2f dir = new Vector2f(curr.vel);
418 Misc.normalise(dir);
419 dir.scale(cohesionWeight);
420 Vector2f.add(total, dir, total);
421 }
422 }
423
424 if (total.length() <= 0) {
426 } else {
428 }
429 }
430
431
432 public void setDoNotFireDelay(float amount) {}
434 public boolean needsRefit() { return false; }
435 public void cancelCurrentManeuver() {}
436 public ShipAIConfig getConfig() { return null; }
437}
438
439
440
441
442
443
444
445
446
447
448
449
450
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
static DwellerShroud getShroudFor(CombatEntityAPI entity)
static Vector2f getUnitVectorAtDegreeAngle(float degrees)
Definition Misc.java:1196
static float getAngleDiff(float from, float to)
Definition Misc.java:1716
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static Vector2f getUnitVector(Vector2f from, Vector2f to)
Definition Misc.java:1191
static float getClosestTurnDirection(float facing, float desired)
Definition Misc.java:2102
static float getAngleInDegrees(Vector2f v)
Definition Misc.java:1126
static Vector2f normalise(Vector2f v)
Definition Misc.java:1134
Iterator< Object > getCheckIterator(Vector2f loc, float checkWidth, float checkHeight)
boolean isAwareOf(int owner, CombatEntityAPI other)
void applyDamage(CombatEntityAPI entity, Vector2f point, float damageAmount, DamageType damageType, float empAmount, boolean bypassShields, boolean dealsSoftFlux, Object source, boolean playSound)
Vector2f getAimPointWithLeadForAutofire(CombatEntityAPI from, float accuracyFactor, CombatEntityAPI to, float projSpeed)
void headInDirectionWithoutTurning(MissileAPI missile, float desiredHeading, float desiredSpeed)
void setCollisionClass(CollisionClass collisionClass)
List< WeaponGroupAPI > getWeaponGroupsCopy()
void giveCommand(ShipCommand command, Object param, int groupNumber)