Starsector API
All Classes Namespaces Files Functions Variables
CryofluxTransducerEffect.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.EnumSet;
6import java.util.Iterator;
7import java.util.List;
8
9import org.lwjgl.util.vector.Vector2f;
10
11import com.fs.starfarer.api.Global;
12import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin;
13import com.fs.starfarer.api.combat.CombatEngineAPI;
14import com.fs.starfarer.api.combat.CombatEngineLayers;
15import com.fs.starfarer.api.combat.CombatEntityAPI;
16import com.fs.starfarer.api.combat.DamagingProjectileAPI;
17import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
18import com.fs.starfarer.api.combat.OnFireEffectPlugin;
19import com.fs.starfarer.api.combat.OnHitEffectPlugin;
20import com.fs.starfarer.api.combat.ShipAPI;
21import com.fs.starfarer.api.combat.ViewportAPI;
22import com.fs.starfarer.api.combat.WeaponAPI;
23import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI;
24import com.fs.starfarer.api.graphics.SpriteAPI;
25import com.fs.starfarer.api.util.FaderUtil;
26import com.fs.starfarer.api.util.Misc;
27
35public class CryofluxTransducerEffect extends BaseCombatLayeredRenderingPlugin implements OnFireEffectPlugin,
36 OnHitEffectPlugin,
37 EveryFrameWeaponEffectPlugin {
38
40 }
41
42 protected List<CryofluxTransducerEffect> trails;
43 public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) {
44 if (trails == null) return;
45
46 Iterator<CryofluxTransducerEffect> iter = trails.iterator();
47 while (iter.hasNext()) {
48 if (iter.next().isExpired()) iter.remove();
49 }
50
51 // sound loop playback
52 if (weapon.getShip() != null) {
53 float maxRange = weapon.getRange();
54 ShipAPI ship = weapon.getShip();
55 Vector2f com = new Vector2f();
56 float weight = 0f;
57 float totalDist = 0f;
58 Vector2f source = weapon.getLocation();
59 for (CryofluxTransducerEffect curr : trails) {
60 if (curr.proj != null) {
61 Vector2f.add(com, curr.proj.getLocation(), com);
62 weight += curr.proj.getBrightness();
63 totalDist += Misc.getDistance(source, curr.proj.getLocation());
64 }
65 }
66 if (weight > 0.1f) {
67 com.scale(1f / weight);
68 float volume = Math.min(weight, 1f);
69 if (trails.size() > 0) {
70 totalDist /= (float) trails.size();
71 float mult = totalDist / Math.max(maxRange, 1f);
72 mult = 1f - mult;
73 if (mult > 1f) mult = 1f;
74 if (mult < 0f) mult = 0f;
75 mult = (float) Math.sqrt(mult);
76 volume *= mult;
77 }
78 Global.getSoundPlayer().playLoop("cryoflamer_loop", ship, 1f, volume, com, ship.getVelocity());
79 }
80 }
81
82
83 //System.out.println("Trails: " + trails.size());
84 float numIter = 1f; // more doesn't actually change anything
85 amount /= numIter;
86 // drag along the previous projectile, starting with the most recently launched; new ones are added at the start
87 // note: prev is fired before and so is in front of proj
88 for (int i = 0; i < numIter; i++) {
89 for (CryofluxTransducerEffect trail : trails) {
90 //trail.proj.setFacing(trail.proj.getFacing() + 180f * amount);
91 if (trail.prev != null && !trail.prev.isExpired() && Global.getCombatEngine().isEntityInPlay(trail.prev)) {
92 float dist1 = Misc.getDistance(trail.prev.getLocation(), trail.proj.getLocation());
93 if (dist1 < trail.proj.getProjectileSpec().getLength() * 1f) {
94 float maxSpeed = trail.prev.getMoveSpeed() * 0.5f;// * Math.max(0.5f, 1f - trail.prev.getElapsed() * 0.5f);
95 // goal here is to prevent longer shot series (e.g. from Paragon) from moving too unnaturally
96 float e = trail.prev.getElapsed();
97 float t = 0.5f;
98 if (e > t) {
99 maxSpeed *= Math.max(0.25f, 1f - (e - t) * 0.5f);
100 }
101 if (dist1 < 20f && e > t) {
102 maxSpeed *= dist1 / 20f;
103 }
104
105 Vector2f driftTo = Misc.closestPointOnLineToPoint(trail.proj.getLocation(), trail.proj.getTailEnd(), trail.prev.getLocation());
106 float dist = Misc.getDistance(driftTo, trail.prev.getLocation());
107 Vector2f diff = Vector2f.sub(driftTo, trail.prev.getLocation(), new Vector2f());
108 diff = Misc.normalise(diff);
109 diff.scale(Math.min(dist, maxSpeed * amount));
110 Vector2f.add(trail.prev.getLocation(), diff, trail.prev.getLocation());
111 Vector2f.add(trail.prev.getTailEnd(), diff, trail.prev.getTailEnd());
112 }
113 }
114 }
115 }
116 }
117
118
119 public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine) {
120 Color color = projectile.getProjectileSpec().getFringeColor();
121// Color inverted = NSLanceEffect.getColorForDarkening(color);
122// inverted = Misc.setAlpha(inverted, 50);
123// Color inverted = new Color(255, 255, 100, 50);
124
125 Vector2f vel = new Vector2f();
126 if (target instanceof ShipAPI) {
127 vel.set(target.getVelocity());
128 }
129
130 float size = projectile.getProjectileSpec().getWidth() * 1f;
131 //size = Misc.getHitGlowSize(size, projectile.getDamage().getBaseDamage(), damageResult);
132 float sizeMult = Misc.getHitGlowSize(100f, projectile.getDamage().getBaseDamage(), damageResult) / 100f;
133// sizeMult = 1.5f;
134// System.out.println(sizeMult);
135 float dur = 1f;
136 float rampUp = 0f;
137 Color c = Misc.scaleAlpha(color, projectile.getBrightness());
138 engine.addNebulaParticle(point, vel, size, 5f + 3f * sizeMult,
139 rampUp, 0f, dur, c);
140// engine.addNegativeNebulaParticle(point, vel, size, 2f,
141// rampUp, 0f, dur, inverted);
142// engine.addNegativeParticle(point, vel, size,
143// rampUp, dur, inverted);
144
145 Misc.playSound(damageResult, point, vel,
146 "cryoflamer_hit_shield_light",
147 "cryoflamer_hit_shield_solid",
148 "cryoflamer_hit_shield_heavy",
149 "cryoflamer_hit_light",
150 "cryoflamer_hit_solid",
151 "cryoflamer_hit_heavy");
152
153 }
154
155 public void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) {
156 String prevKey = "cryo_prev_" + weapon.getShip().getId() + "_" + weapon.getSlot().getId();
157 DamagingProjectileAPI prev = (DamagingProjectileAPI) engine.getCustomData().get(prevKey);
158
160 CombatEntityAPI e = engine.addLayeredRenderingPlugin(trail);
161 e.getLocation().set(projectile.getLocation());
162
163 engine.getCustomData().put(prevKey, projectile);
164
165 if (trails == null) {
166 trails = new ArrayList<CryofluxTransducerEffect>();
167 }
168 trails.add(0, trail);
169 }
170
171
172
173 public static class ParticleData {
174 public SpriteAPI sprite;
175 public Vector2f offset = new Vector2f();
176 public Vector2f vel = new Vector2f();
177 public float scale = 1f;
178 public DamagingProjectileAPI proj;
179 public float scaleIncreaseRate = 1f;
180 public float turnDir = 1f;
181 public float angle = 1f;
182 public FaderUtil fader;
183
184 public ParticleData(DamagingProjectileAPI proj) {
185 this.proj = proj;
186 sprite = Global.getSettings().getSprite("misc", "nebula_particles");
187 //sprite = Global.getSettings().getSprite("misc", "dust_particles");
188 float i = Misc.random.nextInt(4);
189 float j = Misc.random.nextInt(4);
190 sprite.setTexWidth(0.25f);
191 sprite.setTexHeight(0.25f);
192 sprite.setTexX(i * 0.25f);
193 sprite.setTexY(j * 0.25f);
194 sprite.setAdditiveBlend();
195
196 angle = (float) Math.random() * 360f;
197
198 float maxDur = proj.getWeapon().getRange() / proj.getWeapon().getProjectileSpeed();
199 scaleIncreaseRate = 2f / maxDur;
200 scale = 1f;
201// scale = 0.1f;
202// scaleIncreaseRate = 2.9f / maxDur;
203// scale = 0.1f;
204// scaleIncreaseRate = 2.5f / maxDur;
205// scale = 0.5f;
206
207 turnDir = Math.signum((float) Math.random() - 0.5f) * 60f * (float) Math.random();
208 //turnDir = 0f;
209
210 float driftDir = (float) Math.random() * 360f;
211 vel = Misc.getUnitVectorAtDegreeAngle(driftDir);
212 vel.scale(proj.getProjectileSpec().getWidth() / maxDur * 0.33f);
213
214// offset.x += vel.x * 1f;
215// offset.y += vel.y * 1f;
216 fader = new FaderUtil(0f, 0.25f, 0.5f);
217 fader.fadeIn();
218 }
219
220 public void advance(float amount) {
221 scale += scaleIncreaseRate * amount;
222 if (scale < 1f) {
223 scale += scaleIncreaseRate * amount * 1f;
224 }
225
226 offset.x += vel.x * amount;
227 offset.y += vel.y * amount;
228
229 angle += turnDir * amount;
230
231 fader.advance(amount);
232 }
233 }
234
235 protected List<ParticleData> particles = new ArrayList<ParticleData>();
236
237 protected DamagingProjectileAPI proj;
238 protected DamagingProjectileAPI prev;
239 protected float baseFacing = 0f;
240 public CryofluxTransducerEffect(DamagingProjectileAPI proj, DamagingProjectileAPI prev) {
241 this.proj = proj;
242 this.prev = prev;
243
244 baseFacing = proj.getFacing();
245
246 int num = 7;
247 for (int i = 0; i < num; i++) {
248 particles.add(new ParticleData(proj));
249 }
250
251 float length = proj.getProjectileSpec().getLength();
252 float width = proj.getProjectileSpec().getWidth();
253
254 float index = 0;
255 for (ParticleData p : particles) {
256 float f = index / (particles.size() - 1);
257 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(proj.getFacing() + 180f);
258 dir.scale(length * f);
259 Vector2f.add(p.offset, dir, p.offset);
260
261 p.offset = Misc.getPointWithinRadius(p.offset, width * 0.5f);
262 //p.scale = 0.25f + 0.75f * (1 - f);
263
264 index++;
265 }
266 }
267
268 public float getRenderRadius() {
269 return 300f;
270 }
271
272
273 protected EnumSet<CombatEngineLayers> layers = EnumSet.of(CombatEngineLayers.ABOVE_SHIPS_AND_MISSILES_LAYER);
274 @Override
275 public EnumSet<CombatEngineLayers> getActiveLayers() {
276 return layers;
277 }
278
279
280 public void init(CombatEntityAPI entity) {
281 super.init(entity);
282 }
283
284 public void advance(float amount) {
285 if (Global.getCombatEngine().isPaused()) return;
286
287 entity.getLocation().set(proj.getLocation());
288
289 for (ParticleData p : particles) {
290 p.advance(amount);
291 }
292 }
293
294
295 public boolean isExpired() {
296 return proj.isExpired() || !Global.getCombatEngine().isEntityInPlay(proj);
297 }
298
299 public void render(CombatEngineLayers layer, ViewportAPI viewport) {
300 float x = entity.getLocation().x;
301 float y = entity.getLocation().y;
302
303 //Color color = new Color(100,150,255,50);
304 Color color = proj.getProjectileSpec().getFringeColor();
305 color = Misc.setAlpha(color, 50);
306 float b = proj.getBrightness();
307 b *= viewport.getAlphaMult();
308
309 for (ParticleData p : particles) {
310 float size = proj.getProjectileSpec().getWidth() * 0.6f;
311 size *= p.scale;
312
313 float alphaMult = 1f;
314 Vector2f offset = p.offset;
315 float diff = Misc.getAngleDiff(baseFacing, proj.getFacing());
316 if (Math.abs(diff) > 0.1f) {
317 offset = Misc.rotateAroundOrigin(offset, diff);
318 }
319 Vector2f loc = new Vector2f(x + offset.x, y + offset.y);
320
321 p.sprite.setAngle(p.angle);
322 p.sprite.setSize(size, size);
323 p.sprite.setAlphaMult(b * alphaMult * p.fader.getBrightness());
324 p.sprite.setColor(color);
325 p.sprite.renderAtCenter(loc.x, loc.y);
326 }
327 }
328
329}
330
331
332
333
static SettingsAPI getSettings()
Definition Global.java:51
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:43
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine)
void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon)
void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine)
void render(CombatEngineLayers layer, ViewportAPI viewport)
CryofluxTransducerEffect(DamagingProjectileAPI proj, DamagingProjectileAPI prev)
SpriteAPI getSprite(String filename)
void playLoop(String id, Object playingEntity, float pitch, float volume, Vector2f loc, Vector2f vel)