Multiblock testing, JEI support, Alloying Recipe type

This commit is contained in:
XevianLight
2026-01-17 22:07:49 -07:00
parent 28639dae81
commit 557c2761a7
46 changed files with 988 additions and 121 deletions

View File

@@ -19,6 +19,17 @@ group = mod_group_id
repositories {
// Add here additional repositories if required by some of the dependencies below.
mavenLocal()
maven {
// location of the maven that hosts JEI files since January 2023
name = "Jared's maven"
url = "https://maven.blamejared.com/"
}
maven {
// location of a maven mirror for JEI files, as a fallback
name = "ModMaven"
url = "https://modmaven.dev"
}
}
base {
@@ -119,6 +130,11 @@ dependencies {
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
// compile against the JEI API but do not include it at runtime
compileOnly("mezz.jei:jei-${minecraft_version}-neoforge-api:${jei_version}")
// at runtime, use the full JEI jar for NeoForge
runtimeOnly("mezz.jei:jei-${minecraft_version}-neoforge:${jei_version}")
// Example mod dependency using a mod jar from ./libs with a flat dir repository
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
// The group id is ignored when searching -- in this case, it is "blank"

View File

@@ -22,6 +22,8 @@ neo_version=21.1.217
# The loader version range can only use the major version of FML as bounds
loader_version_range=[1,)
jei_version=19.25.0.322
## Mod Properties
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}

View File

@@ -1,4 +1,5 @@
// 1.21.1 2026-01-11T16:25:14.5769018 Loot Tables
// 1.21.1 2026-01-17T20:45:45.6230298 Loot Tables
69d8318ddba171526d1fabb87d9d93548ed8598e data/aphelion/loot_table/blocks/arc_furnace_casing.json
05f08985e601d30116f67e2f07b48b03b40cdca6 data/aphelion/loot_table/blocks/block_steel.json
ff43a9c3741faf10b1e156a7a74d5cfb035cc118 data/aphelion/loot_table/blocks/dimension_changer.json
b63130d9c10485676303d729807b6fcaac080294 data/aphelion/loot_table/blocks/electric_arc_furnace.json

View File

@@ -1,5 +1,5 @@
// 1.21.1 2026-01-11T16:22:45.6439549 Tags for minecraft:block mod id aphelion
// 1.21.1 2026-01-17T21:22:18.4350515 Tags for minecraft:block mod id aphelion
058c56a0c17204ed5d9cadaffae84292b4752213 data/c/tags/block/storage_blocks.json
058c56a0c17204ed5d9cadaffae84292b4752213 data/c/tags/block/storage_blocks/steel.json
74a1db8ab013dc51b65440e5b6521b340b37af7c data/minecraft/tags/block/mineable/pickaxe.json
74a1db8ab013dc51b65440e5b6521b340b37af7c data/minecraft/tags/block/needs_stone_tool.json
51e9ab2ffa3ba81700cbaab7f39985bb2ab673fa data/minecraft/tags/block/mineable/pickaxe.json
51e9ab2ffa3ba81700cbaab7f39985bb2ab673fa data/minecraft/tags/block/needs_stone_tool.json

View File

@@ -1,12 +1,11 @@
// 1.21.1 2026-01-14T23:38:35.7389032 Block States: aphelion
// 1.21.1 2026-01-17T21:22:18.4335623 Block States: aphelion
851ff42f7b21dec86107c8e0cefb3934ae4ebc08 assets/aphelion/blockstates/block_steel.json
30b9c0efd7aaadb5412d98e4568f98b3632adbb9 assets/aphelion/blockstates/dimension_changer.json
29dfcf9c4933ebf2020c57a83c60e7e7d3bd2bb5 assets/aphelion/blockstates/electric_arc_furnace.json
b86c50fddcf6c8c6c19cb748529239d5962a3ede assets/aphelion/blockstates/test_block.json
a810b97f4dace35d026f28d96cb9c47c93600d75 assets/aphelion/models/block/block_steel.json
2d3592b7ab7132908709243e97540151e0fb762e assets/aphelion/models/block/dimension_changer.json
5f7e8674070f31a63875b5d6147153bfa0eef61a assets/aphelion/models/block/electric_arc_furnace.json
e0971228b4a1c4bc9dbab58a7dacdc3ae6037e02 assets/aphelion/models/block/test_block.json
cdc831b0f1c462be64825fd34bd446e5b95afac6 assets/aphelion/models/item/arc_furnace_casing.json
3599f9037eb2f66de1765318b97ab564c3eae92f assets/aphelion/models/item/block_steel.json
db0ec473a016ce05c258cde18a217d47a9ea8324 assets/aphelion/models/item/dimension_changer.json
279080c06ada87f54fd0a7b885b256dbe25a946a assets/aphelion/models/item/electric_arc_furnace.json

View File

@@ -1,34 +0,0 @@
{
"variants": {
"facing=east,lit=false": {
"model": "aphelion:block/electric_arc_furnace",
"y": 90
},
"facing=east,lit=true": {
"model": "aphelion:block/electric_arc_furnace",
"y": 90
},
"facing=north,lit=false": {
"model": "aphelion:block/electric_arc_furnace"
},
"facing=north,lit=true": {
"model": "aphelion:block/electric_arc_furnace"
},
"facing=south,lit=false": {
"model": "aphelion:block/electric_arc_furnace",
"y": 180
},
"facing=south,lit=true": {
"model": "aphelion:block/electric_arc_furnace",
"y": 180
},
"facing=west,lit=false": {
"model": "aphelion:block/electric_arc_furnace",
"y": 270
},
"facing=west,lit=true": {
"model": "aphelion:block/electric_arc_furnace",
"y": 270
}
}
}

View File

@@ -2,6 +2,7 @@
"values": [
"aphelion:test_block",
"aphelion:electric_arc_furnace",
"aphelion:block_steel"
"aphelion:block_steel",
"aphelion:arc_furnace_casing"
]
}

View File

@@ -2,6 +2,7 @@
"values": [
"aphelion:test_block",
"aphelion:electric_arc_furnace",
"aphelion:block_steel"
"aphelion:block_steel",
"aphelion:arc_furnace_casing"
]
}

View File

