From 6a8c0d82eea963b9cd6bbaee524ee13c9f8ab5e9 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 17 May 2024 18:23:38 +0100 Subject: [PATCH] Ground pound now prevents fall damage. Fixes #351 --- .../ability/EarthPonyStompAbility.java | 7 +++- .../unicopia/entity/Living.java | 33 ++++++++++++----- .../unicopia/entity/player/Pony.java | 37 ++++++++----------- .../unicopia/mixin/MixinLivingEntity.java | 6 +++ .../unicopia/mixin/MixinPlayerEntity.java | 16 +------- 5 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 78314ba7..4f9ffd1c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -20,6 +20,7 @@ import com.minelittlepony.unicopia.server.world.BlockDestructionManager; import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.VecHelper; +import it.unimi.dsi.fastutil.floats.Float2FloatFunction; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.enchantment.EnchantmentHelper; @@ -106,7 +107,8 @@ public class EarthPonyStompAbility implements Ability { public boolean apply(Pony iplayer, Hit data) { PlayerEntity player = iplayer.asEntity(); - Runnable r = () -> { + Float2FloatFunction r = fallDistance -> { + player.fallDistance = 0; BlockPos center = PosHelper.findSolidGroundAt(player.getEntityWorld(), player.getBlockPos(), iplayer.getPhysics().getGravitySignum()); float heavyness = 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player); @@ -163,13 +165,14 @@ public class EarthPonyStompAbility implements Ability { iplayer.subtractEnergyCost(rad); iplayer.asEntity().addExhaustion(3); + return 0F; }; if (iplayer.asEntity().isOnGround()) { iplayer.setAnimation(Animation.STOMP, Animation.Recipient.ANYONE, 10); iplayer.asEntity().jump(); iplayer.updateVelocity(); - AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.run(), 5); + AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.get(0F), 5); } else { thrustDownwards(iplayer); iplayer.waitForFall(r); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index 1696abef..d006a811 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -1,6 +1,7 @@ package com.minelittlepony.unicopia.entity; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -38,6 +39,7 @@ import com.minelittlepony.unicopia.projectile.ProjectileImpactListener; import com.minelittlepony.unicopia.server.world.DragonBreathStore; import com.minelittlepony.unicopia.util.*; +import it.unimi.dsi.fastutil.floats.Float2FloatFunction; import it.unimi.dsi.fastutil.floats.Float2ObjectFunction; import net.fabricmc.fabric.api.util.TriState; import net.minecraft.block.BlockState; @@ -83,7 +85,8 @@ public abstract class Living implements Equine, Caste private final Interactable jumpingHeuristic; @Nullable - private Runnable landEvent; + private final AtomicReference landEvent = new AtomicReference<>(); + private float prevFallDistance; private boolean invisible = false; @@ -131,11 +134,11 @@ public abstract class Living implements Equine, Caste this.invisible = invisible; } - public void waitForFall(Runnable action) { + public void waitForFall(Float2FloatFunction action) { if (entity.isOnGround()) { - action.run(); + action.get(0F); } else { - landEvent = action; + landEvent.set(action); } } @@ -206,6 +209,7 @@ public abstract class Living implements Equine, Caste @Override public boolean beforeUpdate() { + prevFallDistance = entity.fallDistance; if (EffectUtils.getAmplifier(entity, UEffects.PARALYSIS) > 1 && entity.getVelocity().horizontalLengthSquared() > 0) { entity.setVelocity(entity.getVelocity().multiply(0, 1, 0)); updateVelocity(); @@ -236,9 +240,8 @@ public abstract class Living implements Equine, Caste invinsibilityTicks--; } - if (landEvent != null && entity.isOnGround() && landedChanged()) { - landEvent.run(); - landEvent = null; + if (entity.isOnGround() && landedChanged()) { + onLanded(prevFallDistance); } if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) { @@ -261,6 +264,11 @@ public abstract class Living implements Equine, Caste transportation.tick(); } + private float onLanded(float fallDistance) { + var event = landEvent.getAndSet(null); + return event == null ? fallDistance : event.get(fallDistance); + } + public void updateAttributeModifier(UUID id, EntityAttribute attribute, float desiredValue, Float2ObjectFunction modifierSupplier, boolean permanent) { @Nullable EntityAttributeInstance instance = asEntity().getAttributeInstance(attribute); @@ -465,10 +473,17 @@ public abstract class Living implements Equine, Caste .isPresent(); } - protected void handleFall(float distance, float damageMultiplier, DamageSource cause) { + public float onImpact(float distance, float damageMultiplier, DamageSource cause) { + float fallDistance = onLanded(getEffectiveFallDistance(distance)); + getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).ifPresent(spell -> { - spell.getDisguise().onImpact(this, distance, damageMultiplier, cause); + spell.getDisguise().onImpact(this, fallDistance, damageMultiplier, cause); }); + return fallDistance; + } + + protected float getEffectiveFallDistance(float distance) { + return distance; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index aa174dd4..07a13cec 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -723,9 +723,22 @@ public class Pony extends Living implements Copyable, Update }); } - public Optional onImpact(float distance, float damageMultiplier, DamageSource cause) { - float originalDistance = distance; + @Override + public float onImpact(float distance, float damageMultiplier, DamageSource cause) { + distance = super.onImpact(distance, damageMultiplier, cause); + if (getCompositeRace().canUseEarth() && entity.isSneaking()) { + double radius = distance / 10; + if (radius > 0) { + EarthPonyStompAbility.spawnEffectAround(entity, entity.getSteppingPos(), radius, radius); + } + } + + return distance; + } + + @Override + protected float getEffectiveFallDistance(float distance) { boolean extraProtection = getSpellSlot().get(SpellType.SHIELD, false).isPresent(); if (!entity.isCreative() && !entity.isSpectator()) { @@ -740,14 +753,9 @@ public class Pony extends Living implements Copyable, Update if (getCompositeRace().canFly() || (getCompositeRace().canUseEarth() && entity.isSneaking())) { distance -= 5; } - distance = Math.max(0, distance); } - handleFall(distance, damageMultiplier, cause); - if (distance != originalDistance) { - return Optional.of(distance); - } - return Optional.empty(); + return Math.max(0, distance); } public void onEat(ItemStack stack) { @@ -762,19 +770,6 @@ public class Pony extends Living implements Copyable, Update } } - @SuppressWarnings("deprecation") - @Override - protected void handleFall(float distance, float damageMultiplier, DamageSource cause) { - super.handleFall(distance, damageMultiplier, cause); - - if (getCompositeRace().canUseEarth() && entity.isSneaking()) { - double radius = distance / 10; - if (radius > 0) { - EarthPonyStompAbility.spawnEffectAround(entity, entity.getLandingPos(), radius, radius); - } - } - } - public void onKill(Entity killedEntity, DamageSource damage) { if (killedEntity != null && killedEntity.getType() == EntityType.PHANTOM && getPhysics().isFlying()) { UCriteria.KILL_PHANTOM_WHILE_FLYING.trigger(entity); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java index 7d41f099..da1cff6a 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java @@ -6,6 +6,7 @@ import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.gen.Accessor; 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; @@ -151,6 +152,11 @@ abstract class MixinLivingEntity extends Entity implements LivingEntityDuck, Equ get().onDamage(source, amount).ifPresent(info::setReturnValue); } + @ModifyVariable(method = "handleFallDamage(FFLnet/minecraft/entity/damage/DamageSource;)Z", at = @At("HEAD"), ordinal = 0, argsOnly = true) + private float onHandleFallDamage(float distance, float distanceAgain, float damageMultiplier, DamageSource cause) { + return get().onImpact(distance, damageMultiplier, cause); + } + @Inject(method = "hurtByWater()Z", at = @At("HEAD"), cancellable = true) private void onCanBeHurtByWater(CallbackInfoReturnable info) { TriState hurtByWater = get().canBeHurtByWater(); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java index b67432fc..d04692bb 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPlayerEntity.java @@ -26,7 +26,6 @@ import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.stat.Stats; import net.minecraft.util.Unit; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -48,24 +47,11 @@ abstract class MixinPlayerEntity extends LivingEntity implements Equine.Containe Pony.registerAttributes(info.getReturnValue()); } - @ModifyVariable(method = "applyDamage(Lnet/minecraft/entity/damage/DamageSource;F)V", at = @At("HEAD"), ordinal = 0) + @ModifyVariable(method = "applyDamage(Lnet/minecraft/entity/damage/DamageSource;F)V", at = @At("HEAD"), ordinal = 0, argsOnly = true) protected float modifyDamageAmount(float amount, DamageSource source) { return get().modifyDamage(source, amount).orElse(amount); } - @Inject(method = "handleFallDamage(FFLnet/minecraft/entity/damage/DamageSource;)Z", at = @At("HEAD"), cancellable = true) - private void onHandleFallDamage(float distance, float damageMultiplier, DamageSource cause, CallbackInfoReturnable info) { - get().onImpact(fallDistance, damageMultiplier, cause).ifPresent(newDistance -> { - PlayerEntity self = (PlayerEntity)(Object)this; - - if (distance >= 2) { - self.increaseStat(Stats.FALL_ONE_CM, Math.round(distance * 100)); - } - - info.setReturnValue(super.handleFallDamage(newDistance, damageMultiplier, cause)); - }); - } - @Inject(method = "eatFood(Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;)Lnet/minecraft/item/ItemStack;", at = @At("HEAD")) private void onEatFood(World world, ItemStack stack, CallbackInfoReturnable info) { get().onEat(stack);