Skip to content

Commit

Permalink
Hot Cocoa (almost complete)
Browse files Browse the repository at this point in the history
  • Loading branch information
crispytwig committed Dec 2, 2024
1 parent 1ec3777 commit 84a6590
Show file tree
Hide file tree
Showing 53 changed files with 620 additions and 281 deletions.
8 changes: 7 additions & 1 deletion src/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@
- Items can be sheared off of Snow Golems, and will prioritize head items over scarves.


2. **Snowballs** now have sounds when they hit things.
2. **Snowballs** now have sounds when they hit things.
- They also have a 20% chance to place Snow Layers on the ground when they hit a block.

### 3. Cauldrons
- Powder Snow Cauldrons can now be right-clicked with any item other than a Bucket to empty them layer by layer and collect Snowballs.
- Water Cauldrons above heat sources can have Chocolate thrown into them to become a Hot Cocoa Cauldron.
- Right-clicking on Hot Cocoa Cauldrons with Bottles will give up to 3 Hot Cocoa Bottles, which can be drank to give the player the "Cozy" effect, temporarily making them immune to Freezing.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.starfish_studios.seasons_greetings;

import com.starfish_studios.seasons_greetings.client.SeasonsGreetingsClient;
import com.starfish_studios.seasons_greetings.event.SnowCauldronUseEvent;
import com.starfish_studios.seasons_greetings.registry.SGRegistry;
import net.fabricmc.api.ModInitializer;

import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -26,5 +28,8 @@ public void onInitialize() {
SGRegistry.registerAll();

LOGGER.info("Hello Fabric world!");


UseBlockCallback.EVENT.register(new SnowCauldronUseEvent());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.starfish_studios.seasons_greetings.block;

import com.mojang.serialization.MapCodec;
import com.starfish_studios.seasons_greetings.registry.*;
import net.minecraft.core.BlockPos;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.AbstractCauldronBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LayeredCauldronBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.NotNull;

public class HotCocoaCauldronBlock extends AbstractCauldronBlock {

@Override
protected MapCodec<? extends AbstractCauldronBlock> codec() {
return null;
}

public HotCocoaCauldronBlock(Properties properties, CauldronInteraction.InteractionMap interactionMap) {
super(properties, interactionMap);
this.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3);
}

protected @NotNull InteractionResult useWithoutItem(BlockState blockState, Level level, BlockPos blockPos, Player player, BlockHitResult blockHitResult) {
return InteractionResult.PASS;
}

@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> stateDefinition) {
stateDefinition.add(LayeredCauldronBlock.LEVEL);
}

public void animateTick(BlockState blockState, Level level, BlockPos blockPos, RandomSource randomSource) {
if (blockState.is(SGBlocks.HOT_COCOA_CAULDRON) && level.getBlockState(blockPos.below()).is(SGTags.SGBlockTags.HEAT_SOURCES)) {

if (blockState.getValue(LayeredCauldronBlock.LEVEL) == 3) {
for (int i = 0; i < 2; ++i) {
double x = (double) blockPos.getX() + 0.3 + ((level.random.nextDouble() * 0.8D) - 0.2D);
double y = (double) blockPos.getY() + 1 + (level.random.nextDouble() * 0.2D - 0.1D);
double z = (double) blockPos.getZ() + 0.3 + ((level.random.nextDouble() * 0.8D) - 0.2D);

level.addParticle(SGParticles.COCOA_BUBBLE, x, y, z, 0.0D, 0.0D, 0.0D);
}
} else if (blockState.getValue(LayeredCauldronBlock.LEVEL) == 2) {
for (int i = 0; i < 2; ++i) {
double x = (double) blockPos.getX() + 0.3 + ((level.random.nextDouble() * 0.8D) - 0.2D);
double y = (double) blockPos.getY() + 0.8 + (level.random.nextDouble() * 0.2D - 0.1D);
double z = (double) blockPos.getZ() + 0.3 + ((level.random.nextDouble() * 0.8D) - 0.2D);

level.addParticle(SGParticles.COCOA_BUBBLE, x, y, z, 0.0D, 0.0D, 0.0D);
}
} else if (blockState.getValue(LayeredCauldronBlock.LEVEL) == 1) {
for (int i = 0; i < 2; ++i) {
double x = (double) blockPos.getX() + 0.3 + ((level.random.nextDouble() * 0.8D) - 0.2D);
double y = (double) blockPos.getY() + 0.6 + (level.random.nextDouble() * 0.2D - 0.1D);
double z = (double) blockPos.getZ() + 0.3 + ((level.random.nextDouble() * 0.8D) - 0.2D);

level.addParticle(SGParticles.COCOA_BUBBLE, x, y, z, 0.0D, 0.0D, 0.0D);
}
}


if (randomSource.nextInt(10) == 0) {
level.playLocalSound((double) blockPos.getX() + 0.5, (double) blockPos.getY() + 0.5, (double) blockPos.getZ() + 0.5, SGSoundEvents.HOT_COCOA_BUBBLE, SoundSource.BLOCKS,
0.3F, 1.0F + level.random.nextFloat() * 0.2F, false);
}
}

}