@@ -11,6 +11,7 @@ import net.xevianlight.aphelion.core.init.*;
import net.xevianlight.aphelion.fluid.BaseFluidType;
import net.xevianlight.aphelion.fluid.ModFluidTypes;
import net.xevianlight.aphelion.fluid.ModFluids;
import net.xevianlight.aphelion.recipe.ModRecipes;
import net.xevianlight.aphelion.screen.ElectricArcFurnaceScreen;
import net.xevianlight.aphelion.screen.ModMenuTypes;
import net.xevianlight.aphelion.screen.TestBlockScreen;
@@ -55,6 +56,7 @@ public class Aphelion {
ModFluidTypes.FLUID_TYPES.register(MOD_BUS);
ModFluids.register(MOD_BUS);
ModSounds.register(MOD_BUS);
ModRecipes.register(MOD_BUS);
// Register ourselves for server and other game events we are interested in.
// Note that this is necessary if and only if we want *this* class (ExtremeRocketry) to respond directly to events.

View File

@@ -0,0 +1,100 @@
package net.xevianlight.aphelion.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.xevianlight.aphelion.block.entity.custom.EAFPartEntity;
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
import org.jetbrains.annotations.Nullable;
public class ArcFurnaceCasingBlock extends BaseEntityBlock {
public static final BooleanProperty FORMED = BooleanProperty.create("formed");
public ArcFurnaceCasingBlock(Properties properties) {
super(properties);
this.registerDefaultState(this.getStateDefinition().any()
.setValue(FORMED, false));
}
public static Properties getProperties() {
return Properties
.of()
.sound(SoundType.ANVIL)
.destroyTime(2f)
.explosionResistance(10f)
.requiresCorrectToolForDrops();
}
public static Item.Properties getItemProperties() {
return new Item.Properties();
}
public static final MapCodec<ArcFurnaceCasingBlock> CODEC = simpleCodec(ArcFurnaceCasingBlock::new);
@Override
protected MapCodec<? extends BaseEntityBlock> codec() {
return CODEC;
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new EAFPartEntity(blockPos, blockState);
}
private void pingNearbyController(Level level, BlockPos pos) {
int r = 3;
BlockPos.MutableBlockPos mp = new BlockPos.MutableBlockPos();
for (int dx=-r; dx<=r; dx++)
for (int dy=-r; dy<=r; dy++)
for (int dz=-r; dz<=r; dz++) {
mp.set(pos.getX()+dx, pos.getY()+dy, pos.getZ()+dz);
BlockEntity be = level.getBlockEntity(mp);
if (be instanceof ElectricArcFurnaceEntity eaf) {
if (level.getBlockState(eaf.getBlockPos()).getBlock() instanceof ElectricArcFurnace) {
eaf.tryForm();
}
}
}
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
super.onPlace(state, level, pos, oldState, movedByPiston);
if (!level.isClientSide()) pingNearbyController(level, pos);
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!level.isClientSide() && state.getBlock() != newState.getBlock()) {
pingNearbyController(level, pos);
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
public RenderShape getRenderShape(BlockState pState) {
return RenderShape.MODEL;
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FORMED, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FORMED);
}
}

View File

@@ -36,13 +36,21 @@ public class ElectricArcFurnace extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
public static final BooleanProperty FORMED = BooleanProperty.create("formed");
public ElectricArcFurnace(Properties properties) {
super(properties);
this.registerDefaultState(this.getStateDefinition().any()
.setValue(FORMED, false));
}
public static final MapCodec<ElectricArcFurnace> CODEC = simpleCodec(ElectricArcFurnace::new);
private final int INPUT_SLOT = 0;
private final int SECONDARY_INPUT_SLOT = 1;
private final int OUTPUT_SLOT = 2;
private final int ENERGY_SLOT = 3;
@Override
protected MapCodec<? extends BaseEntityBlock> codec() {
return CODEC;
@@ -64,7 +72,7 @@ public class ElectricArcFurnace extends BaseEntityBlock {
@Override
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) {
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity && state.getValue(FORMED)) {
serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos);
}
return InteractionResult.sidedSuccess(level.isClientSide);
@@ -84,15 +92,27 @@ public class ElectricArcFurnace extends BaseEntityBlock {
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (state.getBlock() != newState.getBlock()) {
if (!level.isClientSide && state.getBlock() != newState.getBlock()) {
BlockEntity blockEntity= level.getBlockEntity(pos);
if (blockEntity instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
if(state.getValue(FORMED)) electricArcFurnaceEntity.unformForRemoval();
electricArcFurnaceEntity.drops();
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
super.onPlace(state, level, pos, oldState, movedByPiston);
if (!level.isClientSide() && oldState.getBlock() != state.getBlock()) {
BlockEntity blockEntity= level.getBlockEntity(pos);
if (blockEntity instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
electricArcFurnaceEntity.tryForm();
}
}
}
public static int getRedstoneSignalFromItemHandler(@javax.annotation.Nullable ItemStackHandler itemStackHandler, List<Integer> slots) {
if (itemStackHandler == null) {
return 0;
@@ -131,12 +151,15 @@ public class ElectricArcFurnace extends BaseEntityBlock {
@Override
protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
List<Integer> slots = new ArrayList<>();
slots.add(0);
slots.add(1);
slots.add(2);
slots.add(3);
slots.add(ElectricArcFurnaceEntity.INPUT_SLOT);
slots.add(ElectricArcFurnaceEntity.SECONDARY_INPUT_SLOT);
slots.add(ElectricArcFurnaceEntity.OUTPUT_SLOT);
ElectricArcFurnaceEntity electricArcFurnaceEntity = ((ElectricArcFurnaceEntity) level.getBlockEntity(pos));
return getRedstoneSignalFromItemHandler(electricArcFurnaceEntity.inventory, slots);
if (electricArcFurnaceEntity != null)
return getRedstoneSignalFromItemHandler(electricArcFurnaceEntity.inventory, slots);
return 0;
}
@Override
@@ -160,11 +183,11 @@ public class ElectricArcFurnace extends BaseEntityBlock {
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false);
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(FORMED, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
builder.add(FACING, LIT, FORMED);
}
}

View File

@@ -0,0 +1,39 @@
package net.xevianlight.aphelion.block.entity.custom;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import org.jetbrains.annotations.Nullable;
public class EAFPartEntity extends BlockEntity {
@Nullable private BlockPos controllerPos;
public EAFPartEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.EAF_PART_ENTITY.get(), pos, blockState);
}
public void setControllerPos(@Nullable BlockPos pos) {
controllerPos = pos;
setChanged();
}
public @Nullable BlockPos getControllerPos() {
return controllerPos;
}
@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
if (controllerPos != null) tag.putLong("controller", controllerPos.asLong());
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
controllerPos = tag.contains("controller") ? BlockPos.of(tag.getLong("controller")) : null;
}
}

View File

@@ -3,41 +3,45 @@ package net.xevianlight.aphelion.block.entity.custom;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Containers;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.energy.EnergyStorage;
import net.neoforged.neoforge.energy.IEnergyStorage;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemStackHandler;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.custom.ArcFurnaceCasingBlock;
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
import net.xevianlight.aphelion.block.entity.energy.ModEnergyUtil;
import net.xevianlight.aphelion.client.dimension.DimensionRendererCache;
import net.xevianlight.aphelion.core.init.ModBlockEntities;
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe;
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipeInput;
import net.xevianlight.aphelion.recipe.ModRecipes;
import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu;
import net.xevianlight.aphelion.util.SidedSlotHandler;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvider {
@@ -49,12 +53,12 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
private int maxProgress = 100;
private final int DEFAULT_MAX_PROGRESS = 100;
private final ContainerData data;
private int Blasting_ENERGY_COST = 20;
private int MACHINE_ENERGY_COST = 20;
private final int INPUT_SLOT = 0;
private final int SECONDARY_INPUT_SLOT = 1;
private final int OUTPUT_SLOT = 2;
private final int ENERGY_SLOT = 3;
public static final int INPUT_SLOT = 0;
public static final int SECONDARY_INPUT_SLOT = 1;
public static final int OUTPUT_SLOT = 2;
public static final int ENERGY_SLOT = 3;
public ElectricArcFurnaceEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), pos, blockState);
@@ -104,10 +108,17 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
public void tick(Level level, BlockPos pos, BlockState blockState) {
if (!blockState.getValue(ElectricArcFurnace.FORMED))
return;
chargeFromItem();
if (inventory.getStackInSlot(SECONDARY_INPUT_SLOT).isEmpty()) {
if (hasFurnaceRecipe(INPUT_SLOT) && hasEnoughEnergyToCraft(Blasting_ENERGY_COST)) {
// Secondary slot is empty, try furnace recipes
if (hasFurnaceRecipe(INPUT_SLOT) && hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
// Recipe detected! We have enough energy to process
progress++;
useEnergyForBlasting();
useEnergy();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true));
setChanged(level, pos, blockState);
@@ -115,22 +126,46 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
outputBlastingResult(INPUT_SLOT, OUTPUT_SLOT);
resetProgress();
}
} else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(Blasting_ENERGY_COST)) {
} else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
// Recipe detected but we ran out of power
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
progress = progress > 0 ? progress - 1 : 0;
} else {
// Invalid recipe
resetProgress();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
// Secondary slot is NOT empty, try alloying recipes
if (hasAlloyingRecipe(INPUT_SLOT, SECONDARY_INPUT_SLOT)) {
if (hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
// Alloy recipe detected! We have enough energy to process
progress++;
useEnergy();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true));
setChanged(level, pos, blockState);
if (hasCraftingFinished()) {
outputAlloyingResult(INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT);
resetProgress();
}
} else {
// Recipe detected but we ran out of power
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
progress = progress > 0 ? progress - 1 : 0;
}
} else {
// Invalid recipe
resetProgress();
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
setChanged(level, pos, blockState);
}
}
chargeFromItem();
}
private void chargeFromItem() {
@@ -177,13 +212,22 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
inventory.getStackInSlot(resultSlot).getCount() + (output.getCount() * 2)));
}
private void outputAlloyingResult(int inputSlot, int secondaryInputSlot, int outputSlot) {
Optional<RecipeHolder<ElectricArcFurnaceRecipe>> recipe = getAlloyingRecipe(inventory.getStackInSlot(inputSlot), inventory.getStackInSlot(secondaryInputSlot));
ItemStack output = recipe.get().value().getResultItem(null);
inventory.extractItem(inputSlot, recipe.get().value().baseCount(), false);
inventory.extractItem(secondaryInputSlot, recipe.get().value().alloyCount(), false);
inventory.setStackInSlot(outputSlot, new ItemStack(output.getItem(), inventory.getStackInSlot(outputSlot).getCount() + (output.getCount())));
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void useEnergyForBlasting() {
this.ENERGY_STORAGE.extractEnergy(Blasting_ENERGY_COST, false);
private void useEnergy() {
this.ENERGY_STORAGE.extractEnergy(MACHINE_ENERGY_COST, false);
}
private boolean hasEnoughEnergyToCraft(int energyCost) {
@@ -208,9 +252,47 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
}
private boolean hasCraftingFinished() {
maxProgress = DEFAULT_MAX_PROGRESS;
return this.progress >= this.maxProgress;
}
private boolean hasAlloyingRecipe(int slotBase, int slotAlloy) {
ItemStack baseStack = inventory.getStackInSlot(slotBase);
ItemStack alloyStack = inventory.getStackInSlot(slotAlloy);
Optional<RecipeHolder<ElectricArcFurnaceRecipe>> opt =
getAlloyingRecipe(baseStack, alloyStack);
if (opt.isEmpty()) return false;
ElectricArcFurnaceRecipe recipe = opt.get().value();
// 1) Check required input counts
if (baseStack.getCount() < recipe.baseCount()) return false;
if (alloyStack.getCount() < recipe.alloyCount()) return false;
// 2) Check output slot compatibility + space
ItemStack output = recipe.getResultItem(null);
maxProgress = recipe.cookTime();
return canInsertItemIntoOutputSlot(output, OUTPUT_SLOT)
&& canInsertAmountIntoOutputSlot(output.getCount(), OUTPUT_SLOT);
}
private Optional<RecipeHolder<ElectricArcFurnaceRecipe>> getAlloyingRecipe(ItemStack base, ItemStack alloy) {
if (base.isEmpty()) return Optional.empty();
if (alloy.isEmpty()) return Optional.empty();
return this.level.getRecipeManager()
.getRecipeFor(
ModRecipes.ELECTRIC_ARC_FURNACE_RECIPE_TYPE.get(),
new ElectricArcFurnaceRecipeInput(base, alloy),
level
);
}
private boolean hasFurnaceRecipe(int slot) {
Optional<RecipeHolder<BlastingRecipe>> recipe = getFurnaceRecipe(new ItemStack(inventory.getStackInSlot(slot).getItem().asItem(), 1));
if (recipe.isEmpty())
@@ -233,7 +315,7 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
private final IItemHandler fullHandler = new SidedSlotHandler(inventory, new int[]{0,1}, true, true);
private final IItemHandler fullHandler = new SidedSlotHandler(inventory, new int[]{INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT, ENERGY_SLOT}, true, true);
private final IItemHandler inputHandler = new SidedSlotHandler(inventory, new int[]{0,1}, true, true);
private final IItemHandler outputHandler = new SidedSlotHandler(inventory, new int[]{2,3}, false, true);
private final IItemHandler jadeHandler = new SidedSlotHandler(inventory, new int[]{0}, false, false);
@@ -247,6 +329,7 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
}
private final ModEnergyStorage ENERGY_STORAGE = createEnergyStorage();
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(ENERGY_CAPACITY, MAX_TRANSFER) {
@Override
@@ -308,4 +391,144 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
public CompoundTag getUpdateTag(HolderLookup.Provider pRegistries) {
return saveWithoutMetadata(pRegistries);
}
private static BlockPos rotateY(BlockPos off, Direction facing) {
// Assumes "default" shape faces NORTH.
return switch (facing) {
case NORTH -> off;
case EAST -> new BlockPos(-off.getZ(), off.getY(), off.getX());
case SOUTH -> new BlockPos(-off.getX(), off.getY(), -off.getZ());
case WEST -> new BlockPos(off.getZ(), off.getY(), -off.getX());
default -> off;
};
}
/// MULTIBLOCK
/// LOGIC
/// BELOW
private boolean formed = false; // cached runtime state
private static final BlockPos[] SHAPE = new BlockPos[] {
new BlockPos(1, 0, 0),
new BlockPos(0, 0, 1),
new BlockPos(1, 0, 1),
new BlockPos(0, 1, 0),
new BlockPos(1, 1, 0),
new BlockPos(0, 1, 1),
new BlockPos(1, 1, 1),
};
private List<BlockPos> getPartPositions() {
Direction facing = getBlockState().getValue(BlockStateProperties.HORIZONTAL_FACING);
BlockPos origin = getBlockPos();
List<BlockPos> out = new ArrayList<>(SHAPE.length);
for (BlockPos off : SHAPE) {
BlockPos ro = rotateY(off, facing);
out.add(origin.offset(ro));
}
return out;
}
private boolean structureMatches() {
if (level == null) return false;
for (BlockPos p : getPartPositions()) {
BlockState st = level.getBlockState(p);
// Accept only your casing/part blocks
if (!(st.getBlock() instanceof ArcFurnaceCasingBlock)) {
return false;
}
}
return true;
}
public boolean isFormed() {
return formed;
}
public void tryForm() {
if (level == null || level.isClientSide()) return;
if (!(getBlockState().getBlock() instanceof ElectricArcFurnace)) return; // don't form if not actually present
boolean valid = structureMatches();
if (valid && !formed) {
formed = true;
setFormedState(true);
linkParts();
} else if (!valid && formed) {
unform();
}
}
public void unform() {
if (level == null || level.isClientSide()) return;
formed = false;
setFormedState(false);
unlinkParts();
}
public void unformForRemoval() {
if (level == null || level.isClientSide()) return;
formed = false;
unlinkParts();
}
private void setFormedState(boolean value) {
BlockState state = getBlockState();
if (state.hasProperty(ElectricArcFurnace.FORMED) && state.getValue(ElectricArcFurnace.FORMED) != value) {
level.setBlock(worldPosition, state.setValue(ElectricArcFurnace.FORMED, value), 3);
}
}
private void linkParts() {
if (level == null || level.isClientSide()) return;
for (BlockPos p : getPartPositions()) {
BlockState st = level.getBlockState(p);
if (st.getBlock() instanceof ArcFurnaceCasingBlock && st.hasProperty(ArcFurnaceCasingBlock.FORMED)) {
if (!st.getValue(ArcFurnaceCasingBlock.FORMED)) {
level.setBlock(p, st.setValue(ArcFurnaceCasingBlock.FORMED, true), 3);
}
}
BlockEntity be = level.getBlockEntity(p);
if (be instanceof EAFPartEntity part) {
part.setControllerPos(worldPosition);
part.setChanged();
}
}
}
private void unlinkParts() {
if (level == null || level.isClientSide()) return;
for (BlockPos p : getPartPositions()) {
BlockState st = level.getBlockState(p);
if (st.getBlock() instanceof ArcFurnaceCasingBlock && st.hasProperty(ArcFurnaceCasingBlock.FORMED)) {
if (st.getValue(ArcFurnaceCasingBlock.FORMED)) {
level.setBlock(p, st.setValue(ArcFurnaceCasingBlock.FORMED, false), 3);
}
}
BlockEntity be = level.getBlockEntity(p);
if (be instanceof EAFPartEntity part && worldPosition.equals(part.getControllerPos())) {
part.setControllerPos(null);
part.setChanged();
}
}
}
}

View File

@@ -44,7 +44,7 @@ public class AphelionCommand {
SpacePartitionSavedData.get(level).setOrbitForPartition(x, z, orbit);
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.set", x, z, orbit.toString()),
() -> Component.translatable("command.aphelion.station.orbit.set", x, z, orbit.toString()),
true
);
@@ -62,7 +62,7 @@ public class AphelionCommand {
SpacePartitionSavedData.get(level).overwriteAllExistingOrbits(orbit);
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.overwriteall", orbit.toString()),
() -> Component.translatable("command.aphelion.station.orbit.overwriteall", orbit.toString()),
true
);
@@ -82,12 +82,12 @@ public class AphelionCommand {
if (orbit != null) {
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.get", x, z, orbit.toString()),
() -> Component.translatable("command.aphelion.station.orbit.get", x, z, orbit.toString()),
true
);
} else {
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.get.unassigned", x, z),
() -> Component.translatable("command.aphelion.station.orbit.get.unassigned", x, z),
true
);
}
@@ -108,7 +108,7 @@ public class AphelionCommand {
if (success) {
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.cleared", x, z),
() -> Component.translatable("command.aphelion.station.orbit.cleared", x, z),
true
);
}
@@ -123,7 +123,7 @@ public class AphelionCommand {
SpacePartitionSavedData.get(level).clearAllOrbits();
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.clearall"),
() -> Component.translatable("command.aphelion.station.orbit.clearall"),
true
);
@@ -153,7 +153,7 @@ public class AphelionCommand {
);
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.debug.posToKey", x, z, clickableOutput),
() -> Component.translatable("command.aphelion.station.orbit.debug.posToKey", x, z, clickableOutput),
true
);
@@ -185,7 +185,7 @@ public class AphelionCommand {
);
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.debug.keyToPos", key, clickableOutput),
() -> Component.translatable("command.aphelion.station.orbit.debug.keyToPos", key, clickableOutput),
true
);
@@ -213,7 +213,7 @@ public class AphelionCommand {
);
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.orbit.debug.getPartition", x, z, clickableOutput),
() -> Component.translatable("command.aphelion.station.orbit.debug.getPartition", x, z, clickableOutput),
true
);
@@ -263,7 +263,7 @@ public class AphelionCommand {
player.teleportTo(space, destX, player.position().y, destZ, EnumSet.noneOf(RelativeMovement.class), player.getYRot(), player.getXRot());
context.getSource().sendSuccess(
() -> Component.translatable("aphelion.command.station.teleport.success", player.getDisplayName(), clickablePos, clickableId),
() -> Component.translatable("command.aphelion.station.teleport.success", player.getDisplayName(), clickablePos, clickableId),
true
);
@@ -271,7 +271,7 @@ public class AphelionCommand {
}
context.getSource().sendFailure(
Component.translatable("aphelion.command.station.teleport.failure")
Component.translatable("command.aphelion.station.teleport.failure")
);
return Command.SINGLE_SUCCESS;

View File

@@ -0,0 +1,43 @@
package net.xevianlight.aphelion.compat;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.BlastingRecipe;
import net.minecraft.world.item.crafting.Ingredient;
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe;
import org.jetbrains.annotations.Nullable;
public record ElectricArcFurnaceJeiRecipe (
Ingredient base,
int baseCount,
@Nullable Ingredient alloy,
int secondaryCount,
ItemStack result,
int cookTime
){
public boolean isAlloying() {
return alloy != null;
}
public static ElectricArcFurnaceJeiRecipe fromAlloying(ElectricArcFurnaceRecipe r) {
return new ElectricArcFurnaceJeiRecipe(
r.base(),
r.baseCount(),
r.alloy(),
r.alloyCount(),
r.getResultItem(null),
r.cookTime()
);
}
public static ElectricArcFurnaceJeiRecipe fromBlasting(BlastingRecipe r) {
Ingredient in = r.getIngredients().get(0);
return new ElectricArcFurnaceJeiRecipe(
in,
1,
null,
1,
r.getResultItem(null),
r.getCookingTime()
);
}
}

View File

@@ -0,0 +1,96 @@
package net.xevianlight.aphelion.compat;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.drawable.IDrawableAnimated;
import mezz.jei.api.gui.drawable.IDrawableStatic;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.core.init.ModBlocks;
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ElectricArcFurnaceRecipeCategory implements IRecipeCategory<ElectricArcFurnaceJeiRecipe> {
private final Int2ObjectMap<IDrawableAnimated> arrows = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<IDrawableAnimated> energyBars = new Int2ObjectOpenHashMap<>();
private final IGuiHelper helper;
public static final ResourceLocation UID = ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "alloying");
public static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID,
"textures/gui/electric_arc_furnace/jei.png");
public static final RecipeType<ElectricArcFurnaceJeiRecipe> ELECTRIC_ARC_FURNACE_RECIPE_TYPE =
new RecipeType<>(UID, ElectricArcFurnaceJeiRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public ElectricArcFurnaceRecipeCategory(IGuiHelper helper) {
this.helper = helper;
this.background = helper.createDrawable(TEXTURE, 0, 0, 176, 82);
this.icon = helper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.ELECTRIC_ARC_FURNACE.get()));
}
private IDrawableAnimated getArrow(int cookTime) {
return arrows.computeIfAbsent(cookTime, t -> {
IDrawableStatic arrowStatic = helper.createDrawable(TEXTURE, 176, 0, 24, 15);
return helper.createAnimatedDrawable(arrowStatic, t, IDrawableAnimated.StartDirection.LEFT, false);
});
}
private IDrawableAnimated getEnergyBar() {
return arrows.computeIfAbsent(200, t -> {
IDrawableStatic arrowStatic = helper.createDrawable(TEXTURE, 176, 15, 14, 42);
return helper.createAnimatedDrawable(arrowStatic, t, IDrawableAnimated.StartDirection.TOP, true);
});
}
@Override
public void draw(ElectricArcFurnaceJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
getArrow(recipe.cookTime()).draw(guiGraphics, 89, 35);
getEnergyBar().draw(guiGraphics, 9, 9);
}
@Override
public RecipeType<ElectricArcFurnaceJeiRecipe> getRecipeType() {
return ELECTRIC_ARC_FURNACE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.aphelion.electric_arc_furnace");
}
@Override
public @NotNull IDrawable getBackground() {
return background;
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, ElectricArcFurnaceJeiRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 63, 35).addIngredients(recipe.base());
if(recipe.alloy() != null) {
builder.addSlot(RecipeIngredientRole.INPUT, 40, 35).addIngredients(recipe.alloy());
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 125, 35).addItemStack(recipe.result());
}
}

