001package com.fs.starfarer.api.impl.combat.threat;
002
003import java.util.Iterator;
004
005import java.awt.Color;
006
007import org.lwjgl.util.vector.Vector2f;
008
009import com.fs.starfarer.api.Global;
010import com.fs.starfarer.api.combat.CollisionClass;
011import com.fs.starfarer.api.combat.CombatEngineAPI;
012import com.fs.starfarer.api.combat.CombatEntityAPI;
013import com.fs.starfarer.api.combat.DamageType;
014import com.fs.starfarer.api.combat.DamagingProjectileAPI;
015import com.fs.starfarer.api.combat.EmpArcEntityAPI;
016import com.fs.starfarer.api.combat.EmpArcEntityAPI.EmpArcParams;
017import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
018import com.fs.starfarer.api.combat.MissileAPI;
019import com.fs.starfarer.api.combat.OnFireEffectPlugin;
020import com.fs.starfarer.api.combat.ShipAPI;
021import com.fs.starfarer.api.combat.WeaponAPI;
022import com.fs.starfarer.api.combat.WeaponAPI.AIHints;
023import com.fs.starfarer.api.impl.campaign.ids.Stats;
024import com.fs.starfarer.api.impl.combat.threat.RoilingSwarmEffect.SwarmMember;
025import com.fs.starfarer.api.util.Misc;
026import com.fs.starfarer.api.util.WeightedRandomPicker;
027
028/**
029 */
030public class VoltaicDischargeOnFireEffect implements OnFireEffectPlugin, EveryFrameWeaponEffectPlugin, 
031                                                                                                        FragmentWeapon {
032
033        public static String SWARM_TAG_PHASE_MODE = "swarm_tag_phase_mode";
034        
035        public static Color EMP_FRINGE_COLOR_BRIGHT = new Color(213,255,237,255);
036        
037        public static Color EMP_FRINGE_COLOR = new Color(130,155,145,255);
038        public static Color PHASE_FRINGE_COLOR = new Color(120,110,185,255);
039        public static Color PHASE_CORE_COLOR = new Color(255,255,255,127);
040        
041        public static float EXTRA_ARC = 360f;
042        public static int FRAGMENTS_TO_FIRE = 10;
043        
044        
045        public static boolean isSwarmPhaseMode(ShipAPI ship) {
046                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
047                return swarm != null && swarm.params.tags.contains(SWARM_TAG_PHASE_MODE);
048        }
049        public static void setSwarmPhaseMode(ShipAPI ship) {
050                new AttackSwarmPhaseModeScript(ship);
051                
052//              RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
053//              if (swarm != null) {
054//                      Color color = Misc.setAlpha(VoltaicDischargeOnFireEffect.PHASE_FRINGE_COLOR, 60);
055//                      swarm.params.flashFringeColor = color;
056//                      swarm.params.flashRadius = 180f;
057//                      swarm.params.tags.add(VoltaicDischargeOnFireEffect.SWARM_TAG_PHASE_MODE);
058//                      
059//                      for (WeaponAPI w : ship.getAllWeapons()) {
060//                              if (w.usesAmmo() && w.getSpec().hasTag(Tags.FRAGMENT_GLOW)) {
061//                                      w.setAmmo(Integer.MAX_VALUE);
062//                                      w.setMaxAmmo(Integer.MAX_VALUE);
063//                              }
064//                              if (w.getSpec().hasTag(Tags.OVERSEER_CHARGE) || 
065//                                              (ship.isFighter() && w.getSpec().hasTag(Tags.OVERSEER_CHARGE_FIGHTER))) {
066//                                      w.setAmmo(w.getMaxAmmo());
067//                              }
068//                      }
069//              }
070        }
071        
072        @Override
073        public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) {
074                ShipAPI ship = weapon.getShip();
075                if (ship == null) return;
076                
077                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
078                int active = swarm == null ? 0 : swarm.getNumActiveMembers();
079                int required = getNumFragmentsToFire();
080                boolean disable = active < required;
081                weapon.setForceDisabled(disable);
082                
083                showNoFragmentSwarmWarning(weapon, ship);
084        }
085        
086        @Override
087        public int getNumFragmentsToFire() {
088                return FRAGMENTS_TO_FIRE;
089        }
090        
091        public void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) {
092                //ARC = 30f;
093                float emp = projectile.getEmpAmount();
094                float dam = projectile.getDamageAmount();
095                
096                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(projectile.getSource());
097                if (swarm == null || swarm.getAttachedTo() == null) return;
098                
099                CombatEntityAPI target = findTarget(projectile, weapon, engine);
100                
101                Vector2f noTargetDest = null;
102                if (target == null) noTargetDest = pickNoTargetDest(projectile, weapon, engine);
103                
104                Vector2f towards = noTargetDest;
105                if (target != null) towards = target.getLocation();
106                
107                SwarmMember pick = pickFragmentTowardsPointWithinRange(swarm, towards, 150f);
108                if (pick == null) return;
109                
110                pick.setRecentlyPicked(1f);
111                
112                float thickness = 30f;
113                //Color color = weapon.getSpec().getGlowColor();
114                //Color color = new Color(255,0,0,255);
115                Color color = EMP_FRINGE_COLOR;
116                Color coreColor = Color.white;
117                
118                boolean phaseMode = isSwarmPhaseMode(projectile.getSource());
119                if (phaseMode) {
120                        color = PHASE_FRINGE_COLOR;
121                        if (target instanceof ShipAPI && ((ShipAPI)target).isPhased()) {
122                                coreColor = PHASE_CORE_COLOR;
123                        }
124                }
125                
126                float coreWidthMult = 0.75f;
127                
128                EmpArcParams params = new EmpArcParams();
129                params.segmentLengthMult = 8f;
130                //params.zigZagReductionFactor = 0.25f;
131                params.zigZagReductionFactor = 0.5f;
132                //params.maxZigZagMult = 0f;
133                //params.flickerRateMult = 0.75f;
134                params.flickerRateMult = 1f;
135                params.fadeOutDist = 1000f;
136                params.minFadeOutMult = 1f;
137//              params.fadeOutDist = 200f;
138//              params.minFadeOutMult = 2f;
139                params.glowSizeMult = 0.5f;
140                params.glowAlphaMult = 0.75f;
141                
142                // actually, probably fine given how long it takes to chew through the missile health with low damage per hit
143                //params.flamesOutMissiles = false; // a bit much given the RoF and general prevalence
144                
145                pick.flash();
146                pick.flash.forceIn();
147                pick.flash.setDurationOut(0.25f);
148                
149                //weapon.setAmmo(20);
150                
151                if (target != null) {
152                        EmpArcEntityAPI arc = engine.spawnEmpArc(projectile.getSource(), pick.loc, weapon.getShip(),
153                                           target,
154                                           DamageType.ENERGY, 
155                                           dam,
156                                           emp, // emp 
157                                           100000f, // max range 
158                                           "voltaic_discharge_emp_impact",
159                                           thickness, // thickness
160                                           color,
161                                           coreColor,
162                                           params
163                                           );
164                        arc.setCoreWidthOverride(thickness * coreWidthMult);
165                        arc.setSingleFlickerMode();
166                        arc.setUpdateFromOffsetEveryFrame(true);
167                        arc.setRenderGlowAtStart(false);
168                        arc.setFadedOutAtStart(true);
169                } else {
170                        params.flickerRateMult = 1f;
171                        
172                        Vector2f to = noTargetDest;
173                        //Vector2f to = targetLoc;
174                        EmpArcEntityAPI arc = engine.spawnEmpArcVisual(pick.loc, weapon.getShip(), to, weapon.getShip(), thickness, color, coreColor, params);
175                        arc.setCoreWidthOverride(thickness * coreWidthMult);
176                        arc.setSingleFlickerMode();
177                        arc.setUpdateFromOffsetEveryFrame(true);
178                        arc.setRenderGlowAtStart(false);
179                        arc.setFadedOutAtStart(true);
180                        //Global.getSoundPlayer().playSound("shock_repeater_emp_impact", 1f, 1f, to, new Vector2f());
181                }
182        }
183        
184        public Vector2f pickNoTargetDest(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) {
185                float spread = 50f;
186                float range = Math.min(weapon.getRange() - spread, 150f);
187                Vector2f from = projectile.getLocation();
188                Vector2f dir = Misc.getUnitVectorAtDegreeAngle(weapon.getCurrAngle() + (EXTRA_ARC/2f - EXTRA_ARC * (float) Math.random()));
189                dir.scale(range);
190                Vector2f.add(from, dir, dir);
191                dir = Misc.getPointWithinRadius(dir, spread);
192                return dir;
193        }
194        
195        public CombatEntityAPI findTarget(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) {
196                float range = weapon.getRange() + 50f;
197                Vector2f from = projectile.getLocation();
198                
199                Iterator<Object> iter = Global.getCombatEngine().getAllObjectGrid().getCheckIterator(from,
200                                                                                                                                                        range * 2f, range * 2f);
201                int owner = weapon.getShip().getOwner();
202                CombatEntityAPI best = null;
203                float minScore = Float.MAX_VALUE;
204                
205                ShipAPI ship = weapon.getShip();
206                boolean ignoreFlares = ship != null && ship.getMutableStats().getDynamic().getValue(Stats.PD_IGNORES_FLARES, 0) >= 1;
207                ignoreFlares |= weapon.hasAIHint(AIHints.IGNORES_FLARES);
208                
209                boolean phaseMode = isSwarmPhaseMode(ship);
210                
211                while (iter.hasNext()) {
212                        Object o = iter.next();
213                        if (!(o instanceof MissileAPI) &&
214                                        //!(o instanceof CombatAsteroidAPI) &&
215                                        !(o instanceof ShipAPI)) continue;
216                        CombatEntityAPI other = (CombatEntityAPI) o;
217                        if (other.getOwner() == owner) continue;
218                        
219                        boolean phaseHit = false;
220                        if (other instanceof ShipAPI) {
221                                ShipAPI otherShip = (ShipAPI) other;
222                                if (otherShip.isHulk()) continue;
223                                //if (!otherShip.isAlive()) continue;
224                                if (otherShip.isPhased()) {
225                                        if (phaseMode) {
226                                                phaseHit = true;
227                                        } else {
228                                                continue;
229                                        }
230                                }
231                                if (!otherShip.isTargetable()) continue;
232                        }
233                        
234                        if (!phaseHit && other.getCollisionClass() == CollisionClass.NONE) continue;
235                        
236                        if (ignoreFlares && other instanceof MissileAPI) {
237                                MissileAPI missile = (MissileAPI) other;
238                                if (missile.isFlare()) continue;
239                        }
240
241                        float radius = Misc.getTargetingRadius(from, other, false);
242                        float dist = Misc.getDistance(from, other.getLocation()) - radius;
243                        if (dist > range) continue;
244                        
245                        if (!Misc.isInArc(weapon.getCurrAngle(), EXTRA_ARC, from, other.getLocation())) continue;
246                        
247                        float score = dist;
248                        
249                        if (score < minScore) {
250                                minScore = score;
251                                best = other;
252                        }
253                }
254                return best;
255        }
256        
257        public static SwarmMember pickFragmentTowardsPointWithinRange(RoilingSwarmEffect swarm, Vector2f towards, float maxRange) {
258                WeightedRandomPicker<SwarmMember> picker = swarm.getPicker(true, true, towards);
259                while (!picker.isEmpty()) {
260                        SwarmMember p = picker.pickAndRemove();
261                        float dist = Misc.getDistance(p.loc, swarm.getAttachedTo().getLocation());
262                        if (dist > maxRange) continue;
263                        return p;
264                }
265                return null;
266        }
267
268
269}