Move all the logic for hanging and climbing into a separate class and fix bat ponies not automatically cancelling when they move

This commit is contained in:
Sollace 2023-11-07 18:46:05 +00:00
parent 04cefd8961
commit 884ced25f1
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
4 changed files with 203 additions and 139 deletions

View file

@ -38,13 +38,13 @@ public class BatPonyHangAbility implements Ability<Multi> {
@Override
public Optional<Multi> prepare(Pony player) {
if (player.isHanging()) {
if (player.getAcrobatics().isHanging()) {
return Optional.of(new Multi(BlockPos.ZERO, 0));
}
return TraceHelper.findBlock(player.asEntity(), 5, 1)
.map(BlockPos::down)
.filter(player::canHangAt)
.filter(player.getAcrobatics()::canHangAt)
.map(pos -> new Multi(pos, 1));
}
@ -55,13 +55,13 @@ public class BatPonyHangAbility implements Ability<Multi> {
@Override
public boolean apply(Pony player, Multi data) {
if (data.hitType() == 0 && player.isHanging()) {
player.stopHanging();
if (data.hitType() == 0 && player.getAcrobatics().isHanging()) {
player.getAcrobatics().stopHanging();
return true;
}
if (data.hitType() == 1 && player.canHangAt(data.pos().pos())) {
player.startHanging(data.pos().pos());
if (data.hitType() == 1 && player.getAcrobatics().canHangAt(data.pos().pos())) {
player.getAcrobatics().startHanging(data.pos().pos());
}
return true;

View file

@ -0,0 +1,183 @@
package com.minelittlepony.unicopia.entity.player;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation.Recipient;
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.Tickable;
import net.minecraft.block.BlockState;
import net.minecraft.block.SideShapeType;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class Acrobatics implements Tickable, NbtSerialisable {
static final TrackedData<Optional<BlockPos>> HANGING_POSITION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.OPTIONAL_BLOCK_POS);
private int ticksHanging;
private Direction attachDirection;
double distanceClimbed;
private final Pony pony;
private final PlayerEntity entity;
public Acrobatics(Pony pony) {
this.pony = pony;
this.entity = pony.asEntity();
entity.getDataTracker().startTracking(HANGING_POSITION, Optional.empty());
pony.addTicker(this::checkDislodge);
}
@Override
public void tick() {
BlockPos climbingPos = entity.getClimbingPos().orElse(null);
if (!pony.getPhysics().isFlying() && !entity.getAbilities().flying
&& climbingPos != null
&& pony.getObservedSpecies() == Race.CHANGELING) {
Vec3d vel = entity.getVelocity();
if (entity.isSneaking()) {
entity.setVelocity(vel.x, 0, vel.z);
}
distanceClimbed += Math.abs(pony.getMotion().getClientVelocity().y);
BlockPos hangingPos = entity.getBlockPos().up();
boolean canhangHere = canHangAt(hangingPos);
if (distanceClimbed > 1.5) {
if (vel.length() > 0.08F && entity.age % (3 + entity.getRandom().nextInt(5)) == 0) {
entity.playSound(USounds.ENTITY_PLAYER_CHANGELING_CLIMB,
(float)entity.getRandom().nextTriangular(0.5, 0.3),
entity.getSoundPitch()
);
}
boolean skipHangCheck = false;
Direction newAttachDirection = entity.getHorizontalFacing();
if (isFaceClimbable(entity.getWorld(), entity.getBlockPos(), newAttachDirection) && (newAttachDirection != attachDirection)) {
attachDirection = newAttachDirection;
skipHangCheck = true;
}
if (!skipHangCheck && canhangHere) {
if (!isHanging()) {
startHanging(hangingPos);
} else {
if (((LivingEntityDuck)entity).isJumping()) {
// Jump to let go
return;
}
entity.setVelocity(entity.getVelocity().multiply(1, 0, 1));
entity.setSneaking(false);
}
} else if (attachDirection != null) {
if (isFaceClimbable(entity.getWorld(), entity.getBlockPos(), attachDirection)) {
entity.setBodyYaw(attachDirection.asRotation());
entity.prevBodyYaw = attachDirection.asRotation();
} else {
entity.setVelocity(vel);
entity.isClimbing();
}
}
}
if (canhangHere) {
pony.setAnimation(Animation.HANG, Recipient.ANYONE);
} else if (distanceClimbed > 1.5) {
pony.setAnimation(Animation.CLIMB, Recipient.ANYONE);
}
} else {
distanceClimbed = 0;
attachDirection = null;
}
}
private void checkDislodge() {
if (isHanging()) {
((LivingEntityDuck)entity).setLeaningPitch(0);
if (!pony.isClient() && !canKeepHanging()) {
stopHanging();
}
} else {
ticksHanging = 0;
}
}
boolean isFaceClimbable(World world, BlockPos pos, Direction direction) {
pos = pos.offset(direction);
return world.getBlockState(pos).isSideSolid(world, pos, direction, SideShapeType.CENTER);
}
public Optional<BlockPos> getHangingPosition() {
return entity.getDataTracker().get(HANGING_POSITION);
}
public boolean isHanging() {
return getHangingPosition().isPresent();
}
public void stopHanging() {
entity.getDataTracker().set(HANGING_POSITION, Optional.empty());
entity.calculateDimensions();
ticksHanging = 0;
}
public void startHanging(BlockPos pos) {
entity.getDataTracker().set(HANGING_POSITION, Optional.of(pos));
entity.teleport(pos.getX() + 0.5, pos.getY() - 1, pos.getZ() + 0.5);
entity.setVelocity(Vec3d.ZERO);
entity.setSneaking(false);
entity.stopFallFlying();
pony.getPhysics().cancelFlight(true);
}
public boolean canHangAt(BlockPos pos) {
if (!pony.asWorld().isAir(pos) || !pony.asWorld().isAir(pos.down())) {
return false;
}
pos = pos.up();
BlockState state = pony.asWorld().getBlockState(pos);
return state.isSolidSurface(pony.asWorld(), pos, entity, Direction.DOWN) && entity.getWorld().isAir(entity.getBlockPos().down());
}
private boolean canKeepHanging() {
Race race = pony.getObservedSpecies();
if (!race.canHang()) {
return false;
}
if (ticksHanging++ <= 2) {
return true;
}
return getHangingPosition().filter(hangingPos -> {
return (race != Race.BAT || hangingPos.equals(pony.getOrigin().down())) && canHangAt(hangingPos);
}).isPresent();
}
@Override
public void toNBT(NbtCompound compound) {
compound.putInt("ticksHanging", ticksHanging);
BLOCK_POS.writeOptional("hangingPosition", compound, getHangingPosition());
}
@Override
public void fromNBT(NbtCompound compound) {
ticksHanging = compound.getInt("ticksHanging");
pony.asEntity().getDataTracker().set(HANGING_POSITION, NbtSerialisable.BLOCK_POS.readOptional("hangingPosition", compound));
}
}

View file

@ -115,7 +115,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
@Override
public float getGravityModifier() {
float modifier = getPersistantGravityModifier();
if (pony.isHanging() && pony.getObservedSpecies() == Race.BAT) {
if (pony.getAcrobatics().isHanging() && pony.getObservedSpecies() == Race.BAT) {
modifier *= -0.05F;
}
return modifier;
@ -385,7 +385,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
entity.setVelocity(velocity.toImmutable());
if (isFlying() && !entity.isFallFlying() && !pony.isHanging() && pony.isClient()) {
if (isFlying() && !entity.isFallFlying() && !pony.getAcrobatics().isHanging() && pony.isClient()) {
if (!MineLPDelegate.getInstance().getPlayerPonyRace(entity).isEquine() && getHorizontalMotion() > 0.03) {
float pitch = ((LivingEntityDuck)entity).getLeaningPitch();
if (pitch < 1) {
@ -533,7 +533,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
&& pony.getJumpingHeuristic().hasChanged(Heuristic.TWICE);
boolean fallingTakeOffCondition = !entity.isOnGround() && velocity.y < -1.6 * getGravitySignum() && entity.fallDistance > 1;
if ((takeOffCondition || fallingTakeOffCondition) && !pony.isHanging() && !isCancelled) {
if ((takeOffCondition || fallingTakeOffCondition) && !pony.getAcrobatics().isHanging() && !isCancelled) {
initiateTakeoff(velocity);
}
}

View file

@ -6,7 +6,6 @@ import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation.Recipient;
import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate;
import com.minelittlepony.unicopia.client.render.PlayerPoser.AnimationInstance;
import com.minelittlepony.unicopia.*;
@ -37,8 +36,6 @@ import com.google.common.collect.Streams;
import com.minelittlepony.common.util.animation.Interpolator;
import com.mojang.authlib.GameProfile;
import net.minecraft.block.BlockState;
import net.minecraft.block.SideShapeType;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.*;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
@ -63,7 +60,6 @@ import net.minecraft.util.Hand;
import net.minecraft.util.math.*;
import net.minecraft.world.GameMode;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, UpdateCallback {
private static final TrackedData<String> RACE = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.STRING);
@ -71,7 +67,6 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
static final TrackedData<Float> ENERGY = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
static final TrackedData<Float> EXHAUSTION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
static final TrackedData<Float> EXERTION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
static final TrackedData<Optional<BlockPos>> HANGING_POSITION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.OPTIONAL_BLOCK_POS);
static final TrackedData<Float> MANA = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
static final TrackedData<Float> XP = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
static final TrackedData<Float> CHARGE = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
@ -87,6 +82,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
private final PlayerCharmTracker charms = new PlayerCharmTracker(this);
private final PlayerCamera camera = new PlayerCamera(this);
private final TraitDiscovery discoveries = new TraitDiscovery(this);
private final Acrobatics acrobatics = new Acrobatics(this);
private final Map<String, Integer> advancementProgress = new HashMap<>();
@ -101,8 +97,6 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
private boolean dirty;
private int ticksHanging;
private float magicExhaustion = 0;
private int ticksInvulnerable;
@ -111,9 +105,6 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
private boolean hasShades;
private int ticksSunImmunity = INITIAL_SUN_IMMUNITY;
private Direction attachDirection;
private double distanceClimbed;
private AnimationInstance animation = new AnimationInstance(Animation.NONE, Animation.Recipient.ANYONE);
private int animationMaxDuration;
private int animationDuration;
@ -125,7 +116,6 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
this.mana = addTicker(new ManaContainer(this));
player.getDataTracker().startTracking(RACE, Race.DEFAULT_ID);
player.getDataTracker().startTracking(HANGING_POSITION, Optional.empty());
addTicker(this::updateAnimations);
addTicker(this::updateBatPonyAbilities);
@ -250,6 +240,10 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
return charms;
}
public Acrobatics getAcrobatics() {
return acrobatics;
}
@Override
public LevelStore getLevel() {
return levels;
@ -393,67 +387,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
magicExhaustion = ManaConsumptionUtil.burnFood(entity, magicExhaustion);
powers.tick();
BlockPos climbingPos = entity.getClimbingPos().orElse(null);
if (!getPhysics().isFlying() && !entity.getAbilities().flying
&& climbingPos != null
&& getObservedSpecies() == Race.CHANGELING) {
Vec3d vel = entity.getVelocity();
if (entity.isSneaking()) {
entity.setVelocity(vel.x, 0, vel.z);
}
distanceClimbed += Math.abs(getMotion().getClientVelocity().y);
BlockPos hangingPos = entity.getBlockPos().up();
boolean canhangHere = canHangAt(hangingPos);
if (distanceClimbed > 1.5) {
if (vel.length() > 0.08F && entity.age % (3 + entity.getRandom().nextInt(5)) == 0) {
entity.playSound(USounds.ENTITY_PLAYER_CHANGELING_CLIMB,
(float)entity.getRandom().nextTriangular(0.5, 0.3),
entity.getSoundPitch()
);
}
boolean skipHangCheck = false;
Direction newAttachDirection = entity.getHorizontalFacing();
if (isFaceClimbable(entity.getWorld(), entity.getBlockPos(), newAttachDirection) && (newAttachDirection != attachDirection)) {
attachDirection = newAttachDirection;
skipHangCheck = true;
}
if (!skipHangCheck && canhangHere) {
if (!isHanging()) {
startHanging(hangingPos);
} else {
if (((LivingEntityDuck)entity).isJumping()) {
// Jump to let go
return false;
}
entity.setVelocity(entity.getVelocity().multiply(1, 0, 1));
entity.setSneaking(false);
}
} else if (attachDirection != null) {
if (isFaceClimbable(entity.getWorld(), entity.getBlockPos(), attachDirection)) {
entity.setBodyYaw(attachDirection.asRotation());
entity.prevBodyYaw = attachDirection.asRotation();
} else {
entity.setVelocity(vel);
entity.isClimbing();
}
}
}
if (canhangHere) {
setAnimation(Animation.HANG, Recipient.ANYONE);
} else if (distanceClimbed > 1.5) {
setAnimation(Animation.CLIMB, Recipient.ANYONE);
}
} else {
distanceClimbed = 0;
attachDirection = null;
}
acrobatics.tick();
if (getObservedSpecies() == Race.KIRIN) {
var charge = getMagicalReserves().getCharge();
@ -488,49 +422,10 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
return super.beforeUpdate();
}
private boolean isFaceClimbable(World world, BlockPos pos, Direction direction) {
pos = pos.offset(direction);
return world.getBlockState(pos).isSideSolid(world, pos, direction, SideShapeType.CENTER);
}
public Optional<BlockPos> getHangingPosition() {
return entity.getDataTracker().get(HANGING_POSITION);
}
public boolean isHanging() {
return getHangingPosition().isPresent();
}
public void stopHanging() {
entity.getDataTracker().set(HANGING_POSITION, Optional.empty());
entity.calculateDimensions();
ticksHanging = 0;
}
public void startHanging(BlockPos pos) {
entity.getDataTracker().set(HANGING_POSITION, Optional.of(pos));
entity.teleport(pos.getX() + 0.5, pos.getY() - 1, pos.getZ() + 0.5);
entity.setVelocity(Vec3d.ZERO);
entity.setSneaking(false);
entity.stopFallFlying();
getPhysics().cancelFlight(true);
}
public boolean canHangAt(BlockPos pos) {
if (!asWorld().isAir(pos) || !asWorld().isAir(pos.down())) {
return false;
}
pos = pos.up();
BlockState state = asWorld().getBlockState(pos);
return state.isSolidSurface(asWorld(), pos, entity, Direction.DOWN) && entity.getWorld().isAir(entity.getBlockPos().down());
}
@Override
public Optional<BlockPos> chooseClimbingPos() {
if (getObservedSpecies() == Race.CHANGELING && getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).isEmpty()) {
if (isFaceClimbable(entity.getWorld(), entity.getBlockPos(), entity.getHorizontalFacing()) || canHangAt(entity.getBlockPos())) {
if (acrobatics.isFaceClimbable(entity.getWorld(), entity.getBlockPos(), entity.getHorizontalFacing()) || acrobatics.canHangAt(entity.getBlockPos())) {
return Optional.of(entity.getBlockPos());
}
}
@ -539,7 +434,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
private void updateAnimations() {
if (distanceClimbed > 0
if (acrobatics.distanceClimbed > 0
&& ((animation.isOf(Animation.CLIMB) && entity.isSneaking()) || animation.isOf(Animation.HANG))
&& entity.getClimbingPos().isPresent()
&& entity.getVelocity().length() < 0.08F) {
@ -551,7 +446,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
if (animationDuration > 0 && --animationDuration <= 0) {
if (animation.renderBothArms() && distanceClimbed > 0) {
if (animation.renderBothArms() && acrobatics.distanceClimbed > 0) {
return;
}
@ -564,17 +459,6 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
ticksSunImmunity--;
}
if (isHanging()) {
((LivingEntityDuck)entity).setLeaningPitch(0);
if (!getObservedSpecies().canHang() || (ticksHanging++ > 2 && getHangingPosition().filter(this::canHangAt).isEmpty())) {
if (!isClient()) {
stopHanging();
}
}
} else {
ticksHanging = 0;
}
if (getObservedSpecies() == Race.BAT && !entity.hasPortalCooldown()) {
boolean hasShades = TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.FACE).anyMatch(s -> s.isIn(UTags.SHADES));
if (!this.hasShades && hasShades && getObservedSpecies() == Race.BAT) {
@ -859,10 +743,9 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
super.toSyncronisedNbt(compound);
compound.putString("playerSpecies", Race.REGISTRY.getId(getSpecies()).toString());
compound.putFloat("magicExhaustion", magicExhaustion);
compound.putInt("ticksHanging", ticksHanging);
BLOCK_POS.writeOptional("hangingPosition", compound, getHangingPosition());
compound.putInt("ticksInSun", ticksInSun);
compound.putBoolean("hasShades", hasShades);
compound.put("acrobatics", acrobatics.toNBT());
compound.put("powers", powers.toNBT());
compound.put("gravity", gravity.toNBT());
compound.put("charms", charms.toNBT());
@ -890,11 +773,9 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
levels.set(compound.getInt("levels"));
corruption.set(compound.getInt("corruption"));
mana.fromNBT(compound.getCompound("mana"));
acrobatics.fromNBT(compound.getCompound("acrobatics"));
magicExhaustion = compound.getFloat("magicExhaustion");
ticksHanging = compound.getInt("ticksHanging");
ticksInvulnerable = compound.getInt("ticksInvulnerable");
entity.getDataTracker().set(HANGING_POSITION, NbtSerialisable.BLOCK_POS.readOptional("hangingPosition", compound));
ticksInSun = compound.getInt("ticksInSun");
hasShades = compound.getBoolean("hasShades");