001package com.fs.starfarer.api.impl.campaign.skills;
002
003import org.lwjgl.util.vector.Vector2f;
004
005import com.fs.starfarer.api.Global;
006import com.fs.starfarer.api.characters.AfterShipCreationSkillEffect;
007import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
008import com.fs.starfarer.api.characters.ShipSkillEffect;
009import com.fs.starfarer.api.characters.SkillSpecAPI;
010import com.fs.starfarer.api.combat.CombatEntityAPI;
011import com.fs.starfarer.api.combat.DamageAPI;
012import com.fs.starfarer.api.combat.MutableShipStatsAPI;
013import com.fs.starfarer.api.combat.ShipAPI;
014import com.fs.starfarer.api.combat.ShipAPI.HullSize;
015import com.fs.starfarer.api.combat.listeners.AdvanceableListener;
016import com.fs.starfarer.api.combat.listeners.DamageTakenModifier;
017import com.fs.starfarer.api.ui.TooltipMakerAPI;
018import com.fs.starfarer.api.util.Misc;
019
020public class CombatEndurance {
021        
022        public static float PEAK_TIME_BONUS = 60;
023        public static float DEGRADE_REDUCTION_PERCENT = 25f;
024        public static float MAX_CR_BONUS = 15;
025        
026        //public static float OVERLOAD_REDUCTION = 30f;
027        
028        //public static float MAX_REGEN_LEVEL = 0.5f;
029        public static float MAX_REGEN_LEVEL = 1f;
030        public static float REGEN_RATE = 0.005f;
031        public static float TOTAL_REGEN_MAX_POINTS = 2000f;
032        public static float TOTAL_REGEN_MAX_HULL_FRACTION = 0.5f;
033
034        public static class Level1 implements ShipSkillEffect {
035                public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
036                        stats.getPeakCRDuration().modifyFlat(id, PEAK_TIME_BONUS);
037                }
038                
039                public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
040                        stats.getPeakCRDuration().unmodifyFlat(id);
041                }       
042                
043                public String getEffectDescription(float level) {
044                        return "+" + (int)(PEAK_TIME_BONUS) + " seconds peak operating time";
045                }
046                
047                public String getEffectPerLevelDescription() {
048                        return null;
049                }
050                
051                public ScopeDescription getScopeDescription() {
052                        return ScopeDescription.PILOTED_SHIP;
053                }
054        }
055        
056        public static class Level2 implements ShipSkillEffect {
057                public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
058                        stats.getCRLossPerSecondPercent().modifyMult(id, 1f - DEGRADE_REDUCTION_PERCENT / 100f);
059                }
060                
061                public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
062                        stats.getCRLossPerSecondPercent().unmodifyMult(id);
063                }       
064                
065                public String getEffectDescription(float level) {
066                        return "-" + (int)(DEGRADE_REDUCTION_PERCENT) + "% combat readiness degradation rate after peak performance time runs out";
067                }
068                
069                public String getEffectPerLevelDescription() {
070                        return null;
071                }
072                
073                public ScopeDescription getScopeDescription() {
074                        return ScopeDescription.PILOTED_SHIP;
075                }
076        }
077
078        public static class Level3 implements ShipSkillEffect {
079
080                public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
081                        stats.getMaxCombatReadiness().modifyFlat(id, MAX_CR_BONUS * 0.01f, "Combat Endurance skill");
082                }
083                
084                public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
085                        stats.getMaxCombatReadiness().unmodify(id);
086                }
087                
088                public String getEffectDescription(float level) {
089                        return "+" + (int)(MAX_CR_BONUS) + "% maximum combat readiness";
090                }
091                
092                public String getEffectPerLevelDescription() {
093                        return null;
094                }
095
096                public ScopeDescription getScopeDescription() {
097                        return ScopeDescription.PILOTED_SHIP;
098                }
099        }
100        
101        public static class Level4 extends BaseSkillEffectDescription implements ShipSkillEffect, AfterShipCreationSkillEffect {
102                public void applyEffectsAfterShipCreation(ShipAPI ship, String id) {
103                        ship.addListener(new CombatEnduranceRegen(ship));
104                }
105
106                public void unapplyEffectsAfterShipCreation(ShipAPI ship, String id) {
107                        ship.removeListenerOfClass(CombatEnduranceRegen.class);
108                }
109                
110                
111                public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {}
112                
113                public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {}
114                
115                public void createCustomDescription(MutableCharacterStatsAPI stats, SkillSpecAPI skill, 
116                                                                TooltipMakerAPI info, float width) {
117                        initElite(stats, skill);
118                        
119                        info.addPara("When below %s hull, repair %s per second; total repair is "
120                                        + "the higher of %s points or %s of maximum hull", 0f, hc, hc,
121                                        "" + (int)Math.round(MAX_REGEN_LEVEL * 100f) + "%",
122                                        //"" + (int)Math.round(REGEN_RATE * 100f) + "%",
123                                        "" + Misc.getRoundedValueMaxOneAfterDecimal(REGEN_RATE * 100f) + "%",
124                                        "" + (int)Math.round(TOTAL_REGEN_MAX_POINTS) + "",
125                                        "" + (int)Math.round(TOTAL_REGEN_MAX_HULL_FRACTION * 100f) + "%"
126                        );
127
128                        //info.addSpacer(5f);
129                }
130        }
131        
132        public static class CombatEnduranceRegen implements DamageTakenModifier, AdvanceableListener {
133                protected ShipAPI ship;
134                protected boolean inited = false;
135                protected float limit = 0f;
136                protected float repaired = 0f;
137                protected String repKey1;
138                protected String repKey2;
139                public CombatEnduranceRegen(ShipAPI ship) {
140                        this.ship = ship;
141                }
142                
143                protected void init() {
144                        inited = true;
145                        
146                        float maxHull = ship.getMaxHitpoints();
147                        limit = Math.max(TOTAL_REGEN_MAX_POINTS, TOTAL_REGEN_MAX_HULL_FRACTION * maxHull);
148                        
149                        repKey1 = "CombatEnduranceRegen_ " + ship.getId() + "_repaired";
150                        repKey2 = "CombatEnduranceRegen_ " + ship.getCaptain().getId() + "_repaired";
151                        float r1 = getRepaired(repKey1);
152                        float r2 = getRepaired(repKey2);
153                        
154                        repaired = Math.max(repaired, r1);
155                        repaired = Math.max(repaired, r2);
156                }
157                
158                protected float getRepaired(String key) {
159                        Float r = (Float) Global.getCombatEngine().getCustomData().get(key);
160                        if (r == null) r = 0f;
161                        return r;
162                }
163                
164                public void advance(float amount) {
165                        if (!inited) {
166                                init();
167                        }
168                        
169                        if (repaired >= limit) return;
170                        if (ship.getHullLevel() >= MAX_REGEN_LEVEL) return;
171                        if (ship.isHulk()) return;
172                        
173                        float maxHull = ship.getMaxHitpoints();
174                        float currHull = ship.getHitpoints();
175                        float maxPoints = maxHull * MAX_REGEN_LEVEL;
176                        
177                        float repairAmount = Math.min(limit - repaired, maxHull * REGEN_RATE * amount);
178                        // fix up remainder instantly so that there's no incentive to wait to finish a fight
179                        // so that hull is higher when it's over
180                        // actually - don't need to do this due to ship.getLowestHullLevelReached()
181                        // always being the hull level after combat
182//                      if (Global.getCombatEngine().isCombatOver()) {
183//                              repairAmount = limit - repaired;
184//                      }
185                        if (repairAmount > maxPoints - currHull) repairAmount = maxPoints - currHull;
186                        
187                        if (repairAmount > 0) {
188                                ship.setHitpoints(ship.getHitpoints() + repairAmount);
189                                repaired += repairAmount;
190                                Global.getCombatEngine().getCustomData().put(repKey1, repaired);
191                                // allow *some* extra repairs when switching to another ship
192                                Global.getCombatEngine().getCustomData().put(repKey2, repaired * 0.5f);
193                        }
194                }
195
196                public String modifyDamageTaken(Object param,
197                                                                                CombatEntityAPI target, DamageAPI damage,
198                                                                                Vector2f point, boolean shieldHit) {
199                        return null;
200                }
201
202        }
203
204        
205//      public static final float MALFUNCTION_REDUCTION = 50f;
206//      public static final float CRITICAL_MALFUNCTION_REDUCTION = 50f;
207//      public static final float PEAK_TIME_BONUS = 25;
208//      public static final float MASTERY_PEAK_TIME_BONUS = 10;
209//      //public static final float RECOVERY_RATE_BONUS = 25;
210//      public static final float MAX_CR_BONUS = 15;
211//
212//      public static class Level1 implements ShipSkillEffect {
213//
214//              public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
215//                      stats.getPeakCRDuration().modifyPercent(id, PEAK_TIME_BONUS);
216//              }
217//              
218//              public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
219//                      stats.getPeakCRDuration().unmodify(id);
220//              }       
221//              
222//              public String getEffectDescription(float level) {
223//                      return "+" + (int)(PEAK_TIME_BONUS) + "% peak operating time";
224//              }
225//              
226//              public String getEffectPerLevelDescription() {
227//                      return null;
228//              }
229//              
230//              public ScopeDescription getScopeDescription() {
231//                      return ScopeDescription.PILOTED_SHIP;
232//              }
233//      }
234//
235//      public static class Level2 implements ShipSkillEffect {
236//              public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
237//                      stats.getCriticalMalfunctionChance().modifyMult(id, 1f - CRITICAL_MALFUNCTION_REDUCTION / 100f);
238//                      stats.getWeaponMalfunctionChance().modifyMult(id, 1f - MALFUNCTION_REDUCTION / 100f);
239//                      stats.getEngineMalfunctionChance().modifyMult(id, 1f - MALFUNCTION_REDUCTION / 100f);
240//              }
241//              
242//              public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
243//                      stats.getCriticalMalfunctionChance().unmodify(id);
244//                      stats.getWeaponMalfunctionChance().unmodify(id);
245//                      stats.getEngineMalfunctionChance().unmodify(id);
246//              }
247//              
248//              public String getEffectDescription(float level) {
249//                      //return "" + (int)(RECOVERY_RATE_BONUS) + "% faster repairs and CR recovery";
250//                      //return "-" + (int)(CRITICAL_MALFUNCTION_REDUCTION) + "% chance of critical malfunctions when at low combat readiness";
251//                      return "-" + (int)(CRITICAL_MALFUNCTION_REDUCTION) + "% chance of malfunctions when at low combat readiness";
252//              }
253//              
254//              public String getEffectPerLevelDescription() {
255//                      return null;
256//              }
257//
258//              public ScopeDescription getScopeDescription() {
259//                      return ScopeDescription.PILOTED_SHIP;
260//              }
261//      }
262//      
263//      
264////    public static class Level2 implements ShipSkillEffect {
265////
266////            public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
267////                    stats.getBaseCRRecoveryRatePercentPerDay().modifyPercent(id, RECOVERY_RATE_BONUS);
268////                    //stats.getRepairRatePercentPerDay().modifyPercent(id, RECOVERY_RATE_BONUS);
269////            }
270////            
271////            public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
272////                    stats.getBaseCRRecoveryRatePercentPerDay().unmodify(id);
273////                    //stats.getRepairRatePercentPerDay().unmodify(id);
274////            }
275////            
276////            public String getEffectDescription(float level) {
277////                    //return "" + (int)(RECOVERY_RATE_BONUS) + "% faster repairs and CR recovery";
278////                    return "" + (int)(RECOVERY_RATE_BONUS) + "% faster CR recovery";
279////            }
280////            
281////            public String getEffectPerLevelDescription() {
282////                    return null;
283////            }
284////
285////            public ScopeDescription getScopeDescription() {
286////                    return ScopeDescription.PILOTED_SHIP;
287////            }
288////
289////    }
290//      
291//      public static class Level3 implements ShipSkillEffect {
292//
293//              public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
294//                      stats.getMaxCombatReadiness().modifyFlat(id, MAX_CR_BONUS * 0.01f, "Combat endurance skill");
295//              }
296//              
297//              public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
298//                      stats.getMaxCombatReadiness().unmodify(id);
299//              }
300//              
301//              public String getEffectDescription(float level) {
302//                      return "+" + (int)(MAX_CR_BONUS) + "% maximum combat readiness";
303//              }
304//              
305//              public String getEffectPerLevelDescription() {
306//                      return null;
307//              }
308//
309//              public ScopeDescription getScopeDescription() {
310//                      return ScopeDescription.PILOTED_SHIP;
311//              }
312//      }
313//      
314//      
315//      public static class Mastery implements ShipSkillEffect {
316//
317//              public void apply(MutableShipStatsAPI stats, HullSize hullSize, String id, float level) {
318//                      stats.getPeakCRDuration().modifyPercent(id, MASTERY_PEAK_TIME_BONUS);
319//              }
320//              
321//              public void unapply(MutableShipStatsAPI stats, HullSize hullSize, String id) {
322//                      stats.getPeakCRDuration().unmodify(id);
323//              }       
324//              
325//              public String getEffectDescription(float level) {
326//                      return "+" + (int)(MASTERY_PEAK_TIME_BONUS) + "% peak operating time";
327//              }
328//              
329//              public String getEffectPerLevelDescription() {
330//                      return null;
331//              }
332//              
333//              public ScopeDescription getScopeDescription() {
334//                      return ScopeDescription.ALL_SHIPS;
335//              }
336//      }
337}