001package com.fs.starfarer.api.impl.combat.dweller; 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.CombatEngineAPI; 009import com.fs.starfarer.api.combat.CombatEntityAPI; 010import com.fs.starfarer.api.combat.EmpArcEntityAPI; 011import com.fs.starfarer.api.combat.EmpArcEntityAPI.EmpArcParams; 012import com.fs.starfarer.api.combat.ShipAPI; 013import com.fs.starfarer.api.impl.combat.RiftLanceEffect; 014import com.fs.starfarer.api.impl.combat.threat.RoilingSwarmEffect; 015import com.fs.starfarer.api.util.IntervalUtil; 016import com.fs.starfarer.api.util.Misc; 017 018public class DwellerShroud extends RoilingSwarmEffect { 019 020 public static Color SHROUD_COLOR = new Color(100, 0, 25, 255); 021 public static Color SHROUD_GLOW_COLOR = new Color(150, 0, 30, 255); 022 public static Color SHROUD_OVERLOAD_FRINGE_COLOR = new Color(150, 0, 0, 255); 023 024 public static interface ShroudNegativeParticleFilter { 025 boolean isParticleOk(DwellerShroud shroud, Vector2f loc); 026 } 027 028 public static class DwellerShroudParams extends RoilingSwarmParams { 029 public float overloadGlowSizeMult = 1f; 030 public float overloadArcThickness = 40f; 031 public float overloadArcCoreThickness = 20f; 032 public Color overloadArcFringeColor = SHROUD_OVERLOAD_FRINGE_COLOR; 033 public float overloadArcGenRate = 0.25f; 034 public float overloadArcOffsetMult = 1f; 035 036 public float negativeParticleGenRate = 1f; 037 public float negativeParticleSizeMult = 1f; 038 public float negativeParticleVelMult = 1f; 039 public float negativeParticleDurMult = 1f; 040 public float negativeParticleSpeedCapMult = 1.5f; 041 public float negativeParticleSpeedCap = 10000f; 042 public float negativeParticleAreaMult = 1f; 043 public float negativeParticleClearCenterAreaRadius = 0f; 044 public boolean negativeParticleHighContrastMode = false; 045 public int negativeParticleNumBase = 11; 046 public int negativeParticleNumOverloaded = 5; 047 public int negativeParticleAlphaIntOverride = -1; 048 public Color negativeParticleColorOverride = null; 049 public ShroudNegativeParticleFilter negativeParticleFilter = null; 050 } 051 052 053 public static DwellerShroud getShroudFor(CombatEntityAPI entity) { 054 RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(entity); 055 if (swarm instanceof DwellerShroud) { 056 return (DwellerShroud) swarm; 057 } 058 return null; 059 } 060 061 public static DwellerShroudParams createBaselineParams(CombatEntityAPI attachedTo) { 062 if (!(attachedTo instanceof ShipAPI)) { 063 return null; 064 } 065 066 ShipAPI ship = (ShipAPI) attachedTo; 067 DwellerShroudParams params = new DwellerShroudParams(); 068 float radius = 20f; 069 int numMembers = 50; 070 071 072// "fx_particles1":"graphics/fx/fx_clouds00.png", 073// "fx_particles2":"graphics/fx/fx_clouds01.png", 074// "nebula_particles":"graphics/fx/nebula_colorless.png", 075// "nebula_particles2":"graphics/fx/cleaner_clouds00.png", 076// "dust_particles":"graphics/fx/dust_clouds_colorless.png", 077 078// params.spriteKey = "dust_particles"; 079// params.spriteKey = "nebula_particles"; 080// params.spriteKey = "fx_particles1"; 081 082 params.spriteCat = "dweller"; 083 params.spriteKey = "dweller_pieces"; 084 085 params.despawnSound = null; // no free-flying swarms, all are ships that have an explosion sound 086 087 params.baseDur = 1f; 088 params.durRange = 2f; 089 params.memberRespawnRate = 100f; 090 091 params.memberExchangeClass = null; 092 params.flockingClass = null; 093 params.maxSpeed = ship.getMaxSpeedWithoutBoost() + 094 Math.max(ship.getMaxSpeedWithoutBoost() * 0.25f + 50f, 100f); 095 096 params.baseSpriteSize = 256f; 097 params.baseSpriteSize = 128f * 1.5f * 0.67f; 098 params.maxTurnRate = 120f; 099 100 numMembers = 100; 101 radius = 150f; 102 103// radius = 100; 104// numMembers = 40; 105 106 params.flashCoreRadiusMult = 0f; 107 //params.flashRadius = 0f; 108 params.flashRadius = 300f; 109 params.flashRadius = 150f; 110 params.renderFlashOnSameLayer = true; 111 params.flashRateMult = 0.25f; 112 //params.flashFrequency = 10f; 113 //params.flashFrequency = 20f; 114 //params.flashFrequency = 40f; 115 params.flashFrequency = 17f; 116 params.numToFlash = 2; 117 //params.flashFrequency = 50f; 118 params.flashProbability = 1f; 119 120 params.swarmLeadsByFractionOfVelocity = 0f; 121 122 params.alphaMult = 1f; 123 params.alphaMultBase = 1f; 124 params.alphaMultFlash = 1f; 125 126// params.alphaMult = 0.25f; 127// params.negativeParticleGenRate = 0f; 128 129 //params.color = RiftCascadeEffect.EXPLOSION_UNDERCOLOR; 130 params.color = SHROUD_COLOR; 131 params.flashFringeColor = SHROUD_GLOW_COLOR; 132 133// params.color = new Color(121, 56, 171, 255); 134// params.color = Misc.setBrightness(params.color, 200); 135// //params.flashFringeColor = new Color(7, 163, 169, 255); 136// params.flashFringeColor = params.color; 137// params.flashFringeColor = Misc.setBrightness(params.flashFringeColor, 250); 138 139 params.flashCoreColor = Misc.setBrightness(params.color, 255); 140 141 142 //params.despawnDist = params.maxOffset + 300f; 143 144 params.maxOffset = radius; 145 params.initialMembers = numMembers; 146 params.baseMembersToMaintain = params.initialMembers; 147 148 return params; 149 } 150 151 152 153 154 protected IntervalUtil interval = new IntervalUtil(0.075f, 0.125f); 155 protected IntervalUtil overloadInterval = new IntervalUtil(0.075f, 0.125f); 156 //protected IntervalUtil ventingInterval = new IntervalUtil(0.075f, 0.125f); 157 protected ShipAPI ship; 158 protected DwellerShroudParams shroudParams; 159 160 161 public DwellerShroud(CombatEntityAPI attachedTo) { 162 this(attachedTo, createBaselineParams(attachedTo)); 163 } 164 public DwellerShroud(CombatEntityAPI attachedTo, DwellerShroudParams params) { 165 super(attachedTo, params); 166 this.shroudParams = params; 167 if (attachedTo instanceof ShipAPI) { 168 ship = (ShipAPI) attachedTo; 169 } 170 } 171 172 173 174 @Override 175 public int getNumMembersToMaintain() { 176 return super.getNumMembersToMaintain(); 177 } 178 179 @Override 180 public void advance(float amount) { 181 super.advance(amount); 182 183 boolean venting = ship.getFluxTracker().isVenting(); 184 boolean overloaded = ship.getFluxTracker().isOverloaded() || venting; 185// overloaded = true; 186// overloaded = !ship.getShield().isOn(); 187 188 if (overloaded) { 189 params.springStretchMult = 1f; 190 params.flashProbability = 0.25f; 191 params.despawnDist = ship.getCollisionRadius(); 192 //params.alphaMult = 0.1f; 193 } else { 194 params.springStretchMult = 10f; 195 params.flashProbability = 1f; 196 params.despawnDist = 0f; 197 //params.alphaMult = 1f; 198 } 199 200 201// ventingInterval.advance(amount * 1f); 202// if (ventingInterval.intervalElapsed() && ship != null && venting) { 203// 204// } 205 206 float empArcGenRate = shroudParams.overloadArcGenRate; 207 overloadInterval.advance(amount * empArcGenRate * 1f); 208 if (overloadInterval.intervalElapsed() && ship != null && overloaded && !isDespawning()) { 209 EmpArcParams params = new EmpArcParams(); 210 params.segmentLengthMult = 4f; 211 params.glowSizeMult = 5f + ship.getFluxLevel() * 2f; 212 params.glowSizeMult *= shroudParams.overloadGlowSizeMult; 213 //params.zigZagReductionFactor = 0f; 214 //params.brightSpotFadeFraction = 0.33f; 215 //params.brightSpotFullFraction = 0.5f; 216 //params.movementDurMax = 0.2f; 217 //params.flickerRateMult = overloadRate; 218 params.flickerRateMult = 0.5f + (float) Math.random() * 0.5f; 219 220 Color fringe = this.params.flashFringeColor; 221 //fringe = Misc.setAlpha(fringe, 127); 222// fringe = Misc.scaleColor(fringe, 0.75f); 223 fringe = shroudParams.overloadArcFringeColor; 224 //fringe = new Color(240,255,0,255); 225 Color core = Color.white; 226 227 float thickness = shroudParams.overloadArcThickness; 228 229 Vector2f loc = ship.getLocation(); 230 float r = this.params.maxOffset; 231 r = r * 0.5f + r * 0.5f * (float) Math.random(); 232 //r *= 1.5f; 233 r *= shroudParams.overloadArcOffsetMult; 234 Vector2f from = Misc.getPointAtRadius(loc, r); 235 float angle = Misc.getAngleInDegrees(from, loc); 236 angle = angle + 90f * ((float) Math.random() - 0.5f); 237 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(angle); 238 float dist = this.params.maxOffset; 239 dist = dist * 0.5f + dist * 0.5f * (float) Math.random(); 240 dist *= 1.5f; 241 dist *= shroudParams.overloadArcOffsetMult; 242 dir.scale(dist); 243 Vector2f to = Vector2f.add(from, dir, new Vector2f()); 244 245// float minBright = 200f; 246// if (dist * params.brightSpotFullFraction < minBright) { 247// params.brightSpotFullFraction = minBright / Math.max(minBright, dist); 248// } 249 250 CombatEngineAPI engine = Global.getCombatEngine(); 251 EmpArcEntityAPI arc = (EmpArcEntityAPI)engine.spawnEmpArcVisual( 252 from, ship, to, ship, thickness, fringe, core, params); 253 254 arc.setCoreWidthOverride(shroudParams.overloadArcCoreThickness); 255 256 Global.getSoundPlayer().playSound("dweller_venting_or_overloaded", 1f, 1f, to, ship.getVelocity()); 257 //arc.setSingleFlickerMode(true); 258 //arc.setSingleFlickerMode(false); 259 } 260 261 //params.alphaMult = 1f; 262 //params.alphaMult = 0f; 263 interval.advance(amount * shroudParams.negativeParticleGenRate); 264 if (interval.intervalElapsed()) { 265 CombatEngineAPI engine = Global.getCombatEngine(); 266 267 boolean smallerDark = false; 268 //smallerDark = true; 269 Color c = RiftLanceEffect.getColorForDarkening(params.color); 270 c = Misc.setAlpha(c, 100); 271 int num = shroudParams.negativeParticleNumBase; 272 if (smallerDark) num = 8; 273 if (overloaded) { 274 c = Misc.setAlpha(c, 150); 275 num = shroudParams.negativeParticleNumOverloaded; 276 if (smallerDark) num = 4; 277 } 278 if (shroudParams.negativeParticleHighContrastMode) { 279 c = Misc.setAlpha(c, 150); 280 } 281 if (shroudParams.negativeParticleColorOverride != null) { 282 c = shroudParams.negativeParticleColorOverride; 283 } 284 if (shroudParams.negativeParticleAlphaIntOverride >= 0) { 285 c = Misc.setAlpha(c, shroudParams.negativeParticleAlphaIntOverride); 286 } 287 288 float baseDuration = 2f; 289 Vector2f vel = new Vector2f(attachedTo.getVelocity()); 290 float speed = vel.length(); 291 if (attachedTo instanceof ShipAPI) { 292 float maxSpeed = ((ShipAPI)attachedTo).getMaxSpeed() * shroudParams.negativeParticleSpeedCapMult; 293 maxSpeed = Math.min(maxSpeed, shroudParams.negativeParticleSpeedCap); 294 if (speed > maxSpeed && speed > 1f) { 295 vel.scale(maxSpeed / speed); 296 } 297 } 298 299 float baseSize = params.maxOffset * 2f; 300 //baseSize = params.maxOffset * 1f; 301 302 //float size = ship.getCollisionRadius() * 0.35f; 303 float size = baseSize * 0.33f; 304 305 float extraDur = 0f; 306 307 // so that switching the view to another ship near a dweller part 308 // doesn't result in it not having negative particles 309 Global.getCombatEngine().getViewport().setEverythingNearViewport(true); 310 311 //for (int i = 0; i < 3; i++) { 312 for (int i = 0; i < num; i++) { 313 //for (int i = 0; i < 7; i++) { 314 Vector2f point = new Vector2f(attachedTo.getLocation()); 315 float min = shroudParams.negativeParticleClearCenterAreaRadius; 316 if (min > 0) { 317 point = Misc.getPointWithinRadiusUniform(point, min, 318 Math.max(min, baseSize * 0.75f * (smallerDark ? 0.85f : 1f) * shroudParams.negativeParticleAreaMult), Misc.random); 319 } else { 320 point = Misc.getPointWithinRadiusUniform(point, 321 baseSize * 0.75f * (smallerDark ? 0.85f : 1f) * shroudParams.negativeParticleAreaMult, Misc.random); 322 } 323 324 float dur = baseDuration + baseDuration * (float) Math.random(); 325 dur += extraDur; 326 float nSize = size; 327 Vector2f pt = Misc.getPointWithinRadius(point, nSize * 0.5f); 328 Vector2f v = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f); 329 v.scale(nSize + nSize * (float) Math.random() * 0.5f); 330 v.scale(0.2f * shroudParams.negativeParticleVelMult); 331 Vector2f.add(vel, v, v); 332 333 float maxSpeed = nSize * 1.5f * 0.2f; 334 float minSpeed = nSize * 1f * 0.2f; 335 float overMin = v.length() - minSpeed; 336 if (overMin > 0) { 337 float durMult = 1f - overMin / (maxSpeed - minSpeed); 338 if (durMult < 0.1f) durMult = 0.1f; 339 dur *= 0.5f + 0.5f * durMult; 340 } 341 342 dur *= shroudParams.negativeParticleDurMult; 343 344 //nSize *= 1.5f; 345 346 if (shroudParams.negativeParticleFilter != null && 347 !shroudParams.negativeParticleFilter.isParticleOk(this, pt)) { 348 continue; 349 } 350 351 engine.addNegativeNebulaParticle(pt, v, nSize * 1f * shroudParams.negativeParticleSizeMult, 2f, 352 0.5f / dur, 0f, dur, c); 353 } 354 Global.getCombatEngine().getViewport().setEverythingNearViewport(false); 355 } 356 357 } 358 359 public DwellerShroudParams getShroudParams() { 360 return shroudParams; 361 } 362}