Skip to content

Commit 6577fdc

Browse files
Merge pull request #369 from jpenilla/feat/improved-portal-transitions
Make portal transitions more seamless
2 parents eef65ca + 8fc840a commit 6577fdc

File tree

1 file changed

+54
-17
lines changed

1 file changed

+54
-17
lines changed

src/main/java/com/direwolf20/justdirethings/common/entities/PortalEntity.java

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@
1111
import net.minecraft.server.level.ServerLevel;
1212
import net.minecraft.server.level.ServerPlayer;
1313
import net.minecraft.sounds.SoundSource;
14+
import net.minecraft.util.Mth;
1415
import net.minecraft.world.entity.Entity;
1516
import net.minecraft.world.entity.EntityType;
1617
import net.minecraft.world.entity.player.Player;
1718
import net.minecraft.world.level.ChunkPos;
1819
import net.minecraft.world.level.Level;
1920
import net.minecraft.world.phys.AABB;
21+
import net.minecraft.world.phys.Vec2;
2022
import net.minecraft.world.phys.Vec3;
23+
import net.minecraft.world.phys.shapes.Shapes;
2124
import net.neoforged.neoforge.common.Tags;
2225
import net.neoforged.neoforge.entity.PartEntity;
2326
import org.joml.Quaternionf;
@@ -354,18 +357,42 @@ public Vec3 getTeleportTo(Entity entity, PortalEntity matchingPortal) {
354357
} else {
355358
entityHeightFraction = (entityBB.minY - portalBB.minY) / portalBB.getYsize();
356359
}
360+
361+
// Clamp fraction to [0, 1] to avoid out-of-bounds positioning
362+
entityHeightFraction = Math.max(0.0, Math.min(1.0, entityHeightFraction));
363+
357364
if (matchingPortal.getDirection().getAxis() == Direction.Axis.Y) {
358365
if (matchingPortal.getAlignment() == Direction.Axis.X) {
359-
teleportTo = new Vec3(linkedPortal.getBoundingBox().minX + entityHeightFraction * linkedPortal.getBoundingBox().getXsize(), linkedPortal.getY(), linkedPortal.getZ()).relative(linkedPortal.getDirection(), 0.5f);
366+
double offset = entityHeightFraction * linkedPortal.getBoundingBox().getXsize();
367+
double buffer = entityBB.getXsize() / 2 + Shapes.EPSILON;
368+
// Don't collide in wall next to top/bottom of floor/ceiling portal
369+
offset = Mth.clamp(offset, buffer, linkedPortal.getBoundingBox().getXsize() - buffer);
370+
teleportTo = new Vec3(linkedPortal.getBoundingBox().minX + offset, linkedPortal.getY(), linkedPortal.getZ());
360371
} else {
361-
teleportTo = new Vec3(linkedPortal.getX(), linkedPortal.getY(), linkedPortal.getBoundingBox().minZ + entityHeightFraction * linkedPortal.getBoundingBox().getZsize()).relative(linkedPortal.getDirection(), 0.5f);
372+
double offset = entityHeightFraction * linkedPortal.getBoundingBox().getZsize();
373+
double buffer = entityBB.getZsize() / 2 + Shapes.EPSILON;
374+
// Don't collide in wall next to top/bottom of floor/ceiling portal
375+
offset = Mth.clamp(offset, buffer, linkedPortal.getBoundingBox().getZsize() - buffer);
376+
teleportTo = new Vec3(linkedPortal.getX(), linkedPortal.getY(), linkedPortal.getBoundingBox().minZ + offset);
362377
}
363378
} else {
364-
teleportTo = new Vec3(linkedPortal.getX(), linkedPortal.getBoundingBox().minY + entityHeightFraction * linkedPortal.getBoundingBox().getYsize(), linkedPortal.getZ()).relative(linkedPortal.getDirection(), 0.5f);
379+
teleportTo = new Vec3(linkedPortal.getX(), linkedPortal.getBoundingBox().minY + entityHeightFraction * linkedPortal.getBoundingBox().getYsize(), linkedPortal.getZ());
380+
}
381+
382+
// Move to 'side' of portal (except for portals facing up, where it would give us extra velocity)
383+
if (linkedPortal.getDirection() == Direction.DOWN) {
384+
teleportTo = teleportTo.relative(Direction.DOWN, entityBB.getYsize());
385+
} else if (linkedPortal.getDirection() != Direction.UP) {
386+
if (linkedPortal.getDirection().getAxis() == Direction.Axis.X) {
387+
teleportTo = teleportTo.relative(linkedPortal.getDirection(), linkedPortal.getBoundingBox().getXsize() / 2 + entityBB.getXsize() / 2);
388+
} else if (linkedPortal.getDirection().getAxis() == Direction.Axis.Z) {
389+
teleportTo = teleportTo.relative(linkedPortal.getDirection(), linkedPortal.getBoundingBox().getZsize() / 2 + entityBB.getZsize() / 2);
390+
}
365391
}
366392

