Add Block Breaker

This commit is contained in:
TheBrokenRail 2020-07-22 14:07:29 -04:00
parent 83c787a586
commit fbaf0308da
26 changed files with 335 additions and 89 deletions

View File

@ -1,4 +1,4 @@
# Changelog
**1.0.0**
**1.0.0 (Unreleased)**
* Initial Release

View File

@ -3,6 +3,8 @@
# EnergonRelics
Simple Technology Mod
**WARNING:** This mod is still in-development and there may be bugs.
## Changelog
[View Changelog](CHANGELOG.md)

View File

@ -8,7 +8,7 @@ org.gradle.jvmargs = -Xmx1G
fabric_loader_version = 0.9.0+build.204
# Mod Properties
mod_version = 1.0.0
mod_version = 0.0.1
maven_group = com.thebrokenrail
# Dependencies

View File

@ -1,5 +1,6 @@
package com.thebrokenrail.energonrelics;
import com.thebrokenrail.energonrelics.block.BlockBreakerBlock;
import com.thebrokenrail.energonrelics.block.DefensiveLaserBlock;
import com.thebrokenrail.energonrelics.block.structure.StructureGeneratorBlock;
import com.thebrokenrail.energonrelics.block.ThermalGlassBlock;
@ -65,6 +66,8 @@ public class EnergonRelics implements ModInitializer {
public static final Item DEFENSIVE_LASER_CORE_ITEM = new Item(new Item.Settings().group(ITEM_GROUP));
public static final DefensiveLaserBlock DEFENSIVE_LASER_BLOCK = new DefensiveLaserBlock();
public static final BlockBreakerBlock BLOCK_BREAKER_BLOCK = new BlockBreakerBlock();
@Override
public void onInitialize() {
NETWORK_CHIP_ITEM = Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "network_chip"), new NetworkChipItem());
@ -94,6 +97,8 @@ public class EnergonRelics implements ModInitializer {
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "defensive_laser_core"), DEFENSIVE_LASER_CORE_ITEM);
DEFENSIVE_LASER_BLOCK.register("defensive_laser");
BLOCK_BREAKER_BLOCK.register("block_breaker");
StructureGeneratorBlock.register();
}
}

View File

@ -0,0 +1,50 @@
package com.thebrokenrail.energonrelics.block;
import com.thebrokenrail.energonrelics.block.entity.BlockBreakerBlockEntity;
import com.thebrokenrail.energonrelics.block.util.energy.FacingEnergyProviderBlock;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
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.BlockEntityType;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.WorldAccess;
import java.util.function.Function;
public class BlockBreakerBlock extends FacingEnergyProviderBlock {
public static final BooleanProperty POWERED = Properties.POWERED;
public BlockBreakerBlock() {
super(FabricBlockSettings.copy(Blocks.BLACKSTONE).strength(2.0F, 6.0F).lightLevel(state -> state.get(POWERED) ? 7 : 0));
setDefaultState(getDefaultState().with(POWERED, false));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder);
builder.add(POWERED);
}
@SuppressWarnings("deprecation")
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState newState, WorldAccess world, BlockPos pos, BlockPos posFrom) {
if (state.get(FACING) == direction) {
BlockEntity entity = world.getBlockEntity(pos);
if (entity instanceof BlockBreakerBlockEntity) {
((BlockBreakerBlockEntity) entity).resetProgress();
}
}
return super.getStateForNeighborUpdate(state, direction, newState, world, pos, posFrom);
}
@Override
protected Function<BlockEntityType<BlockEntity>, BlockEntity> getFactory() {
return BlockBreakerBlockEntity::new;
}
}

View File

