diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index f72440bf9..b051bf54f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -118,7 +118,7 @@ public final class CraftEngineBlocks { FastNMS.INSTANCE.method$BlockStateBase$onPlace(blockState, worldServer, blockPos, oldBlockState, false); if (playSound) { SoundData data = block.sounds().placeSound(); - location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume(), data.pitch()); + location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); } } return success; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 7bb781fd2..5447f2cec 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -311,7 +311,7 @@ public class BlockEventListener implements Listener { event.setCancelled(true); return; } - player.playSound(location, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch()); + player.playSound(location, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume().get(), state.sounds().stepSound().pitch().get()); } else if (Config.enableSoundSystem()) { Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); if (manager.isBlockSoundRemoved(ownerBlock)) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java index ab5571ac0..7b2ee5751 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java @@ -329,8 +329,8 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior { SoundData openSound = null; SoundData closeSound = null; if (sounds != null) { - openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, 1, 1)).orElse(null); - closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, 1, 1)).orElse(null); + openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null); + closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null); } return new DoorBlockBehavior(block, half, facing, hinge, powered, open, canOpenWithHand, canOpenByWindCharge, openSound, closeSound); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java index a75504d60..cd6b51932 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java @@ -18,6 +18,7 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import org.bukkit.Location; import org.bukkit.SoundCategory; @@ -74,8 +75,8 @@ public class StackableBlockBehavior extends BukkitBlockBehavior { ImmutableBlockState nextStage = state.cycle(this.amountProperty); Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z()); FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_NONE.flags()); - if (stackSound != null) { - location.getWorld().playSound(location, stackSound.id().toString(), SoundCategory.BLOCKS, stackSound.volume(), stackSound.pitch()); + if (this.stackSound != null) { + world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound); } FastNMS.INSTANCE.method$ItemStack$consume(item.getLiteralObject(), 1, player.serverPlayer()); player.swingHand(hand); @@ -90,7 +91,7 @@ public class StackableBlockBehavior extends BukkitBlockBehavior { Map sounds = (Map) arguments.get("sounds"); SoundData stackSound = null; if (sounds != null) { - stackSound = Optional.ofNullable(sounds.get("stack")).map(obj -> SoundData.create(obj, 1, 1)).orElse(null); + stackSound = Optional.ofNullable(sounds.get("stack")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null); } Object itemsObj = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("items"), "warning.config.block.behavior.stackable.missing_items"); List items = MiscUtils.getAsStringList(itemsObj).stream().map(Key::of).toList(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java index f1e886827..638196aa9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java @@ -247,8 +247,8 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior { SoundData openSound = null; SoundData closeSound = null; if (sounds != null) { - openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, 1, 1)).orElse(null); - closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, 1, 1)).orElse(null); + openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null); + closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null); } return new TrapDoorBlockBehavior(block, half, facing, powered, open, canOpenWithHand, canOpenByWindCharge, openSound, closeSound); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 8b55f60df..1a1744ced 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -84,7 +84,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { }); if (playSound) { SoundData data = furniture.settings().sounds().placeSound(); - location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume(), data.pitch()); + location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); } return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java index 04ffd23a1..a7e58df55 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSounds.java @@ -13,7 +13,7 @@ public class BlockSounds { Hit 0.5 0.5 Break 1 0.8 */ - public static final SoundData EMPTY_SOUND = new SoundData(Key.of("minecraft:intentionally_empty"), 1, 1); + public static final SoundData EMPTY_SOUND = new SoundData(Key.of("minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1); public static final BlockSounds EMPTY = new BlockSounds(EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND); private final SoundData breakSound; @@ -37,13 +37,13 @@ public class BlockSounds { public static BlockSounds fromMap(Map map) { if (map == null) return EMPTY; return new BlockSounds( - SoundData.create(map.getOrDefault("break", "minecraft:intentionally_empty"), 1f, 0.8f), - SoundData.create(map.getOrDefault("step", "minecraft:intentionally_empty"), 0.15f, 1f), - SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), 1f, 0.8f), - SoundData.create(map.getOrDefault("hit", "minecraft:intentionally_empty"), 0.5f, 0.5f), - SoundData.create(map.getOrDefault("fall", "minecraft:intentionally_empty"), 0.5f, 0.75f), - SoundData.create(map.getOrDefault("land", "minecraft:intentionally_empty"), 0.3f, 1f), - SoundData.create(map.getOrDefault("destroy", "minecraft:intentionally_empty"), 1f, 1f) + SoundData.create(map.getOrDefault("break", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), + SoundData.create(map.getOrDefault("step", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_15, SoundData.SoundValue.FIXED_1), + SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), + SoundData.create(map.getOrDefault("hit", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_5), + SoundData.create(map.getOrDefault("fall", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.FIXED_0_75), + SoundData.create(map.getOrDefault("land", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_0_3, SoundData.SoundValue.FIXED_1), + SoundData.create(map.getOrDefault("destroy", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1) ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java index 97cd3a6b0..4434eaed4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.util.Key; import java.util.Map; public class FurnitureSounds { - public static final SoundData EMPTY_SOUND = new SoundData(Key.of("minecraft:intentionally_empty"), 1, 1); + public static final SoundData EMPTY_SOUND = new SoundData(Key.of("minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1); public static final FurnitureSounds EMPTY = new FurnitureSounds(EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND); private final SoundData breakSound; @@ -22,9 +22,9 @@ public class FurnitureSounds { public static FurnitureSounds fromMap(Map map) { if (map == null) return EMPTY; return new FurnitureSounds( - SoundData.create(map.getOrDefault("break", "minecraft:intentionally_empty"), 1f, 0.8f), - SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), 1f, 0.8f), - SoundData.create(map.getOrDefault("rotate", "minecraft:intentionally_empty"), 1f, 0.8f) + SoundData.create(map.getOrDefault("break", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), + SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), + SoundData.create(map.getOrDefault("rotate", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8) ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index b908dbcd1..bb4f1d20c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -332,7 +332,7 @@ public class ItemSettings { })); registerFactory("helmet", (value -> { Map args = MiscUtils.castToMap(value, false); - return settings -> settings.helmet(new Helmet(SoundData.create(args.getOrDefault("equip-sound", "minecraft:intentionally_empty"), 1f, 1f))); + return settings -> settings.helmet(new Helmet(SoundData.create(args.getOrDefault("equip-sound", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1))); })); registerFactory("compost-probability", (value -> { float chance = ResourceConfigUtils.getAsFloat(value, "compost-probability"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index 6f0863a6d..7c3f386ac 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -2,24 +2,86 @@ package net.momirealms.craftengine.core.sound; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; -public record SoundData(Key id, float volume, float pitch) { +public record SoundData(Key id, SoundValue volume, SoundValue pitch) { - // todo 支持范围音量音调 - public static SoundData create(Object obj, float volume, float pitch) { + public static SoundData create(Object obj, SoundValue volume, SoundValue pitch) { if (obj instanceof String key) { return new SoundData(Key.of(key), volume, pitch); } else if (obj instanceof Map map) { Map data = MiscUtils.castToMap(map, false); Key id = Key.of((String) data.get("id")); - float volumeFloat = ResourceConfigUtils.getAsFloat(data.getOrDefault("volume", volume), "volume"); - float pitchFloat = ResourceConfigUtils.getAsFloat(data.getOrDefault("pitch", pitch), "pitch"); - return new SoundData(id, volumeFloat, pitchFloat); + SoundValue volumeValue = Optional.ofNullable(SoundValue.of(map.get("volume"))).orElse(volume); + SoundValue pitchValue = Optional.ofNullable(SoundValue.of(map.get("pitch"))).orElse(volume); + return new SoundData(id, volumeValue, pitchValue); } else { throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass()); } } + + public interface SoundValue extends Supplier { + Map FIXED = new HashMap<>(); + SoundValue FIXED_1 = new Fixed(1f); + SoundValue FIXED_0_8 = new Fixed(0.8f); + SoundValue FIXED_0_75 = new Fixed(0.75f); + SoundValue FIXED_0_15 = new Fixed(0.15f); + SoundValue FIXED_0_5 = new Fixed(0.5f); + SoundValue FIXED_0_3 = new Fixed(0.3f); + + static SoundValue of(Object obj) { + if (obj instanceof Number number) { + return SoundValue.fixed(number.floatValue()); + } else { + String volumeString = obj.toString(); + if (volumeString.contains("~")) { + String[] split = volumeString.split("~"); + return SoundValue.ranged(Float.parseFloat(split[0]), Float.parseFloat(split[1])); + } + } + return null; + } + + static SoundValue fixed(float value) { + return FIXED.computeIfAbsent(value, v -> new Fixed(value)); + } + + static SoundValue ranged(float min, float max) { + return new Ranged(min, max); + } + + class Fixed implements SoundValue { + private final float value; + + public Fixed(float value) { + this.value = value; + } + + @Override + public Float get() { + return this.value; + } + } + + class Ranged implements SoundValue { + private final float min; + private final float max; + + public Ranged(float min, float max) { + this.min = min; + this.max = max; + } + + @Override + public Float get() { + return RandomUtils.generateRandomFloat(this.min, this.max); + } + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/World.java b/core/src/main/java/net/momirealms/craftengine/core/world/World.java index 1a20bf82d..fb4923698 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/World.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/World.java @@ -44,7 +44,7 @@ public interface World { void playBlockSound(Position location, Key sound, float volume, float pitch); default void playBlockSound(Position location, SoundData data) { - playBlockSound(location, data.id(), data.volume(), data.pitch()); + playBlockSound(location, data.id(), data.volume().get(), data.pitch().get()); } void levelEvent(int id, BlockPos pos, int data);