367-
if (linkedPortal.getDirection() == Direction.DOWN)
368-
teleportTo = teleportTo.relative(Direction.DOWN, 1f);
393+
// Don't immediately collide again
394+
teleportTo = teleportTo.relative(linkedPortal.getDirection(), Shapes.EPSILON);
395+
369396
return teleportTo;
370397
}
371398

@@ -399,14 +426,13 @@ public void teleport(Entity entity) {
399426
if (getLinkedPortal() != null) {
400427
Vec3 teleportTo = getTeleportTo(entity, linkedPortal);
401428
// Adjust the entity's rotation to match the exit portal's direction
402-
float newYaw = getYawFromDirection(linkedPortal.getDirection());
403-
float newPitch = entity.getXRot(); // Maintain the same pitch
429+
Vec2 newLookAngle = transformLookAngle(entity, linkedPortal);
404430
entity.resetFallDistance();
405431

406432
Vec3 newMotion = calculateVelocity(entity);
407433

408434
// Teleport the entity to the new location and set its rotation
409-
boolean success = entity.teleportTo((ServerLevel) linkedPortal.level(), teleportTo.x(), teleportTo.y(), teleportTo.z(), new HashSet<>(), newYaw, newPitch);
435+
boolean success = entity.teleportTo((ServerLevel) linkedPortal.level(), teleportTo.x(), teleportTo.y(), teleportTo.z(), new HashSet<>(), newLookAngle.y, newLookAngle.x);
410436

411437
if (success) {
412438
entity.resetFallDistance();
@@ -462,14 +488,25 @@ public static Vec3 transformMotion(Vec3 motion, Direction from, Direction to) {
462488
return new Vec3(motionVec.x(), motionVec.y(), motionVec.z());
463489
}
464490

465-
// Helper method to get the yaw from a direction
466-
private float getYawFromDirection(Direction direction) {
467-
return switch (direction) {
468-
case NORTH -> 180.0F;
469-
case SOUTH -> 0.0F;
470-
case WEST -> 90.0F;
471-
case EAST -> -90.0F;
472-
default -> 0.0F;
473-
};
491+
/**
492+
* Adjust the entity's look angle (pitch and yaw) for seamless transitions between portals.
493+
*
494+
* @param entity entity
495+
* @param destination destination portal
496+
* @return adjusted (xrot, yrot)
497+
*/
498+
private Vec2 transformLookAngle(Entity entity, PortalEntity destination) {
499+
final Vec3 newLook = transformMotion(entity.getLookAngle(), getDirection(), destination.getDirection().getOpposite());
500+
return toDegrees(newLook);
501+
}
502+
503+
private static Vec2 toDegrees(Vec3 vector) {
504+
double x = vector.x;
505+
double y = vector.y;
506+
double z = vector.z;
507+
double hyp = Math.sqrt(x * x + z * z);
508+
float xrot = Mth.wrapDegrees((float)(-(Mth.atan2(y, hyp) * 180.0F / (float)Math.PI)));
509+
float yrot = Mth.wrapDegrees((float)(Mth.atan2(z, x) * 180.0F / (float)Math.PI) - 90.0F);
510+
return new Vec2(xrot, yrot);
474511
}
475512
}

0 commit comments

Comments
 (0)