@ -1,50 +1,17 @@
package com.thebrokenrail.energonrelics.block.battery;
import com.thebrokenrail.energonrelics.block.entity.battery.PassiveBatteryControllerBlockEntity;
import com.thebrokenrail.energonrelics.block.util.energy.EnergyProviderBlock;
import com.thebrokenrail.energonrelics.block.util.energy.FacingEnergyProviderBlock;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.Direction;
import java.util.function.Function;
@SuppressWarnings("deprecation")
public class PassiveBatteryControllerBlock extends EnergyProviderBlock {
public static final DirectionProperty FACING = Properties.FACING;
public class PassiveBatteryControllerBlock extends FacingEnergyProviderBlock {
public PassiveBatteryControllerBlock() {
super(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1.5f, 6.0f));
setDefaultState(getDefaultState().with(FACING, Direction.NORTH));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(FACING);
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.rotate(mirror.getRotation(state.get(FACING)));
}
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return getDefaultState().with(FACING, ctx.getPlayerLookDirection().getOpposite());
}
@Override

View File

@ -0,0 +1,65 @@
package com.thebrokenrail.energonrelics.block.entity;
import com.thebrokenrail.energonrelics.EnergonRelics;
import com.thebrokenrail.energonrelics.block.BlockBreakerBlock;
import com.thebrokenrail.energonrelics.config.HardcodedConfig;
import com.thebrokenrail.energonrelics.energy.core.EnergyReceiverBlockEntity;
import com.thebrokenrail.energonrelics.energy.core.util.Action;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.explosion.Explosion;
import java.util.Objects;
public class BlockBreakerBlockEntity extends EnergyReceiverBlockEntity {
private int progress = 0;
public BlockBreakerBlockEntity(BlockEntityType<?> type) {
super(type);
}
public void resetProgress() {
progress = 0;
}
@Override
public CompoundTag toTag(CompoundTag tag) {
super.toTag(tag);
tag.putInt("Progress", progress);
return tag;
}
@Override
public void fromTag(BlockState state, CompoundTag tag) {
super.fromTag(state, tag);
progress = tag.getInt("Progress");
}
@Override
protected void tickEnergy() {
addAction(Action.createBlockStatePropertyAction(HardcodedConfig.BLOCK_BREAKER_ENERGY_REQUIRED_IDLE, BlockBreakerBlock.POWERED, true, false, new Identifier(EnergonRelics.NAMESPACE, "block_breaker_idle")));
if (getCachedState().get(BlockBreakerBlock.POWERED)) {
BlockPos targetPos = getPos().offset(getCachedState().get(BlockBreakerBlock.FACING));
BlockState target = Objects.requireNonNull(getWorld()).getBlockState(targetPos);
if (!target.isAir() && (!target.isToolRequired() || HardcodedConfig.BLOCK_BREAKER_ITEM.isEffectiveOn(target))) {
if (progress >= HardcodedConfig.BLOCK_BREAKER_TIME) {
addAction(new Action(HardcodedConfig.BLOCK_BREAKER_ENERGY_REQUIRED_BREAK, (world, pos, state) -> {
world.createExplosion(null, targetPos.getX() + 0.5d, targetPos.getY() + 0.5d, targetPos.getZ() + 0.5d, 0.5f, Explosion.DestructionType.NONE);
Block.dropStacks(target, world, targetPos, target.getBlock().hasBlockEntity() ? world.getBlockEntity(targetPos) : null, null, new ItemStack(HardcodedConfig.BLOCK_BREAKER_ITEM));
world.breakBlock(targetPos, false);
progress = 0;
}, (world, pos, state) -> progress = 0, new Identifier(EnergonRelics.NAMESPACE, "block_breaker_break")));
}
progress++;
}
}
}
}

View File

@ -17,6 +17,8 @@ import net.minecraft.state.StateManager;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.structure.StructurePieceType;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
@ -34,6 +36,7 @@ import java.util.Map;
import java.util.Random;
import java.util.function.Function;
@SuppressWarnings("deprecation")
public class StructureGeneratorBlock extends SimpleBlockWithEntity {
public static final DirectionProperty HORIZONTAL_FACING = Properties.HORIZONTAL_FACING;
@ -53,7 +56,17 @@ public class StructureGeneratorBlock extends SimpleBlockWithEntity {
}
@Override
protected boolean registerItem() {
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.rotate(mirror.getRotation(state.get(HORIZONTAL_FACING)));
}
@Override
protected boolean addToItemGroup() {
return false;
}
@ -68,7 +81,6 @@ public class StructureGeneratorBlock extends SimpleBlockWithEntity {
return type -> new StructureGeneratorBlockEntity(type, structureFactory);
}
@SuppressWarnings("deprecation")
@Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
super.scheduledTick(state, world, pos, random);
@ -78,7 +90,6 @@ public class StructureGeneratorBlock extends SimpleBlockWithEntity {
}
}
@SuppressWarnings("deprecation")
@Override
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
super.onBlockAdded(state, world, pos, oldState, notify);

