Skip to content

Commit

Permalink
Begin adding explosions (aka nuclear hellfile)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rearth committed Dec 13, 2024
1 parent 5dbf2c3 commit 7932886
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package rearth.oritech.block.blocks.reactor;

import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.block.entity.reactor.NuclearExplosionEntity;

public class NuclearExplosionBlock extends Block implements BlockEntityProvider {
public NuclearExplosionBlock(Settings settings) {
super(settings);
}

@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new NuclearExplosionEntity(pos, state);
}

@SuppressWarnings({"rawtypes", "unchecked"})
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
return (world1, pos, state1, blockEntity) -> {
if (blockEntity instanceof BlockEntityTicker ticker)
ticker.tick(world1, pos, state1, blockEntity);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
package rearth.oritech.block.entity.reactor;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import rearth.oritech.init.BlockContent;
import rearth.oritech.init.BlockEntitiesContent;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class NuclearExplosionEntity extends BlockEntity implements BlockEntityTicker<NuclearExplosionEntity> {

private long startTime = -1;
private final Set<BlockPos> removedBlocks = new HashSet<>();
private final Set<BlockPos> borderBlocks = new HashSet<>();
private final Set<DirectionExplosionWave> waves = new HashSet<>();

public NuclearExplosionEntity(BlockPos pos, BlockState state) {
super(BlockEntitiesContent.REACTOR_EXPLOSION_ENTITY, pos, state);
}

@Override
public void tick(World world, BlockPos pos, BlockState state, NuclearExplosionEntity blockEntity) {
if (world.isClient) return;

var initialRadius = 9;

if (startTime == -1) {
startTime = world.getTime();
explosionSphere(initialRadius + 7, 200, pos);
}

var age = world.getTime() - startTime;

if (age == 1) {
createExplosionWaves(initialRadius);
}

if (age > 1) {
waves.forEach(DirectionExplosionWave::nextGeneration);
processBorderBlocks(initialRadius * initialRadius);
}

if (age > initialRadius * 2) {
// done
world.setBlockState(pos, Blocks.AIR.getDefaultState());
}

}

private void createExplosionWaves(int initialRadius) {
var rayCount = initialRadius / 2 + 3;
var directions = getRandomRayDirections(rayCount);
for (var direction : directions) {
var data = new DirectionExplosionWave(initialRadius, addRandomOffset(direction, 0.15f), pos.add(0, world.random.nextBetween(-initialRadius / 2, initialRadius / 2), 0).toImmutable());
waves.add(data);
}
}

private void processBorderBlocks(int maxDist) {

borderBlocks.forEach(target -> {
if (removedBlocks.contains(target)) return;
var distSq = target.getSquaredDistance(pos);
var targetBlock = world.getBlockState(target);
var percentageDist = distSq / (maxDist * maxDist) * 8;
var percentageVaried = percentageDist * (world.random.nextFloat() * 0.6 - 0.3 + 1);

var replaced = false;
var replacementState = Blocks.AIR.getDefaultState();

if (targetBlock.isIn(BlockTags.LOGS)) {
replaced = true;
replacementState = world.random.nextFloat() < 0.8 ? Blocks.BASALT.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
if (percentageVaried < 0.4f) replacementState = Blocks.AIR.getDefaultState();
} else if (targetBlock.isIn(BlockTags.LEAVES)) {
replaced = true;
replacementState = world.random.nextFloat() > 0.4 ? Blocks.MANGROVE_ROOTS.getDefaultState() : Blocks.AIR.getDefaultState();
if (percentageVaried < 0.6f) replacementState = Blocks.AIR.getDefaultState();
} else if (targetBlock.isIn(BlockTags.SAPLINGS) || targetBlock.isOf(Blocks.SHORT_GRASS)) {
replaced = true;
replacementState = world.random.nextFloat() > 0.4 ? Blocks.DEAD_BUSH.getDefaultState() : Blocks.AIR.getDefaultState();
if (percentageVaried < 0.5f) replacementState = Blocks.AIR.getDefaultState();
} else if (targetBlock.isOf(Blocks.GRASS_BLOCK)) {
replaced = true;
if (percentageVaried < 0.05) {
replacementState = world.random.nextFloat() > 0.5 ? Blocks.TUFF.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else if (percentageVaried < 0.3) {
replacementState = world.random.nextFloat() > 0.2 ? Blocks.TUFF.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else if (percentageVaried < 0.55) {
replacementState = world.random.nextFloat() > 0.1 ? Blocks.COARSE_DIRT.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else {
replacementState = Blocks.DIRT.getDefaultState();
}

if (world.random.nextFloat() > 0.7) replaced = false;
} else if (targetBlock.isOf(Blocks.DIRT)) {
replaced = true;
if (percentageVaried < 0.15) {
replacementState = world.random.nextFloat() > 0.6 ? Blocks.COARSE_DIRT.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else if (percentageVaried < 0.3) {
replacementState = world.random.nextFloat() > 0.3 ? Blocks.TUFF.getDefaultState() : Blocks.COARSE_DIRT.getDefaultState();
} else if (percentageVaried < 0.65) {
replacementState = world.random.nextFloat() > 0.2 ? Blocks.COARSE_DIRT.getDefaultState() : Blocks.TUFF.getDefaultState();
} else {
replaced = false;
}

if (world.random.nextFloat() > 0.1) replaced = false;
} else if (targetBlock.isIn(BlockTags.BASE_STONE_OVERWORLD)) {
replaced = true;
if (percentageVaried < 0.3) {
replacementState = world.random.nextFloat() > 0.5 ? Blocks.DEEPSLATE.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else if (percentageVaried < 0.5) {
replacementState = world.random.nextFloat() > 0.3 ? Blocks.STONE.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else if (percentageVaried < 0.7) {
replacementState = world.random.nextFloat() > 0.2 ? Blocks.GRANITE.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else {
replaced = false;
}
} else if (targetBlock.isIn(BlockTags.SAND) || targetBlock.isOf(Blocks.SANDSTONE)) {
replaced = true;
if (percentageVaried < 0.2) {
replacementState = world.random.nextFloat() > 0.7 ? Blocks.SANDSTONE.getDefaultState() : Blocks.MAGMA_BLOCK.getDefaultState();
} else {
replacementState = Blocks.GLASS.getDefaultState();
}

if (percentageVaried > 0.8) replaced = false;
}

if (replaced) {
world.setBlockState(target, replacementState, Block.SKIP_DROPS | Block.NOTIFY_LISTENERS, 1);

// random fire chance
if (world.getBlockState(target.up()).isReplaceable() && world.random.nextFloat() > 0.97) {
world.setBlockState(target.up(), Blocks.FIRE.getDefaultState(), Block.SKIP_DROPS | Block.NOTIFY_LISTENERS, 0);
}
}
});

borderBlocks.clear();
}

private void collectExtraEdgeBlocks(BlockPos center) {
BlockPos.iterate(center.add(-8, -8, -8), center.add(8, 8, 8)).forEach(target -> {
if (removedBlocks.contains(target)) return;
var targetState = world.getBlockState(target);
if (targetState.isAir()) return;
borderBlocks.add(target.toImmutable());
});
}

// remove all blocks in X radius below hardness 'power', return amount of hardness used in total
private int explosionSphere(int radius, int power, BlockPos pos) {

var radiusSq = radius * radius;
var radiusSqExtra = (radius + 3) * (radius + 3);
var usedPower = 0;
var hardBusters = radius;

for (var target : BlockPos.iterateOutwards(pos, radius + 2, radius + 2, radius + 2)) {
if (removedBlocks.contains(target)) continue;
var distSq = target.getSquaredDistance(pos);

if (distSq > radiusSq) {
if (distSq <= (radiusSqExtra)) {
// border block, was almost destroyed
borderBlocks.add(target.toImmutable());
}
continue;
}

// if less than half dist, 100%, then slowly ramp up to 0%
var removalPercentage = (distSq - radiusSq / 2f) / radiusSq;
if (world.random.nextFloat() < removalPercentage - 0.2) {
borderBlocks.add(target.toImmutable());
continue;
}

var targetState = world.getBlockState(target);
var targetBlock = targetState.getBlock();
var targetHardness = targetBlock.getBlastResistance();

if (targetBlock.equals(BlockContent.REACTOR_EXPLOSION) || targetState.isAir() && !targetState.isLiquid()) continue;

// skip too hard blocks (except for the first few)
if (targetHardness > power && hardBusters-- < 0) continue;

usedPower += targetHardness;

// todo find all onBreak overrides in project and move to onBroken
targetBlock.onBroken(world, pos, targetState);
world.setBlockState(target, Blocks.AIR.getDefaultState(), Block.SKIP_DROPS | Block.NOTIFY_LISTENERS, 0);
removedBlocks.add(target.toImmutable());
borderBlocks.remove(target.toImmutable());

}

return usedPower;
}

private List<Vec3d> getRandomRayDirections(int count) {
List<Vec3d> rayDirections = new ArrayList<>(count);

// Divide the circle into 12 equal parts
var angleIncrement = 2 * Math.PI / count; // 360 degrees / 12

for (int i = 0; i < count; i++) {
// Calculate the base angle for this ray
var baseAngle = i * angleIncrement;

// Add a small random perturbation to the angle
var randomPerturbation = (world.random.nextFloat() - 0.5) * (angleIncrement / 2);

// Final angle with randomness
var angle = baseAngle + randomPerturbation;

// Calculate the direction vector
var x = Math.cos(angle);
var z = Math.sin(angle);

rayDirections.add(new Vec3d(x, 0, z)); // Horizontal direction
}

return rayDirections;
}

private Vec3d addRandomOffset(Vec3d direction, float amount) {
return direction.add(world.random.nextFloat() * amount - amount / 2, world.random.nextFloat() * amount - amount / 2, world.random.nextFloat() * amount - amount / 2);
}

private class DirectionExplosionWave {

private final Vec3d direction;

private int lastRadius;
private BlockPos lastPosition;
private int lastRadiusReduction;

private DirectionExplosionWave(int initialRadius, Vec3d direction, BlockPos pos) {
this.direction = direction;
this.lastRadius = initialRadius;
this.lastPosition = pos;
this.lastRadiusReduction = 1;
}

private void nextGeneration() {
var currentRadius = lastRadius - lastRadiusReduction;
if (currentRadius <= 1) return;
var rayOffset = direction.multiply(currentRadius);
var target = lastPosition.add(BlockPos.ofFloored(rayOffset));
var power = currentRadius * 3;
lastRadius = currentRadius;
lastPosition = target;

var usedPower = explosionSphere(currentRadius, power, target);
var expectedPower = currentRadius * currentRadius * currentRadius * 3;
if (usedPower > expectedPower) {
lastRadiusReduction = 2;
}

var isLastGeneration = currentRadius - lastRadiusReduction <= 1;
if (isLastGeneration)
collectExtraEdgeBlocks(target.add(BlockPos.ofFloored(rayOffset.multiply(3))));

}
}
}
1 change: 1 addition & 0 deletions common/src/main/java/rearth/oritech/init/BlockContent.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public class BlockContent implements ArchitecturyBlockRegistryContainer {
public static final Block REACTOR_FUEL_PORT = new ReactorFuelPortBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK));
public static final Block REACTOR_ABSORBER_PORT = new ReactorAbsorberPortBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK));
public static final Block REACTOR_ENERGY_PORT = new ReactorEnergyPortBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK));
public static final Block REACTOR_EXPLOSION = new NuclearExplosionBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK));

// cooling cell, early game re-fillable component

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@
import rearth.oritech.block.entity.pipes.ItemFilterBlockEntity;
import rearth.oritech.block.entity.pipes.ItemPipeInterfaceEntity;
import rearth.oritech.block.entity.processing.*;
import rearth.oritech.block.entity.reactor.ReactorAbsorberPortEntity;
import rearth.oritech.block.entity.reactor.ReactorControllerBlockEntity;
import rearth.oritech.block.entity.reactor.ReactorEnergyPortEntity;
import rearth.oritech.block.entity.reactor.ReactorFuelPortEntity;
import rearth.oritech.block.entity.reactor.*;
import rearth.oritech.block.entity.storage.CreativeStorageBlockEntity;
import rearth.oritech.block.entity.storage.LargeStorageBlockEntity;
import rearth.oritech.block.entity.storage.SmallFluidTankEntity;
Expand Down Expand Up @@ -155,6 +152,7 @@ public class BlockEntitiesContent implements ArchitecturyRegistryContainer<Block
public static final BlockEntityType<ReactorAbsorberPortEntity> REACTOR_ABSORBER_PORT_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(ReactorAbsorberPortEntity::new, BlockContent.REACTOR_ABSORBER_PORT).build();
@AssignSidedEnergy
public static final BlockEntityType<ReactorEnergyPortEntity> REACTOR_ENERGY_PORT_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(ReactorEnergyPortEntity::new, BlockContent.REACTOR_ENERGY_PORT).build();
public static final BlockEntityType<NuclearExplosionEntity> REACTOR_EXPLOSION_ENTITY = FabricBlockEntityTypeBuilder.create(NuclearExplosionEntity::new, BlockContent.REACTOR_EXPLOSION).build();

@AssignSidedInventory
public static final BlockEntityType<AcceleratorControllerBlockEntity> ACCELERATOR_CONTROLLER_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(AcceleratorControllerBlockEntity::new, BlockContent.ACCELERATOR_CONTROLLER).build();
Expand Down

0 comments on commit 7932886

Please sign in to comment.