protected @NotNull ItemInteractionResult useItemOn(ItemStack itemStack, BlockState blockState, Level level, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult) {
if (itemStack.is(Items.GLASS_BOTTLE)) {
if (blockState.getValue(LayeredCauldronBlock.LEVEL) == 1) {
level.setBlockAndUpdate(blockPos, Blocks.CAULDRON.defaultBlockState());
if (!player.getInventory().add(new ItemStack(SGItems.HOT_COCOA_BOTTLE))) {
player.drop(new ItemStack(SGItems.HOT_COCOA_BOTTLE), false);
}
player.playSound(SoundEvents.BOTTLE_FILL, 1.0F, 1.0F);
return ItemInteractionResult.SUCCESS;
} else if (blockState.getValue(LayeredCauldronBlock.LEVEL) > 1) {
level.setBlockAndUpdate(blockPos, blockState.setValue(LayeredCauldronBlock.LEVEL, blockState.getValue(LayeredCauldronBlock.LEVEL) - 1));
if (!player.getInventory().add(new ItemStack(SGItems.HOT_COCOA_BOTTLE))) {
player.drop(new ItemStack(SGItems.HOT_COCOA_BOTTLE), false);
}
player.playSound(SoundEvents.BOTTLE_FILL, 1.0F, 1.0F);
return ItemInteractionResult.SUCCESS;
}
}

return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}

@Override
public boolean isFull(BlockState blockState) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,225 +1,35 @@
package com.starfish_studios.seasons_greetings.block;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.mojang.serialization.MapCodec;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.*;
import net.minecraft.world.level.block.GlowLichenBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Iterator;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class WrappedBlock extends Block {

public static final MapCodec<WrappedBlock> CODEC = simpleCodec(WrappedBlock::new);
public static final BooleanProperty NORTH = PipeBlock.NORTH;
public static final BooleanProperty EAST = PipeBlock.EAST;
public static final BooleanProperty SOUTH = PipeBlock.SOUTH;
public static final BooleanProperty WEST = PipeBlock.WEST;
public static final Map<Direction, BooleanProperty> PROPERTY_BY_DIRECTION = ImmutableMap.copyOf((Map) Util.make(Maps.newEnumMap(Direction.class), (enumMap) -> {
enumMap.put(Direction.NORTH, NORTH);
enumMap.put(Direction.EAST, EAST);
enumMap.put(Direction.SOUTH, SOUTH);
enumMap.put(Direction.WEST, WEST);
}));
private static final VoxelShape WEST_AABB = Block.box(0.0, 0.0, 0.0, 1.0, 16.0, 16.0);
private static final VoxelShape EAST_AABB = Block.box(15.0, 0.0, 0.0, 16.0, 16.0, 16.0);
private static final VoxelShape NORTH_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 1.0);
private static final VoxelShape SOUTH_AABB = Block.box(0.0, 0.0, 15.0, 16.0, 16.0, 16.0);
private final Map<BlockState, VoxelShape> shapesCache;
public class WrappedBlock extends GlowLichenBlock {

public WrappedBlock(Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any()
.setValue(NORTH, false)
.setValue(EAST, false)
.setValue(SOUTH, false)
.setValue(WEST, false));
this.shapesCache = ImmutableMap.copyOf((Map)this.stateDefinition.getPossibleStates().stream().collect(Collectors.toMap(Function.identity(), WrappedBlock::calculateShape)));
}

