[Change] Improved Eternal Portal Code
This commit is contained in:
parent
7f8d8bef0c
commit
9f4999c966
10 changed files with 775 additions and 531 deletions
|
@ -3,25 +3,15 @@ package org.betterx.betterend.blocks;
|
|||
import org.betterx.bclib.client.render.BCLRenderLayer;
|
||||
import org.betterx.bclib.interfaces.CustomColorProvider;
|
||||
import org.betterx.bclib.interfaces.RenderLayerProvider;
|
||||
import org.betterx.betterend.advancements.BECriteria;
|
||||
import org.betterx.betterend.interfaces.TeleportingEntity;
|
||||
import org.betterx.betterend.portal.TravelingEntity;
|
||||
import org.betterx.betterend.registry.EndParticles;
|
||||
import org.betterx.betterend.registry.EndPortals;
|
||||
import org.betterx.betterend.rituals.EternalRitual;
|
||||
|
||||
import net.minecraft.client.color.block.BlockColor;
|
||||
import net.minecraft.client.color.item.ItemColor;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Direction.Axis;
|
||||
import net.minecraft.core.Direction.AxisDirection;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
|
@ -31,19 +21,14 @@ import net.minecraft.world.level.LevelAccessor;
|
|||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.NetherPortalBlock;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class EndPortalBlock extends NetherPortalBlock implements RenderLayerProvider, CustomColorProvider {
|
||||
public static final IntegerProperty PORTAL = EndBlockProperties.PORTAL;
|
||||
|
||||
|
@ -104,39 +89,16 @@ public class EndPortalBlock extends NetherPortalBlock implements RenderLayerProv
|
|||
return state;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
|
||||
if (world.isClientSide || !validate(entity)) return;
|
||||
entity.setPortalCooldown();
|
||||
ServerLevel currentWorld = (ServerLevel) world;
|
||||
MinecraftServer server = currentWorld.getServer();
|
||||
ServerLevel targetWorld = EndPortals.getWorld(server, state.getValue(PORTAL));
|
||||
boolean isInEnd = currentWorld.dimension().equals(Level.END);
|
||||
ServerLevel destination = isInEnd ? targetWorld : server.getLevel(Level.END);
|
||||
BlockPos exitPos = findExitPos(currentWorld, destination, pos, entity);
|
||||
if (exitPos == null) return;
|
||||
if (entity instanceof ServerPlayer sp && sp.isCreative()) {
|
||||
((ServerPlayer) entity).teleportTo(
|
||||
destination,
|
||||
exitPos.getX() + 0.5,
|
||||
exitPos.getY(),
|
||||
exitPos.getZ() + 0.5,
|
||||
entity.getYRot(),
|
||||
entity.getXRot()
|
||||
);
|
||||
BECriteria.PORTAL_TRAVEL.trigger(sp);
|
||||
} else {
|
||||
if (entity instanceof ServerPlayer sp) {
|
||||
BECriteria.PORTAL_TRAVEL.trigger(sp);
|
||||
}
|
||||
((TeleportingEntity) entity).be_setExitPos(exitPos);
|
||||
Optional<Entity> teleported = Optional.ofNullable(entity.changeDimension(destination));
|
||||
teleported.ifPresent(Entity::setPortalCooldown);
|
||||
if (validate(entity) && entity instanceof TravelingEntity te && te.be_getTravelerState() != null) {
|
||||
te.be_getTravelerState().handleInsidePortal(pos);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validate(Entity entity) {
|
||||
return !entity.isPassenger() && !entity.isVehicle() && entity.canChangeDimensions() && !entity.isOnPortalCooldown();
|
||||
return !entity.isPassenger() && !entity.isVehicle() && entity.canChangeDimensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -144,79 +106,6 @@ public class EndPortalBlock extends NetherPortalBlock implements RenderLayerProv
|
|||
return BCLRenderLayer.TRANSLUCENT;
|
||||
}
|
||||
|
||||
private BlockPos findExitPos(
|
||||
ServerLevel currentWorld,
|
||||
ServerLevel targetWorld,
|
||||
BlockPos currentPos,
|
||||
Entity entity
|
||||
) {
|
||||
if (targetWorld == null) return null;
|
||||
Registry<DimensionType> registry = targetWorld.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||
ResourceLocation targetWorldId = targetWorld.dimension().location();
|
||||
ResourceLocation currentWorldId = currentWorld.dimension().location();
|
||||
double targetMultiplier = Objects.requireNonNull(registry.get(targetWorldId)).coordinateScale();
|
||||
double currentMultiplier = Objects.requireNonNull(registry.get(currentWorldId)).coordinateScale();
|
||||
double multiplier = targetMultiplier > currentMultiplier ? 1.0 / targetMultiplier : currentMultiplier;
|
||||
MutableBlockPos basePos = currentPos.mutable()
|
||||
.set(
|
||||
currentPos.getX() * multiplier,
|
||||
currentPos.getY(),
|
||||
currentPos.getZ() * multiplier
|
||||
);
|
||||
MutableBlockPos checkPos = basePos.mutable();
|
||||
BlockState currentState = currentWorld.getBlockState(currentPos);
|
||||
int radius = (EternalRitual.SEARCH_RADIUS >> 4) + 1;
|
||||
checkPos = EternalRitual.findBlockPos(
|
||||
targetWorld,
|
||||
checkPos,
|
||||
radius,
|
||||
this,
|
||||
state -> state.is(this) && state.getValue(PORTAL).equals(currentState.getValue(PORTAL))
|
||||
);
|
||||
if (checkPos != null) {
|
||||
BlockState checkState = targetWorld.getBlockState(checkPos);
|
||||
Axis axis = checkState.getValue(AXIS);
|
||||
checkPos = findCenter(targetWorld, checkPos, axis);
|
||||
Direction frontDir = Direction.fromAxisAndDirection(axis, AxisDirection.POSITIVE).getClockWise();
|
||||
Direction entityDir = entity.getMotionDirection();
|
||||
if (entityDir.getAxis().isVertical()) {
|
||||
entityDir = frontDir;
|
||||
}
|
||||
if (frontDir != entityDir && frontDir.getOpposite() != entityDir) {
|
||||
entity.rotate(Rotation.CLOCKWISE_90);
|
||||
entityDir = entityDir.getClockWise();
|
||||
}
|
||||
return checkPos.relative(entityDir);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MutableBlockPos findCenter(Level world, MutableBlockPos pos, Direction.Axis axis) {
|
||||
return findCenter(world, pos, axis, 1);
|
||||
}
|
||||
|
||||
private MutableBlockPos findCenter(Level world, MutableBlockPos pos, Direction.Axis axis, int step) {
|
||||
if (step > 8) return pos;
|
||||
BlockState right, left;
|
||||
Direction rightDir, leftDir;
|
||||
rightDir = Direction.fromAxisAndDirection(axis, AxisDirection.POSITIVE);
|
||||
leftDir = rightDir.getOpposite();
|
||||
right = world.getBlockState(pos.relative(rightDir));
|
||||
left = world.getBlockState(pos.relative(leftDir));
|
||||
BlockState down = world.getBlockState(pos.below());
|
||||
if (down.is(this)) {
|
||||
return findCenter(world, pos.move(Direction.DOWN), axis, step);
|
||||
} else if (right.is(this) && left.is(this)) {
|
||||
return pos;
|
||||
} else if (right.is(this)) {
|
||||
return findCenter(world, pos.move(rightDir), axis, ++step);
|
||||
} else if (left.is(this)) {
|
||||
return findCenter(world, pos.move(leftDir), axis, ++step);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockColor getProvider() {
|
||||
return (state, world, pos, tintIndex) -> EndPortals.getColor(state.getValue(PORTAL));
|
||||
|
|
|
@ -38,11 +38,11 @@ public class EternalPedestal extends PedestalBlock {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void checkRitual(Level world, Player player, BlockPos pos) {
|
||||
BlockEntity blockEntity = world.getBlockEntity(pos);
|
||||
public void checkRitual(Level sourceLevel, Player player, BlockPos pos) {
|
||||
BlockEntity blockEntity = sourceLevel.getBlockEntity(pos);
|
||||
if (blockEntity instanceof EternalPedestalEntity) {
|
||||
EternalPedestalEntity pedestal = (EternalPedestalEntity) blockEntity;
|
||||
BlockState updatedState = world.getBlockState(pos);
|
||||
BlockState updatedState = sourceLevel.getBlockState(pos);
|
||||
if (pedestal.isEmpty()) {
|
||||
if (pedestal.hasRitual()) {
|
||||
EternalRitual ritual = pedestal.getRitual();
|
||||
|
@ -57,16 +57,19 @@ public class EternalPedestal extends PedestalBlock {
|
|||
ritual.disablePortal(portalId);
|
||||
}
|
||||
}
|
||||
world.setBlockAndUpdate(pos, updatedState.setValue(ACTIVATED, false).setValue(HAS_LIGHT, false));
|
||||
sourceLevel.setBlockAndUpdate(pos, updatedState.setValue(ACTIVATED, false).setValue(HAS_LIGHT, false));
|
||||
} else {
|
||||
ItemStack itemStack = pedestal.getItem(0);
|
||||
ResourceLocation id = BuiltInRegistries.ITEM.getKey(itemStack.getItem());
|
||||
if (EndPortals.isAvailableItem(id)) {
|
||||
world.setBlockAndUpdate(pos, updatedState.setValue(ACTIVATED, true).setValue(HAS_LIGHT, true));
|
||||
sourceLevel.setBlockAndUpdate(
|
||||
pos,
|
||||
updatedState.setValue(ACTIVATED, true).setValue(HAS_LIGHT, true)
|
||||
);
|
||||
if (pedestal.hasRitual()) {
|
||||
pedestal.getRitual().checkStructure(player);
|
||||
} else {
|
||||
EternalRitual ritual = new EternalRitual(world, pos);
|
||||
EternalRitual ritual = new EternalRitual(sourceLevel, pos);
|
||||
ritual.checkStructure(player);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,11 @@ public class CommandRegistry {
|
|||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
.then(Commands.literal("locate_portal")
|
||||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
.executes(ctx -> find_poi(ctx, EndPoiTypes.ETERNAL_PORTAL_INACTIVE))
|
||||
.executes(ctx -> find_poi(ctx, EndPoiTypes.ETERNAL_PORTAL))
|
||||
)
|
||||
.then(Commands.literal("locate_portal_frame")
|
||||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
.executes(ctx -> find_poi(ctx, EndPoiTypes.ETERNAL_PORTAL_FRAME))
|
||||
)
|
||||
.then(Commands.literal("tpnext")
|
||||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.betterx.betterend.mixin.common.portal;
|
||||
|
||||
import org.betterx.betterend.portal.TravelerState;
|
||||
import org.betterx.betterend.portal.TravelingEntity;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.portal.PortalInfo;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public class EntityMixin implements TravelingEntity {
|
||||
|
||||
private final TravelerState be_travelerState = TravelerState.init((net.minecraft.world.entity.Entity) (Object) this);
|
||||
|
||||
public TravelerState be_getTravelerState() {
|
||||
return be_travelerState;
|
||||
}
|
||||
|
||||
@Inject(method = "handleNetherPortal", at = @At("HEAD"))
|
||||
void be_handleNetherPortal(CallbackInfo ci) {
|
||||
if (be_travelerState != null) be_travelerState.portalTick();
|
||||
}
|
||||
|
||||
@Inject(method = "findDimensionEntryPoint", at = @At("HEAD"), cancellable = true)
|
||||
void be_findDimensionEntryPoint(ServerLevel serverLevel, CallbackInfoReturnable<PortalInfo> cir) {
|
||||
// if (be_travelerState != null) {
|
||||
// PortalInfo pi = be_travelerState.findDimensionEntryPoint(serverLevel);
|
||||
// if (pi != null) cir.setReturnValue(pi);
|
||||
// }
|
||||
}
|
||||
}
|
380
src/main/java/org/betterx/betterend/portal/PortalBuilder.java
Normal file
380
src/main/java/org/betterx/betterend/portal/PortalBuilder.java
Normal file
|
@ -0,0 +1,380 @@
|
|||
package org.betterx.betterend.portal;
|
||||
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
import org.betterx.betterend.blocks.EndPortalBlock;
|
||||
import org.betterx.betterend.registry.EndBlocks;
|
||||
import org.betterx.betterend.registry.EndFeatures;
|
||||
import org.betterx.betterend.registry.EndPoiTypes;
|
||||
import org.betterx.betterend.rituals.EternalRitual;
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import net.minecraft.BlockUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class PortalBuilder {
|
||||
public final static Set<Point> FRAME_POSITIONS = Sets.newHashSet(
|
||||
new Point(0, 0),
|
||||
new Point(0, 6),
|
||||
new Point(1, 0),
|
||||
new Point(1, 6),
|
||||
new Point(2, 1),
|
||||
new Point(2, 5),
|
||||
new Point(3, 2),
|
||||
new Point(3, 3),
|
||||
new Point(3, 4)
|
||||
);
|
||||
public final static Set<Point> PORTAL_POSITIONS = Sets.newHashSet(
|
||||
new Point(0, 0),
|
||||
new Point(0, 1),
|
||||
new Point(0, 2),
|
||||
new Point(0, 3),
|
||||
new Point(0, 4),
|
||||
new Point(1, 0),
|
||||
new Point(1, 1),
|
||||
new Point(1, 2),
|
||||
new Point(1, 3),
|
||||
new Point(1, 4),
|
||||
new Point(2, 1),
|
||||
new Point(2, 2),
|
||||
new Point(2, 3)
|
||||
);
|
||||
private final static Set<Point> BASE_POSITIONS = Sets.newHashSet(
|
||||
new Point(3, 0),
|
||||
new Point(2, 0),
|
||||
new Point(2, 1),
|
||||
new Point(1, 1),
|
||||
new Point(1, 2),
|
||||
new Point(0, 1),
|
||||
new Point(0, 2)
|
||||
);
|
||||
private final static Block BASE = EndBlocks.FLAVOLITE.tiles;
|
||||
public final static Block FRAME = EndBlocks.FLAVOLITE_RUNED_ETERNAL;
|
||||
public final static Block PORTAL = EndBlocks.END_PORTAL_BLOCK;
|
||||
public static int SPIRAL_SEARCH_RADIUS = 128;
|
||||
private final ServerLevel targetLevel;
|
||||
private final Level sourceLevel;
|
||||
|
||||
public PortalBuilder(Level sourceLevel, ServerLevel targetLevel) {
|
||||
this.targetLevel = targetLevel;
|
||||
this.sourceLevel = sourceLevel;
|
||||
}
|
||||
|
||||
public static void generatePortal(Level world, BlockPos center, Direction.Axis axis, int portalId) {
|
||||
BlockPos framePos = center.below();
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.EAST : Direction.NORTH;
|
||||
BlockState frame = FRAME.defaultBlockState().setValue(EternalRitual.ACTIVE, true);
|
||||
FRAME_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = framePos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, frame);
|
||||
pos = framePos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, frame);
|
||||
});
|
||||
BlockState portal = PORTAL.defaultBlockState()
|
||||
.setValue(EndPortalBlock.AXIS, axis)
|
||||
.setValue(EndPortalBlock.PORTAL, portalId);
|
||||
PORTAL_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, portal);
|
||||
pos = center.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, portal);
|
||||
});
|
||||
generateBase(world, framePos, moveDir);
|
||||
}
|
||||
|
||||
private static void generateBase(Level world, BlockPos center, Direction moveX) {
|
||||
BlockState base = BASE.defaultBlockState();
|
||||
Direction moveY = moveX.getClockWise();
|
||||
BASE_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = center.mutable().move(moveX, point.x).move(moveY, point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
pos = center.mutable().move(moveX, -point.x).move(moveY, point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
pos = center.mutable().move(moveX, point.x).move(moveY, -point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
pos = center.mutable().move(moveX, -point.x).move(moveY, -point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean checkIsAreaValid(Level world, BlockPos pos, Direction.Axis axis) {
|
||||
if (pos.getY() >= world.getHeight() - 1) return false;
|
||||
if (!isBaseValid(world, pos, axis)) return false;
|
||||
return checkArea(world, pos, axis);
|
||||
}
|
||||
|
||||
private static boolean isBaseValid(Level world, BlockPos pos, Direction.Axis axis) {
|
||||
boolean solid = true;
|
||||
if (axis.equals(Direction.Axis.X)) {
|
||||
pos = pos.below().offset(0, 0, -3);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
BlockPos checkPos = pos.offset(0, 0, i);
|
||||
BlockState state = world.getBlockState(checkPos);
|
||||
solid &= validBlock(world, checkPos, state);
|
||||
}
|
||||
} else {
|
||||
pos = pos.below().offset(-3, 0, 0);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
BlockPos checkPos = pos.offset(i, 0, 0);
|
||||
BlockState state = world.getBlockState(checkPos);
|
||||
solid &= validBlock(world, checkPos, state);
|
||||
}
|
||||
}
|
||||
return solid;
|
||||
}
|
||||
|
||||
private static boolean validBlock(Level world, BlockPos pos, BlockState state) {
|
||||
return state.isRedstoneConductor(world, pos) && state.isCollisionShapeFullBlock(world, pos);
|
||||
}
|
||||
|
||||
public static boolean checkArea(Level world, BlockPos center, Direction.Axis axis) {
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
|
||||
for (BlockPos checkPos : BlockPos.betweenClosed(
|
||||
center.relative(moveDir.getClockWise()),
|
||||
center.relative(moveDir.getCounterClockWise())
|
||||
)) {
|
||||
for (Point point : PORTAL_POSITIONS) {
|
||||
BlockPos pos = checkPos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
if (isStateInvalid(state)) return false;
|
||||
pos = checkPos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
state = world.getBlockState(pos);
|
||||
if (isStateInvalid(state)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isStateInvalid(BlockState state) {
|
||||
if (!state.getFluidState().isEmpty()) return true;
|
||||
return !BlocksHelper.replaceableOrPlant(state);
|
||||
}
|
||||
|
||||
public Optional<BlockUtil.FoundRectangle> findPortalAround(BlockPos blockPos, WorldBorder worldBorder) {
|
||||
PoiManager poiManager = this.targetLevel.getPoiManager();
|
||||
poiManager.ensureLoadedAndValid(this.targetLevel, blockPos, SPIRAL_SEARCH_RADIUS);
|
||||
|
||||
Optional<BlockPos> oPos = EndPoiTypes.ETERNAL_PORTAL.findPoiAround(
|
||||
this.targetLevel,
|
||||
blockPos,
|
||||
SPIRAL_SEARCH_RADIUS,
|
||||
worldBorder
|
||||
);
|
||||
|
||||
return oPos.map(poiPos -> {
|
||||
this.targetLevel.getChunkSource().addRegionTicket(TicketType.PORTAL, new ChunkPos(poiPos), 3, poiPos);
|
||||
BlockState blockState = this.targetLevel.getBlockState(poiPos);
|
||||
return BlockUtil.getLargestRectangleAround(
|
||||
poiPos,
|
||||
blockState.getValue(BlockStateProperties.HORIZONTAL_AXIS),
|
||||
21,
|
||||
Direction.Axis.Y,
|
||||
21,
|
||||
bp -> this.targetLevel.getBlockState(bp) == blockState
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public static BlockPos getStartingPos(
|
||||
Level sourceLevel,
|
||||
Level targetLevel,
|
||||
Entity entity,
|
||||
WorldBorder worldBorder
|
||||
) {
|
||||
final double dimensionScale = DimensionType.getTeleportationScale(
|
||||
sourceLevel.dimensionType(),
|
||||
targetLevel.dimensionType()
|
||||
);
|
||||
return worldBorder.clampToBounds(
|
||||
entity.getX() * dimensionScale,
|
||||
entity.getY(),
|
||||
entity.getZ() * dimensionScale
|
||||
);
|
||||
}
|
||||
|
||||
public Optional<BlockPos> createPortal(
|
||||
BlockPos startPosition,
|
||||
Direction.Axis axis,
|
||||
int portalID
|
||||
) {
|
||||
Direction portalDirection = Direction.get(Direction.AxisDirection.POSITIVE, axis);
|
||||
double d = -1.0;
|
||||
BlockPos centerPos = null;
|
||||
double e = -1.0;
|
||||
BlockPos blockPos3 = null;
|
||||
final WorldBorder worldBorder = this.targetLevel.getWorldBorder();
|
||||
|
||||
final int maxHeight = Math.min(
|
||||
this.targetLevel.getMaxBuildHeight(),
|
||||
this.targetLevel.getMinBuildHeight() + this.targetLevel.getLogicalHeight()
|
||||
) - 1;
|
||||
|
||||
BlockPos.MutableBlockPos currentPos = startPosition.mutable();
|
||||
for (BlockPos.MutableBlockPos testPosition : BlockPos.spiralAround(
|
||||
startPosition, SPIRAL_SEARCH_RADIUS, Direction.EAST, Direction.SOUTH
|
||||
)) {
|
||||
final int levelHeight = Math.min(
|
||||
maxHeight,
|
||||
this.targetLevel.getHeight(
|
||||
Heightmap.Types.MOTION_BLOCKING,
|
||||
testPosition.getX(),
|
||||
testPosition.getZ()
|
||||
)
|
||||
);
|
||||
|
||||
// Check if the portal is within the world border
|
||||
if (!worldBorder.isWithinBounds(testPosition)
|
||||
|| !worldBorder.isWithinBounds(testPosition.move(portalDirection, 1)))
|
||||
continue;
|
||||
|
||||
testPosition.move(portalDirection.getOpposite(), 1);
|
||||
for (int yy = levelHeight; yy >= this.targetLevel.getMinBuildHeight(); --yy) {
|
||||
int n;
|
||||
testPosition.setY(yy);
|
||||
if (!this.canPortalReplaceBlock(testPosition)) continue;
|
||||
int startY = yy;
|
||||
while (yy > this.targetLevel.getMinBuildHeight() && this.canPortalReplaceBlock(testPosition.move(
|
||||
Direction.DOWN))) {
|
||||
--yy;
|
||||
}
|
||||
if (yy + 4 > maxHeight || (n = startY - yy) > 0 && n < 3) continue;
|
||||
testPosition.setY(yy);
|
||||
if (!this.canHostFrame(testPosition, currentPos, portalDirection, 0)) continue;
|
||||
double f = startPosition.distSqr(testPosition);
|
||||
if (this.canHostFrame(testPosition, currentPos, portalDirection, -1) && this.canHostFrame(
|
||||
testPosition,
|
||||
currentPos,
|
||||
portalDirection,
|
||||
1
|
||||
) && (d == -1.0 || d > f)) {
|
||||
d = f;
|
||||
centerPos = testPosition.immutable();
|
||||
}
|
||||
if (d != -1.0 || e != -1.0 && !(e > f)) continue;
|
||||
e = f;
|
||||
blockPos3 = testPosition.immutable();
|
||||
}
|
||||
}
|
||||
if (d == -1.0 && e != -1.0) {
|
||||
centerPos = blockPos3;
|
||||
d = e;
|
||||
}
|
||||
if (d == -1.0) {
|
||||
int p = maxHeight - 9;
|
||||
int o = Math.max(this.targetLevel.getMinBuildHeight() - -1, 70);
|
||||
if (p < o) {
|
||||
return Optional.empty();
|
||||
}
|
||||
centerPos = new BlockPos(
|
||||
startPosition.getX(),
|
||||
Mth.clamp(startPosition.getY(), o, p),
|
||||
startPosition.getZ()
|
||||
).immutable();
|
||||
//Direction direction2 = portalDirection.getClockWise();
|
||||
if (!worldBorder.isWithinBounds(centerPos)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
buildPortal(portalDirection, centerPos, portalID);
|
||||
|
||||
return Optional.of(centerPos.immutable());
|
||||
}
|
||||
|
||||
private void buildPortal(Direction portalDirection, BlockPos centerPos, int portalID) {
|
||||
Direction.Axis portalAxis = (Direction.Axis.X == portalDirection.getAxis())
|
||||
? Direction.Axis.Z
|
||||
: Direction.Axis.X;
|
||||
if (!checkIsAreaValid(targetLevel, centerPos, portalAxis)) {
|
||||
if (targetLevel.dimension() == Level.END) {
|
||||
WorldBootstrap.getLastRegistryAccess()
|
||||
.registryOrThrow(Registries.CONFIGURED_FEATURE)
|
||||
.get(net.minecraft.data.worldgen.features.EndFeatures.END_ISLAND)
|
||||
.place(
|
||||
targetLevel,
|
||||
targetLevel.getChunkSource().getGenerator(),
|
||||
new LegacyRandomSource(centerPos.asLong()),
|
||||
centerPos.below()
|
||||
);
|
||||
} else if (targetLevel.dimension() == Level.OVERWORLD) {
|
||||
centerPos = centerPos
|
||||
.mutable()
|
||||
.setY(targetLevel.getChunk(centerPos)
|
||||
.getHeight(
|
||||
Heightmap.Types.WORLD_SURFACE,
|
||||
centerPos.getX(),
|
||||
centerPos.getZ()
|
||||
) + 1);
|
||||
}
|
||||
EndFeatures.BIOME_ISLAND
|
||||
.getPlacedFeature()
|
||||
.value()
|
||||
.place(
|
||||
targetLevel,
|
||||
targetLevel.getChunkSource().getGenerator(),
|
||||
new LegacyRandomSource(centerPos.asLong()),
|
||||
centerPos.below()
|
||||
);
|
||||
|
||||
}
|
||||
generatePortal(targetLevel, centerPos, portalAxis, portalID);
|
||||
}
|
||||
|
||||
private int getPortalID(BlockPos portalEntrancePos) {
|
||||
BlockState currentState = this.sourceLevel.getBlockState(portalEntrancePos);
|
||||
int portalID = currentState.hasProperty(EndPortalBlock.PORTAL)
|
||||
? currentState.getValue(EndPortalBlock.PORTAL)
|
||||
: -1;
|
||||
return portalID;
|
||||
}
|
||||
|
||||
private boolean canPortalReplaceBlock(BlockPos currentPos) {
|
||||
BlockState blockState = this.targetLevel.getBlockState(currentPos);
|
||||
return blockState.canBeReplaced() && blockState.getFluidState().isEmpty();
|
||||
}
|
||||
|
||||
private boolean canHostFrame(
|
||||
BlockPos pos,
|
||||
BlockPos.MutableBlockPos currentPos,
|
||||
Direction direction,
|
||||
int widthScale
|
||||
) {
|
||||
Direction orthogonalDir = direction.getClockWise();
|
||||
for (int x = -1; x < 3; ++x) {
|
||||
for (int y = -1; y < 4; ++y) {
|
||||
currentPos.setWithOffset(
|
||||
pos,
|
||||
direction.getStepX() * x + orthogonalDir.getStepX() * widthScale,
|
||||
y,
|
||||
direction.getStepZ() * x + orthogonalDir.getStepZ() * widthScale
|
||||
);
|
||||
if (y < 0 && !this.targetLevel.getBlockState(currentPos).isSolid()) {
|
||||
return false;
|
||||
}
|
||||
if (y < 0 || this.canPortalReplaceBlock(currentPos)) continue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
222
src/main/java/org/betterx/betterend/portal/TravelerState.java
Normal file
222
src/main/java/org/betterx/betterend/portal/TravelerState.java
Normal file
|
@ -0,0 +1,222 @@
|
|||
package org.betterx.betterend.portal;
|
||||
|
||||
import org.betterx.betterend.BetterEnd;
|
||||
import org.betterx.betterend.advancements.BECriteria;
|
||||
|
||||
import net.minecraft.BlockUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.portal.PortalInfo;
|
||||
import net.minecraft.world.level.portal.PortalShape;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class TravelerState {
|
||||
public final Entity entity;
|
||||
private BlockPos portalEntrancePos;
|
||||
private boolean isInsidePortal;
|
||||
private int portalTime;
|
||||
|
||||
public TravelerState(Entity entity) {
|
||||
this.entity = entity;
|
||||
this.portalEntrancePos = null;
|
||||
this.isInsidePortal = false;
|
||||
}
|
||||
|
||||
public static TravelerState init(Entity e) {
|
||||
return new TravelerState(e);
|
||||
}
|
||||
|
||||
public void handleInsidePortal(BlockPos blockPos) {
|
||||
if (entity.isOnPortalCooldown()) {
|
||||
entity.setPortalCooldown();
|
||||
return;
|
||||
}
|
||||
if (!this.level().isClientSide && !blockPos.equals(this.portalEntrancePos)) {
|
||||
this.portalEntrancePos = blockPos.immutable();
|
||||
}
|
||||
this.isInsidePortal = true;
|
||||
}
|
||||
|
||||
public Level level() {
|
||||
return this.entity.level();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void portalTick() {
|
||||
if (!(this.level() instanceof ServerLevel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isInsidePortal) {
|
||||
final int waitTimer = entity.getPortalWaitTime();
|
||||
final ServerLevel sourceDimension = (ServerLevel) this.level();
|
||||
|
||||
MinecraftServer minecraftServer = sourceDimension.getServer();
|
||||
ServerLevel targetDimension = minecraftServer.getLevel(this.level().dimension() == Level.END
|
||||
? Level.OVERWORLD
|
||||
: Level.END);
|
||||
|
||||
if (targetDimension != null && !entity.isPassenger() && this.portalTime++ >= waitTimer) {
|
||||
this.level().getProfiler().push("end_portal");
|
||||
this.portalTime = waitTimer;
|
||||
entity.setPortalCooldown();
|
||||
|
||||
this.changeDimension(targetDimension);
|
||||
this.level().getProfiler().pop();
|
||||
}
|
||||
this.isInsidePortal = false;
|
||||
} else {
|
||||
if (this.portalTime > 0) {
|
||||
this.portalTime -= 4;
|
||||
}
|
||||
if (this.portalTime < 0) {
|
||||
this.portalTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public void changeDimension(ServerLevel targetLevel) {
|
||||
if (!(this.level() instanceof ServerLevel) || this.entity.isRemoved()) {
|
||||
return;
|
||||
}
|
||||
this.level().getProfiler().push("be_findEntry");
|
||||
this.entity.unRide();
|
||||
|
||||
//EternalRitual.findRitualForActivePortal(this.level(), portalEntrancePos);
|
||||
PortalInfo portalInfo = this.findDimensionEntryPoint(targetLevel);
|
||||
if (portalInfo == null) {
|
||||
return;
|
||||
}
|
||||
this.level().getProfiler().push("be_reposition");
|
||||
teleportEntity(targetLevel, portalInfo);
|
||||
|
||||
this.level().getProfiler().pop();
|
||||
this.level().getProfiler().pop();
|
||||
return;
|
||||
}
|
||||
|
||||
private void teleportEntity(ServerLevel serverLevel, PortalInfo portalInfo) {
|
||||
final boolean targetIsEnd = serverLevel.dimension().equals(Level.END);
|
||||
final MinecraftServer server = serverLevel.getServer();
|
||||
final ServerLevel destination = targetIsEnd ? server.getLevel(Level.END) : server.getLevel(Level.OVERWORLD);
|
||||
if (entity instanceof ServerPlayer sp && sp.isCreative()) {
|
||||
sp.teleportTo(
|
||||
destination,
|
||||
portalInfo.pos.x + 0.5,
|
||||
portalInfo.pos.y,
|
||||
portalInfo.pos.z + 0.5,
|
||||
entity.getYRot() + 180,
|
||||
entity.getXRot()
|
||||
);
|
||||
BECriteria.PORTAL_TRAVEL.trigger(sp);
|
||||
} else {
|
||||
if (entity instanceof ServerPlayer sp) {
|
||||
BECriteria.PORTAL_TRAVEL.trigger(sp);
|
||||
}
|
||||
entity.setPortalCooldown();
|
||||
}
|
||||
}
|
||||
|
||||
// /execute in the_end run tp 849 84 891
|
||||
// /execute in overworld run tp 849 64 891
|
||||
@Nullable
|
||||
private PortalInfo findDimensionEntryPoint(ServerLevel targetLevel) {
|
||||
boolean toEnd = targetLevel.dimension() == Level.END;
|
||||
if (this.level().dimension() != Level.END && !toEnd) {
|
||||
return null;
|
||||
}
|
||||
final WorldBorder worldBorder = targetLevel.getWorldBorder();
|
||||
final BlockPos startingPos = PortalBuilder.getStartingPos(this.level(), targetLevel, entity, worldBorder);
|
||||
|
||||
return this.getExitPortal(targetLevel, startingPos, toEnd, worldBorder).map(foundRectangle -> {
|
||||
Vec3 vec3;
|
||||
Direction.Axis axis;
|
||||
BlockState blockState = this.level().getBlockState(this.portalEntrancePos);
|
||||
if (blockState.hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) {
|
||||
axis = blockState.getValue(BlockStateProperties.HORIZONTAL_AXIS);
|
||||
BlockUtil.FoundRectangle foundRectangle2 = BlockUtil.getLargestRectangleAround(
|
||||
this.portalEntrancePos,
|
||||
axis,
|
||||
21,
|
||||
Direction.Axis.Y,
|
||||
21,
|
||||
blockPos -> this.level().getBlockState(blockPos) == blockState
|
||||
);
|
||||
vec3 = this.getRelativePortalPosition(axis, foundRectangle2);
|
||||
} else {
|
||||
axis = Direction.Axis.X;
|
||||
vec3 = new Vec3(0.5, 0.0, 0.0);
|
||||
}
|
||||
return PortalShape.createPortalInfo(
|
||||
targetLevel, foundRectangle, axis, vec3,
|
||||
this.entity, entity.getDeltaMovement(), entity.getYRot(), entity.getXRot()
|
||||
);
|
||||
}).orElse(null);
|
||||
}
|
||||
|
||||
protected Optional<BlockUtil.FoundRectangle> getExitPortal(
|
||||
ServerLevel targetLevel,
|
||||
BlockPos startingPos,
|
||||
boolean bl,
|
||||
WorldBorder worldBorder
|
||||
) {
|
||||
final PortalBuilder builder = new PortalBuilder(this.level(), targetLevel);
|
||||
final Optional<BlockUtil.FoundRectangle> portalRectangle = builder.findPortalAround(
|
||||
startingPos,
|
||||
worldBorder
|
||||
);
|
||||
if (portalRectangle.isPresent()) {
|
||||
return portalRectangle;
|
||||
}
|
||||
|
||||
BetterEnd.LOGGER.error("Unable to locate an active portal");
|
||||
return portalRectangle;
|
||||
}
|
||||
|
||||
protected Vec3 getRelativePortalPosition(Direction.Axis axis, BlockUtil.FoundRectangle foundRectangle) {
|
||||
return PortalShape.getRelativePosition(
|
||||
foundRectangle,
|
||||
axis,
|
||||
entity.position(),
|
||||
entity.getDimensions(entity.getPose())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (TravelerState) obj;
|
||||
return Objects.equals(this.entity, that.entity) &&
|
||||
Objects.equals(this.portalEntrancePos, that.portalEntrancePos) &&
|
||||
this.isInsidePortal == that.isInsidePortal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(entity, portalEntrancePos, isInsidePortal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TravelerState[" +
|
||||
"entity=" + entity + ", " +
|
||||
"portalEntrancePos=" + portalEntrancePos + ", " +
|
||||
"isInsidePortal=" + isInsidePortal + ']';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.betterx.betterend.portal;
|
||||
|
||||
/**
|
||||
* Manages state for entities traveling through end portals.
|
||||
*/
|
||||
public interface TravelingEntity {
|
||||
TravelerState be_getTravelerState();
|
||||
|
||||
|
||||
}
|
|
@ -5,18 +5,22 @@ import org.betterx.bclib.api.v2.poi.PoiManager;
|
|||
import org.betterx.betterend.BetterEnd;
|
||||
import org.betterx.betterend.blocks.RunedFlavolite;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class EndPoiTypes {
|
||||
public static final BCLPoiType ETERNAL_PORTAL_INACTIVE = PoiManager.register(
|
||||
BetterEnd.makeID("eternal_portal_inactive"),
|
||||
Set.of(EndBlocks.FLAVOLITE_RUNED_ETERNAL.defaultBlockState().setValue(RunedFlavolite.ACTIVATED, false)),
|
||||
public static final BCLPoiType ETERNAL_PORTAL = PoiManager.register(
|
||||
BetterEnd.makeID("eternal_portal"),
|
||||
ImmutableSet.copyOf(EndBlocks.END_PORTAL_BLOCK.getStateDefinition().getPossibleStates()),
|
||||
0, 1
|
||||
);
|
||||
|
||||
public static final BCLPoiType ETERNAL_PORTAL_ACTIVE = PoiManager.register(
|
||||
BetterEnd.makeID("eternal_portal_active"),
|
||||
Set.of(EndBlocks.FLAVOLITE_RUNED_ETERNAL.defaultBlockState().setValue(RunedFlavolite.ACTIVATED, true)),
|
||||
public static final BCLPoiType ETERNAL_PORTAL_FRAME = PoiManager.register(
|
||||
BetterEnd.makeID("eternal_portal_frame"),
|
||||
Set.of(
|
||||
EndBlocks.FLAVOLITE_RUNED_ETERNAL.defaultBlockState().setValue(RunedFlavolite.ACTIVATED, false)
|
||||
),
|
||||
0, 1
|
||||
);
|
||||
|
||||
|
|
|
@ -1,49 +1,37 @@
|
|||
package org.betterx.betterend.rituals;
|
||||
|
||||
import org.betterx.bclib.blocks.BlockProperties;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
import org.betterx.betterend.BetterEnd;
|
||||
import org.betterx.betterend.advancements.BECriteria;
|
||||
import org.betterx.betterend.blocks.EndPortalBlock;
|
||||
import org.betterx.betterend.blocks.RunedFlavolite;
|
||||
import org.betterx.betterend.blocks.entities.EternalPedestalEntity;
|
||||
import org.betterx.betterend.portal.PortalBuilder;
|
||||
import org.betterx.betterend.registry.EndBlocks;
|
||||
import org.betterx.betterend.registry.EndFeatures;
|
||||
import org.betterx.betterend.registry.EndPoiTypes;
|
||||
import org.betterx.betterend.registry.EndPortals;
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import net.minecraft.BlockUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.QuartPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.particles.BlockParticleOption;
|
||||
import net.minecraft.core.particles.ParticleOptions;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.NetherPortalBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
|
@ -51,11 +39,10 @@ import java.awt.*;
|
|||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EternalRitual {
|
||||
private final static Set<Point> STRUCTURE_MAP = Sets.newHashSet(
|
||||
private final static Set<Point> PEDESTAL_POSITIONS = Sets.newHashSet(
|
||||
new Point(-4, -5),
|
||||
new Point(-4, 5),
|
||||
new Point(-6, 0),
|
||||
|
@ -63,49 +50,9 @@ public class EternalRitual {
|
|||
new Point(4, 5),
|
||||
new Point(6, 0)
|
||||
);
|
||||
private final static Set<Point> FRAME_MAP = Sets.newHashSet(
|
||||
new Point(0, 0),
|
||||
new Point(0, 6),
|
||||
new Point(1, 0),
|
||||
new Point(1, 6),
|
||||
new Point(2, 1),
|
||||
new Point(2, 5),
|
||||
new Point(3, 2),
|
||||
new Point(3, 3),
|
||||
new Point(3, 4)
|
||||
);
|
||||
private final static Set<Point> PORTAL_MAP = Sets.newHashSet(
|
||||
new Point(0, 0),
|
||||
new Point(0, 1),
|
||||
new Point(0, 2),
|
||||
new Point(0, 3),
|
||||
new Point(0, 4),
|
||||
new Point(1, 0),
|
||||
new Point(1, 1),
|
||||
new Point(1, 2),
|
||||
new Point(1, 3),
|
||||
new Point(1, 4),
|
||||
new Point(2, 1),
|
||||
new Point(2, 2),
|
||||
new Point(2, 3)
|
||||
);
|
||||
private final static Set<Point> BASE_MAP = Sets.newHashSet(
|
||||
new Point(3, 0),
|
||||
new Point(2, 0),
|
||||
new Point(2, 1),
|
||||
new Point(1, 1),
|
||||
new Point(1, 2),
|
||||
new Point(0, 1),
|
||||
new Point(0, 2)
|
||||
);
|
||||
|
||||
private final static Block BASE = EndBlocks.FLAVOLITE.tiles;
|
||||
private final static Block PEDESTAL = EndBlocks.ETERNAL_PEDESTAL;
|
||||
private final static Block FRAME = EndBlocks.FLAVOLITE_RUNED_ETERNAL;
|
||||
private final static Block PORTAL = EndBlocks.END_PORTAL_BLOCK;
|
||||
private final static BooleanProperty ACTIVE = BlockProperties.ACTIVE;
|
||||
|
||||
public final static int SEARCH_RADIUS = calculateSearchSteps(48);
|
||||
public final static BooleanProperty ACTIVE = BlockProperties.ACTIVE;
|
||||
|
||||
private Level world;
|
||||
private Direction.Axis axis;
|
||||
|
@ -148,7 +95,7 @@ public class EternalRitual {
|
|||
}
|
||||
boolean valid = checkFrame(world, center.below());
|
||||
Item item = null;
|
||||
for (Point pos : STRUCTURE_MAP) {
|
||||
for (Point pos : PEDESTAL_POSITIONS) {
|
||||
BlockPos.MutableBlockPos checkPos = center.mutable();
|
||||
checkPos.move(moveX, pos.x).move(moveY, pos.y);
|
||||
valid &= isActive(checkPos);
|
||||
|
@ -165,7 +112,7 @@ public class EternalRitual {
|
|||
}
|
||||
}
|
||||
if (valid && item != null) {
|
||||
activatePortal(item);
|
||||
activatePortal(player, item);
|
||||
if (player instanceof ServerPlayer sp) {
|
||||
BECriteria.PORTAL_ON.trigger(sp);
|
||||
}
|
||||
|
@ -175,7 +122,7 @@ public class EternalRitual {
|
|||
private boolean checkFrame(Level world, BlockPos framePos) {
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
|
||||
boolean valid = true;
|
||||
for (Point point : FRAME_MAP) {
|
||||
for (Point point : PortalBuilder.FRAME_POSITIONS) {
|
||||
BlockPos pos = framePos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
valid &= state.getBlock() instanceof RunedFlavolite;
|
||||
|
@ -190,7 +137,7 @@ public class EternalRitual {
|
|||
return active;
|
||||
}
|
||||
|
||||
private void activatePortal(Item keyItem) {
|
||||
private void activatePortal(Player player, Item keyItem) {
|
||||
if (active) return;
|
||||
ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(keyItem);
|
||||
int portalId = EndPortals.getPortalIdByItem(itemId);
|
||||
|
@ -198,13 +145,13 @@ public class EternalRitual {
|
|||
ResourceLocation worldId = targetWorld.dimension().location();
|
||||
try {
|
||||
if (exit == null) {
|
||||
initPortal(worldId, portalId);
|
||||
initPortal(player, worldId, portalId);
|
||||
} else {
|
||||
if (!worldId.equals(targetWorldId)) {
|
||||
initPortal(worldId, portalId);
|
||||
initPortal(player, worldId, portalId);
|
||||
} else if (!checkFrame(targetWorld, exit.below())) {
|
||||
Direction.Axis portalAxis = (Direction.Axis.X == axis) ? Direction.Axis.Z : Direction.Axis.X;
|
||||
generatePortal(targetWorld, exit, portalAxis, portalId);
|
||||
PortalBuilder.generatePortal(targetWorld, exit, portalAxis, portalId);
|
||||
}
|
||||
activatePortal(targetWorld, exit, portalId);
|
||||
}
|
||||
|
@ -219,9 +166,27 @@ public class EternalRitual {
|
|||
}
|
||||
}
|
||||
|
||||
private void initPortal(ResourceLocation worldId, int portalId) {
|
||||
private void initPortal(Player player, ResourceLocation worldId, int portalId) {
|
||||
targetWorldId = worldId;
|
||||
exit = findPortalPos(portalId);
|
||||
if (world instanceof ServerLevel sourceWorld) {
|
||||
ServerLevel targetLevel = (ServerLevel) getTargetWorld(portalId);
|
||||
PortalBuilder builder = new PortalBuilder(world, targetLevel);
|
||||
final WorldBorder worldBorder = targetLevel.getWorldBorder();
|
||||
final Optional<BlockUtil.FoundRectangle> foundRectangle = builder.findPortalAround(
|
||||
this.center,
|
||||
worldBorder
|
||||
);
|
||||
|
||||
if (!foundRectangle.isPresent()) {
|
||||
Optional<BlockPos> centerPos;
|
||||
centerPos = builder.createPortal(
|
||||
PortalBuilder.getStartingPos(sourceWorld, targetLevel, player, worldBorder),
|
||||
axis,
|
||||
portalId
|
||||
);
|
||||
centerPos.ifPresent(blockPos -> this.exit = blockPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doEffects(ServerLevel serverWorld, BlockPos center) {
|
||||
|
@ -233,30 +198,20 @@ public class EternalRitual {
|
|||
moveX = Direction.SOUTH;
|
||||
moveY = Direction.EAST;
|
||||
}
|
||||
for (Point pos : STRUCTURE_MAP) {
|
||||
for (Point pos : PEDESTAL_POSITIONS) {
|
||||
BlockPos.MutableBlockPos p = center.mutable();
|
||||
p.move(moveX, pos.x).move(moveY, pos.y);
|
||||
serverWorld.sendParticles(
|
||||
ParticleTypes.PORTAL,
|
||||
p.getX() + 0.5,
|
||||
p.getY() + 1.5,
|
||||
p.getZ() + 0.5,
|
||||
p.getX() + 0.5, p.getY() + 1.5, p.getZ() + 0.5,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
0, 0, 0, 1
|
||||
);
|
||||
serverWorld.sendParticles(
|
||||
ParticleTypes.REVERSE_PORTAL,
|
||||
p.getX() + 0.5,
|
||||
p.getY() + 1.5,
|
||||
p.getZ() + 0.5,
|
||||
p.getX() + 0.5, p.getY() + 1.5, p.getZ() + 0.5,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.3
|
||||
0, 0, 0, 0.3
|
||||
);
|
||||
}
|
||||
serverWorld.playSound(null, center, SoundEvents.END_PORTAL_SPAWN, SoundSource.NEUTRAL, 16, 1);
|
||||
|
@ -265,8 +220,8 @@ public class EternalRitual {
|
|||
private void activatePortal(Level world, BlockPos center, int portalId) {
|
||||
BlockPos framePos = center.below();
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
|
||||
BlockState frame = FRAME.defaultBlockState().setValue(ACTIVE, true);
|
||||
FRAME_MAP.forEach(point -> {
|
||||
BlockState frame = PortalBuilder.FRAME.defaultBlockState().setValue(ACTIVE, true);
|
||||
PortalBuilder.FRAME_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = framePos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
if (state.hasProperty(ACTIVE) && !state.getValue(ACTIVE)) {
|
||||
|
@ -279,63 +234,43 @@ public class EternalRitual {
|
|||
}
|
||||
});
|
||||
Direction.Axis portalAxis = Direction.Axis.X == axis ? Direction.Axis.Z : Direction.Axis.X;
|
||||
BlockState portal = PORTAL.defaultBlockState()
|
||||
BlockState portal = PortalBuilder.PORTAL.defaultBlockState()
|
||||
.setValue(EndPortalBlock.AXIS, portalAxis)
|
||||
.setValue(EndPortalBlock.PORTAL, portalId);
|
||||
ParticleOptions effect = new BlockParticleOption(ParticleTypes.BLOCK, portal);
|
||||
ServerLevel serverWorld = (ServerLevel) world;
|
||||
|
||||
PORTAL_MAP.forEach(point -> {
|
||||
PortalBuilder.PORTAL_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
if (!world.getBlockState(pos).is(PORTAL)) {
|
||||
if (!world.getBlockState(pos).is(PortalBuilder.PORTAL)) {
|
||||
world.setBlockAndUpdate(pos, portal);
|
||||
serverWorld.sendParticles(
|
||||
effect,
|
||||
pos.getX() + 0.5,
|
||||
pos.getY() + 0.5,
|
||||
pos.getZ() + 0.5,
|
||||
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
|
||||
10,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.1
|
||||
0.5, 0.5, 0.5, 0.1
|
||||
);
|
||||
serverWorld.sendParticles(
|
||||
ParticleTypes.REVERSE_PORTAL,
|
||||
pos.getX() + 0.5,
|
||||
pos.getY() + 0.5,
|
||||
pos.getZ() + 0.5,
|
||||
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
|
||||
10,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.3
|
||||
0.5, 0.5, 0.5, 0.3
|
||||
);
|
||||
}
|
||||
pos = center.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
if (!world.getBlockState(pos).is(PORTAL)) {
|
||||
if (!world.getBlockState(pos).is(PortalBuilder.PORTAL)) {
|
||||
world.setBlockAndUpdate(pos, portal);
|
||||
serverWorld.sendParticles(
|
||||
effect,
|
||||
pos.getX() + 0.5,
|
||||
pos.getY() + 0.5,
|
||||
pos.getZ() + 0.5,
|
||||
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
|
||||
10,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.1
|
||||
0.5, 0.5, 0.5, 0.1
|
||||
);
|
||||
serverWorld.sendParticles(
|
||||
ParticleTypes.REVERSE_PORTAL,
|
||||
pos.getX() + 0.5,
|
||||
pos.getY() + 0.5,
|
||||
pos.getZ() + 0.5,
|
||||
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
|
||||
10,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.3
|
||||
0.5, 0.5, 0.5, 0.3
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -350,146 +285,31 @@ public class EternalRitual {
|
|||
private void removePortal(Level world, BlockPos center) {
|
||||
BlockPos framePos = center.below();
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
|
||||
FRAME_MAP.forEach(point -> {
|
||||
PortalBuilder.FRAME_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = framePos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
if (state.is(FRAME) && state.getValue(ACTIVE)) {
|
||||
if (state.is(PortalBuilder.FRAME) && state.getValue(ACTIVE)) {
|
||||
world.setBlockAndUpdate(pos, state.setValue(ACTIVE, false));
|
||||
}
|
||||
pos = framePos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
state = world.getBlockState(pos);
|
||||
if (state.is(FRAME) && state.getValue(ACTIVE)) {
|
||||
if (state.is(PortalBuilder.FRAME) && state.getValue(ACTIVE)) {
|
||||
world.setBlockAndUpdate(pos, state.setValue(ACTIVE, false));
|
||||
}
|
||||
});
|
||||
PORTAL_MAP.forEach(point -> {
|
||||
PortalBuilder.PORTAL_POSITIONS.forEach(point -> {
|
||||
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
if (world.getBlockState(pos).is(PORTAL)) {
|
||||
if (world.getBlockState(pos).is(PortalBuilder.PORTAL)) {
|
||||
world.removeBlock(pos, false);
|
||||
}
|
||||
pos = center.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
if (world.getBlockState(pos).is(PORTAL)) {
|
||||
if (world.getBlockState(pos).is(PortalBuilder.PORTAL)) {
|
||||
world.removeBlock(pos, false);
|
||||
}
|
||||
});
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos findFrame(ServerLevel level, BlockPos.MutableBlockPos startPos) {
|
||||
Optional<BlockPos> found = EndPoiTypes.ETERNAL_PORTAL_INACTIVE.findPoiAround(
|
||||
level,
|
||||
startPos,
|
||||
SEARCH_RADIUS >> 4 + 1,
|
||||
level.getWorldBorder()
|
||||
);
|
||||
if (found.isPresent()) {
|
||||
if (checkFrame(level, found.get()))
|
||||
return found.get();
|
||||
}
|
||||
// List<BlockPos.MutableBlockPos> foundPos = findAllBlockPos(
|
||||
// level,
|
||||
// startPos,
|
||||
// (SEARCH_RADIUS >> 4) + 1,
|
||||
// FRAME,
|
||||
// blockState -> blockState.is(FRAME) && !blockState.getValue(ACTIVE)
|
||||
// );
|
||||
// for (BlockPos.MutableBlockPos testPos : foundPos) {
|
||||
// if (checkFrame(level, testPos)) {
|
||||
// return testPos;
|
||||
// }
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
private BlockPos findPortalPos(int portalId) {
|
||||
MinecraftServer server = world.getServer();
|
||||
ServerLevel targetWorld = (ServerLevel) getTargetWorld(portalId);
|
||||
Registry<DimensionType> registry = Objects.requireNonNull(server)
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||
double multiplier = Objects.requireNonNull(registry.get(targetWorldId)).coordinateScale();
|
||||
BlockPos.MutableBlockPos basePos = center.mutable()
|
||||
.set(
|
||||
center.getX() / multiplier,
|
||||
center.getY(),
|
||||
center.getZ() / multiplier
|
||||
);
|
||||
BlockPos framePos = findFrame(targetWorld, basePos.mutable());
|
||||
if (framePos != null) {
|
||||
return framePos.above();
|
||||
}
|
||||
Direction.Axis portalAxis = (Direction.Axis.X == axis) ? Direction.Axis.Z : Direction.Axis.X;
|
||||
int worldCeil = targetWorld.getHeight() - 1;
|
||||
|
||||
if (checkIsAreaValid(targetWorld, basePos, portalAxis)) {
|
||||
generatePortal(targetWorld, basePos, portalAxis, portalId);
|
||||
return basePos.immutable();
|
||||
} else {
|
||||
Direction direction = Direction.EAST;
|
||||
BlockPos.MutableBlockPos checkPos = basePos.mutable();
|
||||
int radius = (int) ((SEARCH_RADIUS / multiplier) + 1);
|
||||
//make sure chunks are properly loaded for faster searches
|
||||
PoiManager poiManager = targetWorld.getPoiManager();
|
||||
poiManager.ensureLoadedAndValid(world, checkPos, radius >> 4);
|
||||
|
||||
for (int step = 1; step < radius; step++) {
|
||||
for (int i = 0; i < (step >> 1); i++) {
|
||||
ChunkAccess chunk = targetWorld.getChunk(checkPos);
|
||||
if (chunk != null) {
|
||||
int surfaceY = chunk.getHeight(
|
||||
Heightmap.Types.WORLD_SURFACE,
|
||||
checkPos.getX() & 15,
|
||||
checkPos.getZ() & 15
|
||||
);
|
||||
int motionY = chunk.getHeight(
|
||||
Heightmap.Types.MOTION_BLOCKING,
|
||||
checkPos.getX() & 15,
|
||||
checkPos.getZ() & 15
|
||||
);
|
||||
int ceil = Math.min(Math.max(surfaceY, motionY) + 1, worldCeil);
|
||||
if (ceil < 5) continue;
|
||||
checkPos.setY(ceil);
|
||||
while (checkPos.getY() >= 5) {
|
||||
if (checkIsAreaValid(targetWorld, checkPos, portalAxis)) {
|
||||
generatePortal(targetWorld, checkPos, portalAxis, portalId);
|
||||
return checkPos.immutable();
|
||||
}
|
||||
checkPos.move(Direction.DOWN);
|
||||
}
|
||||
}
|
||||
checkPos.move(direction);
|
||||
}
|
||||
direction = direction.getClockWise();
|
||||
}
|
||||
}
|
||||
if (targetWorld.dimension() == Level.END) {
|
||||
WorldBootstrap.getLastRegistryAccess()
|
||||
.registryOrThrow(Registries.CONFIGURED_FEATURE)
|
||||
.get(net.minecraft.data.worldgen.features.EndFeatures.END_ISLAND)
|
||||
.place(
|
||||
targetWorld,
|
||||
targetWorld.getChunkSource().getGenerator(),
|
||||
new LegacyRandomSource(basePos.asLong()),
|
||||
basePos.below()
|
||||
);
|
||||
} else if (targetWorld.dimension() == Level.OVERWORLD) {
|
||||
basePos.setY(targetWorld.getChunk(basePos)
|
||||
.getHeight(Heightmap.Types.WORLD_SURFACE, basePos.getX(), basePos.getZ()) + 1);
|
||||
}
|
||||
EndFeatures.BIOME_ISLAND
|
||||
.getPlacedFeature()
|
||||
.value()
|
||||
.place(
|
||||
targetWorld,
|
||||
targetWorld.getChunkSource().getGenerator(),
|
||||
new LegacyRandomSource(basePos.asLong()),
|
||||
basePos.below()
|
||||
);
|
||||
generatePortal(targetWorld, basePos, portalAxis, portalId);
|
||||
return basePos.immutable();
|
||||
}
|
||||
|
||||
private Level getTargetWorld(int state) {
|
||||
if (world.dimension() == Level.END) {
|
||||
return EndPortals.getWorld(world.getServer(), state);
|
||||
|
@ -497,36 +317,6 @@ public class EternalRitual {
|
|||
return Objects.requireNonNull(world.getServer()).getLevel(Level.END);
|
||||
}
|
||||
|
||||
private boolean checkIsAreaValid(Level world, BlockPos pos, Direction.Axis axis) {
|
||||
if (pos.getY() >= world.getHeight() - 1) return false;
|
||||
if (!isBaseValid(world, pos, axis)) return false;
|
||||
return EternalRitual.checkArea(world, pos, axis);
|
||||
}
|
||||
|
||||
private boolean isBaseValid(Level world, BlockPos pos, Direction.Axis axis) {
|
||||
boolean solid = true;
|
||||
if (axis.equals(Direction.Axis.X)) {
|
||||
pos = pos.below().offset(0, 0, -3);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
BlockPos checkPos = pos.offset(0, 0, i);
|
||||
BlockState state = world.getBlockState(checkPos);
|
||||
solid &= validBlock(world, checkPos, state);
|
||||
}
|
||||
} else {
|
||||
pos = pos.below().offset(-3, 0, 0);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
BlockPos checkPos = pos.offset(i, 0, 0);
|
||||
BlockState state = world.getBlockState(checkPos);
|
||||
solid &= validBlock(world, checkPos, state);
|
||||
}
|
||||
}
|
||||
return solid;
|
||||
}
|
||||
|
||||
private boolean validBlock(Level world, BlockPos pos, BlockState state) {
|
||||
return state.isRedstoneConductor(world, pos) && state.isCollisionShapeFullBlock(world, pos);
|
||||
}
|
||||
|
||||
public void configure(BlockPos initial) {
|
||||
BlockPos checkPos = initial.east(12);
|
||||
if (this.hasPedestal(checkPos)) {
|
||||
|
@ -653,146 +443,50 @@ public class EternalRitual {
|
|||
return world.equals(ritual.world) && Objects.equals(center, ritual.center) && Objects.equals(exit, ritual.exit);
|
||||
}
|
||||
|
||||
public static void generatePortal(Level world, BlockPos center, Direction.Axis axis, int portalId) {
|
||||
BlockPos framePos = center.below();
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.EAST : Direction.NORTH;
|
||||
BlockState frame = FRAME.defaultBlockState().setValue(ACTIVE, true);
|
||||
FRAME_MAP.forEach(point -> {
|
||||
BlockPos pos = framePos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, frame);
|
||||
pos = framePos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, frame);
|
||||
});
|
||||
BlockState portal = PORTAL.defaultBlockState()
|
||||
.setValue(EndPortalBlock.AXIS, axis)
|
||||
.setValue(EndPortalBlock.PORTAL, portalId);
|
||||
PORTAL_MAP.forEach(point -> {
|
||||
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, portal);
|
||||
pos = center.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
world.setBlockAndUpdate(pos, portal);
|
||||
});
|
||||
generateBase(world, framePos, moveDir);
|
||||
public static EternalRitual findRitualForActivePortal(Level world, BlockPos startPos) {
|
||||
BlockState state = world.getBlockState(startPos);
|
||||
|
||||
if (isActivePortalBlock(state)) {
|
||||
final var direction = state.getValue(NetherPortalBlock.AXIS) == Direction.Axis.X
|
||||
? Direction.EAST
|
||||
: Direction.NORTH;
|
||||
while (world.getBlockState(startPos.below()).is(EndBlocks.END_PORTAL_BLOCK)) {
|
||||
startPos = startPos.below();
|
||||
}
|
||||
|
||||
private static void generateBase(Level world, BlockPos center, Direction moveX) {
|
||||
BlockState base = BASE.defaultBlockState();
|
||||
Direction moveY = moveX.getClockWise();
|
||||
BASE_MAP.forEach(point -> {
|
||||
BlockPos pos = center.mutable().move(moveX, point.x).move(moveY, point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
pos = center.mutable().move(moveX, -point.x).move(moveY, point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
pos = center.mutable().move(moveX, point.x).move(moveY, -point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
pos = center.mutable().move(moveX, -point.x).move(moveY, -point.y);
|
||||
world.setBlockAndUpdate(pos, base);
|
||||
});
|
||||
}
|
||||
// X O O O O O X
|
||||
// X O O O O O X
|
||||
// X a c O c b X
|
||||
// X a O b X
|
||||
// X X X
|
||||
|
||||
public static boolean checkArea(Level world, BlockPos center, Direction.Axis axis) {
|
||||
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
|
||||
for (BlockPos checkPos : BlockPos.betweenClosed(
|
||||
center.relative(moveDir.getClockWise()),
|
||||
center.relative(moveDir.getCounterClockWise())
|
||||
)) {
|
||||
for (Point point : PORTAL_MAP) {
|
||||
BlockPos pos = checkPos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
if (isStateInvalid(state)) return false;
|
||||
pos = checkPos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
|
||||
state = world.getBlockState(pos);
|
||||
if (isStateInvalid(state)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isStateInvalid(BlockState state) {
|
||||
if (!state.getFluidState().isEmpty()) return true;
|
||||
return !BlocksHelper.replaceableOrPlant(state);
|
||||
}
|
||||
if (world.getBlockState(startPos.relative(direction, -1)).is(EndBlocks.FLAVOLITE_RUNED_ETERNAL))
|
||||
startPos = startPos.relative(direction, 1); //pos (a)
|
||||
else if (world.getBlockState(startPos.relative(direction, 1)).is(EndBlocks.FLAVOLITE_RUNED_ETERNAL))
|
||||
startPos = startPos.relative(direction, -1); //pos (b)
|
||||
|
||||
/**
|
||||
* @param world World for search
|
||||
* @param checkPos Start search position
|
||||
* @param radius Search radius
|
||||
* @param searchBlock Target block
|
||||
* @param condition Predicate for test block states in the chunk section
|
||||
* @return Position of the first found block or null.
|
||||
*/
|
||||
@Nullable
|
||||
public static BlockPos.MutableBlockPos findBlockPos(
|
||||
Level world,
|
||||
BlockPos.MutableBlockPos checkPos,
|
||||
int radius,
|
||||
Block searchBlock,
|
||||
Predicate<BlockState> condition
|
||||
) {
|
||||
if (world instanceof ServerLevel server) {
|
||||
BlockPos.MutableBlockPos altCheckPos = new BlockPos.MutableBlockPos(
|
||||
checkPos.getX(),
|
||||
checkPos.getY(),
|
||||
checkPos.getZ()
|
||||
);
|
||||
var pos = findClosestPoi(server, altCheckPos);
|
||||
if (pos != null) {
|
||||
checkPos = pos.mutable();
|
||||
}
|
||||
}
|
||||
Direction moveDirection = Direction.EAST;
|
||||
for (int step = 1; step < radius; step++) {
|
||||
for (int i = 0; i < (step >> 1); i++) {
|
||||
ChunkAccess chunk = world.getChunk(checkPos);
|
||||
if (!(chunk instanceof LevelChunk) || ((LevelChunk) chunk).isEmpty()) continue;
|
||||
if (!world.getBlockState(startPos.below()).is(EndBlocks.FLAVOLITE_RUNED_ETERNAL))
|
||||
startPos = startPos.below(); //pos (c)
|
||||
|
||||
final LevelHeightAccessor levelHeightAccessor = chunk.getHeightAccessorForGeneration();
|
||||
LevelChunkSection section;
|
||||
for (int k = levelHeightAccessor.getMinSection(); k < levelHeightAccessor.getMaxSection(); ++k) {
|
||||
section = chunk.getSection(chunk.getSectionIndexFromSectionY(k));
|
||||
if (world.getBlockState(startPos.relative(direction, -1)).is(EndBlocks.FLAVOLITE_RUNED_ETERNAL))
|
||||
startPos = startPos.relative(direction, 1); //pos (a)
|
||||
else if (world.getBlockState(startPos.relative(direction, 1)).is(EndBlocks.FLAVOLITE_RUNED_ETERNAL))
|
||||
startPos = startPos.relative(direction, -1); //pos (b)
|
||||
|
||||
if (section == null || !section.getStates().maybeHas(condition)) continue;
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
BlockState checkState = section.getBlockState(x, y, z);
|
||||
if (checkState.is(searchBlock)) {
|
||||
int worldX = (chunk.getPos().x << 4) + x;
|
||||
int worldY = QuartPos.fromSection(k) + y;
|
||||
int worldZ = (chunk.getPos().z << 4) + z;
|
||||
checkPos.set(worldX, worldY, worldZ);
|
||||
return checkPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.print(checkPos + " -> " + moveDirection + " -> ");
|
||||
checkPos.move(moveDirection, 16);
|
||||
System.out.println(checkPos);
|
||||
}
|
||||
moveDirection = moveDirection.getClockWise();
|
||||
startPos = startPos.relative(direction.getClockWise(), 6);
|
||||
state = world.getBlockState(startPos);
|
||||
if (state.is(EndBlocks.ETERNAL_PEDESTAL) && world.getBlockEntity(startPos) instanceof EternalPedestalEntity pedestal) {
|
||||
return pedestal.getRitual();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param world World for search
|
||||
* @param checkPos Start search position
|
||||
* @return List of positions of the all found blocks or empty list.
|
||||
*/
|
||||
public static BlockPos findClosestPoi(
|
||||
ServerLevel world,
|
||||
BlockPos.MutableBlockPos checkPos
|
||||
) {
|
||||
//make sure chunks are properly loaded for faster searches
|
||||
PoiManager poiManager = world.getPoiManager();
|
||||
poiManager.ensureLoadedAndValid(world, checkPos, 128);
|
||||
return EndPoiTypes.ETERNAL_PORTAL_ACTIVE.findClosest(world, checkPos, 128)
|
||||
.orElse(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int calculateSearchSteps(int chunkRadius) {
|
||||
return chunkRadius * 4 - 1;
|
||||
private static boolean isActivePortalBlock(BlockState state) {
|
||||
return state.is(EndBlocks.END_PORTAL_BLOCK)
|
||||
&& state.hasProperty(NetherPortalBlock.AXIS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
"ServerPlayerMixin",
|
||||
"SlimeMixin",
|
||||
"SpikeFeatureMixin",
|
||||
"WorldGenRegionMixin"
|
||||
"WorldGenRegionMixin",
|
||||
"portal.EntityMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue