diff --git a/core/src/mindustry/entities/part/DrawPart.java b/core/src/mindustry/entities/part/DrawPart.java index 245d1c8064..f3ebfc6ceb 100644 --- a/core/src/mindustry/entities/part/DrawPart.java +++ b/core/src/mindustry/entities/part/DrawPart.java @@ -14,6 +14,8 @@ public abstract class DrawPart{ public boolean under = false; /** For units, this is the index of the weapon this part gets its progress for. */ public int weaponIndex = 0; + /** Which recoil counter to use. < 0 to use base recoil. */ + public int recoilIndex = -1; public abstract void draw(PartParams params); public abstract void load(String name); @@ -41,6 +43,11 @@ public abstract class DrawPart{ this.sideMultiplier = 1; return this; } + + public PartParams setRecoil(float recoils){ + this.recoil = recoils; + return this; + } } public static class PartMove{ diff --git a/core/src/mindustry/entities/pattern/ShootAlternate.java b/core/src/mindustry/entities/pattern/ShootAlternate.java index 0e64e43423..7fd3795fad 100644 --- a/core/src/mindustry/entities/pattern/ShootAlternate.java +++ b/core/src/mindustry/entities/pattern/ShootAlternate.java @@ -1,5 +1,7 @@ package mindustry.entities.pattern; +import arc.util.*; + public class ShootAlternate extends ShootPattern{ /** number of barrels used for shooting. */ public int barrels = 2; @@ -16,10 +18,11 @@ public class ShootAlternate extends ShootPattern{ } @Override - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ float index = ((totalShots + i + barrelOffset) % barrels) - (barrels-1)/2f; handler.shoot(index * spread, 0, 0f, firstShotDelay + shotDelay * i); + if(barrelIncrementer != null) barrelIncrementer.run(); } } } diff --git a/core/src/mindustry/entities/pattern/ShootBarrel.java b/core/src/mindustry/entities/pattern/ShootBarrel.java index 18e6610ab6..debea85726 100644 --- a/core/src/mindustry/entities/pattern/ShootBarrel.java +++ b/core/src/mindustry/entities/pattern/ShootBarrel.java @@ -1,5 +1,7 @@ package mindustry.entities.pattern; +import arc.util.*; + public class ShootBarrel extends ShootPattern{ /** barrels [in x, y, rotation] format. */ public float[] barrels = {0f, 0f, 0f}; @@ -16,10 +18,11 @@ public class ShootBarrel extends ShootPattern{ } @Override - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ int index = ((i + totalShots + barrelOffset) % (barrels.length / 3)) * 3; handler.shoot(barrels[index], barrels[index + 1], barrels[index + 2], firstShotDelay + shotDelay * i); + if(barrelIncrementer != null) barrelIncrementer.run(); } } } diff --git a/core/src/mindustry/entities/pattern/ShootHelix.java b/core/src/mindustry/entities/pattern/ShootHelix.java index 88ca6d20c0..0b61c594c1 100644 --- a/core/src/mindustry/entities/pattern/ShootHelix.java +++ b/core/src/mindustry/entities/pattern/ShootHelix.java @@ -1,12 +1,13 @@ package mindustry.entities.pattern; import arc.math.*; +import arc.util.*; public class ShootHelix extends ShootPattern{ public float scl = 2f, mag = 1.5f, offset = Mathf.PI * 1.25f; @Override - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ for(int sign : Mathf.signs){ handler.shoot(0, 0, 0, firstShotDelay + shotDelay * i, diff --git a/core/src/mindustry/entities/pattern/ShootMulti.java b/core/src/mindustry/entities/pattern/ShootMulti.java index 34497957cd..10fe77ac3e 100644 --- a/core/src/mindustry/entities/pattern/ShootMulti.java +++ b/core/src/mindustry/entities/pattern/ShootMulti.java @@ -1,5 +1,7 @@ package mindustry.entities.pattern; +import arc.util.*; + public class ShootMulti extends ShootPattern{ public ShootPattern source; public ShootPattern[] dest = {}; @@ -25,7 +27,7 @@ public class ShootMulti extends ShootPattern{ } @Override - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ source.shoot(totalShots, (x, y, rotation, delay, move) -> { for(var pattern : dest){ pattern.shoot(totalShots, (x2, y2, rot2, delay2, mover) -> { @@ -35,6 +37,6 @@ public class ShootMulti extends ShootPattern{ }); }); } - }); + }, barrelIncrementer); } } diff --git a/core/src/mindustry/entities/pattern/ShootPattern.java b/core/src/mindustry/entities/pattern/ShootPattern.java index 2098d24291..2738eb5656 100644 --- a/core/src/mindustry/entities/pattern/ShootPattern.java +++ b/core/src/mindustry/entities/pattern/ShootPattern.java @@ -1,5 +1,6 @@ package mindustry.entities.pattern; +import arc.util.*; import mindustry.entities.*; /** Handles different types of bullet patterns for shooting. */ @@ -12,12 +13,17 @@ public class ShootPattern implements Cloneable{ public float shotDelay = 0; /** Called on a single "trigger pull". This function should call the handler with any bullets that result. */ - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ handler.shoot(0, 0, 0, firstShotDelay + shotDelay * i); } } + /** Called on a single "trigger pull". This function should call the handler with any bullets that result. */ + public void shoot(int totalShots, BulletHandler handler){ + shoot(totalShots, handler, null); + } + /** Subclasses should override this to flip its sides. */ public void flip(){ diff --git a/core/src/mindustry/entities/pattern/ShootSine.java b/core/src/mindustry/entities/pattern/ShootSine.java index 1e8215bde4..4f4d5bc1e2 100644 --- a/core/src/mindustry/entities/pattern/ShootSine.java +++ b/core/src/mindustry/entities/pattern/ShootSine.java @@ -1,6 +1,7 @@ package mindustry.entities.pattern; import arc.math.*; +import arc.util.*; public class ShootSine extends ShootPattern{ /** scaling applied to bullet index */ @@ -17,7 +18,7 @@ public class ShootSine extends ShootPattern{ } @Override - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ float angleOffset = Mathf.sin(i + totalShots, scl, mag); handler.shoot(0, 0, angleOffset, firstShotDelay + shotDelay * i); diff --git a/core/src/mindustry/entities/pattern/ShootSpread.java b/core/src/mindustry/entities/pattern/ShootSpread.java index 98236c993f..0508fc8a91 100644 --- a/core/src/mindustry/entities/pattern/ShootSpread.java +++ b/core/src/mindustry/entities/pattern/ShootSpread.java @@ -1,5 +1,7 @@ package mindustry.entities.pattern; +import arc.util.*; + public class ShootSpread extends ShootPattern{ /** spread between bullets, in degrees. */ public float spread = 5f; @@ -13,7 +15,7 @@ public class ShootSpread extends ShootPattern{ } @Override - public void shoot(int totalShots, BulletHandler handler){ + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ float angleOffset = i * spread - (shots - 1) * spread / 2f; handler.shoot(0, 0, angleOffset, firstShotDelay + shotDelay * i); diff --git a/core/src/mindustry/entities/pattern/ShootSummon.java b/core/src/mindustry/entities/pattern/ShootSummon.java index 3a0f1f164d..c57bd8936d 100644 --- a/core/src/mindustry/entities/pattern/ShootSummon.java +++ b/core/src/mindustry/entities/pattern/ShootSummon.java @@ -14,9 +14,7 @@ public class ShootSummon extends ShootPattern{ } @Override - public void shoot(int totalShots, BulletHandler handler){ - - + public void shoot(int totalShots, BulletHandler handler, @Nullable Runnable barrelIncrementer){ for(int i = 0; i < shots; i++){ Tmp.v1.trns(Mathf.random(360f), Mathf.random(radius)); diff --git a/core/src/mindustry/entities/units/WeaponMount.java b/core/src/mindustry/entities/units/WeaponMount.java index 2bf0df7e6e..752b3fd071 100644 --- a/core/src/mindustry/entities/units/WeaponMount.java +++ b/core/src/mindustry/entities/units/WeaponMount.java @@ -14,6 +14,8 @@ public class WeaponMount{ public float rotation; /** weapon recoil */ public float recoil; + /** weapon barrel recoil */ + public @Nullable float[] recoils; /** destination rotation; do not modify! */ public float targetRotation; /** current heat, 0 to 1*/ @@ -34,8 +36,10 @@ public class WeaponMount{ public boolean rotate = false; /** extra state for alternating weapons */ public boolean side; - /** total bullets fired from this mount; used for alternating patterns */ + /** total bullets fired from this mount */ public int totalShots; + /** counter for which barrel bullets have been fired from; used for alternating patterns */ + public int barrelCounter; /** current bullet for continuous weapons */ public @Nullable Bullet bullet; /** sound loop for continuous weapons */ diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index f3b0eb5e75..243e6b7be2 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -75,6 +75,8 @@ public class Weapon implements Cloneable{ public float shake = 0f; /** visual weapon knockback. */ public float recoil = 1.5f; + /** Number of additional counters for recoil. */ + public int recoils = -1; /** time taken for weapon to return to starting position in ticks. uses reload time by default */ public float recoilTime = -1f; /** power curve applied to visual recoil */ @@ -219,6 +221,7 @@ public class Weapon implements Cloneable{ for(int i = 0; i < parts.size; i++){ var part = parts.get(i); + DrawPart.params.setRecoil(part.recoilIndex >= 0 ? mount.recoils[part.recoilIndex] : mount.recoil); if(part.under){ part.draw(DrawPart.params); } @@ -250,6 +253,7 @@ public class Weapon implements Cloneable{ //TODO does it need an outline? for(int i = 0; i < parts.size; i++){ var part = parts.get(i); + DrawPart.params.setRecoil(part.recoilIndex >= 0 ? mount.recoils[part.recoilIndex] : mount.recoil); if(!part.under){ part.draw(DrawPart.params); } @@ -270,6 +274,12 @@ public class Weapon implements Cloneable{ float lastReload = mount.reload; mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0); mount.recoil = Mathf.approachDelta(mount.recoil, 0, unit.reloadMultiplier / recoilTime); + if(recoils > 0){ + if(mount.recoils == null) mount.recoils = new float[recoils]; + for(int i = 0; i < recoils; i++){ + mount.recoils[i] = Mathf.approachDelta(mount.recoils[i], 0, unit.reloadMultiplier / recoilTime); + } + } mount.smoothReload = Mathf.lerpDelta(mount.smoothReload, mount.reload / reload, smoothReloadSpeed); mount.charge = mount.charging && shoot.firstShotDelay > 0 ? Mathf.approachDelta(mount.charge, 1, 1 / shoot.firstShotDelay) : 0; @@ -420,14 +430,13 @@ public class Weapon implements Cloneable{ bullet.chargeEffect.at(shootX, shootY, rotation, bullet.keepVelocity || parentizeEffects ? unit : null); } - shoot.shoot(mount.totalShots, (xOffset, yOffset, angle, delay, mover) -> { - mount.totalShots++; + shoot.shoot(mount.barrelCounter, (xOffset, yOffset, angle, delay, mover) -> { if(delay > 0f){ Time.run(delay, () -> bullet(unit, mount, xOffset, yOffset, angle, mover)); }else{ bullet(unit, mount, xOffset, yOffset, angle, mover); } - }); + }, () -> mount.barrelCounter++); } protected void bullet(Unit unit, WeaponMount mount, float xOffset, float yOffset, float angleOffset, Mover mover){ @@ -459,7 +468,11 @@ public class Weapon implements Cloneable{ unit.vel.add(Tmp.v1.trns(shootAngle + 180f, bullet.recoil)); Effect.shake(shake, shake, bulletX, bulletY); mount.recoil = 1f; + if(recoils > 0){ + mount.recoils[mount.barrelCounter % recoils] = 1f; + } mount.heat = 1f; + mount.totalShots++; } //override to do special things to a bullet after spawning diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index ff6b1659fd..ff35b122f9 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -112,6 +112,8 @@ public class Turret extends ReloadTurret{ public boolean linearWarmup = false; /** Visual amount by which the turret recoils back per shot. */ public float recoil = 1f; + /** Number of additional counters for recoil. */ + public int recoils = -1; /** ticks taken for turret to return to starting position in ticks. uses reload time by default */ public float recoilTime = -1f; /** power curve applied to visual recoil */ @@ -210,8 +212,9 @@ public class Turret extends ReloadTurret{ public Seq ammo = new Seq<>(); public int totalAmmo; public float curRecoil, heat, logicControlTime = -1; + public @Nullable float[] curRecoils; public float shootWarmup, charge, warmupHold = 0f; - public int totalShots; + public int totalShots, barrelCounter; public boolean logicShooting = false; public @Nullable Posc target; public Vec2 targetPos = new Vec2(); @@ -365,6 +368,12 @@ public class Turret extends ReloadTurret{ wasShooting = false; curRecoil = Mathf.approachDelta(curRecoil, 0, 1 / recoilTime); + if(recoils > 0){ + if(curRecoils == null) curRecoils = new float[recoils]; + for(int i = 0; i < recoils; i++){ + curRecoils[i] = Mathf.approachDelta(curRecoils[i], 0, 1 / recoilTime); + } + } heat = Mathf.approachDelta(heat, 0, 1 / cooldownTime); charge = charging() ? Mathf.approachDelta(charge, 1, 1 / shoot.firstShotDelay) : 0; @@ -537,15 +546,14 @@ public class Turret extends ReloadTurret{ type.chargeEffect.at(bulletX, bulletY, rotation); } - shoot.shoot(totalShots, (xOffset, yOffset, angle, delay, mover) -> { - queuedBullets ++; + shoot.shoot(barrelCounter, (xOffset, yOffset, angle, delay, mover) -> { + queuedBullets++; if(delay > 0f){ Time.run(delay, () -> bullet(type, xOffset, yOffset, angle, mover)); }else{ bullet(type, xOffset, yOffset, angle, mover); } - totalShots ++; - }); + }, () -> barrelCounter++); if(consumeAmmoOnce){ useAmmo(); @@ -583,7 +591,11 @@ public class Turret extends ReloadTurret{ } curRecoil = 1f; + if(recoils > 0){ + curRecoils[barrelCounter % recoils] = 1f; + } heat = 1f; + totalShots++; if(!consumeAmmoOnce){ useAmmo(); diff --git a/core/src/mindustry/world/draw/DrawTurret.java b/core/src/mindustry/world/draw/DrawTurret.java index 8b4a19dd72..4b00d51a32 100644 --- a/core/src/mindustry/world/draw/DrawTurret.java +++ b/core/src/mindustry/world/draw/DrawTurret.java @@ -75,6 +75,7 @@ public class DrawTurret extends DrawBlock{ var params = DrawPart.params.set(build.warmup(), 1f - progress, 1f - progress, tb.heat, tb.curRecoil, tb.charge, tb.x + tb.recoilOffset.x, tb.y + tb.recoilOffset.y, tb.rotation); for(var part : parts){ + params.setRecoil(part.recoilIndex >= 0 ? tb.curRecoils[part.recoilIndex] : tb.curRecoil); part.draw(params); } }