Unicopia/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java

235 lines
8.6 KiB
Java
Raw Normal View History

package com.minelittlepony.unicopia.block;
import java.util.*;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.EarthPonyKickAbility.Buckable;
import com.minelittlepony.unicopia.compat.seasons.FertilizableUtil;
2023-12-30 11:55:26 +01:00
import com.minelittlepony.unicopia.util.CodecUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
2022-09-25 15:39:07 +02:00
import net.fabricmc.fabric.api.registry.FlammableBlockRegistry;
import net.minecraft.block.*;
import net.minecraft.item.ItemStack;
2023-12-30 11:55:26 +01:00
import net.minecraft.registry.Registries;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.*;
import net.minecraft.state.property.Properties;
import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.*;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.*;
import net.minecraft.world.event.GameEvent;
public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Buckable, Fertilizable {
2023-12-30 11:55:26 +01:00
public static final MapCodec<FruitBearingBlock> CODEC = RecordCodecBuilder.<FruitBearingBlock>mapCodec(instance -> instance.group(
Codec.INT.fieldOf("overlay").forGetter(b -> b.overlay),
CodecUtils.supplierOf(Registries.BLOCK.getCodec()).fieldOf("fruit").forGetter(b -> b.fruit),
CodecUtils.supplierOf(ItemStack.CODEC).fieldOf("rotten_fruit").forGetter(b -> b.rottenFruitSupplier),
BedBlock.createSettingsCodec()
).apply(instance, FruitBearingBlock::new));
public static final IntProperty AGE = Properties.AGE_25;
public static final int MAX_AGE = 25;
public static final EnumProperty<Stage> STAGE = EnumProperty.of("stage", Stage.class);
public static final List<FruitBearingBlock> REGISTRY = new ArrayList<>();
protected final Supplier<Block> fruit;
protected final Supplier<ItemStack> rottenFruitSupplier;
protected final int overlay;
2023-12-30 11:55:26 +01:00
public FruitBearingBlock(int overlay, Supplier<Block> fruit, Supplier<ItemStack> rottenFruitSupplier, Settings settings) {
super(settings
.ticksRandomly()
.nonOpaque()
2023-05-25 20:02:08 +02:00
.allowsSpawning(BlockConstructionUtils::canSpawnOnLeaves)
.suffocates(BlockConstructionUtils::never)
.blockVision(BlockConstructionUtils::never));
setDefaultState(getDefaultState().with(AGE, 0).with(STAGE, Stage.IDLE));
this.overlay = overlay;
this.fruit = fruit;
this.rottenFruitSupplier = rottenFruitSupplier;
2022-09-25 15:39:07 +02:00
FlammableBlockRegistry.getDefaultInstance().add(this, 30, 60);
}
2023-12-30 11:55:26 +01:00
@Override
public MapCodec<? extends FruitBearingBlock> getCodec() {
return CODEC;
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder);
builder.add(STAGE).add(AGE);
}
@Override
public boolean hasRandomTicks(BlockState state) {
return true;
}
2024-01-18 20:52:59 +01:00
protected boolean shouldAdvance(Random random) {
return true;
}
public BlockState getPlacedFruitState(Random random) {
2024-01-18 20:52:59 +01:00
return fruit.get().getDefaultState();
}
@Override
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
super.randomTick(state, world, pos, random);
2022-09-25 15:39:07 +02:00
if (shouldDecay(state) || state.get(PERSISTENT)) {
return;
}
if (world.getBaseLightLevel(pos, 0) > 8) {
2023-09-08 14:02:42 +02:00
int steps = FertilizableUtil.getGrowthSteps(world, pos, state, random);
while (steps-- > 0) {
2024-01-18 20:52:59 +01:00
if (!shouldAdvance(random)) {
continue;
}
state = cycleStage(state);
BlockPos fruitPosition = pos.down();
BlockState fruitState = world.getBlockState(fruitPosition);
switch (state.get(STAGE)) {
case WITHERING:
wither(state, world, pos, fruitPosition, world.getBlockState(fruitPosition));
case BEARING:
if (!fruitState.isOf(fruit.get())) {
state = withStage(state, Stage.IDLE);
}
break;
case FRUITING: {
if (!isPositionValidForFruit(state, pos)) {
state = withStage(state, Stage.IDLE);
} else {
state = grow(state, world, pos, fruitPosition, fruitState, random);
}
break;
2023-09-08 14:02:42 +02:00
}
default:
}
2023-09-08 14:02:42 +02:00
world.setBlockState(pos, state, Block.NOTIFY_ALL);
}
}
}
protected BlockState withStage(BlockState state, Stage stage) {
return state.with(AGE, 0).with(STAGE, stage);
}
private BlockState cycleStage(BlockState state) {
state = state.cycle(AGE);
if (state.get(AGE) == 0) {
state = state.cycle(STAGE);
}
return state;
}
protected BlockState grow(BlockState state, World world, BlockPos pos, BlockPos fruitPosition, BlockState fruitState, Random random) {
if (world.isAir(fruitPosition)) {
world.setBlockState(fruitPosition, getPlacedFruitState(random), Block.NOTIFY_ALL);
return withStage(state, Stage.BEARING);
}
if (!fruitState.isOf(fruit.get())) {
return withStage(state, Stage.IDLE);
}
return state;
}
protected void wither(BlockState state, World world, BlockPos pos, BlockPos fruitPosition, BlockState fruitState) {
if (!fruitState.isOf(fruit.get())) {
if (world.random.nextInt(2) == 0) {
Block.dropStack(world, fruitPosition, rottenFruitSupplier.get());
} else {
Block.dropStacks(fruitState, world, fruitPosition, fruitState.hasBlockEntity() ? world.getBlockEntity(fruitPosition) : null, null, ItemStack.EMPTY);
}
if (world.removeBlock(fruitPosition, false)) {
world.emitGameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Emitter.of(fruitState));
}
BlockSoundGroup group = getSoundGroup(state);
world.playSound(null, pos, USounds.ITEM_APPLE_ROT, SoundCategory.BLOCKS, group.getVolume(), group.getPitch());
}
}
@Override
public List<ItemStack> onBucked(ServerWorld world, BlockState state, BlockPos pos) {
world.setBlockState(pos, state.with(STAGE, Stage.IDLE).with(AGE, 0));
pos = pos.down();
state = world.getBlockState(pos);
if (state.isOf(fruit.get()) && state.getBlock() instanceof Buckable buckable) {
return buckable.onBucked(world, state, pos);
}
return List.of();
}
@Override
public int getTint(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int foliageColor) {
return TintedBlock.blend(foliageColor, overlay);
}
public boolean isPositionValidForFruit(BlockState state, BlockPos pos) {
return state.getRenderingSeed(pos) % 3 == 1;
}
@Override
2024-03-20 22:41:51 +01:00
public boolean isFertilizable(WorldView world, BlockPos pos, BlockState state) {
return switch (state.get(STAGE)) {
case FLOWERING -> world.isAir(pos.down());
default -> !world.getBlockState(pos.down()).isOf(fruit.get());
};
}
@Override
public boolean canGrow(World world, Random random, BlockPos pos, BlockState state) {
2024-03-20 22:41:51 +01:00
return isFertilizable(world, pos, state);
}
@Override
public void grow(ServerWorld world, Random random, BlockPos pos, BlockState state) {
state = state.cycle(AGE);
if (state.get(AGE) == 0) {
state = state.with(STAGE, switch (state.get(STAGE)) {
case IDLE -> Stage.FLOWERING;
case FLOWERING -> Stage.FRUITING;
default -> Stage.FLOWERING;
});
}
if (state.get(STAGE) == Stage.FRUITING && state.get(AGE) == 0) {
state = grow(state, world, pos, pos.down(), world.getBlockState(pos.down()), random);
}
world.setBlockState(pos, state);
}
public enum Stage implements StringIdentifiable {
IDLE,
FLOWERING,
FRUITING,
BEARING,
WITHERING;
@Override
public String asString() {
return name().toLowerCase(Locale.ROOT);
}
}
}