Multi shotgun fix (#8460)

* Multi-barrel shotgun fix

* multi-recoil support

* @Nullable for everything
This commit is contained in:
MEEPofFaith
2023-04-03 13:38:10 -07:00
committed by GitHub
parent 2075a226e1
commit 6d71bcd2eb
13 changed files with 73 additions and 20 deletions

View File

@@ -14,6 +14,8 @@ public abstract class DrawPart{
public boolean under = false; public boolean under = false;
/** For units, this is the index of the weapon this part gets its progress for. */ /** For units, this is the index of the weapon this part gets its progress for. */
public int weaponIndex = 0; 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 draw(PartParams params);
public abstract void load(String name); public abstract void load(String name);
@@ -41,6 +43,11 @@ public abstract class DrawPart{
this.sideMultiplier = 1; this.sideMultiplier = 1;
return this; return this;
} }
public PartParams setRecoil(float recoils){
this.recoil = recoils;
return this;
}
} }
public static class PartMove{ public static class PartMove{

View File

@@ -1,5 +1,7 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.util.*;
public class ShootAlternate extends ShootPattern{ public class ShootAlternate extends ShootPattern{
/** number of barrels used for shooting. */ /** number of barrels used for shooting. */
public int barrels = 2; public int barrels = 2;
@@ -16,10 +18,11 @@ public class ShootAlternate extends ShootPattern{
} }
@Override @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 i = 0; i < shots; i++){
float index = ((totalShots + i + barrelOffset) % barrels) - (barrels-1)/2f; float index = ((totalShots + i + barrelOffset) % barrels) - (barrels-1)/2f;
handler.shoot(index * spread, 0, 0f, firstShotDelay + shotDelay * i); handler.shoot(index * spread, 0, 0f, firstShotDelay + shotDelay * i);
if(barrelIncrementer != null) barrelIncrementer.run();
} }
} }
} }

View File

@@ -1,5 +1,7 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.util.*;
public class ShootBarrel extends ShootPattern{ public class ShootBarrel extends ShootPattern{
/** barrels [in x, y, rotation] format. */ /** barrels [in x, y, rotation] format. */
public float[] barrels = {0f, 0f, 0f}; public float[] barrels = {0f, 0f, 0f};
@@ -16,10 +18,11 @@ public class ShootBarrel extends ShootPattern{
} }
@Override @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 i = 0; i < shots; i++){
int index = ((i + totalShots + barrelOffset) % (barrels.length / 3)) * 3; int index = ((i + totalShots + barrelOffset) % (barrels.length / 3)) * 3;
handler.shoot(barrels[index], barrels[index + 1], barrels[index + 2], firstShotDelay + shotDelay * i); handler.shoot(barrels[index], barrels[index + 1], barrels[index + 2], firstShotDelay + shotDelay * i);
if(barrelIncrementer != null) barrelIncrementer.run();
} }
} }
} }

View File

@@ -1,12 +1,13 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.math.*; import arc.math.*;
import arc.util.*;
public class ShootHelix extends ShootPattern{ public class ShootHelix extends ShootPattern{
public float scl = 2f, mag = 1.5f, offset = Mathf.PI * 1.25f; public float scl = 2f, mag = 1.5f, offset = Mathf.PI * 1.25f;
@Override @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 i = 0; i < shots; i++){
for(int sign : Mathf.signs){ for(int sign : Mathf.signs){
handler.shoot(0, 0, 0, firstShotDelay + shotDelay * i, handler.shoot(0, 0, 0, firstShotDelay + shotDelay * i,

View File

@@ -1,5 +1,7 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.util.*;
public class ShootMulti extends ShootPattern{ public class ShootMulti extends ShootPattern{
public ShootPattern source; public ShootPattern source;
public ShootPattern[] dest = {}; public ShootPattern[] dest = {};
@@ -25,7 +27,7 @@ public class ShootMulti extends ShootPattern{
} }
@Override @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) -> { source.shoot(totalShots, (x, y, rotation, delay, move) -> {
for(var pattern : dest){ for(var pattern : dest){
pattern.shoot(totalShots, (x2, y2, rot2, delay2, mover) -> { pattern.shoot(totalShots, (x2, y2, rot2, delay2, mover) -> {
@@ -35,6 +37,6 @@ public class ShootMulti extends ShootPattern{
}); });
}); });
} }
}); }, barrelIncrementer);
} }
} }

View File

