001package com.fs.starfarer.api.impl.combat; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.EnumSet; 006import java.util.List; 007 008import org.lwjgl.util.vector.Vector2f; 009 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin; 012import com.fs.starfarer.api.combat.CombatEngineAPI; 013import com.fs.starfarer.api.combat.CombatEngineLayers; 014import com.fs.starfarer.api.combat.CombatEntityAPI; 015import com.fs.starfarer.api.combat.DamagingProjectileAPI; 016import com.fs.starfarer.api.combat.OnFireEffectPlugin; 017import com.fs.starfarer.api.combat.OnHitEffectPlugin; 018import com.fs.starfarer.api.combat.ShipAPI; 019import com.fs.starfarer.api.combat.ViewportAPI; 020import com.fs.starfarer.api.combat.WeaponAPI; 021import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI; 022import com.fs.starfarer.api.graphics.SpriteAPI; 023import com.fs.starfarer.api.util.FaderUtil; 024import com.fs.starfarer.api.util.Misc; 025 026public class CryoblasterEffect extends BaseCombatLayeredRenderingPlugin implements OnFireEffectPlugin, 027 OnHitEffectPlugin { 028 029 public CryoblasterEffect() { 030 } 031 032 public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine) { 033 Color color = projectile.getProjectileSpec().getFringeColor(); 034 color = Misc.setAlpha(color, 100); 035 036 Vector2f vel = new Vector2f(); 037 if (target instanceof ShipAPI) { 038 vel.set(target.getVelocity()); 039 } 040 041 float sizeMult = Misc.getHitGlowSize(100f, projectile.getDamage().getBaseDamage(), damageResult) / 100f; 042 043 for (int i = 0; i < 7; i++) { 044 //float size = projectile.getProjectileSpec().getWidth() * (0.75f + (float) Math.random() * 0.5f); 045 float size = 40f * (0.75f + (float) Math.random() * 0.5f); 046 047 float dur = 1f; 048 //dur = 0.25f; 049 float rampUp = 0f; 050 Color c = Misc.scaleAlpha(color, projectile.getBrightness()); 051 engine.addNebulaParticle(point, vel, size, 5f + 3f * sizeMult, 052 rampUp, 0f, dur, c, true); 053 } 054 } 055 056 public void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) { 057 CryoblasterEffect trail = new CryoblasterEffect(projectile); 058 CombatEntityAPI e = engine.addLayeredRenderingPlugin(trail); 059 e.getLocation().set(projectile.getLocation()); 060 } 061 062 063 public static class ParticleData { 064 public SpriteAPI sprite; 065 public Vector2f offset = new Vector2f(); 066 public Vector2f vel = new Vector2f(); 067 public float scale = 1f; 068 public DamagingProjectileAPI proj; 069 public float scaleIncreaseRate = 1f; 070 public float turnDir = 1f; 071 public float angle = 1f; 072 073 public float maxDur; 074 public Vector2f origVel; 075 public FaderUtil fader; 076 public Vector2f dirVelChange; 077 078 public ParticleData(DamagingProjectileAPI proj) { 079 this.proj = proj; 080 sprite = Global.getSettings().getSprite("misc", "nebula_particles"); 081 //sprite = Global.getSettings().getSprite("misc", "dust_particles"); 082 float i = Misc.random.nextInt(4); 083 float j = Misc.random.nextInt(4); 084 sprite.setTexWidth(0.25f); 085 sprite.setTexHeight(0.25f); 086 sprite.setTexX(i * 0.25f); 087 sprite.setTexY(j * 0.25f); 088 sprite.setAdditiveBlend(); 089 090 angle = (float) Math.random() * 360f; 091 092 maxDur = proj.getWeapon().getRange() / proj.getWeapon().getProjectileSpeed(); 093 scaleIncreaseRate = 2.5f / maxDur; 094 scale = 1f; 095 096 turnDir = Math.signum((float) Math.random() - 0.5f) * 30f * (float) Math.random(); 097 //turnDir = 0f; 098 099 float driftDir = proj.getFacing() + 180f + ((float) Math.random() * 30f - 15f); 100 vel = Misc.getUnitVectorAtDegreeAngle(driftDir); 101 //vel.scale(proj.getProjectileSpec().getLength() / maxDur * (0f + (float) Math.random() * 3f)); 102 vel.scale(80f / maxDur * (0f + (float) Math.random() * 3f)); 103 104 origVel = new Vector2f(vel); 105 dirVelChange = Misc.getUnitVectorAtDegreeAngle(proj.getFacing() + 180f); 106 107// offset.x += vel.x * 1f; 108// offset.y += vel.y * 1f; 109 fader = new FaderUtil(0f, 0.25f, 0.05f); 110 fader.fadeIn(); 111 } 112 113 public void advance(float amount) { 114 scale += scaleIncreaseRate * amount; 115 116// if (proj.didDamage()) { 117// vel.set(origVel); 118// //fader.fadeOut(); 119// } 120 121 offset.x += vel.x * amount; 122 offset.y += vel.y * amount; 123 124 if (!proj.didDamage()) { 125 float speed = vel.length(); 126 if (speed > 0) { 127 float speedIncrease = proj.getMoveSpeed() / maxDur * 0.5f; 128 Vector2f dir = new Vector2f(dirVelChange); 129 dir.scale(speedIncrease * amount); 130 Vector2f.add(vel, dir, vel); 131 } 132 } 133 134 angle += turnDir * amount; 135 136 fader.advance(amount); 137 } 138 } 139 140 protected List<ParticleData> particles = new ArrayList<ParticleData>(); 141 142 protected DamagingProjectileAPI proj; 143 protected Vector2f projVel; 144 protected Vector2f projLoc; 145 public CryoblasterEffect(DamagingProjectileAPI proj) { 146 this.proj = proj; 147 148 projVel = new Vector2f(proj.getVelocity()); 149 projLoc = new Vector2f(proj.getLocation()); 150 151 int num = 30; 152 for (int i = 0; i < num; i++) { 153 particles.add(new ParticleData(proj)); 154 } 155 156 float index = 0; 157 for (ParticleData p : particles) { 158 //p.offset = Misc.getPointWithinRadius(p.offset, width * 0.5f); 159 p.offset = Misc.getPointWithinRadius(p.offset, 20f); 160 index++; 161 } 162 } 163 164 public float getRenderRadius() { 165 return 700f; 166 } 167 168 169 protected EnumSet<CombatEngineLayers> layers = EnumSet.of(CombatEngineLayers.ABOVE_SHIPS_AND_MISSILES_LAYER); 170 @Override 171 public EnumSet<CombatEngineLayers> getActiveLayers() { 172 return layers; 173 } 174 175 public void init(CombatEntityAPI entity) { 176 super.init(entity); 177 } 178 179 protected boolean resetTrailSpeed = false; 180 public void advance(float amount) { 181 if (Global.getCombatEngine().isPaused()) return; 182 183 entity.getLocation().set(proj.getLocation()); 184 185 float max = 0f; 186 for (ParticleData p : particles) { 187 p.advance(amount); 188 max = Math.max(max, p.offset.lengthSquared()); 189 } 190 191 // BALLISTIC_AS_BEAM don't get some stuff set right away, catch it in the first few frames 192 // but after that the particles move independently 193 if (proj.getElapsed() < 0.1f) { 194 projVel.set(proj.getVelocity()); 195 projLoc.set(proj.getLocation()); 196 } else { 197 projLoc.x += projVel.x * amount; 198 projLoc.y += projVel.y * amount; 199 200 if (proj.didDamage()) { 201 if (!resetTrailSpeed) { 202 for (ParticleData p : particles) { 203 Vector2f.add(p.vel, projVel, p.vel); 204 } 205 projVel.scale(0f); 206 resetTrailSpeed = true; 207 } 208 for (ParticleData p : particles) { 209 float dist = p.offset.length(); 210 p.vel.scale(Math.min(1f, dist / 100f)); 211 } 212 } 213 } 214 } 215 216 217 public boolean isExpired() { 218 return proj.isExpired() || !Global.getCombatEngine().isEntityInPlay(proj); 219 } 220 221 public void render(CombatEngineLayers layer, ViewportAPI viewport) { 222// float x = entity.getLocation().x; 223// float y = entity.getLocation().y; 224 float x = projLoc.x; 225 float y = projLoc.y; 226 227 Color color = proj.getProjectileSpec().getFringeColor(); 228 color = Misc.setAlpha(color, 30); 229 float b = proj.getBrightness(); 230 b *= viewport.getAlphaMult(); 231 232// Vector2f farAhead = Misc.getUnitVectorAtDegreeAngle(proj.getFacing()); 233// farAhead.scale(10000f); 234// Vector2f.add(proj.getLocation(), farAhead, farAhead); 235 236 for (ParticleData p : particles) { 237 //float size = proj.getProjectileSpec().getWidth() * 0.6f; 238 float size = 25f; 239 size *= p.scale; 240 241 Vector2f loc = new Vector2f(x + p.offset.x, y + p.offset.y); 242 243 float alphaMult = 1f; 244 //float dParticle = Misc.getDistance(farAhead, loc); 245 246 float a = alphaMult; 247 248 p.sprite.setAngle(p.angle); 249 p.sprite.setSize(size, size); 250 p.sprite.setAlphaMult(b * a * p.fader.getBrightness()); 251 p.sprite.setColor(color); 252 p.sprite.renderAtCenter(loc.x, loc.y); 253 } 254 } 255 256} 257 258 259 260