001package com.fs.starfarer.api.impl.combat;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.List;
007
008import java.awt.Color;
009
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.combat.MutableShipStatsAPI;
012import com.fs.starfarer.api.combat.ShipAPI;
013import com.fs.starfarer.api.combat.WeaponAPI;
014import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
015import com.fs.starfarer.api.impl.campaign.ids.Tags;
016import com.fs.starfarer.api.util.Misc;
017
018public class LidarArrayStats extends BaseShipSystemScript {
019
020        public static String LIDAR_WINDUP = "lidar_windup";
021        
022        public static Color WEAPON_GLOW = new Color(255,50,50,155);
023        
024        public static float RANGE_BONUS = 100f;
025        public static float PASSIVE_RANGE_BONUS = 35f;
026        public static float ROF_BONUS = 2f;
027        public static float RECOIL_BONUS = 75f;
028        public static float PROJECTILE_SPEED_BONUS = 50f;
029        
030        
031        public static class LidarDishData {
032                public float turnDir;
033                public float turnRate;
034                public float angle;
035                public float phase;
036                public float count;
037                public WeaponAPI w;
038        }
039        
040        protected List<LidarDishData> dishData = new ArrayList<LidarArrayStats.LidarDishData>();
041        protected boolean needsUnapply = false;
042        protected boolean playedWindup = false;
043        
044        protected boolean inited = false;
045        public void init(ShipAPI ship) {
046                if (inited) return;
047                inited = true;
048                
049                needsUnapply = true;
050                
051                int turnDir = 1;
052                float index = 0f;
053                float count = 0f;
054                for (WeaponAPI w : ship.getAllWeapons()) {
055                        if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
056                                count++;
057                        }
058                }
059                List<WeaponAPI> lidar = new ArrayList<WeaponAPI>();
060                for (WeaponAPI w : ship.getAllWeapons()) {
061                        if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
062                                lidar.add(w);
063                        }
064                }
065                Collections.sort(lidar, new Comparator<WeaponAPI>() {
066                        public int compare(WeaponAPI o1, WeaponAPI o2) {
067                                return (int) Math.signum(o1.getSlot().getLocation().x - o2.getSlot().getLocation().x);
068                        }
069                });
070                for (WeaponAPI w : lidar) {
071                        if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
072                                w.setSuspendAutomaticTurning(true);
073                                LidarDishData data = new LidarDishData();
074                                data.turnDir = Math.signum(turnDir);
075                                data.turnRate = 0.5f;
076                                data.turnRate = 0.1f;
077                                data.w = w;
078                                data.angle = 0f;
079                                data.phase = index / count;
080                                data.count = count;
081                                dishData.add(data);
082                                turnDir = -turnDir;
083                                index++;
084                        }
085                }
086        }
087        
088        public void rotateLidarDishes(boolean active, float effectLevel) {
089                float amount = Global.getCombatEngine().getElapsedInLastFrame();
090                
091                float turnRateMult = 1f;
092                if (active) {
093                        turnRateMult = 20f;
094                }
095                //turnRateMult = 0.1f;
096                //boolean first = true;
097                for (LidarDishData data : dishData) {
098                        float arc = data.w.getArc();
099                        float useTurnDir = data.turnDir;
100                        if (active) {
101                                useTurnDir = Misc.getClosestTurnDirection(data.angle, 0f);
102                        }
103                        float delta = useTurnDir * amount * data.turnRate * turnRateMult * arc;
104                        if (active && effectLevel > 0f && Math.abs(data.angle) < Math.abs(delta * 1.5f)) {
105                                data.angle = 0f;
106                        } else {
107                                data.angle += delta;
108                                data.phase += 1f * amount;
109                                if (arc < 360f) {
110                                        if (data.angle > arc/2f && data.turnDir > 0f) {
111                                                data.angle = arc/2f;
112                                                data.turnDir = -1f;
113                                        }
114                                        if (data.angle < -arc/2f && data.turnDir < 0f) {
115                                                data.angle = -arc/2f;
116                                                data.turnDir = 1f;
117                                        }
118                                } else {
119                                        data.angle = data.angle % 360f;
120                                }
121                        }
122                        
123
124                        float facing = data.angle + data.w.getArcFacing() + data.w.getShip().getFacing();
125                        data.w.setFacing(facing);
126                        data.w.updateBeamFromPoints();
127//                      if (first) {
128//                              System.out.println("Facing: " + facing);
129//                              first = false;
130//                      }
131                }
132        }
133        
134        public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
135                ShipAPI ship = (ShipAPI)stats.getEntity();
136                if (ship == null || ship.isHulk()) {
137                        if (needsUnapply) {
138                                unmodify(id, stats);
139                                for (WeaponAPI w : ship.getAllWeapons()) {
140                                        if (!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
141                                                        (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY)) {
142                                                w.setGlowAmount(0, null);
143                                        }
144                                }
145                                needsUnapply = false;
146                        }
147                        return;
148                }
149                
150                init(ship);
151                
152                //lidarFacingOffset += am
153                
154                boolean active = state == State.IN || state == State.ACTIVE || state == State.OUT;
155                
156                rotateLidarDishes(active, effectLevel);
157                
158                if (active) {
159                        modify(id, stats, effectLevel);
160                        needsUnapply = true;
161                } else {
162                        if (needsUnapply) {
163                                unmodify(id, stats);
164                                for (WeaponAPI w : ship.getAllWeapons()) {
165                                        if (w.getSlot().isSystemSlot()) continue;
166                                        if (!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
167                                                        (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY)) {
168                                                w.setGlowAmount(0, null);
169                                        }
170                                }
171                                needsUnapply = false;
172                        }
173                }
174                
175                if (!active) return;
176                
177
178                for (WeaponAPI w : ship.getAllWeapons()) {
179                        if (w.getSlot().isSystemSlot()) continue;
180                        if (w.getType() == WeaponType.MISSILE) continue;
181                        if (state == State.IN) {
182                                if (!(w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR))) {
183                                        w.setForceNoFireOneFrame(true);
184                                }
185                        } else {
186                                if (!(!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
187                                                (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY))) {
188                                        w.setForceNoFireOneFrame(true);
189                                }
190                        }
191                }
192                
193                Color glowColor = WEAPON_GLOW;
194                
195                float lidarRange = 500;
196                for (WeaponAPI w : ship.getAllWeapons()) {
197                        if (!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
198                                        (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY)) {
199                                lidarRange = Math.max(lidarRange, w.getRange());
200                                w.setGlowAmount(effectLevel, glowColor);
201                        }
202                }
203                lidarRange += 100f;
204                stats.getBeamWeaponRangeBonus().modifyFlat("lidararray", lidarRange);
205//              for (WeaponAPI w : ship.getAllWeapons()) {
206//                      if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
207//                              if (state == State.IN) {
208//                                      w.setForceFireOneFrame(true);
209//                              }
210//                      }
211//              }
212                
213                // always wait a quarter of a second before starting to fire the targeting lasers
214                // this is the worst-case turn time required for the dishes to face front
215                // doing this to keep the timing of the lidar ping sounds consistent relative
216                // to when the windup sound plays
217                float fireThreshold = 0.25f / 3.25f;
218                fireThreshold += 0.02f; // making sure there's only 4 lidar pings; lines up with the timing of the lidardish weapon
219                //fireThreshold = 0f;
220                for (LidarDishData data : dishData) {
221                        boolean skip = data.phase % 1f > 1f / data.count;
222                        //skip = data.phase % 1f > 0.67f;
223                        skip = false;
224                        if (skip) continue;
225                        if (data.w.isDecorative() && data.w.getSpec().hasTag(Tags.LIDAR)) {
226                                if (state == State.IN && Math.abs(data.angle) < 5f && effectLevel >= fireThreshold) {
227                                        data.w.setForceFireOneFrame(true);
228                                }
229                        }
230                }
231                
232                if (((state == State.IN && effectLevel > 0.67f) || state == State.ACTIVE) && !playedWindup) {
233                        Global.getSoundPlayer().playSound(LIDAR_WINDUP, 1f, 1f, ship.getLocation(), ship.getVelocity());
234                        playedWindup = true;
235                }
236        }
237        
238        
239        protected void modify(String id, MutableShipStatsAPI stats, float effectLevel) {
240                float mult = 1f + ROF_BONUS * effectLevel;
241                //float mult = 1f + ROF_BONUS;
242                stats.getBallisticWeaponRangeBonus().modifyPercent(id, RANGE_BONUS);
243                stats.getEnergyWeaponRangeBonus().modifyPercent(id, RANGE_BONUS);
244                stats.getBallisticRoFMult().modifyMult(id, mult);
245                stats.getEnergyRoFMult().modifyMult(id, mult);
246                //stats.getBallisticWeaponFluxCostMod().modifyMult(id, 1f - (FLUX_REDUCTION * 0.01f));
247                stats.getMaxRecoilMult().modifyMult(id, 1f - (0.01f * RECOIL_BONUS));
248                stats.getRecoilPerShotMult().modifyMult(id, 1f - (0.01f * RECOIL_BONUS));
249                stats.getRecoilDecayMult().modifyMult(id, 1f - (0.01f * RECOIL_BONUS));
250                
251                stats.getBallisticProjectileSpeedMult().modifyPercent(id, PROJECTILE_SPEED_BONUS);
252                stats.getEnergyProjectileSpeedMult().modifyPercent(id, PROJECTILE_SPEED_BONUS);
253        }
254        protected void unmodify(String id, MutableShipStatsAPI stats) {
255                stats.getBallisticWeaponRangeBonus().modifyPercent(id, PASSIVE_RANGE_BONUS);
256                stats.getEnergyWeaponRangeBonus().modifyPercent(id, PASSIVE_RANGE_BONUS);
257//              stats.getBallisticWeaponRangeBonus().unmodifyPercent(id);
258//              stats.getEnergyWeaponRangeBonus().unmodifyPercent(id);
259                
260                stats.getBallisticRoFMult().unmodifyMult(id);
261                stats.getEnergyRoFMult().unmodifyMult(id);
262                stats.getMaxRecoilMult().unmodifyMult(id);
263                stats.getRecoilPerShotMult().unmodifyMult(id);
264                stats.getRecoilDecayMult().unmodifyMult(id);
265                
266                stats.getBallisticProjectileSpeedMult().unmodifyPercent(id);
267                stats.getEnergyProjectileSpeedMult().unmodifyPercent(id);
268                
269                playedWindup = false;
270        }
271        
272        
273        public void unapply(MutableShipStatsAPI stats, String id) {
274                // never called due to runScriptWhileIdle:true in the .system file
275        }
276        
277        public StatusData getStatusData(int index, State state, float effectLevel) {
278                if (state == State.IDLE || state == State.COOLDOWN) {
279                        if (index == 3) {
280                                return new StatusData("weapon range +" + (int) PASSIVE_RANGE_BONUS + "%", false);
281                        }       
282                }
283                if (effectLevel <= 0f) return null;
284                
285                //float mult = 1f + ROF_BONUS;
286                float mult = 1f + ROF_BONUS;
287                float bonusPercent = (int) ((mult - 1f) * 100f);
288                if (index == 3) {
289                        return new StatusData("weapon range +" + (int) RANGE_BONUS + "%", false);
290                }
291                if (index == 2) {
292                        return new StatusData("rate of fire +" + (int) bonusPercent + "%", false);
293                }
294//              if (index == 1) {
295//                      return new StatusData("ballistic flux use -" + (int) FLUX_REDUCTION + "%", false);
296//              }
297                if (index == 1) {
298                        return new StatusData("weapon recoil -" + (int) RECOIL_BONUS + "%", false);
299                }
300                if (index == 0 && PROJECTILE_SPEED_BONUS > 0) {
301                        return new StatusData("projectile speed +" + (int) PROJECTILE_SPEED_BONUS + "%", false);
302                }
303                return null;
304        }
305        
306        public String getDisplayNameOverride(State state, float effectLevel) {
307                if (state == State.IDLE || state == State.COOLDOWN) {
308                        return "lidar array - passive";
309                }
310                return null;
311        }
312        
313}