Starsector API
Loading...
Searching...
No Matches
ShardSpawner.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.hullmods;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.HashMap;
6import java.util.Iterator;
7import java.util.List;
8import java.util.Map;
9
10import org.lwjgl.util.vector.Vector2f;
11
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.campaign.FactionAPI;
14import com.fs.starfarer.api.characters.PersonAPI;
15import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
16import com.fs.starfarer.api.combat.BaseHullMod;
17import com.fs.starfarer.api.combat.CollisionClass;
18import com.fs.starfarer.api.combat.CombatEngineAPI;
19import com.fs.starfarer.api.combat.CombatFleetManagerAPI;
20import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
21import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
22import com.fs.starfarer.api.combat.MutableShipStatsAPI;
23import com.fs.starfarer.api.combat.ShieldAPI;
24import com.fs.starfarer.api.combat.ShieldAPI.ShieldType;
25import com.fs.starfarer.api.combat.ShipAPI;
26import com.fs.starfarer.api.combat.ShipAPI.HullSize;
27import com.fs.starfarer.api.combat.ShipCommand;
28import com.fs.starfarer.api.impl.campaign.ids.Factions;
29import com.fs.starfarer.api.impl.campaign.ids.Personalities;
30import com.fs.starfarer.api.impl.campaign.ids.Skills;
31import com.fs.starfarer.api.impl.campaign.ids.Tags;
32import com.fs.starfarer.api.impl.combat.RiftCascadeEffect;
33import com.fs.starfarer.api.impl.combat.RiftLanceEffect;
34import com.fs.starfarer.api.input.InputEventAPI;
35import com.fs.starfarer.api.loading.FighterWingSpecAPI;
36import com.fs.starfarer.api.util.IntervalUtil;
37import com.fs.starfarer.api.util.Misc;
38import com.fs.starfarer.api.util.WeightedRandomPicker;
39
40public class ShardSpawner extends BaseHullMod {
41
42 public static Color JITTER_COLOR = new Color(100,100,255,50);
43 public static String DATA_KEY = "core_shard_spawner_data_key";
44
45 public static float SPAWN_TIME = 4f;
46
47 public static enum ShardType {
48 GENERAL,
49 ANTI_ARMOR,
50 ANTI_SHIELD,
51 POINT_DEFENSE,
52 MISSILE,
53 }
54
55 public static class ShardTypeVariants {
56 public Map<ShardType, WeightedRandomPicker<String>> variants = new HashMap<ShardType, WeightedRandomPicker<String>>();
57 public ShardTypeVariants() {
58 }
59 public WeightedRandomPicker<String> get(ShardType type) {
60 WeightedRandomPicker<String> result = variants.get(type);
61 if (result == null) {
62 result = new WeightedRandomPicker<String>();
63 variants.put(type, result);
64 }
65 return result;
66 }
67 }
68
69 public static Map<HullSize, ShardTypeVariants> variantData = new HashMap<HullSize, ShardTypeVariants>();
70 static {
71 ShardTypeVariants fighters = new ShardTypeVariants();
72 variantData.put(HullSize.FIGHTER, fighters);
73 fighters.get(ShardType.GENERAL).add("aspect_attack_wing", 10f);
74 fighters.get(ShardType.GENERAL).add("aspect_missile_wing", 1f);
75
76 fighters.get(ShardType.MISSILE).add("aspect_missile_wing", 10f);
77
78 fighters.get(ShardType.ANTI_ARMOR).add("aspect_attack_wing", 10f);
79
80 fighters.get(ShardType.ANTI_SHIELD).add("aspect_shieldbreaker_wing", 10f);
81
82 fighters.get(ShardType.POINT_DEFENSE).add("aspect_shock_wing", 10f);
83
84
85 ShardTypeVariants small = new ShardTypeVariants();
86 variantData.put(HullSize.FRIGATE, small);
87
88 small.get(ShardType.GENERAL).add("shard_left_Attack", 10f);
89 small.get(ShardType.GENERAL).add("shard_left_Attack2", 10f);
90 small.get(ShardType.GENERAL).add("shard_right_Attack", 10f);
91 small.get(ShardType.GENERAL).add("aspect_attack_wing", 10f);
92 small.get(ShardType.GENERAL).add("aspect_missile_wing", 1f);
93
94 small.get(ShardType.ANTI_ARMOR).add("shard_left_Armorbreaker", 10f);
95
96 small.get(ShardType.ANTI_SHIELD).add("shard_left_Shieldbreaker", 10f);
97 small.get(ShardType.ANTI_SHIELD).add("shard_right_Shieldbreaker", 10f);
98 //small.get(ShardType.ANTI_SHIELD).add("aspect_shieldbreaker_wing", 10f);
99
100 small.get(ShardType.POINT_DEFENSE).add("shard_left_Defense", 10f);
101 small.get(ShardType.POINT_DEFENSE).add("shard_right_Shock", 10f);
102 //small.get(ShardType.POINT_DEFENSE).add("aspect_shock_wing", 10f);
103
104 small.get(ShardType.MISSILE).add("shard_left_Missile", 10f);
105 small.get(ShardType.MISSILE).add("shard_right_Missile", 10f);
106 //small.get(ShardType.MISSILE).add("aspect_missile_wing", 10f);
107
108
109 ShardTypeVariants medium = new ShardTypeVariants();
110 variantData.put(HullSize.DESTROYER, medium);
111
112 medium.get(ShardType.GENERAL).add("facet_Attack");
113 medium.get(ShardType.GENERAL).add("facet_Attack2");
114
115 medium.get(ShardType.ANTI_ARMOR).add("facet_Armorbreaker");
116
117 medium.get(ShardType.ANTI_SHIELD).add("facet_Shieldbreaker");
118
119 medium.get(ShardType.POINT_DEFENSE).add("facet_Defense");
120
121 medium.get(ShardType.MISSILE).add("facet_Missile");
122
123 ShardTypeVariants large = new ShardTypeVariants();
124 variantData.put(HullSize.CRUISER, large);
125
126 large.get(ShardType.GENERAL).add("tesseract_Attack");
127 large.get(ShardType.GENERAL).add("tesseract_Attack2");
128 large.get(ShardType.GENERAL).add("tesseract_Strike");
129 large.get(ShardType.GENERAL).add("tesseract_Disruptor");
130
131 large.get(ShardType.ANTI_ARMOR).add("tesseract_Disruptor");
132 large.get(ShardType.ANTI_ARMOR).add("tesseract_Strike");
133
134 large.get(ShardType.ANTI_SHIELD).add("tesseract_Shieldbreaker");
135
136 large.get(ShardType.POINT_DEFENSE).add("tesseract_Defense");
137
138 large.get(ShardType.MISSILE).add("tesseract_Strike");
139 }
140
141 public static class ShardSpawnerData {
142 boolean done = false;
143 float delay = 2f + (float) Math.random() * 1f;
144 }
145
146 public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) {
147 stats.getBreakProb().modifyMult(id, 0f);
148 }
149
150 @Override
151 public void advanceInCombat(ShipAPI ship, float amount) {
153 if (ship.getOriginalOwner() != 0) {
155 }
156
157 if (!ship.isHulk() || !engine.isEntityInPlay(ship)) return;
158
159 String key = DATA_KEY + "_" + ship.getId();
160 ShardSpawnerData data = (ShardSpawnerData) engine.getCustomData().get(key);
161 if (data == null) {
162 data = new ShardSpawnerData();
163 engine.getCustomData().put(key, data);
164 }
165
166 if (data.done) return;
167
168
169 ship.setHitpoints(ship.getMaxHitpoints());
170 ship.getMutableStats().getHullDamageTakenMult().modifyMult("ShardSpawnerInvuln", 0f);
171 data.delay -= amount;
172 if (data.delay > 0) return;
173
174 //ship.setCollisionClass(CollisionClass.NONE);
175 float dur = SPAWN_TIME;
176 float extraDur = 0f;
177
178
179 float splitWeight = 0f;
180
181 float probNothingAtAll = 0f;
182 //float forceFighterProb = 0f;
183 float cruiserProb = 0f;
184 float cruiserProbMult = 0f;
185 float maxCruisers = 0f;
186 float destroyerProb = 0f;
187 float destroyerProbMult = 0f;
188 float maxDestroyers = 0f;
189 float frigateProb = 0f;
190 float frigateProbMult = 0f;
191 float maxFrigates = 0f;
192
193 if (ship.isCapital()) {
194 splitWeight = 12f;
195
196 cruiserProb = 1f;
197 cruiserProbMult = 0.5f;
198 maxCruisers = 1f;
199 destroyerProb = 1f;
200 destroyerProbMult = 1f;
201 maxDestroyers = 2f;
202 frigateProb = 1f;
203 frigateProbMult = 1f;
204 maxFrigates = 3f;
205 } else if (ship.isCruiser()) {
206// splitWeight = 7f;
207//
208// destroyerProb = 1f;
209// destroyerProbMult = 0.5f;
210// maxDestroyers = 1f;
211// frigateProb = 1f;
212// frigateProbMult = 0.67f;
213// maxFrigates = 3f;
214
215 splitWeight = 7f;
216
217 destroyerProb = 1f;
218 destroyerProbMult = 0.5f;
219 maxDestroyers = 1f;
220 frigateProb = 1f;
221 frigateProbMult = 1f;
222 maxFrigates = 2f;
223 } else if (ship.isDestroyer()) {
224// splitWeight = 4f;
225//
226// frigateProb = 1f;
227// frigateProbMult = 0.75f;
228// maxFrigates = 3f;
229
230 splitWeight = 4f;
231
232 frigateProb = 1f;
233 frigateProbMult = 1f;
234 maxFrigates = 2f;
235 } else if (ship.isFrigate()) {
236 splitWeight = 2f;
237 }
239
240// splitWeight = 8f;
241// destroyerProb = 0.8f;
242// maxDestroyers = 2f;
243// //maxDestroyers = 0f;
244//
245// splitWeight = 1f;
246
247 if ((float) Math.random() < probNothingAtAll) {
248 splitWeight = 0f;
249 }
250
252 int spawnAnglesIter = 0;
253 float angleOffset = (float) Math.random() * 360f;
254
255 float addedWeight = 0f;
256 float fighters = 0f;
257 float frigates = 0f;
258 float destroyers = 0f;
259 float cruisers = 0f;
260 List<ShardFadeInPlugin> shards = new ArrayList<ShardFadeInPlugin>();
261 while (addedWeight < splitWeight) {
262 ShardType type = typePicker.pick();
263
264 float rem = splitWeight - addedWeight;
265 boolean cruiser = (float) Math.random() < cruiserProb && cruisers < maxCruisers && rem >= 3.5f;
266 boolean destroyer = (float) Math.random() < destroyerProb && destroyers < maxDestroyers && rem >= 1.5f;
267 boolean frigate = (float) Math.random() < frigateProb && frigates < maxFrigates;
268 //boolean fighter = (float) Math.random() < forceFighterProb && fighters < maxFromFightersOnlyCategory;
269 String variant = null;
270 float weight = 1f;
271
272 if (cruiser) {
273 ShardTypeVariants variants = variantData.get(HullSize.CRUISER);
274 WeightedRandomPicker<String> variantPicker = variants.get(type);
275 variant = variantPicker.pick();
276 if (variant != null) {
277 weight = 4f;
278 cruisers++;
279 cruiserProb *= cruiserProbMult;
280 }
281 }
282
283 if (destroyer && variant == null) {
284 ShardTypeVariants variants = variantData.get(HullSize.DESTROYER);
285 WeightedRandomPicker<String> variantPicker = variants.get(type);
286 variant = variantPicker.pick();
287 if (variant != null) {
288 weight = 2f;
289 destroyers++;
290 destroyerProb *= destroyerProbMult;
291 }
292 }
293
294 if (frigate && variant == null) {
295 ShardTypeVariants variants = variantData.get(HullSize.FRIGATE);
296 WeightedRandomPicker<String> variantPicker = variants.get(type);
297 variant = variantPicker.pick();
298 if (variant != null) {
299 weight = 1f;
300 frigates++;
301 frigateProb *= frigateProbMult;
302 }
303 }
304
305 if (variant == null) {
306 ShardTypeVariants variants = variantData.get(HullSize.FIGHTER);
307 WeightedRandomPicker<String> variantPicker = variants.get(type);
308 variant = variantPicker.pick();
309 if (variant != null) {
310 fighters++;
311 }
312 }
313
314 //variant = "aspect_shock_wing";
315 //variant = "aspect_shieldbreaker_wing";
316 //variant = "aspect_missile_wing";
317 if (variant != null) {
318 if (spawnAngles == null || spawnAngles.isEmpty()) {
319 spawnAngles = getSpawnAngles(spawnAnglesIter++);
320 }
321 float angle = spawnAngles.pickAndRemove() + angleOffset;
322 ShardFadeInPlugin shard = createShipFadeInPlugin(variant, ship, extraDur, dur, angle);
323 shards.add(shard);
325 addedWeight += weight;
326
327// float delay = 0.5f + (float) Math.random() * 0.5f;
328// extraDur += delay;
329 } else {
330 addedWeight += 0.1f; // if we didn't manage to add anything, eventually break out of the loop
331 }
332 }
333
334 Global.getCombatEngine().addPlugin(createShipFadeOutPlugin(ship, dur + extraDur * 0.5f, shards));
335
336// Global.getCombatEngine().addPlugin(createShipFadeInPlugin("shard_left_Attack", ship, 0f, dur));
337// Global.getCombatEngine().addPlugin(createShipFadeInPlugin("shard_right_Attack", ship, 0f, dur));
338 data.done = true;
339 }
340
343 float start = 0f;
344 float incr = 60f;
345 if (iter == 1) {
346 start = 30f;
347 } else if (iter == 2) {
348 start = 15f;
349 incr = 30f;
350 } else {
351 incr = 10f;
352 }
353 for (float i = start; i < 360f + start; i += incr) {
354 picker.add(i);
355 }
356 return picker;
357 }
358
359
360
363 float checkRadius = 5000;
364 Iterator<Object> iter = engine.getAiGridShips().getCheckIterator(ship.getLocation(), checkRadius * 2f, checkRadius * 2f);
365
366 float weightFighters = 0f;
367 float weightGoodShields = 0f;
368 float weightGoodArmor = 0f;
369 float weightVulnerable = 0f;
370 float weightCarriers = 0f;
371
372 float weightEnemies = 0f;
373 float weightFriends = 0f;
374
375 while (iter.hasNext()) {
376 Object o = iter.next();
377 if (o instanceof ShipAPI) {
378 ShipAPI other = (ShipAPI) o;
379 if (other.getOwner() == Misc.OWNER_NEUTRAL) continue;
380
381 boolean enemy = ship.getOwner() != other.getOwner();
382 if (enemy) {
383 if (other.isFighter() || other.isDrone()) {
384 weightFighters += 0.25f;
385 weightEnemies += 0.25f;
386 } else {
387 float w = Misc.getShipWeight(other);
388 weightEnemies += w;
389
390 if (hasGoodShields(other)) {
391 weightGoodShields += w;
392 }
393 if (hasGoodArmor(other)) {
394 weightGoodArmor += w;
395 }
396 if (isVulnerableToMissileBarrage(ship, other)) {
397 weightVulnerable += w;
398 }
399 if (other.getVariant().isCarrier()) {
400 weightCarriers += w;
401 }
402 }
403 } else {
404 if (other.isFighter() || other.isDrone()) {
405 weightFriends += 0.25f;
406 } else {
407 float w = Misc.getShipWeight(other);
408 weightFriends += w;
409 }
410 }
411 }
412 }
413
415
416 float total = weightFighters + weightGoodShields + weightGoodArmor + weightVulnerable + weightCarriers;
417 if (total <= 1f) total = 1f;
418
419 float antiFighter = (weightFighters + weightCarriers) / total;
420 float antiShield = weightGoodShields / total;
421 float antiArmor = weightGoodArmor / total;
422 float missile = weightVulnerable / total;
423
424 float friends = weightFriends / Math.max(1f, weightEnemies + weightFriends);
425
426 picker.add(ShardType.GENERAL, 0.0f + (1f - friends) * 0.4f);
427// picker.add(ShardType.GENERAL, 0.2f);
428// if (friends < 0.3f) {
429// picker.add(ShardType.GENERAL, Math.min(0.25f, (1f - friends) * 0.25f));
430// }
431
432 float unlikelyWeight = 0f;
433 float unlikelyThreshold = 0.2f;
434
435 if (antiFighter < unlikelyThreshold) antiFighter = unlikelyWeight;
436 picker.add(ShardType.POINT_DEFENSE, antiFighter);
437
438 if (antiShield < unlikelyThreshold) antiShield = unlikelyWeight;
439 picker.add(ShardType.ANTI_SHIELD, antiShield);
440
441 if (antiArmor < unlikelyThreshold) antiArmor = unlikelyWeight;
442 picker.add(ShardType.ANTI_ARMOR, antiArmor);
443
444 if (missile < unlikelyThreshold) missile = unlikelyWeight;
445 picker.add(ShardType.MISSILE, missile);
446
447 return picker;
448 }
449
450 public boolean isVulnerableToMissileBarrage(ShipAPI from, ShipAPI other) {
451 float incap = Misc.getIncapacitatedTime(other);
452
453 float dist = Misc.getDistance(from.getLocation(), other.getLocation());
454 if (dist > 2000) return false;
455
456 float assumedMissileSpeed = 500;
457 float eta = dist / assumedMissileSpeed;
458 eta += SPAWN_TIME;
459 eta += 2f;
460
461 return incap >= eta || (other.getFluxLevel() >= 0.95f && other.getFluxTracker().getTimeToVent() >= eta);
462 }
463
464 public boolean hasGoodArmor(ShipAPI other) {
465 float requiredArmor = 1240;
466
467 if (other.getArmorGrid().getArmorRating() < requiredArmor) return false;
468
469 float armor = other.getAverageArmorInSlice(other.getFacing(), 120f);
470 return armor >= requiredArmor * 0.8f;
471
472 }
473
474 public boolean hasGoodShields(ShipAPI other) {
475 ShieldAPI shield = other.getShield();
476 if (shield == null) return false;
477 if (shield.getType() == ShieldType.NONE) return false;
478 if (shield.getType() == ShieldType.PHASE) return false;
479
480 float requiredCapacity = 10000000f;
481 switch (other.getHullSize()) {
482 case CAPITAL_SHIP:
483 requiredCapacity = 25000;
484 if (shield.getType() == ShieldType.FRONT && shield.getArc() < 250) {
485 requiredCapacity = 1000000;
486 }
487 break;
488 case CRUISER:
489 requiredCapacity = 12500;
490 if (shield.getType() == ShieldType.FRONT && shield.getArc() < 250) {
491 requiredCapacity = 1000000;
492 }
493 break;
494 case DESTROYER:
495 requiredCapacity = 8000;
496 break;
497 case FRIGATE:
498 requiredCapacity = 4000;
499 break;
500 }
501
502 float e = other.getShield().getFluxPerPointOfDamage() *
504 float capacity = other.getMaxFlux();
505 capacity /= Math.max(0.1f, e);
506
507 return capacity >= requiredCapacity && e <= 1f;
508 }
509
510
511
512 protected EveryFrameCombatPlugin createShipFadeOutPlugin(final ShipAPI ship, final float fadeOutTime,
513 final List<ShardFadeInPlugin> shards) {
514 return new BaseEveryFrameCombatPlugin() {
515 float elapsed = 0f;
516 IntervalUtil interval = new IntervalUtil(0.075f, 0.125f);
517
518 protected void pushShipsAway(float amount) {
519 Vector2f com = new Vector2f();
520 float count = 0f;
521 for (ShardFadeInPlugin shard : shards) {
522 ShipAPI ship = shard.ships[0];
523 if (ship.isFighter()) continue;
524 Vector2f.add(com, ship.getLocation(), com);
525 count++;
526 }
527 com.scale(1f / Math.max(1f, count));
528
529 Vector2f comForFighters = new Vector2f();
530 count = 0f;
531 for (ShardFadeInPlugin shard : shards) {
532 ShipAPI ship = shard.ships[0];
533 if (!ship.isFighter()) continue;
534 Vector2f.add(comForFighters, ship.getLocation(), comForFighters);
535 count++;
536 }
537 comForFighters.scale(1f / Math.max(1f, count));
538
539 float progress = elapsed / fadeOutTime;
540 if (progress > 1f) progress = 1f;
541 for (ShardFadeInPlugin shard : shards) {
542 ShipAPI ship = shard.ships[0];
543 Vector2f currCom = com;
544 if (ship.isFighter()) currCom = comForFighters;
545
546 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(currCom, ship.getLocation()));
547 float speed = ship.getCollisionRadius() * 0.5f;
548 dir.scale(amount * speed * progress);
549 Vector2f.add(ship.getLocation(), dir, ship.getLocation());
550 }
551 }
552
553 @Override
554 public void advance(float amount, List<InputEventAPI> events) {
555 if (Global.getCombatEngine().isPaused()) return;
556
557 elapsed += amount;
558
559
560 float progress = elapsed / fadeOutTime;
561 if (progress > 1f) progress = 1f;
562 ship.setAlphaMult(1f - progress);
563
564 //if (progress < 0.5f) {
565 pushShipsAway(amount);
566 //}
567
568 if (progress > 0.5f) {
570 }
571
572 float jitterLevel = progress;
573 if (jitterLevel < 0.5f) {
574 jitterLevel *= 2f;
575 } else {
576 jitterLevel = (1f - jitterLevel) * 2f;
577 }
578
579 float jitterRange = progress;
580 //jitterRange = (float) Math.sqrt(jitterRange);
581 float maxRangeBonus = 100f;
582 float jitterRangeBonus = jitterRange * maxRangeBonus;
583 Color c = JITTER_COLOR;
584 int alpha = c.getAlpha();
585 alpha += 100f * progress;
586 if (alpha > 255) alpha = 255;
587 c = Misc.setAlpha(c, alpha);
588
589 ship.setJitter(this, c, jitterLevel, 35, 0f, jitterRangeBonus);
590
591 interval.advance(amount);
592 if (interval.intervalElapsed() && elapsed < fadeOutTime * 0.75f) {
595 float baseDuration = 2f;
596 Vector2f vel = new Vector2f(ship.getVelocity());
597 float size = ship.getCollisionRadius() * 0.35f;
598 for (int i = 0; i < 3; i++) {
599 Vector2f point = new Vector2f(ship.getLocation());
600 point = Misc.getPointWithinRadiusUniform(point, ship.getCollisionRadius() * 0.5f, Misc.random);
601 float dur = baseDuration + baseDuration * (float) Math.random();
602 float nSize = size;
603 Vector2f pt = Misc.getPointWithinRadius(point, nSize * 0.5f);
604 Vector2f v = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f);
605 v.scale(nSize + nSize * (float) Math.random() * 0.5f);
606 v.scale(0.2f);
607 Vector2f.add(vel, v, v);
608
609 float maxSpeed = nSize * 1.5f * 0.2f;
610 float minSpeed = nSize * 1f * 0.2f;
611 float overMin = v.length() - minSpeed;
612 if (overMin > 0) {
613 float durMult = 1f - overMin / (maxSpeed - minSpeed);
614 if (durMult < 0.1f) durMult = 0.1f;
615 dur *= 0.5f + 0.5f * durMult;
616 }
617 engine.addNegativeNebulaParticle(pt, v, nSize * 1f, 2f,
618 0.5f / dur, 0f, dur, c);
619 }
620 }
621
622 if (elapsed > fadeOutTime) {
623 ship.setHitpoints(0f);
625 ship.setAlphaMult(0f);
627 }
628 }
629 };
630 }
631
632
633 protected ShardFadeInPlugin createShipFadeInPlugin(final String variantId, final ShipAPI source,
634 final float delay, final float fadeInTime, final float angle) {
635
636 return new ShardFadeInPlugin(variantId, source, delay, fadeInTime, angle);
637 }
638
639 public static class ShardFadeInPlugin extends BaseEveryFrameCombatPlugin {
640 float elapsed = 0f;
641 ShipAPI [] ships = null;
642 CollisionClass collisionClass;
643
644 String variantId;
645 ShipAPI source;
646 float delay;
647 float fadeInTime;
648 float angle;
649
650 public ShardFadeInPlugin(String variantId, ShipAPI source, float delay, float fadeInTime, float angle) {
651 this.variantId = variantId;
652 this.source = source;
653 this.delay = delay;
654 this.fadeInTime = fadeInTime;
655 this.angle = angle;
656
657 }
658
659
660 @Override
661 public void advance(float amount, List<InputEventAPI> events) {
662 if (Global.getCombatEngine().isPaused()) return;
663
664 elapsed += amount;
665 if (elapsed < delay) return;
666
668
669 if (ships == null) {
670 float facing = source.getFacing() + 15f * ((float) Math.random() - 0.5f);
671// Vector2f loc = new Vector2f();
672// loc = Misc.getPointWithinRadius(loc, source.getCollisionRadius() * 0.25f);
673 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle);
674 loc.scale(source.getCollisionRadius() * 0.1f);
675 Vector2f.add(loc, source.getLocation(), loc);
676 CombatFleetManagerAPI fleetManager = engine.getFleetManager(source.getOriginalOwner());
677 boolean wasSuppressed = fleetManager.isSuppressDeploymentMessages();
678 fleetManager.setSuppressDeploymentMessages(true);
679 if (variantId.endsWith("_wing")) {
681 ships = new ShipAPI[spec.getNumFighters()];
683 captain.setPersonality(Personalities.RECKLESS); // doesn't matter for fighters
687 ShipAPI leader = engine.getFleetManager(source.getOriginalOwner()).spawnShipOrWing(variantId, loc, facing, 0f, captain);
688 for (int i = 0; i < ships.length; i++) {
689 ships[i] = leader.getWing().getWingMembers().get(i);
690 ships[i].getLocation().set(loc);
691 }
692 collisionClass = ships[0].getCollisionClass();
693 } else {
694 ships = new ShipAPI[1];
695 ships[0] = engine.getFleetManager(source.getOriginalOwner()).spawnShipOrWing(variantId, loc, facing, 0f, source.getOriginalCaptain());
696 }
697 for (int i = 0; i < ships.length; i++) {
698 ships[i].cloneVariant();
699 ships[i].getVariant().addTag(Tags.SHIP_LIMITED_TOOLTIP);
700
701 if (Global.getCombatEngine().isInCampaign() || Global.getCombatEngine().isInCampaignSim()) {
702 FactionAPI faction = Global.getSector().getFaction(Factions.OMEGA);
703 if (faction != null) {
704 String name = faction.pickRandomShipName();
705 ships[i].setName(name);
706 }
707 }
708 }
709 fleetManager.setSuppressDeploymentMessages(wasSuppressed);
710 collisionClass = ships[0].getCollisionClass();
711
712 DeployedFleetMemberAPI sourceMember = fleetManager.getDeployedFleetMemberFromAllEverDeployed(source);
713 DeployedFleetMemberAPI deployed = fleetManager.getDeployedFleetMemberFromAllEverDeployed(ships[0]);
714 if (sourceMember != null && deployed != null) {
715 Map<DeployedFleetMemberAPI, DeployedFleetMemberAPI> map = fleetManager.getShardToOriginalShipMap();
716 while (map.containsKey(sourceMember)) {
717 sourceMember = map.get(sourceMember);
718 }
719 if (sourceMember != null) {
720 map.put(deployed, sourceMember);
721 }
722 }
723 }
724
725
726
727 float progress = (elapsed - delay) / fadeInTime;
728 if (progress > 1f) progress = 1f;
729
730 for (int i = 0; i < ships.length; i++) {
731 ShipAPI ship = ships[i];
732 ship.setAlphaMult(progress);
733
734 if (progress < 0.5f) {
735 ship.blockCommandForOneFrame(ShipCommand.ACCELERATE);
736 ship.blockCommandForOneFrame(ShipCommand.TURN_LEFT);
737 ship.blockCommandForOneFrame(ShipCommand.TURN_RIGHT);
738 ship.blockCommandForOneFrame(ShipCommand.STRAFE_LEFT);
739 ship.blockCommandForOneFrame(ShipCommand.STRAFE_RIGHT);
740 }
741
742 ship.blockCommandForOneFrame(ShipCommand.USE_SYSTEM);
743 ship.blockCommandForOneFrame(ShipCommand.TOGGLE_SHIELD_OR_PHASE_CLOAK);
744 ship.blockCommandForOneFrame(ShipCommand.FIRE);
745 ship.blockCommandForOneFrame(ShipCommand.PULL_BACK_FIGHTERS);
746 ship.blockCommandForOneFrame(ShipCommand.VENT_FLUX);
747 ship.setHoldFireOneFrame(true);
748 ship.setHoldFire(true);
749
750
751 ship.setCollisionClass(CollisionClass.NONE);
752 ship.getMutableStats().getHullDamageTakenMult().modifyMult("ShardSpawnerInvuln", 0f);
753 if (progress < 0.5f) {
754 ship.getVelocity().set(source.getVelocity());
755 } else if (progress > 0.75f){
756 ship.setCollisionClass(collisionClass);
757 ship.getMutableStats().getHullDamageTakenMult().unmodifyMult("ShardSpawnerInvuln");
758 }
759
760// Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(source.getLocation(), ship.getLocation()));
761// dir.scale(amount * 50f * progress);
762// Vector2f.add(ship.getLocation(), dir, ship.getLocation());
763
764
765 float jitterLevel = progress;
766 if (jitterLevel < 0.5f) {
767 jitterLevel *= 2f;
768 } else {
769 jitterLevel = (1f - jitterLevel) * 2f;
770 }
771
772 float jitterRange = 1f - progress;
773 float maxRangeBonus = 50f;
774 float jitterRangeBonus = jitterRange * maxRangeBonus;
775 Color c = JITTER_COLOR;
776
777 ship.setJitter(this, c, jitterLevel, 25, 0f, jitterRangeBonus);
778 }
779
780 if (elapsed > fadeInTime) {
781 for (int i = 0; i < ships.length; i++) {
782 ShipAPI ship = ships[i];
783 ship.setAlphaMult(1f);
784 ship.setHoldFire(false);
785 ship.setCollisionClass(collisionClass);
786 ship.getMutableStats().getHullDamageTakenMult().unmodifyMult("ShardSpawnerInvuln");
787 }
788 engine.removePlugin(this);
789 }
790 }
791 }
792
793}
794
795
796
797
798
799
800
801
802
static SettingsAPI getSettings()
Definition Global.java:57
static CombatEngineAPI getCombatEngine()
Definition Global.java:69
void modifyMult(String source, float value)
WeightedRandomPicker< ShardType > getTypePickerBasedOnLocalConditions(ShipAPI ship)
boolean isVulnerableToMissileBarrage(ShipAPI from, ShipAPI other)
void advanceInCombat(ShipAPI ship, float amount)
ShardFadeInPlugin createShipFadeInPlugin(final String variantId, final ShipAPI source, final float delay, final float fadeInTime, final float angle)
static Map< HullSize, ShardTypeVariants > variantData
EveryFrameCombatPlugin createShipFadeOutPlugin(final ShipAPI ship, final float fadeOutTime, final List< ShardFadeInPlugin > shards)
WeightedRandomPicker< Float > getSpawnAngles(int iter)
void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id)
static float getShipWeight(ShipAPI ship)
Definition Misc.java:6095
static Vector2f getUnitVectorAtDegreeAngle(float degrees)
Definition Misc.java:1196
static float getIncapacitatedTime(ShipAPI ship)
Definition Misc.java:6118
static Color setAlpha(Color color, int alpha)
Definition Misc.java:1316
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static final int OWNER_NEUTRAL
Definition Misc.java:210
static Vector2f getPointWithinRadius(Vector2f from, float r)
Definition Misc.java:711
static Vector2f getPointWithinRadiusUniform(Vector2f from, float r, Random random)
Definition Misc.java:722
static float getAngleInDegrees(Vector2f v)
Definition Misc.java:1126
FighterWingSpecAPI getFighterWingSpec(String wingId)
MutableCharacterStatsAPI getStats()
void setPersonality(String personality)
Iterator< Object > getCheckIterator(Vector2f loc, float checkWidth, float checkHeight)
CombatFleetManagerAPI getFleetManager(FleetSide side)
void removeEntity(CombatEntityAPI entity)
void removePlugin(EveryFrameCombatPlugin plugin)
boolean isEntityInPlay(CombatEntityAPI entity)
void addPlugin(EveryFrameCombatPlugin plugin)
void addNegativeNebulaParticle(Vector2f loc, Vector2f vel, float size, float endSizeMult, float rampUpFraction, float fullBrightnessFraction, float totalDuration, Color color)
void setCollisionClass(CollisionClass collisionClass)
ShipAPI spawnShipOrWing(String specId, Vector2f location, float facing)
void setAlphaMult(float alphaMult)
MutableShipStatsAPI getMutableStats()
float getAverageArmorInSlice(float direction, float arc)
void setJitter(Object source, Color color, float intensity, int copies, float range)