-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Begin adding explosions (aka nuclear hellfile)
- Loading branch information
Showing
4 changed files
with
315 additions
and
4 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
common/src/main/java/rearth/oritech/block/blocks/reactor/NuclearExplosionBlock.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}; | ||
} | ||
} |
278 changes: 278 additions & 0 deletions
278
common/src/main/java/rearth/oritech/block/entity/reactor/NuclearExplosionEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)))); | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters