Starsector API
All Classes Namespaces Files Functions Variables
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) {
152 CombatEngineAPI engine = Global.getCombatEngine();
153 if (ship.getOriginalOwner() != 0) {
154 engine.setCombatNotOverForAtLeast(SPAWN_TIME + 1f);
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 }
238 WeightedRandomPicker<ShardType> typePicker = getTypePickerBasedOnLocalConditions(ship);
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
251 WeightedRandomPicker<Float> spawnAngles = new WeightedRandomPicker<Float>();
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);
324 Global.getCombatEngine().addPlugin(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
341 public WeightedRandomPicker<Float> getSpawnAngles(int iter) {
342 WeightedRandomPicker<Float> picker = new WeightedRandomPicker<Float>();
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
361 public WeightedRandomPicker<ShardType> getTypePickerBasedOnLocalConditions(ShipAPI ship) {
362 CombatEngineAPI engine = Global.getCombatEngine();
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
414 WeightedRandomPicker<ShardType> picker = new WeightedRandomPicker<ShardType>();
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() *
503 other.getMutableStats().getShieldDamageTakenMult().getModifiedValue();
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) {
569 ship.setCollisionClass(CollisionClass.NONE);
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) {
593 CombatEngineAPI engine = Global.getCombatEngine();
594 c = RiftLanceEffect.getColorForDarkening(RiftCascadeEffect.STANDARD_RIFT_COLOR);
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);
624 Global.getCombatEngine().removeEntity(ship);
625 ship.setAlphaMult(0f);
626 Global.getCombatEngine().removePlugin(this);
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
667 CombatEngineAPI engine = Global.getCombatEngine();
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")) {
680 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(variantId);
681 ships = new ShipAPI[spec.getNumFighters()];
682 PersonAPI captain = Global.getSettings().createPerson();
683 captain.setPersonality(Personalities.RECKLESS); // doesn't matter for fighters
684 captain.getStats().setSkillLevel(Skills.POINT_DEFENSE, 2);
685 captain.getStats().setSkillLevel(Skills.GUNNERY_IMPLANTS, 2);
686 captain.getStats().setSkillLevel(Skills.IMPACT_MITIGATION, 2);
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:51
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
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)
FighterWingSpecAPI getFighterWingSpec(String wingId)