private static VoxelShape calculateShape(BlockState blockState) {
VoxelShape voxelShape = Shapes.empty();

if (blockState.getValue(NORTH)) {
voxelShape = Shapes.or(voxelShape, NORTH_AABB);
}

if (blockState.getValue(SOUTH)) {
voxelShape = Shapes.or(voxelShape, SOUTH_AABB);
}

if (blockState.getValue(EAST)) {
voxelShape = Shapes.or(voxelShape, EAST_AABB);
}

if (blockState.getValue(WEST)) {
voxelShape = Shapes.or(voxelShape, WEST_AABB);
}

return voxelShape.isEmpty() ? Shapes.block() : voxelShape;
}

protected @NotNull VoxelShape getShape(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext) {
return this.shapesCache.get(blockState);
}

protected boolean propagatesSkylightDown(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos) {
return true;
}

protected boolean canSurvive(BlockState blockState, LevelReader levelReader, BlockPos blockPos) {
return this.hasFaces(this.getUpdatedState(blockState, levelReader, blockPos));
}

private boolean hasFaces(BlockState blockState) {
return this.countFaces(blockState) > 0;
}

private int countFaces(BlockState blockState) {
int i = 0;
for (BooleanProperty booleanProperty : PROPERTY_BY_DIRECTION.values()) {
if (blockState.getValue(booleanProperty)) {
++i;
}
}

return i;
}

private boolean canSupportAtFace(BlockGetter blockGetter, BlockPos blockPos, Direction direction) {
BlockPos blockPos2 = blockPos.relative(direction);
if (isAcceptableNeighbour(blockGetter, blockPos2, direction)) {
return true;
} else if (direction.getAxis() == Direction.Axis.Y) {
return false;
} else {
BooleanProperty booleanProperty = PROPERTY_BY_DIRECTION.get(direction);
BlockState blockState = blockGetter.getBlockState(blockPos.above());
return blockState.is(this) && blockState.getValue(booleanProperty);
}
}

public static boolean isAcceptableNeighbour(BlockGetter blockGetter, BlockPos blockPos, Direction direction) {
return MultifaceBlock.canAttachTo(blockGetter, direction, blockPos, blockGetter.getBlockState(blockPos));
}

private BlockState getUpdatedState(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos) {
BlockPos blockPos2 = blockPos.above();

BlockState blockState2 = null;
Iterator<Direction> var6 = Direction.Plane.HORIZONTAL.iterator();

while(true) {
Direction direction;
BooleanProperty booleanProperty;
do {
if (!var6.hasNext()) {
return blockState;
}

direction = var6.next();
booleanProperty = getPropertyForFace(direction);
} while(!(Boolean)blockState.getValue(booleanProperty));

boolean bl = this.canSupportAtFace(blockGetter, blockPos, direction);
if (!bl) {
if (blockState2 == null) {
blockState2 = blockGetter.getBlockState(blockPos2);
}

bl = blockState2.is(this) && blockState2.getValue(booleanProperty);
}

blockState = blockState.setValue(booleanProperty, bl);
}
}

