Starsector API
Loading...
Searching...
No Matches
DisintegratorEffect.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.List;
7
8import org.lwjgl.opengl.GL14;
9import org.lwjgl.util.vector.Vector2f;
10
11import com.fs.starfarer.api.Global;
12import com.fs.starfarer.api.combat.ArmorGridAPI;
13import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin;
14import com.fs.starfarer.api.combat.CombatEngineAPI;
15import com.fs.starfarer.api.combat.CombatEngineLayers;
16import com.fs.starfarer.api.combat.CombatEntityAPI;
17import com.fs.starfarer.api.combat.DamagingProjectileAPI;
18import com.fs.starfarer.api.combat.OnHitEffectPlugin;
19import com.fs.starfarer.api.combat.ShipAPI;
20import com.fs.starfarer.api.combat.ViewportAPI;
21import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI;
22import com.fs.starfarer.api.graphics.SpriteAPI;
23import com.fs.starfarer.api.util.FaderUtil;
24import com.fs.starfarer.api.util.IntervalUtil;
25import com.fs.starfarer.api.util.Misc;
26
27public class DisintegratorEffect extends BaseCombatLayeredRenderingPlugin implements OnHitEffectPlugin {
28
29 // each tick is on average .9 seconds
30 // ticks can't be longer than a second or floating damage numbers separate
31 public static int NUM_TICKS = 22;
32 public static float TOTAL_DAMAGE = 500;
33
35 }
36
37 public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine) {
38 if (shieldHit) return;
39 if (projectile.isFading()) return;
40 if (!(target instanceof ShipAPI)) return;
41
42 Vector2f offset = Vector2f.sub(point, target.getLocation(), new Vector2f());
43 offset = Misc.rotateAroundOrigin(offset, -target.getFacing());
44
45 DisintegratorEffect effect = new DisintegratorEffect(projectile, (ShipAPI) target, offset);
46 CombatEntityAPI e = engine.addLayeredRenderingPlugin(effect);
47 e.getLocation().set(projectile.getLocation());
48 }
49
50 public static class ParticleData {
51 public SpriteAPI sprite;
52 public Vector2f offset = new Vector2f();
53 public Vector2f vel = new Vector2f();
54 public float scale = 1f;
55 public float scaleIncreaseRate = 1f;
56 public float turnDir = 1f;
57 public float angle = 1f;
58
59 public float maxDur;
60 public FaderUtil fader;
61 public float elapsed = 0f;
62 public float baseSize;
63
64 public ParticleData(float baseSize, float maxDur, float endSizeMult) {
65 sprite = Global.getSettings().getSprite("misc", "nebula_particles");
66 //sprite = Global.getSettings().getSprite("misc", "dust_particles");
67 float i = Misc.random.nextInt(4);
68 float j = Misc.random.nextInt(4);
69 sprite.setTexWidth(0.25f);
70 sprite.setTexHeight(0.25f);
71 sprite.setTexX(i * 0.25f);
72 sprite.setTexY(j * 0.25f);
73 sprite.setAdditiveBlend();
74
75 angle = (float) Math.random() * 360f;
76
77 this.maxDur = maxDur;
78 scaleIncreaseRate = endSizeMult / maxDur;
79 if (endSizeMult < 1f) {
80 scaleIncreaseRate = -1f * endSizeMult;
81 }
82 scale = 1f;
83
84 this.baseSize = baseSize;
85 turnDir = Math.signum((float) Math.random() - 0.5f) * 20f * (float) Math.random();
86 //turnDir = 0f;
87
88 float driftDir = (float) Math.random() * 360f;
89 vel = Misc.getUnitVectorAtDegreeAngle(driftDir);
90 //vel.scale(proj.getProjectileSpec().getLength() / maxDur * (0f + (float) Math.random() * 3f));
91 vel.scale(0.25f * baseSize / maxDur * (1f + (float) Math.random() * 1f));
92
93 fader = new FaderUtil(0f, 0.5f, 0.5f);
94 fader.forceOut();
95 fader.fadeIn();
96 }
97
98 public void advance(float amount) {
99 scale += scaleIncreaseRate * amount;
100
101 offset.x += vel.x * amount;
102 offset.y += vel.y * amount;
103
104 angle += turnDir * amount;
105
106 elapsed += amount;
107 if (maxDur - elapsed <= fader.getDurationOut() + 0.1f) {
108 fader.fadeOut();
109 }
110 fader.advance(amount);
111 }
112 }
113
114 protected List<ParticleData> particles = new ArrayList<ParticleData>();
115 protected DamagingProjectileAPI proj;
116 protected ShipAPI target;
117 protected Vector2f offset;
118 protected int ticks = 0;
119 protected IntervalUtil interval;
120 protected FaderUtil fader = new FaderUtil(1f, 0.5f, 0.5f);
121
122 public DisintegratorEffect(DamagingProjectileAPI proj, ShipAPI target, Vector2f offset) {
123 this.proj = proj;
124 this.target = target;
125 this.offset = offset;
126
127 interval = new IntervalUtil(0.8f, 1f);
128 interval.forceIntervalElapsed();
129 }
130
131 public float getRenderRadius() {
132 return 500f;
133 }
134
135
136 protected EnumSet<CombatEngineLayers> layers = EnumSet.of(CombatEngineLayers.BELOW_INDICATORS_LAYER);
137 @Override
138 public EnumSet<CombatEngineLayers> getActiveLayers() {
139 return layers;
140 }
141
142 public void init(CombatEntityAPI entity) {
143 super.init(entity);
144 }
145
146 public void advance(float amount) {
147 if (Global.getCombatEngine().isPaused()) return;
148
149 Vector2f loc = new Vector2f(offset);
150 loc = Misc.rotateAroundOrigin(loc, target.getFacing());
151 Vector2f.add(target.getLocation(), loc, loc);
152 entity.getLocation().set(loc);
153
154 List<ParticleData> remove = new ArrayList<ParticleData>();
155 for (ParticleData p : particles) {
156 p.advance(amount);
157 if (p.elapsed >= p.maxDur) {
158 remove.add(p);
159 }
160 }
161 particles.removeAll(remove);
162
163 float volume = 1f;
164 if (ticks >= NUM_TICKS || !target.isAlive() || !Global.getCombatEngine().isEntityInPlay(target)) {
165 fader.fadeOut();
166 fader.advance(amount);
167 volume = fader.getBrightness();
168 }
169 Global.getSoundPlayer().playLoop("disintegrator_loop", target, 1f, volume, loc, target.getVelocity());
170
171
172 interval.advance(amount);
173 if (interval.intervalElapsed() && ticks < NUM_TICKS) {
174 dealDamage();
175 ticks++;
176 }
177 }
178
179
180 protected void dealDamage() {
181 CombatEngineAPI engine = Global.getCombatEngine();
182
183 int num = 3;
184 for (int i = 0; i < num; i++) {
185 ParticleData p = new ParticleData(30f, 3f + (float) Math.random() * 2f, 2f);
186 particles.add(p);
187 p.offset = Misc.getPointWithinRadius(p.offset, 20f);
188 }
189
190
191 Vector2f point = new Vector2f(entity.getLocation());
192
193 // maximum armor in a cell is 1/15th of the ship's stated armor rating
194
195 ArmorGridAPI grid = target.getArmorGrid();
196 int[] cell = grid.getCellAtLocation(point);
197 if (cell == null) return;
198
199 int gridWidth = grid.getGrid().length;
200 int gridHeight = grid.getGrid()[0].length;
201
202 float damageTypeMult = getDamageTypeMult(proj.getSource(), target);
203
204 float damagePerTick = (float) TOTAL_DAMAGE / (float) NUM_TICKS;
205 float damageDealt = 0f;
206 for (int i = -2; i <= 2; i++) {
207 for (int j = -2; j <= 2; j++) {
208 if ((i == 2 || i == -2) && (j == 2 || j == -2)) continue; // skip corners
209
210 int cx = cell[0] + i;
211 int cy = cell[1] + j;
212
213 if (cx < 0 || cx >= gridWidth || cy < 0 || cy >= gridHeight) continue;
214
215 float damMult = 1/30f;
216 if (i == 0 && j == 0) {
217 damMult = 1/15f;
218 } else if (i <= 1 && i >= -1 && j <= 1 && j >= -1) { // S hits
219 damMult = 1/15f;
220 } else { // T hits
221 damMult = 1/30f;
222 }
223
224 float armorInCell = grid.getArmorValue(cx, cy);
225 float damage = damagePerTick * damMult * damageTypeMult;
226 damage = Math.min(damage, armorInCell);
227 if (damage <= 0) continue;
228
229 target.getArmorGrid().setArmorValue(cx, cy, Math.max(0, armorInCell - damage));
230 damageDealt += damage;
231 }
232 }
233
234 if (damageDealt > 0) {
235 if (Misc.shouldShowDamageFloaty(proj.getSource(), target)) {
236 engine.addFloatingDamageText(point, damageDealt, Misc.FLOATY_ARMOR_DAMAGE_COLOR, target, proj.getSource());
237 }
238 target.syncWithArmorGridState();
239 }
240
241 }
242
243 public boolean isExpired() {
244 return particles.isEmpty() &&
245 (ticks >= NUM_TICKS || !target.isAlive() || !Global.getCombatEngine().isEntityInPlay(target));
246 }
247
248 public void render(CombatEngineLayers layer, ViewportAPI viewport) {
249 float x = entity.getLocation().x;
250 float y = entity.getLocation().y;
251
252 Color color = new Color(100,150,255,35);
253 float b = viewport.getAlphaMult();
254
255 GL14.glBlendEquation(GL14.GL_FUNC_REVERSE_SUBTRACT);
256
257 for (ParticleData p : particles) {
258 //float size = proj.getProjectileSpec().getWidth() * 0.6f;
259 float size = p.baseSize * p.scale;
260
261 Vector2f loc = new Vector2f(x + p.offset.x, y + p.offset.y);
262
263 float alphaMult = 1f;
264
265 p.sprite.setAngle(p.angle);
266 p.sprite.setSize(size, size);
267 p.sprite.setAlphaMult(b * alphaMult * p.fader.getBrightness());
268 p.sprite.setColor(color);
269 p.sprite.renderAtCenter(loc.x, loc.y);
270 }
271
272 GL14.glBlendEquation(GL14.GL_FUNC_ADD);
273 }
274
275
276 public static float getDamageTypeMult(ShipAPI source, ShipAPI target) {
277 if (source == null || target == null) return 1f;
278
279 float damageTypeMult = target.getMutableStats().getArmorDamageTakenMult().getModifiedValue();
280 switch (target.getHullSize()) {
281 case CAPITAL_SHIP:
282 damageTypeMult *= source.getMutableStats().getDamageToCapital().getModifiedValue();
283 break;
284 case CRUISER:
285 damageTypeMult *= source.getMutableStats().getDamageToCruisers().getModifiedValue();
286 break;
287 case DESTROYER:
288 damageTypeMult *= source.getMutableStats().getDamageToDestroyers().getModifiedValue();
289 break;
290 case FRIGATE:
291 damageTypeMult *= source.getMutableStats().getDamageToFrigates().getModifiedValue();
292 break;
293 case FIGHTER:
294 damageTypeMult *= source.getMutableStats().getDamageToFighters().getModifiedValue();
295 break;
296 }
297 return damageTypeMult;
298 }
299
300}
301
302
303
304
static SettingsAPI getSettings()
Definition Global.java:51
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:43
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
DisintegratorEffect(DamagingProjectileAPI proj, ShipAPI target, Vector2f offset)
static float getDamageTypeMult(ShipAPI source, ShipAPI target)
void render(CombatEngineLayers layer, ViewportAPI viewport)
void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine)
SpriteAPI getSprite(String filename)
void playLoop(String id, Object playingEntity, float pitch, float volume, Vector2f loc, Vector2f vel)