001package com.fs.starfarer.api.impl.combat.dweller;
002
003import java.util.ArrayList;
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.CombatEngineAPI;
012import com.fs.starfarer.api.combat.CombatEntityAPI;
013import com.fs.starfarer.api.combat.DamagingProjectileAPI;
014import com.fs.starfarer.api.combat.EmpArcEntityAPI;
015import com.fs.starfarer.api.combat.EmpArcEntityAPI.EmpArcParams;
016import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
017import com.fs.starfarer.api.combat.MissileAPI;
018import com.fs.starfarer.api.combat.OnFireEffectPlugin;
019import com.fs.starfarer.api.combat.OnHitEffectPlugin;
020import com.fs.starfarer.api.combat.ShipAPI;
021import com.fs.starfarer.api.combat.WeaponAPI;
022import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
023import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI;
024import com.fs.starfarer.api.impl.combat.dweller.DwellerShroud.DwellerShroudParams;
025import com.fs.starfarer.api.util.Misc;
026
027/**
028 * Multiple instances of this plugin - one for every projectile (on hit), and one for each weapon.
029 * 
030 * The goal is for the on-hit effect to fire off a lightning arc in case of a hit, and for the onfire/every frame copy
031 * of the plugin to fire off a lightning arc in case there is a miss.
032 * 
033 * @author Alex
034 *
035 */
036public class RiftLightningEffect implements OnHitEffectPlugin, OnFireEffectPlugin, EveryFrameWeaponEffectPlugin {
037        
038
039        
040        
041        public static Color RIFT_LIGHTNING_COLOR = new Color(255,50,50,255);
042        public static float RIFT_LIGHTNING_SPEED = 10000f;
043        
044//      public static String RIFT_LIGHTNING_PROJ_TAG = "rift_lightning_proj_tag";
045        public static String RIFT_LIGHTNING_DAMAGE_REMOVER = "rift_lightning_damage_remover";
046        public static String RIFT_LIGHTNING_FIRED_TAG = "rift_lightning_fired_tag";
047        public static String RIFT_LIGHTNING_SOURCE_WEAPON = "rift_lightning_source_weapon";
048        
049        public static class FiredLightningProjectile {
050                public DamagingProjectileAPI projectile;
051        }
052        
053        
054//      /**
055//       * The actual damage is dealt by the rift explosion.
056//       * (Removing this: setting multiplier to 0 on projectile instead)
057//       * @author Alex
058//       *
059//       */
060//      public static class RiftLightningBaseDamageNegator implements DamageDealtModifier {
061//              @Override
062//              public String modifyDamageDealt(Object param, CombatEntityAPI target, DamageAPI damage, Vector2f point, boolean shieldHit) {
063//                      if (param instanceof DamagingProjectileAPI) {
064//                              DamagingProjectileAPI proj = (DamagingProjectileAPI) param;
065//                              if (proj.getCustomData().containsKey(RIFT_LIGHTNING_PROJ_TAG)) {
066//                                      damage.getModifier().modifyMult(RIFT_LIGHTNING_PROJ_TAG, 0f);
067//                                      return RIFT_LIGHTNING_PROJ_TAG;
068//                              }
069//                      }
070//                      return null;
071//              }
072//      }
073        
074        protected List<FiredLightningProjectile> fired = new ArrayList<>();
075        
076        @Override
077        public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) {
078//              if (!fired.isEmpty()) {
079//                      System.out.println("FIRED");
080//              }
081                List<FiredLightningProjectile> remove = new ArrayList<>();
082                
083                float maxRange = weapon.getRange();
084                for (FiredLightningProjectile data : fired) {
085                        float dist = Misc.getDistance(data.projectile.getSpawnLocation(), data.projectile.getLocation());
086                        boolean firedAlready = data.projectile.getCustomData().containsKey(RIFT_LIGHTNING_FIRED_TAG);
087                        if (dist > maxRange || firedAlready) {
088                                remove.add(data);
089                                if (!firedAlready) {
090                                        fireArc(data.projectile, weapon, null, null);
091                                }
092                        }
093                }
094                fired.removeAll(remove);
095        }
096        
097        public void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) {
098//              if (weapon.getShip() != null && 
099//                              !weapon.getShip().hasListenerOfClass(RiftLightningBaseDamageNegator.class)) {
100//                      weapon.getShip().addListener(new RiftLightningBaseDamageNegator());
101//              }
102                //projectile.setCustomData(RIFT_LIGHTNING_PROJ_TAG, true);
103                
104                projectile.getDamage().getModifier().modifyMult(RIFT_LIGHTNING_DAMAGE_REMOVER, 0f);
105                projectile.setCustomData(RIFT_LIGHTNING_SOURCE_WEAPON, weapon);
106
107                FiredLightningProjectile data = new FiredLightningProjectile();
108                data.projectile = projectile;
109                fired.add(data);
110        }
111        
112
113        public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target,
114                                          Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine) {
115                
116                WeaponAPI weapon = (WeaponAPI) projectile.getCustomData().get(RIFT_LIGHTNING_SOURCE_WEAPON);
117                if (weapon == null) return;
118                
119                fireArc(projectile, weapon, point, target);
120        }
121                        
122        public static void fireArc(DamagingProjectileAPI projectile, WeaponAPI weapon, Vector2f point, CombatEntityAPI target) {
123                boolean firedAlready = projectile.getCustomData().containsKey(RIFT_LIGHTNING_FIRED_TAG);
124                if (firedAlready) return;
125                
126                projectile.setCustomData(RIFT_LIGHTNING_FIRED_TAG, true);
127                
128                CombatEngineAPI engine = Global.getCombatEngine();
129                
130                ShipAPI ship = weapon.getShip();
131                if (ship == null) return;
132                
133                //Vector2f from = weapon.getFirePoint(0);
134                Vector2f from = projectile.getSpawnLocation();
135                
136
137                float dist = Float.MAX_VALUE;
138                if (point != null) dist = Misc.getDistance(from, point);
139                
140                float maxRange = weapon.getRange();
141                if (dist > maxRange || point == null) {
142                        dist = maxRange * (0.5f + 0.5f * (float) Math.random());
143                        if (projectile.didDamage()) {
144                                dist = maxRange;
145                        }
146                        point = Misc.getUnitVectorAtDegreeAngle(projectile.getFacing());
147                        point.scale(dist);
148                        Vector2f.add(point, from, point);
149                }
150                
151                float arcSpeed = RIFT_LIGHTNING_SPEED;
152                
153                DwellerShroud shroud = DwellerShroud.getShroudFor(ship);
154                if (shroud != null) {
155                        float angle = Misc.getAngleInDegrees(ship.getLocation(), point);
156                        from = Misc.getUnitVectorAtDegreeAngle(angle + 90f - 180f * (float) Math.random());
157                        from.scale((0.5f + (float) Math.random() * 0.25f) * shroud.getShroudParams().maxOffset);
158                        Vector2f.add(ship.getLocation(), from, from);
159                }
160                
161
162                EmpArcParams params = new EmpArcParams();
163                params.segmentLengthMult = 8f;
164                params.zigZagReductionFactor = 0.15f;
165                params.fadeOutDist = 50f;
166                params.minFadeOutMult = 10f;
167//              params.flickerRateMult = 0.7f;
168                params.flickerRateMult = 0.3f;
169//              params.flickerRateMult = 0.05f;
170//              params.glowSizeMult = 3f;
171//              params.brightSpotFullFraction = 0.5f;
172                
173                params.movementDurOverride = Math.max(0.05f, dist / arcSpeed);
174                        
175                //Color color = weapon.getSpec().getGlowColor();
176                Color color = RIFT_LIGHTNING_COLOR;
177                EmpArcEntityAPI arc = (EmpArcEntityAPI)engine.spawnEmpArcVisual(from, ship, point, null,
178                                80f, // thickness
179                                color,
180                                new Color(255,255,255,255),
181                                params
182                                );
183                arc.setCoreWidthOverride(40f);
184                
185                arc.setRenderGlowAtStart(false);
186                arc.setFadedOutAtStart(true);
187                arc.setSingleFlickerMode(true);
188        
189                spawnMine(ship, point, params.movementDurOverride * 0.8f); // - 0.05f);
190                
191                
192                if (shroud != null) {
193                        DwellerShroudParams shroudParams = shroud.getShroudParams();
194                        params = new EmpArcParams();
195                        params.segmentLengthMult = 4f;
196                        params.glowSizeMult = 4f;
197                        params.flickerRateMult = 0.5f + (float) Math.random() * 0.5f;
198                        params.flickerRateMult *= 1.5f;
199                        
200                        //Color fringe = shroudParams.overloadArcFringeColor;
201                        Color fringe = color;
202                        Color core = Color.white;
203
204                        float thickness = shroudParams.overloadArcThickness;
205                        
206                        //Vector2f to = Misc.getPointAtRadius(from, 1f);
207                        
208                        float angle = Misc.getAngleInDegrees(from, ship.getLocation());
209                        angle = angle + 90f * ((float) Math.random() - 0.5f);
210                        Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle);
211                        dist = shroudParams.maxOffset;
212                        dist = dist * 0.5f + dist * 0.5f * (float) Math.random();
213                        //dist *= 1.5f;
214                        dist *= 0.5f;
215                        dir.scale(dist);
216                        Vector2f to = Vector2f.add(from, dir, new Vector2f());
217                        
218                        arc = (EmpArcEntityAPI)engine.spawnEmpArcVisual(
219                                        from, ship, to, ship, thickness, fringe, core, params);
220                        
221                        arc.setCoreWidthOverride(shroudParams.overloadArcCoreThickness);
222                        arc.setSingleFlickerMode(false);
223                        //arc.setRenderGlowAtStart(false);
224                }
225                
226        }
227        
228        public static void spawnMine(ShipAPI source, Vector2f mineLoc, float delay) {
229                CombatEngineAPI engine = Global.getCombatEngine();
230                
231                
232                //Vector2f currLoc = mineLoc;
233                MissileAPI mine = (MissileAPI) engine.spawnProjectile(source, null, 
234                                                                                                                          "rift_lightning_minelayer", 
235                                                                                                                          mineLoc, 
236                                                                                                                          (float) Math.random() * 360f, null);
237                if (source != null) {
238                        Global.getCombatEngine().applyDamageModifiersToSpawnedProjectileWithNullWeapon(
239                                                                                        source, WeaponType.ENERGY, false, mine.getDamage());
240                }
241                
242                
243                float fadeInTime = 0.05f;
244                mine.getVelocity().scale(0);
245                mine.fadeOutThenIn(fadeInTime);
246                
247                float liveTime = Math.max(delay, 0f);
248                mine.setFlightTime(mine.getMaxFlightTime() - liveTime);
249                mine.addDamagedAlready(source);
250                mine.setNoMineFFConcerns(true);
251                if (liveTime <= 0.016f) {
252                        mine.explode();
253                }
254        }
255
256}