Skip to content

Instantly share code, notes, and snippets.

@Densamisten
Created September 10, 2024 10:28
Show Gist options
  • Save Densamisten/60cb7487b10043c53a85907598c3af31 to your computer and use it in GitHub Desktop.
Save Densamisten/60cb7487b10043c53a85907598c3af31 to your computer and use it in GitHub Desktop.
package exonihility.block;
import com.mojang.serialization.MapCodec;
import exonihility.effect.AllyshiptStatusEffects;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.block.*;
import net.minecraft.entity.Entity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.BiomeTags;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.IntProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.*;
import net.minecraft.world.dimension.NetherPortal;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class AmaterasuBlock extends AbstractFireBlock implements Waterloggable {
public static final MapCodec<AmaterasuBlock> CODEC = createCodec(AmaterasuBlock::new);
public static final IntProperty AGE;
public static final BooleanProperty NORTH;
public static final BooleanProperty EAST;
public static final BooleanProperty SOUTH;
public static final BooleanProperty WEST;
public static final BooleanProperty UP;
private static final Map<Direction, BooleanProperty> DIRECTION_PROPERTIES;
private static final VoxelShape UP_SHAPE;
private static final VoxelShape WEST_SHAPE;
private static final VoxelShape EAST_SHAPE;
private static final VoxelShape NORTH_SHAPE;
private static final VoxelShape SOUTH_SHAPE;
public static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED;
protected static final VoxelShape BASE_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 1.0, 16.0);
private final Map<BlockState, VoxelShape> shapesByState;
private final Object2IntMap<Block> burnChances = new Object2IntOpenHashMap<>();
private final Object2IntMap<Block> spreadChances = new Object2IntOpenHashMap<>();
public MapCodec<AmaterasuBlock> getCodec() {
return CODEC;
}
public AmaterasuBlock(AbstractBlock.Settings settings) {
super(settings, 1.0F);
this.setDefaultState(this.stateManager.getDefaultState().with(AGE, 0).with(NORTH, false).with(EAST, false).with(SOUTH, false).with(WEST, false).with(UP, false).with(WATERLOGGED, false));
this.shapesByState = this.stateManager.getStates().stream()
.filter((state) -> state.get(AGE) == 0)
.collect(Collectors.toMap(Function.identity(), AmaterasuBlock::getShapeForState));
}
private static VoxelShape getShapeForState(BlockState state) {
VoxelShape voxelShape = VoxelShapes.empty();
if (state.get(UP)) {
voxelShape = UP_SHAPE;
}
if (state.get(NORTH)) {
voxelShape = VoxelShapes.union(voxelShape, NORTH_SHAPE);
}
if (state.get(SOUTH)) {
voxelShape = VoxelShapes.union(voxelShape, SOUTH_SHAPE);
}
if (state.get(EAST)) {
voxelShape = VoxelShapes.union(voxelShape, EAST_SHAPE);
}
if (state.get(WEST)) {
voxelShape = VoxelShapes.union(voxelShape, WEST_SHAPE);
}
return voxelShape.isEmpty() ? BASE_SHAPE : voxelShape;
}
@Override
protected BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
if (state.get(WATERLOGGED)) {
world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
}
return this.canPlaceAt(state, world, pos) ? this.getStateWithAge(world, pos, state.get(AGE)) : Blocks.AIR.getDefaultState();
}
@Override
protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return this.shapesByState.get(state.with(AGE, 0));
}
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return this.getStateForPosition(ctx.getWorld(), ctx.getBlockPos())
.with(WATERLOGGED, ctx.getWorld().getFluidState(ctx.getBlockPos()).isOf(Fluids.WATER));
}
protected BlockState getStateForPosition(BlockView world, BlockPos pos) {
BlockPos blockPos = pos.down();
BlockState blockState = world.getBlockState(blockPos);
if (!this.isFlammable(blockState) && !blockState.isSideSolidFullSquare(world, blockPos, Direction.UP)) {
BlockState blockState2 = this.getDefaultState();
Direction[] var6 = Direction.values();
for (Direction direction : var6) {
BooleanProperty booleanProperty = DIRECTION_PROPERTIES.get(direction);
if (booleanProperty != null) {
blockState2 = blockState2.with(booleanProperty, this.isFlammable(world.getBlockState(pos.offset(direction))));
}
}
return blockState2;
} else {
return this.getDefaultState();
}
}
@Override
protected boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
BlockPos blockPos = pos.down();
BlockPos blockBelowPos = pos.down();
BlockState blockBelowState = world.getBlockState(blockBelowPos);
FluidState fluidState = world.getFluidState(blockBelowPos);
return world.getBlockState(blockPos).isSideSolidFullSquare(world, blockPos, Direction.UP) || this.areBlocksAroundFlammable(world, pos) || blockBelowState.getBlock() instanceof Waterloggable || fluidState.getFluid() == Fluids.WATER;
}
@Override
protected void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
world.scheduleBlockTick(pos, this, getFireTickDelay(world.random));
int i = state.get(AGE);
int j = Math.min(15, i + random.nextInt(3) / 2);
if (i != j) {
state = state.with(AGE, j);
world.setBlockState(pos, state, 4);
}
boolean bl2 = world.getBiome(pos).isIn(BiomeTags.INCREASED_FIRE_BURNOUT);
int k = bl2 ? -50 : 0;
this.trySpreadingFire(world, pos.east(), 1000 + k, random, i);
this.trySpreadingFire(world, pos.west(), 1000 + k, random, i);
this.trySpreadingFire(world, pos.down(), 1000 + k, random, i);
this.trySpreadingFire(world, pos.up(), 1000 + k, random, i);
this.trySpreadingFire(world, pos.north(), 1000 + k, random, i);
this.trySpreadingFire(world, pos.south(), 1000 + k, random, i);
BlockPos.Mutable mutable = new BlockPos.Mutable();
for (int l = -1; l <= 1; ++l) {
for (int m = -1; m <= 1; ++m) {
for (int n = -1; n <= 4; ++n) {
if (l != 0 || n != 0 || m != 0) {
int o = 100;
if (n > 1) {
o += (n - 1) * 100;
}
mutable.set(pos, l, n, m);
int p = this.getBurnChance(world, mutable);
if (p > 0) {
int q = (p + 40 + world.getDifficulty().getId() * 7) / (i + 30);
if (bl2) {
q /= 2;
}
if (q > 0 && random.nextInt(o) <= q) {
int r = Math.min(15, i + random.nextInt(5) / 4);
world.setBlockState(mutable, this.getStateWithAge(world, mutable, r), 3);
}
}
}
}
}
}
}
private int getSpreadChance(BlockState state) {
return this.spreadChances.getInt(state.getBlock());
}
private int getBurnChance(BlockState state) {
return this.burnChances.getInt(state.getBlock());
}
private void trySpreadingFire(World world, BlockPos pos, int spreadFactor, Random random, int currentAge) {
int i = this.getSpreadChance(world.getBlockState(pos));
if (random.nextInt(spreadFactor) < i) {
BlockState blockState = world.getBlockState(pos);
if (random.nextInt(currentAge + 10) < 5) {
int j = Math.min(currentAge + random.nextInt(5) / 4, 15);
world.setBlockState(pos, this.getStateWithAge(world, pos, j), 3);
}
Block block = blockState.getBlock();
if (block instanceof TntBlock) {
TntBlock.primeTnt(world, pos);
}
}
}
public static BlockState getState(BlockView world, BlockPos pos) {
BlockPos blockPos = pos.down();
BlockState blockState = world.getBlockState(blockPos);
return SoulFireBlock.isSoulBase(blockState) ? AllyshipBlocks.AMATERASU_BLOCK.getDefaultState() : ((AmaterasuBlock) AllyshipBlocks.AMATERASU_BLOCK).getStateForPosition(world, pos);
}
private BlockState getStateWithAge(WorldAccess world, BlockPos pos, int age) {
BlockState blockState = getState(world, pos);
return blockState.isOf(AllyshipBlocks.AMATERASU_BLOCK) ? blockState.with(AGE, age) : blockState;
}
private boolean areBlocksAroundFlammable(BlockView world, BlockPos pos) {
Direction[] var3 = Direction.values();
for (Direction direction : var3) {
if (this.isFlammable(world.getBlockState(pos.offset(direction)))) {
return true;
}
}
return false;
}
private int getBurnChance(WorldView world, BlockPos pos) {
if (!world.isAir(pos)) {
return 0;
} else {
int i = 0;
Direction[] var4 = Direction.values();
for (Direction direction : var4) {
BlockState blockState = world.getBlockState(pos.offset(direction));
i = Math.max(this.getBurnChance(blockState), i);
}
return i;
}
}
@Override
public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) {
if (random.nextInt(24) == 0) {
world.playSound((double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5, SoundEvents.BLOCK_FIRE_AMBIENT, SoundCategory.BLOCKS, 1.0F + random.nextFloat(), random.nextFloat() * 0.7F + 0.3F, false);
}
BlockPos blockPos = pos.down();
BlockState blockState = world.getBlockState(blockPos);
int i;
double d;
double e;
double f;
if (!this.isFlammable(blockState) && !blockState.isSideSolidFullSquare(world, blockPos, Direction.UP)) {
if (this.isFlammable(world.getBlockState(pos.west()))) {
// Custom black flame effect
for ( i = 0; i < 3; ++i) {
double x = pos.getX() + random.nextDouble();
double y = pos.getY() + random.nextDouble() * 0.5 + 0.5;
double z = pos.getZ() + random.nextDouble();
world.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 0.0, 0.0); // SMOKE instead of LARGE_SMOKE for a black appearance
}
}
if (this.isFlammable(world.getBlockState(pos.east()))) {
// Custom black flame effect
for (i = 0; i < 3; ++i) {
double x = pos.getX() + random.nextDouble();
double y = pos.getY() + random.nextDouble() * 0.5 + 0.5;
double z = pos.getZ() + random.nextDouble();
world.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 0.0, 0.0); // SMOKE instead of LARGE_SMOKE for a black appearance
}
}
if (this.isFlammable(world.getBlockState(pos.north()))) {
for (i = 0; i < 3; ++i) {
double x = pos.getX() + random.nextDouble();
double y = pos.getY() + random.nextDouble() * 0.5 + 0.5;
double z = pos.getZ() + random.nextDouble();
world.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 0.0, 0.0); // SMOKE instead of LARGE_SMOKE for a black appearance
}
}
if (this.isFlammable(world.getBlockState(pos.south()))) {
for (i = 0; i < 3; ++i) {
double x = pos.getX() + random.nextDouble();
double y = pos.getY() + random.nextDouble() * 0.5 + 0.5;
double z = pos.getZ() + random.nextDouble();
world.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 0.0, 0.0); // SMOKE instead of LARGE_SMOKE for a black appearance
}
}
if (this.isFlammable(world.getBlockState(pos.up()))) {
for (i = 0; i < 3; ++i) {
double x = pos.getX() + random.nextDouble();
double y = pos.getY() + random.nextDouble() * 0.5 + 0.5;
double z = pos.getZ() + random.nextDouble();
world.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 0.0, 0.0); // SMOKE instead of LARGE_SMOKE for a black appearance
}
}
} else {
for (i = 0; i < 3; ++i) {
double x = pos.getX() + random.nextDouble();
double y = pos.getY() + random.nextDouble() * 0.5 + 0.5;
double z = pos.getZ() + random.nextDouble();
world.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 0.0, 0.0); // SMOKE instead of LARGE_SMOKE for a black appearance
}
}
}
@Override
public void onSteppedOn(World world, BlockPos pos, BlockState state, Entity entity) {
if (entity instanceof PlayerEntity) {
((PlayerEntity) entity).addStatusEffect(new StatusEffectInstance(AllyshiptStatusEffects.AMATERASU, 20, 0));
}
super.onSteppedOn(world, pos, state, entity);
}
@Override
protected boolean isFlammable(BlockState state) {
return false;
}
@Override
protected void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
super.onBlockAdded(state, world, pos, oldState, notify);
world.scheduleBlockTick(pos, this, getFireTickDelay(world.random));
}
private static int getFireTickDelay(Random random) {
return 30 + random.nextInt(10);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(AGE, NORTH, EAST, SOUTH, WEST, UP, WATERLOGGED);
}
public void registerFlammableBlock(Block block, int burnChance, int spreadChance) {
this.burnChances.put(block, burnChance);
this.spreadChances.put(block, spreadChance);
}
public static void registerDefaultFlammables() {
AmaterasuBlock amaterasuBlock = (AmaterasuBlock) AllyshipBlocks.AMATERASU_BLOCK;
Registries.BLOCK.forEach(block -> amaterasuBlock.registerFlammableBlock(block, 100, 10));
}
public static boolean canPlaceAt(World world, BlockPos pos, Direction direction) {
BlockState blockState = world.getBlockState(pos);
if (!blockState.isAir()) {
return false;
} else {
return getState(world, pos).canPlaceAt(world, pos) || shouldLightPortalAt(world, pos, direction);
}
}
private static boolean isOverworldOrNether(World world) {
return world.getRegistryKey() == World.OVERWORLD || world.getRegistryKey() == World.NETHER;
}
private static boolean shouldLightPortalAt(World world, BlockPos pos, Direction direction) {
if (!isOverworldOrNether(world)) {
return false;
} else {
BlockPos.Mutable mutable = pos.mutableCopy();
boolean bl = false;
Direction[] var5 = Direction.values();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Direction direction2 = var5[var7];
if (world.getBlockState(mutable.set(pos).move(direction2)).isOf(Blocks.OBSIDIAN)) {
bl = true;
break;
}
}
if (!bl) {
return false;
} else {
Direction.Axis axis = direction.getAxis().isHorizontal() ? direction.rotateYCounterclockwise().getAxis() : Direction.Type.HORIZONTAL.randomAxis(world.random);
return NetherPortal.getNewPortal(world, pos, axis).isPresent();
}
}
}
static {
AGE = Properties.AGE_15;
NORTH = ConnectingBlock.NORTH;
EAST = ConnectingBlock.EAST;
SOUTH = ConnectingBlock.SOUTH;
WEST = ConnectingBlock.WEST;
UP = ConnectingBlock.UP;
DIRECTION_PROPERTIES = ConnectingBlock.FACING_PROPERTIES.entrySet().stream()
.filter(entry -> entry.getKey() != Direction.DOWN)
.collect(Util.toMap());
UP_SHAPE = Block.createCuboidShape(0.0, 15.0, 0.0, 16.0, 16.0, 16.0);
WEST_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 1.0, 16.0, 16.0);
EAST_SHAPE = Block.createCuboidShape(15.0, 0.0, 0.0, 16.0, 16.0, 16.0);
NORTH_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 1.0);
SOUTH_SHAPE = Block.createCuboidShape(0.0, 0.0, 15.0, 16.0, 16.0, 16.0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment