diff --git a/core/src/mindustry/ai/types/MissileAI.java b/core/src/mindustry/ai/types/MissileAI.java index 082c445048..a9b98eef5e 100644 --- a/core/src/mindustry/ai/types/MissileAI.java +++ b/core/src/mindustry/ai/types/MissileAI.java @@ -32,6 +32,7 @@ public class MissileAI extends AIController{ //kill instantly on enemy building contact if(build != null && build.team != unit.team && (build == target || !build.block.underBullets)){ + unit.hasTarget = true; unit.kill(); } } diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 8c71b0a051..53b4f6fee8 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -1891,6 +1891,73 @@ public class Fx{ }).layer(Layer.effect + 1f), + shootQuellPulse = new Effect(40f, e -> { + rand.setSeed(e.id); + + float randSize = 0.1f; + float fout = e.fout() * rand.random(1f - randSize, 1f); + float fin = e.fin() * rand.random(1f - randSize, 1f); + float coreRadius = 30f * e.fout(Interp.smooth2); + + Color coreColor = Tmp.c1.set(e.color).mul(0.8f); + Color edgeColor = e.color; + + e.scaled(10, i -> { + stroke(4f * i.fout()); + Lines.circle(e.x, e.y, 2f + i.fin() * 40f); + }); + + int count = 8; + for(int i = 0; i < count; i++){ + float t = (i + 1f) / count; + float radius = coreRadius + 5f; + color(Tmp.c1.set(coreColor).mul(1f + fout / 8f)); + alpha(Mathf.pow(1f - t, 2.5f) * fout * 0.5f); + Fill.circle(e.x, e.y, Mathf.lerp(coreRadius * 0.6f, coreRadius * 1.7f, t)); + } + + color(Tmp.c1.set(edgeColor).mul(1.2f)); + e.scaled(fout * 0.8f, i -> { + stroke(3f * i.fout()); + Lines.circle(e.x, e.y, coreRadius * 0.6f); + }); + + color(coreColor); + alpha(0.5f * e.fout(Interp.smooth) + 0.8f); + stroke(e.fout(Interp.pow2InInverse) * 3f); + float circleRad = e.finpow() * 28f; + Lines.circle(e.x, e.y, circleRad); + + stroke(e.fout(Interp.smooth) * 3f); + for(int i = 0; i < 9; i++){ + float angle = rand.random(360f); + float lenRand = rand.random(0.5f, 1.2f); + Tmp.v1.trns(angle, circleRad); + + for(int s : Mathf.signs){ + Drawf.tri(e.x + Tmp.v1.x, e.y + Tmp.v1.y, e.fout() * 10f, e.fout() * 10f * lenRand + 8f, angle + 90f + s * 90f); + } + } + + color(edgeColor); + alpha(e.fout(Interp.pow2InInverse) + 0.5f); + + for(int i = 0; i < rand.random(8, 13); i++){ + float randomPos = rand.random(0.9f, 1.1f); + float angle = rand.random(360f); + float len = rand.random(0.7f, 1.3f) * 10f + fout * 2f; + float width = rand.random(1f, 4f) * 1.5f * fout + 1f; + float dist = 8f + coreRadius * rand.random(0.8f, 1.4f); + Tmp.v1.trns(angle, circleRad); + + for(int s : Mathf.signs){ + Drawf.tri(e.x + Angles.trnsx(angle, dist) - Tmp.v1.x / 2, e.y + Angles.trnsy(angle, dist) * randomPos - Tmp.v1.y * randomPos / 2, width, len, angle + 90f + s * 90f); + } + } + + reset(); + }), + shootTitan = new Effect(10, e -> { color(Pal.lightOrange, e.color, e.fin()); float w = 1.3f + 10 * e.fout(); diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index a083858114..369078e45a 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -3504,8 +3504,9 @@ public class UnitTypes{ mirror = false; reload = 1f; shootOnDeath = true; + shootOnDeathEffect = Fx.massiveExplosion; bullet = new ExplosionBulletType(140f, 25f){{ - shootEffect = new MultiEffect(Fx.massiveExplosion, new WrapEffect(Fx.dynamicSpikes, Pal.techBlue, 24f), new WaveEffect(){{ + shootEffect = new MultiEffect(new WrapEffect(Fx.dynamicSpikes, Pal.techBlue, 24f), new WaveEffect(){{ colorFrom = colorTo = Pal.techBlue; sizeTo = 40f; lifetime = 12f; @@ -4147,8 +4148,9 @@ public class UnitTypes{ mirror = false; reload = 1f; shootOnDeath = true; - bullet = new ExplosionBulletType(110f, 25f) {{ - shootEffect = Fx.massiveExplosion; + shootOnDeathEffect = Fx.massiveExplosion; + bullet = new ExplosionBulletType(110f, 25f){{ + shootEffect = new WrapEffect(Fx.shootQuellPulse, Pal.suppress); collidesAir = false; }}; }}); @@ -4289,14 +4291,15 @@ public class UnitTypes{ mirror = false; reload = 1f; shootOnDeath = true; + shootOnDeathEffect = Fx.massiveExplosion; bullet = new ExplosionBulletType(140f, 25f){{ collidesAir = false; suppressionRange = 140f; shootEffect = new ExplosionEffect(){{ lifetime = 50f; waveStroke = 5f; - waveLife = 8f; - waveColor = Color.white; + waveLife = 12f; + waveColor = Pal.sap.cpy().mul(1.8f); sparkColor = smokeColor = Pal.suppress; waveRad = 40f; smokeSize = 4f; diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index b03e48e9b6..e3d08cfa11 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -411,6 +411,9 @@ public class BulletType extends Content implements Cloneable{ if(spawnUnit != null){ return spawnUnit.estimateDps(); } + if(despawnUnit != null){ + return despawnUnit.estimateDps(); + } float sum = (damage + splashDamage*0.75f) * (pierce ? pierceCap == -1 ? 2 : Mathf.clamp(pierceCap, 1, 2) : 1f); if(fragBullet != null && fragBullet != this){ @@ -426,6 +429,7 @@ public class BulletType extends Content implements Cloneable{ protected float calculateRange(){ if(rangeOverride > 0) return rangeOverride; if(spawnUnit != null) return spawnUnit.lifetime * spawnUnit.speed; + if(despawnUnit != null) return despawnUnit.lifetime * despawnUnit.speed; return Math.max(Mathf.zero(drag) ? speed * lifetime : speed * (1f - Mathf.pow(1f - drag, lifetime)) / drag, maxRange); } diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index c814343a5a..62795b572f 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -59,6 +59,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I transient String lastCommanded; transient float shadowAlpha = -1f, healTime; transient int lastFogPos; + /** Only used in suicide units */ + transient boolean hasTarget; private transient float resupplyTime = Mathf.random(10f); private transient boolean wasPlayer; private transient boolean wasHealed; @@ -876,6 +878,10 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I for(WeaponMount mount : mounts){ if(mount.weapon.shootOnDeath && !(mount.weapon.bullet.killShooter && mount.totalShots > 0)){ + if(mount.weapon.shootOnDeathEffect != null && !hasTarget){ + mount.allowShootEffects = false; + mount.weapon.shootOnDeathEffect.at(x, y, rotation); + } mount.reload = 0f; mount.shoot = true; mount.weapon.update(self(), mount); diff --git a/core/src/mindustry/entities/units/WeaponMount.java b/core/src/mindustry/entities/units/WeaponMount.java index 5cc1567533..dd52b82fa0 100644 --- a/core/src/mindustry/entities/units/WeaponMount.java +++ b/core/src/mindustry/entities/units/WeaponMount.java @@ -32,6 +32,8 @@ public class WeaponMount{ public float aimX, aimY; /** whether to shoot right now */ public boolean shoot = false; + /** whether to allow any shooting effects */ + public boolean allowShootEffects = true; /** whether to rotate to face the target right now */ public boolean rotate = false; /** extra state for alternating weapons */ diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 6ab803150a..eedcaa20c7 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -157,6 +157,8 @@ public class Weapon implements Cloneable{ public float shootStatusDuration = 60f * 5f; /** whether this weapon should fire when its owner dies */ public boolean shootOnDeath = false; + /** If not null and shootOnDeath == true, overrides the weapon's shoot effect only when its owner dies. */ + public @Nullable Effect shootOnDeathEffect = null; /** extra animated parts */ public Seq parts = new Seq<>(DrawPart.class); @@ -516,9 +518,11 @@ public class Weapon implements Cloneable{ initialShootSound.at(bulletX, bulletY, Mathf.random(soundPitchMin, soundPitchMax), shootSoundVolume); } - ejectEffect.at(mountX, mountY, angle * Mathf.sign(this.x)); - bullet.shootEffect.at(bulletX, bulletY, angle, bullet.hitColor, unit); - bullet.smokeEffect.at(bulletX, bulletY, angle, bullet.hitColor, unit); + if(mount.allowShootEffects){ + ejectEffect.at(mountX, mountY, angle * Mathf.sign(this.x)); + bullet.shootEffect.at(bulletX, bulletY, angle, bullet.hitColor, unit); + bullet.smokeEffect.at(bulletX, bulletY, angle, bullet.hitColor, unit); + } unit.vel.add(Tmp.v1.trns(shootAngle + 180f, bullet.recoil)); Effect.shake(shake, shake, bulletX, bulletY);