diff --git a/fabric-entity-events-v1/src/client/java/net/fabricmc/fabric/api/entity/event/client/ClientPlayerEvents.java b/fabric-entity-events-v1/src/client/java/net/fabricmc/fabric/api/entity/event/client/ClientPlayerEvents.java new file mode 100644 index 0000000000..20a570d614 --- /dev/null +++ b/fabric-entity-events-v1/src/client/java/net/fabricmc/fabric/api/entity/event/client/ClientPlayerEvents.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.entity.event.client; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.client.network.ClientPlayerEntity; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; + +public final class ClientPlayerEvents { + /** + * An event that is called when a player is moving while using an item. + */ + public static final Event MODIFY_ITEM_USE_MOVEMENT_SPEED = EventFactory.createArrayBacked(ModifyItemUseMovementSpeed.class, callbacks -> player -> { + Float maxSpeedPercentage = null; + + for (ModifyItemUseMovementSpeed callback : callbacks) { + Float speedPercentage = callback.modifyItemUseMovementSpeed(player); + + if (speedPercentage != null) { + maxSpeedPercentage = maxSpeedPercentage == null ? speedPercentage : Math.max(speedPercentage, maxSpeedPercentage); + } + } + + return maxSpeedPercentage; + }); + + @FunctionalInterface + public interface ModifyItemUseMovementSpeed { + /** + * Called when a player is moving while using an item. + * + * @param player the player that is moving while using an item. + * @return a Float representing the speed modification as a percentage (e.g., 0.8 for 80% speed), + * or {@code null} indicates that no modification should be applied. + */ + @Nullable + Float modifyItemUseMovementSpeed(ClientPlayerEntity player); + } +} diff --git a/fabric-entity-events-v1/src/client/java/net/fabricmc/fabric/mixin/client/entity/event/ClientPlayerMixin.java b/fabric-entity-events-v1/src/client/java/net/fabricmc/fabric/mixin/client/entity/event/ClientPlayerMixin.java new file mode 100644 index 0000000000..911f4c534b --- /dev/null +++ b/fabric-entity-events-v1/src/client/java/net/fabricmc/fabric/mixin/client/entity/event/ClientPlayerMixin.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.entity.event; + +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.CallbackInfo; + +import net.minecraft.client.network.ClientPlayerEntity; + +import net.fabricmc.fabric.api.entity.event.client.ClientPlayerEvents; + +@Mixin(ClientPlayerEntity.class) +abstract class ClientPlayerMixin { + @Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isUsingItem()Z")) + private void invokeModifyItemUseMovementSpeedEvent(CallbackInfo ci) { + ClientPlayerEntity player = (ClientPlayerEntity) (Object) this; + + if (player.isUsingItem() && !player.hasVehicle() && (player.input.movementForward != 0.0F || player.input.movementSideways != 0.0F)) { + Float speedPercentage = ClientPlayerEvents.MODIFY_ITEM_USE_MOVEMENT_SPEED.invoker().modifyItemUseMovementSpeed(player); + + if (speedPercentage != null) { + player.input.movementSideways *= speedPercentage * 5.0F; + player.input.movementForward *= speedPercentage * 5.0F; + } + } + } +} diff --git a/fabric-entity-events-v1/src/client/resources/fabric-entity-events-v1.client.mixins.json b/fabric-entity-events-v1/src/client/resources/fabric-entity-events-v1.client.mixins.json index c4866c6e99..c86d331963 100644 --- a/fabric-entity-events-v1/src/client/resources/fabric-entity-events-v1.client.mixins.json +++ b/fabric-entity-events-v1/src/client/resources/fabric-entity-events-v1.client.mixins.json @@ -3,7 +3,8 @@ "package": "net.fabricmc.fabric.mixin.client.entity.event", "compatibilityLevel": "JAVA_17", "client": [ - "elytra.ClientPlayerEntityMixin" + "elytra.ClientPlayerEntityMixin", + "ClientPlayerMixin" ], "injectors": { "defaultRequire": 1, diff --git a/fabric-entity-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/entity/event/client/EntityEventTestsClient.java b/fabric-entity-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/entity/event/client/EntityEventTestsClient.java index ef9c828c1a..1a78032059 100644 --- a/fabric-entity-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/entity/event/client/EntityEventTestsClient.java +++ b/fabric-entity-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/entity/event/client/EntityEventTestsClient.java @@ -16,17 +16,64 @@ package net.fabricmc.fabric.test.entity.event.client; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.Items; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRenderEvents; +import net.fabricmc.fabric.api.entity.event.client.ClientPlayerEvents; import net.fabricmc.fabric.test.entity.event.EntityEventTests; public class EntityEventTestsClient implements ClientModInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(EntityEventTestsClient.class); + @Override public void onInitializeClient() { LivingEntityFeatureRenderEvents.ALLOW_CAPE_RENDER.register(player -> { return !player.getEquippedStack(EquipmentSlot.CHEST).isOf(EntityEventTests.DIAMOND_ELYTRA); }); + + // the most common usage is to modify using item speed based on main hand item, most for custom items. + ClientPlayerEvents.MODIFY_ITEM_USE_MOVEMENT_SPEED.register(player -> { + if (player.getMainHandStack().isOf(Items.GOLDEN_APPLE)) { + LOGGER.info("Player {} can move with half speed when eating golden apple.", player); + return 0.5F; + } + + if (player.getMainHandStack().isOf(Items.ENCHANTED_GOLDEN_APPLE)) { + LOGGER.info("Player {} can move with normal speed when eating enchanted golden apple.", player); + return 1.0F; + } + + return null; + }); + + // another possible usages is to modify using item speed when certain conditions (e.g. equipped items) are met. + ClientPlayerEvents.MODIFY_ITEM_USE_MOVEMENT_SPEED.register(player -> { + if (player.getMainHandStack().isOf(Items.BOW) && player.getEquippedStack(EquipmentSlot.FEET).isOf(Items.LEATHER_BOOTS)) { + LOGGER.info("Player {} can move with 80% speed becase of leather boots on feet.", player); + return 0.8F; + } + + if (player.getMainHandStack().isOf(Items.BOW) && player.getEquippedStack(EquipmentSlot.FEET).isOf(Items.IRON_BOOTS)) { + LOGGER.info("Player {} can't move during using bow becase of iron boots on feet.", player); + return 0.0F; + } + + return null; + }); + + // this is used to test when multiple mods modify the using item speed together. + ClientPlayerEvents.MODIFY_ITEM_USE_MOVEMENT_SPEED.register(player -> { + if (player.getMainHandStack().isOf(Items.BOW) && player.getOffHandStack().isOf(Items.FEATHER)) { + LOGGER.info("Player {} can move with half speed becase of feather in offhand.", player); + return 0.5F; + } + + return null; + }); } }