package wehavecookies56.bonfires.blocks; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.core.component.DataComponents; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.neoforged.neoforge.server.ServerLifecycleHooks; import wehavecookies56.bonfires.BonfiresConfig; import wehavecookies56.bonfires.LocalStrings; import wehavecookies56.bonfires.bonfire.Bonfire; import wehavecookies56.bonfires.bonfire.BonfireRegistry; import wehavecookies56.bonfires.data.BonfireHandler; import wehavecookies56.bonfires.data.DiscoveryHandler; import wehavecookies56.bonfires.data.EstusHandler; import wehavecookies56.bonfires.items.EstusFlaskItem; import wehavecookies56.bonfires.packets.PacketHandler; import wehavecookies56.bonfires.packets.client.*; import wehavecookies56.bonfires.packets.server.LightBonfire; import wehavecookies56.bonfires.setup.BlockSetup; import wehavecookies56.bonfires.setup.ComponentSetup; import wehavecookies56.bonfires.setup.EntitySetup; import wehavecookies56.bonfires.setup.ItemSetup; import wehavecookies56.bonfires.tiles.BonfireTileEntity; import wehavecookies56.bonfires.world.BonfireTeleporter; import java.util.List; import java.util.Optional; import javax.annotation.Nullable; public class AshBonePileBlock extends Block implements EntityBlock { public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; public static final BooleanProperty LIT = BooleanProperty.create("lit"); public static final BooleanProperty EXPLODED = BooleanProperty.create("exploded"); public AshBonePileBlock() { super(BlockBehaviour.Properties.of().sound(SoundType.SAND).noOcclusion().strength(0.8F).lightLevel(AshBonePileBlock::getLightValue)); registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH).setValue(LIT, false).setValue(EXPLODED, false)); } @Override public float getShadeBrightness(BlockState p_220080_1_, BlockGetter p_220080_2_, BlockPos p_220080_3_) { return 1; } @Nullable @Override public BlockState getStateForPlacement(BlockPlaceContext context) { return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(EXPLODED, false); } @Override public Optional getRespawnPosition(BlockState state, EntityType type, LevelReader levelReader, BlockPos pos, float orientation) { return Optional.of(BonfireTeleporter.attemptToPlaceNextToBonfire(pos, (Level) levelReader)); } @Override protected void createBlockStateDefinition(StateDefinition.Builder stateBuilder) { super.createBlockStateDefinition(stateBuilder); stateBuilder.add(FACING, LIT, EXPLODED); } @SuppressWarnings("deprecation") @Override public RenderShape getRenderShape(BlockState state) { return RenderShape.MODEL; } private void repair(ItemStack stack) { if (stack.has(DataComponents.DAMAGE)) { stack.set(DataComponents.DAMAGE, 0); } } @SuppressWarnings("deprecation") @Override public ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { if (world.getBlockEntity(pos) instanceof BonfireTileEntity te) { if (te.isBonfire()) { if (!te.isLit()) { if (!world.isClientSide) { if (BonfireHandler.getServerHandler(world.getServer()).getRegistry().getBonfire(te.getID()) == null) { if (!te.hasUnlitName()) { PacketHandler.sendTo(new OpenCreateScreen(te), (ServerPlayer) player); world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS); } return ItemInteractionResult.SUCCESS; } } else { if (te.hasUnlitName()) { PacketHandler.sendToServer(new LightBonfire(te.getUnlitName(), te, !te.isUnlitPrivate(), BonfiresConfig.Client.enableAutomaticScreenshotOnCreation)); } return ItemInteractionResult.SUCCESS; } } else { if (!world.isClientSide) { if (te.hasUnlitName()) { te.setUnlitName(""); return ItemInteractionResult.SUCCESS; } if (BonfiresConfig.Common.bonfireMonsterCheckRadius > 0.0) { Vec3 vec3 = Vec3.atBottomCenterOf(new Vec3i(pos.getX(), pos.getY(), pos.getZ())); double r = BonfiresConfig.Common.bonfireMonsterCheckRadius; List list = world.getEntitiesOfClass(Monster.class, new AABB(vec3.x() - r, vec3.y() - r, vec3.z() - r, vec3.x() + r, vec3.y() + r, vec3.z() + r), p_9062_ -> p_9062_.isPreventingPlayerRest(player)); if (!list.isEmpty()) { player.sendSystemMessage(Component.translatable(LocalStrings.TEXT_ENEMY_NEARBY)); return ItemInteractionResult.SUCCESS; } } if (BonfiresConfig.Common.repairEquipment) { player.getInventory().items.forEach(this::repair); player.getInventory().armor.forEach(this::repair); player.getInventory().offhand.forEach(this::repair); } DiscoveryHandler.IDiscoveryHandler discoveryHandler = DiscoveryHandler.getHandler(player); BonfireRegistry registry = BonfireHandler.getServerHandler(world.getServer()).getRegistry(); if (registry.getBonfire(te.getID()).isPublic() || registry.getBonfire(te.getID()).getOwner().equals(player.getUUID())) { discoveryHandler.discover(te.getID()); } if (BonfiresConfig.Common.bonfireDiscoveryMode) { registry = registry.getFilteredRegistry(discoveryHandler.getDiscovered().keySet().stream().toList()); } if (registry.getBonfire(te.getID()) != null) { for (int i = 0; i < player.getInventory().items.size(); i++) { if (!player.getInventory().getItem(i).isEmpty()) { if (player.getInventory().getItem(i).getItem() == ItemSetup.estus_flask.get()) { if (player.getInventory().getItem(i).has(ComponentSetup.ESTUS)) { EstusFlaskItem.Estus estus = player.getInventory().getItem(i).get(ComponentSetup.ESTUS); player.getInventory().getItem(i).set(ComponentSetup.ESTUS, new EstusFlaskItem.Estus(estus.maxUses(), estus.maxUses())); } } } } PacketHandler.sendTo(new OpenBonfireGUI(te, BonfireRegistry.getOwnerNames(world.getServer()), registry, BonfiresConfig.Common.enableReinforcing), (ServerPlayer) player); player.heal(player.getMaxHealth()); if (!BonfiresConfig.Common.disableBonfireRespawn) { ((ServerPlayer) player).setRespawnPosition(te.getLevel().dimension(), te.getBlockPos(), player.getYRot(), false, true); } EstusHandler.getHandler(player).setLastRested(te.getID()); PacketHandler.sendTo(new SyncEstusData(EstusHandler.getHandler(player)), (ServerPlayer) player); PacketHandler.sendTo(new SyncDiscoveryData(DiscoveryHandler.getHandler(player), player), (ServerPlayer) player); } else { //Bonfire lit but not in data, so should not be lit te.setLit(false); } world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS); } return ItemInteractionResult.SUCCESS; } } else { if (stack != ItemStack.EMPTY) { if (stack.getItem() == ItemSetup.coiled_sword.get()) { placeItem(world, te, pos, player, hand, BonfireTileEntity.BonfireType.BONFIRE); }/*else if (player.getHeldItemMainhand().getItem() == Bonfires.coiledSwordFragment) { placeItem(world, te, pos, playerIn, TileEntityBonfire.BonfireType.PRIMAL); }*/ world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS); return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } } } return super.useItemOn(stack, state, world, pos, player, hand, hit); } public record BonfireData(String name, boolean privateBonfire) { public static final Codec CODEC = RecordCodecBuilder.create( bonfireDataInstance -> bonfireDataInstance.group( Codec.STRING.fieldOf("bonfire_name").forGetter(BonfireData::name), Codec.BOOL.fieldOf("bonfire_private").forGetter(BonfireData::privateBonfire) ).apply(bonfireDataInstance, BonfireData::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.STRING_UTF8, BonfireData::name, ByteBufCodecs.BOOL, BonfireData::privateBonfire, BonfireData::new ); } @Override public void setPlacedBy(Level pLevel, BlockPos pPos, BlockState pState, @Nullable LivingEntity pPlacer, ItemStack pStack) { if (pStack.has(ComponentSetup.BONFIRE_DATA)) { BonfireData bonfireData = pStack.get(ComponentSetup.BONFIRE_DATA); pLevel.setBlock(pPos, pState, 2); BonfireTileEntity te = new BonfireTileEntity(pPos, pState); te.setBonfire(true); te.setLit(false); te.setBonfireType(BonfireTileEntity.BonfireType.BONFIRE); if (bonfireData.name != null) { te.setUnlitName(bonfireData.name); te.setUnlitPrivate(bonfireData.privateBonfire); } pLevel.setBlockEntity(te); } super.setPlacedBy(pLevel, pPos, pState, pPlacer, pStack); } public void placeItem(Level world, BonfireTileEntity te, BlockPos pos, Player player, InteractionHand hand, BonfireTileEntity.BonfireType type) { if (!world.isClientSide) { if (!player.isCreative()) player.setItemInHand(hand, ItemStack.EMPTY); world.playSound(null, pos, SoundEvents.ANVIL_PLACE, SoundSource.BLOCKS, 1, 1); te.setBonfire(true); te.setLit(false); te.setBonfireType(type); PacketHandler.sendToAll(new SyncBonfire(te.isBonfire(), type, te.isLit(), null, te)); } else { world.playSound(player, pos, SoundEvents.ANVIL_PLACE, SoundSource.BLOCKS, 1, 1); } } public static int getLightValue(BlockState state) { if (state.getValue(LIT)) { return 8; } else { return 0; } } @Override public boolean isPossibleToRespawnInThis(BlockState p_279289_) { return true; } @Override public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moving) { if (newState.getBlock() != this) { if (!world.isClientSide()) { BonfireTileEntity te = (BonfireTileEntity) world.getBlockEntity(pos); if (te != null) { if (te.isBonfire()) { if (!state.getValue(EXPLODED)) { ItemEntity fragment = new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(ItemSetup.coiled_sword_fragment.get())); world.addFreshEntity(fragment); } } if (te.isLit()) { MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); Bonfire destroyed = BonfireHandler.getServerHandler(server).getRegistry().getBonfire(te.getID()); te.destroyBonfire(te.getID()); BonfireHandler.getServerHandler(server).removeBonfire(te.getID()); if (BonfiresConfig.Common.bonfireDiscoveryMode) { server.getPlayerList().getPlayers().forEach(serverPlayer -> { PacketHandler.sendTo(new SendBonfiresToClient(serverPlayer), serverPlayer); }); } else { PacketHandler.sendToAll(new SendBonfiresToClient()); } PacketHandler.sendToAll(new DeleteScreenshot(te.getID(), destroyed.getName())); } } } } super.onRemove(state, world, pos, newState, moving); } @Override public void onBlockExploded(BlockState state, Level world, BlockPos pos, Explosion explosion) { if (BonfiresConfig.Common.enableUBSBonfire) { if (!world.isClientSide) { ItemEntity shard = new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(ItemSetup.undead_bone_shard.get())); world.addFreshEntity(shard); state = state.setValue(EXPLODED, true); world.setBlock(pos, state, 3); } } super.onBlockExploded(state, world, pos, explosion); } @Override public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return getShape(state, world, pos, context); } @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { VoxelShape base = Shapes.join(Block.box(3.0D, 0.0D, 3.0D, 13.0D, 1.0, 13.0D), Block.box(4.0D, 1.0D, 4.0D, 12.0D, 2.0, 12.0D), BooleanOp.OR); VoxelShape sword = Block.box(5, 2, 5, 11, 20, 11); VoxelShape combined = Shapes.join(base, sword, BooleanOp.OR); if (world.getBlockEntity(pos) != null) { if (world.getBlockEntity(pos) instanceof BonfireTileEntity) { if (((BonfireTileEntity)world.getBlockEntity(pos)).isBonfire()) { return combined; } } } return base; } @Override public boolean hasDynamicShape() { return true; } @Override public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource random) { if (world.getBlockEntity(pos) != null) { BonfireTileEntity bonfire = (BonfireTileEntity) world.getBlockEntity(pos); if (bonfire.isLit()) { double d0 = (double)pos.getX() + 0.5D + random.nextDouble() * 3.0D / 16.0D; double d1 = (double)pos.getY() + 0.2D; double d2 = (double)pos.getZ() + 0.5D + random.nextDouble() * 1.0D / 16.0D; double d3 = 0.52D; double d4 = random.nextDouble() * 0.6D - 0.3D; double d5 = random.nextDouble() * 0.6D - 0.3D; ResourceLocation sound = null; if (!BonfiresConfig.Client.bonfireAmbientSound.isEmpty()) { sound = ResourceLocation.tryParse(BonfiresConfig.Client.bonfireAmbientSound); } if (sound != null) { if (random.nextDouble() < 0.1D) { world.playLocalSound((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D, SoundEvent.createVariableRangeEvent(sound), SoundSource.BLOCKS, 0.5F, 1.0F, false); } } //worldIn.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, d0, d1, d2 + d4, 0.0D, 0.0D, 0.0D, new int[0]); if (!BonfiresConfig.Client.disableBonfireParticles) { world.addParticle(ParticleTypes.FLAME, d0 + d5, d1, d2 + d4, 0.0D, 0.05D, 0.0D); world.addParticle(ParticleTypes.FLAME, d0 + d5, d1, d2 + d4, 0.0D, 0.0D, 0.0D); } } } super.animateTick(state, world, pos, random); } @Override public ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, Player player) { if (level.getBlockEntity(pos) instanceof BonfireTileEntity te) { ItemStack stack = new ItemStack(BlockSetup.ash_bone_pile.get()); if (!player.isCrouching() && te.isBonfire()) { stack.set(ComponentSetup.BONFIRE_DATA, new BonfireData("", false)); } else if (te.isLit()) { Bonfire bonfire = BonfireHandler.getServerHandler(ServerLifecycleHooks.getCurrentServer()).getRegistry().getBonfire(te.getID()); if (bonfire != null) { stack.set(ComponentSetup.BONFIRE_DATA, new BonfireData(bonfire.getName(), !bonfire.isPublic())); } } else if (te.hasUnlitName()) { stack.set(ComponentSetup.BONFIRE_DATA, new BonfireData(te.getUnlitName(), te.isUnlitPrivate())); } return stack; } return super.getCloneItemStack(state, target, level, pos, player); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { return EntitySetup.BONFIRE.get().create(pPos, pState); } @Override public void appendHoverText(ItemStack pStack, Item.TooltipContext context, List pTooltip, TooltipFlag pFlag) { super.appendHoverText(pStack, context, pTooltip, pFlag); if(pStack.has(ComponentSetup.BONFIRE_DATA)) { BonfireData bonfireData = pStack.get(ComponentSetup.BONFIRE_DATA); MutableComponent text = Component.translatable(LocalStrings.TOOLTIP_UNLIT); if (bonfireData.name != null) { text.append(" "); text.append(Component.translatable(bonfireData.name)); } if (bonfireData.privateBonfire) { text.append(" "); text.append(Component.translatable(LocalStrings.TEXT_PRIVATE)); } pTooltip.add(text); } } }