View File

@@ -0,0 +1,77 @@
package net.xevianlight.aphelion.compat;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.RecipeTypes;
import mezz.jei.api.registration.IGuiHandlerRegistration;
import mezz.jei.api.registration.IRecipeCatalystRegistration;
import mezz.jei.api.registration.IRecipeCategoryRegistration;
import mezz.jei.api.registration.IRecipeRegistration;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipeType;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.core.init.ModBlocks;
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe;
import net.xevianlight.aphelion.recipe.ModRecipes;
import net.xevianlight.aphelion.screen.ElectricArcFurnaceScreen;
import java.util.ArrayList;
import java.util.List;
@JeiPlugin
public class JEIAphelionPlugin implements IModPlugin {
@Override
public ResourceLocation getPluginUid() {
return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "jei_plugin");
}
@Override
public void registerCategories(IRecipeCategoryRegistration registration) {
registration.addRecipeCategories(new ElectricArcFurnaceRecipeCategory(
registration.getJeiHelpers().getGuiHelper()
));
}
@Override
public void registerRecipes(IRecipeRegistration registration) {
var level = Minecraft.getInstance().level;
if (level == null) return;
RecipeManager recipeManager = level.getRecipeManager();
List<ElectricArcFurnaceJeiRecipe> recipes = new ArrayList<>();
for (var holder : recipeManager.getAllRecipesFor(ModRecipes.ELECTRIC_ARC_FURNACE_RECIPE_TYPE.get())) {
recipes.add(ElectricArcFurnaceJeiRecipe.fromAlloying(holder.value()));
}
for (var holder : recipeManager.getAllRecipesFor(RecipeType.BLASTING)) {
recipes.add(ElectricArcFurnaceJeiRecipe.fromBlasting(holder.value()));
}
// List<ElectricArcFurnaceRecipe> electricArcFurnaceRecipes = recipeManager
// .getAllRecipesFor(ModRecipes.ELECTRIC_ARC_FURNACE_RECIPE_TYPE.get()).stream().map(RecipeHolder::value).toList();
registration.addRecipes(ElectricArcFurnaceRecipeCategory.ELECTRIC_ARC_FURNACE_RECIPE_TYPE, recipes);
}
@Override
public void registerGuiHandlers(IGuiHandlerRegistration registration) {
registration.addRecipeClickArea(ElectricArcFurnaceScreen.class, 88, 35, 24, 16,
ElectricArcFurnaceRecipeCategory.ELECTRIC_ARC_FURNACE_RECIPE_TYPE);
}
@Override
public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) {
registration.addRecipeCatalyst(
new ItemStack(ModBlocks.ELECTRIC_ARC_FURNACE.get()), ElectricArcFurnaceRecipeCategory.ELECTRIC_ARC_FURNACE_RECIPE_TYPE
);
registration.addRecipeCatalyst(
new ItemStack(ModBlocks.ELECTRIC_ARC_FURNACE.get()),
RecipeTypes.BLASTING
);
}
}