View File

@ -1,7 +1,6 @@
package com.thebrokenrail.energonrelics.block.structure;
import com.thebrokenrail.energonrelics.block.entity.structure.StructureGeneratorBlockEntity;
import com.thebrokenrail.energonrelics.structure.StructurePart;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.CompoundTag;
@ -43,7 +42,7 @@ class StructureGeneratorPiece extends StructurePiece {
@Override
public boolean generate(ServerWorldAccess world, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, Random random, BlockBox boundingBox, ChunkPos chunkPos, BlockPos blockPos) {
BlockState state = block.getDefaultState().with(StructureGeneratorBlock.HORIZONTAL_FACING, StructurePart.blockRotationToDirection(rotation));
BlockState state = block.getDefaultState().rotate(rotation);
world.setBlockState(pos, state, 3);
BlockEntity entity = world.getBlockEntity(pos);

View File

@ -1,6 +1,7 @@
package com.thebrokenrail.energonrelics.block.util;
import com.thebrokenrail.energonrelics.EnergonRelics;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
@ -14,12 +15,14 @@ public class SimpleBlock extends Block {
public void register(String name) {
Registry.register(Registry.BLOCK, new Identifier(EnergonRelics.NAMESPACE, name), this);
if (registerItem()) {
Registry.register(Registry.ITEM, new Identifier(EnergonRelics.NAMESPACE, name), new BlockItem(this, new Item.Settings().group(EnergonRelics.ITEM_GROUP)));
Item.Settings settings = new Item.Settings();
if (addToItemGroup() || FabricLoader.getInstance().isDevelopmentEnvironment()) {
settings.group(EnergonRelics.ITEM_GROUP);
}
Registry.register(Registry.ITEM, new Identifier(EnergonRelics.NAMESPACE, name), new BlockItem(this, settings));
}
protected boolean registerItem() {
protected boolean addToItemGroup() {
return true;
}
}

View File

@ -13,6 +13,8 @@ import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.context.LootContext;
import net.minecraft.loot.context.LootContextParameters;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
@ -81,4 +83,17 @@ public abstract class EnergyProviderBlock extends SimpleBlockWithEntity {
return ActionResult.PASS;
}
}
@SuppressWarnings("deprecation")
@Override
public List<ItemStack> getDroppedStacks(BlockState state, LootContext.Builder builder) {
List<ItemStack> stacks = new ArrayList<>(super.getDroppedStacks(state, builder));
if (!builder.getWorld().isClient()) {
BlockEntity entity = builder.getNullable(LootContextParameters.BLOCK_ENTITY);
if (entity instanceof EnergyProviderBlockEntity && ((EnergyProviderBlockEntity) entity).isEnergyProvider()) {
stacks.add(((EnergyProviderBlockEntity) entity).takeStack(builder.getWorld()));
}
}
return stacks;
}
}

View File

@ -0,0 +1,41 @@
package com.thebrokenrail.energonrelics.block.util.energy;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.Direction;
@SuppressWarnings("deprecation")
public abstract class FacingEnergyProviderBlock extends EnergyProviderBlock {
public static final DirectionProperty FACING = Properties.FACING;
public FacingEnergyProviderBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.NORTH));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(FACING);
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.rotate(mirror.getRotation(state.get(FACING)));
}
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return getDefaultState().with(FACING, ctx.getPlayerLookDirection().getOpposite());
}
}

View File

