001package com.fs.starfarer.api.impl.combat; 002 003import java.awt.Color; 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007 008import org.lwjgl.util.vector.Vector2f; 009 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.characters.PersonAPI; 012import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin; 013import com.fs.starfarer.api.combat.CollisionClass; 014import com.fs.starfarer.api.combat.CombatEngineAPI; 015import com.fs.starfarer.api.combat.CombatEntityAPI; 016import com.fs.starfarer.api.combat.CombatFleetManagerAPI; 017import com.fs.starfarer.api.combat.MutableShipStatsAPI; 018import com.fs.starfarer.api.combat.ShipAPI; 019import com.fs.starfarer.api.combat.ShipCommand; 020import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI; 021import com.fs.starfarer.api.combat.listeners.DamageListener; 022import com.fs.starfarer.api.impl.campaign.ids.Personalities; 023import com.fs.starfarer.api.impl.campaign.ids.Skills; 024import com.fs.starfarer.api.impl.hullmods.ShardSpawner; 025import com.fs.starfarer.api.input.InputEventAPI; 026import com.fs.starfarer.api.loading.FighterWingSpecAPI; 027import com.fs.starfarer.api.util.Misc; 028 029public class ChiralFigmentStats extends BaseShipSystemScript { 030 031 public static Map<String, String> FIGMENTS = new HashMap<String, String>(); 032 static { 033 FIGMENTS.put("shard_left_Attack", "shard_right_Attack"); 034 FIGMENTS.put("shard_left_Attack2", "shard_right_Attack"); 035 FIGMENTS.put("shard_right_Attack", "shard_left_Attack"); 036 FIGMENTS.put("shard_left_Armorbreaker", "shard_right_Attack"); 037 FIGMENTS.put("shard_left_Shieldbreaker", "shard_right_Shieldbreaker"); 038 FIGMENTS.put("shard_right_Shieldbreaker", "shard_left_Shieldbreaker"); 039 FIGMENTS.put("shard_left_Defense", "shard_right_Shock"); 040 FIGMENTS.put("shard_right_Shock", "shard_left_Defense"); 041 FIGMENTS.put("shard_left_Missile", "shard_right_Missile"); 042 FIGMENTS.put("shard_right_Missile", "shard_left_Missile"); 043 } 044 045 public static final Color JITTER_COLOR = ShardSpawner.JITTER_COLOR; 046 public static final Color JITTER_UNDER_COLOR = Misc.setAlpha(JITTER_COLOR, 155); 047 048 049 public static float DAMAGE_TAKEN_MULT = 1f; 050 public static float JITTER_PER_DAMAGE_DIVISOR = 100f; 051 public static float JITTER_PER_DAMAGE_DECAY_SECONDS = 1f; 052 053 054 public static float getSpawnAngle(ShipAPI ship) { 055 String variant = FIGMENTS.get(ship.getVariant().getHullVariantId()); 056 if (ship.getVariant().getOriginalVariant() != null && variant == null) { 057 variant = FIGMENTS.get(ship.getVariant().getOriginalVariant()); 058 } 059 if (variant != null && variant.contains("_left")) { 060 return 90f; 061 } 062 return -90f; 063 } 064 065 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) { 066 ShipAPI ship = null; 067 //boolean player = false; 068 if (stats.getEntity() instanceof ShipAPI) { 069 ship = (ShipAPI) stats.getEntity(); 070 } else { 071 return; 072 } 073 074 075 float jitterLevel = effectLevel; 076 if (state == State.OUT) { 077 jitterLevel *= jitterLevel; 078 } 079 float maxRangeBonus = 25f; 080 float jitterRangeBonus = jitterLevel * maxRangeBonus; 081 if (state == State.OUT) { 082 } 083 084 ship.setJitterUnder(this, JITTER_UNDER_COLOR, jitterLevel, 11, 0f, 3f + jitterRangeBonus); 085 ship.setJitter(this, JITTER_COLOR, jitterLevel, 4, 0f, 0 + jitterRangeBonus); 086 087 if (state == State.IN) { 088 } else if (effectLevel >= 1) { 089 String variant = FIGMENTS.get(ship.getVariant().getHullVariantId()); 090 if (ship.getVariant().getOriginalVariant() != null && variant == null) { 091 variant = FIGMENTS.get(ship.getVariant().getOriginalVariant()); 092 } 093 if (variant != null) { 094 float angle = getSpawnAngle(ship); 095 float dist = ship.getCollisionRadius() * 1.25f; 096 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(ship.getFacing() + angle); 097 loc.scale(dist); 098 Vector2f.add(loc, ship.getLocation(), loc); 099 if (isLocationClear(ship, loc, dist / 2f)) { 100 FigmentPlugin figment = new FigmentPlugin(variant, ship, 1f, 20f, loc); 101 Global.getCombatEngine().addPlugin(figment); 102 } 103 } 104 } else if (state == State.OUT ) { 105 } 106 } 107 108 109 public void unapply(MutableShipStatsAPI stats, String id) { 110 } 111 112 113 private boolean isLocationClear(ShipAPI ship, Vector2f loc, float minDist) { 114 for (ShipAPI other : Global.getCombatEngine().getShips()) { 115 if (other.isShuttlePod()) continue; 116 if (other.isFighter()) continue; 117 if (other == ship) continue; 118 119 Vector2f otherLoc = other.getShieldCenterEvenIfNoShield(); 120 float otherR = other.getShieldRadiusEvenIfNoShield(); 121 122 123 float dist = Misc.getDistance(loc, otherLoc); 124 float r = otherR; 125 if (dist < r + minDist) { 126 return false; 127 } 128 } 129 for (CombatEntityAPI other : Global.getCombatEngine().getAsteroids()) { 130 float dist = Misc.getDistance(loc, other.getLocation()); 131 if (dist < other.getCollisionRadius() + minDist) { 132 return false; 133 } 134 } 135 136 return true; 137 } 138 139 public static class FigmentDamageListener implements DamageListener { 140 public FigmentPlugin plugin; 141 public FigmentDamageListener(FigmentPlugin plugin) { 142 this.plugin = plugin; 143 } 144 145 public void reportDamageApplied(Object source, CombatEntityAPI target, ApplyDamageResultAPI result) { 146 float totalDamage = result.getDamageToHull() + result.getDamageToShields() + result.getTotalDamageToArmor(); 147 //plugin.recentHits += totalDamage / JITTER_PER_DAMAGE_DIVISOR; 148 float max = 1f; 149 if (result.isDps()) max = 0.1f; 150 plugin.recentHits += Math.min(max, totalDamage / 10f); 151 } 152 } 153 public static class FigmentPlugin extends BaseEveryFrameCombatPlugin { 154 float elapsed = 0f; 155 ShipAPI [] ships = null; 156 CollisionClass collisionClass; 157 158 String variantId; 159 ShipAPI source; 160 float fadeInTime; 161 Vector2f loc; 162 float dur; 163 164 boolean finishedFadingIn = false; 165 166 float recentHits = 0f; 167 168 public FigmentPlugin(String variantId, ShipAPI source, float fadeInTime, float dur, Vector2f loc) { 169 this.variantId = variantId; 170 this.source = source; 171 this.fadeInTime = fadeInTime; 172 this.dur = dur; 173 this.loc = loc; 174 } 175 176 177 @Override 178 public void advance(float amount, List<InputEventAPI> events) { 179 if (Global.getCombatEngine().isPaused()) return; 180 181 elapsed += amount; 182 183 CombatEngineAPI engine = Global.getCombatEngine(); 184 185 if (ships == null) { 186 float facing = source.getFacing() + 15f * ((float) Math.random() - 0.5f); 187 CombatFleetManagerAPI fleetManager = engine.getFleetManager(source.getOriginalOwner()); 188 boolean wasSuppressed = fleetManager.isSuppressDeploymentMessages(); 189 fleetManager.setSuppressDeploymentMessages(true); 190 if (variantId.endsWith("_wing")) { 191 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(variantId); 192 ships = new ShipAPI[spec.getNumFighters()]; 193 PersonAPI captain = Global.getSettings().createPerson(); 194 captain.setPersonality(Personalities.RECKLESS); // doesn't matter for fighters 195 captain.getStats().setSkillLevel(Skills.POINT_DEFENSE, 2); 196 captain.getStats().setSkillLevel(Skills.GUNNERY_IMPLANTS, 2); 197 captain.getStats().setSkillLevel(Skills.IMPACT_MITIGATION, 2); 198 ShipAPI leader = engine.getFleetManager(source.getOriginalOwner()).spawnShipOrWing(variantId, loc, facing, 0f, captain); 199 for (int i = 0; i < ships.length; i++) { 200 ships[i] = leader.getWing().getWingMembers().get(i); 201 ships[i].getLocation().set(loc); 202 } 203 collisionClass = ships[0].getCollisionClass(); 204 } else { 205 ships = new ShipAPI[1]; 206 ships[0] = engine.getFleetManager(source.getOriginalOwner()).spawnShipOrWing(variantId, loc, facing, 0f, source.getOriginalCaptain()); 207 } 208 fleetManager.setSuppressDeploymentMessages(wasSuppressed); 209 collisionClass = ships[0].getCollisionClass(); 210 } 211 212 213 float progress = elapsed / fadeInTime; 214 215 float maxAlpha = 0.67f; 216 if (progress <= 1f) { 217 for (int i = 0; i < ships.length; i++) { 218 ShipAPI ship = ships[i]; 219 ship.setAlphaMult(progress * maxAlpha); 220 221 if (progress < 0.5f) { 222 ship.blockCommandForOneFrame(ShipCommand.ACCELERATE); 223 ship.blockCommandForOneFrame(ShipCommand.TURN_LEFT); 224 ship.blockCommandForOneFrame(ShipCommand.TURN_RIGHT); 225 ship.blockCommandForOneFrame(ShipCommand.STRAFE_LEFT); 226 ship.blockCommandForOneFrame(ShipCommand.STRAFE_RIGHT); 227 } 228 229 ship.blockCommandForOneFrame(ShipCommand.USE_SYSTEM); 230 ship.blockCommandForOneFrame(ShipCommand.TOGGLE_SHIELD_OR_PHASE_CLOAK); 231 ship.blockCommandForOneFrame(ShipCommand.FIRE); 232 ship.blockCommandForOneFrame(ShipCommand.PULL_BACK_FIGHTERS); 233 ship.blockCommandForOneFrame(ShipCommand.VENT_FLUX); 234 ship.setHoldFireOneFrame(true); 235 ship.setHoldFire(true); 236 237 238 ship.setCollisionClass(CollisionClass.NONE); 239 ship.getMutableStats().getHullDamageTakenMult().modifyMult("FigmentInvuln", 0f); 240 if (progress < 0.5f) { 241 ship.getVelocity().set(source.getVelocity()); 242 } else if (progress > 0.75f){ 243 ship.setCollisionClass(collisionClass); 244 ship.getMutableStats().getHullDamageTakenMult().unmodifyMult("FigmentInvuln"); 245 } 246 247 float jitterLevel = progress; 248// if (jitterLevel < 0.5f) { 249// jitterLevel *= 2f; 250// } else { 251// jitterLevel = (1f - jitterLevel) * 2f; 252// } 253 254 float jitterRange = 1f - progress * 0.5f; 255 float maxRangeBonus = 50f; 256 maxRangeBonus -= 30f * progress; 257 float jitterRangeBonus = jitterRange * maxRangeBonus; 258 Color c = JITTER_COLOR; 259 float num = 18f * (1f - progress * progress) + 7f; 260 261 ship.setJitter(this, c, jitterLevel, (int)Math.round(num), 0f, jitterRangeBonus); 262 } 263 } 264 265 if (elapsed > fadeInTime && !finishedFadingIn) { 266 for (int i = 0; i < ships.length; i++) { 267 ShipAPI ship = ships[i]; 268 ship.setAlphaMult(maxAlpha); 269 ship.setHoldFire(false); 270 ship.setCollisionClass(collisionClass); 271 ship.getMutableStats().getHullDamageTakenMult().unmodifyMult("FigmentInvuln"); 272 273 DAMAGE_TAKEN_MULT = 1f; 274 ship.getMutableStats().getHullDamageTakenMult().modifyMult("FigmentExtraDamage", DAMAGE_TAKEN_MULT); 275 ship.getMutableStats().getShieldDamageTakenMult().modifyMult("FigmentExtraDamage", DAMAGE_TAKEN_MULT); 276 ship.getMutableStats().getArmorDamageTakenMult().modifyMult("FigmentExtraDamage", DAMAGE_TAKEN_MULT); 277 278 ship.addListener(new FigmentDamageListener(this)); 279 } 280 finishedFadingIn = true; 281 } 282 283 if (elapsed > fadeInTime) { 284 for (int i = 0; i < ships.length; i++) { 285 ShipAPI ship = ships[i]; 286 287 if (!ship.isAlive() || !engine.isInPlay(ship)) { 288 engine.removePlugin(this); 289 return; 290 } 291 292 ship.setAlphaMult(maxAlpha); 293 ship.blockCommandForOneFrame(ShipCommand.USE_SYSTEM); 294 ship.blockCommandForOneFrame(ShipCommand.TOGGLE_SHIELD_OR_PHASE_CLOAK); 295 296 297 //float minus = recentHits * amount / JITTER_PER_DAMAGE_DECAY_SECONDS; 298 float minus = recentHits * amount * 2f; 299 minus += 0.1f * amount; 300 recentHits -= minus; 301 if (recentHits < 0) recentHits = 0; 302 if (recentHits > 5f) recentHits = 5f; 303 304 float jitterLevel = 1f; 305 float jitterRange = 0.5f; 306 float maxRangeBonus = 50f; 307 maxRangeBonus = 20f; 308 maxRangeBonus += recentHits * 40f; 309 float jitterRangeBonus = jitterRange * maxRangeBonus; 310 Color c = JITTER_COLOR; 311 312 float numCopies = 7f; 313 numCopies += recentHits * 2f; 314 315 ship.setJitter(this, c, jitterLevel, (int) Math.round(numCopies), 0f, jitterRangeBonus); 316 } 317 } 318 319 if (elapsed > dur) { 320 // destroy ship here? 321 //engine.removePlugin(this); 322 } 323 } 324 } 325 326} 327 328 329 330 331 332 333 334