View File

@@ -5,6 +5,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.entity.custom.DimensionChangerBlockEntity;
import net.xevianlight.aphelion.block.entity.custom.EAFPartEntity;
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity;
@@ -27,4 +28,9 @@ public class ModBlockEntities {
BLOCK_ENTITIES.register("electric_arc_furnace_block_entity", () -> BlockEntityType.Builder.of(
ElectricArcFurnaceEntity::new, ModBlocks.ELECTRIC_ARC_FURNACE.get()).build(null)
);
public static final Supplier<BlockEntityType<EAFPartEntity>> EAF_PART_ENTITY =
BLOCK_ENTITIES.register("eaf_part_entity", () -> BlockEntityType.Builder.of(
EAFPartEntity::new, ModBlocks.ARC_FURNACE_CASING_BLOCK.get()).build(null)
);
}

View File

@@ -4,10 +4,7 @@ import net.minecraft.world.level.block.Block;
import net.neoforged.neoforge.registries.DeferredBlock;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.custom.BlockSteel;
import net.xevianlight.aphelion.block.custom.DimensionChangerBlock;
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
import net.xevianlight.aphelion.block.custom.TestBlock;
import net.xevianlight.aphelion.block.custom.*;
public class ModBlocks {
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Aphelion.MOD_ID);
@@ -16,4 +13,5 @@ public class ModBlocks {
public static final DeferredBlock<Block> BLOCK_STEEL = BLOCKS.register("block_steel", () -> new BlockSteel(BlockSteel.getProperties()));
public static final DeferredBlock<Block> DIMENSION_CHANGER = BLOCKS.register("dimension_changer", () -> new DimensionChangerBlock(DimensionChangerBlock.getProperties()));
public static final DeferredBlock<Block> ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties()));
public static final DeferredBlock<Block> ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties()));
}

View File

@@ -39,5 +39,6 @@ public class ModCreativeTabs {
output.accept(ModItems.TEST_BLOCK);
output.accept(ModItems.ELECTRIC_ARC_FURNACE);
output.accept(ModItems.BLOCK_STEEL);
output.accept(ModItems.ARC_FURNACE_CASING_BLOCK);
}).build());
}

View File

@@ -6,10 +6,7 @@ import net.minecraft.world.item.Rarity;
import net.neoforged.neoforge.registries.DeferredItem;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.block.custom.BlockSteel;
import net.xevianlight.aphelion.block.custom.DimensionChangerBlock;
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
import net.xevianlight.aphelion.block.custom.TestBlock;
import net.xevianlight.aphelion.block.custom.*;
import net.xevianlight.aphelion.item.*;
public class ModItems {
@@ -35,4 +32,5 @@ public static final DeferredItem<Item> MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu
public static final DeferredItem<BlockItem> DIMENSION_CHANGER = ITEMS.register("dimension_changer", () -> new BlockItem(ModBlocks.DIMENSION_CHANGER.get(), DimensionChangerBlock.getItemProperties()));
public static final DeferredItem<BlockItem> ELECTRIC_ARC_FURNACE = ITEMS.register("electric_arc_furnace", () -> new BlockItem(ModBlocks.ELECTRIC_ARC_FURNACE.get(), ElectricArcFurnace.getItemProperties()));
public static final DeferredItem<BlockItem> BLOCK_STEEL = ITEMS.register("block_steel", () -> new BlockItem(ModBlocks.BLOCK_STEEL.get(), BlockSteel.getItemProperties()));
public static final DeferredItem<BlockItem> ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties()));
}

