From 25abf359bfb3d061063ef390307a726911ca49e7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 6 Aug 2023 21:46:07 +0100 Subject: [PATCH] Changelings can now climb on walls and hang from ceilings, like little spider pigs! --- .../com/minelittlepony/unicopia/Race.java | 26 +++--- .../unicopia/client/render/PlayerPoser.java | 29 ++++++- .../unicopia/entity/player/PlayerPhysics.java | 2 +- .../unicopia/entity/player/Pony.java | 82 ++++++++++++++++++- 4 files changed, 119 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index 4524ce67..5c274cfe 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -22,18 +22,18 @@ import net.minecraft.util.Identifier; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; -public record Race (boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal) implements Affine { +public record Race (boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine { public static final String DEFAULT_ID = "unicopia:unset"; public static final Registry REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID); public static final RegistryKey> REGISTRY_KEY = REGISTRY.getKey(); private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("race.unknown", id)); - public static Race register(String name, boolean magic, FlightType flight, boolean earth, boolean nocturnal) { - return register(Unicopia.id(name), magic, flight, earth, nocturnal); + public static Race register(String name, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) { + return register(Unicopia.id(name), magic, flight, earth, nocturnal, canHang); } - public static Race register(Identifier id, boolean magic, FlightType flight, boolean earth, boolean nocturnal) { - return Registry.register(REGISTRY, id, new Race(magic, flight, earth, nocturnal)); + public static Race register(Identifier id, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) { + return Registry.register(REGISTRY, id, new Race(magic, flight, earth, nocturnal, canHang)); } public static RegistryKeyArgumentType argument() { @@ -44,14 +44,14 @@ public record Race (boolean canCast, FlightType flightType, boolean canUseEarth, * The default, unset race. * This is used if there are no other races. */ - public static final Race UNSET = register("unset", false, FlightType.NONE, false, false); - public static final Race HUMAN = register("human", false, FlightType.NONE, false, false); - public static final Race EARTH = register("earth", false, FlightType.NONE, true, false); - public static final Race UNICORN = register("unicorn", true, FlightType.NONE, false, false); - public static final Race PEGASUS = register("pegasus", false, FlightType.AVIAN, false, false); - public static final Race BAT = register("bat", false, FlightType.AVIAN, false, true); - public static final Race ALICORN = register("alicorn", true, FlightType.AVIAN, true, false); - public static final Race CHANGELING = register("changeling", false, FlightType.INSECTOID, false, false); + public static final Race UNSET = register("unset", false, FlightType.NONE, false, false, false); + public static final Race HUMAN = register("human", false, FlightType.NONE, false, false, false); + public static final Race EARTH = register("earth", false, FlightType.NONE, true, false, false); + public static final Race UNICORN = register("unicorn", true, FlightType.NONE, false, false, false); + public static final Race PEGASUS = register("pegasus", false, FlightType.AVIAN, false, false, false); + public static final Race BAT = register("bat", false, FlightType.AVIAN, false, true, true); + public static final Race ALICORN = register("alicorn", true, FlightType.AVIAN, true, false, false); + public static final Race CHANGELING = register("changeling", false, FlightType.INSECTOID, false, false, true); public static void bootstrap() {} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java b/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java index 0f314497..e063a53f 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java @@ -108,11 +108,10 @@ public class PlayerPoser { float pitch = 3F * saw; float yaw = 0.5F * saw; - float roll = saw; if (ponyRace.isEquine()) { rearUp(matrices, model, saw); - pitch = roll * 2F; + pitch = saw * 2F; model.head.pitch += saw * 0.5F; } @@ -132,6 +131,31 @@ public class PlayerPoser { break; } + case CLIMB: { + float saw = AnimationUtil.seesaw(progress); + + float pitch = MathHelper.clamp(3F * saw, 1, 2); + float yaw = 0.5F * saw; + + if (ponyRace.isEquine()) { + rearUp(matrices, model, saw); + pitch = saw * 2F; + model.head.pitch += saw * 0.5F; + } + + rotateArm(model.leftArm, pitch, -yaw, yaw / 2F); + rotateArm(model.rightLeg, pitch / 2F, yaw, 0); + + saw = AnimationUtil.seesaw((progress + 0.5F) % 1); + + pitch = MathHelper.clamp(3F * saw, 1, 2) * (ponyRace.isEquine() ? 2 : 1); + yaw = 0.5F * saw; + + rotateArm(model.rightArm, pitch, yaw, -yaw / 2F); + rotateArm(model.leftLeg, pitch / 2F, -yaw, 0); + + break; + } case WAVE_ONE: case WAVE_TWO: { progress = AnimationUtil.seesaw(progress); @@ -283,6 +307,7 @@ public class PlayerPoser { WAVE_ONE(USounds.ENTITY_PLAYER_WHISTLE, 20), WAVE_TWO(USounds.ENTITY_PLAYER_WHISTLE, 20), KICK(USounds.ENTITY_PLAYER_KICK, 5), + CLIMB(20), STOMP(5), WIGGLE_NOSE(6), SPREAD_WINGS(6), 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 9a812384..03230c70 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -98,7 +98,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab @Override public float getGravityModifier() { float modifier = getPersistantGravityModifier(); - if (pony.isHanging()) { + if (pony.isHanging() && pony.getObservedSpecies() == Race.BAT) { modifier *= -0.05F; } return modifier; 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 755146cd..cd9ad5e4 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -6,6 +6,7 @@ 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.client.render.PlayerPoser.AnimationInstance; import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.ability.*; @@ -34,6 +35,7 @@ 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; @@ -58,6 +60,7 @@ 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); @@ -101,6 +104,9 @@ 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; @@ -359,14 +365,69 @@ public class Pony extends Living implements Copyable, Update powers.tick(); - if (entity.getClimbingPos().isPresent() && entity.isSneaky()) { + BlockPos climbingPos = entity.getClimbingPos().orElse(null); + + if (!getPhysics().isFlying() && !entity.getAbilities().flying && climbingPos != null && getObservedSpecies() == Race.CHANGELING) { Vec3d vel = entity.getVelocity(); - entity.setVelocity(vel.x, 0, vel.z); + if (entity.isSneaky()) { + entity.setVelocity(vel.x, 0, vel.z); + } + + distanceClimbed += vel.length(); + 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(SoundEvents.ENTITY_CHICKEN_STEP, + (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 && isFaceClimbable(entity.getWorld(), entity.getBlockPos(), attachDirection)) { + entity.setBodyYaw(attachDirection.asRotation()); + entity.prevBodyYaw = attachDirection.asRotation(); + } + } + + if (getAnimation().isOf(Animation.NONE) || (getAnimation().isOf(Animation.NONE) && canhangHere)) { + if (canhangHere) { + setAnimation(Animation.ARMS_UP, Recipient.HUMAN); + } else if (distanceClimbed > 1.5) { + setAnimation(Animation.CLIMB, Recipient.HUMAN); + } + } + } else { + distanceClimbed = 0; } return false; } + 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); } @@ -410,6 +471,17 @@ public class Pony extends Living implements Copyable, Update } private void updateAnimations() { + + if (distanceClimbed > 0 + && ((animation.isOf(Animation.CLIMB) && entity.isSneaky()) || (animation.isOf(Animation.ARMS_UP) && isHanging())) + && entity.getClimbingPos().isPresent() + && entity.getVelocity().length() < 0.08F) { + if (animation.isOf(Animation.ARMS_UP)) { + animationDuration = 2; + } + return; + } + if (animationDuration > 0 && --animationDuration <= 0) { setAnimation(AnimationInstance.NONE); } @@ -422,8 +494,10 @@ public class Pony extends Living implements Copyable, Update if (isHanging()) { ((LivingEntityDuck)entity).setLeaningPitch(0); - if (!isClient() && (getObservedSpecies() != Race.BAT || (ticksHanging++ > 2 && getHangingPosition().filter(getOrigin().down()::equals).filter(this::canHangAt).isEmpty()))) { - stopHanging(); + if (!getObservedSpecies().canHang() || (ticksHanging++ > 2 && getHangingPosition().filter(getOrigin().down()::equals).filter(this::canHangAt).isEmpty())) { + if (!isClient()) { + stopHanging(); + } } } else { ticksHanging = 0;