Reorganized Imports/Packages

This commit is contained in:
Frank 2022-05-18 23:56:18 +02:00
parent a8beba9196
commit 770a5b4046
854 changed files with 42775 additions and 41811 deletions

View file

@ -0,0 +1,773 @@
package org.betterx.betterend.rituals;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
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.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.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.item.Item;
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.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.material.Material;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.betterx.bclib.blocks.BlockProperties;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.blocks.EndPortalBlock;
import org.betterx.betterend.blocks.RunedFlavolite;
import org.betterx.betterend.blocks.entities.EternalPedestalEntity;
import org.betterx.betterend.registry.EndBlocks;
import org.betterx.betterend.registry.EndFeatures;
import org.betterx.betterend.registry.EndPortals;
import java.awt.*;
import java.util.List;
import java.util.Objects;
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(
new Point(-4, -5),
new Point(-4, 5),
new Point(-6, 0),
new Point(4, -5),
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);
private Level world;
private Direction.Axis axis;
private ResourceLocation targetWorldId;
private BlockPos center;
private BlockPos exit;
private boolean active = false;
public EternalRitual(Level world) {
this.world = world;
}
public EternalRitual(Level world, BlockPos initial) {
this(world);
this.configure(initial);
}
public void setWorld(Level world) {
this.world = world;
}
@Nullable
public ResourceLocation getTargetWorldId() {
return targetWorldId;
}
private boolean isInvalid() {
return world == null || world.isClientSide() || center == null || axis == null;
}
public void checkStructure() {
if (isInvalid()) return;
Direction moveX, moveY;
if (Direction.Axis.X == axis) {
moveX = Direction.EAST;
moveY = Direction.NORTH;
} else {
moveX = Direction.SOUTH;
moveY = Direction.EAST;
}
boolean valid = checkFrame(world, center.below());
Item item = null;
for (Point pos : STRUCTURE_MAP) {
BlockPos.MutableBlockPos checkPos = center.mutable();
checkPos.move(moveX, pos.x).move(moveY, pos.y);
valid &= isActive(checkPos);
if (valid) {
EternalPedestalEntity pedestal = (EternalPedestalEntity) world.getBlockEntity(checkPos);
if (pedestal != null) {
Item pItem = pedestal.getItem(0).getItem();
if (item == null) {
item = pItem;
} else if (!item.equals(pItem)) {
valid = false;
}
}
}
}
if (valid && item != null) {
activatePortal(item);
}
}
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) {
BlockPos pos = framePos.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
BlockState state = world.getBlockState(pos);
valid &= state.getBlock() instanceof RunedFlavolite;
pos = framePos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
state = world.getBlockState(pos);
valid &= state.getBlock() instanceof RunedFlavolite;
}
return valid;
}
public boolean isActive() {
return active;
}
private void activatePortal(Item keyItem) {
if (active) return;
ResourceLocation itemId = Registry.ITEM.getKey(keyItem);
int portalId = EndPortals.getPortalIdByItem(itemId);
Level targetWorld = getTargetWorld(portalId);
ResourceLocation worldId = targetWorld.dimension().location();
try {
if (exit == null) {
initPortal(worldId, portalId);
} else {
if (!worldId.equals(targetWorldId)) {
initPortal(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);
}
activatePortal(targetWorld, exit, portalId);
}
activatePortal(world, center, portalId);
doEffects((ServerLevel) world, center);
active = true;
} catch (Exception ex) {
BetterEnd.LOGGER.error("Create End portals error.", ex);
removePortal(targetWorld, exit);
removePortal(world, center);
active = false;
}
}
private void initPortal(ResourceLocation worldId, int portalId) {
targetWorldId = worldId;
exit = findPortalPos(portalId);
}
private void doEffects(ServerLevel serverWorld, BlockPos center) {
Direction moveX, moveY;
if (Direction.Axis.X == axis) {
moveX = Direction.EAST;
moveY = Direction.NORTH;
} else {
moveX = Direction.SOUTH;
moveY = Direction.EAST;
}
for (Point pos : STRUCTURE_MAP) {
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,
20,
0,
0,
0,
1
);
serverWorld.sendParticles(
ParticleTypes.REVERSE_PORTAL,
p.getX() + 0.5,
p.getY() + 1.5,
p.getZ() + 0.5,
20,
0,
0,
0,
0.3
);
}
serverWorld.playSound(null, center, SoundEvents.END_PORTAL_SPAWN, SoundSource.NEUTRAL, 16, 1);
}
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 -> {
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)) {
world.setBlockAndUpdate(pos, frame);
}
pos = framePos.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
state = world.getBlockState(pos);
if (state.hasProperty(ACTIVE) && !state.getValue(ACTIVE)) {
world.setBlockAndUpdate(pos, frame);
}
});
Direction.Axis portalAxis = Direction.Axis.X == axis ? Direction.Axis.Z : Direction.Axis.X;
BlockState portal = 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 -> {
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
if (!world.getBlockState(pos).is(PORTAL)) {
world.setBlockAndUpdate(pos, portal);
serverWorld.sendParticles(
effect,
pos.getX() + 0.5,
pos.getY() + 0.5,
pos.getZ() + 0.5,
10,
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,
10,
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)) {
world.setBlockAndUpdate(pos, portal);
serverWorld.sendParticles(
effect,
pos.getX() + 0.5,
pos.getY() + 0.5,
pos.getZ() + 0.5,
10,
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,
10,
0.5,
0.5,
0.5,
0.3
);
}
});
}
public void disablePortal(int state) {
if (!active || isInvalid()) return;
removePortal(getTargetWorld(state), exit);
removePortal(world, center);
}
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 -> {
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)) {
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)) {
world.setBlockAndUpdate(pos, state.setValue(ACTIVE, false));
}
});
PORTAL_MAP.forEach(point -> {
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
if (world.getBlockState(pos).is(PORTAL)) {
world.removeBlock(pos, false);
}
pos = center.mutable().move(moveDir, -point.x).move(Direction.UP, point.y);
if (world.getBlockState(pos).is(PORTAL)) {
world.removeBlock(pos, false);
}
});
this.active = false;
}
@Nullable
private BlockPos findFrame(Level world, BlockPos.MutableBlockPos startPos) {
List<BlockPos.MutableBlockPos> foundPos = findAllBlockPos(
world,
startPos,
(SEARCH_RADIUS >> 4) + 1,
FRAME,
blockState -> blockState.is(FRAME) && !blockState.getValue(ACTIVE)
);
for (BlockPos.MutableBlockPos testPos : foundPos) {
if (checkFrame(world, 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(Registry.DIMENSION_TYPE_REGISTRY);
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);
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) {
net.minecraft.data.worldgen.features.EndFeatures.END_ISLAND.value().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);
}
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)) {
this.axis = Direction.Axis.X;
this.center = initial.east(6);
return;
}
checkPos = initial.west(12);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.X;
this.center = initial.west(6);
return;
}
checkPos = initial.south(12);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.Z;
this.center = initial.south(6);
return;
}
checkPos = initial.north(12);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.Z;
this.center = initial.north(6);
return;
}
checkPos = initial.north(10);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.X;
checkPos = checkPos.east(8);
if (this.hasPedestal(checkPos)) {
this.center = initial.north(5).east(4);
} else {
this.center = initial.north(5).west(4);
}
return;
}
checkPos = initial.south(10);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.X;
checkPos = checkPos.east(8);
if (this.hasPedestal(checkPos)) {
this.center = initial.south(5).east(4);
} else {
this.center = initial.south(5).west(4);
}
return;
}
checkPos = initial.east(10);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.Z;
checkPos = checkPos.south(8);
if (this.hasPedestal(checkPos)) {
this.center = initial.east(5).south(4);
} else {
this.center = initial.east(5).north(4);
}
return;
}
checkPos = initial.west(10);
if (this.hasPedestal(checkPos)) {
this.axis = Direction.Axis.Z;
checkPos = checkPos.south(8);
if (this.hasPedestal(checkPos)) {
this.center = initial.west(5).south(4);
} else {
this.center = initial.west(5).north(4);
}
}
}
private boolean hasPedestal(BlockPos pos) {
return world.getBlockState(pos).is(PEDESTAL);
}
private boolean isActive(BlockPos pos) {
BlockState state = world.getBlockState(pos);
if (state.is(PEDESTAL)) {
EternalPedestalEntity pedestal = (EternalPedestalEntity) world.getBlockEntity(pos);
if (pedestal != null) {
if (!pedestal.hasRitual()) {
pedestal.linkRitual(this);
} else {
EternalRitual ritual = pedestal.getRitual();
if (!ritual.equals(this)) {
pedestal.linkRitual(this);
}
}
}
return state.getValue(ACTIVE);
}
return false;
}
public CompoundTag toTag(CompoundTag tag) {
tag.put("center", NbtUtils.writeBlockPos(center));
tag.putString("axis", axis.getName());
tag.putBoolean("active", active);
if (targetWorldId != null) {
tag.putString("key_item", targetWorldId.toString());
}
if (exit != null) {
tag.put("exit", NbtUtils.writeBlockPos(exit));
}
return tag;
}
public void fromTag(CompoundTag tag) {
axis = Direction.Axis.byName(tag.getString("axis"));
center = NbtUtils.readBlockPos(tag.getCompound("center"));
active = tag.getBoolean("active");
if (tag.contains("exit")) {
exit = NbtUtils.readBlockPos(tag.getCompound("exit"));
}
if (tag.contains("key_item")) {
targetWorldId = new ResourceLocation(tag.getString("key_item"));
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EternalRitual ritual = (EternalRitual) o;
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);
}
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);
});
}
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;
Material material = state.getMaterial();
return !material.isReplaceable() && !material.equals(Material.PLANT);
}
/**
* @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) {
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;
for (LevelChunkSection section : chunk.getSections()) {
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 = section.bottomBlockY() + y;
int worldZ = (chunk.getPos().z << 4) + z;
checkPos.set(worldX, worldY, worldZ);
return checkPos;
}
}
}
}
}
checkPos.move(moveDirection, 16);
}
moveDirection = moveDirection.getClockWise();
}
return null;
}
/**
* @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 List of positions of the all found blocks or empty list.
*/
public static List<BlockPos.MutableBlockPos> findAllBlockPos(Level world,
BlockPos.MutableBlockPos checkPos,
int radius,
Block searchBlock,
Predicate<BlockState> condition) {
List<BlockPos.MutableBlockPos> posFound = Lists.newArrayList();
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;
for (LevelChunkSection section : chunk.getSections()) {
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 = section.bottomBlockY() + y;
int worldZ = (chunk.getPos().z << 4) + z;
checkPos.set(worldX, worldY, worldZ);
posFound.add(checkPos.mutable());
}
}
}
}
}
checkPos.move(moveDirection, 16);
}
moveDirection = moveDirection.getClockWise();
}
return posFound;
}
public static int calculateSearchSteps(int radius) {
return radius * 4 - 1;
}
}

