diff --git a/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributeUtils.java b/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributeUtils.java new file mode 100644 index 00000000..b8cb52a6 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributeUtils.java @@ -0,0 +1,28 @@ +package com.github.manasmods.manascore.attribute; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec3; + +import java.util.Optional; + +public class ManasCoreAttributeUtils { + public static double getAttackRange(Player player) { + double range = player.getAttributeValue(ManasCoreAttributes.ENTITY_REACH.get()); + return range == 0 ? 0 : range + (player.isCreative() ? 3 : 0); + } + + public static double getReachDistance(Player player) { + double reach = player.getAttributeValue(ManasCoreAttributes.BLOCK_REACH.get()); + return reach == 0 ? 0 : reach + (player.isCreative() ? 0.5 : 0); + } + + public static boolean cantHit(Player player, Entity entity, double padding) { + Vec3 eye = player.getEyePosition(); + Vec3 targetCenter = entity.getPosition(1.0F).add(0, entity.getBbHeight() / 2, 0); + Optional hit = entity.getBoundingBox().clip(eye, targetCenter); + + double dist = getAttackRange(player) + padding; + return !(hit.map(eye::distanceToSqr).orElseGet(() -> player.distanceToSqr(entity)) < dist * dist); + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributes.java b/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributes.java index 53bde6a5..91b9a710 100644 --- a/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributes.java +++ b/common/src/main/java/com/github/manasmods/manascore/attribute/ManasCoreAttributes.java @@ -6,6 +6,13 @@ import net.minecraft.world.entity.ai.attributes.RangedAttribute; public class ManasCoreAttributes { + public static final RegistrySupplier BLOCK_REACH = ManasCore.REGISTER.attribute("block_reach") + .withDefaultValue(4.5) + .withMinimumValue(0) + .withMaximumValue(1024) + .applyTo(() -> EntityType.PLAYER) + .syncable() + .end(); public static final RegistrySupplier CRIT_CHANCE = ManasCore.REGISTER.attribute("crit_chance") .withDefaultValue(0) .withMinimumValue(0) @@ -20,13 +27,20 @@ public class ManasCoreAttributes { .applyToAll() .syncable() .end(); - public static final RegistrySupplier JUMP_POWER = ManasCore.REGISTER.attribute("jump_power") - .withDefaultValue(0.42) + public static final RegistrySupplier ENTITY_REACH = ManasCore.REGISTER.attribute("entity_reach") + .withDefaultValue(3) .withMinimumValue(0) .withMaximumValue(1024) .applyTo(() -> EntityType.PLAYER) .syncable() .end(); + public static final RegistrySupplier JUMP_STRENGTH = ManasCore.REGISTER.attribute("jump_strength") + .withDefaultValue(0.42) + .withMinimumValue(0) + .withMaximumValue(1024) + .applyToAll() + .syncable() + .end(); public static final RegistrySupplier MINING_SPEED_MULTIPLIER = ManasCore.REGISTER.attribute("mining_speed_multiplier") .withDefaultValue(1.0) .withMinimumValue(0) diff --git a/common/src/main/java/com/github/manasmods/manascore/core/MixinItem.java b/common/src/main/java/com/github/manasmods/manascore/core/MixinItem.java new file mode 100644 index 00000000..4d7d5421 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/MixinItem.java @@ -0,0 +1,18 @@ +package com.github.manasmods.manascore.core; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(Item.class) +public class MixinItem { + @ModifyConstant(method = "getPlayerPOVHitResult(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/ClipContext$Fluid;)Lnet/minecraft/world/phys/BlockHitResult;", + require = 4, allow = 4, constant = @Constant(doubleValue = 5.0)) + private static double getReachDistance(double reachDistance, Level level, Player player) { + return ManasCoreAttributeUtils.getReachDistance(player); + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/core/MixinLivingEntity.java b/common/src/main/java/com/github/manasmods/manascore/core/MixinLivingEntity.java index 177da4de..5e6133ac 100644 --- a/common/src/main/java/com/github/manasmods/manascore/core/MixinLivingEntity.java +++ b/common/src/main/java/com/github/manasmods/manascore/core/MixinLivingEntity.java @@ -2,7 +2,6 @@ import com.github.manasmods.manascore.attribute.ManasCoreAttributes; import net.minecraft.tags.TagKey; -import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.level.material.Fluid; @@ -21,21 +20,21 @@ public abstract class MixinLivingEntity { @Inject(method = "getJumpPower", at = @At("RETURN"), cancellable = true) protected void getJumpPower(CallbackInfoReturnable cir) { LivingEntity entity = (LivingEntity) (Object) this; - AttributeInstance instance = entity.getAttribute(ManasCoreAttributes.JUMP_POWER.get()); + AttributeInstance instance = entity.getAttribute(ManasCoreAttributes.JUMP_STRENGTH.get()); if (instance == null) return; - double newJump = (cir.getReturnValue() - getJumpBoostPower()) / 0.42 * instance.getValue(); + double newJump = (cir.getReturnValue() - getJumpBoostPower()) / 0.42F * instance.getValue(); cir.setReturnValue((float) (newJump + getJumpBoostPower())); } @ModifyArg(method = "causeFallDamage", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/Entity;causeFallDamage(FFLnet/minecraft/world/damagesource/DamageSource;)Z"), index = 0) - public float causeFallDamage(float fallDistance, float multiplier, DamageSource source) { + target = "Lnet/minecraft/world/entity/LivingEntity;calculateFallDamage(FF)I"), index = 0) + public float causeFallDamage(float fallDistance, float multiplier) { LivingEntity entity = (LivingEntity) (Object) this; - AttributeInstance instance = entity.getAttribute(ManasCoreAttributes.JUMP_POWER.get()); + AttributeInstance instance = entity.getAttribute(ManasCoreAttributes.JUMP_STRENGTH.get()); if (instance == null) return fallDistance; - float additionalJumpBlock = (float) ((instance.getValue() - 0.42) / 0.2); + float additionalJumpBlock = (float) ((instance.getValue() - 0.42F) / 0.1F); return fallDistance - additionalJumpBlock; } diff --git a/common/src/main/java/com/github/manasmods/manascore/core/MixinPlayer.java b/common/src/main/java/com/github/manasmods/manascore/core/MixinPlayer.java index 81768742..b654c0f5 100644 --- a/common/src/main/java/com/github/manasmods/manascore/core/MixinPlayer.java +++ b/common/src/main/java/com/github/manasmods/manascore/core/MixinPlayer.java @@ -1,17 +1,49 @@ package com.github.manasmods.manascore.core; +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; import com.github.manasmods.manascore.attribute.ManasCoreAttributes; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.state.BlockState; +import org.objectweb.asm.Opcodes; 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.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(Player.class) public abstract class MixinPlayer { + @Redirect(method = "attack", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/player/Player;getAttributeValue(Lnet/minecraft/world/entity/ai/attributes/Attribute;)D", opcode = Opcodes.GETSTATIC)) + private double getAttackDamageWithCritChance(Player player, Attribute attribute) { + AttributeInstance instance = player.getAttribute(ManasCoreAttributes.CRIT_CHANCE.get()); + if (instance == null || player.getRandom().nextInt(100) > instance.getValue()) + return player.getAttributeValue(attribute); + + boolean vanillaCrit = player.getAttackStrengthScale(0.5F) > 0.9F && player.fallDistance > 0.0F + && !player.onGround() && !player.onClimbable() && !player.isInWater() && !player.isSprinting() + && !player.hasEffect(MobEffects.BLINDNESS) && !player.isPassenger(); + if (vanillaCrit) return player.getAttributeValue(attribute); + + return player.getAttributeValue(attribute) * getCritMultiplier(1.5F); + } + + @ModifyConstant(method = "attack(Lnet/minecraft/world/entity/Entity;)V", constant = @Constant(floatValue = 1.5F)) + private float getCritMultiplier(float multiplier) { + Player player = (Player) (Object) this; + AttributeInstance instance = player.getAttribute(ManasCoreAttributes.CRIT_MULTIPLIER.get()); + if (instance == null) return multiplier; + return (float) instance.getValue(); + } + + @ModifyConstant(method = "attack(Lnet/minecraft/world/entity/Entity;)V", constant = @Constant(doubleValue = 9.0)) + private double getAttackRangeSquared(double attackRange) { + double range = ManasCoreAttributeUtils.getAttackRange((Player) (Object) this); + return range * range; + } + @Inject(method = "getDestroySpeed", at = @At("RETURN"), cancellable = true) protected void getDestroySpeed(BlockState state, CallbackInfoReturnable cir) { LivingEntity entity = (LivingEntity) (Object) this; diff --git a/common/src/main/java/com/github/manasmods/manascore/core/MixinServerGamePacketListenerImpl.java b/common/src/main/java/com/github/manasmods/manascore/core/MixinServerGamePacketListenerImpl.java new file mode 100644 index 00000000..fc95dec6 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/MixinServerGamePacketListenerImpl.java @@ -0,0 +1,28 @@ +package com.github.manasmods.manascore.core; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import org.objectweb.asm.Opcodes; +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.Redirect; + +@Mixin(ServerGamePacketListenerImpl.class) +public class MixinServerGamePacketListenerImpl { + @Shadow public ServerPlayer player; + @Redirect(method = "handleUseItemOn", at = @At(value = "FIELD", + target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;MAX_INTERACTION_DISTANCE:D", opcode = Opcodes.GETSTATIC)) + private double getBlockInteractDistance() { + double reach = ManasCoreAttributeUtils.getReachDistance(this.player) + 3; + return reach * reach; + } + + @Redirect(method = "handleInteract", at = @At(value = "FIELD", + target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;MAX_INTERACTION_DISTANCE:D", opcode = Opcodes.GETSTATIC)) + private double getEntityInteractDistance() { + double reach = ManasCoreAttributeUtils.getAttackRange(this.player) + 3; + return reach * reach; + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/core/MixinServerPlayerGameMode.java b/common/src/main/java/com/github/manasmods/manascore/core/MixinServerPlayerGameMode.java new file mode 100644 index 00000000..bcad2843 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/MixinServerPlayerGameMode.java @@ -0,0 +1,22 @@ +package com.github.manasmods.manascore.core; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; +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.Redirect; + +@Mixin(ServerPlayerGameMode.class) +public class MixinServerPlayerGameMode { + @Shadow @Final protected ServerPlayer player; + @Redirect(method = "handleBlockBreakAction", at = @At(value = "FIELD", + target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;MAX_INTERACTION_DISTANCE:D", opcode = Opcodes.GETSTATIC)) + private double getReachDistance() { + double reach = ManasCoreAttributeUtils.getReachDistance(player) + 1; + return reach * reach; + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/core/client/MixinGameRenderer.java b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinGameRenderer.java new file mode 100644 index 00000000..fea6bbf0 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinGameRenderer.java @@ -0,0 +1,30 @@ +package com.github.manasmods.manascore.core.client; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(GameRenderer.class) +public class MixinGameRenderer { + @Shadow @Final Minecraft minecraft; + @ModifyConstant(method = "pick", require = 1, allow = 1, constant = @Constant(doubleValue = 6.0)) + private double getReachDistance(double reachDistance) { + Player player = this.minecraft.player; + if (player == null) return reachDistance; + return ManasCoreAttributeUtils.getReachDistance(player); + } + + @ModifyConstant(method = "pick", constant = @Constant(doubleValue = 9.0)) + private double getAttackRange(double attackRange) { + Player player = this.minecraft.player; + if (player == null) return attackRange; + double range = ManasCoreAttributeUtils.getAttackRange(player); + return range * range; + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/core/client/MixinGui.java b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinGui.java new file mode 100644 index 00000000..49f2f5a3 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinGui.java @@ -0,0 +1,26 @@ +package com.github.manasmods.manascore.core.client; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(Gui.class) +public class MixinGui { + @Shadow @Final + private Minecraft minecraft; + @ModifyConstant(method = "renderCrosshair", require = 2, allow = 1, constant = @Constant(floatValue = 1.0F)) + private float getActualReachDistance(float constant) { + Player player = minecraft.player; + if (player == null) return constant; + if (minecraft.crosshairPickEntity == null) return constant; + + if (ManasCoreAttributeUtils.cantHit(player, minecraft.crosshairPickEntity, 0)) return 2.0F; + return constant; + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/core/client/MixinMinecraft.java b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinMinecraft.java new file mode 100644 index 00000000..812c5c61 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinMinecraft.java @@ -0,0 +1,51 @@ +package com.github.manasmods.manascore.core.client; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +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.CallbackInfoReturnable; + +@Mixin(Minecraft.class) +public class MixinMinecraft { + @Shadow protected int missTime; + @Shadow @Nullable public HitResult hitResult; + + @Shadow @Nullable public LocalPlayer player; + @Shadow @Nullable public MultiPlayerGameMode gameMode; + + @Shadow @Nullable public ClientLevel level; + @Inject(method = "startAttack", at = @At("HEAD"), cancellable = true) + private void startAttack(CallbackInfoReturnable cir) { + if (this.missTime > 0) return; + if (this.hitResult == null) return; + + LocalPlayer player = this.player; + if (player == null) return; + if (player.isHandsBusy()) return; + + ItemStack itemStack = this.player.getItemInHand(InteractionHand.MAIN_HAND); + if (level != null && !itemStack.isItemEnabled(level.enabledFeatures())) { + cir.setReturnValue(false); + return; + } + + if(!(this.hitResult instanceof EntityHitResult entityHit)) return; + if(ManasCoreAttributeUtils.cantHit(player, entityHit.getEntity(), 0)) { + if (this.gameMode != null && this.gameMode.hasMissTime()) this.missTime = 10; + this.player.resetAttackStrengthTicker(); + this.player.swing(InteractionHand.MAIN_HAND); + cir.setReturnValue(false); + } + } +} diff --git a/common/src/main/java/com/github/manasmods/manascore/core/client/MixinMultiPlayerGameMode.java b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinMultiPlayerGameMode.java new file mode 100644 index 00000000..aba0a0b5 --- /dev/null +++ b/common/src/main/java/com/github/manasmods/manascore/core/client/MixinMultiPlayerGameMode.java @@ -0,0 +1,23 @@ +package com.github.manasmods.manascore.core.client; + +import com.github.manasmods.manascore.attribute.ManasCoreAttributeUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Final; +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.CallbackInfoReturnable; + +@Mixin(MultiPlayerGameMode.class) +public class MixinMultiPlayerGameMode { + @Shadow @Final private Minecraft minecraft; + @Inject(method = "getPickRange", at = @At("HEAD"), cancellable = true) + protected void getPickRange(CallbackInfoReturnable cir) { + Player player = this.minecraft.player; + if (player == null) return; + cir.setReturnValue((float) ManasCoreAttributeUtils.getReachDistance(player)); + } +} diff --git a/common/src/main/resources/manascore-common.mixins.json b/common/src/main/resources/manascore-common.mixins.json index 7c0788af..bd485caf 100644 --- a/common/src/main/resources/manascore-common.mixins.json +++ b/common/src/main/resources/manascore-common.mixins.json @@ -7,13 +7,20 @@ "MixinChunkSerializer", "MixinDataFixTypes", "MixinEntity", + "MixinItem", "MixinLevel", "MixinLevelChunk", "MixinLivingEntity", "MixinPlayer", - "MixinServerLevel" + "MixinServerGamePacketListenerImpl", + "MixinServerLevel", + "MixinServerPlayerGameMode" ], "client": [ + "client.MixinGameRenderer", + "client.MixinGui", + "client.MixinMinecraft", + "client.MixinMultiPlayerGameMode" ], "injectors": { "defaultRequire": 1