Starsector API
Loading...
Searching...
No Matches
MoteAIScript.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
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.CollisionGridAPI;
11import com.fs.starfarer.api.combat.CombatEngineAPI;
12import com.fs.starfarer.api.combat.CombatEntityAPI;
13import com.fs.starfarer.api.combat.MissileAIPlugin;
14import com.fs.starfarer.api.combat.MissileAPI;
15import com.fs.starfarer.api.combat.ShipAPI;
16import com.fs.starfarer.api.combat.ShipCommand;
17import com.fs.starfarer.api.impl.combat.MoteControlScript.SharedMoteAIData;
18import com.fs.starfarer.api.util.FaderUtil;
19import com.fs.starfarer.api.util.IntervalUtil;
20import com.fs.starfarer.api.util.Misc;
21
22public class MoteAIScript implements MissileAIPlugin {
23
24 public static float MAX_FLOCK_RANGE = 500;
25 public static float MAX_HARD_AVOID_RANGE = 200;
26 public static float AVOID_RANGE = 50;
27 public static float COHESION_RANGE = 100;
28
29 public static float ATTRACTOR_LOCK_STOP_FLOCKING_ADD = 300f;
30
32
33 protected IntervalUtil tracker = new IntervalUtil(0.05f, 0.1f);
34
35 protected IntervalUtil updateListTracker = new IntervalUtil(0.05f, 0.1f);
36 protected List<MissileAPI> missileList = new ArrayList<MissileAPI>();
37 protected List<CombatEntityAPI> hardAvoidList = new ArrayList<CombatEntityAPI>();
38
39 protected float r;
40
42 protected SharedMoteAIData data;
43
45 this.missile = missile;
46 r = (float) Math.random();
47 elapsed = -(float) Math.random() * 0.5f;
48
50
52 }
53
54 public void updateHardAvoidList() {
55 hardAvoidList.clear();
56
58 Iterator<Object> iter = grid.getCheckIterator(missile.getLocation(), MAX_HARD_AVOID_RANGE * 2f, MAX_HARD_AVOID_RANGE * 2f);
59 while (iter.hasNext()) {
60 Object o = iter.next();
61 if (!(o instanceof ShipAPI)) continue;
62
63 ShipAPI ship = (ShipAPI) o;
64
65 if (ship.isFighter()) continue;
66 hardAvoidList.add(ship);
67 }
68
71 while (iter.hasNext()) {
72 Object o = iter.next();
73 if (!(o instanceof CombatEntityAPI)) continue;
74
75 CombatEntityAPI asteroid = (CombatEntityAPI) o;
76 hardAvoidList.add(asteroid);
77 }
78 }
79
80 public void doFlocking() {
81 if (missile.getSource() == null) return;
82
83 ShipAPI source = missile.getSource();
85
86 float avoidRange = AVOID_RANGE;
87 float cohesionRange = COHESION_RANGE;
88
89 float sourceRejoin = source.getCollisionRadius() + 200f;
90
91 float sourceRepel = source.getCollisionRadius() + 50f;
92 float sourceCohesion = source.getCollisionRadius() + 600f;
93
94 float sin = (float) Math.sin(data.elapsed * 1f);
95 float mult = 1f + sin * 0.25f;
96 avoidRange *= mult;
97
98 Vector2f total = new Vector2f();
99 Vector2f attractor = getAttractorLoc();
100
101 if (attractor != null) {
102 float dist = Misc.getDistance(missile.getLocation(), attractor);
104 float f = dist / 200f;
105 if (f > 1f) f = 1f;
106 dir.scale(f * 3f);
107 Vector2f.add(total, dir, total);
108
109 avoidRange *= 3f;
110 }
111
112 boolean hardAvoiding = false;
113 for (CombatEntityAPI other : hardAvoidList) {
114 float dist = Misc.getDistance(missile.getLocation(), other.getLocation());
115 float hardAvoidRange = other.getCollisionRadius() + avoidRange + 50f;
116 if (dist < hardAvoidRange) {
117 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(other.getLocation(), missile.getLocation()));
118 float f = 1f - dist / (hardAvoidRange);
119 dir.scale(f * 5f);
120 Vector2f.add(total, dir, total);
121 hardAvoiding = f > 0.5f;
122 }
123 }
124
125
126 //for (MissileAPI otherMissile : missileList) {
127 for (MissileAPI otherMissile : data.motes) {
128 if (otherMissile == missile) continue;
129
130 float dist = Misc.getDistance(missile.getLocation(), otherMissile.getLocation());
131
132
133 float w = otherMissile.getMaxHitpoints();
134 w = 1f;
135
136 float currCohesionRange = cohesionRange;
137
138 if (dist < avoidRange && otherMissile != missile && !hardAvoiding) {
140 float f = 1f - dist / avoidRange;
141 dir.scale(f * w);
142 Vector2f.add(total, dir, total);
143 }
144
145 if (dist < currCohesionRange) {
146 Vector2f dir = new Vector2f(otherMissile.getVelocity());
147 Misc.normalise(dir);
148 float f = 1f - dist / currCohesionRange;
149 dir.scale(f * w);
150 Vector2f.add(total, dir, total);
151 }
152
153// if (dist < cohesionRange && dist > avoidRange) {
154// //Vector2f dir = Utils.getUnitVectorAtDegreeAngle(Utils.getAngleInDegrees(missile.getLocation(), mote.getLocation()));
155// Vector2f dir = Utils.getUnitVectorAtDegreeAngle(Utils.getAngleInDegrees(mote.getLocation(), missile.getLocation()));
156// float f = dist / cohesionRange - 1f;
157// dir.scale(f * 0.5f);
158// Vector2f.add(total, dir, total);
159// }
160 }
161
162 if (missile.getSource() != null) {
163 float dist = Misc.getDistance(missile.getLocation(), source.getLocation());
164 if (dist > sourceRejoin) {
166 float f = dist / (sourceRejoin + 400f) - 1f;
167 dir.scale(f * 0.5f);
168
169 Vector2f.add(total, dir, total);
170 }
171
172 if (dist < sourceRepel) {
174 float f = 1f - dist / sourceRepel;
175 dir.scale(f * 5f);
176 Vector2f.add(total, dir, total);
177 }
178
179 if (dist < sourceCohesion && source.getVelocity().length() > 20f) {
180 Vector2f dir = new Vector2f(source.getVelocity());
181 Misc.normalise(dir);
182 float f = 1f - dist / sourceCohesion;
183 dir.scale(f * 1f);
184 Vector2f.add(total, dir, total);
185 }
186
187 // if not strongly going anywhere, circle the source ship; only kicks in for lone motes
188 if (total.length() <= 0.05f) {
189 float offset = r > 0.5f ? 90f : -90f;
190 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(
192 float f = 1f;
193 dir.scale(f * 1f);
194 Vector2f.add(total, dir, total);
195 }
196 }
197
198 if (total.length() > 0) {
199 float dir = Misc.getAngleInDegrees(total);
200 engine.headInDirectionWithoutTurning(missile, dir, 10000);
201
202 if (r > 0.5f) {
204 } else {
206 }
208 }
209 }
210
211 //public void accumulate(FlockingData data, Vector2f )
212
213
214 protected IntervalUtil flutterCheck = new IntervalUtil(2f, 4f);
215 protected FaderUtil currFlutter = null;
216 protected float flutterRemaining = 0f;
217
218 protected float elapsed = 0f;
219 public void advance(float amount) {
220 if (missile.isFizzling()) return;
221 if (missile.getSource() == null) return;
222
223 elapsed += amount;
224
226 if (updateListTracker.intervalElapsed()) {
228 }
229
230 //missile.getEngineController().getShipEngines().get(0).
231
232 if (flutterRemaining <= 0) {
233 flutterCheck.advance(amount);
234 if (flutterCheck.intervalElapsed() &&
235 ((float) Math.random() > 0.9f ||
236 (data.attractorLock != null && (float) Math.random() > 0.5f))) {
237 flutterRemaining = 2f + (float) Math.random() * 2f;
238 }
239 }
240
241// if (flutterRemaining > 0) {
242// flutterRemaining -= amount;
243// if (currFlutter == null) {
244// float min = 1/15f;
245// float max = 1/4f;
246// float dur = min + (max - min) * (float) Math.random();
247// //dur *= 0.5f;
248// currFlutter = new FaderUtil(0f, dur/2f, dur/2f, false, true);
249// currFlutter.fadeIn();
250// }
251// currFlutter.advance(amount);
252// if (currFlutter.isFadedOut()) {
253// currFlutter = null;
254// }
255// } else {
256// currFlutter = null;
257// }
258//
259// if (currFlutter != null) {
260// missile.setGlowRadius(currFlutter.getBrightness() * 30f);
261// } else {
262// missile.setGlowRadius(0f);
263// }
264// if (true) {
265// doFlocking();
266// return;
267// }
268
269
270 if (elapsed >= 0.5f) {
271
272 boolean wantToFlock = !isTargetValid();
273 if (data.attractorLock != null) {
274 float dist = Misc.getDistance(missile.getLocation(), data.attractorLock.getLocation());
275 if (dist > data.attractorLock.getCollisionRadius() + ATTRACTOR_LOCK_STOP_FLOCKING_ADD) {
276 wantToFlock = true;
277 }
278 }
279
280 if (wantToFlock) {
281 doFlocking();
282 } else {
284 Vector2f targetLoc = engine.getAimPointWithLeadForAutofire(missile, 1.5f, target, 50);
287 10000);
288 //AIUtils.turnTowardsPointV2(missile, targetLoc);
289 if (r > 0.5f) {
291 } else {
293 }
295 }
296 }
297
298 tracker.advance(amount);
299 if (tracker.intervalElapsed()) {
300 if (elapsed >= 0.5f) {
302 }
303 //causeEnemyMissilesToTargetThis();
304 }
305 }
306
307
308 @SuppressWarnings("unchecked")
309 protected boolean isTargetValid() {
310 if (target == null || (target instanceof ShipAPI && ((ShipAPI)target).isPhased())) {
311 return false;
312 }
314
315 if (target != null && target instanceof ShipAPI && ((ShipAPI)target).isHulk()) return false;
316
317 List list = null;
318 if (target instanceof ShipAPI) {
319 list = engine.getShips();
320 } else {
321 list = engine.getMissiles();
322 }
323 return target != null && list.contains(target) && target.getOwner() != missile.getOwner();
324 }
325
326 protected void acquireNewTargetIfNeeded() {
327 if (data.attractorLock != null) {
328 target = data.attractorLock;
329 return;
330 }
331
333
334 // want to: target nearest missile that is not targeted by another two motes already
335 int owner = missile.getOwner();
336
337 int maxMotesPerMissile = 2;
338 float maxDistFromSourceShip = MoteControlScript.MAX_DIST_FROM_SOURCE_TO_ENGAGE_AS_PD;
340
341 float minDist = Float.MAX_VALUE;
342 CombatEntityAPI closest = null;
343 for (MissileAPI other : engine.getMissiles()) {
344 if (other.getOwner() == owner) continue;
345 if (other.getOwner() == 100) continue;
346 float distToTarget = Misc.getDistance(missile.getLocation(), other.getLocation());
347
348 if (distToTarget > minDist) continue;
349 if (distToTarget > 3000 && !engine.isAwareOf(owner, other)) continue;
350
351 float distFromAttractor = Float.MAX_VALUE;
352 if (data.attractorTarget != null) {
353 distFromAttractor = Misc.getDistance(other.getLocation(), data.attractorTarget);
354 }
355 float distFromSource = Misc.getDistance(other.getLocation(), missile.getSource().getLocation());
356 if (distFromSource > maxDistFromSourceShip &&
357 distFromAttractor > maxDistFromAttractor) continue;
358
359 if (getNumMotesTargeting(other) >= maxMotesPerMissile) continue;
360 if (distToTarget < minDist) {
361 closest = other;
362 minDist = distToTarget;
363 }
364 }
365
366 for (ShipAPI other : engine.getShips()) {
367 if (other.getOwner() == owner) continue;
368 if (other.getOwner() == 100) continue;
369 if (!other.isFighter()) continue;
370 float distToTarget = Misc.getDistance(missile.getLocation(), other.getLocation());
371 if (distToTarget > minDist) continue;
372 if (distToTarget > 3000 && !engine.isAwareOf(owner, other)) continue;
373
374 float distFromAttractor = Float.MAX_VALUE;
375 if (data.attractorTarget != null) {
376 distFromAttractor = Misc.getDistance(other.getLocation(), data.attractorTarget);
377 }
378 float distFromSource = Misc.getDistance(other.getLocation(), missile.getSource().getLocation());
379 if (distFromSource > maxDistFromSourceShip &&
380 distFromAttractor > maxDistFromAttractor) continue;
381
382 if (getNumMotesTargeting(other) >= maxMotesPerMissile) continue;
383 if (distToTarget < minDist) {
384 closest = other;
385 minDist = distToTarget;
386 }
387 }
388
389 target = closest;
390 }
391
393 int count = 0;
394 for (MissileAPI mote : data.motes) {
395 if (mote == missile) continue;
396 if (mote.getUnwrappedMissileAI() instanceof MoteAIScript) {
398 if (ai.getTarget() == other) {
399 count++;
400 }
401 }
402 }
403 return count;
404 }
405
406 public Vector2f getAttractorLoc() {
407 Vector2f attractor = null;
408 if (data.attractorTarget != null) {
409 attractor = data.attractorTarget;
410 if (data.attractorLock != null) {
411 attractor = data.attractorLock.getLocation();
412 }
413 }
414 return attractor;
415 }
416
418 return target;
419 }
420
422 this.target = target;
423 }
424 public void render() {
425
426 }
427}
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
int getNumMotesTargeting(CombatEntityAPI other)
static SharedMoteAIData getSharedData(ShipAPI source)
static Vector2f getUnitVectorAtDegreeAngle(float degrees)
Definition Misc.java:1196
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
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)
Vector2f getAimPointWithLeadForAutofire(CombatEntityAPI from, float accuracyFactor, CombatEntityAPI to, float projSpeed)
void headInDirectionWithoutTurning(MissileAPI missile, float desiredHeading, float desiredSpeed)
void giveCommand(ShipCommand command)
ShipEngineControllerAPI getEngineController()