001package com.fs.starfarer.api.impl.hullmods; 002 003import java.awt.Color; 004 005import org.lwjgl.util.vector.Vector2f; 006 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.combat.BaseHullMod; 009import com.fs.starfarer.api.combat.MutableShipStatsAPI; 010import com.fs.starfarer.api.combat.ShipAPI; 011import com.fs.starfarer.api.combat.ShipAPI.HullSize; 012import com.fs.starfarer.api.combat.ShipCommand; 013import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState; 014import com.fs.starfarer.api.combat.listeners.AdvanceableListener; 015import com.fs.starfarer.api.combat.listeners.HullDamageAboutToBeTakenListener; 016import com.fs.starfarer.api.impl.campaign.ids.HullMods; 017import com.fs.starfarer.api.impl.campaign.ids.Strings; 018import com.fs.starfarer.api.impl.campaign.skills.NeuralLinkScript; 019import com.fs.starfarer.api.util.FaderUtil; 020import com.fs.starfarer.api.util.Misc; 021 022public class PhaseAnchor extends BaseHullMod { 023 024 public static float PHASE_DISSIPATION_MULT = 2f; 025 public static float ACTIVATION_COST_MULT = 0f; 026 027 public static float CR_LOSS_MULT_FOR_EMERGENCY_DIVE = 1f; 028 029 public static class PhaseAnchorScript implements AdvanceableListener, HullDamageAboutToBeTakenListener { 030 public ShipAPI ship; 031 public boolean emergencyDive = false; 032 public float diveProgress = 0f; 033 public FaderUtil diveFader = new FaderUtil(1f, 1f); 034 public PhaseAnchorScript(ShipAPI ship) { 035 this.ship = ship; 036 } 037 038 public boolean notifyAboutToTakeHullDamage(Object param, ShipAPI ship, Vector2f point, float damageAmount) { 039 //if (ship.getCurrentCR() <= 0) return false; 040 041 if (!emergencyDive) { 042 String key = "phaseAnchor_canDive"; 043 boolean canDive = !Global.getCombatEngine().getCustomData().containsKey(key); 044 float depCost = 0f; 045 if (ship.getFleetMember() != null) { 046 depCost = ship.getFleetMember().getDeployCost(); 047 } 048 float crLoss = CR_LOSS_MULT_FOR_EMERGENCY_DIVE * depCost; 049 canDive &= ship.getCurrentCR() >= crLoss; 050 051 float hull = ship.getHitpoints(); 052 if (damageAmount >= hull && canDive) { 053 ship.setHitpoints(1f); 054 055 //ship.setCurrentCR(Math.max(0f, ship.getCurrentCR() - crLoss)); 056 if (ship.getFleetMember() != null) { // fleet member is fake during simulation, so this is fine 057 ship.getFleetMember().getRepairTracker().applyCREvent(-crLoss, "Emergency phase dive"); 058 //ship.getFleetMember().getRepairTracker().setCR(ship.getFleetMember().getRepairTracker().getBaseCR() + crLoss); 059 } 060 emergencyDive = true; 061 Global.getCombatEngine().getCustomData().put(key, true); 062 063 if (!ship.isPhased()) { 064 Global.getSoundPlayer().playSound("system_phase_cloak_activate", 1f, 1f, ship.getLocation(), ship.getVelocity()); 065 } 066 } 067 } 068 069 if (emergencyDive) { 070 return true; 071 } 072 073 return false; 074 } 075 076 public void advance(float amount) { 077 String id = "phase_anchor_modifier"; 078 if (emergencyDive) { 079 Color c = ship.getPhaseCloak().getSpecAPI().getEffectColor2(); 080 c = Misc.setAlpha(c, 255); 081 c = Misc.interpolateColor(c, Color.white, 0.5f); 082 083 if (diveProgress == 0f) { 084 if (ship.getFluxTracker().showFloaty()) { 085 float timeMult = ship.getMutableStats().getTimeMult().getModifiedValue(); 086 Global.getCombatEngine().addFloatingTextAlways(ship.getLocation(), 087 "Emergency dive!", 088 NeuralLinkScript.getFloatySize(ship), c, ship, 16f * timeMult, 3.2f/timeMult, 1f/timeMult, 0f, 0f, 089 1f); 090 } 091 } 092 093 diveFader.advance(amount); 094 ship.setRetreating(true, false); 095 096 ship.blockCommandForOneFrame(ShipCommand.USE_SYSTEM); 097 diveProgress += amount * ship.getPhaseCloak().getChargeUpDur(); 098 float curr = ship.getExtraAlphaMult(); 099 ship.getPhaseCloak().forceState(SystemState.IN, Math.min(1f, Math.max(curr, diveProgress))); 100 ship.getMutableStats().getHullDamageTakenMult().modifyMult(id, 0f); 101 102 if (diveProgress >= 1f) { 103 if (diveFader.isIdle()) { 104 Global.getSoundPlayer().playSound("phase_anchor_vanish", 1f, 1f, ship.getLocation(), ship.getVelocity()); 105 } 106 diveFader.fadeOut(); 107 diveFader.advance(amount); 108 float b = diveFader.getBrightness(); 109 ship.setExtraAlphaMult2(b); 110 111 float r = ship.getCollisionRadius() * 5f; 112 ship.setJitter(this, c, b, 20, r * (1f - b)); 113 114 if (diveFader.isFadedOut()) { 115 ship.getLocation().set(0, -1000000f); 116 } 117 } 118 } 119 120 121 boolean phased = ship.isPhased(); 122 if (ship.getPhaseCloak() != null && ship.getPhaseCloak().isChargedown()) { 123 phased = false; 124 } 125 126 MutableShipStatsAPI stats = ship.getMutableStats(); 127 if (phased) { 128 stats.getFluxDissipation().modifyMult(id, PHASE_DISSIPATION_MULT); 129 stats.getBallisticRoFMult().modifyMult(id, PHASE_DISSIPATION_MULT); 130 stats.getEnergyRoFMult().modifyMult(id, PHASE_DISSIPATION_MULT); 131 stats.getMissileRoFMult().modifyMult(id, PHASE_DISSIPATION_MULT); 132 stats.getBallisticAmmoRegenMult().modifyMult(id, PHASE_DISSIPATION_MULT); 133 stats.getEnergyAmmoRegenMult().modifyMult(id, PHASE_DISSIPATION_MULT); 134 stats.getMissileAmmoRegenMult().modifyMult(id, PHASE_DISSIPATION_MULT); 135 136 // doesn't actually work, needs to update the ammo tracker in the system and this isn't handled 137 // probably overpowered anyway... 138 //stats.getSystemRegenBonus().modifyMult(id, PHASE_DISSIPATION_MULT); 139 } else { 140 stats.getFluxDissipation().unmodifyMult(id); 141 stats.getBallisticRoFMult().unmodifyMult(id); 142 stats.getEnergyRoFMult().unmodifyMult(id); 143 stats.getMissileRoFMult().unmodifyMult(id); 144 stats.getBallisticAmmoRegenMult().unmodifyMult(id); 145 stats.getEnergyAmmoRegenMult().unmodifyMult(id); 146 stats.getMissileAmmoRegenMult().unmodifyMult(id); 147 //stats.getSystemRegenBonus().unmodifyMult(id); 148 } 149 } 150 151 } 152 153 @Override 154 public void applyEffectsAfterShipCreation(ShipAPI ship, String id) { 155 ship.addListener(new PhaseAnchorScript(ship)); 156 } 157 158 public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) { 159 stats.getPhaseCloakActivationCostBonus().modifyMult(id, 0f); 160 } 161 162 public String getDescriptionParam(int index, HullSize hullSize) { 163 if (index == 0) return "zero"; 164 if (index == 1) return "" + (int)PHASE_DISSIPATION_MULT + Strings.X; 165 if (index == 2) return "" + (int)CR_LOSS_MULT_FOR_EMERGENCY_DIVE + Strings.X; 166 167 // if (index == 1) return "" + (int) Math.round(PHASE_TIME_BONUS) + "%"; 168// float multWithoutMod = PhaseCloakStats.MAX_TIME_MULT; 169// float multWithMod = 1f + (multWithoutMod - 1f) * (1f + PHASE_TIME_BONUS/100f); 170// if (index == 2) return "" + (int) Math.round(multWithoutMod) + Strings.X; 171// if (index == 3) return "" + (int) Math.round(multWithMod) + Strings.X; 172// 173// if (index == 4) return "" + (int) Math.round((1f - FLUX_THRESHOLD_DECREASE_MULT) * 100f) + "%"; 174// if (index == 5) return "" + (int) Math.round(PhaseCloakStats.BASE_FLUX_LEVEL_FOR_MIN_SPEED * 100f) + "%"; 175// if (index == 6) return "" + (int)Math.round( 176// PhaseCloakStats.BASE_FLUX_LEVEL_FOR_MIN_SPEED * 100f * FLUX_THRESHOLD_DECREASE_MULT) + "%"; 177// 178 //if (index == 0) return "" + (int) Math.round(PHASE_COOLDOWN_REDUCTION) + "%"; 179 return null; 180 } 181 182 @Override 183 public boolean isApplicableToShip(ShipAPI ship) { 184 if (ship.getVariant().hasHullMod(HullMods.ADAPTIVE_COILS)) return false; 185 return ship.getHullSpec().isPhase(); 186 } 187 188 @Override 189 public String getUnapplicableReason(ShipAPI ship) { 190 if (ship.getVariant().hasHullMod(HullMods.ADAPTIVE_COILS)) { 191 return "Incompatible with Adaptive Phase Coils"; 192 } 193 if (!ship.getHullSpec().isPhase()) { 194 return "Can only be installed on phase ships"; 195 } 196 return super.getUnapplicableReason(ship); 197 } 198 199} 200