feat(sounds): Added simple sounds

This commit is contained in:
Jakub Žitník 2025-03-26 18:11:00 +01:00
parent 0fd5689db7
commit 95e1e52205
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
11 changed files with 162 additions and 0 deletions

View File

@ -13,8 +13,10 @@ import cz.jzitnik.game.sprites.Breaking;
import cz.jzitnik.game.sprites.Steve.SteveState; import cz.jzitnik.game.sprites.Steve.SteveState;
import cz.jzitnik.game.annotations.AutoTransient; import cz.jzitnik.game.annotations.AutoTransient;
import cz.jzitnik.game.annotations.BreaksByPlace; import cz.jzitnik.game.annotations.BreaksByPlace;
import cz.jzitnik.game.annotations.MineSound;
import cz.jzitnik.game.blocks.Chest; import cz.jzitnik.game.blocks.Chest;
import cz.jzitnik.game.blocks.Furnace; import cz.jzitnik.game.blocks.Furnace;
import cz.jzitnik.game.config.Configuration;
import cz.jzitnik.game.core.autotransient.AutoTransientSupport; import cz.jzitnik.game.core.autotransient.AutoTransientSupport;
import cz.jzitnik.game.core.autotransient.initilizers.GameMiningInitializer; import cz.jzitnik.game.core.autotransient.initilizers.GameMiningInitializer;
import cz.jzitnik.game.core.autotransient.initilizers.GameWindowInitializer; import cz.jzitnik.game.core.autotransient.initilizers.GameWindowInitializer;
@ -48,6 +50,7 @@ public class Game extends AutoTransientSupport {
private transient GameStates gameStates = new GameStates(this); private transient GameStates gameStates = new GameStates(this);
@Setter @Setter
private int daytime = 0; // 0-600 private int daytime = 0; // 0-600
private Configuration configuration = new Configuration();
public Game() { public Game() {
Generation.generateWorld(this); Generation.generateWorld(this);
@ -272,6 +275,16 @@ public class Game extends AutoTransientSupport {
gameStates.dependencies.eventHandlerProvider.handleMine(screenRenderer, this, x, y); gameStates.dependencies.eventHandlerProvider.handleMine(screenRenderer, this, x, y);
for (Block block : blocksCopy) {
if (block.getClass().isAnnotationPresent(MineSound.class)) {
var key = block.getClass().getAnnotation(MineSound.class).value();
new Thread(() -> {
gameStates.dependencies.sound.playSound(configuration, key);
}).start();
}
}
screenRenderer.render(this); screenRenderer.render(this);
update(screenRenderer); update(screenRenderer);

View File

@ -0,0 +1,15 @@
package cz.jzitnik.game.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cz.jzitnik.game.core.sound.SoundKey;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@RequireAnnotation(BlockRegistry.class)
public @interface MineSound {
SoundKey value();
}

View File

@ -0,0 +1,18 @@
package cz.jzitnik.game.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cz.jzitnik.game.core.sound.SoundKey;
import cz.jzitnik.game.core.sound.SoundType;
import java.lang.annotation.ElementType;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SoundRegistry {
SoundKey key();
SoundType type();
String resourceLocation();
}

View File

@ -0,0 +1,10 @@
package cz.jzitnik.game.config;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Configuration {
private int soundVolume = 100; // 0-100
}

View File

@ -0,0 +1,50 @@
package cz.jzitnik.game.core.sound;
import java.io.IOException;
import java.util.HashMap;
import java.util.Set;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import org.reflections.Reflections;
import cz.jzitnik.game.annotations.SoundRegistry;
import cz.jzitnik.game.config.Configuration;
import cz.jzitnik.tui.SoundPlayer;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Sound {
private HashMap<SoundKey, SoundRegistry> map = new HashMap<>();
public Sound() {
register();
}
private void register() {
Reflections reflections = new Reflections("cz.jzitnik.game.core.sound.registry");
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(SoundRegistry.class);
for (Class<?> clazz : handlerClasses) {
log.info("Loaded sound {}", clazz.getSimpleName());
var annotation = clazz.getAnnotation(SoundRegistry.class);
map.put(annotation.key(), annotation);
}
}
public void playSound(Configuration configuration, SoundKey soundKey) {
var volume = configuration.getSoundVolume();
var annotation = map.get(soundKey);
try {
Clip clip = SoundPlayer.playSound(annotation.resourceLocation(), volume);
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,5 @@
package cz.jzitnik.game.core.sound;
public enum SoundKey {
DIRT_MINE
}

View File

@ -0,0 +1,5 @@
package cz.jzitnik.game.core.sound;
public enum SoundType {
BLOCK
}

View File

@ -0,0 +1,9 @@
package cz.jzitnik.game.core.sound.registry;
import cz.jzitnik.game.annotations.SoundRegistry;
import cz.jzitnik.game.core.sound.SoundKey;
import cz.jzitnik.game.core.sound.SoundType;
@SoundRegistry(key = SoundKey.DIRT_MINE, resourceLocation = "dirt_mine.wav", type = SoundType.BLOCK)
public class DirtMineSound {
}

View File

@ -1,6 +1,7 @@
package cz.jzitnik.game.entities; package cz.jzitnik.game.entities;
import cz.jzitnik.game.GameSaver; import cz.jzitnik.game.GameSaver;
import cz.jzitnik.game.core.sound.Sound;
import cz.jzitnik.game.handlers.events.EventHandlerProvider; import cz.jzitnik.game.handlers.events.EventHandlerProvider;
import cz.jzitnik.game.handlers.pickup.PickupHandlerProvider; import cz.jzitnik.game.handlers.pickup.PickupHandlerProvider;
import cz.jzitnik.game.handlers.place.PlaceHandler; import cz.jzitnik.game.handlers.place.PlaceHandler;
@ -18,4 +19,5 @@ public class Dependencies {
public EventHandlerProvider eventHandlerProvider = new EventHandlerProvider(); public EventHandlerProvider eventHandlerProvider = new EventHandlerProvider();
public Smelting smelting = new Smelting(); public Smelting smelting = new Smelting();
public ToolUseProvider toolUseProvider = new ToolUseProvider(); public ToolUseProvider toolUseProvider = new ToolUseProvider();
public Sound sound = new Sound();
} }

View File

@ -2,13 +2,16 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.MineSound;
import cz.jzitnik.game.annotations.ResetDataOnMine; import cz.jzitnik.game.annotations.ResetDataOnMine;
import cz.jzitnik.game.core.sound.SoundKey;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.ItemType; import cz.jzitnik.game.entities.items.ItemType;
import cz.jzitnik.game.logic.services.grass.GrassDirtData; import cz.jzitnik.game.logic.services.grass.GrassDirtData;
import java.util.ArrayList; import java.util.ArrayList;
@MineSound(SoundKey.DIRT_MINE)
@ResetDataOnMine @ResetDataOnMine
@BlockRegistry("dirt") @BlockRegistry("dirt")
public class DirtBlock extends Block { public class DirtBlock extends Block {

View File

@ -0,0 +1,32 @@
package cz.jzitnik.tui;
import javax.sound.sampled.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
public class SoundPlayer {
public static Clip playSound(String filePath, int volume)
throws LineUnavailableException, IOException, UnsupportedAudioFileException, InterruptedException {
log.info("Loading resource: {}", "sounds/" + filePath);
var soundFile = SoundPlayer.class.getClassLoader().getResourceAsStream("sounds/" + filePath);
AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundFile);
Clip clip = AudioSystem.getClip();
clip.open(audioStream);
FloatControl volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
float min = volumeControl.getMinimum();
float max = volumeControl.getMaximum();
float gain = min + (max - min) * (volume / 100.0f);
volumeControl.setValue(gain);
log.info("Starting to play {}", filePath);
clip.start();
// Thread.sleep(clip.getMicrosecondLength() / 1000);
return clip;
}
}