Ground pound now prevents fall damage. Fixes #351

This commit is contained in:
Sollace 2024-05-17 18:23:38 +01:00
parent fffbee84e3
commit 6a8c0d82ee
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
5 changed files with 52 additions and 47 deletions

View file

@ -20,6 +20,7 @@ import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.PosHelper;
import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.VecHelper;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
@ -106,7 +107,8 @@ public class EarthPonyStompAbility implements Ability<Hit> {
public boolean apply(Pony iplayer, Hit data) { public boolean apply(Pony iplayer, Hit data) {
PlayerEntity player = iplayer.asEntity(); PlayerEntity player = iplayer.asEntity();
Runnable r = () -> { Float2FloatFunction r = fallDistance -> {
player.fallDistance = 0;
BlockPos center = PosHelper.findSolidGroundAt(player.getEntityWorld(), player.getBlockPos(), iplayer.getPhysics().getGravitySignum()); BlockPos center = PosHelper.findSolidGroundAt(player.getEntityWorld(), player.getBlockPos(), iplayer.getPhysics().getGravitySignum());
float heavyness = 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player); float heavyness = 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player);
@ -163,13 +165,14 @@ public class EarthPonyStompAbility implements Ability<Hit> {
iplayer.subtractEnergyCost(rad); iplayer.subtractEnergyCost(rad);
iplayer.asEntity().addExhaustion(3); iplayer.asEntity().addExhaustion(3);
return 0F;
}; };
if (iplayer.asEntity().isOnGround()) { if (iplayer.asEntity().isOnGround()) {
iplayer.setAnimation(Animation.STOMP, Animation.Recipient.ANYONE, 10); iplayer.setAnimation(Animation.STOMP, Animation.Recipient.ANYONE, 10);
iplayer.asEntity().jump(); iplayer.asEntity().jump();
iplayer.updateVelocity(); iplayer.updateVelocity();
AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.run(), 5); AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.get(0F), 5);
} else { } else {
thrustDownwards(iplayer); thrustDownwards(iplayer);
iplayer.waitForFall(r); iplayer.waitForFall(r);

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.entity; package com.minelittlepony.unicopia.entity;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; 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.server.world.DragonBreathStore;
import com.minelittlepony.unicopia.util.*; import com.minelittlepony.unicopia.util.*;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import it.unimi.dsi.fastutil.floats.Float2ObjectFunction; import it.unimi.dsi.fastutil.floats.Float2ObjectFunction;
import net.fabricmc.fabric.api.util.TriState; import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -83,7 +85,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
private final Interactable jumpingHeuristic; private final Interactable jumpingHeuristic;
@Nullable @Nullable
private Runnable landEvent; private final AtomicReference<Float2FloatFunction> landEvent = new AtomicReference<>();
private float prevFallDistance;
private boolean invisible = false; private boolean invisible = false;
@ -131,11 +134,11 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
this.invisible = invisible; this.invisible = invisible;
} }
public void waitForFall(Runnable action) { public void waitForFall(Float2FloatFunction action) {
if (entity.isOnGround()) { if (entity.isOnGround()) {
action.run(); action.get(0F);
} else { } else {
landEvent = action; landEvent.set(action);
} }
} }
@ -206,6 +209,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override @Override
public boolean beforeUpdate() { public boolean beforeUpdate() {
prevFallDistance = entity.fallDistance;
if (EffectUtils.getAmplifier(entity, UEffects.PARALYSIS) > 1 && entity.getVelocity().horizontalLengthSquared() > 0) { if (EffectUtils.getAmplifier(entity, UEffects.PARALYSIS) > 1 && entity.getVelocity().horizontalLengthSquared() > 0) {
entity.setVelocity(entity.getVelocity().multiply(0, 1, 0)); entity.setVelocity(entity.getVelocity().multiply(0, 1, 0));
updateVelocity(); updateVelocity();
@ -236,9 +240,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
invinsibilityTicks--; invinsibilityTicks--;
} }
if (landEvent != null && entity.isOnGround() && landedChanged()) { if (entity.isOnGround() && landedChanged()) {
landEvent.run(); onLanded(prevFallDistance);
landEvent = null;
} }
if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) { if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) {
@ -261,6 +264,11 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
transportation.tick(); 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<EntityAttributeModifier> modifierSupplier, boolean permanent) { public void updateAttributeModifier(UUID id, EntityAttribute attribute, float desiredValue, Float2ObjectFunction<EntityAttributeModifier> modifierSupplier, boolean permanent) {
@Nullable @Nullable
EntityAttributeInstance instance = asEntity().getAttributeInstance(attribute); EntityAttributeInstance instance = asEntity().getAttributeInstance(attribute);
@ -465,10 +473,17 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
.isPresent(); .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 -> { 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 @Override

View file

@ -723,9 +723,22 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
}); });
} }
public Optional<Float> onImpact(float distance, float damageMultiplier, DamageSource cause) { @Override
float originalDistance = distance; 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(); boolean extraProtection = getSpellSlot().get(SpellType.SHIELD, false).isPresent();
if (!entity.isCreative() && !entity.isSpectator()) { if (!entity.isCreative() && !entity.isSpectator()) {
@ -740,14 +753,9 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
if (getCompositeRace().canFly() || (getCompositeRace().canUseEarth() && entity.isSneaking())) { if (getCompositeRace().canFly() || (getCompositeRace().canUseEarth() && entity.isSneaking())) {
distance -= 5; distance -= 5;
} }
distance = Math.max(0, distance);
} }
handleFall(distance, damageMultiplier, cause); return Math.max(0, distance);
if (distance != originalDistance) {
return Optional.of(distance);
}
return Optional.empty();
} }
public void onEat(ItemStack stack) { public void onEat(ItemStack stack) {
@ -762,19 +770,6 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, 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) { public void onKill(Entity killedEntity, DamageSource damage) {
if (killedEntity != null && killedEntity.getType() == EntityType.PHANTOM && getPhysics().isFlying()) { if (killedEntity != null && killedEntity.getType() == EntityType.PHANTOM && getPhysics().isFlying()) {
UCriteria.KILL_PHANTOM_WHILE_FLYING.trigger(entity); UCriteria.KILL_PHANTOM_WHILE_FLYING.trigger(entity);

View file

@ -6,6 +6,7 @@ import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; 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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 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); 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) @Inject(method = "hurtByWater()Z", at = @At("HEAD"), cancellable = true)
private void onCanBeHurtByWater(CallbackInfoReturnable<Boolean> info) { private void onCanBeHurtByWater(CallbackInfoReturnable<Boolean> info) {
TriState hurtByWater = get().canBeHurtByWater(); TriState hurtByWater = get().canBeHurtByWater();

View file

@ -26,7 +26,6 @@ import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.stat.Stats;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -48,24 +47,11 @@ abstract class MixinPlayerEntity extends LivingEntity implements Equine.Containe
Pony.registerAttributes(info.getReturnValue()); 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) { protected float modifyDamageAmount(float amount, DamageSource source) {
return get().modifyDamage(source, amount).orElse(amount); 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<Boolean> 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")) @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<ItemStack> info) { private void onEatFood(World world, ItemStack stack, CallbackInfoReturnable<ItemStack> info) {
get().onEat(stack); get().onEat(stack);