From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik Date: Fri, 23 Feb 2024 01:48:08 +0000 Subject: [PATCH] Legacy player combat mechanics diff --git a/src/main/java/me/samsuik/sakura/utils/CombatUtil.java b/src/main/java/me/samsuik/sakura/utils/CombatUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..9c58f13396609ddd2bf7a909f2f20bd6bde27a1e --- /dev/null +++ b/src/main/java/me/samsuik/sakura/utils/CombatUtil.java @@ -0,0 +1,90 @@ +package me.samsuik.sakura.utils; + +import it.unimi.dsi.fastutil.objects.Reference2FloatMap; +import it.unimi.dsi.fastutil.objects.Reference2FloatOpenHashMap; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.item.*; +import net.minecraft.world.item.component.ItemAttributeModifiers; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.item.enchantment.ItemEnchantments; +import org.apache.commons.lang3.mutable.MutableFloat; + +public final class CombatUtil { + private static final Reference2FloatMap LEGACY_ITEM_DAMAGE_MAP = new Reference2FloatOpenHashMap<>(); + + static { + LEGACY_ITEM_DAMAGE_MAP.defaultReturnValue(Float.MIN_VALUE); + for (Item item : BuiltInRegistries.ITEM) { + if (item instanceof TieredItem tieredItem) { + float weaponDamage = baseToolDamage(tieredItem) + tieredItem.getTier().getAttackDamageBonus(); + LEGACY_ITEM_DAMAGE_MAP.put(item, weaponDamage); + } + if (item instanceof HoeItem) { + LEGACY_ITEM_DAMAGE_MAP.put(item, 0.0f); + } + } + } + + private static float baseToolDamage(TieredItem item) { + return switch (item) { + case SwordItem swordItem -> 4.0f; + case AxeItem axeItem -> 3.0f; + case PickaxeItem pickaxeItem -> 2.0f; + case ShovelItem shovelItem -> 1.0f; + case null, default -> 0.0f; + }; + } + + public static double getLegacyAttackDifference(ItemStack itemstack) { + ItemAttributeModifiers defaultModifiers = itemstack.getItem().components().get(DataComponents.ATTRIBUTE_MODIFIERS); + if (defaultModifiers != null && !defaultModifiers.modifiers().isEmpty()) { // exists + double baseAttack = 0.0; + for (ItemAttributeModifiers.Entry entry : defaultModifiers.modifiers()) { + if (!entry.slot().test(EquipmentSlot.MAINHAND) || !entry.attribute().is(Attributes.ATTACK_DAMAGE)) + continue; + if (entry.modifier().operation() != AttributeModifier.Operation.ADD_VALUE) + return 0; + baseAttack += entry.modifier().amount(); + } + + float legacyAttack = LEGACY_ITEM_DAMAGE_MAP.getFloat(itemstack.getItem()); + if (baseAttack != 0.0f && legacyAttack != Float.MIN_VALUE) { + return (double) legacyAttack - baseAttack; + } + } + return 0; + } + + public static float calculateLegacySharpnessDamage(LivingEntity entity, ItemStack itemstack, DamageSource damageSource) { + Holder enchantment = getEnchantmentHolder(Enchantments.SHARPNESS); + ItemEnchantments itemEnchantments = itemstack.getEnchantments(); + int enchantmentLevel = itemEnchantments.getLevel(enchantment); + MutableFloat damage = new MutableFloat(); + + if (entity.level() instanceof ServerLevel level) { + enchantment.value().modifyDamage(level, enchantmentLevel, itemstack, entity, damageSource, damage); + } + // legacy - modern + return enchantmentLevel * 1.25F - damage.getValue(); + } + + private static Holder getEnchantmentHolder(ResourceKey enchantmentKey) { + RegistryAccess registryAccess = MinecraftServer.getServer().registryAccess(); + HolderLookup.RegistryLookup enchantments = registryAccess.lookupOrThrow(Registries.ENCHANTMENT); + return enchantments.getOrThrow(enchantmentKey); + } +} diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java index 35b75be4ace452e684439ef2f0a6fa6d83f8f6cc..34721b0555f3977f85d72d9e704bbe49654ee19d 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -299,6 +299,43 @@ public abstract class LivingEntity extends Entity implements Attackable { ++this.noActionTime; // Above all the floats } // Spigot end + // Sakura start - legacy combat mechanics + private static final ResourceLocation LEGACY_COMBAT_MODIFIER_ID = ResourceLocation.fromNamespaceAndPath("sakura", "legacy_combat"); + private static final AttributeModifier LEGACY_ATTACK_SPEED_MODIFIER = new AttributeModifier(LEGACY_COMBAT_MODIFIER_ID, 100.0, AttributeModifier.Operation.ADD_VALUE); + + private void updateAttackSpeedModifier() { + AttributeInstance attackSpeed = this.getAttribute(Attributes.ATTACK_SPEED); + if (attackSpeed != null) { + attackSpeed.removeModifier(LEGACY_ATTACK_SPEED_MODIFIER); + + if (this.level().sakuraConfig().players.combat.legacyCombatMechanics) { + attackSpeed.addTransientModifier(LEGACY_ATTACK_SPEED_MODIFIER); + } + } + } + + protected final float getAttackDamageFromAttributes() { + AttributeInstance attackDamage = this.getAttribute(Attributes.ATTACK_DAMAGE); + AttributeModifier legacyModifier = null; + + if (this.level().sakuraConfig().players.combat.legacyCombatMechanics) { + ItemStack heldItem = this.getLastHandItem(EquipmentSlot.MAINHAND); + double attackDifference = me.samsuik.sakura.utils.CombatUtil.getLegacyAttackDifference(heldItem); + legacyModifier = new AttributeModifier(LEGACY_COMBAT_MODIFIER_ID, attackDifference, AttributeModifier.Operation.ADD_VALUE); + } + + final double damage; + if (attackDamage == null || legacyModifier == null) { + damage = this.getAttributeValue(Attributes.ATTACK_DAMAGE); + } else { + attackDamage.addTransientModifier(legacyModifier); + damage = this.getAttributeValue(Attributes.ATTACK_DAMAGE); + attackDamage.removeModifier(legacyModifier); + } + + return (float) damage; + } + // Sakura end - legacy combat mechanics protected LivingEntity(EntityType type, Level world) { super(type, world); @@ -2210,7 +2247,16 @@ public abstract class LivingEntity extends Entity implements Attackable { protected float getDamageAfterArmorAbsorb(DamageSource source, float amount) { if (!source.is(DamageTypeTags.BYPASSES_ARMOR)) { // this.hurtArmor(damagesource, f); // CraftBukkit - actuallyHurt(DamageSource, float, EntityDamageEvent) for handle damage + // Sakura start - legacy combat mechanics + if (!this.level().sakuraConfig().players.combat.legacyCombatMechanics) { amount = CombatRules.getDamageAfterAbsorb(this, amount, source, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); + } else { + // See: applyArmorModifier(DamageSource, float) + int i = 25 - this.getArmorValue(); + float f1 = amount * (float) i; + amount = f1 / 25.0F; + } + // Sakura end - legacy combat mechanics } return amount; @@ -3352,6 +3398,12 @@ public abstract class LivingEntity extends Entity implements Attackable { }); } + + // Sakura start - legacy combat mechanics + if (this instanceof ServerPlayer && enumitemslot1 == EquipmentSlot.MAINHAND) { + this.updateAttackSpeedModifier(); + } + // Sakura end - legacy combat mechanics } } diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java index 5c3bebf932b6f0376711a2bd679062575bff04a3..5d16462e1fab47f0587449d0f6e94886831d6ff3 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java @@ -1278,14 +1278,20 @@ public abstract class Player extends LivingEntity { if (playerAttackEntityEvent.callEvent() && willAttack) { // Logic moved to willAttack local variable. { // Paper end - PlayerAttackEntityEvent - float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE); + float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : this.getAttackDamageFromAttributes(); // Sakura - legacy combat mechanics ItemStack itemstack = this.getWeaponItem(); DamageSource damagesource = this.damageSources().playerAttack(this); float f1 = this.getEnchantedDamage(target, f, damagesource) - f; float f2 = this.getAttackStrengthScale(0.5F); + // Sakura start - legacy combat mechanics + if (!this.level().sakuraConfig().players.combat.legacyCombatMechanics) { f *= 0.2F + f2 * f2 * 0.8F; f1 *= f2; + } else if (f1 != 0.0) { + f1 += me.samsuik.sakura.utils.CombatUtil.calculateLegacySharpnessDamage(this, itemstack, damagesource); + } + // Sakura end - legacy combat mechanics // this.resetAttackStrengthTicker(); // CraftBukkit - Moved to EntityLiving to reset the cooldown after the damage is dealt if (target.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && target instanceof Projectile) { Projectile iprojectile = (Projectile) target; @@ -1313,7 +1319,7 @@ public abstract class Player extends LivingEntity { } f += itemstack.getItem().getAttackDamageBonus(target, f, damagesource); - boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && !this.isSprinting(); + boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && (this.level().sakuraConfig().players.combat.legacyCombatMechanics || !this.isSprinting()); // Sakura - legacy combat mechanics flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits if (flag2) {