View file

@ -0,0 +1,255 @@
package org.betterx.betterend.rituals;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.betterx.betterend.blocks.entities.InfusionPedestalEntity;
import org.betterx.betterend.blocks.entities.PedestalBlockEntity;
import org.betterx.betterend.particle.InfusionParticleType;
import org.betterx.betterend.recipe.builders.InfusionRecipe;
import java.awt.*;
import java.util.Arrays;
import java.util.Objects;
public class InfusionRitual implements Container {
private static final Point[] PEDESTALS_MAP = new Point[]{
new Point(0, 3),
new Point(2, 2),
new Point(3, 0),
new Point(2, -2),
new Point(0, -3),
new Point(-2, -2),
new Point(-3, 0),
new Point(-2, 2)
};
private Level world;
private BlockPos worldPos;
private InfusionRecipe activeRecipe;
private boolean isDirty = false;
private boolean hasRecipe = false;
private int progress = 0;
private int time = 0;
private final PedestalBlockEntity[] catalysts = new PedestalBlockEntity[8];
private final InfusionPedestalEntity input;
public InfusionRitual(InfusionPedestalEntity pedestal, Level world, BlockPos pos) {
this.input = pedestal;
this.world = world;
this.worldPos = pos;
configure();
}
public void configure() {
if (world == null || worldPos == null) return;
for (int i = 0; i < catalysts.length; i++) {
Point point = PEDESTALS_MAP[i];
MutableBlockPos checkPos = worldPos.mutable().move(Direction.EAST, point.x).move(Direction.NORTH, point.y);
BlockEntity catalystEntity = world.getBlockEntity(checkPos);
if (catalystEntity instanceof PedestalBlockEntity) {
catalysts[i] = (PedestalBlockEntity) catalystEntity;
} else {
catalysts[i] = null;
}
}
}
public boolean checkRecipe() {
if (!isValid()) return false;
InfusionRecipe recipe = world.getRecipeManager().getRecipeFor(InfusionRecipe.TYPE, this, world).orElse(null);
if (hasRecipe()) {
if (recipe == null) {
reset();
return false;
} else if (activeRecipe == null || recipe.getInfusionTime() != time) {
updateRecipe(recipe);
}
return true;
}
if (recipe != null) {
updateRecipe(recipe);
return true;
}
return false;
}
private void updateRecipe(InfusionRecipe recipe) {
activeRecipe = recipe;
hasRecipe = true;
resetTimer();
setChanged();
}
private void resetTimer() {
time = activeRecipe != null ? activeRecipe.getInfusionTime() : 0;
progress = 0;
}
public void reset() {
activeRecipe = null;
hasRecipe = false;
resetTimer();
setChanged();
}
public void tick() {
if (isDirty) {
configure();
isDirty = false;
}
if (!checkRecipe()) return;
progress++;
if (progress == time) {
clearContent();
input.setItem(0, activeRecipe.assemble(this));
reset();
} else if (world instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel) world;
BlockPos target = worldPos.above();
double tx = target.getX() + 0.5;
double ty = target.getY() + 0.5;
double tz = target.getZ() + 0.5;
for (PedestalBlockEntity catalyst : catalysts) {
ItemStack stack = catalyst.getItem(0);
if (!stack.isEmpty()) {
BlockPos start = catalyst.getBlockPos();
double sx = start.getX() + 0.5;
double sy = start.getY() + 1.25;
double sz = start.getZ() + 0.5;
serverLevel.sendParticles(
new InfusionParticleType(stack),
sx,
sy,
sz,
0,
tx - sx,
ty - sy,
tz - sz,
0.5
);
}
}
}
}
@Override
public boolean canPlaceItem(int slot, ItemStack stack) {
return isValid();
}
public boolean isValid() {
if (world == null || worldPos == null || input == null) return false;
return Arrays.stream(catalysts).noneMatch(Objects::isNull);
}
public boolean hasRecipe() {
return hasRecipe;
}
public void setLocation(Level world, BlockPos pos) {
this.world = world;
this.worldPos = pos;
this.isDirty = true;
}
@Override
public void clearContent() {
if (!isValid()) return;
input.clearContent();
Arrays.stream(catalysts).forEach(PedestalBlockEntity::clearContent);
}
@Override
public int getContainerSize() {
return 9;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public ItemStack getItem(int slot) {
if (slot > 8) return ItemStack.EMPTY;
if (slot == 0) {
return input.getItem(0);
} else {
return catalysts[slot - 1].getItem(0);
}
}
@Override
public ItemStack removeItem(int slot, int amount) {
return removeItemNoUpdate(slot);
}
@Override
public ItemStack removeItemNoUpdate(int slot) {
if (slot > 8) return ItemStack.EMPTY;
if (slot == 0) {
return input.removeItemNoUpdate(0);
} else {
return catalysts[slot - 1].removeItemNoUpdate(0);
}
}
@Override
public void setItem(int slot, ItemStack stack) {
if (slot > 8) return;
if (slot == 0) {
input.setItem(0, stack);
} else {
catalysts[slot - 1].setItem(0, stack);
}
}
@Override
public void setChanged() {
if (isValid()) {
input.setChanged();
Arrays.stream(catalysts).forEach(PedestalBlockEntity::setChanged);
}
}
public void setDirty() {
this.isDirty = true;
}
@Override
public boolean stillValid(Player player) {
return true;
}
public void fromTag(CompoundTag tag) {
if (tag.contains("recipe")) {
hasRecipe = tag.getBoolean("recipe");
progress = tag.getInt("progress");
time = tag.getInt("time");
}
}
public CompoundTag toTag(CompoundTag tag) {
if (hasRecipe()) {
tag.putBoolean("recipe", hasRecipe);
tag.putInt("progress", progress);
tag.putInt("time", time);
}
return tag;
}
public static Point[] getMap() {
return PEDESTALS_MAP;
}
}