diff --git a/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java index f7e75dc2..9237ab34 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java @@ -38,13 +38,13 @@ public class BatPonyHangAbility implements Ability { @Override public Optional 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 { @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; diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java new file mode 100644 index 00000000..032d4f89 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Acrobatics.java @@ -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> 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 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)); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index 045dbfdb..ca497829 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -115,7 +115,7 @@ public class PlayerPhysics extends EntityPhysics 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 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 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); } } 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 16d064ba..1a45d170 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -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 implements Copyable, UpdateCallback { private static final TrackedData RACE = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.STRING); @@ -71,7 +67,6 @@ public class Pony extends Living implements Copyable, Update static final TrackedData ENERGY = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT); static final TrackedData EXHAUSTION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT); static final TrackedData EXERTION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT); - static final TrackedData> HANGING_POSITION = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.OPTIONAL_BLOCK_POS); static final TrackedData MANA = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT); static final TrackedData XP = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT); static final TrackedData CHARGE = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT); @@ -87,6 +82,7 @@ public class Pony extends Living implements Copyable, 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 advancementProgress = new HashMap<>(); @@ -101,8 +97,6 @@ public class Pony extends Living implements Copyable, Update private boolean dirty; - private int ticksHanging; - private float magicExhaustion = 0; private int ticksInvulnerable; @@ -111,9 +105,6 @@ public class Pony extends Living implements Copyable, 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 implements Copyable, 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 implements Copyable, Update return charms; } + public Acrobatics getAcrobatics() { + return acrobatics; + } + @Override public LevelStore getLevel() { return levels; @@ -393,67 +387,7 @@ public class Pony extends Living implements Copyable, 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 implements Copyable, 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 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 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 implements Copyable, 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 implements Copyable, 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 implements Copyable, 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 implements Copyable, 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 implements Copyable, 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");