001package com.fs.starfarer.api.impl.campaign.abilities; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import java.awt.Color; 007 008import com.fs.starfarer.api.Global; 009import com.fs.starfarer.api.campaign.BuffManagerAPI.Buff; 010import com.fs.starfarer.api.campaign.CampaignFleetAPI; 011import com.fs.starfarer.api.fleet.FleetMemberAPI; 012import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 013import com.fs.starfarer.api.impl.campaign.terrain.CRRecoveryBuff; 014import com.fs.starfarer.api.ui.TooltipMakerAPI; 015import com.fs.starfarer.api.util.Misc; 016import com.fs.starfarer.api.util.Misc.FleetMemberDamageLevel; 017 018public class ToggleAbilityWithCost extends BaseToggleAbility { 019 020 public float getFuelCostMult(boolean forTooltip) { 021 return 1f; 022 } 023 public float getCRCostMult(boolean forTooltip) { 024 return 0.25f; 025 } 026 027 public float getActivationAtLowCRShipDamageProbability(boolean forTooltip) { 028 return 0.33f; 029 } 030 031 public FleetMemberDamageLevel getActivationDamageLevel(boolean forTooltip) { 032 return FleetMemberDamageLevel.LOW; 033 } 034 035 public boolean canRecoverCRWhileActive(boolean forTooltip) { 036 return true; 037 } 038 039 public boolean shouldApplyCostWhenDeactivating(boolean forTooltip) { 040 return true; 041 } 042 043 @Override 044 protected void activateImpl() { 045 deductCost(); 046 } 047 048 protected void deductCost() { 049 CampaignFleetAPI fleet = getFleet(); 050 if (fleet == null) return; 051 052 float crCostMult = getCRCostMult(false); 053 if (crCostMult > 0) { 054 for (FleetMemberAPI member : getNonReadyShips(false)) { 055 if ((float) Math.random() < getActivationAtLowCRShipDamageProbability(false)) { 056 Misc.applyDamage(member, null, getActivationDamageLevel(false), false, null, null, 057 true, null, member.getShipName() + " suffers damage from " + spec.getName() + " activation"); 058 } 059 } 060 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 061 float crLoss = getCRCost(member, false); 062 member.getRepairTracker().applyCREvent(-crLoss, Misc.ucFirst(spec.getName().toLowerCase())); 063 } 064 } 065 066 float cost = computeFuelCost(false); 067 fleet.getCargo().removeFuel(cost); 068 } 069 070 071 protected void applyStatsEffect(float amount, float level) { 072 073 } 074 075 protected void unapplyStatsEffect() { 076 077 } 078 079 protected void applyFleetVisual(float amount, float level) { 080 081 } 082 083 @Override 084 protected void applyEffect(float amount, float level) { 085 CampaignFleetAPI fleet = getFleet(); 086 if (fleet == null) return; 087 088 applyStatsEffect(amount, level); 089 applyFleetVisual(amount, level); 090 091 if (!canRecoverCRWhileActive(false)) { 092 String buffId = getModId(); 093 float buffDur = 0.1f; 094 boolean needsSync = false; 095 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 096 if (level <= 0) { 097 member.getBuffManager().removeBuff(buffId); 098 needsSync = true; 099 } else { 100 Buff test = member.getBuffManager().getBuff(buffId); 101 if (test instanceof CRRecoveryBuff) { 102 CRRecoveryBuff buff = (CRRecoveryBuff) test; 103 buff.setDur(buffDur); 104 } else { 105 member.getBuffManager().addBuff(new CRRecoveryBuff(buffId, 0f, buffDur)); 106 needsSync = true; 107 } 108 } 109 } 110 111 if (needsSync) { 112 fleet.forceSync(); 113 } 114 } 115 116 } 117 118 @Override 119 protected void deactivateImpl() { 120 cleanupImpl(); 121 if (shouldApplyCostWhenDeactivating(false)) { 122 deductCost(); 123 } 124 } 125 126 @Override 127 protected void cleanupImpl() { 128 unapplyStatsEffect(); 129 } 130 131 protected List<FleetMemberAPI> getNonReadyShips(boolean forTooltip) { 132 List<FleetMemberAPI> result = new ArrayList<FleetMemberAPI>(); 133 if (getCRCostMult(forTooltip) <= 0f) return result; 134 135 CampaignFleetAPI fleet = getFleet(); 136 if (fleet == null) return result; 137 138 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 139 float crLoss = getCRCost(member, forTooltip); 140 if (Math.round(member.getRepairTracker().getCR() * 100) < Math.round(crLoss * 100)) { 141 result.add(member); 142 } 143 } 144 return result; 145 } 146 147 public float getCRCost(FleetMemberAPI member, boolean forTooltip) { 148 float crCostMult = getCRCostMult(forTooltip); 149 float crLoss = member.getDeployCost() * crCostMult; 150 return Math.round(crLoss * 100f) / 100f; 151 } 152 153 protected float computeFuelCost(boolean forTooltip) { 154 CampaignFleetAPI fleet = getFleet(); 155 if (fleet == null) return 0f; 156 157 float cost = fleet.getLogistics().getFuelCostPerLightYear() * getFuelCostMult(forTooltip); 158 return cost; 159 } 160 161 protected float computeSupplyCost(boolean forTooltip) { 162 CampaignFleetAPI fleet = getFleet(); 163 if (fleet == null) return 0f; 164 165 float crCostMult = getCRCostMult(forTooltip); 166 167 float cost = 0f; 168 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 169 cost += member.getDeploymentCostSupplies() * crCostMult; 170 } 171 return cost; 172 } 173 174 175 public void addOtherNotUsableReason(TooltipMakerAPI tooltip, boolean expanded) { 176 177 } 178 public void addCostTooltipSection(TooltipMakerAPI tooltip, boolean expanded, String prefix) { 179 CampaignFleetAPI fleet = getFleet(); 180 if (fleet == null) return; 181 182 Color highlight = Misc.getHighlightColor(); 183 Color bad = Misc.getNegativeHighlightColor(); 184 185 float pad = 10f; 186 187 188 String preventsRecovery = ""; 189 if (!canRecoverCRWhileActive(true)) { 190 preventsRecovery = " Prevents combat readiness recovery while active."; 191 } 192 193 String deactivateCost = ""; 194 if (shouldApplyCostWhenDeactivating(true)) { 195 deactivateCost = " The cost is incurred both when activating and deactivating the ability."; 196 } 197 preventsRecovery += deactivateCost; 198 199 if (Global.CODEX_TOOLTIP_MODE) { 200 String years = "year's"; 201 if (getFuelCostMult(true) != 1) years = "years'"; 202 203 if (getFuelCostMult(true) > 0 && getCRCostMult(true) > 0) { 204 if (prefix == null) { 205 prefix = "C"; 206 } else { 207 prefix += " c"; 208 } 209 tooltip.addPara(prefix + "onsumes %s light " + years + " worth of fuel and reduces the combat readiness " 210 + "of all ships by %s of a combat deployment." + preventsRecovery, 211 pad, 212 highlight, 213 "" + Misc.getRoundedValue(getFuelCostMult(true)), 214 "" + (int) Math.round(getCRCostMult(true) * 100f) + "%"); 215 } else if (getCRCostMult(true) > 0) { 216 if (prefix == null) { 217 prefix = "R"; 218 } else { 219 prefix += " r"; 220 } 221 tooltip.addPara(prefix + "educes the combat readiness " 222 + "of all ships by %s of a combat deployment." + preventsRecovery, 223 pad, 224 highlight, 225 "" + Misc.getRoundedValue(getFuelCostMult(true)), 226 "" + (int) Math.round(getCRCostMult(true) * 100f) + "%"); 227 } else if (getFuelCostMult(true) > 0) { 228 if (prefix == null) { 229 prefix = "C"; 230 } else { 231 prefix += " c"; 232 } 233 tooltip.addPara(prefix + "onsumes %s light " + years + " worth of fuel." + preventsRecovery, 234 pad, 235 highlight, 236 "" + Misc.getRoundedValue(getFuelCostMult(true))); 237 } 238 239 if (getCRCostMult(true) > 0) { 240 tooltip.addPara("Ships with insufficient combat readiness may suffer damage when the ability is activated.", pad); 241 } 242 } else { 243 float fuelCost = computeFuelCost(true); 244 float supplyCost = computeSupplyCost(true); 245 if (supplyCost > 0 && fuelCost > 0) { 246 if (prefix == null) { 247 prefix = "C"; 248 } else { 249 prefix += " c"; 250 } 251 tooltip.addPara(prefix + "onsumes %s fuel and reduces the combat readiness" + 252 " of all ships, costing up to %s supplies to recover." + preventsRecovery, pad, 253 highlight, 254 Misc.getRoundedValueMaxOneAfterDecimal(fuelCost), 255 Misc.getRoundedValueMaxOneAfterDecimal(supplyCost)); 256 } else if (supplyCost > 0) { 257 if (prefix == null) { 258 prefix = "R"; 259 } else { 260 prefix += " r"; 261 } 262 tooltip.addPara(prefix + "educes the combat readiness" + 263 " of all ships, costing up to %s supplies to recover." + preventsRecovery, pad, 264 highlight, 265 Misc.getRoundedValueMaxOneAfterDecimal(supplyCost)); 266 } else if (fuelCost > 0) { 267 if (prefix == null) { 268 prefix = "C"; 269 } else { 270 prefix += " c"; 271 } 272 tooltip.addPara(prefix + "onsumes %s fuel." + preventsRecovery, pad, 273 highlight, 274 Misc.getRoundedValueMaxOneAfterDecimal(fuelCost)); 275 } 276 277 if (fuelCost > 0 && fuelCost > fleet.getCargo().getFuel()) { 278 tooltip.addPara("Not enough fuel.", bad, pad); 279 } else { 280 addOtherNotUsableReason(tooltip, expanded); 281 } 282 283 List<FleetMemberAPI> nonReady = getNonReadyShips(true); 284 if (!nonReady.isEmpty()) { 285 tooltip.addPara("Some ships don't have enough combat readiness " + 286 "and may suffer damage if the ability is activated:", pad, 287 Misc.getNegativeHighlightColor(), "may suffer damage"); 288 int j = 0; 289 int max = 4; 290 float initPad = 5f; 291 for (FleetMemberAPI member : nonReady) { 292 if (j >= max) { 293 if (nonReady.size() > max + 1) { 294 tooltip.addPara(BaseIntelPlugin.INDENT + "... and several other ships", initPad); 295 break; 296 } 297 } 298 String str = ""; 299 if (!member.isFighterWing()) { 300 str += member.getShipName() + ", "; 301 str += member.getHullSpec().getHullNameWithDashClass(); 302 } else { 303 str += member.getVariant().getFullDesignationWithHullName(); 304 } 305 306 tooltip.addPara(BaseIntelPlugin.INDENT + str, initPad); 307 initPad = 0f; 308 j++; 309 } 310 } 311 } 312 } 313 314 public boolean hasTooltip() { 315 return true; 316 } 317 318 319// @Override 320// public void fleetLeftBattle(BattleAPI battle, boolean engagedInHostilities) { 321// if (engagedInHostilities) { 322// deactivate(); 323// } 324// } 325// 326// @Override 327// public void fleetOpenedMarket(MarketAPI market) { 328// deactivate(); 329// } 330 331 @Override 332 public boolean isOnCooldown() { 333 return super.getCooldownFraction() < 1f; 334 } 335 336 @Override 337 public boolean isUsable() { 338 return super.isUsable() && 339 getFleet() != null && 340 (getFleet().isAIMode() || computeFuelCost(false) <= getFleet().getCargo().getFuel()); 341 } 342 343 protected boolean showAlarm() { 344 return !getNonReadyShips(false).isEmpty() && !isOnCooldown() && !isActiveOrInProgress() && isUsable(); 345 } 346 347 @Override 348 public float getCooldownFraction() { 349 if (showAlarm()) { 350 return 0f; 351 } 352 return super.getCooldownFraction(); 353 } 354 355 @Override 356 public Color getCooldownColor() { 357 if (showAlarm()) { 358 Color color = Misc.getNegativeHighlightColor(); 359 return Misc.scaleAlpha(color, Global.getSector().getCampaignUI().getSharedFader().getBrightness() * 0.5f); 360 } 361 return super.getCooldownColor(); 362 } 363 364 @Override 365 public boolean isCooldownRenderingAdditive() { 366 if (showAlarm()) { 367 return true; 368 } 369 return false; 370 } 371} 372 373 374 375 376