diff --git a/src/main/java/eu/ha3/presencefootsteps/sound/Isolator.java b/src/main/java/eu/ha3/presencefootsteps/sound/Isolator.java index 2586ebe5..c455e787 100644 --- a/src/main/java/eu/ha3/presencefootsteps/sound/Isolator.java +++ b/src/main/java/eu/ha3/presencefootsteps/sound/Isolator.java @@ -13,6 +13,7 @@ import eu.ha3.presencefootsteps.util.JsonObjectWriter; import eu.ha3.presencefootsteps.util.ResourceUtils; import eu.ha3.presencefootsteps.util.BlockReport.Reportable; +import eu.ha3.presencefootsteps.world.BiomeVarianceLookup; import eu.ha3.presencefootsteps.world.GolemLookup; import eu.ha3.presencefootsteps.world.HeuristicStateLookup; import eu.ha3.presencefootsteps.world.Index; @@ -34,10 +35,12 @@ public record Isolator ( HeuristicStateLookup heuristics, Lookup> golems, Lookup blocks, + Index biomes, Lookup primitives, AcousticLibrary acoustics ) implements Reportable { private static final Identifier BLOCK_MAP = PresenceFootsteps.id("config/blockmap.json"); + private static final Identifier BIOME_MAP = PresenceFootsteps.id("config/biomevariancemap.json"); private static final Identifier GOLEM_MAP = PresenceFootsteps.id("config/golemmap.json"); private static final Identifier LOCOMOTION_MAP = PresenceFootsteps.id("config/locomotionmap.json"); private static final Identifier PRIMITIVE_MAP = PresenceFootsteps.id("config/primitivemap.json"); @@ -50,6 +53,7 @@ public Isolator(SoundEngine engine) { new HeuristicStateLookup(), new GolemLookup(), new StateLookup(), + new BiomeVarianceLookup(), new PrimitiveLookup(), new AcousticsPlayer(new DelayedSoundPlayer(engine.soundPlayer)) ); @@ -58,6 +62,7 @@ public Isolator(SoundEngine engine) { public boolean load(ResourceManager manager) { boolean hasConfigurations = false; hasConfigurations |= ResourceUtils.forEach(BLOCK_MAP, manager, blocks()::load); + hasConfigurations |= ResourceUtils.forEach(BIOME_MAP, manager, biomes()::load); hasConfigurations |= ResourceUtils.forEach(GOLEM_MAP, manager, golems()::load); hasConfigurations |= ResourceUtils.forEach(PRIMITIVE_MAP, manager, primitives()::load); hasConfigurations |= ResourceUtils.forEach(LOCOMOTION_MAP, manager, locomotions()::load); diff --git a/src/main/java/eu/ha3/presencefootsteps/sound/generator/StepSoundGenerator.java b/src/main/java/eu/ha3/presencefootsteps/sound/generator/StepSoundGenerator.java index 89bf5f5b..84110a0f 100644 --- a/src/main/java/eu/ha3/presencefootsteps/sound/generator/StepSoundGenerator.java +++ b/src/main/java/eu/ha3/presencefootsteps/sound/generator/StepSoundGenerator.java @@ -6,6 +6,11 @@ * @author Hurry */ public interface StepSoundGenerator { + + float getLocalPitch(float tickDelta); + + float getLocalVolume(float tickDelta); + /** * Gets the motion tracker used to determine the direction and speed for an entity during simulation. */ diff --git a/src/main/java/eu/ha3/presencefootsteps/sound/generator/TerrestrialStepSoundGenerator.java b/src/main/java/eu/ha3/presencefootsteps/sound/generator/TerrestrialStepSoundGenerator.java index ae25a21e..d3742625 100644 --- a/src/main/java/eu/ha3/presencefootsteps/sound/generator/TerrestrialStepSoundGenerator.java +++ b/src/main/java/eu/ha3/presencefootsteps/sound/generator/TerrestrialStepSoundGenerator.java @@ -7,6 +7,7 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.entity.passive.AbstractHorseEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.registry.RegistryKey; import net.minecraft.registry.tag.FluidTags; import net.minecraft.util.math.BlockPos; import org.jetbrains.annotations.Nullable; @@ -15,11 +16,13 @@ import eu.ha3.presencefootsteps.config.Variator; import eu.ha3.presencefootsteps.mixins.ILivingEntity; import eu.ha3.presencefootsteps.sound.State; +import eu.ha3.presencefootsteps.util.Lerp; import eu.ha3.presencefootsteps.util.PlayerUtil; import eu.ha3.presencefootsteps.sound.Options; import eu.ha3.presencefootsteps.sound.SoundEngine; import eu.ha3.presencefootsteps.world.Association; import eu.ha3.presencefootsteps.world.AssociationPool; +import eu.ha3.presencefootsteps.world.BiomeVarianceLookup; import eu.ha3.presencefootsteps.world.Solver; import eu.ha3.presencefootsteps.world.SoundsKey; import eu.ha3.presencefootsteps.world.Substrates; @@ -58,6 +61,9 @@ class TerrestrialStepSoundGenerator implements StepSoundGenerator { protected final MotionTracker motionTracker = new MotionTracker(this); protected final AssociationPool associations; + private final Lerp biomePitch = new Lerp(); + private final Lerp biomeVolume = new Lerp(); + public TerrestrialStepSoundGenerator(LivingEntity entity, SoundEngine engine, Modifier modifier) { this.entity = entity; this.engine = engine; @@ -65,6 +71,16 @@ public TerrestrialStepSoundGenerator(LivingEntity entity, SoundEngine engine, Mo this.associations = new AssociationPool(entity, engine); } + @Override + public float getLocalPitch(float tickDelta) { + return biomePitch.get(tickDelta); + } + + @Override + public float getLocalVolume(float tickDelta) { + return biomeVolume.get(tickDelta); + } + @Override public MotionTracker getMotionTracker() { return motionTracker; @@ -72,6 +88,13 @@ public MotionTracker getMotionTracker() { @Override public void generateFootsteps() { + BiomeVarianceLookup.BiomeVariance variance = entity.getWorld().getBiome(entity.getBlockPos()).getKey().map(RegistryKey::getValue).map(key -> { + return engine.getIsolator().biomes().lookup(key); + }).orElse(BiomeVarianceLookup.BiomeVariance.DEFAULT); + + biomePitch.update(variance.pitch(), 0.01F); + biomeVolume.update(variance.volume(), 0.01F); + motionTracker.simulateMotionData(entity); simulateFootsteps(); simulateAirborne(); diff --git a/src/main/java/eu/ha3/presencefootsteps/sound/player/ImmediateSoundPlayer.java b/src/main/java/eu/ha3/presencefootsteps/sound/player/ImmediateSoundPlayer.java index 7b9d735e..868a28d9 100644 --- a/src/main/java/eu/ha3/presencefootsteps/sound/player/ImmediateSoundPlayer.java +++ b/src/main/java/eu/ha3/presencefootsteps/sound/player/ImmediateSoundPlayer.java @@ -11,6 +11,8 @@ import eu.ha3.presencefootsteps.util.PlayerUtil; import eu.ha3.presencefootsteps.sound.Options; import eu.ha3.presencefootsteps.sound.SoundEngine; +import eu.ha3.presencefootsteps.sound.StepSoundSource; +import eu.ha3.presencefootsteps.sound.generator.StepSoundGenerator; /** * A Library that can also play sounds and default footsteps. @@ -41,6 +43,13 @@ public void playSound(LivingEntity location, String soundName, float volume, flo volume *= engine.getVolumeForSource(location); pitch /= ((PlayerUtil.getScale(location) - 1) * 0.6F) + 1; + StepSoundGenerator generator = ((StepSoundSource) location).getStepGenerator(engine).orElse(null); + if (generator != null) { + float tickDelta = mc.getRenderTickCounter().getTickDelta(false); + volume *= generator.getLocalVolume(tickDelta); + pitch *= generator.getLocalPitch(tickDelta); + } + PositionedSoundInstance sound = new UncappedSoundInstance(soundName, volume, pitch, location); if (distance > 100) { diff --git a/src/main/java/eu/ha3/presencefootsteps/util/Lerp.java b/src/main/java/eu/ha3/presencefootsteps/util/Lerp.java new file mode 100644 index 00000000..f077671a --- /dev/null +++ b/src/main/java/eu/ha3/presencefootsteps/util/Lerp.java @@ -0,0 +1,22 @@ +package eu.ha3.presencefootsteps.util; + +import net.minecraft.util.math.MathHelper; + +public class Lerp { + public float previous; + public float current; + + public void update(float newTarget, float rate) { + previous = current; + if (current < newTarget) { + current = Math.min(current + rate, newTarget); + } + if (current > newTarget) { + current = Math.max(current - rate, newTarget); + } + } + + public float get(float tickDelta) { + return MathHelper.lerp(tickDelta, previous, current); + } +} diff --git a/src/main/java/eu/ha3/presencefootsteps/world/AbstractSubstrateLookup.java b/src/main/java/eu/ha3/presencefootsteps/world/AbstractSubstrateLookup.java index 03a2ab21..7e619ba2 100644 --- a/src/main/java/eu/ha3/presencefootsteps/world/AbstractSubstrateLookup.java +++ b/src/main/java/eu/ha3/presencefootsteps/world/AbstractSubstrateLookup.java @@ -5,6 +5,8 @@ import org.jetbrains.annotations.Nullable; +import com.google.gson.JsonElement; + import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import net.minecraft.util.Identifier; @@ -46,14 +48,14 @@ public Set getSubstrates() { } @Override - public void add(String key, String value) { + public void add(String key, JsonElement value) { final String[] split = key.trim().split("@"); final String primitive = split[0]; final String substrate = split.length > 1 ? split[1] : Substrates.DEFAULT; substrates .computeIfAbsent(substrate, s -> new Object2ObjectLinkedOpenHashMap<>()) - .put(Identifier.of(primitive), SoundsKey.of(value)); + .put(Identifier.of(primitive), SoundsKey.of(value.getAsString())); } @Override diff --git a/src/main/java/eu/ha3/presencefootsteps/world/BiomeVarianceLookup.java b/src/main/java/eu/ha3/presencefootsteps/world/BiomeVarianceLookup.java new file mode 100644 index 00000000..069340ae --- /dev/null +++ b/src/main/java/eu/ha3/presencefootsteps/world/BiomeVarianceLookup.java @@ -0,0 +1,48 @@ +package eu.ha3.presencefootsteps.world; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.JsonElement; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import eu.ha3.presencefootsteps.util.JsonObjectWriter; +import net.minecraft.sound.BlockSoundGroup; +import net.minecraft.util.Identifier; + +public class BiomeVarianceLookup implements Index { + private final Map entries = new HashMap<>(); + + @Override + public BiomeVariance lookup(Identifier key) { + return entries.getOrDefault(key, BiomeVariance.DEFAULT); + } + + @Override + public boolean contains(Identifier key) { + return entries.containsKey(key); + } + + @Override + public void add(String key, JsonElement value) { + BiomeVariance.CODEC.decode(JsonOps.INSTANCE, value).result().map(Pair::getFirst).ifPresent(i -> { + entries.put(Identifier.of(key), i); + }); + } + + @Override + public void writeToReport(boolean full, JsonObjectWriter writer, Map groups) throws IOException { + } + + public record BiomeVariance(float volume, float pitch) { + public static final BiomeVariance DEFAULT = new BiomeVariance(1, 1); + static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + Codec.FLOAT.fieldOf("volume").forGetter(BiomeVariance::volume), + Codec.FLOAT.fieldOf("pitch").forGetter(BiomeVariance::pitch) + ).apply(i, BiomeVariance::new)); + } +} diff --git a/src/main/java/eu/ha3/presencefootsteps/world/Loadable.java b/src/main/java/eu/ha3/presencefootsteps/world/Loadable.java index 0d6b6449..c30f5c65 100644 --- a/src/main/java/eu/ha3/presencefootsteps/world/Loadable.java +++ b/src/main/java/eu/ha3/presencefootsteps/world/Loadable.java @@ -3,6 +3,7 @@ import java.io.Reader; import com.google.gson.Gson; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; public interface Loadable { @@ -11,7 +12,7 @@ public interface Loadable { /** * Register a blockmap entry. */ - void add(String key, String value); + void add(String key, JsonElement json); /** * Loads new entries from the given config reader. @@ -21,7 +22,7 @@ default void load(Reader reader) { JsonObject json = GSON.fromJson(reader, JsonObject.class); json.entrySet().forEach(entry -> { - add(entry.getKey(), entry.getValue().getAsString()); + add(entry.getKey(), entry.getValue()); }); } } diff --git a/src/main/java/eu/ha3/presencefootsteps/world/LocomotionLookup.java b/src/main/java/eu/ha3/presencefootsteps/world/LocomotionLookup.java index 945d4bf5..0300b3b0 100644 --- a/src/main/java/eu/ha3/presencefootsteps/world/LocomotionLookup.java +++ b/src/main/java/eu/ha3/presencefootsteps/world/LocomotionLookup.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.util.Map; +import com.google.gson.JsonElement; + public class LocomotionLookup implements Index { private final Map values = new Object2ObjectLinkedOpenHashMap<>(); @@ -36,14 +38,14 @@ public Locomotion lookup(Entity key) { } @Override - public void add(String key, String value) { + public void add(String key, JsonElement value) { Identifier id = Identifier.of(key); if (!Registries.ENTITY_TYPE.containsId(id)) { PresenceFootsteps.logger.warn("Locomotion registered for unknown entity type " + id); } - values.put(id, Locomotion.byName(value.toUpperCase())); + values.put(id, Locomotion.byName(value.getAsString().toUpperCase())); } @Override diff --git a/src/main/java/eu/ha3/presencefootsteps/world/StateLookup.java b/src/main/java/eu/ha3/presencefootsteps/world/StateLookup.java index f433037d..da92b44b 100644 --- a/src/main/java/eu/ha3/presencefootsteps/world/StateLookup.java +++ b/src/main/java/eu/ha3/presencefootsteps/world/StateLookup.java @@ -22,6 +22,8 @@ import org.jetbrains.annotations.Nullable; +import com.google.gson.JsonElement; + /** * A state lookup that finds an association for a given block state within a specific substrate (or no substrate). * @@ -39,8 +41,8 @@ public SoundsKey getAssociation(BlockState state, String substrate) { } @Override - public void add(String key, String value) { - SoundsKey sound = SoundsKey.of(value); + public void add(String key, JsonElement value) { + SoundsKey sound = SoundsKey.of(value.getAsString()); if (!sound.isResult()) { return; } diff --git a/src/main/resources/assets/presencefootsteps/config/biomevariancemap.json b/src/main/resources/assets/presencefootsteps/config/biomevariancemap.json new file mode 100644 index 00000000..53f36d2a --- /dev/null +++ b/src/main/resources/assets/presencefootsteps/config/biomevariancemap.json @@ -0,0 +1,6 @@ +{ + "minecraft:pale_garden": { + "volume": 0.5, + "pitch": 1 + } +}