diff --git a/core/assets/sounds/blockHeal.ogg b/core/assets/sounds/blockHeal.ogg new file mode 100644 index 0000000000..868873a523 Binary files /dev/null and b/core/assets/sounds/blockHeal.ogg differ diff --git a/core/assets/sounds/coreExplode.ogg b/core/assets/sounds/coreExplode.ogg new file mode 100644 index 0000000000..f0b020a6ba Binary files /dev/null and b/core/assets/sounds/coreExplode.ogg differ diff --git a/core/assets/sounds/healAbility.ogg b/core/assets/sounds/healAbility.ogg new file mode 100644 index 0000000000..953d30fd50 Binary files /dev/null and b/core/assets/sounds/healAbility.ogg differ diff --git a/core/assets/sounds/healBeam.ogg b/core/assets/sounds/healBeam.ogg new file mode 100644 index 0000000000..81440f1dc4 Binary files /dev/null and b/core/assets/sounds/healBeam.ogg differ diff --git a/core/src/mindustry/audio/SoundPriority.java b/core/src/mindustry/audio/SoundPriority.java index 9090fb358f..f9c21b2df7 100644 --- a/core/src/mindustry/audio/SoundPriority.java +++ b/core/src/mindustry/audio/SoundPriority.java @@ -63,8 +63,14 @@ public class SoundPriority{ shieldHit.setMaxConcurrent(4); max(4, mechStep, mechStepHeavy, walkerStep, walkerStepSmall, walkerStepTiny, mechStepSmall); + + //repair sounds are lower priority and generally not important + set(-1f, blockHeal, healAbility); + //step sounds are low priority - set(-1f, mechStep, mechStepHeavy, walkerStep, walkerStepSmall, walkerStepTiny, mechStepSmall); + set(-2f, mechStep, mechStepHeavy, walkerStep, walkerStepSmall, walkerStepTiny, mechStepSmall); + + coreExplode.setFalloffOffset(100f); } static void max(int max, Sound... sounds){ diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 620c2354ee..a2a37a07ff 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -2622,7 +2622,7 @@ public class Blocks{ powerProduction = 130f; itemDuration = 140f; ambientSound = Sounds.pulse; - ambientSoundVolume = 0.07f; + ambientSoundVolume = 0.08f; liquidCapacity = 80f; consumePower(25f); @@ -6298,6 +6298,7 @@ public class Blocks{ beamWidth = 0.73f; powerUse = 1f; pulseRadius = 5f; + ambientSoundVolume = 0.9f; }}; repairTurret = new RepairTurret("repair-turret"){{ @@ -6312,6 +6313,7 @@ public class Blocks{ coolantUse = 0.16f; coolantMultiplier = 1.6f; acceptCoolant = true; + ambientSoundVolume = 1.25f; }}; //endregion diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index b1ff7d79a3..cad2f943d8 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -550,6 +550,36 @@ public class Fx{ } }), + coreExplosion = new Effect(55f, 240f, e -> { + color(e.color); + stroke(e.fout() * 4f); + float circleRad = 6f + e.finpow() * 120f; + Lines.circle(e.x, e.y, circleRad); + + stroke(e.fout() * 2.5f); + + rand.setSeed(e.id); + for(int i = 0; i < 30; i++){ + float angle = rand.random(360f); + float lenRand = rand.random(0.5f, 1f); + Lines.lineAngle(e.x, e.y, angle, e.foutpow() * 50f * rand.random(1f, 0.6f) + 2f, e.finpow() * 100f * lenRand + 6f); + } + + stroke(e.fout() * 2f); + + for(int i = 0; i < 30; i++){ + float angle = rand.random(360f); + float lenRand = rand.random(0.5f, 1f); + float speed = rand.random(1f, 0.6f); + float fin = e.finpow() / rand.random(0.3f, 1f); + float fout = 1f - fin; + + if(fin < 1f){ + stroke(fout * 2f); + Lines.lineAngle(e.x, e.y, angle, Interp.pow3Out.apply(fin) * 80f * speed + 2f, fin * 100f * lenRand + 6f); + } + } + }), smokeAoeCloud = new Effect(60f * 3f, 250f, e -> { color(e.color, 0.65f); diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 9bc9fdbe47..b1279caaeb 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -3307,7 +3307,7 @@ public class UnitTypes{ weapons.add(new Weapon("anthicus-weapon"){{ shootSound = Sounds.missileLarge; - shootSoundVolume = 0.65f; + shootSoundVolume = 0.5f; x = 29f / 4f; y = -11f / 4f; shootY = 1.5f; diff --git a/core/src/mindustry/entities/Effect.java b/core/src/mindustry/entities/Effect.java index 2f8e85152d..4d68a8344d 100644 --- a/core/src/mindustry/entities/Effect.java +++ b/core/src/mindustry/entities/Effect.java @@ -262,6 +262,32 @@ public class Effect{ decal(region, x, y, Mathf.random(4) * 90, 3600, Pal.rubble); } + public static void shockwaveDust(float x, float y, float rad){ + shockwaveDust(x, y, rad, 1f); + } + + public static void shockwaveDust(float x, float y, float rad, float density){ + float spacing = 12f; + int waves = Math.max(1, Mathf.ceil(rad / spacing)); + for(int i = 0; i < waves; i++){ + int fi = i; + Time.run(i * 3.5f, () -> { + float radius = 1 + spacing * fi; + int rays = Mathf.ceil(radius * Mathf.PI * 2f / 6f * density); + for(int r = 0; r < rays; r++){ + if(Mathf.chance(0.7f - fi * 0.02f)){ + float angle = r * 360f / (float)rays; + float ox = Angles.trnsx(angle, radius), oy = Angles.trnsy(angle, radius); + Tile t = world.tileWorld(x + ox, y + oy); + if(t != null){ + Fx.podLandDust.at(t.worldx(), t.worldy(), angle + Mathf.range(30f), Tmp.c1.set(t.floor().mapColor).mul(1.7f + Mathf.range(0.15f))); + } + } + } + }); + } + } + public static class EffectContainer implements Scaled{ public float x, y, time, lifetime, rotation; public Color color; diff --git a/core/src/mindustry/entities/abilities/RepairFieldAbility.java b/core/src/mindustry/entities/abilities/RepairFieldAbility.java index 9337b20dc6..a007f0d051 100644 --- a/core/src/mindustry/entities/abilities/RepairFieldAbility.java +++ b/core/src/mindustry/entities/abilities/RepairFieldAbility.java @@ -1,6 +1,8 @@ package mindustry.entities.abilities; import arc.*; +import arc.audio.*; +import arc.math.*; import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; @@ -13,6 +15,8 @@ public class RepairFieldAbility extends Ability{ public float amount = 1, reload = 100, range = 60, healPercent = 0f; public Effect healEffect = Fx.heal; public Effect activeEffect = Fx.healWaveDynamic; + public Sound sound = Sounds.healAbility; + public float soundVolume = 0.5f; public boolean parentizeEffects = false; /** Multiplies healing to units of the same type by this amount. */ public float sameTypeHealMult = 1f; @@ -69,6 +73,7 @@ public class RepairFieldAbility extends Ability{ if(wasHealed){ activeEffect.at(unit, range); + sound.at(unit, 1f + Mathf.range(0.1f), soundVolume); } timer = 0f; diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 7a834f9385..d16015f996 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -161,6 +161,10 @@ public class BulletType extends Content implements Cloneable{ public float healPercent = 0f; /** flat amount of block health healed */ public float healAmount = 0f; + /** sound played when a block is healed */ + public Sound healSound = Sounds.blockHeal; + /** volume of heal sound */ + public float healSoundVolume = 0.9f; /** Fraction of bullet damage that heals that shooter. */ public float lifesteal = 0f; /** Whether to make fire on impact */ @@ -442,6 +446,7 @@ public class BulletType extends Content implements Cloneable{ if(heals() && build.team == b.team && !(build.block instanceof ConstructBlock)){ healEffect.at(build.x, build.y, 0f, healColor, build.block); build.heal(healPercent / 100f * build.maxHealth + healAmount); + healSound.at(build, 1f + Mathf.range(0.1f), healSoundVolume); hit(b); }else if(build.team != b.team && direct){ diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index b9844129be..cdc819a09d 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -2178,7 +2178,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, public void killed(){ dead = true; Events.fire(new BlockDestroyEvent(tile)); - block.destroySound.at(tile, Mathf.random(block.destroyPitchMin, block.destroyPitchMax)); + if(!headless) playDestroySound(); onDestroyed(); if(tile != emptyTile){ tile.remove(); @@ -2187,6 +2187,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, afterDestroyed(); } + public void playDestroySound(){ + block.destroySound.at(tile, Mathf.random(block.destroyPitchMin, block.destroyPitchMax), block.destroySoundVolume); + } + public void checkAllowUpdate(){ if(!allowUpdate()){ enabled = false; diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 361e915598..88d3ef8353 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -10,6 +10,7 @@ import arc.math.geom.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; +import mindustry.*; import mindustry.ai.types.*; import mindustry.annotations.Annotations.*; import mindustry.audio.*; @@ -126,6 +127,10 @@ public class Weapon implements Cloneable{ public int otherSide = -1; /** draw Z offset relative to the default value */ public float layerOffset = 0f; + /** sound looped when shooting */ + public Sound activeSound = Sounds.none; + /** volume of active sound */ + public float activeSoundVolume = 1f; /** sound used for shooting */ public Sound shootSound = Sounds.pew; /** volume of the shoot sound */ @@ -419,6 +424,10 @@ public class Weapon implements Cloneable{ mount.side = !mount.side; } + if(!headless && activeSound != Sounds.none && mount.shoot && can && mount.warmup >= minWarmup){ + Vars.control.sound.loop(activeSound, unit, activeSoundVolume); + } + //shoot if applicable if(mount.shoot && //must be shooting can && //must be able to shoot diff --git a/core/src/mindustry/type/weapons/RepairBeamWeapon.java b/core/src/mindustry/type/weapons/RepairBeamWeapon.java index 0d33d207d1..a1d9b99137 100644 --- a/core/src/mindustry/type/weapons/RepairBeamWeapon.java +++ b/core/src/mindustry/type/weapons/RepairBeamWeapon.java @@ -61,6 +61,7 @@ public class RepairBeamWeapon extends Weapon{ recoil = 0f; noAttack = true; useAttackRange = false; + activeSound = Sounds.healBeam; } @Override diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index f5ddcfc8ee..d8d32c0cc6 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -314,6 +314,8 @@ public class Block extends UnlockableContent implements Senseable{ public Sound breakSound = Sounds.breaks; /** Sounds made when this block is destroyed.*/ public Sound destroySound = Sounds.boom; + /** Volume of destruction sound. */ + public float destroySoundVolume = 1f; /** Range of destroy sound. */ public float destroyPitchMin = 1f, destroyPitchMax = 1f; /** How reflective this block is. */ diff --git a/core/src/mindustry/world/blocks/campaign/Accelerator.java b/core/src/mindustry/world/blocks/campaign/Accelerator.java index 3a0efce2f9..5d76e37bde 100644 --- a/core/src/mindustry/world/blocks/campaign/Accelerator.java +++ b/core/src/mindustry/world/blocks/campaign/Accelerator.java @@ -354,7 +354,7 @@ public class Accelerator extends Block{ float spacing = 12f; for(int i = 0; i < 13; i++){ int fi = i; - Time.run(i * 1.1f, () -> { + Time.run(i * 2f, () -> { float radius = block.size/2f + 1 + spacing * fi; int rays = Mathf.ceil(radius * Mathf.PI * 2f / 6f); for(int r = 0; r < rays; r++){ @@ -369,8 +369,6 @@ public class Accelerator extends Block{ } }); } - - }); } diff --git a/core/src/mindustry/world/blocks/defense/MendProjector.java b/core/src/mindustry/world/blocks/defense/MendProjector.java index 34a69c5f18..c8ce06fd6b 100644 --- a/core/src/mindustry/world/blocks/defense/MendProjector.java +++ b/core/src/mindustry/world/blocks/defense/MendProjector.java @@ -1,5 +1,6 @@ package mindustry.world.blocks.defense; +import arc.audio.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; @@ -28,6 +29,10 @@ public class MendProjector extends Block{ public float phaseBoost = 12f; public float phaseRangeBoost = 50f; public float useTime = 400f; + public Sound mendSound = Sounds.healAbility; + public float mendSoundVolume = 0.5f; + + private boolean any = false; public MendProjector(String name){ super(name); @@ -69,7 +74,7 @@ public class MendProjector extends Block{ @Override public void drawPlace(int x, int y, int rotation, boolean valid){ super.drawPlace(x, y, rotation, valid); - + Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, baseColor); indexer.eachBlock(player.team(), x * tilesize + offset, y * tilesize + offset, range, other -> true, other -> Drawf.selected(other, Tmp.c1.set(baseColor).a(Mathf.absin(4f, 1f)))); @@ -101,11 +106,18 @@ public class MendProjector extends Block{ float realRange = range + phaseHeat * phaseRangeBoost; charge = 0f; + any = false; + indexer.eachBlock(this, realRange, b -> b.damaged() && !b.isHealSuppressed(), other -> { other.heal(other.maxHealth() * (healPercent + phaseHeat * phaseBoost) / 100f * efficiency); other.recentlyHealed(); Fx.healBlockFull.at(other.x, other.y, other.block.size, baseColor, other.block); + any = true; }); + + if(any){ + mendSound.at(this, 1f + Mathf.range(0.1f), mendSoundVolume); + } } } diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index 38d3477f59..0493fd9c70 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -82,6 +82,8 @@ public class CoreBlock extends StorageBlock{ //support everything replaceable = false; + destroySound = Sounds.coreExplode; + destroySoundVolume = 1.6f; } @Remote(called = Loc.server) @@ -621,6 +623,9 @@ public class CoreBlock extends StorageBlock{ super.onDestroyed(); } + Effect.shockwaveDust(x, y, 40f + block.size * tilesize, 0.5f); + Fx.coreExplosion.at(x, y, team.color); + //add a spawn to the map for future reference - waves should be disabled, so it shouldn't matter if(state.isCampaign() && team == state.rules.waveTeam && team.cores().size <= 1 && spawner.getSpawns().size == 0 && state.rules.sector.planet.enemyCoreSpawnReplace){ //do not recache @@ -634,6 +639,16 @@ public class CoreBlock extends StorageBlock{ Events.fire(new CoreChangeEvent(this)); } + @Override + public void playDestroySound(){ + if(team.data().cores.size <= 1 && player != null && player.team() == team && state.rules.canGameOver){ + //play at full volume when doing a game over + block.destroySound.play(block.destroySoundVolume, Mathf.random(block.destroyPitchMin, block.destroyPitchMax), 0f); + }else{ + super.playDestroySound(); + } + } + @Override public void afterDestroyed(){ super.afterDestroyed(); diff --git a/core/src/mindustry/world/blocks/units/RepairTurret.java b/core/src/mindustry/world/blocks/units/RepairTurret.java index 7dd8b27a2e..309bb9b7b9 100644 --- a/core/src/mindustry/world/blocks/units/RepairTurret.java +++ b/core/src/mindustry/world/blocks/units/RepairTurret.java @@ -63,6 +63,8 @@ public class RepairTurret extends Block{ group = BlockGroup.projectors; envEnabled |= Env.space; + ambientSound = Sounds.healBeam; + ambientSoundVolume = 1f; } @Override @@ -176,6 +178,11 @@ public class RepairTurret extends Block{ Drawf.dashCircle(x, y, repairRadius, Pal.accent); } + @Override + public boolean shouldAmbientSound(){ + return target != null && efficiency > 0f; + } + @Override public void updateTile(){ float multiplier = 1f;