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.opengl.GL14; 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin; 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.ViewportAPI; 017import com.fs.starfarer.api.graphics.SpriteAPI; 018import com.fs.starfarer.api.util.FaderUtil; 019import com.fs.starfarer.api.util.Misc; 020 021public class CombatEntityPluginWithParticles extends BaseCombatLayeredRenderingPlugin { 022 023 public static class ParticleData { 024 public SpriteAPI sprite; 025 public Vector2f offset = new Vector2f(); 026 public Vector2f vel = new Vector2f(); 027 public float scale = 1f; 028 public float scaleIncreaseRate = 1f; 029 public float turnDir = 1f; 030 public float angle = 1f; 031 032 public float maxDur; 033 public FaderUtil fader; 034 public float elapsed = 0f; 035 public float baseSize; 036 protected Color color; 037 038 public ParticleData(float baseSize, float durIn, float durOut, float endSizeMult, float maxDriftVel, float maxAngVel, Color color, String spriteSheetKey) { 039 this.color = color; 040 if (spriteSheetKey == null) { 041 spriteSheetKey = "nebula_particles"; 042 } 043 sprite = Global.getSettings().getSprite("misc", spriteSheetKey); 044 //sprite = Global.getSettings().getSprite("misc", "dust_particles"); 045 float i = Misc.random.nextInt(4); 046 float j = Misc.random.nextInt(4); 047 sprite.setTexWidth(0.25f); 048 sprite.setTexHeight(0.25f); 049 sprite.setTexX(i * 0.25f); 050 sprite.setTexY(j * 0.25f); 051 sprite.setAdditiveBlend(); 052 053 angle = (float) Math.random() * 360f; 054 055 this.maxDur = durIn + durOut; 056 scaleIncreaseRate = endSizeMult / maxDur; 057 if (endSizeMult < 1f) { 058 scaleIncreaseRate = -1f * endSizeMult; 059 } 060 scale = 1f; 061 062 this.baseSize = baseSize; 063 turnDir = Math.signum((float) Math.random() - 0.5f) * maxAngVel * (float) Math.random(); 064 //turnDir = 0f; 065 066 float driftDir = (float) Math.random() * 360f; 067 vel = Misc.getUnitVectorAtDegreeAngle(driftDir); 068 //vel.scale(proj.getProjectileSpec().getLength() / maxDur * (0f + (float) Math.random() * 3f)); 069 vel.scale(maxDriftVel * (0.5f + (float) Math.random() * 0.5f)); 070 071 fader = new FaderUtil(0f, durIn, durOut); 072 fader.setBounceDown(true); 073 fader.forceOut(); 074 fader.fadeIn(); 075 } 076 077 public void advance(float amount) { 078 scale += scaleIncreaseRate * amount; 079 080 offset.x += vel.x * amount; 081 offset.y += vel.y * amount; 082 083 angle += turnDir * amount; 084 085 elapsed += amount; 086// if (maxDur - elapsed <= fader.getDurationOut() + 0.1f) { 087// fader.fadeOut(); 088// } 089 fader.advance(amount); 090 } 091 } 092 093 protected List<ParticleData> particles = new ArrayList<ParticleData>(); 094 protected List<ParticleData> darkParticles = new ArrayList<ParticleData>(); 095 protected EnumSet<CombatEngineLayers> layers = EnumSet.of(CombatEngineLayers.ABOVE_SHIPS_AND_MISSILES_LAYER); 096 protected CombatEngineLayers normalLayer; 097 protected CombatEngineLayers darkLayer; 098 099 public CombatEntityPluginWithParticles() { 100 this(CombatEngineLayers.ABOVE_PARTICLES_LOWER, CombatEngineLayers.ABOVE_PARTICLES); 101 } 102 public CombatEntityPluginWithParticles(CombatEngineLayers normalLayer, CombatEngineLayers darkLayer) { 103 this.normalLayer = normalLayer; 104 this.darkLayer = darkLayer; 105 layers = EnumSet.of(normalLayer, darkLayer); 106 } 107 108 public void init(CombatEntityAPI entity) { 109 super.init(entity); 110 } 111 112 @Override 113 public EnumSet<CombatEngineLayers> getActiveLayers() { 114 return layers; 115 } 116 117 protected ParticleData prev; 118 119 protected String spriteSheetKey; 120 protected String darkSpriteSheetKey; 121 public String getSpriteSheetKey() { 122 return spriteSheetKey; 123 } 124 public void setSpriteSheetKey(String spriteSheetKey) { 125 this.spriteSheetKey = spriteSheetKey; 126 } 127 public String getDarkSpriteSheetKey() { 128 return darkSpriteSheetKey; 129 } 130 public void setDarkSpriteSheetKey(String darkSpriteSheetKey) { 131 this.darkSpriteSheetKey = darkSpriteSheetKey; 132 } 133 134 public ParticleData addParticle(float baseSize, float durIn, float durOut, float endSizeMult, float maxDriftVel, float maxAngVel, Color color) { 135 ParticleData p = new ParticleData(baseSize, durIn, durOut, endSizeMult, maxDriftVel, maxAngVel, color, spriteSheetKey); 136 particles.add(p); 137 prev = p; 138 return p; 139 } 140 public ParticleData addDarkParticle(float baseSize, float durIn, float durOut, float endSizeMult, float maxDriftVel, float maxAngVel, Color color) { 141 ParticleData p = new ParticleData(baseSize, durIn, durOut, endSizeMult, maxDriftVel, maxAngVel, color, darkSpriteSheetKey); 142 darkParticles.add(p); 143 prev = p; 144 return p; 145 } 146 147 public void randomizePrevParticleLocation(float maxOffset) { 148 Vector2f loc = Misc.getPointWithinRadius(prev.offset, maxOffset); 149 prev.offset.set(loc); 150 } 151 152 public void advance(float amount) { 153 if (Global.getCombatEngine().isPaused()) return; 154 155 List<ParticleData> remove = new ArrayList<ParticleData>(); 156 for (ParticleData p : particles) { 157 p.advance(amount); 158 if (p.elapsed >= p.maxDur) { 159 remove.add(p); 160 } 161 } 162 particles.removeAll(remove); 163 164 remove = new ArrayList<ParticleData>(); 165 for (ParticleData p : darkParticles) { 166 p.advance(amount); 167 if (p.elapsed >= p.maxDur) { 168 remove.add(p); 169 } 170 } 171 darkParticles.removeAll(remove); 172 } 173 174 175 public boolean isExpired() { 176 return particles.isEmpty() && darkParticles.isEmpty(); 177 } 178 179 protected float getGlobalAlphaMult() { 180 return 1f; 181 } 182 183 protected Float baseFacing = null; 184 public void render(CombatEngineLayers layer, ViewportAPI viewport) { 185 render(layer, viewport, null); 186 } 187 188 public void render(CombatEngineLayers layer, ViewportAPI viewport, DamagingProjectileAPI proj) { 189 float x = entity.getLocation().x; 190 float y = entity.getLocation().y; 191 192 float b = viewport.getAlphaMult(); 193 194 if (proj != null && baseFacing == null) { 195 baseFacing = proj.getFacing(); 196 } 197 198 if (layer == normalLayer) { 199 for (ParticleData p : particles) { 200 float size = p.baseSize * p.scale; 201 202 Vector2f offset = p.offset; 203 float diff = 0f; 204 if (baseFacing != null && proj != null) { 205 diff = Misc.getAngleDiff(baseFacing, proj.getFacing()); 206 if (Math.abs(diff) > 0.1f) { 207 offset = Misc.rotateAroundOrigin(offset, diff); 208 } 209 } 210 Vector2f loc = new Vector2f(x + offset.x, y + offset.y); 211 212 float alphaMult = getGlobalAlphaMult(); 213 214 p.sprite.setAngle(p.angle); 215 p.sprite.setSize(size, size); 216 p.sprite.setAlphaMult(b * alphaMult * p.fader.getBrightness()); 217 p.sprite.setColor(p.color); 218 p.sprite.renderAtCenter(loc.x, loc.y); 219 } 220 } else if (layer == darkLayer) { 221 GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT); 222 223 for (ParticleData p : darkParticles) { 224 //float size = proj.getProjectileSpec().getWidth() * 0.6f; 225 float size = p.baseSize * p.scale; 226 227 Vector2f offset = p.offset; 228 float diff = 0f; 229 if (baseFacing != null && proj != null) { 230 diff = Misc.getAngleDiff(baseFacing, proj.getFacing()); 231 if (Math.abs(diff) > 0.1f) { 232 offset = Misc.rotateAroundOrigin(offset, diff); 233 } 234 } 235 Vector2f loc = new Vector2f(x + offset.x, y + offset.y); 236 237 float alphaMult = getGlobalAlphaMult(); 238 239 p.sprite.setAngle(p.angle); 240 p.sprite.setSize(size, size); 241 p.sprite.setAlphaMult(b * alphaMult * p.fader.getBrightness()); 242 p.sprite.setColor(p.color); 243 p.sprite.renderAtCenter(loc.x, loc.y); 244 } 245 246 GL14.glBlendEquation(GL14.GL_FUNC_ADD); 247 } 248 } 249 250} 251 252 253 254