001package com.fs.starfarer.api.impl.campaign.skills;
002
003import java.util.HashSet;
004import java.util.List;
005import java.util.Set;
006
007import com.fs.starfarer.api.Global;
008import com.fs.starfarer.api.characters.PersonAPI;
009import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
010import com.fs.starfarer.api.combat.BattleObjectiveAPI;
011import com.fs.starfarer.api.combat.CombatEngineAPI;
012import com.fs.starfarer.api.combat.CombatFleetManagerAPI;
013import com.fs.starfarer.api.combat.DeployedFleetMemberAPI;
014import com.fs.starfarer.api.combat.MutableStat.StatMod;
015import com.fs.starfarer.api.combat.ShipAPI;
016import com.fs.starfarer.api.combat.ViewportAPI;
017import com.fs.starfarer.api.impl.campaign.ids.BattleObjectives;
018import com.fs.starfarer.api.impl.campaign.ids.Stats;
019import com.fs.starfarer.api.input.InputEventAPI;
020
021public class CoordinatedManeuversScript extends BaseEveryFrameCombatPlugin {
022        public static final Object KEY_STATUS = new Object();
023        
024        public static final float BASE_MAXIMUM = 20;
025        public static final float PER_BUOY = 5;
026        
027        public static final String BONUS_ID = "coord_maneuvers_bonus";
028        
029        private CombatEngineAPI engine;
030        public void init(CombatEngineAPI engine) {
031                this.engine = engine;
032        }
033        
034        private ShipAPI prevPlayerShip = null;
035        private int skipFrames = 0;
036        private Set<CombatFleetManagerAPI> needsCleanup = new HashSet<CombatFleetManagerAPI>();
037        public void advance(float amount, List<InputEventAPI> events) {
038                if (engine == null) return;
039                if (engine.isPaused()) return;
040                
041                
042                // if the player changed flagships:
043                // skip a few frames to make sure the status ends up on top of the status list
044                ShipAPI playerShip = engine.getPlayerShip();
045                if (playerShip != prevPlayerShip) {
046                        prevPlayerShip = playerShip;
047                        skipFrames = 20;
048                }
049                
050                if (skipFrames > 0) {
051                        skipFrames--;
052                        return;
053                }
054                
055                updateForSide(engine.getFleetManager(0));
056                updateForSide(engine.getFleetManager(1));
057                
058                // nothing to do with Coordinated Maneuvers, just a convenient place to add this
059                updateForceConcentration(engine.getFleetManager(0));
060                updateForceConcentration(engine.getFleetManager(1));
061                
062                updateDPFromSupportDoctrine();
063        }
064        
065        protected ShipAPI undoDPMod = null;
066        protected void updateDPFromSupportDoctrine() {
067                // making sure that while transferring command away from something, the previous player ship
068                // does NOT receive the DP reduction until transferring command is finished
069                
070                ShipAPI from = engine.getShipPlayerIsTransferringCommandFrom();
071                String id = SupportDoctrine.SUPPORT_DOCTRINE_DP_REDUCTION_ID + "_reverse";
072                if (from != null) {
073                        StatMod bonus = from.getMutableStats().getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).getFlatBonus(SupportDoctrine.SUPPORT_DOCTRINE_DP_REDUCTION_ID);
074                        if (bonus != null && bonus.value != 0) {
075                                undoDPMod = from;
076                                from.getMutableStats().getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).modifyFlat(id, -bonus.value);
077                                if (from.getFleetMember() != null) {
078                                        from.getFleetMember().getStats().getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).modifyFlat(id, -bonus.value);
079                                }
080                        }
081                } else if (undoDPMod != null) {
082                        undoDPMod.getMutableStats().getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).unmodifyFlat(id);
083                        if (undoDPMod.getFleetMember() != null) {
084                                undoDPMod.getFleetMember().getStats().getDynamic().getMod(Stats.DEPLOYMENT_POINTS_MOD).unmodifyFlat(id);
085                        }
086                        undoDPMod = null;
087                }
088                
089        }
090
091
092        private void updateForSide(CombatFleetManagerAPI manager) {
093
094//              PersonAPI commander = manager.getFleetCommander();
095//              if (commander == null) {
096//                      cleanUpIfNeeded(manager);
097//                      return;
098//              }
099//              float max = BASE_MAXIMUM + commander.getStats().getDynamic().getValue(Stats.COORDINATED_MANEUVERS_MAX, 0f);
100                
101                float max = 0f;
102                for (PersonAPI commander : manager.getAllFleetCommanders()) {
103                        max = Math.max(max, BASE_MAXIMUM + commander.getStats().getDynamic().getValue(Stats.COORDINATED_MANEUVERS_MAX, 0f));
104                }
105                
106                if (max <= 0f) {
107                        cleanUpIfNeeded(manager);
108                        return;
109                }
110                
111                boolean buoysOnly = true;
112                float total = 0f;
113                List<DeployedFleetMemberAPI> deployed = manager.getDeployedCopyDFM();
114                for (DeployedFleetMemberAPI member : deployed) {
115                        if (member.isFighterWing()) continue;
116                        if (member.isStationModule()) continue;
117                        
118                        float curr = member.getShip().getMutableStats().getDynamic().getValue(Stats.COORDINATED_MANEUVERS_FLAT, 0f);
119                        total += curr;
120                }
121                
122                if (total > 0) buoysOnly = false;
123                
124                int numBuoys = 0;
125                for (BattleObjectiveAPI obj : engine.getObjectives()) {
126                        if (obj.getOwner() == manager.getOwner() && BattleObjectives.NAV_BUOY.equals(obj.getType())) {
127                                total += PER_BUOY;
128                                numBuoys++;
129                        }
130                }
131
132                
133                if (total <= 0f) {
134                        cleanUpIfNeeded(manager);
135                        return;
136                }
137                
138                //if (total > max) total = max;
139                
140                boolean includeSelf = false;
141                includeSelf = true;
142                for (DeployedFleetMemberAPI member : deployed) {
143                        if (member.isFighterWing()) continue;
144                        if (member.getShip() == null) continue;
145                        
146                        float curr = member.getShip().getMutableStats().getDynamic().getValue(Stats.COORDINATED_MANEUVERS_FLAT, 0f);
147                        if (includeSelf) curr = 0f;
148                        
149                        float bonus = Math.min(max, Math.max(0f, total - curr));
150                        member.getShip().getMutableStats().getMaxSpeed().modifyPercent(BONUS_ID, bonus);
151                }
152                
153                needsCleanup.add(manager);
154                
155                
156                if (manager.getOwner() == engine.getPlayerShip().getOwner()) {
157                        //if (engine.getPlayerShip().isShuttlePod()) return;
158                        
159                        float curr = engine.getPlayerShip().getMutableStats().getDynamic().getValue(Stats.COORDINATED_MANEUVERS_FLAT, 0f);
160                        if (includeSelf) curr = 0f;
161
162                        float bonus = Math.min(max, Math.max(0f, total - curr));
163                        
164                        String title = "Coordinated Maneuvers:" + " " + (int) Math.min(max, total) + "%";
165                        //String data = "+" + (int)bonus + "% top speed (ship: " + (int) curr + "%)";
166                        String data = "+" + (int)bonus + "% top speed";
167                        if (buoysOnly) {
168                                title = "Nav Buoy";
169                                if (numBuoys > 1) {
170                                        title += "s";
171                                        title += " (" + numBuoys + ")";
172                                }
173                                data = "+" + (int)bonus + "% top speed";
174                        }
175                        String icon = Global.getSettings().getSpriteName("ui", "icon_tactical_coordinated_maneuvers");
176                        engine.maintainStatusForPlayerShip(KEY_STATUS, icon,
177                                                title, 
178                                                data, false);
179                }
180        }
181        
182        protected void cleanUpIfNeeded(CombatFleetManagerAPI manager) {
183                if (needsCleanup.contains(manager)) {
184                        needsCleanup.remove(manager);
185                        List<DeployedFleetMemberAPI> deployed = manager.getDeployedCopyDFM();
186                        for (DeployedFleetMemberAPI member : deployed) {
187                                if (member.isFighterWing()) continue;
188                                if (member.getShip() == null) continue;
189                                member.getShip().getMutableStats().getMaxSpeed().unmodify(BONUS_ID);
190                        }
191                }
192        }
193        
194        
195        protected void updateForceConcentration(CombatFleetManagerAPI manager) {
196                if (true) return;
197                List<DeployedFleetMemberAPI> deployed = manager.getDeployedCopyDFM();
198                for (DeployedFleetMemberAPI member : deployed) {
199                        if (member.isFighterWing()) continue;
200                        if (member.isStationModule()) continue;
201                        if (member.getMember() == null) continue;
202                        
203                        
204                        PersonAPI fc = member.getMember().getFleetCommander();
205                        if (fc == null) fc = member.getMember().getFleetCommanderForStats();
206                        ShipAPI ship = member.getShip();
207                        
208                        if (ship == null) continue;
209                        if (fc == null) continue;
210                        
211                        //boolean hasFC = fc.getStats().getDynamic().getMod(Stats.HAS_FORCE_CONCENTRATION_BONUS_MOD).computeEffective(0f) > 0f;
212                        boolean hasFC = false;
213                        
214                        String id = "fc_zf_bonus";
215                        if (hasFC) {
216                                boolean hasZF = ship.isEngineBoostActive();
217                                if (ship.areAnyEnemiesInRange()) {
218                                        ship.getMutableStats().getZeroFluxSpeedBoost().modifyFlat(id, ForceConcentration.ZERO_FLUX_SPEED_BONUS_SMALL);
219                                } else {
220                                        ship.getMutableStats().getZeroFluxSpeedBoost().modifyFlat(id, ForceConcentration.ZERO_FLUX_SPEED_BONUS);
221                                }
222                                
223                                boolean applyAccelAndTurnModifiers = !ship.areAnyEnemiesInRange() && hasZF;
224                                if (applyAccelAndTurnModifiers) {
225                                        ship.getMutableStats().getAcceleration().modifyFlat(id, ForceConcentration.ZERO_FLUX_ACCEL_BONUS);
226                                        ship.getMutableStats().getDeceleration().modifyFlat(id, ForceConcentration.ZERO_FLUX_ACCEL_BONUS);
227                                        ship.getMutableStats().getMaxTurnRate().modifyFlat(id, ForceConcentration.ZERO_FLUX_TURN_BONUS);
228                                        ship.getMutableStats().getTurnAcceleration().modifyFlat(id, ForceConcentration.ZERO_FLUX_TURN_ACCEL_BONUS);
229                                } else {
230                                        ship.getMutableStats().getAcceleration().unmodifyFlat(id);
231                                        ship.getMutableStats().getDeceleration().unmodifyFlat(id);
232                                        ship.getMutableStats().getMaxTurnRate().unmodifyFlat(id);
233                                        ship.getMutableStats().getTurnAcceleration().unmodifyFlat(id);
234                                }
235                        }
236                }
237        }
238        
239        
240//      public static PersonAPI getCommander(CombatFleetManagerAPI manager) {
241//              List<DeployedFleetMemberAPI> deployed = manager.getDeployedCopyDFM();
242//              if (deployed.isEmpty()) return null;
243//              
244//              PersonAPI defaultCommander = manager.getDefaultCommander();
245//              for (DeployedFleetMemberAPI member : deployed) {
246//                      if (member.isFighterWing()) continue;
247//                      FleetMemberAPI m = member.getMember();
248//                      PersonAPI commander = m.getFleetCommanderForStats();
249//                      if (commander == null && m.getFleetData() != null) {
250//                              commander = m.getFleetData().getCommander();
251//                      }
252//                      if (commander == null) {
253//                              commander = defaultCommander;
254//                      }
255//                      return commander;
256//              }
257//              return null;
258//      }
259        
260
261        public void renderInUICoords(ViewportAPI viewport) {
262        }
263
264        public void renderInWorldCoords(ViewportAPI viewport) {
265        }
266
267}