protected @NotNull BlockState updateShape(BlockState blockState, Direction direction, BlockState blockState2, LevelAccessor levelAccessor, BlockPos blockPos, BlockPos blockPos2) {
BlockState blockState3 = this.getUpdatedState(blockState, levelAccessor, blockPos);
return !this.hasFaces(blockState3) ? Blocks.AIR.defaultBlockState() : blockState3;
}

@Override
protected boolean canBeReplaced(BlockState blockState, BlockPlaceContext blockPlaceContext) {
BlockState blockState2 = blockPlaceContext.getLevel().getBlockState(blockPlaceContext.getClickedPos());
if (blockState2.is(this)) {
return this.countFaces(blockState2) < PROPERTY_BY_DIRECTION.size();
} else {
return super.canBeReplaced(blockState, blockPlaceContext);
}
}

@Nullable
public BlockState getStateForPlacement(BlockPlaceContext blockPlaceContext) {
BlockState blockState = blockPlaceContext.getLevel().getBlockState(blockPlaceContext.getClickedPos());
boolean bl = blockState.is(this);
BlockState blockState2 = super.getStateForPlacement(blockPlaceContext);
if (blockState2 != null && !blockState2.is(Blocks.AIR)) {
blockState2 = blockState2.setValue(NORTH, this.canSupportAtFace(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos(), Direction.NORTH));
blockState2 = blockState2.setValue(EAST, this.canSupportAtFace(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos(), Direction.EAST));
blockState2 = blockState2.setValue(SOUTH, this.canSupportAtFace(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos(), Direction.SOUTH));
blockState2 = blockState2.setValue(WEST, this.canSupportAtFace(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos(), Direction.WEST));
return blockState2;
} else {
return null;
}
}

protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(NORTH, EAST, SOUTH, WEST);
return blockPlaceContext.getItemInHand().getItem() == this.asItem();
}

protected @NotNull BlockState rotate(BlockState blockState, Rotation rotation) {
switch (rotation) {
case CLOCKWISE_180 -> {
return blockState.setValue(NORTH, blockState.getValue(SOUTH)).setValue(EAST, blockState.getValue(WEST)).setValue(SOUTH, blockState.getValue(NORTH)).setValue(WEST, blockState.getValue(EAST));
}
case COUNTERCLOCKWISE_90 -> {
return blockState.setValue(NORTH, blockState.getValue(EAST)).setValue(EAST, blockState.getValue(SOUTH)).setValue(SOUTH, blockState.getValue(WEST)).setValue(WEST, blockState.getValue(NORTH));
}
case CLOCKWISE_90 -> {
return blockState.setValue(NORTH, blockState.getValue(WEST)).setValue(EAST, blockState.getValue(NORTH)).setValue(SOUTH, blockState.getValue(EAST)).setValue(WEST, blockState.getValue(SOUTH));
}
default -> {
return blockState;
}
}
@Override
public boolean isValidBonemealTarget(LevelReader levelReader, BlockPos blockPos, BlockState blockState) {
return false;
}

protected @NotNull BlockState mirror(BlockState blockState, Mirror mirror) {
switch (mirror) {
case LEFT_RIGHT -> {
return blockState.setValue(NORTH, blockState.getValue(SOUTH)).setValue(SOUTH, blockState.getValue(NORTH));
}
case FRONT_BACK -> {
return blockState.setValue(EAST, blockState.getValue(WEST)).setValue(WEST, blockState.getValue(EAST));
}
default -> {
return super.mirror(blockState, mirror);
}
}
@Override
public boolean isBonemealSuccess(Level level, RandomSource randomSource, BlockPos blockPos, BlockState blockState) {
return false;
}

public static BooleanProperty getPropertyForFace(Direction direction) {
return PROPERTY_BY_DIRECTION.get(direction);
@Override
public void performBonemeal(ServerLevel serverLevel, RandomSource randomSource, BlockPos blockPos, BlockState blockState) {
}
}
Loading

0 comments on commit 84a6590

Please sign in to comment.