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}