001package com.fs.starfarer.api.impl.combat; 002 003import java.awt.Color; 004import java.util.List; 005 006import org.lwjgl.util.vector.Vector2f; 007 008import com.fs.starfarer.api.Global; 009import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin; 010import com.fs.starfarer.api.combat.CombatEngineAPI; 011import com.fs.starfarer.api.combat.CombatEntityAPI; 012import com.fs.starfarer.api.combat.EveryFrameCombatPlugin; 013import com.fs.starfarer.api.combat.MissileAPI; 014import com.fs.starfarer.api.combat.MutableShipStatsAPI; 015import com.fs.starfarer.api.combat.ShipAPI; 016import com.fs.starfarer.api.combat.ShipSystemAPI; 017import com.fs.starfarer.api.combat.ShipSystemAPI.SystemState; 018import com.fs.starfarer.api.combat.ShipwideAIFlags.AIFlags; 019import com.fs.starfarer.api.combat.WeaponAPI.WeaponType; 020import com.fs.starfarer.api.input.InputEventAPI; 021import com.fs.starfarer.api.util.Misc; 022import com.fs.starfarer.api.util.WeightedRandomPicker; 023 024public class MineStrikeStats extends BaseShipSystemScript implements MineStrikeStatsAIInfoProvider { 025 026 protected static float MINE_RANGE = 1000f; 027 028 public static final float MIN_SPAWN_DIST = 75f; 029 public static final float MIN_SPAWN_DIST_FRIGATE = 110f; 030 031 public static final float LIVE_TIME = 5f; 032 033 public static final Color JITTER_COLOR = new Color(255,155,255,75); 034 public static final Color JITTER_UNDER_COLOR = new Color(255,155,255,155); 035 036 037 public static float getRange(ShipAPI ship) { 038 if (ship == null) return MINE_RANGE; 039 return ship.getMutableStats().getSystemRangeBonus().computeEffective(MINE_RANGE); 040 } 041 042 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) { 043 ShipAPI ship = null; 044 //boolean player = false; 045 if (stats.getEntity() instanceof ShipAPI) { 046 ship = (ShipAPI) stats.getEntity(); 047 } else { 048 return; 049 } 050 051 052 float jitterLevel = effectLevel; 053 if (state == State.OUT) { 054 jitterLevel *= jitterLevel; 055 } 056 float maxRangeBonus = 25f; 057 float jitterRangeBonus = jitterLevel * maxRangeBonus; 058 if (state == State.OUT) { 059 } 060 061 ship.setJitterUnder(this, JITTER_UNDER_COLOR, jitterLevel, 11, 0f, 3f + jitterRangeBonus); 062 ship.setJitter(this, JITTER_COLOR, jitterLevel, 4, 0f, 0 + jitterRangeBonus); 063 064 if (state == State.IN) { 065 } else if (effectLevel >= 1) { 066 Vector2f target = ship.getMouseTarget(); 067 if (ship.getShipAI() != null && ship.getAIFlags().hasFlag(AIFlags.SYSTEM_TARGET_COORDS)){ 068 target = (Vector2f) ship.getAIFlags().getCustom(AIFlags.SYSTEM_TARGET_COORDS); 069 } 070 if (target != null) { 071 float dist = Misc.getDistance(ship.getLocation(), target); 072 float max = getMaxRange(ship) + ship.getCollisionRadius(); 073 if (dist > max) { 074 float dir = Misc.getAngleInDegrees(ship.getLocation(), target); 075 target = Misc.getUnitVectorAtDegreeAngle(dir); 076 target.scale(max); 077 Vector2f.add(target, ship.getLocation(), target); 078 } 079 080 target = findClearLocation(ship, target); 081 082 if (target != null) { 083 spawnMine(ship, target); 084 } 085 } 086 087 } else if (state == State.OUT ) { 088 } 089 } 090 091 092 public void unapply(MutableShipStatsAPI stats, String id) { 093 } 094 095 public void spawnMine(ShipAPI source, Vector2f mineLoc) { 096 CombatEngineAPI engine = Global.getCombatEngine(); 097 Vector2f currLoc = Misc.getPointAtRadius(mineLoc, 30f + (float) Math.random() * 30f); 098 //Vector2f currLoc = null; 099 float start = (float) Math.random() * 360f; 100 for (float angle = start; angle < start + 390; angle += 30f) { 101 if (angle != start) { 102 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle); 103 loc.scale(50f + (float) Math.random() * 30f); 104 currLoc = Vector2f.add(mineLoc, loc, new Vector2f()); 105 } 106 for (MissileAPI other : Global.getCombatEngine().getMissiles()) { 107 if (!other.isMine()) continue; 108 109 float dist = Misc.getDistance(currLoc, other.getLocation()); 110 if (dist < other.getCollisionRadius() + 40f) { 111 currLoc = null; 112 break; 113 } 114 } 115 if (currLoc != null) { 116 break; 117 } 118 } 119 if (currLoc == null) { 120 currLoc = Misc.getPointAtRadius(mineLoc, 30f + (float) Math.random() * 30f); 121 } 122 123 124 125 //Vector2f currLoc = mineLoc; 126 MissileAPI mine = (MissileAPI) engine.spawnProjectile(source, null, 127 "minelayer2", 128 currLoc, 129 (float) Math.random() * 360f, null); 130 if (source != null) { 131 Global.getCombatEngine().applyDamageModifiersToSpawnedProjectileWithNullWeapon( 132 source, WeaponType.MISSILE, false, mine.getDamage()); 133// float extraDamageMult = source.getMutableStats().getMissileWeaponDamageMult().getModifiedValue(); 134// mine.getDamage().setMultiplier(mine.getDamage().getMultiplier() * extraDamageMult); 135 } 136 137 138 float fadeInTime = 0.5f; 139 mine.getVelocity().scale(0); 140 mine.fadeOutThenIn(fadeInTime); 141 142 Global.getCombatEngine().addPlugin(createMissileJitterPlugin(mine, fadeInTime)); 143 144 //mine.setFlightTime((float) Math.random()); 145 float liveTime = LIVE_TIME; 146 //liveTime = 0.01f; 147 mine.setFlightTime(mine.getMaxFlightTime() - liveTime); 148 149 Global.getSoundPlayer().playSound("mine_teleport", 1f, 1f, mine.getLocation(), mine.getVelocity()); 150 } 151 152 protected EveryFrameCombatPlugin createMissileJitterPlugin(final MissileAPI mine, final float fadeInTime) { 153 return new BaseEveryFrameCombatPlugin() { 154 float elapsed = 0f; 155 @Override 156 public void advance(float amount, List<InputEventAPI> events) { 157 if (Global.getCombatEngine().isPaused()) return; 158 159 elapsed += amount; 160 161 float jitterLevel = mine.getCurrentBaseAlpha(); 162 if (jitterLevel < 0.5f) { 163 jitterLevel *= 2f; 164 } else { 165 jitterLevel = (1f - jitterLevel) * 2f; 166 } 167 168 float jitterRange = 1f - mine.getCurrentBaseAlpha(); 169 //jitterRange = (float) Math.sqrt(jitterRange); 170 float maxRangeBonus = 50f; 171 float jitterRangeBonus = jitterRange * maxRangeBonus; 172 Color c = JITTER_UNDER_COLOR; 173 c = Misc.setAlpha(c, 70); 174 //mine.setJitter(this, c, jitterLevel, 15, jitterRangeBonus * 0.1f, jitterRangeBonus); 175 mine.setJitter(this, c, jitterLevel, 15, jitterRangeBonus * 0, jitterRangeBonus); 176 177 if (jitterLevel >= 1 || elapsed > fadeInTime) { 178 Global.getCombatEngine().removePlugin(this); 179 } 180 } 181 }; 182 } 183 184 185 protected float getMaxRange(ShipAPI ship) { 186 return getMineRange(ship); 187 } 188 189 190 @Override 191 public String getInfoText(ShipSystemAPI system, ShipAPI ship) { 192 if (system.isOutOfAmmo()) return null; 193 if (system.getState() != SystemState.IDLE) return null; 194 195 Vector2f target = ship.getMouseTarget(); 196 if (target != null) { 197 float dist = Misc.getDistance(ship.getLocation(), target); 198 float max = getMaxRange(ship) + ship.getCollisionRadius(); 199 if (dist > max) { 200 return "OUT OF RANGE"; 201 } else { 202 return "READY"; 203 } 204 } 205 return null; 206 } 207 208 209 @Override 210 public boolean isUsable(ShipSystemAPI system, ShipAPI ship) { 211 return ship.getMouseTarget() != null; 212 } 213 214 215 private Vector2f findClearLocation(ShipAPI ship, Vector2f dest) { 216 if (isLocationClear(dest)) return dest; 217 218 float incr = 50f; 219 220 WeightedRandomPicker<Vector2f> tested = new WeightedRandomPicker<Vector2f>(); 221 for (float distIndex = 1; distIndex <= 32f; distIndex *= 2f) { 222 float start = (float) Math.random() * 360f; 223 for (float angle = start; angle < start + 360; angle += 60f) { 224 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle); 225 loc.scale(incr * distIndex); 226 Vector2f.add(dest, loc, loc); 227 tested.add(loc); 228 if (isLocationClear(loc)) { 229 return loc; 230 } 231 } 232 } 233 234 if (tested.isEmpty()) return dest; // shouldn't happen 235 236 return tested.pick(); 237 } 238 239 private boolean isLocationClear(Vector2f loc) { 240 for (ShipAPI other : Global.getCombatEngine().getShips()) { 241 if (other.isShuttlePod()) continue; 242 if (other.isFighter()) continue; 243 244// Vector2f otherLoc = other.getLocation(); 245// float otherR = other.getCollisionRadius(); 246 247// if (other.isPiece()) { 248// System.out.println("ewfewfewfwe"); 249// } 250 Vector2f otherLoc = other.getShieldCenterEvenIfNoShield(); 251 float otherR = other.getShieldRadiusEvenIfNoShield(); 252 if (other.isPiece()) { 253 otherLoc = other.getLocation(); 254 otherR = other.getCollisionRadius(); 255 } 256 257 258// float dist = Misc.getDistance(loc, other.getLocation()); 259// float r = other.getCollisionRadius(); 260 float dist = Misc.getDistance(loc, otherLoc); 261 float r = otherR; 262 //r = Math.min(r, Misc.getTargetingRadius(loc, other, false) + r * 0.25f); 263 float checkDist = MIN_SPAWN_DIST; 264 if (other.isFrigate()) checkDist = MIN_SPAWN_DIST_FRIGATE; 265 if (dist < r + checkDist) { 266 return false; 267 } 268 } 269 for (CombatEntityAPI other : Global.getCombatEngine().getAsteroids()) { 270 float dist = Misc.getDistance(loc, other.getLocation()); 271 if (dist < other.getCollisionRadius() + MIN_SPAWN_DIST) { 272 return false; 273 } 274 } 275 276 return true; 277 } 278 279 280 public float getFuseTime() { 281 return 3f; 282 } 283 284 285 public float getMineRange(ShipAPI ship) { 286 return getRange(ship); 287 //return MINE_RANGE; 288 } 289 290 291} 292 293 294 295 296 297 298 299