001package com.fs.starfarer.api.impl.combat; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.List; 007 008import com.fs.starfarer.api.Global; 009import com.fs.starfarer.api.combat.CombatEngineAPI; 010import com.fs.starfarer.api.combat.MutableShipStatsAPI; 011import com.fs.starfarer.api.combat.ShipAPI; 012import com.fs.starfarer.api.combat.ShipEngineControllerAPI; 013import com.fs.starfarer.api.combat.ShipSystemAPI; 014import com.fs.starfarer.api.combat.ShipAPI.HullSize; 015import com.fs.starfarer.api.combat.ShipEngineControllerAPI.ShipEngineAPI; 016import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState; 017import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags; 018import com.fs.starfarer.api.util.Misc; 019import com.fs.starfarer.api.util.Misc.FindShipFilter; 020 021public class InterdictorArrayStats extends BaseShipSystemScript { 022 public static final Object SHIP_KEY = new Object(); 023 public static final Object TARGET_KEY = new Object(); 024 025 public static final float WING_EFFECT_RANGE = 200f; 026 027 public static final float RANGE = 1000f; 028 public static final Color EFFECT_COLOR = new Color(100,165,255,75); 029 030 031 public static class TargetData { 032 public ShipAPI target; 033 public float sinceLastAfterimage = 0f; 034 public boolean lastAbove = false; 035 public TargetData(ShipAPI target) { 036 this.target = target; 037 } 038 } 039 040 public void apply(MutableShipStatsAPI stats, final String id, State state, float effectLevel) { 041 ShipAPI ship = null; 042 if (stats.getEntity() instanceof ShipAPI) { 043 ship = (ShipAPI) stats.getEntity(); 044 } else { 045 return; 046 } 047 048 final String targetDataKey = ship.getId() + "_interdictor_target_data"; 049 050 Object targetDataObj = Global.getCombatEngine().getCustomData().get(targetDataKey); 051 if (state == State.IN && targetDataObj == null) { 052 ShipAPI target = findTarget(ship); 053 Global.getCombatEngine().getCustomData().put(targetDataKey, new TargetData(target)); 054 } else if (state == State.IDLE && targetDataObj != null) { 055 Global.getCombatEngine().getCustomData().remove(targetDataKey); 056 } 057 if (targetDataObj == null || ((TargetData) targetDataObj).target == null) return; 058 059 final TargetData targetData = (TargetData) targetDataObj; 060 061 //ShipAPI target = targetData.target; 062 List<ShipAPI> targets = new ArrayList<ShipAPI>(); 063 if (targetData.target.isFighter() || targetData.target.isDrone()) { 064 CombatEngineAPI engine = Global.getCombatEngine(); 065 List<ShipAPI> ships = engine.getShips(); 066 for (ShipAPI other : ships) { 067 if (other.isShuttlePod()) continue; 068 if (other.isHulk()) continue; 069 if (!other.isDrone() && !other.isFighter()) continue; 070 if (other.getOriginalOwner() != targetData.target.getOriginalOwner()) continue; 071 072 float dist = Misc.getDistance(other.getLocation(), targetData.target.getLocation()); 073 if (dist > WING_EFFECT_RANGE) continue; 074 075 targets.add(other); 076 } 077 } else { 078 targets.add(targetData.target); 079 } 080 081 boolean first = true; 082 for (ShipAPI target : targets) { 083 if (effectLevel >= 1) { 084 Color color = getEffectColor(target); 085 color = Misc.setAlpha(color, 255); 086 087 if (first) { 088 if (target.getFluxTracker().showFloaty() || 089 ship == Global.getCombatEngine().getPlayerShip() || 090 target == Global.getCombatEngine().getPlayerShip()) { 091 target.getFluxTracker().showOverloadFloatyIfNeeded("Drive Interdicted!", color, 4f, true); 092 } 093 first = false; 094 } 095 096 ShipEngineControllerAPI ec = target.getEngineController(); 097 float limit = ec.getFlameoutFraction(); 098 if (target.isDrone() || target.isFighter()) { 099 limit = 1f; 100 } 101 102 float disabledSoFar = 0f; 103 boolean disabledAnEngine = false; 104 List<ShipEngineAPI> engines = new ArrayList<ShipEngineAPI>(ec.getShipEngines()); 105 Collections.shuffle(engines); 106 107 for (ShipEngineAPI engine : engines) { 108 if (engine.isDisabled()) continue; 109 float contrib = engine.getContribution(); 110 if (disabledSoFar + contrib <= limit) { 111 engine.disable(); 112 disabledSoFar += contrib; 113 disabledAnEngine = true; 114 } 115 } 116 if (!disabledAnEngine) { 117 for (ShipEngineAPI engine : engines) { 118 if (engine.isDisabled()) continue; 119 engine.disable(); 120 break; 121 } 122 } 123 ec.computeEffectiveStats(ship == Global.getCombatEngine().getPlayerShip()); 124 } 125 126 if (effectLevel > 0) { 127 float jitterLevel = effectLevel; 128 float maxRangeBonus = 20f + target.getCollisionRadius() * 0.25f; 129 float jitterRangeBonus = jitterLevel * maxRangeBonus; 130 if (state == State.OUT) { 131 jitterRangeBonus = maxRangeBonus + (1f - jitterLevel) * maxRangeBonus; 132 } 133 target.setJitter(this, 134 //target.getSpriteAPI().getAverageColor(), 135 getEffectColor(target), 136 jitterLevel, 6, 0f, 0 + jitterRangeBonus); 137 138 if (first) { 139 ship.setJitter(this, 140 //target.getSpriteAPI().getAverageColor(), 141 getEffectColor(targetData.target), 142 jitterLevel, 6, 0f, 0 + jitterRangeBonus); 143 } 144 } 145 } 146 } 147 148 149 protected Color getEffectColor(ShipAPI ship) { 150 if (ship.getEngineController().getShipEngines().isEmpty()) { 151 return EFFECT_COLOR; 152 } 153 return Misc.setAlpha(ship.getEngineController().getShipEngines().get(0).getEngineColor(), EFFECT_COLOR.getAlpha()); 154 } 155 156 157 public void unapply(MutableShipStatsAPI stats, String id) { 158 159 } 160 161 protected ShipAPI findTarget(ShipAPI ship) { 162 FindShipFilter filter = new FindShipFilter() { 163 public boolean matches(ShipAPI ship) { 164 return !ship.getEngineController().isFlamedOut(); 165 } 166 }; 167 168 float range = getMaxRange(ship); 169 boolean player = ship == Global.getCombatEngine().getPlayerShip(); 170 ShipAPI target = ship.getShipTarget(); 171 if (target != null) { 172 float dist = Misc.getDistance(ship.getLocation(), target.getLocation()); 173 float radSum = ship.getCollisionRadius() + target.getCollisionRadius(); 174 if (dist > range + radSum) target = null; 175 } else { 176 if (target == null || target.getOwner() == ship.getOwner()) { 177 if (player) { 178 target = Misc.findClosestShipEnemyOf(ship, ship.getMouseTarget(), HullSize.FIGHTER, range, true, filter); 179 } else { 180 Object test = ship.getAIFlags().getCustom(AIFlags.MANEUVER_TARGET); 181 if (test instanceof ShipAPI) { 182 target = (ShipAPI) test; 183 float dist = Misc.getDistance(ship.getLocation(), target.getLocation()); 184 float radSum = ship.getCollisionRadius() + target.getCollisionRadius(); 185 if (dist > range + radSum) target = null; 186 } 187 } 188 } 189 if (target == null) { 190 target = Misc.findClosestShipEnemyOf(ship, ship.getLocation(), HullSize.FIGHTER, range, true, filter); 191 } 192 } 193 194 return target; 195 } 196 197 198 protected float getMaxRange(ShipAPI ship) { 199 return RANGE; 200 } 201 202 203 public StatusData getStatusData(int index, State state, float effectLevel) { 204// if (effectLevel > 0) { 205// if (index == 0) { 206// float damMult = 1f + (DAM_MULT - 1f) * effectLevel; 207// return new StatusData("" + (int)((damMult - 1f) * 100f) + "% more damage to target", false); 208// } 209// } 210 return null; 211 } 212 213 214 @Override 215 public String getInfoText(ShipSystemAPI system, ShipAPI ship) { 216 if (system.isOutOfAmmo()) return null; 217 if (system.getState() != SystemState.IDLE) return null; 218 219 ShipAPI target = findTarget(ship); 220 if (target != null && target != ship) { 221 return "READY"; 222 } 223 if (target == null && ship.getShipTarget() != null) { 224 return "OUT OF RANGE"; 225 } 226 return "NO TARGET"; 227 } 228 229 230 @Override 231 public boolean isUsable(ShipSystemAPI system, ShipAPI ship) { 232 if (system.isActive()) return true; 233 ShipAPI target = findTarget(ship); 234 return target != null && target != ship; 235 } 236 237} 238 239 240 241 242 243 244 245