diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 4f9ffd1c..6e862f6f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -6,9 +6,11 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; +import com.minelittlepony.unicopia.entity.LandingEventHandler; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.player.Pony; @@ -20,7 +22,6 @@ 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; @@ -105,74 +106,89 @@ public class EarthPonyStompAbility implements Ability { @Override public boolean apply(Pony iplayer, Hit data) { - PlayerEntity player = iplayer.asEntity(); + final PlayerEntity player = iplayer.asEntity(); + final double initialY = player.getY() + 5; - 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); - - iplayer.asWorld().getOtherEntities(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> { - double dist = Math.sqrt(center.getSquaredDistance(i.getBlockPos())); - - if (dist <= rad + 3) { - double inertia = 2 / dist; - - if (i instanceof LivingEntity) { - inertia *= 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, (LivingEntity)i); - } - inertia /= heavyness; - - double liftAmount = Math.sin(Math.PI * dist / rad) * 12 * iplayer.getPhysics().getGravitySignum(); - - i.addVelocity( - -(player.getX() - i.getX()) / inertia, - -(player.getY() - i.getY() - liftAmount) / inertia + (dist < 1 ? dist : 0), - -(player.getZ() - i.getZ()) / inertia); - - double amount = (1.5F * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue() + heavyness * 0.4) / (float)(dist * 1.3F); - - if (i instanceof PlayerEntity) { - Race.Composite race = Pony.of((PlayerEntity)i).getCompositeRace(); - if (race.canUseEarth()) { - amount /= 3; - } - - if (race.canFly()) { - amount *= 4; - } - } - - if (i instanceof LivingEntity) { - amount /= 1 + (EnchantmentHelper.getEquipmentLevel(UEnchantments.PADDED, (LivingEntity)i) / 6F); - } - - i.damage(iplayer.damageOf(UDamageTypes.SMASH, iplayer), (float)amount); - Living.updateVelocity(i); + var r = new LandingEventHandler.Callback() { + @Override + public float dispatch(float fallDistance) { + // fail if landing above the starting position + if (player.getY() > initialY) { + return fallDistance; } - }); - double radius = rad + heavyness * 0.3; + player.fallDistance = 0; + BlockPos center = PosHelper.findSolidGroundAt(player.getEntityWorld(), player.getBlockPos(), iplayer.getPhysics().getGravitySignum()); - spawnEffectAround(player, center, radius, rad); + float heavyness = 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player); - ParticleUtils.spawnParticle(player.getWorld(), UParticles.GROUND_POUND, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); - BlockState steppingState = player.getSteppingBlockState(); - if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) { - ParticleUtils.spawnParticle(player.getWorld(), new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), player.getBlockPos().down().toCenterPos(), Vec3d.ZERO); + iplayer.asWorld().getOtherEntities(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> { + double dist = Math.sqrt(center.getSquaredDistance(i.getBlockPos())); + + if (dist <= rad + 3) { + double inertia = 2 / dist; + + if (i instanceof LivingEntity) { + inertia *= 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, (LivingEntity)i); + } + inertia /= heavyness; + + double liftAmount = Math.sin(Math.PI * dist / rad) * 12 * iplayer.getPhysics().getGravitySignum(); + + i.addVelocity( + -(player.getX() - i.getX()) / inertia, + -(player.getY() - i.getY() - liftAmount) / inertia + (dist < 1 ? dist : 0), + -(player.getZ() - i.getZ()) / inertia); + + double amount = (1.5F * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue() + heavyness * 0.4) / (float)(dist * 1.3F); + + if (i instanceof PlayerEntity) { + Race.Composite race = Pony.of((PlayerEntity)i).getCompositeRace(); + if (race.canUseEarth()) { + amount /= 3; + } + + if (race.canFly()) { + amount *= 4; + } + } + + if (i instanceof LivingEntity) { + amount /= 1 + (EnchantmentHelper.getEquipmentLevel(UEnchantments.PADDED, (LivingEntity)i) / 6F); + } + + i.damage(iplayer.damageOf(UDamageTypes.SMASH, iplayer), (float)amount); + Living.updateVelocity(i); + } + }); + + double radius = rad + heavyness * 0.3; + + spawnEffectAround(player, center, radius, rad); + + ParticleUtils.spawnParticle(player.getWorld(), UParticles.GROUND_POUND, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); + BlockState steppingState = player.getSteppingBlockState(); + if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) { + ParticleUtils.spawnParticle(player.getWorld(), new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), player.getBlockPos().down().toCenterPos(), Vec3d.ZERO); + } + + iplayer.subtractEnergyCost(rad); + iplayer.asEntity().addExhaustion(3); + return 0F; } - iplayer.subtractEnergyCost(rad); - iplayer.asEntity().addExhaustion(3); - return 0F; + @Override + public void onCancelled() { + System.out.println("Cancel Stomp"); + iplayer.playSound(USounds.GUI_ABILITY_FAIL, 1F); + } }; if (iplayer.asEntity().isOnGround()) { iplayer.setAnimation(Animation.STOMP, Animation.Recipient.ANYONE, 10); iplayer.asEntity().jump(); iplayer.updateVelocity(); - AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.get(0F), 5); + AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.dispatch(0F), 5); } else { thrustDownwards(iplayer); iplayer.waitForFall(r); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/LandingEventHandler.java b/src/main/java/com/minelittlepony/unicopia/entity/LandingEventHandler.java new file mode 100644 index 00000000..41516581 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/LandingEventHandler.java @@ -0,0 +1,68 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.concurrent.atomic.AtomicReference; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.util.Tickable; + +public class LandingEventHandler implements Tickable { + + private final Living living; + + @Nullable + private final AtomicReference callback = new AtomicReference<>(); + private double prevY; + private float prevFallDistance; + + public LandingEventHandler(Living living) { + this.living = living; + } + + public void setCallback(LandingEventHandler.Callback callback) { + if (living.asEntity().isOnGround()) { + callback.dispatch(0F); + } else { + updateCallback(callback); + } + } + + public void beforeTick() { + + } + + @Override + public void tick() { + if (living.asEntity().getY() > prevY) { + discard(); + } + prevY = living.asEntity().getY(); + + if (living.asEntity().isOnGround() && living.landedChanged()) { + fire(prevFallDistance); + } + prevFallDistance = living.asEntity().fallDistance; + } + + float fire(float fallDistance) { + var event = callback.getAndSet(null); + return event == null ? fallDistance : event.dispatch(fallDistance); + } + + void discard() { + updateCallback(null); + } + + void updateCallback(@Nullable Callback callback) { + var event = this.callback.getAndSet(callback); + if (event != null) { + event.onCancelled(); + } + } + + public interface Callback { + float dispatch(float fallDistance); + + void onCancelled(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index d006a811..95631823 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -1,7 +1,6 @@ package com.minelittlepony.unicopia.entity; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -39,7 +38,6 @@ 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; @@ -84,10 +82,6 @@ public abstract class Living implements Equine, Caste private final Interactable landedHeuristic; private final Interactable jumpingHeuristic; - @Nullable - private final AtomicReference landEvent = new AtomicReference<>(); - private float prevFallDistance; - private boolean invisible = false; @Nullable @@ -101,6 +95,7 @@ public abstract class Living implements Equine, Caste private final List tickers = new ArrayList<>(); + private final LandingEventHandler landEvent = addTicker(new LandingEventHandler(this)); private final Enchantments enchants = addTicker(new Enchantments(this)); private final ItemTracker armour = addTicker(new ItemTracker(this)); //private final Transportation transportation = new Transportation<>(this); @@ -134,12 +129,8 @@ public abstract class Living implements Equine, Caste this.invisible = invisible; } - public void waitForFall(Float2FloatFunction action) { - if (entity.isOnGround()) { - action.get(0F); - } else { - landEvent.set(action); - } + public void waitForFall(LandingEventHandler.Callback callback) { + landEvent.setCallback(callback); } public boolean sneakingChanged() { @@ -209,7 +200,7 @@ public abstract class Living implements Equine, Caste @Override public boolean beforeUpdate() { - prevFallDistance = entity.fallDistance; + landEvent.beforeTick(); if (EffectUtils.getAmplifier(entity, UEffects.PARALYSIS) > 1 && entity.getVelocity().horizontalLengthSquared() > 0) { entity.setVelocity(entity.getVelocity().multiply(0, 1, 0)); updateVelocity(); @@ -240,10 +231,6 @@ public abstract class Living implements Equine, Caste invinsibilityTicks--; } - if (entity.isOnGround() && landedChanged()) { - onLanded(prevFallDistance); - } - if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) { entity.setVelocity(entity.getVelocity().multiply(0, 1, 0)); updateVelocity(); @@ -264,11 +251,6 @@ 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); @@ -474,7 +456,7 @@ public abstract class Living implements Equine, Caste } public float onImpact(float distance, float damageMultiplier, DamageSource cause) { - float fallDistance = onLanded(getEffectiveFallDistance(distance)); + float fallDistance = landEvent.fire(getEffectiveFallDistance(distance)); getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).ifPresent(spell -> { spell.getDisguise().onImpact(this, fallDistance, damageMultiplier, cause);