Cancel stomps when moving upwards

This commit is contained in:
Sollace 2024-05-17 19:28:08 +01:00
parent e6ec2083ef
commit 04298eefcf
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
3 changed files with 145 additions and 79 deletions

View file

@ -6,9 +6,11 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; 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.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony; 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.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;
@ -105,74 +106,89 @@ public class EarthPonyStompAbility implements Ability<Hit> {
@Override @Override
public boolean apply(Pony iplayer, Hit data) { 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 -> { var r = new LandingEventHandler.Callback() {
player.fallDistance = 0; @Override
BlockPos center = PosHelper.findSolidGroundAt(player.getEntityWorld(), player.getBlockPos(), iplayer.getPhysics().getGravitySignum()); public float dispatch(float fallDistance) {
// fail if landing above the starting position
float heavyness = 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player); if (player.getY() > initialY) {
return fallDistance;
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; 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); iplayer.asWorld().getOtherEntities(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> {
BlockState steppingState = player.getSteppingBlockState(); double dist = Math.sqrt(center.getSquaredDistance(i.getBlockPos()));
if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) {
ParticleUtils.spawnParticle(player.getWorld(), new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), player.getBlockPos().down().toCenterPos(), Vec3d.ZERO); 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); @Override
iplayer.asEntity().addExhaustion(3); public void onCancelled() {
return 0F; System.out.println("Cancel Stomp");
iplayer.playSound(USounds.GUI_ABILITY_FAIL, 1F);
}
}; };
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.get(0F), 5); AwaitTickQueue.scheduleTask(iplayer.asWorld(), w -> r.dispatch(0F), 5);
} else { } else {
thrustDownwards(iplayer); thrustDownwards(iplayer);
iplayer.waitForFall(r); iplayer.waitForFall(r);

View file

@ -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> 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();
}
}

View file

@ -1,7 +1,6 @@
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;
@ -39,7 +38,6 @@ 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;
@ -84,10 +82,6 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
private final Interactable landedHeuristic; private final Interactable landedHeuristic;
private final Interactable jumpingHeuristic; private final Interactable jumpingHeuristic;
@Nullable
private final AtomicReference<Float2FloatFunction> landEvent = new AtomicReference<>();
private float prevFallDistance;
private boolean invisible = false; private boolean invisible = false;
@Nullable @Nullable
@ -101,6 +95,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
private final List<Tickable> tickers = new ArrayList<>(); private final List<Tickable> tickers = new ArrayList<>();
private final LandingEventHandler landEvent = addTicker(new LandingEventHandler(this));
private final Enchantments enchants = addTicker(new Enchantments(this)); private final Enchantments enchants = addTicker(new Enchantments(this));
private final ItemTracker armour = addTicker(new ItemTracker(this)); private final ItemTracker armour = addTicker(new ItemTracker(this));
//private final Transportation<T> transportation = new Transportation<>(this); //private final Transportation<T> transportation = new Transportation<>(this);
@ -134,12 +129,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
this.invisible = invisible; this.invisible = invisible;
} }
public void waitForFall(Float2FloatFunction action) { public void waitForFall(LandingEventHandler.Callback callback) {
if (entity.isOnGround()) { landEvent.setCallback(callback);
action.get(0F);
} else {
landEvent.set(action);
}
} }
public boolean sneakingChanged() { public boolean sneakingChanged() {
@ -209,7 +200,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override @Override
public boolean beforeUpdate() { public boolean beforeUpdate() {
prevFallDistance = entity.fallDistance; landEvent.beforeTick();
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();
@ -240,10 +231,6 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
invinsibilityTicks--; invinsibilityTicks--;
} }
if (entity.isOnGround() && landedChanged()) {
onLanded(prevFallDistance);
}
if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) { if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) {
entity.setVelocity(entity.getVelocity().multiply(0, 1, 0)); entity.setVelocity(entity.getVelocity().multiply(0, 1, 0));
updateVelocity(); updateVelocity();
@ -264,11 +251,6 @@ 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);
@ -474,7 +456,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
} }
public float onImpact(float distance, float damageMultiplier, DamageSource cause) { 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 -> { getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).ifPresent(spell -> {
spell.getDisguise().onImpact(this, fallDistance, damageMultiplier, cause); spell.getDisguise().onImpact(this, fallDistance, damageMultiplier, cause);