283 lines
13 KiB
Java
283 lines
13 KiB
Java
|
package net.minecraft.world.level.block;
|
||
|
|
||
|
import com.mojang.logging.LogUtils;
|
||
|
import com.mojang.serialization.MapCodec;
|
||
|
import java.util.Map;
|
||
|
import java.util.Optional;
|
||
|
import javax.annotation.Nullable;
|
||
|
import net.minecraft.BlockUtil;
|
||
|
import net.minecraft.core.BlockPos;
|
||
|
import net.minecraft.core.Direction;
|
||
|
import net.minecraft.core.particles.ParticleTypes;
|
||
|
import net.minecraft.resources.ResourceKey;
|
||
|
import net.minecraft.server.level.ServerLevel;
|
||
|
import net.minecraft.sounds.SoundEvents;
|
||
|
import net.minecraft.sounds.SoundSource;
|
||
|
import net.minecraft.util.RandomSource;
|
||
|
import net.minecraft.world.entity.Entity;
|
||
|
import net.minecraft.world.entity.EntityDimensions;
|
||
|
import net.minecraft.world.entity.EntitySpawnReason;
|
||
|
import net.minecraft.world.entity.EntityType;
|
||
|
import net.minecraft.world.entity.InsideBlockEffectApplier;
|
||
|
import net.minecraft.world.entity.Relative;
|
||
|
import net.minecraft.world.entity.player.Player;
|
||
|
import net.minecraft.world.item.ItemStack;
|
||
|
import net.minecraft.world.level.BlockGetter;
|
||
|
import net.minecraft.world.level.GameRules;
|
||
|
import net.minecraft.world.level.Level;
|
||
|
import net.minecraft.world.level.LevelReader;
|
||
|
import net.minecraft.world.level.ScheduledTickAccess;
|
||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||
|
import net.minecraft.world.level.block.state.BlockState;
|
||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||
|
import net.minecraft.world.level.border.WorldBorder;
|
||
|
import net.minecraft.world.level.dimension.DimensionType;
|
||
|
import net.minecraft.world.level.portal.PortalShape;
|
||
|
import net.minecraft.world.level.portal.TeleportTransition;
|
||
|
import net.minecraft.world.phys.Vec3;
|
||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||
|
import org.slf4j.Logger;
|
||
|
|
||
|
public class NetherPortalBlock extends Block implements Portal {
|
||
|
private static final Logger LOGGER = LogUtils.getLogger();
|
||
|
public static final MapCodec<NetherPortalBlock> CODEC = simpleCodec(NetherPortalBlock::new);
|
||
|
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;
|
||
|
private static final Map<Direction.Axis, VoxelShape> SHAPES = Shapes.rotateHorizontalAxis(Block.column(4.0, 16.0, 0.0, 16.0));
|
||
|
|
||
|
@Override
|
||
|
public MapCodec<NetherPortalBlock> codec() {
|
||
|
return CODEC;
|
||
|
}
|
||
|
|
||
|
public NetherPortalBlock(BlockBehaviour.Properties p_54909_) {
|
||
|
super(p_54909_);
|
||
|
this.registerDefaultState(this.stateDefinition.any().setValue(AXIS, Direction.Axis.X));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected VoxelShape getShape(BlockState p_54942_, BlockGetter p_54943_, BlockPos p_54944_, CollisionContext p_54945_) {
|
||
|
return SHAPES.get(p_54942_.getValue(AXIS));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected VoxelShape getEntityInsideCollisionShape(BlockState p_396779_, BlockGetter p_395715_, BlockPos p_396107_, Entity p_395206_) {
|
||
|
return p_396779_.getShape(p_395715_, p_396107_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void randomTick(BlockState p_221799_, ServerLevel p_221800_, BlockPos p_221801_, RandomSource p_221802_) {
|
||
|
if (p_221800_.dimensionType().natural()
|
||
|
&& p_221800_.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)
|
||
|
&& p_221802_.nextInt(2000) < p_221800_.getDifficulty().getId()
|
||
|
&& p_221800_.anyPlayerCloseEnoughForSpawning(p_221801_)) {
|
||
|
while (p_221800_.getBlockState(p_221801_).is(this)) {
|
||
|
p_221801_ = p_221801_.below();
|
||
|
}
|
||
|
|
||
|
if (p_221800_.getBlockState(p_221801_).isValidSpawn(p_221800_, p_221801_, EntityType.ZOMBIFIED_PIGLIN)) {
|
||
|
Entity entity = EntityType.ZOMBIFIED_PIGLIN.spawn(p_221800_, p_221801_.above(), EntitySpawnReason.STRUCTURE);
|
||
|
if (entity != null) {
|
||
|
entity.setPortalCooldown();
|
||
|
Entity entity1 = entity.getVehicle();
|
||
|
if (entity1 != null) {
|
||
|
entity1.setPortalCooldown();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected BlockState updateShape(
|
||
|
BlockState p_54928_,
|
||
|
LevelReader p_367647_,
|
||
|
ScheduledTickAccess p_363604_,
|
||
|
BlockPos p_54932_,
|
||
|
Direction p_54929_,
|
||
|
BlockPos p_54933_,
|
||
|
BlockState p_54930_,
|
||
|
RandomSource p_368816_
|
||
|
) {
|
||
|
Direction.Axis direction$axis = p_54929_.getAxis();
|
||
|
Direction.Axis direction$axis1 = p_54928_.getValue(AXIS);
|
||
|
boolean flag = direction$axis1 != direction$axis && direction$axis.isHorizontal();
|
||
|
return !flag && !p_54930_.is(this) && !PortalShape.findAnyShape(p_367647_, p_54932_, direction$axis1).isComplete()
|
||
|
? Blocks.AIR.defaultBlockState()
|
||
|
: super.updateShape(p_54928_, p_367647_, p_363604_, p_54932_, p_54929_, p_54933_, p_54930_, p_368816_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void entityInside(BlockState p_54915_, Level p_54916_, BlockPos p_54917_, Entity p_54918_, InsideBlockEffectApplier p_392916_) {
|
||
|
if (p_54918_.canUsePortal(false)) {
|
||
|
p_54918_.setAsInsidePortal(this, p_54917_);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int getPortalTransitionTime(ServerLevel p_342064_, Entity p_344634_) {
|
||
|
return p_344634_ instanceof Player player
|
||
|
? Math.max(0, p_342064_.getGameRules().getInt(player.getAbilities().invulnerable ? GameRules.RULE_PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.RULE_PLAYERS_NETHER_PORTAL_DEFAULT_DELAY))
|
||
|
: 0;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public TeleportTransition getPortalDestination(ServerLevel p_342106_, Entity p_343065_, BlockPos p_344977_) {
|
||
|
ResourceKey<Level> resourcekey = p_342106_.dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER;
|
||
|
ServerLevel serverlevel = p_342106_.getServer().getLevel(resourcekey);
|
||
|
if (serverlevel == null) {
|
||
|
return null;
|
||
|
} else {
|
||
|
boolean flag = serverlevel.dimension() == Level.NETHER;
|
||
|
WorldBorder worldborder = serverlevel.getWorldBorder();
|
||
|
double d0 = DimensionType.getTeleportationScale(p_342106_.dimensionType(), serverlevel.dimensionType());
|
||
|
BlockPos blockpos = worldborder.clampToBounds(p_343065_.getX() * d0, p_343065_.getY(), p_343065_.getZ() * d0);
|
||
|
return this.getExitPortal(serverlevel, p_343065_, p_344977_, blockpos, flag, worldborder);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
private TeleportTransition getExitPortal(
|
||
|
ServerLevel p_343269_, Entity p_343673_, BlockPos p_343381_, BlockPos p_343194_, boolean p_343644_, WorldBorder p_343185_
|
||
|
) {
|
||
|
Optional<BlockPos> optional = p_343269_.getPortalForcer().findClosestPortalPosition(p_343194_, p_343644_, p_343185_);
|
||
|
BlockUtil.FoundRectangle blockutil$foundrectangle;
|
||
|
TeleportTransition.PostTeleportTransition teleporttransition$postteleporttransition;
|
||
|
if (optional.isPresent()) {
|
||
|
BlockPos blockpos = optional.get();
|
||
|
BlockState blockstate = p_343269_.getBlockState(blockpos);
|
||
|
blockutil$foundrectangle = BlockUtil.getLargestRectangleAround(
|
||
|
blockpos, blockstate.getValue(BlockStateProperties.HORIZONTAL_AXIS), 21, Direction.Axis.Y, 21, p_343533_ -> p_343269_.getBlockState(p_343533_) == blockstate
|
||
|
);
|
||
|
teleporttransition$postteleporttransition = TeleportTransition.PLAY_PORTAL_SOUND.then(p_343530_ -> p_343530_.placePortalTicket(blockpos));
|
||
|
} else {
|
||
|
Direction.Axis direction$axis = p_343673_.level().getBlockState(p_343381_).getOptionalValue(AXIS).orElse(Direction.Axis.X);
|
||
|
Optional<BlockUtil.FoundRectangle> optional1 = p_343269_.getPortalForcer().createPortal(p_343194_, direction$axis);
|
||
|
if (optional1.isEmpty()) {
|
||
|
LOGGER.error("Unable to create a portal, likely target out of worldborder");
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
blockutil$foundrectangle = optional1.get();
|
||
|
teleporttransition$postteleporttransition = TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET);
|
||
|
}
|
||
|
|
||
|
return getDimensionTransitionFromExit(p_343673_, p_343381_, blockutil$foundrectangle, p_343269_, teleporttransition$postteleporttransition);
|
||
|
}
|
||
|
|
||
|
private static TeleportTransition getDimensionTransitionFromExit(
|
||
|
Entity p_344252_, BlockPos p_343376_, BlockUtil.FoundRectangle p_343595_, ServerLevel p_343963_, TeleportTransition.PostTeleportTransition p_368919_
|
||
|
) {
|
||
|
BlockState blockstate = p_344252_.level().getBlockState(p_343376_);
|
||
|
Direction.Axis direction$axis;
|
||
|
Vec3 vec3;
|
||
|
if (blockstate.hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) {
|
||
|
direction$axis = blockstate.getValue(BlockStateProperties.HORIZONTAL_AXIS);
|
||
|
BlockUtil.FoundRectangle blockutil$foundrectangle = BlockUtil.getLargestRectangleAround(
|
||
|
p_343376_, direction$axis, 21, Direction.Axis.Y, 21, p_342174_ -> p_344252_.level().getBlockState(p_342174_) == blockstate
|
||
|
);
|
||
|
vec3 = p_344252_.getRelativePortalPosition(direction$axis, blockutil$foundrectangle);
|
||
|
} else {
|
||
|
direction$axis = Direction.Axis.X;
|
||
|
vec3 = new Vec3(0.5, 0.0, 0.0);
|
||
|
}
|
||
|
|
||
|
return createDimensionTransition(p_343963_, p_343595_, direction$axis, vec3, p_344252_, p_368919_);
|
||
|
}
|
||
|
|
||
|
private static TeleportTransition createDimensionTransition(
|
||
|
ServerLevel p_344368_,
|
||
|
BlockUtil.FoundRectangle p_345089_,
|
||
|
Direction.Axis p_345454_,
|
||
|
Vec3 p_344397_,
|
||
|
Entity p_344167_,
|
||
|
TeleportTransition.PostTeleportTransition p_361307_
|
||
|
) {
|
||
|
BlockPos blockpos = p_345089_.minCorner;
|
||
|
BlockState blockstate = p_344368_.getBlockState(blockpos);
|
||
|
Direction.Axis direction$axis = blockstate.getOptionalValue(BlockStateProperties.HORIZONTAL_AXIS).orElse(Direction.Axis.X);
|
||
|
double d0 = p_345089_.axis1Size;
|
||
|
double d1 = p_345089_.axis2Size;
|
||
|
EntityDimensions entitydimensions = p_344167_.getDimensions(p_344167_.getPose());
|
||
|
int i = p_345454_ == direction$axis ? 0 : 90;
|
||
|
double d2 = entitydimensions.width() / 2.0 + (d0 - entitydimensions.width()) * p_344397_.x();
|
||
|
double d3 = (d1 - entitydimensions.height()) * p_344397_.y();
|
||
|
double d4 = 0.5 + p_344397_.z();
|
||
|
boolean flag = direction$axis == Direction.Axis.X;
|
||
|
Vec3 vec3 = new Vec3(blockpos.getX() + (flag ? d2 : d4), blockpos.getY() + d3, blockpos.getZ() + (flag ? d4 : d2));
|
||
|
Vec3 vec31 = PortalShape.findCollisionFreePosition(vec3, p_344368_, p_344167_, entitydimensions);
|
||
|
return new TeleportTransition(p_344368_, vec31, Vec3.ZERO, i, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), p_361307_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Portal.Transition getLocalTransition() {
|
||
|
return Portal.Transition.CONFUSION;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void animateTick(BlockState p_221794_, Level p_221795_, BlockPos p_221796_, RandomSource p_221797_) {
|
||
|
if (p_221797_.nextInt(100) == 0) {
|
||
|
p_221795_.playLocalSound(
|
||
|
p_221796_.getX() + 0.5,
|
||
|
p_221796_.getY() + 0.5,
|
||
|
p_221796_.getZ() + 0.5,
|
||
|
SoundEvents.PORTAL_AMBIENT,
|
||
|
SoundSource.BLOCKS,
|
||
|
0.5F,
|
||
|
p_221797_.nextFloat() * 0.4F + 0.8F,
|
||
|
false
|
||
|
);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < 4; i++) {
|
||
|
double d0 = p_221796_.getX() + p_221797_.nextDouble();
|
||
|
double d1 = p_221796_.getY() + p_221797_.nextDouble();
|
||
|
double d2 = p_221796_.getZ() + p_221797_.nextDouble();
|
||
|
double d3 = (p_221797_.nextFloat() - 0.5) * 0.5;
|
||
|
double d4 = (p_221797_.nextFloat() - 0.5) * 0.5;
|
||
|
double d5 = (p_221797_.nextFloat() - 0.5) * 0.5;
|
||
|
int j = p_221797_.nextInt(2) * 2 - 1;
|
||
|
if (!p_221795_.getBlockState(p_221796_.west()).is(this) && !p_221795_.getBlockState(p_221796_.east()).is(this)) {
|
||
|
d0 = p_221796_.getX() + 0.5 + 0.25 * j;
|
||
|
d3 = p_221797_.nextFloat() * 2.0F * j;
|
||
|
} else {
|
||
|
d2 = p_221796_.getZ() + 0.5 + 0.25 * j;
|
||
|
d5 = p_221797_.nextFloat() * 2.0F * j;
|
||
|
}
|
||
|
|
||
|
p_221795_.addParticle(ParticleTypes.PORTAL, d0, d1, d2, d3, d4, d5);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected ItemStack getCloneItemStack(LevelReader p_310044_, BlockPos p_54912_, BlockState p_54913_, boolean p_376456_) {
|
||
|
return ItemStack.EMPTY;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected BlockState rotate(BlockState p_54925_, Rotation p_54926_) {
|
||
|
switch (p_54926_) {
|
||
|
case COUNTERCLOCKWISE_90:
|
||
|
case CLOCKWISE_90:
|
||
|
switch ((Direction.Axis)p_54925_.getValue(AXIS)) {
|
||
|
case X:
|
||
|
return p_54925_.setValue(AXIS, Direction.Axis.Z);
|
||
|
case Z:
|
||
|
return p_54925_.setValue(AXIS, Direction.Axis.X);
|
||
|
default:
|
||
|
return p_54925_;
|
||
|
}
|
||
|
default:
|
||
|
return p_54925_;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> p_54935_) {
|
||
|
p_54935_.add(AXIS);
|
||
|
}
|
||
|
}
|