View File

@@ -20,6 +20,7 @@ public class ModBlockLootTableProvider extends BlockLootSubProvider {
dropSelf(ModBlocks.BLOCK_STEEL.get());
dropSelf(ModBlocks.DIMENSION_CHANGER.get());
dropSelf(ModBlocks.ELECTRIC_ARC_FURNACE.get());
dropSelf(ModBlocks.ARC_FURNACE_CASING_BLOCK.get());
}
@Override

View File

@@ -18,14 +18,15 @@ public class ModBlockStateProvider extends BlockStateProvider {
protected void registerStatesAndModels() {
blockWithItem(ModBlocks.TEST_BLOCK);
horizontalBlock(ModBlocks.ELECTRIC_ARC_FURNACE.get(), models().orientable("aphelion:electric_arc_furnace",
mcLoc("block/blast_furnace_side"),
modLoc("block/electric_arc_furnace_front"),
mcLoc("block/blast_furnace_top")));
// horizontalBlock(ModBlocks.ELECTRIC_ARC_FURNACE.get(), models().orientable("aphelion:electric_arc_furnace",
// mcLoc("block/blast_furnace_side"),
// modLoc("block/electric_arc_furnace_front"),
// mcLoc("block/blast_furnace_top")));
blockItem(ModBlocks.ELECTRIC_ARC_FURNACE);
blockWithItem(ModBlocks.BLOCK_STEEL);
blockWithItem(ModBlocks.DIMENSION_CHANGER);
blockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK);
}
private void blockWithItem(DeferredBlock<?> deferredBlock) {

View File

@@ -22,11 +22,14 @@ public class ModBlockTagProvider extends BlockTagsProvider {
tag(BlockTags.MINEABLE_WITH_PICKAXE)
.add(ModBlocks.TEST_BLOCK.get())
.add(ModBlocks.ELECTRIC_ARC_FURNACE.get())
.add(ModBlocks.BLOCK_STEEL.get());
.add(ModBlocks.BLOCK_STEEL.get())
.add(ModBlocks.ARC_FURNACE_CASING_BLOCK.get());
tag(BlockTags.NEEDS_STONE_TOOL)
.add(ModBlocks.TEST_BLOCK.get())
.add(ModBlocks.ELECTRIC_ARC_FURNACE.get())
.add(ModBlocks.BLOCK_STEEL.get());
.add(ModBlocks.BLOCK_STEEL.get())
.add(ModBlocks.ARC_FURNACE_CASING_BLOCK.get());
tag(ModTags.Blocks.STORAGE_BLOCKS_STEEL)
.add(ModBlocks.BLOCK_STEEL.get());

View File

@@ -0,0 +1,92 @@
package net.xevianlight.aphelion.recipe;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.Level;
public record ElectricArcFurnaceRecipe(Ingredient base, int baseCount, Ingredient alloy, int alloyCount, int cookTime, ItemStack output) implements Recipe<ElectricArcFurnaceRecipeInput> {
@Override
public NonNullList<Ingredient> getIngredients() {
NonNullList<Ingredient> list = NonNullList.create();
list.add(base);
list.add(alloy);
return list;
}
@Override
public boolean matches(ElectricArcFurnaceRecipeInput electricArcFurnaceRecipeInput, Level level) {
if (level.isClientSide()) { return false; }
return base.test(electricArcFurnaceRecipeInput.getItem(0)) && alloy.test(electricArcFurnaceRecipeInput.getItem(1));
}
@Override
public ItemStack assemble(ElectricArcFurnaceRecipeInput electricArcFurnaceRecipeInput, HolderLookup.Provider provider) {
return output.copy();
}
@Override
public boolean canCraftInDimensions(int i, int i1) {
return true;
}
@Override
public ItemStack getResultItem(HolderLookup.Provider provider) {
return output;
}
@Override
public RecipeSerializer<?> getSerializer() {
return ModRecipes.EAF_SERIALIZER.get();
}
@Override
public RecipeType<?> getType() {
return ModRecipes.ELECTRIC_ARC_FURNACE_RECIPE_TYPE.get();
}
public static class Serializer implements RecipeSerializer<ElectricArcFurnaceRecipe> {
public static final MapCodec<ElectricArcFurnaceRecipe> CODEC = RecordCodecBuilder.mapCodec(inst -> inst.group(
Ingredient.CODEC_NONEMPTY.fieldOf("base").forGetter(ElectricArcFurnaceRecipe::base),
Codec.INT.optionalFieldOf("base_count", 1).forGetter(ElectricArcFurnaceRecipe::baseCount),
Ingredient.CODEC_NONEMPTY.fieldOf("alloy").forGetter(ElectricArcFurnaceRecipe::alloy),
Codec.INT.optionalFieldOf("alloy_count", 1).forGetter(ElectricArcFurnaceRecipe::alloyCount),
Codec.INT.optionalFieldOf("cook_time", 100).forGetter(ElectricArcFurnaceRecipe::cookTime),
ItemStack.CODEC.fieldOf("result").forGetter(ElectricArcFurnaceRecipe::output)
).apply(inst, ElectricArcFurnaceRecipe::new));
public static final StreamCodec<RegistryFriendlyByteBuf, ElectricArcFurnaceRecipe> STREAM_CODEC =
StreamCodec.composite(
Ingredient.CONTENTS_STREAM_CODEC, ElectricArcFurnaceRecipe::base,
ByteBufCodecs.VAR_INT, ElectricArcFurnaceRecipe::baseCount,
Ingredient.CONTENTS_STREAM_CODEC, ElectricArcFurnaceRecipe::alloy,
ByteBufCodecs.VAR_INT, ElectricArcFurnaceRecipe::alloyCount,
ByteBufCodecs.VAR_INT, ElectricArcFurnaceRecipe::cookTime,
ItemStack.STREAM_CODEC, ElectricArcFurnaceRecipe::output,
ElectricArcFurnaceRecipe::new
);
@Override
public MapCodec<ElectricArcFurnaceRecipe> codec() {
return CODEC;
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, ElectricArcFurnaceRecipe> streamCodec() {
return STREAM_CODEC;
}
}
}

View File

@@ -0,0 +1,20 @@
package net.xevianlight.aphelion.recipe;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeInput;
public record ElectricArcFurnaceRecipeInput (ItemStack base, ItemStack alloy) implements RecipeInput {
@Override
public ItemStack getItem(int i) {
return switch (i) {
case 0 -> base;
case 1 -> alloy;
default -> ItemStack.EMPTY;
};
}
@Override
public int size() {
return 2;
}
}

View File

@@ -0,0 +1,32 @@
package net.xevianlight.aphelion.recipe;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion;
public class ModRecipes {
public static final DeferredRegister<RecipeSerializer<?>> SERIALIZERS =
DeferredRegister.create(Registries.RECIPE_SERIALIZER, Aphelion.MOD_ID);
public static final DeferredRegister<RecipeType<?>> TYPES =
DeferredRegister.create(Registries.RECIPE_TYPE, Aphelion.MOD_ID);
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<ElectricArcFurnaceRecipe>> EAF_SERIALIZER =
SERIALIZERS.register("alloying", ElectricArcFurnaceRecipe.Serializer::new);
public static final DeferredHolder<RecipeType<?>, RecipeType<ElectricArcFurnaceRecipe>> ELECTRIC_ARC_FURNACE_RECIPE_TYPE =
TYPES.register("alloying", () -> new RecipeType<ElectricArcFurnaceRecipe>() {
@Override
public String toString() {
return "alloying";
}
});
public static void register(IEventBus eventBus) {
SERIALIZERS.register(eventBus);
TYPES.register(eventBus);
}
}

View File

@@ -39,10 +39,10 @@ public class ElectricArcFurnaceMenu extends AbstractContainerMenu {
addPlayerInventory(inventory);
addPlayerHotbar(inventory);
this.addSlot(new EnergyItemSlot(this.blockEntity.inventory, 3, 8, 54));
this.addSlot(new SlotItemHandler(this.blockEntity.inventory, 0, 63, 35));
this.addSlot(new SlotItemHandler(this.blockEntity.inventory, 1, 40, 35));
this.addSlot(new ExtractOnlySlot(this.blockEntity.inventory, 2, 125, 35));
this.addSlot(new EnergyItemSlot(this.blockEntity.inventory, ElectricArcFurnaceEntity.ENERGY_SLOT, 8, 54));
this.addSlot(new SlotItemHandler(this.blockEntity.inventory, ElectricArcFurnaceEntity.INPUT_SLOT, 63, 35));
this.addSlot(new SlotItemHandler(this.blockEntity.inventory, ElectricArcFurnaceEntity.SECONDARY_INPUT_SLOT, 40, 35));
this.addSlot(new ExtractOnlySlot(this.blockEntity.inventory, ElectricArcFurnaceEntity.OUTPUT_SLOT, 125, 35));
addDataSlots(data);
}

View File

@@ -0,0 +1,10 @@
{
"variants": {
"formed=false": {
"model": "aphelion:block/arc_furnace_casing"
},
"formed=true": {
"model": "aphelion:block/arc_furnace_casing_formed"
}
}
}

View File

@@ -0,0 +1,64 @@
{
"variants": {
"facing=east,formed=false,lit=false": {
"model": "aphelion:block/electric_arc_furnace",
"y": 90
},
"facing=east,formed=false,lit=true": {
"model": "aphelion:block/electric_arc_furnace",
"y": 90
},
"facing=east,formed=true,lit=false": {
"model": "aphelion:block/electric_arc_furnace_formed",
"y": 90
},
"facing=east,formed=true,lit=true": {
"model": "aphelion:block/electric_arc_furnace_formed",
"y": 90
},
"facing=north,formed=false,lit=false": {
"model": "aphelion:block/electric_arc_furnace"
},
"facing=north,formed=false,lit=true": {
"model": "aphelion:block/electric_arc_furnace"
},
"facing=north,formed=true,lit=false": {
"model": "aphelion:block/electric_arc_furnace_formed"
},
"facing=north,formed=true,lit=true": {
"model": "aphelion:block/electric_arc_furnace_formed"
},
"facing=south,formed=false,lit=false": {
"model": "aphelion:block/electric_arc_furnace",
"y": 180
},
"facing=south,formed=false,lit=true": {
"model": "aphelion:block/electric_arc_furnace",
"y": 180
},
"facing=south,formed=true,lit=false": {
"model": "aphelion:block/electric_arc_furnace_formed",
"y": 180
},
"facing=south,formed=true,lit=true": {
"model": "aphelion:block/electric_arc_furnace_formed",
"y": 180
},
"facing=west,formed=false,lit=false": {
"model": "aphelion:block/electric_arc_furnace",
"y": 270
},
"facing=west,formed=false,lit=true": {
"model": "aphelion:block/electric_arc_furnace",
"y": 270
},
"facing=west,formed=true,lit=false": {
"model": "aphelion:block/electric_arc_furnace_formed",
"y": 270
},
"facing=west,formed=true,lit=true": {
"model": "aphelion:block/electric_arc_furnace_formed",
"y": 270
}
}
}

View File

@@ -3,12 +3,11 @@
"custom_sky": false,
"custom_weather": false,
"dimension": "aphelion:default",
"has_fog": false,
"has_thick_fog": true,
"has_fog": true,
"has_thick_fog": false,
"render_in_rain": true,
"sunrise_angle": 0,
"sunrise_color": 14180147,
"render_void_fog": false,
"horizon_height": -128,
"clear_color_scale": 1.0
}

View File

@@ -3,12 +3,11 @@
"custom_sky": false,
"custom_weather": false,
"dimension": "aphelion:earth",
"has_fog": false,
"has_fog": true,
"has_thick_fog": false,
"render_in_rain": true,
"sunrise_angle": 0,
"sunrise_color": 14180147,
"render_void_fog": false,
"horizon_height": -128,
"clear_color_scale": 1.0
}

View File

@@ -8,7 +8,6 @@
"render_in_rain": true,
"sunrise_angle": 0,
"sunrise_color": 14180147,
"render_void_fog": false,
"horizon_height": -128,
"clear_color_scale": 1.0
}

