diff --git a/client/src/main/java/nl/andrewlalis/aos_client/Client.java b/client/src/main/java/nl/andrewlalis/aos_client/Client.java index ae90fe8..3642e87 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/Client.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/Client.java @@ -80,7 +80,7 @@ public class Client { team.setScore(t.getScore()); } } - this.soundManager.play(update.getSoundsToPlay()); + this.soundManager.play(update.getSoundsToPlay(), myPlayer); } public void setWorld(World world) { diff --git a/client/src/main/java/nl/andrewlalis/aos_client/SoundManager.java b/client/src/main/java/nl/andrewlalis/aos_client/SoundManager.java index 1020f18..67aa2e4 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/SoundManager.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/SoundManager.java @@ -1,32 +1,56 @@ package nl.andrewlalis.aos_client; +import nl.andrewlalis.aos_core.geom.Vec2; +import nl.andrewlalis.aos_core.model.Player; import nl.andrewlalis.aos_core.net.data.Sound; +import nl.andrewlalis.aos_core.net.data.SoundType; import javax.sound.sampled.*; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class SoundManager { - private static final int CLIP_COUNT = 10; + private static final float HEARING_RANGE = 50.0f; private final Map> soundData = new HashMap<>(); private final Map clipIndexes = new HashMap<>(); + public void play(List sounds, Player player) { + for (Sound sound : sounds) { + this.play(sound, player); + } + } public void play(List sounds) { for (Sound sound : sounds) { - this.play(sound); + this.play(sound, null); } } public void play(Sound sound) { - var clip = this.getClip(sound.getType().getSoundName()); + this.play(sound, null); + } + + public void play(Sound sound, Player player) { + var clip = this.getClip(sound.getType()); if (clip == null) { return; } clip.setFramePosition(0); - setVolume(clip, sound.getVolume()); + float v = sound.getVolume(); + if (player != null && sound.getPosition() != null) { + float dist = player.getPosition().dist(sound.getPosition()); + v *= (Math.max(HEARING_RANGE - dist, 0) / HEARING_RANGE); + } + if (v <= 0.0f) return; + if (player != null && player.getTeam() != null && sound.getPosition() != null) { + setPan(clip, player.getPosition(), sound.getPosition(), player.getTeam().getOrientation()); + } + setVolume(clip, v); clip.start(); } @@ -36,7 +60,19 @@ public class SoundManager { gainControl.setValue(20f * (float) Math.log10(volume)); } - private Clip getClip(String sound) { + private void setPan(Clip clip, Vec2 playerPos, Vec2 soundPos, Vec2 playerOrientation) { + Vec2 soundDir = soundPos + .sub(playerPos) + .rotate(playerOrientation.perp().angle()) + .unit(); + float pan = Math.max(Math.min(soundDir.dot(Vec2.RIGHT), 1.0f), -1.0f); + if (Float.isNaN(pan)) pan = 0f; + FloatControl panControl = (FloatControl) clip.getControl(FloatControl.Type.PAN); + panControl.setValue(pan); + } + + private Clip getClip(SoundType soundType) { + String sound = soundType.getSoundName(); var clips = this.soundData.get(sound); if (clips == null) { InputStream is = Client.class.getResourceAsStream("/nl/andrewlalis/aos_client/sound/" + sound); @@ -48,8 +84,8 @@ public class SoundManager { ByteArrayOutputStream bos = new ByteArrayOutputStream(); is.transferTo(bos); byte[] data = bos.toByteArray(); - clips = new ArrayList<>(CLIP_COUNT); - for (int i = 0; i < CLIP_COUNT; i++) { + clips = new ArrayList<>(soundType.getClipBufferCount()); + for (int i = 0; i < soundType.getClipBufferCount(); i++) { var ais = AudioSystem.getAudioInputStream(new ByteArrayInputStream(data)); var clip = AudioSystem.getClip(); clip.open(ais); @@ -64,7 +100,7 @@ public class SoundManager { } } int index = this.clipIndexes.get(sound); - if (index >= CLIP_COUNT) { + if (index >= soundType.getClipBufferCount()) { index = 0; } Clip clip = clips.get(index); diff --git a/core/src/main/java/nl/andrewlalis/aos_core/geom/Vec2.java b/core/src/main/java/nl/andrewlalis/aos_core/geom/Vec2.java index 537c399..9d4d62f 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/geom/Vec2.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/geom/Vec2.java @@ -9,6 +9,9 @@ import java.util.concurrent.ThreadLocalRandom; public record Vec2(float x, float y) implements Serializable { public static final Vec2 ZERO = new Vec2(0, 0); public static final Vec2 UP = new Vec2(0, -1); + public static final Vec2 DOWN = new Vec2(0, 1); + public static final Vec2 RIGHT = new Vec2(1, 0); + public static final Vec2 LEFT = new Vec2(-1, 0); public float mag() { diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/data/SoundType.java b/core/src/main/java/nl/andrewlalis/aos_core/net/data/SoundType.java index 285ccc1..a936299 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/net/data/SoundType.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/data/SoundType.java @@ -8,24 +8,26 @@ import java.util.Map; * efficient transmission to clients. */ public enum SoundType { - SHOT_SMG(0, "ak47shot1.wav"), - SHOT_RIFLE(1, "m1garand-shot1.wav"), - SHOT_SHOTGUN(2, "shotgun-shot1.wav"), - RELOAD(3, "reload.wav"), - CHAT(4, "chat.wav"), - DEATH(5, "death.wav"), - BULLET_IMPACT_1(6, "bullet_impact_1.wav"), - BULLET_IMPACT_2(7, "bullet_impact_2.wav"), - BULLET_IMPACT_3(8, "bullet_impact_3.wav"), - BULLET_IMPACT_4(9, "bullet_impact_4.wav"), - BULLET_IMPACT_5(10, "bullet_impact_5.wav"); + SHOT_SMG(0, "ak47shot1.wav", 25), + SHOT_RIFLE(1, "m1garand-shot1.wav", 25), + SHOT_SHOTGUN(2, "shotgun-shot1.wav", 25), + RELOAD(3, "reload.wav", 10), + CHAT(4, "chat.wav", 5), + DEATH(5, "death.wav", 5), + BULLET_IMPACT_1(6, "bullet_impact_1.wav", 10), + BULLET_IMPACT_2(7, "bullet_impact_2.wav", 10), + BULLET_IMPACT_3(8, "bullet_impact_3.wav", 10), + BULLET_IMPACT_4(9, "bullet_impact_4.wav", 10), + BULLET_IMPACT_5(10, "bullet_impact_5.wav", 10); private final byte code; private final String soundName; + private final int clipBufferCount; - SoundType(int code, String soundName) { + SoundType(int code, String soundName, int clipBufferCount) { this.code = (byte) code; this.soundName = soundName; + this.clipBufferCount = clipBufferCount; } public byte getCode() { @@ -36,6 +38,10 @@ public enum SoundType { return soundName; } + public int getClipBufferCount() { + return clipBufferCount; + } + private static final Map typeIndex = new HashMap<>(); static { for (var val : values()) { diff --git a/server/src/main/resources/default_settings.yaml b/server/src/main/resources/default_settings.yaml index 765061e..1d002be 100644 --- a/server/src/main/resources/default_settings.yaml +++ b/server/src/main/resources/default_settings.yaml @@ -60,6 +60,18 @@ gun-settings: bullet-speed: 90 base-damage: 40 + - name: M-249 + category: SMG + color: "#001942" + max-clip-count: 3 + clip-size: 100 + bullets-per-round: 1 + accuracy: 0.08 + shot-cooldown-time: 0.03 + reload-time: 3.5 + bullet-speed: 80 + base-damage: 35 + - name: M1 Garand category: RIFLE color: "#452d06"