diff --git a/core/assets/contributors b/core/assets/contributors index abba64c04b..3a95b07aac 100644 --- a/core/assets/contributors +++ b/core/assets/contributors @@ -41,7 +41,7 @@ Jan Polák JrTRinny JustYanns BasedUser -RexHm +Rex Aliis BLucky-gh DinoWattz Jae @@ -149,4 +149,5 @@ KayAyeAre SMOLKEYS 1stvaliduser(SUS) GlennFolker -BlackDeluxeCat \ No newline at end of file +BlackDeluxeCat +zenonet diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 563d9048b8..705b9d26c7 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -440,7 +440,7 @@ public class Damage{ /** Applies a status effect to all enemy units in a range. */ public static void status(Team team, float x, float y, float radius, StatusEffect effect, float duration, boolean air, boolean ground){ Cons cons = entity -> { - if(entity.team == team || !entity.within(x, y, radius) || (entity.isFlying() && !air) || (entity.isGrounded() && !ground)){ + if(entity.team == team || !entity.checkTarget(air, ground) || !entity.hittable() || !entity.within(x, y, radius)){ return; } diff --git a/core/src/mindustry/entities/bullet/EmpBulletType.java b/core/src/mindustry/entities/bullet/EmpBulletType.java index 8f793c4e50..6306d81b7a 100644 --- a/core/src/mindustry/entities/bullet/EmpBulletType.java +++ b/core/src/mindustry/entities/bullet/EmpBulletType.java @@ -48,7 +48,7 @@ public class EmpBulletType extends BasicBulletType{ if(hitUnits){ Units.nearbyEnemies(b.team, x, y, radius, other -> { - if(other.team != b.team){ + if(other.team != b.team && other.hittable()){ var absorber = Damage.findAbsorber(b.team, x, y, other.x, other.y); if(absorber != null){ return; diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 4011e88b1f..6d538cf7d8 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -474,7 +474,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I drag = type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f) * dragMultiplier * state.rules.dragMultiplier; //apply knockback based on spawns - if(team != state.rules.waveTeam && state.hasSpawns() && (!net.client() || isLocal())){ + if(team != state.rules.waveTeam && state.hasSpawns() && (!net.client() || isLocal()) && hittable()){ float relativeSize = state.rules.dropZoneRadius + hitSize/2f + 1f; for(Tile spawn : spawner.getSpawns()){ if(within(spawn.worldx(), spawn.worldy(), relativeSize)){ diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 6efcc19e04..faaad3d6be 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -554,6 +554,7 @@ public class EventType{ } } + /** Called before a player leaves the game. */ public static class PlayerLeave{ public final Player player; diff --git a/core/src/mindustry/net/Administration.java b/core/src/mindustry/net/Administration.java index 7982dce937..e6ed620551 100644 --- a/core/src/mindustry/net/Administration.java +++ b/core/src/mindustry/net/Administration.java @@ -487,7 +487,8 @@ public class Administration{ autosaveAmount = new Config("autosaveAmount", "The maximum amount of autosaves. Older ones get replaced.", 10), autosaveSpacing = new Config("autosaveSpacing", "Spacing between autosaves in seconds.", 60 * 5), debug = new Config("debug", "Enable debug logging", false, () -> Log.level = debug() ? LogLevel.debug : LogLevel.info), - snapshotInterval = new Config("snapshotInterval", "Client entity snapshot interval in ms.", 200); + snapshotInterval = new Config("snapshotInterval", "Client entity snapshot interval in ms.", 200), + autoPause = new Config("autoPause", "Whether the game should pause when nobody is online.", false); public final Object defaultValue; public final String name, key, description; diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 50e9e2b017..d4b2517b57 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -93,6 +93,8 @@ public class Weapon implements Cloneable{ public float minWarmup = 0f; /** lerp speed for shoot warmup, only used for parts */ public float shootWarmupSpeed = 0.1f, smoothReloadSpeed = 0.15f; + /** If true, shoot warmup is linear instead of a curve. */ + public boolean linearWarmup = false; /** random sound pitch range */ public float soundPitchMin = 0.8f, soundPitchMax = 1f; /** whether shooter rotation is ignored when shooting. */ @@ -253,9 +255,15 @@ 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); - mount.warmup = Mathf.lerpDelta(mount.warmup, (can && mount.shoot) || (continuous && mount.bullet != null) ? 1f : 0f, shootWarmupSpeed); 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; + + float warmupTarget = (can && mount.shoot) || (continuous && mount.bullet != null) || mount.charging ? 1f : 0f; + if(linearWarmup){ + mount.warmup = Mathf.approachDelta(mount.warmup, warmupTarget, shootWarmupSpeed); + }else{ + mount.warmup = Mathf.lerpDelta(mount.warmup, warmupTarget, shootWarmupSpeed); + } //rotate if applicable if(rotate && (mount.rotate || mount.shoot) && can){ diff --git a/core/src/mindustry/world/blocks/defense/turrets/PayloadAmmoTurret.java b/core/src/mindustry/world/blocks/defense/turrets/PayloadAmmoTurret.java index 2da5ae500e..93308b8b0e 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/PayloadAmmoTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/PayloadAmmoTurret.java @@ -26,6 +26,7 @@ public class PayloadAmmoTurret extends Turret{ super(name); maxAmmo = 3; + acceptsPayload = true; } /** Initializes accepted ammo map. Format: [block1, bullet1, block2, bullet2...] */ diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index 95addae14a..994515f016 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -343,7 +343,7 @@ public class Turret extends ReloadTurret{ public void updateTile(){ if(!validateTarget()) target = null; - float warmupTarget = isShooting() && canConsume() ? 1f : 0f; + float warmupTarget = (isShooting() && canConsume()) || charging() ? 1f : 0f; if(linearWarmup){ shootWarmup = Mathf.approachDelta(shootWarmup, warmupTarget, shootWarmupSpeed * (warmupTarget > 0 ? efficiency : 1f)); }else{ diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 6efe7f7ede..5440f775e1 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -64,6 +64,7 @@ public class ServerControl implements ApplicationListener{ private ServerSocket serverSocket; private PrintWriter socketOutput; private String suggested; + private boolean autoPaused = false; public ServerControl(String[] args){ setup(args); @@ -269,6 +270,23 @@ public class ServerControl implements ApplicationListener{ info("Server loaded. Type @ for help.", "'help'"); }); + + Events.on(PlayerJoin.class, e -> { + if(state.serverPaused && autoPaused && Config.autoPause.bool()){ + state.serverPaused = false; + autoPaused = false; + } + }); + + Events.on(PlayerLeave.class, e -> { + // The player list length is compared with 1 and not 0 here, + // because when PlayerLeave gets fired, the player hasn't been removed from the player list yet + if(!state.serverPaused && Config.autoPause.bool() && Groups.player.size() == 1){ + state.serverPaused = true; + autoPaused = true; + } + }); + } protected void registerCommands(){ @@ -352,6 +370,11 @@ public class ServerControl implements ApplicationListener{ info("Map loaded."); netServer.openServer(); + + if(Config.autoPause.bool()){ + state.serverPaused = true; + autoPaused = true; + } }catch(MapException e){ err(e.map.name() + ": " + e.getMessage()); } @@ -467,6 +490,7 @@ public class ServerControl implements ApplicationListener{ handler.register("pause", "", "Pause or unpause the game.", arg -> { boolean pause = arg[0].equals("on"); + autoPaused = false; state.serverPaused = pause; info(pause ? "Game paused." : "Game unpaused."); });