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.BattleAPI; 010import com.fs.starfarer.api.campaign.BuffManagerAPI.Buff; 011import com.fs.starfarer.api.campaign.CampaignFleetAPI; 012import com.fs.starfarer.api.campaign.econ.MarketAPI; 013import com.fs.starfarer.api.fleet.FleetMemberAPI; 014import com.fs.starfarer.api.fleet.FleetMemberViewAPI; 015import com.fs.starfarer.api.impl.campaign.ids.Stats; 016import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 017import com.fs.starfarer.api.impl.campaign.terrain.CRRecoveryBuff; 018import com.fs.starfarer.api.ui.LabelAPI; 019import com.fs.starfarer.api.ui.TooltipMakerAPI; 020import com.fs.starfarer.api.util.Misc; 021import com.fs.starfarer.api.util.Misc.FleetMemberDamageLevel; 022 023public class EmergencyBurnAbility extends BaseDurationAbility { 024 025 public static float SENSOR_RANGE_MULT = 0.5f; 026 //public static final float DETECTABILITY_MULT = 1.25f; 027 public static float DETECTABILITY_PERCENT = 50f; 028 public static float MAX_BURN_MOD = 8f; 029 //public static final float CR_COST_MULT = 1f; 030 public static float CR_COST_MULT = 0.25f; 031 public static float FUEL_USE_MULT = 1f; 032 033 public static float ACCELERATION_MULT = 4f; 034 035 public static float ACTIVATION_DAMAGE_PROB = 0.33f; 036 037// public String getSpriteName() { 038// return Global.getSettings().getSpriteName("abilities", Abilities.EMERGENCY_BURN); 039// } 040 041 042// @Override 043// protected String getActivationText() { 044// return Misc.ucFirst(spec.getName().toLowerCase()); 045// } 046 047 048 @Override 049 protected void activateImpl() { 050// if (entity.isInCurrentLocation()) { 051// entity.addFloatingText("Emergency burn", entity.getFaction().getBaseUIColor(), 0.5f); 052// } 053 054// AbilityPlugin goDark = entity.getAbility(Abilities.GO_DARK); 055// if (goDark != null && goDark.isActive()) { 056// goDark.deactivate(); 057// } 058// AbilityPlugin sb = entity.getAbility(Abilities.SUSTAINED_BURN); 059// if (sb != null && sb.isActive()) { 060// sb.deactivate(); 061// } 062 063 CampaignFleetAPI fleet = getFleet(); 064 if (fleet == null) return; 065 066 float crCostFleetMult = fleet.getStats().getDynamic().getValue(Stats.EMERGENCY_BURN_CR_MULT); 067 if (crCostFleetMult > 0) { 068 for (FleetMemberAPI member : getNonReadyShips()) { 069 if ((float) Math.random() < ACTIVATION_DAMAGE_PROB) { 070 Misc.applyDamage(member, null, FleetMemberDamageLevel.LOW, false, null, null, 071 true, null, member.getShipName() + " suffers damage from Emergency Burn activation"); 072 } 073 } 074 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 075 float crLoss = member.getDeployCost() * CR_COST_MULT * crCostFleetMult; 076 member.getRepairTracker().applyCREvent(-crLoss, "Emergency burn"); 077 } 078 } 079 080 float cost = computeFuelCost(); 081 fleet.getCargo().removeFuel(cost); 082 083// float distLY = Misc.getDistanceToPlayerLY(fleet); 084// //if (fleet.isPlayerFleet()) { 085// if (distLY < 4f) { 086// SpatialAnomalyParams params = new SpatialAnomalyParams(fleet, 1f, fleet.getRadius() * 2f + 100f, null); 087// SectorEntityToken anomaly = fleet.getContainingLocation().addTerrain(Terrain.SPATIAL_ANOMALY, params); 088// anomaly.getLocation().set(fleet.getLocation()); 089// } 090 } 091 092 @Override 093 protected void applyEffect(float amount, float level) { 094// if (level > 0) { 095// AbilityPlugin goDark = entity.getAbility(Abilities.GO_DARK); 096// if (goDark != null) goDark.forceDisable(); 097// AbilityPlugin sb = entity.getAbility(Abilities.SUSTAINED_BURN); 098// if (sb != null) sb.forceDisable(); 099// 100//// if (entity instanceof CampaignFleetAPI) { 101//// CampaignFleetAPI fleet = (CampaignFleetAPI) entity; 102//// if (fleet.getBattle() != null) { 103//// deactivate(); 104//// } 105//// } 106// } 107 108 CampaignFleetAPI fleet = getFleet(); 109 if (fleet == null) return; 110 111 fleet.getStats().getSensorRangeMod().modifyMult(getModId(), 1f + (SENSOR_RANGE_MULT - 1f) * level, "Emergency burn"); 112 fleet.getStats().getDetectedRangeMod().modifyPercent(getModId(), DETECTABILITY_PERCENT * level, "Emergency burn"); 113 fleet.getStats().getFleetwideMaxBurnMod().modifyFlat(getModId(), (int)(MAX_BURN_MOD * level), "Emergency burn"); 114 fleet.getStats().getAccelerationMult().modifyMult(getModId(), 1f + (ACCELERATION_MULT - 1f) * level); 115 //fleet.getCommanderStats().getDynamic().getStat(Stats.NAVIGATION_PENALTY_MULT).modifyMult(getModId(), 0f);//1f - level); 116 117 for (FleetMemberViewAPI view : fleet.getViews()) { 118 //view.getContrailColor().shift(getModId(), view.getEngineColor().getBase(), 1f, 1f, 0.25f); 119 view.getContrailColor().shift(getModId(), new Color(250,150,100,255), 1f, 1f, .75f); 120 //view.getContrailColor().shift(getModId(), new Color(255,100,255), 1f, 1f, 0.5f); 121 //view.getContrailDurMult().shift(getModId(), 0.5f, 1f, 1f, 1f); 122 //view.getContrailWidthMult().shift(getModId(), 2f, 1f, 1f, 1f); 123 view.getEngineGlowSizeMult().shift(getModId(), 2f, 1f, 1f, 1f); 124 view.getEngineHeightMult().shift(getModId(), 5f, 1f, 1f, 1f); 125 view.getEngineWidthMult().shift(getModId(), 3f, 1f, 1f, 1f); 126 } 127 128 //member.getStats().getBaseCRRecoveryRatePercentPerDay()for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 129 String buffId = getModId(); 130 float buffDur = 0.1f; 131 boolean needsSync = false; 132 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 133 if (level <= 0) { 134 member.getBuffManager().removeBuff(buffId); 135 needsSync = true; 136 } else { 137 Buff test = member.getBuffManager().getBuff(buffId); 138 if (test instanceof CRRecoveryBuff) { 139 CRRecoveryBuff buff = (CRRecoveryBuff) test; 140 buff.setDur(buffDur); 141 } else { 142 member.getBuffManager().addBuff(new CRRecoveryBuff(buffId, 0f, buffDur)); 143 needsSync = true; 144 } 145 } 146 } 147 148 //if (needsSync || fleet.isPlayerFleet()) { 149 if (needsSync) { 150 fleet.forceSync(); 151 } 152 153 154// if (level > 0) { 155// SlipstreamTerrainPlugin slipstream = SlipstreamTerrainPlugin.getSlipstreamPlugin(fleet.getContainingLocation()); 156// if (slipstream != null) { 157// slipstream.disrupt(fleet, 0.1f); 158// } 159// } 160 } 161 162 @Override 163 protected void deactivateImpl() { 164 cleanupImpl(); 165 } 166 167 @Override 168 protected void cleanupImpl() { 169 CampaignFleetAPI fleet = getFleet(); 170 if (fleet == null) return; 171 172 fleet.getStats().getSensorRangeMod().unmodify(getModId()); 173 fleet.getStats().getDetectedRangeMod().unmodify(getModId()); 174 fleet.getStats().getFleetwideMaxBurnMod().unmodify(getModId()); 175 fleet.getStats().getAccelerationMult().unmodify(getModId()); 176 //fleet.getCommanderStats().getDynamic().getStat(Stats.NAVIGATION_PENALTY_MULT).unmodify(getModId()); 177 } 178 179// @Override 180// public float getActivationDays() { 181// return 0.25f; 182// } 183// 184// @Override 185// public float getCooldownDays() { 186// return 1f; 187// } 188// 189// @Override 190// public float getDeactivationDays() { 191// return 0.25f; 192// } 193// 194// @Override 195// public float getDurationDays() { 196// return 1.5f; 197// } 198 199 200 protected List<FleetMemberAPI> getNonReadyShips() { 201 List<FleetMemberAPI> result = new ArrayList<FleetMemberAPI>(); 202 CampaignFleetAPI fleet = getFleet(); 203 if (fleet == null) return result; 204 205 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 206 //if (member.isMothballed()) continue; 207 //float crLoss = member.getDeployCost() * CR_COST_MULT * crCostFleetMult; 208 float crLoss = getCRCost(member, fleet); 209 if (Math.round(member.getRepairTracker().getCR() * 100) < Math.round(crLoss * 100)) { 210 result.add(member); 211 } 212 } 213 return result; 214 } 215 216 217// public static boolean isReadyForEBurn(FleetMemberAPI member, CampaignFleetAPI fleet) { 218// float crLoss = getCRCost(member, fleet); 219// return Math.round(member.getRepairTracker().getCR() * 100) < Math.round(crLoss * 100); 220// } 221 222 public static float getCRCost(FleetMemberAPI member, CampaignFleetAPI fleet) { 223 float crCostFleetMult = fleet.getStats().getDynamic().getValue(Stats.EMERGENCY_BURN_CR_MULT); 224 float crLoss = member.getDeployCost() * CR_COST_MULT * crCostFleetMult; 225 return Math.round(crLoss * 100f) / 100f; 226 } 227 228 protected float computeFuelCost() { 229 CampaignFleetAPI fleet = getFleet(); 230 if (fleet == null) return 0f; 231 232 float cost = fleet.getLogistics().getFuelCostPerLightYear() * FUEL_USE_MULT; 233 return cost; 234 } 235 236 protected float computeSupplyCost() { 237 CampaignFleetAPI fleet = getFleet(); 238 if (fleet == null) return 0f; 239 240 float crCostFleetMult = fleet.getStats().getDynamic().getValue(Stats.EMERGENCY_BURN_CR_MULT); 241 242 float cost = 0f; 243 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 244 cost += member.getDeploymentCostSupplies() * CR_COST_MULT * crCostFleetMult; 245 } 246 return cost; 247 } 248 249 250 @Override 251 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded) { 252 CampaignFleetAPI fleet = getFleet(); 253 if (fleet == null) return; 254 255 Color gray = Misc.getGrayColor(); 256 Color highlight = Misc.getHighlightColor(); 257 Color fuel = Global.getSettings().getColor("progressBarFuelColor"); 258 Color bad = Misc.getNegativeHighlightColor(); 259 260 if (!Global.CODEX_TOOLTIP_MODE) { 261 LabelAPI title = tooltip.addTitle("Emergency Burn"); 262 } else { 263 tooltip.addSpacer(-10f); 264 } 265 266// title.highlightLast(status); 267// title.setHighlightColor(gray); 268 269 float pad = 10f; 270 271 272 tooltip.addPara("Increases the maximum burn level by %s." + 273 " Reduces sensor range by %s and increases the range at" + 274 " which the fleet can be detected by %s.",// + 275// " The fleet will also be unaffected by most terrain " + 276// "movement penalties and hazards while the ability is active.", 277 pad, 278 highlight, 279 "" + (int) MAX_BURN_MOD, 280 "" + (int)((1f - SENSOR_RANGE_MULT) * 100f) + "%", 281 //"" + (int)((DETECTABILITY_MULT - 1f) * 100f) + "%" 282 "" + (int)(DETECTABILITY_PERCENT) + "%" 283 ); 284 285 if (Global.CODEX_TOOLTIP_MODE) { 286 String years = "year's"; 287 if (FUEL_USE_MULT != 1) years = "years'"; 288 tooltip.addPara("Consumes %s light " + years + " worth of fuel and reduces the combat readiness " 289 + "of all ships by %s of a combat deployment. " + 290 "Prevents combat readiness recovery while active.", pad, 291 highlight, 292 "" + Misc.getRoundedValue(FUEL_USE_MULT), 293 "" + (int) Math.round(CR_COST_MULT * 100f) + "%"); 294 295 tooltip.addPara("Ships with insufficient combat readiness may suffer damage when the ability is activated.", pad); 296 } else { 297 float fuelCost = computeFuelCost(); 298 float supplyCost = computeSupplyCost(); 299 300 if (supplyCost > 0) { 301 tooltip.addPara("Consumes %s fuel and reduces the combat readiness" + 302 " of all ships, costing up to %s supplies to recover. Also prevents combat readiness recovery while active.", pad, 303 highlight, 304 Misc.getRoundedValueMaxOneAfterDecimal(fuelCost), 305 Misc.getRoundedValueMaxOneAfterDecimal(supplyCost)); 306 } else { 307 tooltip.addPara("Consumes %s fuel and prevents combat readiness recovery while active.", pad, 308 highlight, 309 Misc.getRoundedValueMaxOneAfterDecimal(fuelCost)); 310 } 311 312 if (fuelCost > fleet.getCargo().getFuel()) { 313 tooltip.addPara("Not enough fuel.", bad, pad); 314 } 315 316 List<FleetMemberAPI> nonReady = getNonReadyShips(); 317 if (!nonReady.isEmpty()) { 318 //tooltip.addPara("Not all ships have enough combat readiness to initiate an emergency burn. Ships that require higher CR:", pad); 319 tooltip.addPara("Some ships don't have enough combat readiness to safely initiate an emergency burn " + 320 "and may suffer damage if the ability is activated:", pad, 321 Misc.getNegativeHighlightColor(), "may suffer damage"); 322 //tooltip.beginGridFlipped(getTooltipWidth(), 1, 30, pad); 323 //tooltip.setGridLabelColor(bad); 324 int j = 0; 325 int max = 4; 326 float initPad = 5f; 327 for (FleetMemberAPI member : nonReady) { 328 if (j >= max) { 329 if (nonReady.size() > max + 1) { 330 tooltip.addPara(BaseIntelPlugin.INDENT + "... and several other ships", initPad); 331 break; 332 } 333 } 334 335 //float crLoss = member.getDeployCost() * CR_COST_MULT; 336 //String cost = "" + Math.round(crLoss * 100) + "%"; 337 String str = ""; 338 if (!member.isFighterWing()) { 339 str += member.getShipName() + ", "; 340 str += member.getHullSpec().getHullNameWithDashClass(); 341 } else { 342 str += member.getVariant().getFullDesignationWithHullName(); 343 } 344 345 tooltip.addPara(BaseIntelPlugin.INDENT + str, initPad); 346 initPad = 0f; 347 j++; 348 349 //tooltip.addToGrid(0, j++, str, cost, bad); 350 } 351 //tooltip.addGrid(3f); 352 } 353 } 354 355 //tooltip.addPara("Disables \"Go Dark\" when activated.", pad); 356 addIncompatibleToTooltip(tooltip, expanded); 357 } 358 359 public boolean hasTooltip() { 360 return true; 361 } 362 363 364 @Override 365 public void fleetLeftBattle(BattleAPI battle, boolean engagedInHostilities) { 366 if (engagedInHostilities) { 367 deactivate(); 368 } 369 } 370 371 @Override 372 public void fleetOpenedMarket(MarketAPI market) { 373 deactivate(); 374 } 375 376 377 378 379// @Override 380// public boolean showProgressIndicator() { 381// if (true) return true; 382// return !getNonReadyShips().isEmpty(); 383// } 384// @Override 385// public float getProgressFraction() { 386// return 1f; 387// } 388// @Override 389// public Color getProgressColor() { 390// Color color = Misc.getNegativeHighlightColor(); 391// color = Misc.getHighlightColor(); 392// return Misc.scaleAlpha(color, Global.getSector().getCampaignUI().getSharedFader().getBrightness()); 393// } 394 395 396 protected boolean showAlarm() { 397 return !getNonReadyShips().isEmpty() && !isOnCooldown() && !isActiveOrInProgress() && isUsable(); 398 } 399 400 @Override 401 public boolean isUsable() { 402 return super.isUsable() && 403 getFleet() != null && 404 //getNonReadyShips().isEmpty() && 405 (getFleet().isAIMode() || computeFuelCost() <= getFleet().getCargo().getFuel()); 406 } 407 @Override 408 public float getCooldownFraction() { 409 if (showAlarm()) { 410 return 0f; 411 } 412 return super.getCooldownFraction(); 413 } 414 @Override 415 public boolean showCooldownIndicator() { 416 return super.showCooldownIndicator(); 417 } 418 @Override 419 public boolean isOnCooldown() { 420 return super.getCooldownFraction() < 1f; 421 } 422 423 @Override 424 public Color getCooldownColor() { 425 if (showAlarm()) { 426 Color color = Misc.getNegativeHighlightColor(); 427 return Misc.scaleAlpha(color, Global.getSector().getCampaignUI().getSharedFader().getBrightness() * 0.5f); 428 } 429 return super.getCooldownColor(); 430 } 431 432 @Override 433 public boolean isCooldownRenderingAdditive() { 434 if (showAlarm()) { 435 return true; 436 } 437 return false; 438 } 439} 440 441 442 443 444