001package com.fs.starfarer.api.impl.combat.dweller; 002 003import java.util.Iterator; 004import java.util.List; 005 006import java.awt.Color; 007 008import org.lwjgl.util.vector.Vector2f; 009 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.combat.CollisionClass; 012import com.fs.starfarer.api.combat.CombatEngineAPI; 013import com.fs.starfarer.api.combat.CombatEntityAPI; 014import com.fs.starfarer.api.combat.DamageType; 015import com.fs.starfarer.api.combat.DamagingProjectileAPI; 016import com.fs.starfarer.api.combat.EmpArcEntityAPI; 017import com.fs.starfarer.api.combat.EmpArcEntityAPI.EmpArcParams; 018import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin; 019import com.fs.starfarer.api.combat.MissileAPI; 020import com.fs.starfarer.api.combat.OnFireEffectPlugin; 021import com.fs.starfarer.api.combat.ShipAPI; 022import com.fs.starfarer.api.combat.WeaponAPI; 023import com.fs.starfarer.api.combat.WeaponAPI.AIHints; 024import com.fs.starfarer.api.impl.campaign.ids.Stats; 025import com.fs.starfarer.api.impl.combat.RealityDisruptorChargeGlow; 026import com.fs.starfarer.api.impl.combat.RealityDisruptorChargeGlow.EMPArcHitType; 027import com.fs.starfarer.api.impl.combat.RealityDisruptorChargeGlow.RDRepairRateDebuff; 028import com.fs.starfarer.api.util.Misc; 029 030/** 031 */ 032public class InimicalEmanationOnFireEffect implements OnFireEffectPlugin, EveryFrameWeaponEffectPlugin { 033 034 public static float EXTRA_ARC = 30f; 035 public static float REPAIR_RATE_DEBUFF_DUR = 5f; 036 037// public static String PREV_INIMICAL_EMANATION_FIRE_TIMESTAMP_KEY = "prev_inimical_emanation_fire_timestamp_key"; 038// public static float EXTRA_RANGE_SYMPATHETIC = 300f; 039 040 041 protected float extraRangeOnNextFire = 0f; 042 043 @Override 044 public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) { 045// Float firedPrev = (Float) engine.getCustomData().get(PREV_INIMICAL_EMANATION_FIRE_TIMESTAMP_KEY); 046// if (firedPrev == null) firedPrev = -1000f; 047// 048// float currTimestamp = engine.getTotalElapsedTime(false); 049// float sinceFired = currTimestamp - firedPrev; 050// if (sinceFired < 0.1f && (float) Math.random() > 0.85f) { 051// extraRangeOnNextFire = EXTRA_RANGE_SYMPATHETIC; 052// weapon.setForceFireOneFrame(true); 053// } 054 } 055 056 public void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) { 057 ShipAPI ship = weapon.getShip(); 058 if (ship == null) return; 059 060 float emp = projectile.getEmpAmount(); 061 float dam = projectile.getDamageAmount(); 062 063 CombatEntityAPI target = findTarget(projectile, weapon, engine); 064 065 Vector2f noTargetDest = null; 066 if (target == null) noTargetDest = pickNoTargetDest(projectile, weapon, engine); 067 068 Vector2f towards = noTargetDest; 069 if (target != null) towards = target.getLocation(); 070 071 float thickness = 30f; 072 Color color = weapon.getSpec().getGlowColor(); 073 Color coreColor = Color.white; 074 coreColor = Misc.zeroColor; 075 coreColor = color; 076 077 078 color = new Color(255,0,30,255); 079 //coreColor = new Color(255,10,50,155); 080 coreColor = new Color(255,10,255,255); 081// coreColor = new Color(255,0,30,255); 082// color = new Color(255,10,255,255); 083 084 //color = RiftLightningEffect.RIFT_LIGHTNING_COLOR; 085 color = DwellerShroud.SHROUD_GLOW_COLOR; 086 coreColor = color; 087// coreColor = Misc.interpolateColor(color, Color.white, 0.25f); 088// coreColor = Color.white; 089 090 float coreWidthMult = 1f; 091 092 093 Vector2f from = projectile.getLocation(); 094 DwellerShroud shroud = DwellerShroud.getShroudFor(ship); 095 if (shroud != null) { 096 float angle = Misc.getAngleInDegrees(ship.getLocation(), towards); 097 from = Misc.getUnitVectorAtDegreeAngle(angle + 90f - 180f * (float) Math.random()); 098 from.scale((0.5f + (float) Math.random() * 0.25f) * shroud.getShroudParams().maxOffset); 099 Vector2f.add(ship.getLocation(), from, from); 100 } 101 102 EmpArcParams params = new EmpArcParams(); 103 //params.segmentLengthMult = 10000f; 104 params.segmentLengthMult = 4f; 105 106// params.maxZigZagMult = 0f; 107// params.zigZagReductionFactor = 1f; 108 109 params.maxZigZagMult = 0.25f; 110 params.zigZagReductionFactor = 1f; 111 112 //params.glowColorOverride = new Color(255,10,155,255); 113 114 //params.zigZagReductionFactor = 0.25f; 115 //params.maxZigZagMult = 0f; 116 //params.flickerRateMult = 0.75f; 117// params.flickerRateMult = 1f; 118// params.flickerRateMult = 0.75f; 119 params.flickerRateMult = 0.75f + 0.25f * (float) Math.random(); 120 121 params.fadeOutDist = 150f; 122 params.minFadeOutMult = 5f; 123 124 params.glowSizeMult = 0.5f; 125 //params.glowAlphaMult = 0.5f; 126 //params.flamesOutMissiles = false; 127 128// params.movementDurOverride = 0.1f; 129// params.flickerRateMult = 0.5f; 130// params.glowSizeMult = 1f; 131// params.brightSpotFadeFraction = 0.1f; 132// params.brightSpotFullFraction = 0.9f; 133 134// params.maxZigZagMult = 1f; 135// params.zigZagReductionFactor = 0f; 136// params.flickerRateMult = 0.25f; 137 138 if (target != null) { 139 EmpArcEntityAPI arc = engine.spawnEmpArc(ship, from, ship, 140 target, 141 DamageType.ENERGY, 142 dam, 143 emp, // emp 144 100000f, // max range 145 "inimical_emanation_impact", 146 thickness, // thickness 147 color, 148 coreColor, 149 params 150 ); 151 arc.setCoreWidthOverride(thickness * coreWidthMult); 152 arc.setSingleFlickerMode(); 153 arc.setRenderGlowAtStart(false); 154 if (shroud != null) { 155 arc.setFadedOutAtStart(true); 156 } 157 arc.setWarping(0.2f); 158 159 if (target instanceof ShipAPI && !arc.isShieldHit()) { 160 ShipAPI s = (ShipAPI) target; 161 List<RDRepairRateDebuff> listeners = s.getListeners(RDRepairRateDebuff.class); 162 if (listeners.isEmpty()) { 163 s.addListener(new RDRepairRateDebuff(s, REPAIR_RATE_DEBUFF_DUR)); 164 } else { 165 listeners.get(0).resetDur(REPAIR_RATE_DEBUFF_DUR); 166 } 167 } 168 169 170 if (arc.getTargetLocation() != null) { 171 RealityDisruptorChargeGlow.spawnEMPParticles(EMPArcHitType.INIMICAL_EMANATION, null, arc.getTargetLocation(), target); 172 } 173 174 } else { 175 Vector2f to = noTargetDest; 176 EmpArcEntityAPI arc = engine.spawnEmpArcVisual(from, ship, to, ship, thickness, color, coreColor, params); 177 arc.setCoreWidthOverride(thickness * coreWidthMult); 178 arc.setSingleFlickerMode(); 179 arc.setRenderGlowAtStart(false); 180 if (shroud != null) { 181 arc.setFadedOutAtStart(true); 182 } 183 arc.setWarping(0.2f); 184 185 RealityDisruptorChargeGlow.spawnEMPParticles(EMPArcHitType.INIMICAL_EMANATION, null, to, ship); 186 } 187 188// float fireTimestamp = engine.getTotalElapsedTime(false); 189// engine.getCustomData().put(PREV_INIMICAL_EMANATION_FIRE_TIMESTAMP_KEY, Float.valueOf(fireTimestamp)); 190 } 191 192 public Vector2f pickNoTargetDest(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) { 193 float spread = 50f; 194 float range = Math.min(weapon.getRange() - spread, 300f); 195 Vector2f from = projectile.getLocation(); 196 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(weapon.getCurrAngle() + (EXTRA_ARC/2f - EXTRA_ARC * (float) Math.random())); 197 dir.scale(range); 198 Vector2f.add(from, dir, dir); 199 dir = Misc.getPointWithinRadius(dir, spread); 200 return dir; 201 } 202 203 public CombatEntityAPI findTarget(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) { 204 float range = weapon.getRange() + 50f + extraRangeOnNextFire; 205 extraRangeOnNextFire = 0f; 206 Vector2f from = projectile.getLocation(); 207 208 Iterator<Object> iter = Global.getCombatEngine().getAllObjectGrid().getCheckIterator(from, 209 range * 2f, range * 2f); 210 int owner = weapon.getShip().getOwner(); 211 CombatEntityAPI best = null; 212 float minScore = Float.MAX_VALUE; 213 214 ShipAPI ship = weapon.getShip(); 215 boolean ignoreFlares = ship != null && ship.getMutableStats().getDynamic().getValue(Stats.PD_IGNORES_FLARES, 0) >= 1; 216 ignoreFlares |= weapon.hasAIHint(AIHints.IGNORES_FLARES); 217 218 boolean phaseMode = true; 219 220 while (iter.hasNext()) { 221 Object o = iter.next(); 222 if (!(o instanceof MissileAPI) && 223 //!(o instanceof CombatAsteroidAPI) && 224 !(o instanceof ShipAPI)) continue; 225 CombatEntityAPI other = (CombatEntityAPI) o; 226 if (other.getOwner() == owner) continue; 227 228 boolean phaseHit = false; 229 if (other instanceof ShipAPI) { 230 ShipAPI otherShip = (ShipAPI) other; 231 if (otherShip.isHulk()) continue; 232 //if (!otherShip.isAlive()) continue; 233 if (otherShip.isPhased()) { 234 if (phaseMode) { 235 phaseHit = true; 236 } else { 237 continue; 238 } 239 } 240 if (!otherShip.isTargetable()) continue; 241 } 242 243 if (!phaseHit && other.getCollisionClass() == CollisionClass.NONE) continue; 244 245 if (ignoreFlares && other instanceof MissileAPI) { 246 MissileAPI missile = (MissileAPI) other; 247 if (missile.isFlare()) continue; 248 } 249 250 float radius = Misc.getTargetingRadius(from, other, false); 251 float dist = Misc.getDistance(from, other.getLocation()) - radius; 252 if (dist > range) continue; 253 254 if (!Misc.isInArc(weapon.getCurrAngle(), EXTRA_ARC, from, other.getLocation())) continue; 255 256 float score = dist; 257 258 if (score < minScore) { 259 minScore = score; 260 best = other; 261 } 262 } 263 return best; 264 } 265 266}