diff --git a/src/main/java/com/bibireden/playerex/mixin/ItemStackMixin.java b/src/main/java/com/bibireden/playerex/mixin/ItemStackMixin.java new file mode 100644 index 00000000..4073d2f6 --- /dev/null +++ b/src/main/java/com/bibireden/playerex/mixin/ItemStackMixin.java @@ -0,0 +1,77 @@ +package com.bibireden.playerex.mixin; + +import com.bibireden.playerex.util.PlayerEXUtil; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.function.Consumer; + +@Mixin(ItemStack.class) +abstract class ItemStackMixin { + @Shadow + public abstract boolean damage(int amount, Random random, @Nullable ServerPlayerEntity serverPlayer); + + @Inject(method = "use(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;)Lnet/minecraft/util/TypedActionResult;", at = @At(value = "HEAD"), cancellable = true) + public void preventUse(World world, PlayerEntity user, Hand hand, CallbackInfoReturnable> cir) { + ItemStack stack = (ItemStack)(Object)this; + if (PlayerEXUtil.isBroken(stack)) { + cir.setReturnValue(TypedActionResult.fail(stack)); + } + } + + @Inject(method = "useOnBlock(Lnet/minecraft/item/ItemUsageContext;)Lnet/minecraft/util/ActionResult;", at = @At(value = "HEAD"), cancellable = true) + public void preventUseOnBlock(ItemUsageContext context, CallbackInfoReturnable cir) { + ItemStack stack = (ItemStack)(Object)this; + if (PlayerEXUtil.isBroken(stack)) { + cir.setReturnValue(ActionResult.FAIL); + } + } + + @Inject(method = "damage(ILnet/minecraft/util/math/random/Random;Lnet/minecraft/server/network/ServerPlayerEntity;)Z", at = @At(value = "HEAD"), cancellable = true) + public void preventDamage(int amount, Random random, ServerPlayerEntity player, CallbackInfoReturnable cir) { + ItemStack stack = (ItemStack)(Object)this; + if (PlayerEXUtil.isBroken(stack)) { + cir.setReturnValue(true); + } + } + + @Inject(method = "damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItem()Lnet/minecraft/item/Item;"), cancellable = true) + public void preventBreak(int amount, T entity, Consumer onBroken, CallbackInfo ci) { + ItemStack stack = (ItemStack)(Object)this; + // TODO: If "unbreakable" item, maybe use a tag? + if (true) { + if (!PlayerEXUtil.isBroken(stack)) { + NbtCompound tag = stack.getNbt(); + tag.putBoolean("broken", true); + stack.setNbt(tag); + } + ci.cancel(); + } + } + + @Inject(method = "setDamage(I)V", at = @At(value = "HEAD")) + public void removeBrokenOnRepair(int damage, CallbackInfo ci) { + ItemStack stack = (ItemStack)(Object)this; + if (PlayerEXUtil.isBroken(stack) && damage < stack.getDamage()) { + NbtCompound tag = stack.getNbt(); + tag.putBoolean("broken", false); + stack.setNbt(tag); + } + } +} diff --git a/src/main/java/com/bibireden/playerex/mixin/LivingEntityMixin.java b/src/main/java/com/bibireden/playerex/mixin/LivingEntityMixin.java index 3e50b0e5..82175156 100644 --- a/src/main/java/com/bibireden/playerex/mixin/LivingEntityMixin.java +++ b/src/main/java/com/bibireden/playerex/mixin/LivingEntityMixin.java @@ -1,15 +1,18 @@ package com.bibireden.playerex.mixin; import com.bibireden.playerex.api.event.LivingEntityEvents; +import com.bibireden.playerex.util.PlayerEXUtil; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.util.Hand; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(LivingEntity.class) public abstract class LivingEntityMixin { @@ -19,6 +22,14 @@ public abstract class LivingEntityMixin { @Unique private int playerex_ticks; + @Inject(method = "setCurrentHand(Lnet/minecraft/util/Hand;)V", at = @At(value = "HEAD"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true) + public void preventAttack(Hand hand, CallbackInfo ci) { + LivingEntity entity = (LivingEntity)(Object)this; + if (PlayerEXUtil.isBroken(entity.getStackInHand(hand))) { + ci.cancel(); + } + } + @ModifyVariable(method = "heal", at = @At("HEAD"), argsOnly = true) private float playerex$heal(float original) { return LivingEntityEvents.ON_HEAL.invoker().onHeal((LivingEntity) (Object) this, original); diff --git a/src/main/java/com/bibireden/playerex/mixin/PlayerEntityMixin.java b/src/main/java/com/bibireden/playerex/mixin/PlayerEntityMixin.java index b4dec88f..bd40423b 100644 --- a/src/main/java/com/bibireden/playerex/mixin/PlayerEntityMixin.java +++ b/src/main/java/com/bibireden/playerex/mixin/PlayerEntityMixin.java @@ -1,14 +1,48 @@ package com.bibireden.playerex.mixin; import com.bibireden.playerex.api.event.PlayerEntityEvents; +import com.bibireden.playerex.util.PlayerEXUtil; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.GameMode; +import net.minecraft.world.World; 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.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(PlayerEntity.class) public abstract class PlayerEntityMixin { + @Inject(method = "attack(Lnet/minecraft/entity/Entity;)V", at = @At(value = "HEAD"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true) + public void preventAttack(Entity target, CallbackInfo ci) { + PlayerEntity entity = (PlayerEntity)(Object)this; + // TODO: BetterCombat compat + if (PlayerEXUtil.isBroken(entity.getMainHandStack())) { + ci.cancel(); + } + } + + @Inject(method = "interact(Lnet/minecraft/entity/Entity;Lnet/minecraft/util/Hand;)Lnet/minecraft/util/ActionResult;", at = @At(value = "HEAD"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true) + public void preventInteract(Entity entity, Hand hand, CallbackInfoReturnable cir) { + PlayerEntity player = (PlayerEntity)(Object)this; + if (PlayerEXUtil.isBroken(player.getStackInHand(hand))) { + cir.setReturnValue(ActionResult.FAIL); + } + } + + @Inject(method = "isBlockBreakingRestricted(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/GameMode;)Z", at = @At(value = "HEAD"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true) + public void preventBreakBlock(World world, BlockPos pos, GameMode gameMode, CallbackInfoReturnable cir) { + PlayerEntity player = (PlayerEntity)(Object)this; + if (PlayerEXUtil.isBroken(player.getMainHandStack())) { + cir.setReturnValue(true); + } + } @ModifyVariable(method = "attack", at = @At("STORE"), name = "bl3", ordinal = 2) private boolean playerex_attack(boolean bl3, Entity target) { diff --git a/src/main/java/com/bibireden/playerex/mixin/PlayerInventoryMixin.java b/src/main/java/com/bibireden/playerex/mixin/PlayerInventoryMixin.java index c8bbcd50..6d671fe4 100644 --- a/src/main/java/com/bibireden/playerex/mixin/PlayerInventoryMixin.java +++ b/src/main/java/com/bibireden/playerex/mixin/PlayerInventoryMixin.java @@ -18,7 +18,7 @@ public abstract class PlayerInventoryMixin { @ModifyReturnValue(method = "getBlockBreakingSpeed", at = @At("RETURN")) private float playerex$getBlockBreakingSpeed(float original) { - Optional maybeBreakingSpeed = DataAttributesAPI.INSTANCE.getValue(PlayerEXAttributes.BREAKING_SPEED, this.player); + Optional maybeBreakingSpeed = DataAttributesAPI.getValue(PlayerEXAttributes.BREAKING_SPEED, this.player); return maybeBreakingSpeed.map(v -> original + v.floatValue() - 1.0F).orElse(original); } } diff --git a/src/main/kotlin/com/bibireden/playerex/util/PlayerEXUtil.kt b/src/main/kotlin/com/bibireden/playerex/util/PlayerEXUtil.kt index 7767dcad..62e78065 100644 --- a/src/main/kotlin/com/bibireden/playerex/util/PlayerEXUtil.kt +++ b/src/main/kotlin/com/bibireden/playerex/util/PlayerEXUtil.kt @@ -4,6 +4,7 @@ import com.bibireden.data_attributes.api.util.Maths import com.bibireden.playerex.PlayerEX import com.bibireden.playerex.ext.level import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.ItemStack import net.objecthunter.exp4j.Expression import net.objecthunter.exp4j.ExpressionBuilder import net.objecthunter.exp4j.function.Function @@ -46,4 +47,12 @@ object PlayerEXUtil { /** todo: document, none evident on former, resolve if orElse is needed here, and if we can do nullable or not without drastically changing things */ @JvmStatic fun getRequiredXpForNextLevel(player: PlayerEntity): Int = getRequiredXpForLevel(player, player.level + 1) + + @JvmStatic + fun isBroken(stack: ItemStack): Boolean { + if (stack.nbt != null) { + return stack.nbt!!.getBoolean("broken") + } + return false; + } } \ No newline at end of file diff --git a/src/main/resources/assets/playerex/lang/en_us.json b/src/main/resources/assets/playerex/lang/en_us.json index 7f511e20..73361e58 100644 --- a/src/main/resources/assets/playerex/lang/en_us.json +++ b/src/main/resources/assets/playerex/lang/en_us.json @@ -74,6 +74,8 @@ "attribute.name.playerex.smithing": "Smithing", "attribute.name.playerex.farming": "Farming", + "playerex.broken": "Broken", + "playerex.command.reset_chunk": "Reset experience negation factor for chunk at %s", "playerex.command.refund": "Refunded %s skill points for player %s", diff --git a/src/main/resources/playerex.mixins.json b/src/main/resources/playerex.mixins.json index 03cde5cf..fc03c87f 100644 --- a/src/main/resources/playerex.mixins.json +++ b/src/main/resources/playerex.mixins.json @@ -9,7 +9,8 @@ "PlayerEntityMixin", "PlayerInventoryMixin", "ServerPlayerEntityMixin", - "ServerWorldMixin" + "ServerWorldMixin", + "ItemStackMixin" ], "injectors": { "defaultRequire": 1