View File

@@ -8,7 +8,6 @@
"render_in_rain": true,
"sunrise_angle": 0,
"sunrise_color": 14180147,
"render_void_fog": false,
"horizon_height": -128,
"clear_color_scale": 1.0
}

View File

@@ -27,15 +27,15 @@
"tag.item.c.ingots.aluminum": "Aluminum Ingots",
"aphelion.command.station.orbit.set": "Set station (%s, %s)'s orbit to %s",
"aphelion.command.station.orbit.get": "Station (%s, %s)'s orbit is assigned to %s",
"aphelion.command.station.orbit.get.unassigned": "Station (%s, %s)'s orbit is not assigned",
"aphelion.command.station.orbit.cleared": "Cleared station (%s, %s)'s orbit",
"aphelion.command.station.orbit.clearall": "Cleared all station orbits",
"aphelion.command.station.orbit.overwriteall": "Set all existing station orbits with %s (unassigned stations were not affected)",
"aphelion.command.station.orbit.debug.posToKey": "Key of station (%s, %s) is %s",
"aphelion.command.station.orbit.debug.keyToPos": "Key %s belongs to station %s",
"aphelion.command.station.teleport.success": "Teleported %s to station %s, id: %s",
"aphelion.command.station.teleport.failure": "Failed to teleport, entity is null",
"aphelion.command.station.orbit.debug.getPartition": "Partition of %s, %s is %s"
"command.aphelion.station.orbit.set": "Set station (%s, %s)'s orbit to %s",
"command.aphelion.station.orbit.get": "Station (%s, %s)'s orbit is assigned to %s",
"command.aphelion.station.orbit.get.unassigned": "Station (%s, %s)'s orbit is not assigned",
"command.aphelion.station.orbit.cleared": "Cleared station (%s, %s)'s orbit",
"command.aphelion.station.orbit.clearall": "Cleared all station orbits",
"command.aphelion.station.orbit.overwriteall": "Set all existing station orbits with %s (unassigned stations were not affected)",
"command.aphelion.station.orbit.debug.posToKey": "Key of station (%s, %s) is %s",
"command.aphelion.station.orbit.debug.keyToPos": "Key %s belongs to station %s",
"command.aphelion.station.teleport.success": "Teleported %s to station %s, id: %s",
"command.aphelion.station.teleport.failure": "Failed to teleport, entity is null",
"command.aphelion.station.orbit.debug.getPartition": "Partition of %s, %s is %s"
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "aphelion:block/arc_furnace_casing"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "aphelion:block/arc_furnace_casing_formed"
}
}

View File

@@ -0,0 +1,8 @@
{
"parent": "minecraft:block/orientable",
"textures": {
"front": "aphelion:block/electric_arc_furnace_front_formed",
"side": "minecraft:block/blast_furnace_side",
"top": "minecraft:block/blast_furnace_top"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,15 @@
{
"type": "aphelion:alloying",
"base": {
"tag": "c:ingots/iron"
},
"base_count": 1,
"alloy": {
"tag": "minecraft:coals"
},
"alloy_count": 1,
"result": {
"id": "aphelion:ingot_steel",
"count": 1
}
}

View File

@@ -0,0 +1,21 @@
{
"type": "immersiveengineering:arc_furnace",
"additives": [
{
"tag": "minecraft:coals"
}
],
"energy": 204800,
"input": {
"tag": "c:ingots/iron"
},
"results": [
{
"tag": "c:ingots/titanium"
}
],
"slag": {
"tag": "c:slag"
},
"time": 400
}