1package com.fs.starfarer.api.impl.combat;
4import java.util.ArrayList;
5import java.util.HashMap;
6import java.util.Iterator;
10import org.lwjgl.util.vector.Vector2f;
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
14import com.fs.starfarer.api.combat.CollisionClass;
15import com.fs.starfarer.api.combat.CombatEngineAPI;
16import com.fs.starfarer.api.combat.CombatEngineLayers;
17import com.fs.starfarer.api.combat.CombatEntityAPI;
18import com.fs.starfarer.api.combat.DamageType;
19import com.fs.starfarer.api.combat.EmpArcEntityAPI;
20import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
21import com.fs.starfarer.api.combat.MissileAPI;
22import com.fs.starfarer.api.combat.MutableShipStatsAPI;
23import com.fs.starfarer.api.combat.ShipAPI;
24import com.fs.starfarer.api.combat.ShipSystemAPI;
25import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState;
26import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
27import com.fs.starfarer.api.impl.campaign.ids.HullMods;
28import com.fs.starfarer.api.input.InputEventAPI;
29import com.fs.starfarer.api.loading.WeaponSlotAPI;
30import com.fs.starfarer.api.util.IntervalUtil;
31import com.fs.starfarer.api.util.Misc;
32import com.fs.starfarer.api.util.WeightedRandomPicker;
69 public static class MoteData {
70 public Color jitterColor;
71 public Color empColor;
75 public float antiFighterDamage;
76 public String impactSound;
77 public String loopSound;
80 public static Map<String, MoteData>
MOTE_DATA =
new HashMap<String, MoteData>();
86 MoteData normal =
new MoteData();
87 normal.jitterColor =
new Color(100,165,255,175);
88 normal.empColor =
new Color(100,165,255,255);
91 normal.impactSound =
"mote_attractor_impact_normal";
92 normal.loopSound =
"mote_attractor_loop";
96 MoteData hf =
new MoteData();
97 hf.jitterColor =
new Color(255,100,255,175);
98 hf.empColor =
new Color(255,100,255,255);
101 hf.impactSound =
"mote_attractor_impact_damage";
102 hf.loopSound =
"mote_attractor_loop_dark";
109 return ship !=
null && ship.getVariant().hasHullMod(HullMods.HIGH_FREQUENCY_ATTRACTOR);
139 public static class SharedMoteAIData {
140 public float elapsed = 0f;
141 public List<MissileAPI> motes =
new ArrayList<MissileAPI>();
143 public float attractorRemaining = 0f;
144 public Vector2f attractorTarget =
null;
145 public ShipAPI attractorLock =
null;
149 String key = source +
"_mote_AI_shared";
152 data =
new SharedMoteAIData();
162 protected WeightedRandomPicker<WeaponSlotAPI>
launchSlots =
new WeightedRandomPicker<WeaponSlotAPI>();
170 for (WeaponSlotAPI slot : ship.getHullSpec().getAllWeaponSlotsCopy()) {
171 if (slot.isSystemSlot()) {
172 if (slot.getSlotSize() == WeaponSize.SMALL) {
175 if (slot.getSlotSize() == WeaponSize.MEDIUM) {
182 public void apply(MutableShipStatsAPI stats, String
id, State state,
float effectLevel) {
184 if (stats.getEntity() instanceof ShipAPI) {
185 ship = (ShipAPI) stats.getEntity();
195 data.elapsed += amount;
197 if (data.attractorRemaining > 0) {
198 data.attractorRemaining -= amount;
199 if (data.attractorRemaining <= 0 ||
200 (data.attractorLock !=
null && !data.attractorLock.isAlive()) ||
201 data.motes.isEmpty()) {
202 data.attractorTarget =
null;
203 data.attractorLock =
null;
204 data.attractorRemaining = 0;
207 if (effectLevel <= 0) {
220 Iterator<MissileAPI> iter = data.motes.iterator();
221 while (iter.hasNext()) {
222 if (!engine.isMissileAlive(iter.next())) {
228 for (MissileAPI mote : data.motes) {
236 if (data.motes.size() < maxMotes && data.attractorLock ==
null &&
237 !ship.getFluxTracker().isOverloadedOrVenting()) {
242 Vector2f loc = slot.computePosition(ship);
243 float dir = slot.computeMidArcAngle(ship);
244 float arc = slot.getArc();
245 dir += arc * (float) Math.random() - arc /2f;
248 MissileAPI mote = (MissileAPI) engine.spawnProjectile(ship,
null,
251 mote.setWeaponSpec(weaponId);
253 mote.getActiveLayers().remove(CombatEngineLayers.FF_INDICATORS_LAYER);
257 mote.setEmpResistance(10000);
258 data.motes.add(mote);
260 engine.spawnMuzzleFlashOrSmoke(ship, slot, mote.getWeaponSpec(), 0, dir);
267 float fraction = data.motes.size() / (Math.max(1f, maxMotes));
268 float volume = fraction * 3f;
269 if (volume > 1f) volume = 1f;
270 if (data.motes.size() > 3) {
271 Vector2f com =
new Vector2f();
272 for (MissileAPI mote : data.motes) {
273 Vector2f.add(com, mote.getLocation(), com);
275 com.scale(1f / data.motes.size());
286 if (effectLevel == 1) {
288 if (data.attractorTarget ==
null) {
293 Vector2f slotLoc =
attractor.computePosition(ship);
295 CombatEntityAPI asteroid = engine.spawnAsteroid(0, data.attractorTarget.x, data.attractorTarget.y, 0, 0);
296 asteroid.setCollisionClass(CollisionClass.NONE);
297 CombatEntityAPI target = asteroid;
298 if (data.attractorLock !=
null) {
299 target = data.attractorLock;
304 EmpArcEntityAPI arc = (EmpArcEntityAPI)engine.spawnEmpArc(ship, slotLoc, ship, target,
309 "mote_attractor_targeted_ship",
313 new Color(255,255,255,255)
315 if (data.attractorLock !=
null) {
316 arc.setTargetToShipCenter(slotLoc, data.attractorLock);
318 arc.setCoreWidthOverride(30f);
320 if (data.attractorLock ==
null) {
356 engine.removeEntity(asteroid);
364 if (data.attractorTarget ==
null)
return;
368 Vector2f targetLoc = data.attractorTarget;
371 float maxRadius = 300f;
372 float minRadius = 200f;
374 if (data.attractorLock !=
null) {
375 maxRadius += data.attractorLock.getCollisionRadius();
376 minRadius += data.attractorLock.getCollisionRadius();
377 targetLoc = data.attractorLock.getShieldCenterEvenIfNoShield();
381 float maxDur = 0.75f;
385 for (
int i = 0; i < glows; i++) {
386 float radius = minRadius + (float) Math.random() * (maxRadius - minRadius);
387 Vector2f loc = Misc.getPointAtRadius(targetLoc, radius);
388 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(loc, targetLoc));
389 float dist = Misc.getDistance(loc, targetLoc);
391 float dur = minDur + (float) Math.random() * (maxDur - minDur);
392 float speed = dist / dur;
395 float size = minSize + (float) Math.random() * (maxSize - minSize);
397 engine.addHitParticle(loc, dir, size, 1f, 0.3f, dur, color);
398 engine.addHitParticle(loc, dir, size * 0.5f, 1f, 0.3f, dur, Color.white);
403 public void unapply(MutableShipStatsAPI stats, String
id) {
406 public StatusData
getStatusData(
int index, State state,
float effectLevel) {
417 if (data.attractorLock !=
null) {
418 targetLoc =
new Vector2f(data.attractorLock.getLocation());
421 data.attractorTarget = targetLoc;
423 if (data.attractorLock !=
null) {
427 ship.getSystem().getChargeUpDur(), ship.getSystem().getChargeDownDur(),
434 if (system.isOutOfAmmo())
return null;
435 if (system.getState() != SystemState.IDLE)
return null;
442 return "OUT OF RANGE";
453 public boolean isUsable(ShipSystemAPI system, ShipAPI ship) {
458 Vector2f loc = from.getSystem().getTargetLoc();
460 loc =
new Vector2f(from.getMouseTarget());
468 Vector2f slotLoc =
attractor.computePosition(from);
470 float dist = Misc.getDistance(slotLoc, targetLoc);
472 targetLoc = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(slotLoc, targetLoc));
474 Vector2f.add(targetLoc, slotLoc, targetLoc);
480 Vector2f targetLoc =
new Vector2f(from.getMouseTarget());
487 Vector2f slotLoc =
attractor.computePosition(from);
488 float dist = Misc.getDistance(slotLoc, loc);
497 Vector2f slotLoc =
attractor.computePosition(from);
499 if (other.isFighter())
continue;
500 if (other.getOwner() == from.getOwner())
continue;
501 if (other.isHulk())
continue;
502 if (!other.isTargetable())
continue;
504 float dist = Misc.getDistance(slotLoc, other.getLocation());
505 if (dist >
getRange(from))
continue;
507 dist = Misc.getDistance(loc, other.getLocation());
508 if (dist < other.getCollisionRadius() + 50f) {
527 final Color jitterColor) {
528 return new BaseEveryFrameCombatPlugin() {
531 public void advance(
float amount, List<InputEventAPI> events) {
539 level = elapsed / in;
540 }
else if (elapsed < in + out) {
541 level = 1f - (elapsed - in) / out;
550 float jitterLevel = level;
551 float maxRangeBonus = 50f;
552 float jitterRangeBonus = jitterLevel * maxRangeBonus;
553 target.setJitterUnder(
this, jitterColor, jitterLevel, 10, 0f, jitterRangeBonus);
554 target.setJitter(
this, jitterColor, jitterLevel, 4, 0f, 0 + jitterRangeBonus);
static SoundPlayerAPI getSoundPlayer()
static CombatEngineAPI getCombatEngine()
ShipAPI getLockTarget(ShipAPI from, Vector2f loc)
boolean isMouseInRange(ShipAPI from)
IntervalUtil attractorParticleInterval
Vector2f getTargetedLocation(ShipAPI from)
static float ANTI_FIGHTER_DAMAGE
static String MOTELAUNCHER_HF
IntervalUtil launchInterval
boolean findNewTargetOnUse
boolean isUsable(ShipSystemAPI system, ShipAPI ship)
static String getImpactSoundId(ShipAPI ship)
String getInfoText(ShipSystemAPI system, ShipAPI ship)
static Map< String, MoteData > MOTE_DATA
static Color getEMPColor(ShipAPI ship)
static float getRange(ShipAPI ship)
static float getAntiFighterDamage(ShipAPI ship)
static String getLoopSound(ShipAPI ship)
void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
static boolean isHighFrequency(ShipAPI ship)
static float ATTRACTOR_DURATION
void findSlots(ShipAPI ship)
Vector2f getTargetLoc(ShipAPI from)
void calculateTargetData(ShipAPI ship)
void unapply(MutableShipStatsAPI stats, String id)
static float ANTI_FIGHTER_DAMAGE_HF
static float MAX_DIST_FROM_SOURCE_TO_ENGAGE_AS_PD
static String getWeaponId(ShipAPI ship)
WeightedRandomPicker< WeaponSlotAPI > launchSlots
static String MOTELAUNCHER
static Color getJitterColor(ShipAPI ship)
void spawnAttractorParticles(ShipAPI ship)
static int getMaxMotes(ShipAPI ship)
static float ATTRACTOR_DURATION_LOCK
static float MAX_ATTRACTOR_RANGE
static float MAX_DIST_FROM_ATTRACTOR_TO_ENGAGE_AS_PD
StatusData getStatusData(int index, State state, float effectLevel)
boolean isLocationInRange(ShipAPI from, Vector2f loc)
static SharedMoteAIData getSharedData(ShipAPI source)
EveryFrameCombatPlugin createTargetJitterPlugin(final ShipAPI target, final float in, final float out, final Color jitterColor)
void playLoop(String id, Object playingEntity, float pitch, float volume, Vector2f loc, Vector2f vel)
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)