Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
at master 334 lines 16 kB view raw
1package net.lerariemann.infinity.item; 2 3import net.lerariemann.infinity.block.entity.InfinityPortalBlockEntity; 4import net.lerariemann.infinity.registry.core.ModBlocks; 5import net.lerariemann.infinity.registry.core.ModComponentTypes; 6import net.lerariemann.infinity.util.VersionMethods; 7import net.lerariemann.infinity.util.ClientMethods; 8import net.lerariemann.infinity.util.InfinityMethods; 9import net.lerariemann.infinity.util.teleport.InfinityPortal; 10import net.minecraft.BlockUtil; 11import net.minecraft.ChatFormatting; 12import net.minecraft.MethodsReturnNonnullByDefault; 13import net.minecraft.core.BlockPos; 14import net.minecraft.core.Direction; 15import net.minecraft.network.chat.Component; 16import net.minecraft.network.chat.MutableComponent; 17import net.minecraft.resources.ResourceLocation; 18import net.minecraft.sounds.SoundEvents; 19import net.minecraft.sounds.SoundSource; 20import net.minecraft.world.InteractionHand; 21import net.minecraft.world.InteractionResult; 22//? if >1.21.4 { 23import net.minecraft.world.item.component.TooltipDisplay; 24import java.util.function.Consumer; 25//?} else { 26/*import net.minecraft.world.InteractionResultHolder; 27*///?} 28import net.minecraft.world.entity.player.Player; 29import net.minecraft.world.item.Item; 30import net.minecraft.world.item.ItemStack; 31import net.minecraft.world.item.TooltipFlag; 32import net.minecraft.world.item.context.UseOnContext; 33import net.minecraft.world.level.Level; 34import net.minecraft.world.level.block.Blocks; 35import net.minecraft.world.level.block.NetherPortalBlock; 36import net.minecraft.world.level.block.state.BlockState; 37import net.minecraft.world.level.block.state.properties.BlockStateProperties; 38import org.jetbrains.annotations.Nullable; 39//? if neoforge { 40/*import javax.annotation.ParametersAreNonnullByDefault; 41*///?} 42import java.util.*; 43import java.util.function.Consumer; 44 45//? if neoforge { 46/*@ParametersAreNonnullByDefault 47*///?} 48@MethodsReturnNonnullByDefault 49public class F4Item extends Item implements PortalDataHolder.Destinable { 50 static final BlockState OBSIDIAN = Blocks.OBSIDIAN.defaultBlockState(); 51 52 public F4Item(Properties settings) { 53 super(settings); 54 } 55 56 public static MutableComponent getDimensionTooltip(@Nullable ResourceLocation dimension) { 57 if (dimension == null) return Component.translatable(Blocks.NETHER_PORTAL.getDescriptionId()); 58 String name = dimension.toString(); 59 MutableComponent text = InfinityMethods.getDimensionNameAsText(dimension); 60 if (name.contains("infinity:generated_") || name.equals(InfinityMethods.ofRandomDim)) 61 return text; 62 return Component.translatable("tooltip.infinity.f4", text); 63 } 64 65 public static int getCharge(ItemStack f4) { 66 return VersionMethods.getOrDefaultInt(f4, ModComponentTypes.CHARGE, 0); 67 } 68 69 @Override 70 public ItemStack getStack() { 71 return getDefaultInstance(); 72 } 73 74 @Override 75 public void appendHoverText(ItemStack stack, 76 //? if >1.21 { 77 TooltipContext 78 //?} else { 79 /*Level 80 *///?} 81 context, 82 //? if >1.21.2 { 83 TooltipDisplay tooltipDisplay, Consumer<Component> tooltipAdder 84 //?} else { 85 /*List<Component> tooltip 86 *///?} 87 , TooltipFlag type) { 88 //? if >1.21.2 { 89 super.appendHoverText(stack, context, tooltipDisplay, tooltipAdder, type); 90 //?} else { 91 /*super.appendHoverText(stack, context, tooltip, type); 92 Consumer<Component> tooltipAdder = tooltip::add; 93 *///?} 94 ResourceLocation dimension = VersionMethods.getDimensionIdentifier(stack); 95 MutableComponent mutableText = getDimensionTooltip(dimension).withStyle(ChatFormatting.GRAY); 96 tooltipAdder.accept(mutableText); 97 MutableComponent mutableText2 = Component.translatable("tooltip.infinity.f4.charges", getCharge(stack)).withStyle(ChatFormatting.GRAY); 98 tooltipAdder.accept(mutableText2); 99 } 100 101 //? if >1.21 { 102 @Nullable 103 //?} 104 public static ItemStack placePortal(Level world, Player player, ItemStack stack, BlockPos lowerCenter, 105 int size_x, int size_y) { 106 Direction.Axis dir2 = player.getDirection().getClockWise(Direction.Axis.Y).getAxis(); 107 108 int charges = getCharge(stack); 109 int useCharges = player.isCreative() ? 0 : 2*(2 + size_x + size_y); 110 if (charges < useCharges) { 111 if (!world.isClientSide()) { 112 //? if >1.21.4 { 113 player.displayClientMessage(Component.translatable("error.infinity.f4.no_charges", useCharges), false); 114 //?} else { 115 /*player.sendSystemMessage(Component.translatable("error.infinity.f4.no_charges", useCharges)); 116 *///?} 117 } 118 return null; 119 } 120 BlockPos lowerLeft = lowerCenter.relative(dir2, -(size_x/2)); 121 ResourceLocation id = VersionMethods.getDimensionIdentifier(stack); 122 boolean doNotRenderPortal = (world.isClientSide() && (id == null || !id.getPath().contains("generated_"))); 123 if (PortalDataHolder.isDestinationRandom(id)) 124 id = InfinityMethods.getRandomId(world.random); 125 int obsNotReplaced = 0; 126 127 //placing the portal 128 for (int x = -1; x <= size_x; x++) { 129 if (!world.setBlockAndUpdate(lowerLeft.relative(dir2, x).above(size_y), OBSIDIAN)) obsNotReplaced++; 130 if (!world.setBlockAndUpdate(lowerLeft.relative(dir2, x).below(), OBSIDIAN)) obsNotReplaced++; 131 } 132 for (int y = 0; y < size_y; y++) { 133 if (!world.setBlockAndUpdate(lowerLeft.relative(dir2, -1).relative(Direction.UP, y), OBSIDIAN)) obsNotReplaced++; 134 if (!world.setBlockAndUpdate(lowerLeft.relative(dir2, size_x).relative(Direction.UP, y), OBSIDIAN)) obsNotReplaced++; 135 if (!doNotRenderPortal) for (int x = 0; x < size_x; x++) { 136 BlockPos pos = lowerLeft.relative(dir2, x).relative(Direction.UP, y); 137 world.setBlockAndUpdate(pos, 138 ((id == null) ? Blocks.NETHER_PORTAL : ModBlocks.PORTAL.get()) 139 .defaultBlockState().setValue(NetherPortalBlock.AXIS, dir2)); 140 if (id != null && world.getBlockEntity(pos) instanceof InfinityPortalBlockEntity ipbe) { 141 ipbe.setData(world.getServer(), id); 142 } 143 } 144 } 145 useCharges -= obsNotReplaced; 146 147 world.playSound(player, player.blockPosition(), SoundEvents.BELL_BLOCK, SoundSource.BLOCKS, 1, 0.75f); 148 return VersionMethods.apply(stack, ModComponentTypes.CHARGE, charges-useCharges); 149 150 } 151 152 @Override 153 public 154 //? if <1.21.5 { 155 /*InteractionResultHolder<ItemStack> 156 *///?} else { 157 InteractionResult 158 //?} 159 use(Level world, Player player, InteractionHand hand) { 160 if (world.isClientSide()) 161 ClientMethods.setF4Screen(player); 162 return VersionMethods.success(player.getItemInHand(hand)); 163 } 164 165 public static 166 //? if <1.21.5 { 167 /*InteractionResultHolder<ItemStack> 168 *///?} else { 169 InteractionResult 170 //?} 171 deploy(Level world, Player player, InteractionHand hand) { 172 Direction dir = player.getDirection(); 173 Direction.Axis dir2 = dir.getClockWise(Direction.Axis.Y).getAxis(); 174 BlockPos lowerCenter = player.blockPosition().relative(dir, 4); 175 ItemStack stack = player.getItemInHand(hand); 176 177 int size_x = VersionMethods.getOrDefaultInt(stack, ModComponentTypes.SIZE_X, 3); 178 int size_y = VersionMethods.getOrDefaultInt(stack, ModComponentTypes.SIZE_Y, 3); 179 if (size_y % 2 == 0) { 180 double d = dir2.equals(Direction.Axis.X) ? player.position().x : player.position().z; 181 if (d % 1 > 0.5) { //player on the positive side of the block 182 lowerCenter = lowerCenter.relative(dir2, 1); 183 } 184 } 185 186 int lowerY = lowerCenter.getY(); 187 if (world.isOutsideBuildHeight(lowerY)) return VersionMethods.pass(stack); 188 189 //finding a place position 190 int i; 191 boolean positionFound = true; 192 for (i = 0; i <= 8 && !world.isOutsideBuildHeight(lowerY + i + size_y); i++) { 193 positionFound = true; 194 for (int j = 0; j <= size_y+1 && positionFound; j++) for (int k = -1; k <= size_x; k++) { 195 BlockState bs = world.getBlockState(lowerCenter.above(i+j-1).relative(dir2, k - (size_x /2))); 196 if (bs.canBeReplaced()) continue; 197 if (bs.is(Blocks.OBSIDIAN)) { 198 if (j == 0 || j == size_y+1 || k == -1 || k == size_x) continue; 199 i += j-1; 200 } 201 else i += j; 202 positionFound = false; 203 break; 204 } 205 if (positionFound) break; 206 } 207 if (!positionFound) return VersionMethods.pass(stack); 208 209 ItemStack newStack = placePortal(world, player, stack.copy(), lowerCenter.above(i), size_x, size_y); 210 if (newStack == null) return VersionMethods.pass(stack); 211 return VersionMethods.consume(player.isCreative() ? stack : newStack); 212 } 213 214 public static boolean isPortal(BlockState state) { 215 return state.is(Blocks.NETHER_PORTAL) || state.is(ModBlocks.PORTAL.get()); 216 } 217 218 @Override 219 public InteractionResult useOn(UseOnContext context) { 220 Player player = context.getPlayer(); 221 if (player == null) return InteractionResult.FAIL; 222 ItemStack stack = context.getItemInHand(); 223 Level world = context.getLevel(); 224 BlockPos pos = context.getClickedPos(); 225 BlockState bs = world.getBlockState(pos); 226 if (bs.canBeReplaced()) { 227 pos = pos.below(); 228 bs = world.getBlockState(pos); 229 } 230 InteractionHand hand = context.getHand(); 231 232 if (isPortal(bs)) { 233 ItemStack newStack = useOnPortalBlock(world, pos, stack.copy()); 234 if (!player.isCreative()) player.setItemInHand(hand, newStack); 235 return InteractionResult.CONSUME; 236 } 237 pos = pos.above(bs.is(Blocks.OBSIDIAN) ? 1 : 2); 238 239 Direction.Axis dir2 = 240 player.getDirection().getAxis().equals(Direction.Axis.X) ? Direction.Axis.Z : Direction.Axis.X; 241 242 int size_x = VersionMethods.getOrDefaultInt(stack, ModComponentTypes.SIZE_X, 3); 243 int size_y = VersionMethods.getOrDefaultInt(stack, ModComponentTypes.SIZE_Y, 3); 244 245 //validating the place position 246 for (int j = -1; j <= size_y; j++) for (int k = -1; k <= size_x; k++) { 247 bs = world.getBlockState(pos.above(j).relative(dir2, k - (size_x /2))); 248 if (bs.is(Blocks.OBSIDIAN) && (j == -1 || j == size_y || k == -1 || k == size_x)) continue; 249 if (!bs.canBeReplaced()) return InteractionResult.FAIL; 250 } 251 252 ItemStack newStack = placePortal(world, player, context.getItemInHand().copy(), pos, 253 size_x, size_y); 254 if (newStack == null) { 255 return InteractionResult.FAIL; 256 } 257 if (!player.isCreative()) player.setItemInHand(hand, newStack); 258 return InteractionResult.CONSUME; 259 } 260 261 public static boolean checkIfValidAxis(Direction.Axis axisFound, Direction.Axis axisBeingChecked, @Nullable Direction.Axis forceAxis) { 262 if (forceAxis == null) { 263 return axisBeingChecked.isVertical() || axisFound.equals(axisBeingChecked); 264 } 265 return axisFound.equals(forceAxis); 266 } 267 268 public static boolean checkNeighbors(Level world, BlockPos bp, 269 Collection<Direction> primaryOffsets, //possible directions in which blocks of other portals may be 270 Collection<Direction> secondaryOffsets, //possible secondary directions from a neighboring obsidian in case we found a corner 271 int max, @Nullable Direction.Axis forcePrimaryAxis) { 272 int i = 0; 273 boolean checkCorners = !secondaryOffsets.isEmpty(); 274 for (Direction dir : primaryOffsets) { 275 BlockState bs = world.getBlockState(bp.relative(dir)); 276 Direction.Axis axis = dir.getAxis(); 277 if (isPortal(bs)) { 278 Direction.Axis axisFound = bs.getValue(BlockStateProperties.HORIZONTAL_AXIS); 279 if (checkIfValidAxis(axisFound, axis, forcePrimaryAxis)) 280 if (++i > max) return true; 281 } 282 if (checkCorners && bs.is(Blocks.OBSIDIAN)) { 283 Direction.Axis forceAxis = axis.isHorizontal() ? axis : null; 284 return checkNeighbors(world, bp.relative(dir), secondaryOffsets, Set.of(), 0, forceAxis); 285 } 286 } 287 return false; 288 } 289 290 public static void checkObsidianRemovalSides(Level world, BlockPos bp, 291 Set<BlockPos> toRemove, 292 Set<BlockPos> toLeave, 293 Direction direction) { 294 if (world.getBlockState(bp).is(Blocks.OBSIDIAN)) { 295 boolean bl = direction.getAxis().isVertical(); 296 Set<Direction> primaryOffsets = bl ? Set.of(direction) : Set.of(direction, 297 direction.getClockWise(Direction.Axis.Y), 298 direction.getCounterClockWise(Direction.Axis.Y)); 299 Set<Direction> secondaryOffsets = bl ? Set.of(Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST) : 300 Set.of(Direction.UP, Direction.DOWN); 301 if (checkNeighbors(world, bp, primaryOffsets, secondaryOffsets, 0, null)) { 302 toLeave.add(bp); 303 return; 304 } 305 toRemove.add(bp); 306 } 307 } 308 309 public static ItemStack useOnPortalBlock(Level world, BlockPos origin, ItemStack stack) { 310 Direction.Axis axis = world.getBlockState(origin).getValue(NetherPortalBlock.AXIS); 311 Direction positive = axis.equals(Direction.Axis.X) ? Direction.EAST : Direction.SOUTH; 312 BlockUtil.FoundRectangle portal = InfinityPortal.getRect(world, origin); 313 Set<BlockPos> toRemove = new HashSet<>(); 314 Set<BlockPos> toLeave = new HashSet<>(); 315 for (int i = -1; i <= portal.axis1Size; i++) { 316 checkObsidianRemovalSides(world, portal.minCorner.relative(axis, i).above(-1), toRemove, toLeave, Direction.DOWN); 317 checkObsidianRemovalSides(world, portal.minCorner.relative(axis, i).above(portal.axis2Size), toRemove, toLeave, Direction.UP); 318 } 319 for (int j = -1; j <= portal.axis2Size; j++) { 320 checkObsidianRemovalSides(world, portal.minCorner.relative(axis, -1).above(j), toRemove, toLeave, positive.getOpposite()); 321 checkObsidianRemovalSides(world, portal.minCorner.relative(axis, portal.axis1Size).above(j), toRemove, toLeave, positive); 322 } 323 int obsidian = 0; 324 for (BlockPos bp : toRemove) if (!toLeave.contains(bp)) { //double check since we're checking the corners twice 325 world.setBlock(bp, Blocks.AIR.defaultBlockState(), 3, 0); 326 obsidian++; 327 } 328 for (int i = 0; i < portal.axis1Size; i++) for (int j = 0; j < portal.axis2Size; j++) 329 world.setBlock(portal.minCorner.relative(axis, i).above(j), Blocks.AIR.defaultBlockState(), 3, 0); 330 world.playSound(null, origin, SoundEvents.GLASS_BREAK, SoundSource.BLOCKS, 1, 0.75f); 331 return VersionMethods.apply(stack, ModComponentTypes.CHARGE, getCharge(stack)+obsidian); 332 333 } 334}