@@ -1,5 +1,6 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.util.*;
import mindustry.entities.*; import mindustry.entities.*;
/** Handles different types of bullet patterns for shooting. */ /** Handles different types of bullet patterns for shooting. */
@@ -12,12 +13,17 @@ public class ShootPattern implements Cloneable{
public float shotDelay = 0; public float shotDelay = 0;
/** Called on a single "trigger pull". This function should call the handler with any bullets that result. */ /** 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++){ for(int i = 0; i < shots; i++){
handler.shoot(0, 0, 0, firstShotDelay + shotDelay * 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. */ /** Subclasses should override this to flip its sides. */
public void flip(){ public void flip(){

View File

@@ -1,6 +1,7 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.math.*; import arc.math.*;
import arc.util.*;
public class ShootSine extends ShootPattern{ public class ShootSine extends ShootPattern{
/** scaling applied to bullet index */ /** scaling applied to bullet index */
@@ -17,7 +18,7 @@ public class ShootSine extends ShootPattern{
} }
@Override @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 i = 0; i < shots; i++){
float angleOffset = Mathf.sin(i + totalShots, scl, mag); float angleOffset = Mathf.sin(i + totalShots, scl, mag);
handler.shoot(0, 0, angleOffset, firstShotDelay + shotDelay * i); handler.shoot(0, 0, angleOffset, firstShotDelay + shotDelay * i);

View File

@@ -1,5 +1,7 @@
package mindustry.entities.pattern; package mindustry.entities.pattern;
import arc.util.*;
public class ShootSpread extends ShootPattern{ public class ShootSpread extends ShootPattern{
/** spread between bullets, in degrees. */ /** spread between bullets, in degrees. */
public float spread = 5f; public float spread = 5f;
@@ -13,7 +15,7 @@ public class ShootSpread extends ShootPattern{
} }
@Override @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 i = 0; i < shots; i++){
float angleOffset = i * spread - (shots - 1) * spread / 2f; float angleOffset = i * spread - (shots - 1) * spread / 2f;
handler.shoot(0, 0, angleOffset, firstShotDelay + shotDelay * i); handler.shoot(0, 0, angleOffset, firstShotDelay + shotDelay * i);

View File

@@ -14,9 +14,7 @@ public class ShootSummon extends ShootPattern{
} }
@Override @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 i = 0; i < shots; i++){
Tmp.v1.trns(Mathf.random(360f), Mathf.random(radius)); Tmp.v1.trns(Mathf.random(360f), Mathf.random(radius));

View File

@@ -14,6 +14,8 @@ public class WeaponMount{
public float rotation; public float rotation;
/** weapon recoil */ /** weapon recoil */
public float recoil; public float recoil;
/** weapon barrel recoil */
public @Nullable float[] recoils;
/** destination rotation; do not modify! */ /** destination rotation; do not modify! */
public float targetRotation; public float targetRotation;
/** current heat, 0 to 1*/ /** current heat, 0 to 1*/
@@ -34,8 +36,10 @@ public class WeaponMount{
public boolean rotate = false; public boolean rotate = false;
/** extra state for alternating weapons */ /** extra state for alternating weapons */
public boolean side; public boolean side;
/** total bullets fired from this mount; used for alternating patterns */ /** total bullets fired from this mount */
public int totalShots; public int totalShots;
/** counter for which barrel bullets have been fired from; used for alternating patterns */
public int barrelCounter;
/** current bullet for continuous weapons */ /** current bullet for continuous weapons */
public @Nullable Bullet bullet; public @Nullable Bullet bullet;
/** sound loop for continuous weapons */ /** sound loop for continuous weapons */

View File

@@ -75,6 +75,8 @@ public class Weapon implements Cloneable{
public float shake = 0f; public float shake = 0f;
/** visual weapon knockback. */ /** visual weapon knockback. */
public float recoil = 1.5f; 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 */ /** time taken for weapon to return to starting position in ticks. uses reload time by default */
public float recoilTime = -1f; public float recoilTime = -1f;
/** power curve applied to visual recoil */ /** power curve applied to visual recoil */
@@ -219,6 +221,7 @@ public class Weapon implements Cloneable{
for(int i = 0; i < parts.size; i++){ for(int i = 0; i < parts.size; i++){
var part = parts.get(i); var part = parts.get(i);
DrawPart.params.setRecoil(part.recoilIndex >= 0 ? mount.recoils[part.recoilIndex] : mount.recoil);
if(part.under){ if(part.under){
part.draw(DrawPart.params); part.draw(DrawPart.params);
} }
@@ -250,6 +253,7 @@ public class Weapon implements Cloneable{
//TODO does it need an outline? //TODO does it need an outline?
for(int i = 0; i < parts.size; i++){ for(int i = 0; i < parts.size; i++){
var part = parts.get(i); var part = parts.get(i);
DrawPart.params.setRecoil(part.recoilIndex >= 0 ? mount.recoils[part.recoilIndex] : mount.recoil);
if(!part.under){ if(!part.under){
part.draw(DrawPart.params); part.draw(DrawPart.params);
} }
@@ -270,6 +274,12 @@ public class Weapon implements Cloneable{
float lastReload = mount.reload; float lastReload = mount.reload;
mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0); mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0);
mount.recoil = Mathf.approachDelta(mount.recoil, 0, unit.reloadMultiplier / recoilTime); 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.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; 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); bullet.chargeEffect.at(shootX, shootY, rotation, bullet.keepVelocity || parentizeEffects ? unit : null);
} }
shoot.shoot(mount.totalShots, (xOffset, yOffset, angle, delay, mover) -> { shoot.shoot(mount.barrelCounter, (xOffset, yOffset, angle, delay, mover) -> {
mount.totalShots++;
if(delay > 0f){ if(delay > 0f){
Time.run(delay, () -> bullet(unit, mount, xOffset, yOffset, angle, mover)); Time.run(delay, () -> bullet(unit, mount, xOffset, yOffset, angle, mover));
}else{ }else{
bullet(unit, mount, xOffset, yOffset, angle, mover); bullet(unit, mount, xOffset, yOffset, angle, mover);
} }
}); }, () -> mount.barrelCounter++);
} }
protected void bullet(Unit unit, WeaponMount mount, float xOffset, float yOffset, float angleOffset, Mover mover){ 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)); unit.vel.add(Tmp.v1.trns(shootAngle + 180f, bullet.recoil));
Effect.shake(shake, shake, bulletX, bulletY); Effect.shake(shake, shake, bulletX, bulletY);
mount.recoil = 1f; mount.recoil = 1f;
if(recoils > 0){
mount.recoils[mount.barrelCounter % recoils] = 1f;
}
mount.heat = 1f; mount.heat = 1f;
mount.totalShots++;
} }
//override to do special things to a bullet after spawning //override to do special things to a bullet after spawning

View File

@@ -112,6 +112,8 @@ public class Turret extends ReloadTurret{
public boolean linearWarmup = false; public boolean linearWarmup = false;
/** Visual amount by which the turret recoils back per shot. */ /** Visual amount by which the turret recoils back per shot. */
public float recoil = 1f; 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 */ /** ticks taken for turret to return to starting position in ticks. uses reload time by default */
public float recoilTime = -1f; public float recoilTime = -1f;
/** power curve applied to visual recoil */ /** power curve applied to visual recoil */
@@ -210,8 +212,9 @@ public class Turret extends ReloadTurret{
public Seq<AmmoEntry> ammo = new Seq<>(); public Seq<AmmoEntry> ammo = new Seq<>();
public int totalAmmo; public int totalAmmo;
public float curRecoil, heat, logicControlTime = -1; public float curRecoil, heat, logicControlTime = -1;
public @Nullable float[] curRecoils;
public float shootWarmup, charge, warmupHold = 0f; public float shootWarmup, charge, warmupHold = 0f;
public int totalShots; public int totalShots, barrelCounter;
public boolean logicShooting = false; public boolean logicShooting = false;
public @Nullable Posc target; public @Nullable Posc target;
public Vec2 targetPos = new Vec2(); public Vec2 targetPos = new Vec2();
@@ -365,6 +368,12 @@ public class Turret extends ReloadTurret{
wasShooting = false; wasShooting = false;
curRecoil = Mathf.approachDelta(curRecoil, 0, 1 / recoilTime); 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); heat = Mathf.approachDelta(heat, 0, 1 / cooldownTime);
charge = charging() ? Mathf.approachDelta(charge, 1, 1 / shoot.firstShotDelay) : 0; 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); type.chargeEffect.at(bulletX, bulletY, rotation);
} }
shoot.shoot(totalShots, (xOffset, yOffset, angle, delay, mover) -> { shoot.shoot(barrelCounter, (xOffset, yOffset, angle, delay, mover) -> {
queuedBullets ++; queuedBullets++;
if(delay > 0f){ if(delay > 0f){
Time.run(delay, () -> bullet(type, xOffset, yOffset, angle, mover)); Time.run(delay, () -> bullet(type, xOffset, yOffset, angle, mover));
}else{ }else{
bullet(type, xOffset, yOffset, angle, mover); bullet(type, xOffset, yOffset, angle, mover);
} }
totalShots ++; }, () -> barrelCounter++);
});
if(consumeAmmoOnce){ if(consumeAmmoOnce){
useAmmo(); useAmmo();
@@ -583,7 +591,11 @@ public class Turret extends ReloadTurret{
} }
curRecoil = 1f; curRecoil = 1f;
if(recoils > 0){
curRecoils[barrelCounter % recoils] = 1f;
}
heat = 1f; heat = 1f;
totalShots++;
if(!consumeAmmoOnce){ if(!consumeAmmoOnce){
useAmmo(); useAmmo();

View File

@@ -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); 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){ for(var part : parts){
params.setRecoil(part.recoilIndex >= 0 ? tb.curRecoils[part.recoilIndex] : tb.curRecoil);
part.draw(params); part.draw(params);
} }
} }