@ -1,5 +1,8 @@
package com.thebrokenrail.energonrelics.config;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
public class HardcodedConfig {
public static final int POWER_RANGE = 64;
@ -13,6 +16,11 @@ public class HardcodedConfig {
public static final int REACTOR_ENERGY_OUTPUT = 250;
public static final int DEFENSIVE_LASER_RANGE = 28;
public static final int DEFENSIVE_LASER_IDLE_ENERGY_REQUIRED = 32;
public static final int DEFENSIVE_LASER_FIRE_ENERGY_REQUIRED = 64;
public static final long DEFENSIVE_LASER_IDLE_ENERGY_REQUIRED = 32;
public static final long DEFENSIVE_LASER_FIRE_ENERGY_REQUIRED = 64;
public static final long BLOCK_BREAKER_ENERGY_REQUIRED_IDLE = 25;
public static final long BLOCK_BREAKER_ENERGY_REQUIRED_BREAK = 36;
public static final int BLOCK_BREAKER_TIME = 48;
public static final Item BLOCK_BREAKER_ITEM = Items.IRON_PICKAXE;
}

View File

@ -26,8 +26,6 @@ import java.util.List;
import java.util.Objects;
public class EnergyProviderBlockEntity extends BlockEntity implements EnergyProvider, BlockEntityClientSerializable, Tickable {
private final ArrayList<Action.PropagatedAction> pendingPropagatedActions = new ArrayList<>();
private static final List<EnergyProviderBlockEntity> scheduledTicks = new ArrayList<>();
public static void tickScheduled() {
@ -45,7 +43,10 @@ public class EnergyProviderBlockEntity extends BlockEntity implements EnergyProv
@Override
public final void addPropagatedAction(Action.PropagatedAction action) {
if (isEnergyProvider()) {
pendingPropagatedActions.add(action);
if (!completedActions.contains(action)) {
handlePropagatedAction(action);
completedActions.add(action);
}
} else {
throw new UnsupportedOperationException();
}
@ -91,27 +92,21 @@ public class EnergyProviderBlockEntity extends BlockEntity implements EnergyProv
return false;
}
private final List<Action.PropagatedAction> completedActions = new ArrayList<>();
protected void handlePropagatedAction(Action.PropagatedAction action) {
throw new UnsupportedOperationException();
}
private void tickPropagatedActions() {
completedActions.clear();
if (isEnergyProvider()) {
List<Action.PropagatedAction> done = new ArrayList<>();
for (Action.PropagatedAction action : pendingPropagatedActions) {
if (!done.contains(action)) {
handlePropagatedAction(action);
done.add(action);
}
}
NetworkComponent component = NetworkComponent.getInstance((ServerWorld) Objects.requireNonNull(getWorld()));
List<BlockPosWithDimension> sources = component.getSourcePos(EnergonRelics.NETWORK_CHIP_ITEM.getID(stack));
if (!sources.contains(new BlockPosWithDimension(getPos(), getWorld().getRegistryKey()))) {
takeStack(getWorld());
}
}
pendingPropagatedActions.clear();
}
protected void serverTick() {

View File

@ -22,7 +22,7 @@ public abstract class EnergyReceiverBlockEntity extends EnergyProviderBlockEntit
private class SelfProvider implements EnergyProvider {
@Override
public void addPropagatedAction(Action.PropagatedAction action) {
pendingActionsFromSelf.add(action);
action.pay(0);
}
@Override
@ -46,7 +46,6 @@ public abstract class EnergyReceiverBlockEntity extends EnergyProviderBlockEntit
}
private final List<Action.PropagatedAction> pendingActions = new ArrayList<>();
private final List<Action.PropagatedAction> pendingActionsFromSelf = new ArrayList<>();
private final EnergyProvider selfProvider = new SelfProvider();
private final List<EnergyProvider> providers = new ArrayList<>();
private final List<Integer> networks = new ArrayList<>();
@ -55,7 +54,6 @@ public abstract class EnergyReceiverBlockEntity extends EnergyProviderBlockEntit
protected void propagateAction(Action.PropagatedAction action) {
totalCost = totalCost + action.amountOwed();
action.expandPayments(providers.size());
pendingActions.add(action);
}
@ -66,12 +64,6 @@ public abstract class EnergyReceiverBlockEntity extends EnergyProviderBlockEntit
@Override
public void serverTick() {
if (hasWorld() && !Objects.requireNonNull(getWorld()).isClient()) {
// Every Action Must Be Paid At Least Once For a Failure State To Occur
for (Action.PropagatedAction action : pendingActionsFromSelf) {
action.pay(0);
}
pendingActionsFromSelf.clear();
ServerWorld world = (ServerWorld) getWorld();
NetworkComponent component = NetworkComponent.getInstance(world);
@ -97,6 +89,7 @@ public abstract class EnergyReceiverBlockEntity extends EnergyProviderBlockEntit
super.serverTick();
for (Action.PropagatedAction action : pendingActions) {
action.expandPayments(providers.size());
for (EnergyProvider provider : providers) {
provider.addPropagatedAction(action);
}

View File

@ -172,26 +172,6 @@ public abstract class StructurePart<T> {
}
}
public static Direction blockRotationToDirection(BlockRotation rotation) {
switch (rotation) {
case NONE: {
return Direction.NORTH;
}
case CLOCKWISE_90: {
return Direction.EAST;
}
case CLOCKWISE_180: {
return Direction.SOUTH;
}
case COUNTERCLOCKWISE_90: {
return Direction.WEST;
}
default: {
throw new MissingCaseException(rotation);
}
}
}
protected void part(StructureContext context, BlockPos originalPos, StructurePart<?> part) {
BlockPos pos = transform(originalPos);
context.addPart(pos.getX(), pos.getY(), pos.getZ(), part);

View File

@ -0,0 +1,50 @@
{
"variants": {
"facing=down,powered=false": {
"model": "energonrelics:block/block_breaker_off",
"x": 90
},
"facing=down,powered=true": {
"model": "energonrelics:block/block_breaker_on",
"x": 90
},
"facing=east,powered=false": {
"model": "energonrelics:block/block_breaker_off",
"y": 90
},
"facing=east,powered=true": {
"model": "energonrelics:block/block_breaker_on",
"y": 90
},
"facing=north,powered=false": {
"model": "energonrelics:block/block_breaker_off"
},
"facing=north,powered=true": {
"model": "energonrelics:block/block_breaker_on"
},
"facing=south,powered=false": {
"model": "energonrelics:block/block_breaker_off",
"y": 180
},
"facing=south,powered=true": {
"model": "energonrelics:block/block_breaker_on",
"y": 180
},
"facing=up,powered=false": {
"model": "energonrelics:block/block_breaker_off",
"x": 270
},
"facing=up,powered=true": {
"model": "energonrelics:block/block_breaker_on",
"x": 270
},
"facing=west,powered=false": {
"model": "energonrelics:block/block_breaker_off",
"y": 270
},
"facing=west,powered=true": {
"model": "energonrelics:block/block_breaker_on",
"y": 270
}
}
}

View File

@ -26,5 +26,6 @@
"text.autoconfig.energonrelics.option.textureSet": "Texture Set",
"death.attack.energonrelics.defensive_laser": "%s was evaporated by a laser",
"death.attack.energonrelics.defensive_laser.player": "%s was evaporated by a laser whilst fighting %s",
"block.energonrelics.research_complex_generator": "Research Complex Generator"
"block.energonrelics.research_complex_generator": "Research Complex Generator",
"block.energonrelics.block_breaker": "Block Breaker"
}

View File

@ -0,0 +1,8 @@
{
"parent": "minecraft:block/orientable",
"textures": {
"top": "energonrelics:block/block_breaker_side",
"front": "energonrelics:block/block_breaker_off",
"side": "energonrelics:block/block_breaker_side"
}
}

View File

@ -0,0 +1,8 @@
{
"parent": "minecraft:block/orientable",
"textures": {
"top": "energonrelics:block/block_breaker_side",
"front": "energonrelics:block/block_breaker_on",
"side": "energonrelics:block/block_breaker_side"
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "energonrelics:block/block_breaker_on"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "energonrelics:block_breaker"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@ -0,0 +1,23 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"#B#",
"BTB",
"#B#"
],
"key": {
"#": {
"item": "energonrelics:circuit_board"
},
"B": {
"item": "minecraft:polished_blackstone"
},
"T": {
"item": "minecraft:tnt"
}
},
"result": {
"item": "energonrelics:defensive_laser_core",
"count": 1
}
}