diff --git a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/BergamuteBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/BergamuteBlockEntity.java index 84c99da3c1..ba482004b3 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/BergamuteBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/BergamuteBlockEntity.java @@ -11,8 +11,10 @@ import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; import vazkii.botania.api.block_entity.RadiusDescriptor; import vazkii.botania.api.block_entity.SpecialFlowerBlockEntity; @@ -78,6 +80,42 @@ public static boolean isBergamuteNearby(Level level, double x, double y, double return getBergamutesNearby(level, x, y, z, 1).getFirst() > 0; } + public static boolean isBergamuteOccludingVibration(Level level, Vec3 sourcePos, Vec3 destPos) { + BlockPos sourceBlockPos = BlockPos.containing(sourcePos); + BlockPos destBlockPos = BlockPos.containing(destPos); + + // vibration occlusions assume block centers as source and target positions, so do that here as well + Vec3 sourceCenterPos = sourceBlockPos.getCenter(); + if (sourceBlockPos.equals(destBlockPos)) { + // trivial case: source and dest are in the same block, check Bergamute proximity to that block's center + return isBergamuteNearby(level, sourceCenterPos.x, sourceCenterPos.y, sourceCenterPos.z); + } + + // find the point on the line between source and destination that is closest to each Bergamute, + // and check whether it's actually in range of that Bergamute + // (based on https://stackoverflow.com/questions/51905268/how-to-find-closest-point-on-line) + Vec3 destCenterPos = destBlockPos.getCenter(); + Vec3 vibrationTravelVector = sourceCenterPos.vectorTo(destCenterPos); + double vibrationTravelDist = vibrationTravelVector.length(); + Vec3 vibrationTravelDir = vibrationTravelVector.normalize(); + + for (BergamuteBlockEntity f : level.isClientSide ? clientFlowers : serverFlowers) { + if (f.disabled || f.level != level) { + continue; + } + + Vec3 flowerPos = f.getEffectivePos().getCenter(); + Vec3 vecSourceToFlower = sourceCenterPos.vectorTo(flowerPos); + double travelPosition = Mth.clamp(vibrationTravelDir.dot(vecSourceToFlower), 0, vibrationTravelDist); + Vec3 closestPos = sourceCenterPos.add(vibrationTravelDir.scale(travelPosition)); + if (flowerPos.distanceToSqr(closestPos) <= RANGE * RANGE) { + return true; + } + } + + return false; + } + public static void particle(BergamuteBlockEntity berg) { int color = ManaPoolBlockEntity.PARTICLE_COLOR; float red = (color >> 16 & 0xFF) / 255F; diff --git a/Xplat/src/main/java/vazkii/botania/mixin/VibrationSystemListenerMixin.java b/Xplat/src/main/java/vazkii/botania/mixin/VibrationSystemListenerMixin.java new file mode 100644 index 0000000000..db0a462ff0 --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/mixin/VibrationSystemListenerMixin.java @@ -0,0 +1,26 @@ +package vazkii.botania.mixin; + +import net.minecraft.world.level.Level; +import net.minecraft.world.level.gameevent.vibrations.VibrationSystem; +import net.minecraft.world.phys.Vec3; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import vazkii.botania.common.block.flower.functional.BergamuteBlockEntity; + +@Mixin(VibrationSystem.Listener.class) +public class VibrationSystemListenerMixin { + /** + * Check if any active Bergamutes are near the direct line of sight between a vibration source + * and the vibration listener currently checking whether it can receive the vibration. + */ + @Inject(method = "isOccluded", at = @At("HEAD"), cancellable = true) + private static void checkBergamuteOcclusion(Level level, Vec3 sourcePos, Vec3 destPos, CallbackInfoReturnable cir) { + if (BergamuteBlockEntity.isBergamuteOccludingVibration(level, sourcePos, destPos)) { + cir.setReturnValue(true); + } + } +} diff --git a/Xplat/src/main/resources/assets/botania/lang/en_us.json b/Xplat/src/main/resources/assets/botania/lang/en_us.json index 43f444d768..dc75fdab5d 100644 --- a/Xplat/src/main/resources/assets/botania/lang/en_us.json +++ b/Xplat/src/main/resources/assets/botania/lang/en_us.json @@ -2414,7 +2414,7 @@ "botania.entry.bergamute": "Bergamute", "botania.tagline.bergamute": "Absorbs sound", "botania.page.bergamute0": "Anyone who's ever attempted ranching knows of the cacophonous din emitted by herds of animals. Luckily, the $(item)Bergamute$(0) can deafen such dins.", - "botania.page.bergamute1": "The $(item)Bergamute$(0) absorbs sound energy emitted in a close radius around itself, converting it into trace amounts of mana and dispersing it harmlessly. It halves the volume of sounds within reach, the effect stacking with other nearby $(item)Bergamutes$(0).$(p)Additionally, $(l:tools/grass_horn)$(item)Horns$(0)$(/l) or $(l:devices/forest_drum)$(item)Drums$(0)$(/l) will not break blocks within its range.", + "botania.page.bergamute1": "The $(item)Bergamute$(0) absorbs sound energy emitted in a close radius around itself, converting it into trace amounts of mana and dispersing it harmlessly. It halves the volume of sounds within reach, the effect stacking with other nearby $(item)Bergamutes$(0).$(p)Additionally, $(l:tools/grass_horn)$(item)Horns$(0)$(/l) or $(l:devices/forest_drum)$(item)Drums$(0)$(/l) will not break blocks within its range, and vibrations passing through its area of effect are muffled.", "botania.page.bergamute2": "Deaf to All but the Song", "botania.entry.gIntro": "Generating Flora", diff --git a/Xplat/src/main/resources/botania_xplat.mixins.json b/Xplat/src/main/resources/botania_xplat.mixins.json index 74287d5f38..8561352d1f 100644 --- a/Xplat/src/main/resources/botania_xplat.mixins.json +++ b/Xplat/src/main/resources/botania_xplat.mixins.json @@ -53,6 +53,7 @@ "StatsAccessor", "TextureSlotAccessor", "ThrowableProjectileMixin", + "VibrationSystemListenerMixin", "WitherEntityAccessor" ], "client": [ diff --git a/web/changelog.md b/web/changelog.md index 7898db2788..4204a74deb 100644 --- a/web/changelog.md +++ b/web/changelog.md @@ -19,6 +19,7 @@ and start a new "Upcoming" section. {% include changelog_header.html version="Upcoming" %} * Add: Horn and Drum of the Wild can break moss carpet, with the option to add more blocks through a block tag +* Add: Bergamute occludes vibrations within its range * Change: Cellular blocks around the boundary of a Dandelifeon's simulation area are ignored, unless they belong to another active Dandelifeon (NEstoll) * Change: Charm of the Diva also supports charming or targeting neutral mobs that are angry at the player or attacking one of the player's tamed animals, and properly prevents the player's tamed animals from being affected or targeted by the charm * Change: Dandelifeon recipe also requires a redstone root (zacharybarbanell)