initial commit for shared version repo of Batteries mod - currently supports 1.21.11 and 1.21.1 of NeoForge.
This commit is contained in:
8
neoforge-1.21.11/build.gradle
Normal file
8
neoforge-1.21.11/build.gradle
Normal file
@@ -0,0 +1,8 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'net.neoforged.moddev' version '2.0.140'
|
||||
id 'idea'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('gradle/neoforge-module.gradle')
|
||||
@@ -0,0 +1,245 @@
|
||||
package com.trunksbomb.batteries;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import com.trunksbomb.batteries.block.BatteryBlock;
|
||||
import com.trunksbomb.batteries.block.ChargerBlock;
|
||||
import com.trunksbomb.batteries.block.CoalGeneratorBlock;
|
||||
import com.trunksbomb.batteries.block.entity.BatteryBlockEntity;
|
||||
import com.trunksbomb.batteries.block.entity.CoalGeneratorBlockEntity;
|
||||
import com.trunksbomb.batteries.block.entity.ChargerBlockEntity;
|
||||
import com.trunksbomb.batteries.command.BatteryDebugCommands;
|
||||
import com.trunksbomb.batteries.item.BatteryBlockItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredAxeItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredArmorItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredBowItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredHoeItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredPickaxeItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredShovelItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredShieldItem;
|
||||
import com.trunksbomb.batteries.item.BatteryPoweredSwordItem;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import com.trunksbomb.batteries.item.BatteryItemData.Tier;
|
||||
import com.trunksbomb.batteries.item.PoweredItem;
|
||||
import com.trunksbomb.batteries.menu.BatteriesMenu;
|
||||
import com.trunksbomb.batteries.menu.BatteryBlockMenu;
|
||||
import com.trunksbomb.batteries.menu.CoalGeneratorMenu;
|
||||
import com.trunksbomb.batteries.recipe.BatteryBlockUpgradeRecipe;
|
||||
import com.trunksbomb.batteries.recipe.BatteryTierUpgradeRecipe;
|
||||
import com.trunksbomb.batteries.recipe.PoweredGearUpgradeRecipe;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.equipment.ArmorMaterials;
|
||||
import net.minecraft.world.item.equipment.ArmorType;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.config.ModConfig;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
|
||||
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
||||
import net.neoforged.neoforge.transfer.access.ItemAccess;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.Transaction;
|
||||
import net.neoforged.neoforge.registries.DeferredBlock;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredItem;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@Mod(Batteries.MODID)
|
||||
public class Batteries {
|
||||
public static final String MODID = "batteries";
|
||||
public static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID);
|
||||
public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID);
|
||||
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITY_TYPES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, MODID);
|
||||
public static final DeferredRegister<MenuType<?>> MENU_TYPES = DeferredRegister.create(Registries.MENU, MODID);
|
||||
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);
|
||||
public static final DeferredRegister<RecipeSerializer<?>> RECIPE_SERIALIZERS = DeferredRegister.create(Registries.RECIPE_SERIALIZER, MODID);
|
||||
|
||||
public static final DeferredItem<Item> BATTERY = registerBattery("battery", Tier.BASIC);
|
||||
public static final DeferredItem<Item> BATTERY1 = registerBattery("battery1", Tier.ADVANCED);
|
||||
public static final DeferredItem<Item> BATTERY2 = registerBattery("battery2", Tier.ELITE);
|
||||
public static final DeferredItem<Item> BATTERY3 = registerBattery("battery3", Tier.ULTIMATE);
|
||||
public static final DeferredItem<Item> BATTERY_ENDER = registerBattery("battery_ender", Tier.ENDER);
|
||||
public static final DeferredItem<Item> BATTERY_CREATIVE = registerBattery("battery_creative", Tier.CREATIVE);
|
||||
public static final DeferredItem<Item> BATTERY_PICKAXE = ITEMS.register("battery_pickaxe",
|
||||
id -> new BatteryPoweredPickaxeItem(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_AXE = ITEMS.register("battery_axe",
|
||||
id -> new BatteryPoweredAxeItem(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_SHOVEL = ITEMS.register("battery_shovel",
|
||||
id -> new BatteryPoweredShovelItem(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_HOE = ITEMS.register("battery_hoe",
|
||||
id -> new BatteryPoweredHoeItem(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_SWORD = ITEMS.register("battery_sword",
|
||||
id -> new BatteryPoweredSwordItem(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_HELMET = ITEMS.register("battery_helmet",
|
||||
id -> new BatteryPoweredArmorItem(ArmorMaterials.DIAMOND, ArmorType.HELMET, new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_CHESTPLATE = ITEMS.register("battery_chestplate",
|
||||
id -> new BatteryPoweredArmorItem(ArmorMaterials.DIAMOND, ArmorType.CHESTPLATE, new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_LEGGINGS = ITEMS.register("battery_leggings",
|
||||
id -> new BatteryPoweredArmorItem(ArmorMaterials.DIAMOND, ArmorType.LEGGINGS, new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_BOOTS = ITEMS.register("battery_boots",
|
||||
id -> new BatteryPoweredArmorItem(ArmorMaterials.DIAMOND, ArmorType.BOOTS, new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_SHIELD = ITEMS.register("battery_shield",
|
||||
id -> new BatteryPoweredShieldItem(new Item.Properties().durability(336).setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<Item> BATTERY_BOW = ITEMS.register("battery_bow",
|
||||
id -> new BatteryPoweredBowItem(new Item.Properties().durability(384).setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredBlock<Block> BATTERY_BLOCK = BLOCKS.register("battery_block", id -> new BatteryBlock(BatteryBlock.batteryBlockProperties()
|
||||
.setId(ResourceKey.create(Registries.BLOCK, id))));
|
||||
public static final DeferredBlock<Block> COAL_GENERATOR = BLOCKS.register("coal_generator", id -> new CoalGeneratorBlock(CoalGeneratorBlock.generatorProperties()
|
||||
.setId(ResourceKey.create(Registries.BLOCK, id))));
|
||||
|
||||
public static final DeferredBlock<Block> CHARGER = BLOCKS.register("charger", id -> new ChargerBlock(ChargerBlock.chargerProperties()
|
||||
.setId(ResourceKey.create(Registries.BLOCK, id))));
|
||||
public static final DeferredBlock<Block> ENDER_CHARGER = BLOCKS.register("ender_charger", id -> new ChargerBlock(ChargerBlock.chargerProperties()
|
||||
.setId(ResourceKey.create(Registries.BLOCK, id))));
|
||||
public static final DeferredItem<BlockItem> BATTERY_BLOCK_ITEM = ITEMS.register("battery_block",
|
||||
id -> new BatteryBlockItem(BATTERY_BLOCK.get(), new Item.Properties().setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
public static final DeferredItem<BlockItem> COAL_GENERATOR_ITEM = ITEMS.registerSimpleBlockItem("coal_generator", COAL_GENERATOR);
|
||||
public static final DeferredItem<BlockItem> CHARGER_ITEM = ITEMS.registerSimpleBlockItem("charger", CHARGER);
|
||||
public static final DeferredItem<BlockItem> ENDER_CHARGER_ITEM = ITEMS.registerSimpleBlockItem("ender_charger", ENDER_CHARGER);
|
||||
public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<BatteryBlockEntity>> BATTERY_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("battery_block",
|
||||
id -> new BlockEntityType<>(BatteryBlockEntity::new, BATTERY_BLOCK.get()));
|
||||
public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<CoalGeneratorBlockEntity>> COAL_GENERATOR_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("coal_generator",
|
||||
id -> new BlockEntityType<>(CoalGeneratorBlockEntity::new, COAL_GENERATOR.get()));
|
||||
public static final DeferredHolder<BlockEntityType<?>, BlockEntityType<ChargerBlockEntity>> CHARGER_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("charger",
|
||||
id -> new BlockEntityType<>(ChargerBlockEntity::new, CHARGER.get(), ENDER_CHARGER.get()));
|
||||
public static final DeferredHolder<MenuType<?>, MenuType<BatteriesMenu>> BATTERIES_MENU = MENU_TYPES.register("batteries_menu",
|
||||
() -> IMenuTypeExtension.create(BatteriesMenu::new));
|
||||
public static final DeferredHolder<MenuType<?>, MenuType<BatteryBlockMenu>> BATTERY_BLOCK_MENU = MENU_TYPES.register("battery_block_menu",
|
||||
() -> IMenuTypeExtension.create(BatteryBlockMenu::new));
|
||||
public static final DeferredHolder<MenuType<?>, MenuType<CoalGeneratorMenu>> COAL_GENERATOR_MENU = MENU_TYPES.register("coal_generator_menu",
|
||||
() -> IMenuTypeExtension.create(CoalGeneratorMenu::new));
|
||||
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<PoweredGearUpgradeRecipe>> POWERED_GEAR_UPGRADE_RECIPE =
|
||||
RECIPE_SERIALIZERS.register("powered_gear_upgrade", () -> new CustomRecipe.Serializer<>(PoweredGearUpgradeRecipe::new));
|
||||
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<BatteryBlockUpgradeRecipe>> BATTERY_BLOCK_UPGRADE_RECIPE =
|
||||
RECIPE_SERIALIZERS.register("battery_block_upgrade", () -> new CustomRecipe.Serializer<>(BatteryBlockUpgradeRecipe::new));
|
||||
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<BatteryTierUpgradeRecipe>> BATTERY_TIER_UPGRADE_RECIPE =
|
||||
RECIPE_SERIALIZERS.register("battery_tier_upgrade", () -> new CustomRecipe.Serializer<>(BatteryTierUpgradeRecipe::new));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> BATTERIES_TAB = CREATIVE_MODE_TABS.register("main", () -> CreativeModeTab.builder()
|
||||
.title(Component.translatable("itemGroup.batteries"))
|
||||
.withTabsBefore(CreativeModeTabs.REDSTONE_BLOCKS)
|
||||
.icon(() -> BATTERY.get().getDefaultInstance())
|
||||
.displayItems((parameters, output) -> {
|
||||
output.accept(BATTERY.get());
|
||||
output.accept(BATTERY1.get());
|
||||
output.accept(BATTERY2.get());
|
||||
output.accept(BATTERY3.get());
|
||||
output.accept(BATTERY_ENDER.get());
|
||||
output.accept(BATTERY_CREATIVE.get());
|
||||
if (BatteriesConfig.poweredToolsEnabled()) {
|
||||
output.accept(BATTERY_PICKAXE.get());
|
||||
output.accept(BATTERY_AXE.get());
|
||||
output.accept(BATTERY_SHOVEL.get());
|
||||
output.accept(BATTERY_HOE.get());
|
||||
}
|
||||
if (BatteriesConfig.poweredWeaponsEnabled()) {
|
||||
output.accept(BATTERY_SWORD.get());
|
||||
output.accept(BATTERY_SHIELD.get());
|
||||
output.accept(BATTERY_BOW.get());
|
||||
}
|
||||
if (BatteriesConfig.poweredArmorEnabled()) {
|
||||
output.accept(BATTERY_HELMET.get());
|
||||
output.accept(BATTERY_CHESTPLATE.get());
|
||||
output.accept(BATTERY_LEGGINGS.get());
|
||||
output.accept(BATTERY_BOOTS.get());
|
||||
}
|
||||
output.accept(BATTERY_BLOCK_ITEM.get());
|
||||
output.accept(COAL_GENERATOR_ITEM.get());
|
||||
output.accept(CHARGER_ITEM.get());
|
||||
output.accept(ENDER_CHARGER_ITEM.get());
|
||||
})
|
||||
.build());
|
||||
|
||||
public Batteries(IEventBus modEventBus, ModContainer modContainer) {
|
||||
BLOCKS.register(modEventBus);
|
||||
ITEMS.register(modEventBus);
|
||||
BLOCK_ENTITY_TYPES.register(modEventBus);
|
||||
MENU_TYPES.register(modEventBus);
|
||||
CREATIVE_MODE_TABS.register(modEventBus);
|
||||
RECIPE_SERIALIZERS.register(modEventBus);
|
||||
modEventBus.addListener(Batteries::registerCapabilities);
|
||||
NeoForge.EVENT_BUS.addListener(Batteries::registerCommands);
|
||||
modContainer.registerConfig(ModConfig.Type.SERVER, BatteriesConfig.SPEC);
|
||||
|
||||
LOGGER.info("Loading Batteries MVP for Minecraft 1.21");
|
||||
}
|
||||
|
||||
private static void registerCapabilities(RegisterCapabilitiesEvent event) {
|
||||
event.registerItem(Capabilities.Energy.ITEM, (stack, context) -> BatteryItem.createEnergyHandler(stack),
|
||||
BATTERY.get(), BATTERY1.get(), BATTERY2.get(), BATTERY3.get(), BATTERY_ENDER.get(), BATTERY_CREATIVE.get());
|
||||
event.registerItem(Capabilities.Energy.ITEM, (stack, context) -> stack.getItem() instanceof PoweredItem poweredItem ? poweredItem.createEnergyHandler(stack) : null,
|
||||
BATTERY_PICKAXE.get(), BATTERY_AXE.get(), BATTERY_SHOVEL.get(), BATTERY_HOE.get(), BATTERY_SWORD.get(),
|
||||
BATTERY_HELMET.get(), BATTERY_CHESTPLATE.get(), BATTERY_LEGGINGS.get(), BATTERY_BOOTS.get(), BATTERY_SHIELD.get(), BATTERY_BOW.get());
|
||||
event.registerItem(Capabilities.Energy.ITEM, (stack, context) -> stack.getItem() instanceof BatteryBlockItem batteryBlockItem ? batteryBlockItem.createEnergyHandler(stack) : null,
|
||||
BATTERY_BLOCK_ITEM.get());
|
||||
event.registerBlockEntity(Capabilities.Energy.BLOCK, BATTERY_BLOCK_ENTITY.get(), BatteryBlockEntity::getEnergyHandler);
|
||||
event.registerBlockEntity(Capabilities.Energy.BLOCK, COAL_GENERATOR_BLOCK_ENTITY.get(), CoalGeneratorBlockEntity::getEnergyHandler);
|
||||
event.registerBlockEntity(Capabilities.Item.BLOCK, COAL_GENERATOR_BLOCK_ENTITY.get(), CoalGeneratorBlockEntity::getItemHandler);
|
||||
event.registerBlockEntity(Capabilities.Energy.BLOCK, CHARGER_BLOCK_ENTITY.get(), ChargerBlockEntity::getEnergyHandler);
|
||||
}
|
||||
|
||||
private static void registerCommands(RegisterCommandsEvent event) {
|
||||
BatteryDebugCommands.register(event.getDispatcher(), new BatteryDebugCommands.EnergyAdapter<EnergyHandler>() {
|
||||
@Override
|
||||
public EnergyHandler resolve(ItemStack stack) {
|
||||
return stack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(stack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stored(EnergyHandler handler) {
|
||||
return handler.getAmountAsInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capacity(EnergyHandler handler) {
|
||||
return handler.getCapacityAsInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int charge(EnergyHandler handler, int amount) {
|
||||
try (Transaction transaction = Transaction.openRoot()) {
|
||||
int accepted = handler.insert(amount, transaction);
|
||||
if (accepted > 0) {
|
||||
transaction.commit();
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drain(EnergyHandler handler, int amount) {
|
||||
try (Transaction transaction = Transaction.openRoot()) {
|
||||
int extracted = handler.extract(amount, transaction);
|
||||
if (extracted > 0) {
|
||||
transaction.commit();
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static DeferredItem<Item> registerBattery(String name, Tier tier) {
|
||||
return ITEMS.register(name, id -> new BatteryItem(tier, new Item.Properties()
|
||||
.stacksTo(1)
|
||||
.setId(ResourceKey.create(Registries.ITEM, id))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.trunksbomb.batteries;
|
||||
|
||||
import com.trunksbomb.batteries.client.screen.BatteryBlockScreen;
|
||||
import com.trunksbomb.batteries.client.screen.BatteriesScreen;
|
||||
import com.trunksbomb.batteries.client.screen.CoalGeneratorScreen;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
|
||||
|
||||
// This class will not load on dedicated servers. Accessing client side code from here is safe.
|
||||
@Mod(value = Batteries.MODID, dist = Dist.CLIENT)
|
||||
@EventBusSubscriber(modid = Batteries.MODID, value = Dist.CLIENT)
|
||||
public class BatteriesClient {
|
||||
public BatteriesClient() {
|
||||
// TODO: Register battery screens, renderers, and client-only behavior here.
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
static void onClientSetup(FMLClientSetupEvent event) {
|
||||
Batteries.LOGGER.info("Batteries client setup complete");
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
static void registerScreens(RegisterMenuScreensEvent event) {
|
||||
event.register(Batteries.BATTERIES_MENU.get(), BatteriesScreen::new);
|
||||
event.register(Batteries.BATTERY_BLOCK_MENU.get(), BatteryBlockScreen::new);
|
||||
event.register(Batteries.COAL_GENERATOR_MENU.get(), CoalGeneratorScreen::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.trunksbomb.batteries.block;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.block.entity.BatteryBlockEntity;
|
||||
import com.trunksbomb.batteries.menu.BatteryBlockMenu;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
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.HorizontalDirectionalBlock;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class BatteryBlock extends BaseEntityBlock {
|
||||
public static final MapCodec<BatteryBlock> CODEC = simpleCodec(BatteryBlock::new);
|
||||
public static final net.minecraft.world.level.block.state.properties.EnumProperty<Direction> FACING = HorizontalDirectionalBlock.FACING;
|
||||
public static final IntegerProperty CHARGE = IntegerProperty.create("charge", 0, 4);
|
||||
|
||||
public BatteryBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(CHARGE, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull MapCodec<? extends BaseEntityBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<net.minecraft.world.level.block.Block, BlockState> builder) {
|
||||
builder.add(FACING, CHARGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull BlockState rotate(BlockState state, Rotation rotation) {
|
||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull BlockState mirror(BlockState state, Mirror mirror) {
|
||||
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull RenderShape getRenderShape(@NonNull BlockState state) {
|
||||
return RenderShape.MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull InteractionResult useWithoutItem(@NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull Player player, net.minecraft.world.phys.@NonNull BlockHitResult hitResult) {
|
||||
return openMenu(level, pos, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull InteractionResult useItemOn(@NonNull ItemStack stack, @NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull Player player, @NonNull InteractionHand hand, net.minecraft.world.phys.@NonNull BlockHitResult hitResult) {
|
||||
return openMenu(level, pos, player);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(@NonNull BlockPos pos, @NonNull BlockState state) {
|
||||
return new BatteryBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, @NonNull BlockState state, @NonNull BlockEntityType<T> blockEntityType) {
|
||||
return level.isClientSide() ? null : createTickerHelper(blockEntityType, Batteries.BATTERY_BLOCK_ENTITY.get(), BatteryBlockEntity::serverTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlacedBy(@NonNull Level level, @NonNull BlockPos pos, @NonNull BlockState state, @Nullable LivingEntity placer, @NonNull ItemStack stack) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack);
|
||||
if (level.getBlockEntity(pos) instanceof BatteryBlockEntity batteryBlockEntity) {
|
||||
batteryBlockEntity.loadFromItem(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerDestroy(Level level, @NonNull Player player, @NonNull BlockPos pos, @NonNull BlockState state, @Nullable BlockEntity blockEntity, @NonNull ItemStack tool) {
|
||||
if (!level.isClientSide() && !player.isCreative() && blockEntity instanceof BatteryBlockEntity batteryBlockEntity) {
|
||||
popResource(level, pos, batteryBlockEntity.createDropStack());
|
||||
}
|
||||
super.playerDestroy(level, player, pos, state, blockEntity, tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlace(@NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull BlockState oldState, boolean movedByPiston) {
|
||||
super.onPlace(state, level, pos, oldState, movedByPiston);
|
||||
level.invalidateCapabilities(pos);
|
||||
}
|
||||
|
||||
public static BlockBehaviour.Properties batteryBlockProperties() {
|
||||
return BlockBehaviour.Properties.of()
|
||||
.strength(2.0F)
|
||||
.requiresCorrectToolForDrops();
|
||||
}
|
||||
|
||||
private static InteractionResult openMenu(Level level, BlockPos pos, Player player) {
|
||||
if (!level.isClientSide() && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof BatteryBlockEntity blockEntity) {
|
||||
MenuProvider provider = new SimpleMenuProvider(
|
||||
(containerId, inventory, menuPlayer) -> BatteryBlockMenu.forBlock(containerId, inventory, blockEntity),
|
||||
Component.translatable("container.batteries.battery_block")
|
||||
);
|
||||
serverPlayer.openMenu(provider, buffer -> BatteryBlockMenu.writeBlockPos(buffer, pos));
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.trunksbomb.batteries.block;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.block.ChargerBlockData.BatteryState;
|
||||
import com.trunksbomb.batteries.block.entity.ChargerBlockEntity;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ChargerBlock extends BaseEntityBlock {
|
||||
public static final EnumProperty<Direction> FACING = HorizontalDirectionalBlock.FACING;
|
||||
public static final EnumProperty<BatteryState> BATTERY = EnumProperty.create("battery", BatteryState.class);
|
||||
public static final MapCodec<ChargerBlock> CODEC = simpleCodec(ChargerBlock::new);
|
||||
|
||||
public ChargerBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(BATTERY, BatteryState.NONE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull MapCodec<? extends BaseEntityBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(FACING, BATTERY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull BlockState getStateForPlacement(@NonNull BlockPlaceContext context) {
|
||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull BlockState rotate(@NonNull BlockState state, @NonNull Rotation rotation) {
|
||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull BlockState mirror(@NonNull BlockState state, @NonNull Mirror mirror) {
|
||||
return this.rotate(state, mirror.getRotation(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull RenderShape getRenderShape(@NonNull BlockState state) {
|
||||
return RenderShape.MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull InteractionResult useWithoutItem(@NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull Player player, net.minecraft.world.phys.@NonNull BlockHitResult hitResult) {
|
||||
return tryRetrieveBattery(state, level, pos, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull InteractionResult useItemOn(@NonNull ItemStack stack, @NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull Player player, @NonNull InteractionHand hand, net.minecraft.world.phys.@NonNull BlockHitResult hitResult) {
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
if (!(blockEntity instanceof ChargerBlockEntity chargerBlockEntity)) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (chargerBlockEntity.hasBattery()) {
|
||||
return tryRetrieveBattery(state, level, pos, player);
|
||||
}
|
||||
|
||||
if (!(stack.getItem() instanceof BatteryItem batteryItem) || !canAcceptBattery(state, batteryItem)) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (!level.isClientSide()) {
|
||||
if (state.is(Batteries.ENDER_CHARGER.get())) {
|
||||
UUID batteryUuid = BatteryItem.ensureBatteryId(stack);
|
||||
chargerBlockEntity.linkBattery(batteryUuid, player.getUUID());
|
||||
updateBatteryState(level, pos, state, BatteryState.fromTier(batteryItem.tier()));
|
||||
} else {
|
||||
ItemStack remainingStack = chargerBlockEntity.insertBattery(stack);
|
||||
player.setItemInHand(hand, remainingStack);
|
||||
updateBatteryState(level, pos, state, BatteryState.fromTier(batteryItem.tier()));
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(@NonNull BlockPos pos, @NonNull BlockState state) {
|
||||
return new ChargerBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(@NonNull Level level, @NonNull BlockState state, @NonNull BlockEntityType<T> blockEntityType) {
|
||||
return level.isClientSide() ? null : createTickerHelper(blockEntityType, Batteries.CHARGER_BLOCK_ENTITY.get(), ChargerBlockEntity::serverTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerDestroy(@NonNull Level level, @NonNull Player player, @NonNull BlockPos pos, @NonNull BlockState state, @Nullable BlockEntity blockEntity, @NonNull ItemStack tool) {
|
||||
if (blockEntity instanceof ChargerBlockEntity chargerBlockEntity && chargerBlockEntity.hasStoredBattery() && level instanceof ServerLevel serverLevel) {
|
||||
popResource(serverLevel, pos, chargerBlockEntity.extractBattery());
|
||||
}
|
||||
super.playerDestroy(level, player, pos, state, blockEntity, tool);
|
||||
}
|
||||
|
||||
public static BlockBehaviour.Properties chargerProperties() {
|
||||
return BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.METAL)
|
||||
.strength(2.0F)
|
||||
.requiresCorrectToolForDrops();
|
||||
}
|
||||
|
||||
private static boolean canAcceptBattery(BlockState state, BatteryItem batteryItem) {
|
||||
return ChargerBlockData.canAcceptBattery(state.is(Batteries.ENDER_CHARGER.get()), batteryItem.tier());
|
||||
}
|
||||
|
||||
private static void updateBatteryState(Level level, BlockPos pos, BlockState state, BatteryState batteryState) {
|
||||
level.setBlock(pos, state.setValue(BATTERY, batteryState), Block.UPDATE_ALL);
|
||||
}
|
||||
|
||||
private static InteractionResult tryRetrieveBattery(BlockState state, Level level, BlockPos pos, Player player) {
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
if (!(blockEntity instanceof ChargerBlockEntity chargerBlockEntity) || !chargerBlockEntity.hasBattery()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (!level.isClientSide()) {
|
||||
if (state.is(Batteries.ENDER_CHARGER.get())) {
|
||||
chargerBlockEntity.clearLinkedBattery();
|
||||
updateBatteryState(level, pos, state, BatteryState.NONE);
|
||||
} else {
|
||||
ItemStack extractedBattery = chargerBlockEntity.extractBattery();
|
||||
updateBatteryState(level, pos, state, BatteryState.NONE);
|
||||
if (!player.addItem(extractedBattery)) {
|
||||
player.drop(extractedBattery, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.trunksbomb.batteries.block;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.block.entity.CoalGeneratorBlockEntity;
|
||||
import com.trunksbomb.batteries.menu.CoalGeneratorMenu;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
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.HorizontalDirectionalBlock;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class CoalGeneratorBlock extends BaseEntityBlock {
|
||||
public static final MapCodec<CoalGeneratorBlock> CODEC = simpleCodec(CoalGeneratorBlock::new);
|
||||
public static final net.minecraft.world.level.block.state.properties.EnumProperty<Direction> FACING = HorizontalDirectionalBlock.FACING;
|
||||
public static final BooleanProperty LIT = BlockStateProperties.LIT;
|
||||
|
||||
public CoalGeneratorBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(LIT, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull MapCodec<? extends BaseEntityBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<net.minecraft.world.level.block.Block, BlockState> builder) {
|
||||
builder.add(FACING, LIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull BlockState getStateForPlacement(@NonNull BlockPlaceContext context) {
|
||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull BlockState rotate(@NonNull BlockState state, @NonNull Rotation rotation) {
|
||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull BlockState mirror(@NonNull BlockState state, @NonNull Mirror mirror) {
|
||||
return this.rotate(state, mirror.getRotation(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull RenderShape getRenderShape(@NonNull BlockState state) {
|
||||
return RenderShape.MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull InteractionResult useWithoutItem(@NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull Player player, net.minecraft.world.phys.@NonNull BlockHitResult hitResult) {
|
||||
return openMenu(level, pos, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull InteractionResult useItemOn(@NonNull ItemStack stack, @NonNull BlockState state, @NonNull Level level, @NonNull BlockPos pos, @NonNull Player player, @NonNull InteractionHand hand, net.minecraft.world.phys.@NonNull BlockHitResult hitResult) {
|
||||
return openMenu(level, pos, player);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(@NonNull BlockPos pos, @NonNull BlockState state) {
|
||||
return new CoalGeneratorBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(@NonNull Level level, @NonNull BlockState state, @NonNull BlockEntityType<T> blockEntityType) {
|
||||
return level.isClientSide() ? null : createTickerHelper(blockEntityType, Batteries.COAL_GENERATOR_BLOCK_ENTITY.get(), CoalGeneratorBlockEntity::serverTick);
|
||||
}
|
||||
|
||||
public static BlockBehaviour.Properties generatorProperties() {
|
||||
return BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.WOOD)
|
||||
.strength(2.0F)
|
||||
.requiresCorrectToolForDrops();
|
||||
}
|
||||
|
||||
public static void setLit(Level level, BlockPos pos, BlockState state, boolean lit) {
|
||||
if (state.hasProperty(LIT) && state.getValue(LIT) != lit) {
|
||||
level.setBlock(pos, state.setValue(LIT, lit), net.minecraft.world.level.block.Block.UPDATE_CLIENTS);
|
||||
}
|
||||
}
|
||||
|
||||
private static InteractionResult openMenu(Level level, BlockPos pos, Player player) {
|
||||
if (!level.isClientSide() && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof CoalGeneratorBlockEntity blockEntity) {
|
||||
MenuProvider provider = new SimpleMenuProvider(
|
||||
(containerId, inventory, menuPlayer) -> CoalGeneratorMenu.forBlock(containerId, inventory, blockEntity),
|
||||
Component.translatable("container.batteries.coal_generator")
|
||||
);
|
||||
serverPlayer.openMenu(provider, buffer -> CoalGeneratorMenu.writeBlockPos(buffer, pos));
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;
|
||||
|
||||
final class BatteryBlockEnergyHandler extends SimpleEnergyHandler {
|
||||
private final BatteryBlockEntity blockEntity;
|
||||
|
||||
BatteryBlockEnergyHandler(BatteryBlockEntity blockEntity) {
|
||||
super(blockEntity.getEnergyCapacity(), blockEntity.getMaxTransfer(), blockEntity.getMaxTransfer(), blockEntity.getStoredEnergy());
|
||||
this.blockEntity = blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnergyChanged(int previousAmount) {
|
||||
this.blockEntity.setStoredEnergyInternal(this.energy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.block.BatteryBlock;
|
||||
import com.trunksbomb.batteries.block.BatteryBlockData;
|
||||
import com.trunksbomb.batteries.item.BatteryBlockItem;
|
||||
import com.trunksbomb.batteries.item.EnergyTierHelper;
|
||||
import com.trunksbomb.batteries.item.PoweredItemEnergy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
import net.minecraft.world.level.storage.ValueOutput;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.Transaction;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class BatteryBlockEntity extends net.minecraft.world.level.block.entity.BlockEntity {
|
||||
private static final String ENERGY_KEY = "energy";
|
||||
private static final String CAPACITY_KEY = "capacity";
|
||||
private int storedEnergy;
|
||||
private int energyCapacity = BatteryBlockItem.BASE_CAPACITY;
|
||||
private BatteryBlockEnergyHandler internalEnergyHandler;
|
||||
private final BatteryBlockData.SideMode[] sideModes = new BatteryBlockData.SideMode[] {
|
||||
BatteryBlockData.SideMode.BOTH, BatteryBlockData.SideMode.BOTH, BatteryBlockData.SideMode.BOTH,
|
||||
BatteryBlockData.SideMode.BOTH, BatteryBlockData.SideMode.BOTH, BatteryBlockData.SideMode.BOTH
|
||||
};
|
||||
|
||||
public BatteryBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(Batteries.BATTERY_BLOCK_ENTITY.get(), pos, blockState);
|
||||
}
|
||||
|
||||
public static void serverTick(Level level, BlockPos pos, BlockState state, BatteryBlockEntity blockEntity) {
|
||||
if (blockEntity.storedEnergy <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int remainingTransfer = Math.min(blockEntity.storedEnergy, blockEntity.getMaxTransfer());
|
||||
if (remainingTransfer <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnergyHandler source = blockEntity.getInternalEnergyHandler();
|
||||
for (Direction direction : Direction.values()) {
|
||||
if (!blockEntity.canExtract(direction)) {
|
||||
continue;
|
||||
}
|
||||
if (remainingTransfer <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockPos targetPos = pos.relative(direction);
|
||||
if (!level.isLoaded(targetPos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EnergyHandler target = level.getCapability(Capabilities.Energy.BLOCK, targetPos, direction.getOpposite());
|
||||
if (target == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (Transaction transaction = Transaction.openRoot()) {
|
||||
int accepted = target.insert(remainingTransfer, transaction);
|
||||
if (accepted <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int extracted = source.extract(accepted, transaction);
|
||||
if (extracted != accepted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
remainingTransfer -= accepted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EnergyHandler getEnergyHandler(@org.jspecify.annotations.Nullable Direction side) {
|
||||
return new BatteryBlockSidedEnergyHandler(this.getInternalEnergyHandler(), side, this);
|
||||
}
|
||||
|
||||
private BatteryBlockEnergyHandler getInternalEnergyHandler() {
|
||||
if (this.internalEnergyHandler == null) {
|
||||
this.internalEnergyHandler = new BatteryBlockEnergyHandler(this);
|
||||
}
|
||||
return this.internalEnergyHandler;
|
||||
}
|
||||
|
||||
public int getStoredEnergy() {
|
||||
return this.storedEnergy;
|
||||
}
|
||||
|
||||
public int getEnergyCapacity() {
|
||||
return this.energyCapacity;
|
||||
}
|
||||
|
||||
public int getMaxTransfer() {
|
||||
return EnergyTierHelper.transferRateForCapacity(this.energyCapacity);
|
||||
}
|
||||
|
||||
public BatteryBlockData.SideMode getSideMode(Direction direction) {
|
||||
return this.sideModes[direction.ordinal()];
|
||||
}
|
||||
|
||||
public void setSideMode(Direction direction, BatteryBlockData.SideMode mode) {
|
||||
this.sideModes[direction.ordinal()] = mode;
|
||||
this.setChanged();
|
||||
if (this.level != null) {
|
||||
this.level.invalidateCapabilities(this.worldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public void cycleSideMode(Direction direction) {
|
||||
this.setSideMode(direction, this.getSideMode(direction).next());
|
||||
}
|
||||
|
||||
public boolean canInsert(Direction direction) {
|
||||
BatteryBlockData.SideMode mode = this.getSideMode(direction);
|
||||
return mode == BatteryBlockData.SideMode.INPUT || mode == BatteryBlockData.SideMode.BOTH;
|
||||
}
|
||||
|
||||
public boolean canExtract(Direction direction) {
|
||||
BatteryBlockData.SideMode mode = this.getSideMode(direction);
|
||||
return mode == BatteryBlockData.SideMode.OUTPUT || mode == BatteryBlockData.SideMode.BOTH;
|
||||
}
|
||||
|
||||
public static Direction directionForIndex(int index) {
|
||||
return BatteryBlockData.directionForIndex(index);
|
||||
}
|
||||
|
||||
public void loadFromItem(ItemStack stack) {
|
||||
BatteryBlockItem.initializeDefaults(stack);
|
||||
this.energyCapacity = BatteryBlockItem.getEnergyCapacity(stack);
|
||||
this.storedEnergy = Math.min(BatteryBlockItem.getStoredEnergy(stack), this.energyCapacity);
|
||||
this.internalEnergyHandler = null;
|
||||
this.refreshState();
|
||||
}
|
||||
|
||||
public ItemStack createDropStack() {
|
||||
ItemStack stack = new ItemStack(Batteries.BATTERY_BLOCK_ITEM.get());
|
||||
PoweredItemEnergy.setEnergyCapacity(stack, this.energyCapacity);
|
||||
PoweredItemEnergy.setStoredEnergy(stack, this.storedEnergy);
|
||||
return stack;
|
||||
}
|
||||
|
||||
void setStoredEnergyInternal(int energy) {
|
||||
this.storedEnergy = Math.max(0, Math.min(energy, this.energyCapacity));
|
||||
this.refreshState();
|
||||
}
|
||||
|
||||
private void refreshState() {
|
||||
this.setChanged();
|
||||
if (this.level == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState state = this.getBlockState();
|
||||
int targetCharge = BatteryBlockData.chargeStage(this.storedEnergy, this.energyCapacity);
|
||||
if (state.hasProperty(BatteryBlock.CHARGE) && state.getValue(BatteryBlock.CHARGE) != targetCharge) {
|
||||
this.level.setBlock(this.worldPosition, state.setValue(BatteryBlock.CHARGE, targetCharge), net.minecraft.world.level.block.Block.UPDATE_CLIENTS);
|
||||
}
|
||||
this.level.invalidateCapabilities(this.worldPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAdditional(@NonNull ValueInput input) {
|
||||
super.loadAdditional(input);
|
||||
this.energyCapacity = Math.max(BatteryBlockItem.BASE_CAPACITY, input.getIntOr(CAPACITY_KEY, BatteryBlockItem.BASE_CAPACITY));
|
||||
this.storedEnergy = Math.max(0, Math.min(input.getIntOr(ENERGY_KEY, 0), this.energyCapacity));
|
||||
for (Direction direction : Direction.values()) {
|
||||
int ordinal = input.getIntOr("side_" + direction.getSerializedName(), BatteryBlockData.SideMode.BOTH.ordinal());
|
||||
this.sideModes[direction.ordinal()] = BatteryBlockData.SideMode.values()[Math.max(0, Math.min(BatteryBlockData.SideMode.values().length - 1, ordinal))];
|
||||
}
|
||||
this.internalEnergyHandler = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(@NonNull ValueOutput output) {
|
||||
super.saveAdditional(output);
|
||||
output.putInt(CAPACITY_KEY, this.energyCapacity);
|
||||
output.putInt(ENERGY_KEY, this.storedEnergy);
|
||||
for (Direction direction : Direction.values()) {
|
||||
output.putInt("side_" + direction.getSerializedName(), this.sideModes[direction.ordinal()].ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.TransactionContext;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
final class BatteryBlockSidedEnergyHandler implements EnergyHandler {
|
||||
private final EnergyHandler delegate;
|
||||
@Nullable
|
||||
private final Direction side;
|
||||
private final BatteryBlockEntity blockEntity;
|
||||
|
||||
BatteryBlockSidedEnergyHandler(EnergyHandler delegate, @Nullable Direction side, BatteryBlockEntity blockEntity) {
|
||||
this.delegate = delegate;
|
||||
this.side = side;
|
||||
this.blockEntity = blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAmountAsLong() {
|
||||
return this.delegate.getAmountAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCapacityAsLong() {
|
||||
return this.delegate.getCapacityAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insert(int maxAmount, @NonNull TransactionContext transaction) {
|
||||
if (maxAmount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (this.side != null && !this.blockEntity.canInsert(this.side)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.delegate.insert(maxAmount, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extract(int maxAmount, @NonNull TransactionContext transaction) {
|
||||
if (maxAmount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (this.side != null && !this.blockEntity.canExtract(this.side)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.delegate.extract(maxAmount, transaction);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.BatteriesConfig;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
import net.minecraft.world.level.storage.ValueOutput;
|
||||
import net.neoforged.neoforge.transfer.transaction.Transaction;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class ChargerBlockEntity extends BlockEntity {
|
||||
public static final UUID DEFAULT_UUID = new UUID(0L, 0L);
|
||||
private ItemStack battery = ItemStack.EMPTY;
|
||||
private UUID linkedBatteryUuid = DEFAULT_UUID;
|
||||
private UUID linkedPlayerUuid = DEFAULT_UUID;
|
||||
private ChargerEnergyHandler energyHandler;
|
||||
|
||||
public ChargerBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(Batteries.CHARGER_BLOCK_ENTITY.get(), pos, blockState);
|
||||
}
|
||||
|
||||
public boolean hasBattery() {
|
||||
return this.hasStoredBattery() || this.hasLinkedBattery();
|
||||
}
|
||||
|
||||
public boolean hasStoredBattery() {
|
||||
return !this.battery.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasLinkedBattery() {
|
||||
return !DEFAULT_UUID.equals(this.linkedBatteryUuid) && !DEFAULT_UUID.equals(this.linkedPlayerUuid);
|
||||
}
|
||||
|
||||
public ItemStack getBattery() {
|
||||
return this.battery.copy();
|
||||
}
|
||||
|
||||
public net.neoforged.neoforge.transfer.energy.EnergyHandler getEnergyHandler(Direction side) {
|
||||
if (this.energyHandler == null) {
|
||||
this.energyHandler = new ChargerEnergyHandler(this);
|
||||
}
|
||||
return this.energyHandler;
|
||||
}
|
||||
|
||||
public ItemStack getChargeTarget() {
|
||||
if (this.hasStoredBattery()) {
|
||||
return this.battery;
|
||||
}
|
||||
|
||||
if (this.level instanceof net.minecraft.server.level.ServerLevel serverLevel && this.hasLinkedBattery()) {
|
||||
ServerPlayer player = serverLevel.getServer().getPlayerList().getPlayer(this.linkedPlayerUuid);
|
||||
if (player != null) {
|
||||
return BatteryItem.findLinkedEnderBattery(player, this.linkedBatteryUuid);
|
||||
}
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public boolean canInsert(ItemStack stack) {
|
||||
return !this.hasBattery() && stack.getItem() instanceof BatteryItem;
|
||||
}
|
||||
|
||||
public ItemStack insertBattery(ItemStack stack) {
|
||||
if (!canInsert(stack)) {
|
||||
return stack;
|
||||
}
|
||||
|
||||
this.battery = stack.copyWithCount(1);
|
||||
stack.shrink(1);
|
||||
this.onBatteryStateChanged();
|
||||
return stack;
|
||||
}
|
||||
|
||||
public ItemStack extractBattery() {
|
||||
if (this.battery.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack extracted = this.battery;
|
||||
this.battery = ItemStack.EMPTY;
|
||||
this.onBatteryStateChanged();
|
||||
return extracted;
|
||||
}
|
||||
|
||||
public void linkBattery(UUID batteryUuid, UUID playerUuid) {
|
||||
this.linkedBatteryUuid = batteryUuid;
|
||||
this.linkedPlayerUuid = playerUuid;
|
||||
this.onBatteryStateChanged();
|
||||
}
|
||||
|
||||
public void clearLinkedBattery() {
|
||||
this.linkedBatteryUuid = DEFAULT_UUID;
|
||||
this.linkedPlayerUuid = DEFAULT_UUID;
|
||||
this.onBatteryStateChanged();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void serverTick(net.minecraft.world.level.Level level, BlockPos pos, BlockState state, ChargerBlockEntity blockEntity) {
|
||||
if (!BatteriesConfig.chargerGeneratesCreativePower()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack chargeTarget = blockEntity.getChargeTarget();
|
||||
if (!chargeTarget.isEmpty()) {
|
||||
chargeBattery(chargeTarget, blockEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private static void chargeBattery(ItemStack battery, ChargerBlockEntity blockEntity) {
|
||||
var energyHandler = BatteryItem.getEnergyHandler(battery);
|
||||
if (energyHandler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (var transaction = Transaction.openRoot()) {
|
||||
int charged = energyHandler.insert(BatteryItem.getMaxTransfer(battery), transaction);
|
||||
if (charged > 0) {
|
||||
transaction.commit();
|
||||
blockEntity.onBatteryChargeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAdditional(@NonNull ValueInput input) {
|
||||
super.loadAdditional(input);
|
||||
this.battery = input.read("battery", ItemStack.CODEC).orElse(ItemStack.EMPTY);
|
||||
this.linkedBatteryUuid = parseUuid(input.getStringOr("linked_battery_uuid", ""));
|
||||
this.linkedPlayerUuid = parseUuid(input.getStringOr("linked_player_uuid", ""));
|
||||
this.energyHandler = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(@NonNull ValueOutput output) {
|
||||
super.saveAdditional(output);
|
||||
if (!this.battery.isEmpty()) {
|
||||
output.store("battery", ItemStack.CODEC, this.battery);
|
||||
}
|
||||
if (this.hasLinkedBattery()) {
|
||||
output.putString("linked_battery_uuid", this.linkedBatteryUuid.toString());
|
||||
output.putString("linked_player_uuid", this.linkedPlayerUuid.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void onBatteryStateChanged() {
|
||||
this.setChanged();
|
||||
this.invalidateEnergyCapability();
|
||||
}
|
||||
|
||||
private void onBatteryChargeChanged() {
|
||||
this.setChanged();
|
||||
this.invalidateEnergyCapability();
|
||||
}
|
||||
|
||||
private void invalidateEnergyCapability() {
|
||||
if (this.level != null) {
|
||||
this.level.invalidateCapabilities(this.worldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private static UUID parseUuid(String value) {
|
||||
return value.isBlank() ? DEFAULT_UUID : UUID.fromString(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.TransactionContext;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
final class ChargerEnergyHandler implements EnergyHandler {
|
||||
private final ChargerBlockEntity blockEntity;
|
||||
|
||||
ChargerEnergyHandler(ChargerBlockEntity blockEntity) {
|
||||
this.blockEntity = blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAmountAsLong() {
|
||||
ItemStack battery = this.blockEntity.getChargeTarget();
|
||||
return battery.isEmpty() ? 0L : BatteryItem.getStoredEnergy(battery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCapacityAsLong() {
|
||||
ItemStack battery = this.blockEntity.getChargeTarget();
|
||||
return battery.isEmpty() ? 0L : BatteryItem.getEnergyCapacity(battery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insert(int maxAmount, @NonNull TransactionContext transaction) {
|
||||
ItemStack battery = this.blockEntity.getChargeTarget();
|
||||
if (battery.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EnergyHandler batteryHandler = BatteryItem.getEnergyHandler(battery);
|
||||
if (batteryHandler == null || batteryHandler.getAmountAsInt() >= batteryHandler.getCapacityAsInt()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return batteryHandler.insert(maxAmount, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extract(int maxAmount, @NonNull TransactionContext transaction) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.BatteriesConfig;
|
||||
import com.trunksbomb.batteries.block.CoalGeneratorBlock;
|
||||
import com.trunksbomb.batteries.item.BatteryBlockItem;
|
||||
import com.trunksbomb.batteries.item.EnergyTierHelper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
import net.minecraft.world.level.storage.ValueOutput;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.item.ItemResource;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.Transaction;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class CoalGeneratorBlockEntity extends net.minecraft.world.level.block.entity.BlockEntity {
|
||||
private static final String ENERGY_KEY = "energy";
|
||||
private static final String BURN_TIME_KEY = "burn_time";
|
||||
private static final String MAX_BURN_TIME_KEY = "max_burn_time";
|
||||
|
||||
private final SimpleContainer fuelSlot = new SimpleContainer(1) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
CoalGeneratorBlockEntity.this.setChanged();
|
||||
}
|
||||
};
|
||||
|
||||
private int storedEnergy;
|
||||
private int burnTimeRemaining;
|
||||
private int maxBurnTime;
|
||||
private CoalGeneratorEnergyHandler internalEnergyHandler;
|
||||
private GeneratorOutputEnergyHandler outputEnergyHandler;
|
||||
private CoalGeneratorItemHandler itemHandler;
|
||||
|
||||
public CoalGeneratorBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(Batteries.COAL_GENERATOR_BLOCK_ENTITY.get(), pos, blockState);
|
||||
}
|
||||
|
||||
public static void serverTick(Level level, BlockPos pos, BlockState state, CoalGeneratorBlockEntity blockEntity) {
|
||||
boolean wasBurning = blockEntity.isBurning();
|
||||
blockEntity.tickGeneration(level);
|
||||
if (wasBurning != blockEntity.isBurning()) {
|
||||
CoalGeneratorBlock.setLit(level, pos, state, blockEntity.isBurning());
|
||||
}
|
||||
blockEntity.tickOutput(level, pos);
|
||||
}
|
||||
|
||||
public SimpleContainer fuelSlot() {
|
||||
return this.fuelSlot;
|
||||
}
|
||||
|
||||
public EnergyHandler getEnergyHandler(@Nullable Direction side) {
|
||||
if (this.outputEnergyHandler == null) {
|
||||
this.outputEnergyHandler = new GeneratorOutputEnergyHandler(this.getInternalEnergyHandler());
|
||||
}
|
||||
return this.outputEnergyHandler;
|
||||
}
|
||||
|
||||
public ResourceHandler<ItemResource> getItemHandler(@Nullable Direction side) {
|
||||
if (this.itemHandler == null) {
|
||||
this.itemHandler = new CoalGeneratorItemHandler(this);
|
||||
}
|
||||
return this.itemHandler;
|
||||
}
|
||||
|
||||
public int getStoredEnergy() {
|
||||
return this.storedEnergy;
|
||||
}
|
||||
|
||||
public int getEnergyCapacity() {
|
||||
return BatteryBlockItem.BASE_CAPACITY;
|
||||
}
|
||||
|
||||
public int getBurnTimeRemaining() {
|
||||
return this.burnTimeRemaining;
|
||||
}
|
||||
|
||||
public int getMaxBurnTime() {
|
||||
return this.maxBurnTime;
|
||||
}
|
||||
|
||||
public boolean isBurning() {
|
||||
return this.burnTimeRemaining > 0;
|
||||
}
|
||||
|
||||
private CoalGeneratorEnergyHandler getInternalEnergyHandler() {
|
||||
if (this.internalEnergyHandler == null) {
|
||||
this.internalEnergyHandler = new CoalGeneratorEnergyHandler(this);
|
||||
}
|
||||
return this.internalEnergyHandler;
|
||||
}
|
||||
|
||||
private void tickGeneration(Level level) {
|
||||
if (this.burnTimeRemaining <= 0 && this.storedEnergy < this.getEnergyCapacity()) {
|
||||
ItemStack fuel = this.fuelSlot.getItem(0);
|
||||
int burnTime = getFuelBurnTime(level, fuel);
|
||||
if (burnTime > 0) {
|
||||
this.burnTimeRemaining = burnTime;
|
||||
this.maxBurnTime = burnTime;
|
||||
fuel.shrink(1);
|
||||
if (fuel.isEmpty()) {
|
||||
this.fuelSlot.setItem(0, ItemStack.EMPTY);
|
||||
}
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.burnTimeRemaining > 0 && this.storedEnergy < this.getEnergyCapacity()) {
|
||||
this.getInternalEnergyHandler().set(Math.min(this.getEnergyCapacity(), this.storedEnergy + BatteriesConfig.generatorEnergyPerTick()));
|
||||
this.burnTimeRemaining--;
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickOutput(Level level, BlockPos pos) {
|
||||
if (this.storedEnergy <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int remainingTransfer = Math.min(this.storedEnergy, this.getMaxTransfer());
|
||||
EnergyHandler source = this.getInternalEnergyHandler();
|
||||
|
||||
for (Direction direction : Direction.values()) {
|
||||
if (remainingTransfer <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
BlockPos targetPos = pos.relative(direction);
|
||||
if (!level.isLoaded(targetPos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EnergyHandler target = level.getCapability(Capabilities.Energy.BLOCK, targetPos, direction.getOpposite());
|
||||
if (target == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (Transaction transaction = Transaction.openRoot()) {
|
||||
int accepted = target.insert(remainingTransfer, transaction);
|
||||
if (accepted <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int extracted = source.extract(accepted, transaction);
|
||||
if (extracted != accepted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
remainingTransfer -= accepted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxTransfer() {
|
||||
return EnergyTierHelper.transferRateForCapacity(this.getEnergyCapacity());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadAdditional(@NonNull ValueInput input) {
|
||||
super.loadAdditional(input);
|
||||
this.storedEnergy = Math.max(0, Math.min(input.getIntOr(ENERGY_KEY, 0), this.getEnergyCapacity()));
|
||||
this.burnTimeRemaining = Math.max(0, input.getIntOr(BURN_TIME_KEY, 0));
|
||||
this.maxBurnTime = Math.max(0, input.getIntOr(MAX_BURN_TIME_KEY, 0));
|
||||
this.fuelSlot.setItem(0, input.read("fuel", ItemStack.CODEC).orElse(ItemStack.EMPTY));
|
||||
this.internalEnergyHandler = null;
|
||||
this.outputEnergyHandler = null;
|
||||
this.itemHandler = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(@NonNull ValueOutput output) {
|
||||
super.saveAdditional(output);
|
||||
output.putInt(ENERGY_KEY, this.storedEnergy);
|
||||
output.putInt(BURN_TIME_KEY, this.burnTimeRemaining);
|
||||
output.putInt(MAX_BURN_TIME_KEY, this.maxBurnTime);
|
||||
if (!this.fuelSlot.getItem(0).isEmpty()) {
|
||||
output.store("fuel", ItemStack.CODEC, this.fuelSlot.getItem(0));
|
||||
}
|
||||
}
|
||||
|
||||
void setStoredEnergyInternal(int energy) {
|
||||
this.storedEnergy = Math.max(0, Math.min(energy, this.getEnergyCapacity()));
|
||||
this.setChanged();
|
||||
if (this.level != null) {
|
||||
this.level.invalidateCapabilities(this.worldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getFuelBurnTime(Level level, ItemStack stack) {
|
||||
if (!isFuel(level, stack)) {
|
||||
return 0;
|
||||
}
|
||||
return level.fuelValues().burnDuration(stack);
|
||||
}
|
||||
|
||||
public static boolean isFuel(Level level, ItemStack stack) {
|
||||
return !stack.isEmpty() && level.fuelValues() != null && level.fuelValues().isFuel(stack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;
|
||||
|
||||
final class CoalGeneratorEnergyHandler extends SimpleEnergyHandler {
|
||||
private final CoalGeneratorBlockEntity blockEntity;
|
||||
|
||||
CoalGeneratorEnergyHandler(CoalGeneratorBlockEntity blockEntity) {
|
||||
super(blockEntity.getEnergyCapacity(), blockEntity.getMaxTransfer(), blockEntity.getMaxTransfer(), blockEntity.getStoredEnergy());
|
||||
this.blockEntity = blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnergyChanged(int previousAmount) {
|
||||
this.blockEntity.setStoredEnergyInternal(this.energy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
import net.neoforged.neoforge.transfer.TransferPreconditions;
|
||||
import net.neoforged.neoforge.transfer.item.ItemResource;
|
||||
import net.neoforged.neoforge.transfer.transaction.SnapshotJournal;
|
||||
import net.neoforged.neoforge.transfer.transaction.TransactionContext;
|
||||
|
||||
public class CoalGeneratorItemHandler extends SnapshotJournal<ItemStack> implements ResourceHandler<ItemResource> {
|
||||
private final CoalGeneratorBlockEntity blockEntity;
|
||||
|
||||
public CoalGeneratorItemHandler(CoalGeneratorBlockEntity blockEntity) {
|
||||
this.blockEntity = blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemResource getResource(int index) {
|
||||
validateIndex(index);
|
||||
return ItemResource.of(this.blockEntity.fuelSlot().getItem(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAmountAsLong(int index) {
|
||||
validateIndex(index);
|
||||
return this.blockEntity.fuelSlot().getItem(index).getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCapacityAsLong(int index, ItemResource resource) {
|
||||
validateIndex(index);
|
||||
ItemStack current = this.blockEntity.fuelSlot().getItem(index);
|
||||
if (resource.isEmpty()) {
|
||||
return current.isEmpty() ? 64 : current.getMaxStackSize();
|
||||
}
|
||||
return isValid(index, resource) ? resource.getMaxStackSize() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int index, ItemResource resource) {
|
||||
validateIndex(index);
|
||||
if (resource.isEmpty() || this.blockEntity.getLevel() == null) {
|
||||
return false;
|
||||
}
|
||||
return CoalGeneratorBlockEntity.isFuel(this.blockEntity.getLevel(), resource.toStack());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insert(int index, ItemResource resource, int amount, TransactionContext transaction) {
|
||||
validateIndex(index);
|
||||
TransferPreconditions.checkNonEmptyNonNegative(resource, amount);
|
||||
if (amount == 0 || !this.isValid(index, resource)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack current = this.blockEntity.fuelSlot().getItem(index);
|
||||
if (!current.isEmpty() && !resource.matches(current)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxStackSize = resource.getMaxStackSize();
|
||||
int inserted = Math.min(amount, maxStackSize - current.getCount());
|
||||
if (inserted <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.updateSnapshots(transaction);
|
||||
if (current.isEmpty()) {
|
||||
this.blockEntity.fuelSlot().setItem(index, resource.toStack(inserted));
|
||||
} else {
|
||||
current.grow(inserted);
|
||||
this.blockEntity.fuelSlot().setItem(index, current);
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extract(int index, ItemResource resource, int amount, TransactionContext transaction) {
|
||||
validateIndex(index);
|
||||
TransferPreconditions.checkNonEmptyNonNegative(resource, amount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack createSnapshot() {
|
||||
return this.blockEntity.fuelSlot().getItem(0).copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void revertToSnapshot(ItemStack snapshot) {
|
||||
this.blockEntity.fuelSlot().setItem(0, snapshot);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRootCommit(ItemStack originalState) {
|
||||
this.blockEntity.setChanged();
|
||||
if (this.blockEntity.getLevel() != null) {
|
||||
this.blockEntity.getLevel().invalidateCapabilities(this.blockEntity.getBlockPos());
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateIndex(int index) {
|
||||
if (index != 0) {
|
||||
throw new IndexOutOfBoundsException("Coal generator only has one slot");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.trunksbomb.batteries.block.entity;
|
||||
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.TransactionContext;
|
||||
|
||||
final class GeneratorOutputEnergyHandler implements EnergyHandler {
|
||||
private final EnergyHandler delegate;
|
||||
|
||||
GeneratorOutputEnergyHandler(EnergyHandler delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAmountAsLong() {
|
||||
return this.delegate.getAmountAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCapacityAsLong() {
|
||||
return this.delegate.getCapacityAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insert(int maxAmount, TransactionContext transaction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extract(int maxAmount, TransactionContext transaction) {
|
||||
return this.delegate.extract(maxAmount, transaction);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.trunksbomb.batteries.client.screen;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import com.trunksbomb.batteries.menu.BatteriesMenu;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.input.MouseButtonEvent;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
public class BatteriesScreen extends AbstractContainerScreen<BatteriesMenu> {
|
||||
private static final Identifier TEXTURE = Identifier.fromNamespaceAndPath(Batteries.MODID, "textures/battery_gui.png");
|
||||
|
||||
private static final int WIDTH = 200;
|
||||
private static final int HEIGHT = 164;
|
||||
private static final int BUTTON_WIDTH = 20;
|
||||
private static final int BUTTON_HEIGHT = 18;
|
||||
private static final int BUTTON_SCREEN_GAP_X = 7;
|
||||
private static final int BUTTON_SCREEN_GAP_Y = 3;
|
||||
private static final int BUTTON_START_X = 48;
|
||||
private static final int BUTTON_START_Y = 39;
|
||||
private static final int INVENTORY_START_X = 32;
|
||||
private static final int INVENTORY_START_Y = 19;
|
||||
private static final int PLAYER_INVENTORY_START_Y = 82;
|
||||
private static final int ARMOR_START_X = 7;
|
||||
private static final int ARMOR_START_Y = 19;
|
||||
private static final int BUTTON_TEXTURE_GAP = 1;
|
||||
private static final int BUTTON_TEXTURE_START_X = 201;
|
||||
private static final int BUTTON_TEXTURE_START_Y = 0;
|
||||
private static final int ARMOR_TEXTURE_X = 42;
|
||||
private static final int ARMOR_TEXTURE_Y = 170;
|
||||
private static final int CHECK_TEXTURE_X = 25;
|
||||
private static final int CHECK_TEXTURE_Y = 170;
|
||||
private static final int PLUS_TEXTURE_X = 144;
|
||||
private static final int PLUS_TEXTURE_Y = 170;
|
||||
|
||||
public BatteriesScreen(BatteriesMenu menu, Inventory playerInventory, Component title) {
|
||||
super(menu, playerInventory, title);
|
||||
this.imageWidth = WIDTH;
|
||||
this.imageHeight = HEIGHT;
|
||||
this.inventoryLabelX = INVENTORY_START_X;
|
||||
this.inventoryLabelY = PLAYER_INVENTORY_START_Y - 11;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) {
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight, 256, 256);
|
||||
drawButtons(guiGraphics, mouseX, mouseY);
|
||||
drawOverlays(guiGraphics);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
renderTitle(guiGraphics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
this.renderBackground(guiGraphics, mouseX, mouseY, partialTick);
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
||||
this.renderTooltip(guiGraphics, mouseX, mouseY);
|
||||
renderCustomTooltip(guiGraphics, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) {
|
||||
double mouseX = event.x();
|
||||
double mouseY = event.y();
|
||||
for (int index = 0; index < this.menu.buttonCount(); index++) {
|
||||
if (isBatteryModeToggle(index)) {
|
||||
if (buttonBounds(0, 1).contains(mouseX, mouseY)) {
|
||||
pressMenuButton(0);
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Bounds bounds = buttonBounds(indexToRow(index), indexToColumn(index));
|
||||
if (bounds.contains(mouseX, mouseY)) {
|
||||
pressMenuButton(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseClicked(event, doubleClick);
|
||||
}
|
||||
|
||||
private void drawButtons(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
drawBatteryModeButton(guiGraphics, mouseX, mouseY);
|
||||
|
||||
for (int index = 2; index < this.menu.buttonCount(); index++) {
|
||||
Bounds bounds = buttonBounds(indexToRow(index), indexToColumn(index));
|
||||
drawButton(guiGraphics, bounds, index, bounds.contains(mouseX, mouseY));
|
||||
}
|
||||
}
|
||||
|
||||
private void drawBatteryModeButton(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
Bounds bounds = buttonBounds(0, 1);
|
||||
boolean hovering = bounds.contains(mouseX, mouseY);
|
||||
int textureIndex = this.menu.isButtonEnabled(0) ? 0 : 1;
|
||||
int offset = hovering ? BUTTON_WIDTH + BUTTON_TEXTURE_GAP : 0;
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, bounds.x, bounds.y,
|
||||
BUTTON_TEXTURE_START_X + offset,
|
||||
BUTTON_TEXTURE_START_Y + textureIndex * (BUTTON_HEIGHT + BUTTON_TEXTURE_GAP),
|
||||
BUTTON_WIDTH, BUTTON_HEIGHT, 256, 256);
|
||||
}
|
||||
|
||||
private void drawButton(GuiGraphics guiGraphics, Bounds bounds, int index, boolean hovering) {
|
||||
int textureIndex = index;
|
||||
int offset = hovering ? BUTTON_WIDTH + BUTTON_TEXTURE_GAP : 0;
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, bounds.x, bounds.y,
|
||||
BUTTON_TEXTURE_START_X + offset,
|
||||
BUTTON_TEXTURE_START_Y + textureIndex * (BUTTON_HEIGHT + BUTTON_TEXTURE_GAP),
|
||||
BUTTON_WIDTH, BUTTON_HEIGHT, 256, 256);
|
||||
}
|
||||
|
||||
private void drawOverlays(GuiGraphics guiGraphics) {
|
||||
for (int index = 2; index < this.menu.buttonCount(); index++) {
|
||||
if (!this.menu.isButtonEnabled(index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bounds bounds = buttonBounds(indexToRow(index), indexToColumn(index));
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, bounds.x + 4, bounds.y + 4,
|
||||
CHECK_TEXTURE_X, CHECK_TEXTURE_Y, 16, 16, 256, 256);
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < BatteriesMenu.FILTER_SLOT_COUNT; slot++) {
|
||||
if (this.menu.isFilterSlotEmpty(slot)) {
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE,
|
||||
this.leftPos + INVENTORY_START_X + 18 * slot,
|
||||
this.topPos + INVENTORY_START_Y,
|
||||
PLUS_TEXTURE_X, PLUS_TEXTURE_Y, 16, 16, 256, 256);
|
||||
}
|
||||
}
|
||||
|
||||
for (int armorIndex = 0; armorIndex < 4; armorIndex++) {
|
||||
int slot = 39 - armorIndex;
|
||||
ItemStack stack = this.menu.playerInventoryItem(slot);
|
||||
if (stack.isEmpty()) {
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE,
|
||||
this.leftPos + ARMOR_START_X,
|
||||
this.topPos + ARMOR_START_Y + armorIndex * 18,
|
||||
ARMOR_TEXTURE_X + armorIndex * 17,
|
||||
ARMOR_TEXTURE_Y,
|
||||
16, 16, 256, 256);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.menu.playerInventoryItem(40).isEmpty()) {
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE,
|
||||
this.leftPos + ARMOR_START_X,
|
||||
this.topPos + 91,
|
||||
110, 170, 16, 16, 256, 256);
|
||||
}
|
||||
}
|
||||
|
||||
private String hoveredButtonTranslationKey(int mouseX, int mouseY) {
|
||||
if (buttonBounds(0, 1).contains(mouseX, mouseY)) {
|
||||
return this.menu.isButtonEnabled(0)
|
||||
? "batteries.gui.button.whitelist"
|
||||
: "batteries.gui.button.blacklist";
|
||||
}
|
||||
|
||||
for (int index = 2; index < this.menu.buttonCount(); index++) {
|
||||
Bounds bounds = buttonBounds(indexToRow(index), indexToColumn(index));
|
||||
if (bounds.contains(mouseX, mouseY)) {
|
||||
return BatteriesMenu.buttonTranslationKey(index);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void pressMenuButton(int index) {
|
||||
if (this.minecraft.gameMode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.minecraft.gameMode.handleInventoryButtonClick(this.menu.containerId, index);
|
||||
this.menu.toggleClientPreview(index);
|
||||
}
|
||||
|
||||
private void renderCustomTooltip(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
String hoverKey = hoveredButtonTranslationKey(mouseX, mouseY);
|
||||
if (hoverKey != null) {
|
||||
List<Component> tooltip = new ArrayList<>();
|
||||
tooltip.add(Component.translatable(hoverKey));
|
||||
guiGraphics.setComponentTooltipForNextFrame(this.font, tooltip, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
private Component titleText() {
|
||||
ItemStack battery = this.menu.batteryStack();
|
||||
if (!(battery.getItem() instanceof BatteryItem)) {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
return Component.literal(battery.getHoverName().getString()
|
||||
+ " - "
|
||||
+ BatteryItem.formatCompactAmount(BatteryItem.getStoredEnergy(battery))
|
||||
+ " / "
|
||||
+ BatteryItem.formatCompactAmount(BatteryItem.getEnergyCapacity(battery))
|
||||
+ " E");
|
||||
}
|
||||
|
||||
private void renderTitle(GuiGraphics guiGraphics) {
|
||||
Component title = titleText();
|
||||
int leftAlignedX = INVENTORY_START_X;
|
||||
int titleY = 8;
|
||||
int maxWidth = this.imageWidth - INVENTORY_START_X - 8;
|
||||
int textWidth = this.font.width(title);
|
||||
float scale = textWidth > maxWidth ? maxWidth / (float) textWidth : 1.0F;
|
||||
|
||||
guiGraphics.pose().pushMatrix();
|
||||
guiGraphics.pose().scale(scale, scale);
|
||||
guiGraphics.drawString(this.font, title, Math.round(leftAlignedX / scale), Math.round(titleY / scale), 0xFF202020, false);
|
||||
guiGraphics.pose().popMatrix();
|
||||
}
|
||||
|
||||
private Bounds buttonBounds(int rowIndex, int colIndex) {
|
||||
return new Bounds(
|
||||
this.leftPos + BUTTON_START_X + colIndex * (BUTTON_WIDTH + BUTTON_SCREEN_GAP_X),
|
||||
this.topPos + BUTTON_START_Y + rowIndex * (BUTTON_HEIGHT + BUTTON_SCREEN_GAP_Y),
|
||||
BUTTON_WIDTH,
|
||||
BUTTON_HEIGHT
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean isBatteryModeToggle(int index) {
|
||||
return index == 0;
|
||||
}
|
||||
|
||||
private static int indexToRow(int index) {
|
||||
return index <= 3 ? 0 : 1;
|
||||
}
|
||||
|
||||
private static int indexToColumn(int index) {
|
||||
return switch (index) {
|
||||
case 0, 1, 4 -> 1;
|
||||
case 2, 5 -> 2;
|
||||
case 3, 6 -> 3;
|
||||
default -> 1;
|
||||
};
|
||||
}
|
||||
|
||||
private record Bounds(int x, int y, int width, int height) {
|
||||
private boolean contains(double mouseX, double mouseY) {
|
||||
return mouseX >= this.x && mouseX < this.x + this.width && mouseY >= this.y && mouseY < this.y + this.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.trunksbomb.batteries.client.screen;
|
||||
|
||||
import com.trunksbomb.batteries.block.BatteryBlockData;
|
||||
import com.trunksbomb.batteries.block.entity.BatteryBlockEntity;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import com.trunksbomb.batteries.menu.BatteryBlockMenu;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class BatteryBlockScreen extends AbstractContainerScreen<BatteryBlockMenu> {
|
||||
private static final Identifier TEXTURE = Identifier.fromNamespaceAndPath("batteries", "textures/battery_block_gui.png");
|
||||
private static final int TEXTURE_WIDTH = 176;
|
||||
private static final int TEXTURE_HEIGHT = 164;
|
||||
private static final int BUTTON_WIDTH = 50;
|
||||
private static final int BUTTON_HEIGHT = 20;
|
||||
private static final int BUTTON_START_X = 8;
|
||||
private static final int BUTTON_START_Y = 18;
|
||||
private static final int BUTTON_GAP_X = 5;
|
||||
private static final int BUTTON_GAP_Y = 4;
|
||||
|
||||
public BatteryBlockScreen(BatteryBlockMenu menu, Inventory playerInventory, Component title) {
|
||||
super(menu, playerInventory, title);
|
||||
this.imageWidth = TEXTURE_WIDTH;
|
||||
this.imageHeight = TEXTURE_HEIGHT;
|
||||
this.inventoryLabelY = this.imageHeight - 94;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
for (int index = 0; index < BatteryBlockMenu.SIDE_BUTTON_COUNT; index++) {
|
||||
int buttonIndex = index;
|
||||
this.addRenderableWidget(Button.builder(buttonLabel(index), button -> pressButton(buttonIndex))
|
||||
.bounds(this.leftPos + buttonX(index), this.topPos + buttonY(index), BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
.tooltip(buttonTooltip(index))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containerTick() {
|
||||
super.containerTick();
|
||||
int widgetIndex = 0;
|
||||
for (AbstractWidget widget : this.renderables.stream().filter(AbstractWidget.class::isInstance).map(AbstractWidget.class::cast).toList()) {
|
||||
if (widgetIndex >= BatteryBlockMenu.SIDE_BUTTON_COUNT) {
|
||||
break;
|
||||
}
|
||||
widget.setMessage(buttonLabel(widgetIndex));
|
||||
widget.setTooltip(buttonTooltip(widgetIndex));
|
||||
widgetIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) {
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
Component title = Component.literal(this.title.getString()
|
||||
+ " - "
|
||||
+ BatteryItem.formatCompactAmount(this.menu.storedEnergy())
|
||||
+ " / "
|
||||
+ BatteryItem.formatCompactAmount(this.menu.energyCapacity())
|
||||
+ " E");
|
||||
guiGraphics.drawString(this.font, title, 8, 6, 0xFF404040, false);
|
||||
guiGraphics.drawString(this.font, this.playerInventoryTitle, this.inventoryLabelX, this.inventoryLabelY, 0xFF404040, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NonNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
this.renderBackground(guiGraphics, mouseX, mouseY, partialTick);
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
||||
this.renderTooltip(guiGraphics, mouseX, mouseY);
|
||||
}
|
||||
|
||||
private void pressButton(int index) {
|
||||
if (this.minecraft.gameMode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.minecraft.gameMode.handleInventoryButtonClick(this.menu.containerId, index);
|
||||
this.menu.cycleClientPreview(index);
|
||||
}
|
||||
|
||||
private Component buttonLabel(int index) {
|
||||
return Component.literal(shortDirection(index) + ": " + shortMode(this.menu.sideMode(index)));
|
||||
}
|
||||
|
||||
private Tooltip buttonTooltip(int index) {
|
||||
return Tooltip.create(Component.literal(fullDirection(index) + ": " + fullMode(this.menu.sideMode(index))));
|
||||
}
|
||||
|
||||
private static int buttonX(int index) {
|
||||
return BUTTON_START_X + (index % 3) * (BUTTON_WIDTH + BUTTON_GAP_X);
|
||||
}
|
||||
|
||||
private static int buttonY(int index) {
|
||||
return BUTTON_START_Y + (index / 3) * (BUTTON_HEIGHT + BUTTON_GAP_Y);
|
||||
}
|
||||
|
||||
private static String shortDirection(int index) {
|
||||
return switch (BatteryBlockEntity.directionForIndex(index)) {
|
||||
case UP -> "U";
|
||||
case DOWN -> "D";
|
||||
case NORTH -> "N";
|
||||
case SOUTH -> "S";
|
||||
case WEST -> "W";
|
||||
case EAST -> "E";
|
||||
};
|
||||
}
|
||||
|
||||
private static String fullDirection(int index) {
|
||||
return switch (BatteryBlockEntity.directionForIndex(index)) {
|
||||
case UP -> "Up";
|
||||
case DOWN -> "Down";
|
||||
case NORTH -> "North";
|
||||
case SOUTH -> "South";
|
||||
case WEST -> "West";
|
||||
case EAST -> "East";
|
||||
};
|
||||
}
|
||||
|
||||
private static String shortMode(BatteryBlockData.SideMode mode) {
|
||||
return switch (mode) {
|
||||
case INPUT -> "In";
|
||||
case OUTPUT -> "Out";
|
||||
case BOTH -> "Both";
|
||||
case NONE -> "None";
|
||||
};
|
||||
}
|
||||
|
||||
private static String fullMode(BatteryBlockData.SideMode mode) {
|
||||
return switch (mode) {
|
||||
case INPUT -> "Input";
|
||||
case OUTPUT -> "Output";
|
||||
case BOTH -> "Both";
|
||||
case NONE -> "None";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.trunksbomb.batteries.client.screen;
|
||||
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import com.trunksbomb.batteries.menu.CoalGeneratorMenu;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
public class CoalGeneratorScreen extends AbstractContainerScreen<CoalGeneratorMenu> {
|
||||
private static final Identifier TEXTURE = Identifier.fromNamespaceAndPath("batteries", "textures/coal_generator_gui.png");
|
||||
private static final int FLAME_X = 81;
|
||||
private static final int FLAME_Y = 61;
|
||||
private static final int FLAME_WIDTH = 14;
|
||||
private static final int FLAME_HEIGHT = 14;
|
||||
private static final int FLAME_BORDER = 0xFF3A2A17;
|
||||
private static final int FLAME_BG = 0xFF5B4A2C;
|
||||
private static final int FLAME_FILL = 0xFFFFB347;
|
||||
private static final int FLAME_CORE = 0xFFFFE08A;
|
||||
|
||||
public CoalGeneratorScreen(CoalGeneratorMenu menu, Inventory playerInventory, Component title) {
|
||||
super(menu, playerInventory, title);
|
||||
this.imageWidth = 176;
|
||||
this.imageHeight = 164;
|
||||
this.inventoryLabelY = this.imageHeight - 94;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) {
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight, this.imageWidth, this.imageHeight);
|
||||
renderFlame(guiGraphics);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
Component title = Component.literal(this.title.getString()
|
||||
+ " - "
|
||||
+ BatteryItem.formatCompactAmount(this.menu.storedEnergy())
|
||||
+ " / "
|
||||
+ BatteryItem.formatCompactAmount(this.menu.energyCapacity())
|
||||
+ " E");
|
||||
guiGraphics.drawString(this.font, title, 8, 6, 0xFF404040, false);
|
||||
guiGraphics.drawString(this.font, this.playerInventoryTitle, this.inventoryLabelX, this.inventoryLabelY, 0x404040, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
this.renderBackground(guiGraphics, mouseX, mouseY, partialTick);
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
||||
this.renderTooltip(guiGraphics, mouseX, mouseY);
|
||||
}
|
||||
|
||||
private void renderFlame(GuiGraphics guiGraphics) {
|
||||
int maxBurn = this.menu.maxBurnTime();
|
||||
int burn = this.menu.burnTimeRemaining();
|
||||
int x = this.leftPos + FLAME_X;
|
||||
int y = this.topPos + FLAME_Y;
|
||||
|
||||
guiGraphics.fill(x, y, x + FLAME_WIDTH, y + FLAME_HEIGHT, FLAME_BORDER);
|
||||
guiGraphics.fill(x + 1, y + 1, x + FLAME_WIDTH - 1, y + FLAME_HEIGHT - 1, FLAME_BG);
|
||||
|
||||
if (maxBurn <= 0 || burn <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int flameHeight = Math.max(1, burn * (FLAME_HEIGHT - 2) / maxBurn);
|
||||
int top = y + FLAME_HEIGHT - 1 - flameHeight;
|
||||
guiGraphics.fill(x + 2, top, x + FLAME_WIDTH - 2, y + FLAME_HEIGHT - 2, FLAME_FILL);
|
||||
guiGraphics.fill(x + 4, Math.max(y + 2, top + 2), x + FLAME_WIDTH - 4, y + FLAME_HEIGHT - 4, FLAME_CORE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.trunksbomb.batteries.integration.jei;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import java.util.List;
|
||||
import mezz.jei.api.IModPlugin;
|
||||
import mezz.jei.api.JeiPlugin;
|
||||
import mezz.jei.api.constants.RecipeTypes;
|
||||
import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory;
|
||||
import mezz.jei.api.registration.IRecipeRegistration;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.CraftingRecipe;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.display.SlotDisplay;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
@JeiPlugin
|
||||
public class BatteriesJeiPlugin implements IModPlugin {
|
||||
private static final Identifier PLUGIN_UID = Identifier.parse(Batteries.MODID + ":jei_plugin");
|
||||
|
||||
@Override
|
||||
public @NonNull Identifier getPluginUid() {
|
||||
return PLUGIN_UID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRecipes(IRecipeRegistration registration) {
|
||||
IVanillaRecipeFactory recipeFactory = registration.getVanillaRecipeFactory();
|
||||
registration.addRecipes(RecipeTypes.CRAFTING, List.of(
|
||||
twoItemRecipe("battery_pickaxe", recipeFactory, Batteries.BATTERY_PICKAXE.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_PICKAXE, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_axe", recipeFactory, Batteries.BATTERY_AXE.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_AXE, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_shovel", recipeFactory, Batteries.BATTERY_SHOVEL.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_SHOVEL, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_hoe", recipeFactory, Batteries.BATTERY_HOE.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_HOE, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_sword", recipeFactory, Batteries.BATTERY_SWORD.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_SWORD, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_helmet", recipeFactory, Batteries.BATTERY_HELMET.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_HELMET, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_chestplate", recipeFactory, Batteries.BATTERY_CHESTPLATE.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_CHESTPLATE, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_leggings", recipeFactory, Batteries.BATTERY_LEGGINGS.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_LEGGINGS, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_boots", recipeFactory, Batteries.BATTERY_BOOTS.get(), CraftingBookCategory.EQUIPMENT, Items.DIAMOND_BOOTS, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_shield", recipeFactory, Batteries.BATTERY_SHIELD.get(), CraftingBookCategory.EQUIPMENT, Items.SHIELD, Batteries.BATTERY.get()),
|
||||
twoItemRecipe("battery_bow", recipeFactory, Batteries.BATTERY_BOW.get(), CraftingBookCategory.EQUIPMENT, Items.BOW, Batteries.BATTERY.get()),
|
||||
recipe("battery1_upgrade", recipeFactory, Batteries.BATTERY1.get(), Items.REDSTONE, Items.GOLD_BLOCK, Batteries.BATTERY.get(), Items.REDSTONE_BLOCK),
|
||||
recipe("battery2_upgrade", recipeFactory, Batteries.BATTERY2.get(), Items.REDSTONE, Items.DIAMOND, Batteries.BATTERY1.get(), Items.REDSTONE_BLOCK),
|
||||
recipe("battery3_upgrade", recipeFactory, Batteries.BATTERY3.get(), Items.REDSTONE, Items.DIAMOND_BLOCK, Batteries.BATTERY2.get(), Items.REDSTONE_BLOCK),
|
||||
recipe("battery_ender_upgrade", recipeFactory, Batteries.BATTERY_ENDER.get(), Items.END_STONE, Items.DIAMOND_BLOCK, Batteries.BATTERY3.get(), Items.ENDER_PEARL)
|
||||
));
|
||||
}
|
||||
|
||||
private static RecipeHolder<CraftingRecipe> recipe(String name, IVanillaRecipeFactory recipeFactory, ItemLike result,
|
||||
ItemLike corner, ItemLike topBottom, ItemLike center, ItemLike middleSide) {
|
||||
CraftingRecipe recipe = recipeFactory.createShapedRecipeBuilder(CraftingBookCategory.REDSTONE, new SlotDisplay.ItemStackSlotDisplay(new ItemStack(result)))
|
||||
.define('r', Ingredient.of(corner))
|
||||
.define('g', Ingredient.of(topBottom))
|
||||
.define('i', Ingredient.of(center))
|
||||
.define('b', Ingredient.of(middleSide))
|
||||
.pattern("rgr")
|
||||
.pattern("bib")
|
||||
.pattern("rgr")
|
||||
.build();
|
||||
ResourceKey<Recipe<?>> recipeId = ResourceKey.create(Registries.RECIPE, Identifier.parse(Batteries.MODID + ":jei/" + name));
|
||||
return new RecipeHolder<>(recipeId, recipe);
|
||||
}
|
||||
|
||||
private static RecipeHolder<CraftingRecipe> twoItemRecipe(String name, IVanillaRecipeFactory recipeFactory, ItemLike result,
|
||||
CraftingBookCategory category, ItemLike first, ItemLike second) {
|
||||
CraftingRecipe recipe = recipeFactory.createShapedRecipeBuilder(category, new SlotDisplay.ItemStackSlotDisplay(new ItemStack(result)))
|
||||
.define('a', Ingredient.of(first))
|
||||
.define('b', Ingredient.of(second))
|
||||
.pattern("ab")
|
||||
.build();
|
||||
ResourceKey<Recipe<?>> recipeId = ResourceKey.create(Registries.RECIPE, Identifier.parse(Batteries.MODID + ":jei/" + name));
|
||||
return new RecipeHolder<>(recipeId, recipe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.trunksbomb.batteries.item;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class BatteryBlockItem extends BlockItem implements PoweredItem {
|
||||
public static final int BASE_CAPACITY = 50_000;
|
||||
|
||||
public BatteryBlockItem(Block block, Properties properties) {
|
||||
super(block, properties.stacksTo(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnergyHandler createEnergyHandler(ItemStack stack) {
|
||||
return new PoweredEnergyHandler(stack, getEnergyCapacity(stack), this.getMaxTransfer(stack), getStoredEnergy(stack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxTransfer(ItemStack stack) {
|
||||
return EnergyTierHelper.transferRateForCapacity(getEnergyCapacity(stack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBarVisible(@NonNull ItemStack stack) {
|
||||
return getEnergyCapacity(stack) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBarWidth(@NonNull ItemStack stack) {
|
||||
int capacity = getEnergyCapacity(stack);
|
||||
return capacity <= 0 ? 0 : Math.round(13.0F * getStoredEnergy(stack) / (float) capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBarColor(@NonNull ItemStack stack) {
|
||||
return this.getEnergyBarColor(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(@NonNull ItemStack stack, @NonNull TooltipContext context, net.minecraft.world.item.component.@NonNull TooltipDisplay tooltipDisplay, @NonNull Consumer<Component> tooltipAdder, @NonNull TooltipFlag flag) {
|
||||
this.addEnergyTooltip(stack, tooltipAdder, flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCauseReequipAnimation(@NonNull ItemStack oldStack, @NonNull ItemStack newStack, boolean slotChanged) {
|
||||
return this.shouldCauseEnergyReequipAnimation(oldStack, newStack, slotChanged);
|
||||
}
|
||||
|
||||
public static int getEnergyCapacity(ItemStack stack) {
|
||||
int capacity = PoweredItemEnergy.getEnergyCapacity(stack);
|
||||
return capacity > 0 ? capacity : BASE_CAPACITY;
|
||||
}
|
||||
|
||||
public static int getStoredEnergy(ItemStack stack) {
|
||||
return Math.min(PoweredItemEnergy.getStoredEnergy(stack), getEnergyCapacity(stack));
|
||||
}
|
||||
|
||||
public static void initializeDefaults(ItemStack stack) {
|
||||
if (PoweredItemEnergy.getEnergyCapacity(stack) <= 0) {
|
||||
PoweredItemEnergy.setEnergyCapacity(stack, BASE_CAPACITY);
|
||||
PoweredItemEnergy.setStoredEnergy(stack, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.trunksbomb.batteries.item;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.TransactionContext;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
final class BatteryEnergyHandler extends SimpleEnergyHandler {
|
||||
private final ItemStack stack;
|
||||
private final boolean creative;
|
||||
|
||||
BatteryEnergyHandler(ItemStack stack, int capacity, int maxTransfer, int energy, boolean creative) {
|
||||
super(capacity, maxTransfer, maxTransfer, creative ? capacity : energy);
|
||||
this.stack = stack;
|
||||
this.creative = creative;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAmountAsLong() {
|
||||
return this.creative ? this.capacity : super.getAmountAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insert(int amount, @NonNull TransactionContext transaction) {
|
||||
if (this.creative) {
|
||||
return amount;
|
||||
}
|
||||
return super.insert(amount, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extract(int amount, @NonNull TransactionContext transaction) {
|
||||
if (this.creative) {
|
||||
return amount;
|
||||
}
|
||||
return super.extract(amount, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnergyChanged(int previousAmount) {
|
||||
if (!this.creative) {
|
||||
BatteryItem.setStoredEnergy(this.stack, this.energy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
package com.trunksbomb.batteries.item;
|
||||
|
||||
import com.trunksbomb.batteries.BatteriesConfig;
|
||||
import com.trunksbomb.batteries.item.BatteryItemData.Tier;
|
||||
import com.trunksbomb.batteries.menu.BatteriesMenu;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.transfer.access.ItemAccess;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
import net.neoforged.neoforge.transfer.transaction.Transaction;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class BatteryItem extends Item {
|
||||
private static final String BATTERY_UUID_KEY = "battery_uuid";
|
||||
private static final String WHITELIST_KEY = "whitelist";
|
||||
private static final String CHARGE_HOTBAR_KEY = "charge_hotbar";
|
||||
private static final String CHARGE_INVENTORY_KEY = "charge_inventory";
|
||||
private static final String CHARGE_ARMOR_KEY = "charge_armor";
|
||||
private static final String CHARGE_FAIRLY_KEY = "charge_fairly";
|
||||
private static final String CHARGE_MACHINE_KEY = "charge_machine";
|
||||
private static final String FILTER_SLOT_KEY_PREFIX = "filter_slot_";
|
||||
private final Tier tier;
|
||||
|
||||
public BatteryItem(Tier tier, Properties properties) {
|
||||
super(properties.stacksTo(1));
|
||||
this.tier = tier;
|
||||
}
|
||||
|
||||
public Tier tier() {
|
||||
return this.tier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull InteractionResult useOn(@NonNull UseOnContext context) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull InteractionResult use(@NonNull Level level, @NonNull Player player, @NonNull InteractionHand usedHand) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
ItemStack stack = player.getItemInHand(usedHand);
|
||||
MenuProvider provider = new SimpleMenuProvider(
|
||||
(containerId, inventory, menuPlayer) -> BatteriesMenu.forBattery(containerId, inventory, usedHand),
|
||||
stack.getHoverName()
|
||||
);
|
||||
serverPlayer.openMenu(provider, buffer -> BatteriesMenu.writeBatteryMenu(buffer, usedHand));
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(@NonNull ItemStack stack, @NonNull ServerLevel level, @NonNull Entity entity, @Nullable EquipmentSlot slot) {
|
||||
if (!(entity instanceof ServerPlayer player) || !(stack.getItem() instanceof BatteryItem batteryItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (batteryItem.tier != Tier.CREATIVE && getStoredEnergy(stack) <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isChargeHotbarEnabled(stack)) {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
chargeItemIfValid(stack, player.getInventory().getItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (isChargeInventoryEnabled(stack)) {
|
||||
for (int i = 9; i < 36; i++) {
|
||||
chargeItemIfValid(stack, player.getInventory().getItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (isChargeWornEnabled(stack)) {
|
||||
for (int i = 36; i <= 40; i++) {
|
||||
chargeItemIfValid(stack, player.getInventory().getItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (isChargeMachineEnabled(stack)) {
|
||||
chargeNearbyMachines(stack, player, level);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCauseReequipAnimation(@NonNull ItemStack oldStack, @NonNull ItemStack newStack, boolean slotChanged) {
|
||||
return slotChanged || !newStack.is(oldStack.getItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBarVisible(@NonNull ItemStack stack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBarWidth(@NonNull ItemStack stack) {
|
||||
int capacity = getEnergyCapacity(stack);
|
||||
if (capacity <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.round(13.0F * getStoredEnergy(stack) / (float) capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBarColor(@NonNull ItemStack stack) {
|
||||
return 0x55CC55;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void appendHoverText(@NonNull ItemStack stack, @NonNull TooltipContext context, net.minecraft.world.item.component.@NonNull TooltipDisplay tooltipDisplay, @NonNull Consumer<Component> tooltipAdder, @NonNull TooltipFlag flag) {
|
||||
tooltipAdder.accept(Component.translatable("batteries.tooltip.energy", formatCompactAmount(getStoredEnergy(stack)), formatCompactAmount(getEnergyCapacity(stack)))
|
||||
.withStyle(ChatFormatting.GREEN));
|
||||
}
|
||||
|
||||
public static EnergyHandler createEnergyHandler(ItemStack stack) {
|
||||
if (!(stack.getItem() instanceof BatteryItem batteryItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int capacity = BatteryItemData.getEnergyCapacity(batteryItem.tier);
|
||||
int maxTransfer = BatteryItemData.getMaxTransfer(batteryItem.tier);
|
||||
return new BatteryEnergyHandler(stack, capacity, maxTransfer, getStoredEnergy(stack), batteryItem.tier == Tier.CREATIVE);
|
||||
}
|
||||
|
||||
public static int getStoredEnergy(ItemStack stack) {
|
||||
if (stack.getItem() instanceof BatteryItem batteryItem) {
|
||||
return BatteryItemData.getStoredEnergy(stack, batteryItem.tier);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setStoredEnergy(ItemStack stack, int energy) {
|
||||
BatteryItemData.setStoredEnergy(stack, energy);
|
||||
}
|
||||
|
||||
public static int getEnergyCapacity(ItemStack stack) {
|
||||
if (stack.getItem() instanceof BatteryItem batteryItem) {
|
||||
return BatteryItemData.getEnergyCapacity(batteryItem.tier);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int getMaxTransfer(ItemStack stack) {
|
||||
if (stack.getItem() instanceof BatteryItem batteryItem) {
|
||||
return BatteryItemData.getMaxTransfer(batteryItem.tier);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static EnergyHandler getEnergyHandler(ItemStack stack) {
|
||||
return stack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(stack));
|
||||
}
|
||||
|
||||
public static UUID ensureBatteryId(ItemStack stack) {
|
||||
CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY).copyTag();
|
||||
String existingUuid = tag.getStringOr(BATTERY_UUID_KEY, "");
|
||||
if (!existingUuid.isBlank()) {
|
||||
return UUID.fromString(existingUuid);
|
||||
}
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, data -> data.putString(BATTERY_UUID_KEY, uuid.toString()));
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public static UUID getBatteryId(ItemStack stack) {
|
||||
CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY).copyTag();
|
||||
String uuid = tag.getStringOr(BATTERY_UUID_KEY, "");
|
||||
return uuid.isBlank() ? null : UUID.fromString(uuid);
|
||||
}
|
||||
|
||||
public static ItemStack findLinkedEnderBattery(Player player, UUID batteryId) {
|
||||
if (batteryId == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < player.getInventory().getContainerSize(); slot++) {
|
||||
ItemStack stack = player.getInventory().getItem(slot);
|
||||
if (!(stack.getItem() instanceof BatteryItem batteryItem) || batteryItem.tier() != Tier.ENDER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UUID stackId = getBatteryId(stack);
|
||||
if (batteryId.equals(stackId)) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
private static void chargeItemIfValid(ItemStack battery, ItemStack receivingStack) {
|
||||
if (receivingStack.isEmpty() || receivingStack == battery || receivingStack.getItem() instanceof BatteryItem || !matchesFilter(battery, receivingStack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnergyHandler receivingEnergy = receivingStack.getCapability(Capabilities.Energy.ITEM, ItemAccess.forStack(receivingStack));
|
||||
if (receivingEnergy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
transferEnergyToTarget(battery, receivingEnergy, isFairChargingEnabled(battery));
|
||||
}
|
||||
|
||||
private static void chargeNearbyMachines(ItemStack battery, ServerPlayer player, ServerLevel level) {
|
||||
int range = BatteriesConfig.machineChargeRange();
|
||||
BlockPos center = player.blockPosition();
|
||||
boolean fairCharging = isFairChargingEnabled(battery);
|
||||
|
||||
for (BlockPos pos : BlockPos.betweenClosed(center.offset(-range, -range, -range), center.offset(range, range, range))) {
|
||||
if (!level.isLoaded(pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Direction side = getFacingFromBlockToPlayer(player, pos);
|
||||
EnergyHandler energyHandler = level.getCapability(Capabilities.Energy.BLOCK, pos.immutable(), side);
|
||||
if (energyHandler == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
transferEnergyToTarget(battery, energyHandler, fairCharging);
|
||||
if (((BatteryItem) battery.getItem()).tier != Tier.CREATIVE && getStoredEnergy(battery) <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Direction getFacingFromBlockToPlayer(Player player, BlockPos pos) {
|
||||
double dx = player.getX() - (pos.getX() + 0.5D);
|
||||
double dy = player.getEyeY() - (pos.getY() + 0.5D);
|
||||
double dz = player.getZ() - (pos.getZ() + 0.5D);
|
||||
double absX = Math.abs(dx);
|
||||
double absY = Math.abs(dy);
|
||||
double absZ = Math.abs(dz);
|
||||
if (absY >= absX && absY >= absZ) {
|
||||
return dy >= 0.0D ? Direction.UP : Direction.DOWN;
|
||||
}
|
||||
if (absX >= absZ) {
|
||||
return dx >= 0.0D ? Direction.EAST : Direction.WEST;
|
||||
}
|
||||
return dz >= 0.0D ? Direction.SOUTH : Direction.NORTH;
|
||||
}
|
||||
|
||||
private static void transferEnergyToTarget(ItemStack battery, EnergyHandler target, boolean fairCharging) {
|
||||
EnergyHandler batteryEnergy = getEnergyHandler(battery);
|
||||
if (batteryEnergy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int targetStored = target.getAmountAsInt();
|
||||
int targetCapacity = target.getCapacityAsInt();
|
||||
if (targetCapacity <= targetStored) {
|
||||
return;
|
||||
}
|
||||
|
||||
int batteryStored = getStoredEnergy(battery);
|
||||
if (fairCharging && batteryStored <= targetStored) {
|
||||
return;
|
||||
}
|
||||
|
||||
int transferLimit = Math.min(getMaxTransfer(battery), targetCapacity - targetStored);
|
||||
if (transferLimit <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (((BatteryItem) battery.getItem()).tier == Tier.CREATIVE) {
|
||||
try (Transaction transaction = Transaction.openRoot()) {
|
||||
if (target.insert(transferLimit, transaction) > 0) {
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try (Transaction transaction = Transaction.openRoot()) {
|
||||
int accepted = target.insert(transferLimit, transaction);
|
||||
if (accepted <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int extracted = batteryEnergy.extract(accepted, transaction);
|
||||
if (extracted != accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchesFilter(ItemStack battery, ItemStack candidate) {
|
||||
CompoundTag tag = battery.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY).copyTag();
|
||||
boolean whitelist = tag.getBooleanOr(WHITELIST_KEY, false);
|
||||
boolean hasFilterEntries = false;
|
||||
boolean matched = false;
|
||||
|
||||
for (int i = 0; i < BatteriesMenu.FILTER_SLOT_COUNT; i++) {
|
||||
String itemId = tag.getStringOr(filterSlotKey(i), "");
|
||||
if (itemId.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hasFilterEntries = true;
|
||||
Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId));
|
||||
if (candidate.is(item)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasFilterEntries) {
|
||||
return !whitelist;
|
||||
}
|
||||
|
||||
return whitelist == matched;
|
||||
}
|
||||
|
||||
private static boolean isChargeHotbarEnabled(ItemStack stack) {
|
||||
return getToggle(stack, CHARGE_HOTBAR_KEY);
|
||||
}
|
||||
|
||||
private static boolean isChargeInventoryEnabled(ItemStack stack) {
|
||||
return getToggle(stack, CHARGE_INVENTORY_KEY);
|
||||
}
|
||||
|
||||
private static boolean isChargeWornEnabled(ItemStack stack) {
|
||||
return getToggle(stack, CHARGE_ARMOR_KEY);
|
||||
}
|
||||
|
||||
private static boolean isFairChargingEnabled(ItemStack stack) {
|
||||
return getToggle(stack, CHARGE_FAIRLY_KEY);
|
||||
}
|
||||
|
||||
private static boolean isChargeMachineEnabled(ItemStack stack) {
|
||||
return getToggle(stack, CHARGE_MACHINE_KEY);
|
||||
}
|
||||
|
||||
private static boolean getToggle(ItemStack stack, String key) {
|
||||
CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY).copyTag();
|
||||
return tag.getBooleanOr(key, false);
|
||||
}
|
||||
|
||||
private static String filterSlotKey(int index) {
|
||||
return FILTER_SLOT_KEY_PREFIX + index;
|
||||
}
|
||||
|
||||
public static String formatWithCommas(int amount) {
|
||||
return BatteryItemData.formatWithCommas(amount);
|
||||
}
|
||||
|
||||
public static String formatCompactAmount(int amount) {
|
||||
return BatteryItemData.formatCompactAmount(amount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package com.trunksbomb.batteries.menu;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
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.ClickType;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.inventory.SimpleContainerData;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
|
||||
public class BatteriesMenu extends AbstractContainerMenu {
|
||||
public static final int BATTERY_BUTTON_COUNT = 7;
|
||||
public static final int FILTER_SLOT_COUNT = 9;
|
||||
|
||||
private static final String BATTERY_WHITELIST_KEY = "whitelist";
|
||||
private static final String FILTER_SLOT_KEY_PREFIX = "filter_slot_";
|
||||
private static final String[] BATTERY_BUTTON_KEYS = new String[] {
|
||||
"whitelist",
|
||||
"blacklist",
|
||||
"charge_hotbar",
|
||||
"charge_inventory",
|
||||
"charge_armor",
|
||||
"charge_fairly",
|
||||
"charge_machine"
|
||||
};
|
||||
|
||||
private final InteractionHand hand;
|
||||
private final ContainerData toggleData;
|
||||
private final SimpleContainer filterSlots;
|
||||
private final Inventory playerInventory;
|
||||
|
||||
public BatteriesMenu(int containerId, Inventory inventory, RegistryFriendlyByteBuf buffer) {
|
||||
this(containerId, inventory, readHand(buffer), new SimpleContainerData(BATTERY_BUTTON_COUNT));
|
||||
}
|
||||
|
||||
private BatteriesMenu(int containerId, Inventory inventory, InteractionHand hand, ContainerData toggleData) {
|
||||
super(Batteries.BATTERIES_MENU.get(), containerId);
|
||||
this.hand = hand;
|
||||
this.toggleData = toggleData;
|
||||
this.playerInventory = inventory;
|
||||
this.filterSlots = new SimpleContainer(FILTER_SLOT_COUNT);
|
||||
this.addDataSlots(toggleData);
|
||||
addSlots(inventory);
|
||||
}
|
||||
|
||||
public static BatteriesMenu forBattery(int containerId, Inventory inventory, InteractionHand hand) {
|
||||
SimpleContainerData data = new SimpleContainerData(BATTERY_BUTTON_COUNT);
|
||||
ItemStack stack = inventory.player.getItemInHand(hand);
|
||||
for (int i = 0; i < BATTERY_BUTTON_COUNT; i++) {
|
||||
data.set(i, getBatteryToggle(stack, resolveStateIndex(i)) ? 1 : 0);
|
||||
}
|
||||
BatteriesMenu menu = new BatteriesMenu(containerId, inventory, hand, data);
|
||||
menu.loadFilterSlots(stack);
|
||||
return menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clickMenuButton(Player player, int buttonId) {
|
||||
if (buttonId < 0 || buttonId >= this.toggleData.getCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int stateIndex = resolveStateIndex(buttonId);
|
||||
int newValue = this.toggleData.get(stateIndex) == 0 ? 1 : 0;
|
||||
this.toggleData.set(stateIndex, newValue);
|
||||
|
||||
if (!player.level().isClientSide() && this.hand != null) {
|
||||
ItemStack stack = player.getItemInHand(this.hand);
|
||||
setBatteryToggle(stack, stateIndex, newValue == 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int slotId, int button, ClickType clickType, Player player) {
|
||||
if (slotId >= 0 && slotId < FILTER_SLOT_COUNT) {
|
||||
Slot slot = this.slots.get(slotId);
|
||||
ItemStack carried = this.getCarried();
|
||||
if (!carried.isEmpty() && slot.mayPlace(carried)) {
|
||||
ItemStack ghostStack = carried.copy();
|
||||
ghostStack.setCount(1);
|
||||
slot.set(ghostStack);
|
||||
} else {
|
||||
slot.set(ItemStack.EMPTY);
|
||||
}
|
||||
if (!player.level().isClientSide() && this.hand != null) {
|
||||
saveFilterSlots(player.getItemInHand(this.hand));
|
||||
}
|
||||
this.broadcastChanges();
|
||||
return;
|
||||
}
|
||||
|
||||
super.clicked(slotId, button, clickType, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int buttonCount() {
|
||||
return BATTERY_BUTTON_COUNT;
|
||||
}
|
||||
|
||||
public boolean isButtonEnabled(int index) {
|
||||
return this.toggleData.get(resolveStateIndex(index)) == 1;
|
||||
}
|
||||
|
||||
public void toggleClientPreview(int index) {
|
||||
if (index < 0 || index >= this.buttonCount()) {
|
||||
return;
|
||||
}
|
||||
int stateIndex = resolveStateIndex(index);
|
||||
this.toggleData.set(stateIndex, this.toggleData.get(stateIndex) == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
public static String buttonTranslationKey(int index) {
|
||||
return "batteries.gui.button." + BATTERY_BUTTON_KEYS[index];
|
||||
}
|
||||
|
||||
private static InteractionHand readHand(RegistryFriendlyByteBuf buffer) {
|
||||
return buffer.readEnum(InteractionHand.class);
|
||||
}
|
||||
|
||||
public static void writeBatteryMenu(RegistryFriendlyByteBuf buffer, InteractionHand hand) {
|
||||
buffer.writeEnum(hand);
|
||||
}
|
||||
|
||||
private static boolean getBatteryToggle(ItemStack stack, int index) {
|
||||
CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
||||
CompoundTag tag = customData.copyTag();
|
||||
return tag.getBooleanOr(stateKey(index), false);
|
||||
}
|
||||
|
||||
private static void setBatteryToggle(ItemStack stack, int index, boolean enabled) {
|
||||
CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putBoolean(stateKey(index), enabled));
|
||||
}
|
||||
|
||||
private void addSlots(Inventory inventory) {
|
||||
for (int i = 0; i < FILTER_SLOT_COUNT; i++) {
|
||||
this.addSlot(new BatteryFilterSlot(this.filterSlots, i, 32 + i * 18, 19));
|
||||
}
|
||||
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
int x = 32 + col * 18;
|
||||
int y = 82 + row * 18;
|
||||
int index = col + row * 9 + 9;
|
||||
this.addSlot(new Slot(inventory, index, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
for (int col = 0; col < 9; col++) {
|
||||
int x = 32 + col * 18;
|
||||
int y = 140;
|
||||
this.addSlot(new Slot(inventory, col, x, y));
|
||||
}
|
||||
|
||||
for (int slotIndex = 39; slotIndex >= 36; slotIndex--) {
|
||||
int armorOffset = -slotIndex + 39;
|
||||
this.addSlot(new Slot(inventory, slotIndex, 7, 19 + armorOffset * 18));
|
||||
}
|
||||
|
||||
this.addSlot(new Slot(inventory, 40, 7, 91));
|
||||
}
|
||||
|
||||
private static int resolveStateIndex(int buttonId) {
|
||||
if (buttonId == 1) {
|
||||
return 0;
|
||||
}
|
||||
return buttonId;
|
||||
}
|
||||
|
||||
private static String stateKey(int index) {
|
||||
return switch (index) {
|
||||
case 0 -> BATTERY_WHITELIST_KEY;
|
||||
case 2 -> "charge_hotbar";
|
||||
case 3 -> "charge_inventory";
|
||||
case 4 -> "charge_armor";
|
||||
case 5 -> "charge_fairly";
|
||||
case 6 -> "charge_machine";
|
||||
default -> throw new IllegalArgumentException("Unexpected battery state index: " + index);
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isFilterSlotEmpty(int index) {
|
||||
return this.filterSlots != null && this.filterSlots.getItem(index).isEmpty();
|
||||
}
|
||||
|
||||
public ItemStack playerInventoryItem(int index) {
|
||||
return this.playerInventory.getItem(index);
|
||||
}
|
||||
|
||||
public ItemStack batteryStack() {
|
||||
return this.hand == null ? ItemStack.EMPTY : this.playerInventory.player.getItemInHand(this.hand);
|
||||
}
|
||||
|
||||
private void loadFilterSlots(ItemStack stack) {
|
||||
if (this.filterSlots == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
||||
CompoundTag tag = customData.copyTag();
|
||||
for (int i = 0; i < FILTER_SLOT_COUNT; i++) {
|
||||
this.filterSlots.setItem(i, decodeFilterStack(tag.getStringOr(filterSlotKey(i), "")));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveFilterSlots(ItemStack stack) {
|
||||
if (this.filterSlots == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
|
||||
for (int i = 0; i < FILTER_SLOT_COUNT; i++) {
|
||||
ItemStack filterStack = this.filterSlots.getItem(i);
|
||||
if (filterStack.isEmpty()) {
|
||||
tag.remove(filterSlotKey(i));
|
||||
} else {
|
||||
tag.putString(filterSlotKey(i), BuiltInRegistries.ITEM.getKey(filterStack.getItem()).toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static ItemStack decodeFilterStack(String itemId) {
|
||||
if (itemId.isBlank()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack stack = new ItemStack(BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId)));
|
||||
return stack.isEmpty() ? ItemStack.EMPTY : stack;
|
||||
}
|
||||
|
||||
private static String filterSlotKey(int index) {
|
||||
return FILTER_SLOT_KEY_PREFIX + index;
|
||||
}
|
||||
|
||||
private static class BatteryFilterSlot extends Slot {
|
||||
public BatteryFilterSlot(SimpleContainer container, int slot, int x, int y) {
|
||||
super(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack stack) {
|
||||
return !(stack.getItem() instanceof BatteryItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.trunksbomb.batteries.menu;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.block.BatteryBlockData;
|
||||
import com.trunksbomb.batteries.block.entity.BatteryBlockEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
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.inventory.ContainerLevelAccess;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class BatteryBlockMenu extends AbstractContainerMenu {
|
||||
public static final int SIDE_BUTTON_COUNT = 6;
|
||||
private static final int ENERGY_INDEX = 6;
|
||||
private static final int CAPACITY_INDEX = 7;
|
||||
|
||||
private final ContainerLevelAccess access;
|
||||
private final ContainerData data;
|
||||
|
||||
public BatteryBlockMenu(int containerId, Inventory inventory, RegistryFriendlyByteBuf buffer) {
|
||||
this(containerId, inventory, buffer.readBlockPos(), createPlaceholderData());
|
||||
}
|
||||
|
||||
private BatteryBlockMenu(int containerId, Inventory inventory, BlockPos blockPos, ContainerData data) {
|
||||
super(Batteries.BATTERY_BLOCK_MENU.get(), containerId);
|
||||
this.access = ContainerLevelAccess.create(inventory.player.level(), blockPos);
|
||||
this.data = data;
|
||||
this.addDataSlots(data);
|
||||
this.addPlayerSlots(inventory);
|
||||
}
|
||||
|
||||
public static BatteryBlockMenu forBlock(int containerId, Inventory inventory, BatteryBlockEntity blockEntity) {
|
||||
return new BatteryBlockMenu(containerId, inventory, blockEntity.getBlockPos(), new ContainerData() {
|
||||
@Override
|
||||
public int get(int index) {
|
||||
if (index >= 0 && index < SIDE_BUTTON_COUNT) {
|
||||
return blockEntity.getSideMode(BatteryBlockEntity.directionForIndex(index)).ordinal();
|
||||
}
|
||||
if (index == ENERGY_INDEX) {
|
||||
return blockEntity.getStoredEnergy();
|
||||
}
|
||||
if (index == CAPACITY_INDEX) {
|
||||
return blockEntity.getEnergyCapacity();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, int value) {
|
||||
if (index >= 0 && index < SIDE_BUTTON_COUNT) {
|
||||
BatteryBlockData.SideMode[] modes = BatteryBlockData.SideMode.values();
|
||||
blockEntity.setSideMode(BatteryBlockEntity.directionForIndex(index), modes[Math.max(0, Math.min(modes.length - 1, value))]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 8;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clickMenuButton(@NonNull Player player, int buttonId) {
|
||||
if (buttonId < 0 || buttonId >= SIDE_BUTTON_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.data.set(buttonId, (this.data.get(buttonId) + 1) % BatteryBlockData.SideMode.values().length);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.item.@NonNull ItemStack quickMoveStack(@NonNull Player player, int index) {
|
||||
return net.minecraft.world.item.ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@NonNull Player player) {
|
||||
return stillValid(this.access, player, Batteries.BATTERY_BLOCK.get());
|
||||
}
|
||||
|
||||
public BatteryBlockData.SideMode sideMode(int index) {
|
||||
return BatteryBlockData.SideMode.values()[this.data.get(index)];
|
||||
}
|
||||
|
||||
public int storedEnergy() {
|
||||
return this.data.get(ENERGY_INDEX);
|
||||
}
|
||||
|
||||
public int energyCapacity() {
|
||||
return this.data.get(CAPACITY_INDEX);
|
||||
}
|
||||
|
||||
public void cycleClientPreview(int index) {
|
||||
if (index >= 0 && index < SIDE_BUTTON_COUNT) {
|
||||
this.data.set(index, (this.data.get(index) + 1) % BatteryBlockData.SideMode.values().length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeBlockPos(RegistryFriendlyByteBuf buffer, BlockPos blockPos) {
|
||||
buffer.writeBlockPos(blockPos);
|
||||
}
|
||||
|
||||
private void addPlayerSlots(Inventory inventory) {
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col + row * 9 + 9, 8 + col * 18, 82 + row * 18));
|
||||
}
|
||||
}
|
||||
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col, 8 + col * 18, 140));
|
||||
}
|
||||
}
|
||||
|
||||
private static ContainerData createPlaceholderData() {
|
||||
return new ContainerData() {
|
||||
private final int[] values = new int[8];
|
||||
|
||||
@Override
|
||||
public int get(int index) {
|
||||
return this.values[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, int value) {
|
||||
this.values[index] = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return this.values.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.trunksbomb.batteries.menu;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.block.entity.CoalGeneratorBlockEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
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.inventory.ContainerLevelAccess;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class CoalGeneratorMenu extends AbstractContainerMenu {
|
||||
private static final int ENERGY_INDEX = 0;
|
||||
private static final int CAPACITY_INDEX = 1;
|
||||
private static final int BURN_TIME_INDEX = 2;
|
||||
private static final int MAX_BURN_TIME_INDEX = 3;
|
||||
private final ContainerLevelAccess access;
|
||||
private final ContainerData data;
|
||||
|
||||
public CoalGeneratorMenu(int containerId, Inventory inventory, RegistryFriendlyByteBuf buffer) {
|
||||
this(containerId, inventory, buffer.readBlockPos(), createPlaceholderData(), new net.minecraft.world.SimpleContainer(1));
|
||||
}
|
||||
|
||||
private CoalGeneratorMenu(int containerId, Inventory inventory, BlockPos blockPos, ContainerData data, net.minecraft.world.Container fuelContainer) {
|
||||
super(Batteries.COAL_GENERATOR_MENU.get(), containerId);
|
||||
this.access = ContainerLevelAccess.create(inventory.player.level(), blockPos);
|
||||
this.data = data;
|
||||
this.addDataSlots(data);
|
||||
this.addSlot(new FuelSlot(fuelContainer, 0, 80, 42));
|
||||
addPlayerSlots(inventory);
|
||||
}
|
||||
|
||||
public static CoalGeneratorMenu forBlock(int containerId, Inventory inventory, CoalGeneratorBlockEntity blockEntity) {
|
||||
ContainerData data = new ContainerData() {
|
||||
@Override
|
||||
public int get(int index) {
|
||||
return switch (index) {
|
||||
case ENERGY_INDEX -> blockEntity.getStoredEnergy();
|
||||
case CAPACITY_INDEX -> blockEntity.getEnergyCapacity();
|
||||
case BURN_TIME_INDEX -> blockEntity.getBurnTimeRemaining();
|
||||
case MAX_BURN_TIME_INDEX -> blockEntity.getMaxBurnTime();
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, int value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 4;
|
||||
}
|
||||
};
|
||||
return new CoalGeneratorMenu(containerId, inventory, blockEntity.getBlockPos(), data, blockEntity.fuelSlot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return stillValid(this.access, player, Batteries.COAL_GENERATOR.get());
|
||||
}
|
||||
|
||||
public int storedEnergy() {
|
||||
return this.getData(ENERGY_INDEX);
|
||||
}
|
||||
|
||||
public int energyCapacity() {
|
||||
return this.getData(CAPACITY_INDEX);
|
||||
}
|
||||
|
||||
public int burnTimeRemaining() {
|
||||
return this.getData(BURN_TIME_INDEX);
|
||||
}
|
||||
|
||||
public int maxBurnTime() {
|
||||
return this.getData(MAX_BURN_TIME_INDEX);
|
||||
}
|
||||
|
||||
private int getData(int index) {
|
||||
return this.data.get(index);
|
||||
}
|
||||
|
||||
public static void writeBlockPos(RegistryFriendlyByteBuf buffer, BlockPos blockPos) {
|
||||
buffer.writeBlockPos(blockPos);
|
||||
}
|
||||
|
||||
private void addPlayerSlots(Inventory inventory) {
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col + row * 9 + 9, 8 + col * 18, 82 + row * 18));
|
||||
}
|
||||
}
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col, 8 + col * 18, 140));
|
||||
}
|
||||
}
|
||||
|
||||
private static ContainerData createPlaceholderData() {
|
||||
return new net.minecraft.world.inventory.SimpleContainerData(4);
|
||||
}
|
||||
|
||||
private final class FuelSlot extends Slot {
|
||||
private FuelSlot(net.minecraft.world.Container container, int slot, int x, int y) {
|
||||
super(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack stack) {
|
||||
return CoalGeneratorMenu.this.access.evaluate(
|
||||
(level, pos) -> CoalGeneratorBlockEntity.isFuel(level, stack),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.trunksbomb.batteries.recipe;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.item.BatteryBlockItem;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import com.trunksbomb.batteries.item.PoweredItemEnergy;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.CraftingInput;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class BatteryBlockUpgradeRecipe extends CustomRecipe {
|
||||
public BatteryBlockUpgradeRecipe(CraftingBookCategory category) {
|
||||
super(category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CraftingInput input, @NonNull Level level) {
|
||||
return this.findMatch(input) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ItemStack assemble(CraftingInput input, HolderLookup.@NonNull Provider registries) {
|
||||
Match match = this.findMatch(input);
|
||||
if (match == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack result = new ItemStack(Batteries.BATTERY_BLOCK_ITEM.get());
|
||||
int newCapacity = PoweredItemEnergy.clampToInt((long) match.baseCapacity() + match.addedCapacity());
|
||||
int newEnergy = PoweredItemEnergy.clampToInt((long) match.baseEnergy() + match.addedEnergy());
|
||||
PoweredItemEnergy.setEnergyCapacity(result, newCapacity);
|
||||
PoweredItemEnergy.setStoredEnergy(result, Math.min(newCapacity, newEnergy));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull RecipeSerializer<? extends CustomRecipe> getSerializer() {
|
||||
return Batteries.BATTERY_BLOCK_UPGRADE_RECIPE.get();
|
||||
}
|
||||
|
||||
public ItemStack getResultItem(HolderLookup.Provider registries) {
|
||||
ItemStack result = new ItemStack(Batteries.BATTERY_BLOCK_ITEM.get());
|
||||
BatteryBlockItem.initializeDefaults(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Match findMatch(CraftingInput input) {
|
||||
ItemStack baseStack = ItemStack.EMPTY;
|
||||
long addedCapacity = 0L;
|
||||
long addedEnergy = 0L;
|
||||
|
||||
for (ItemStack stack : input.items()) {
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stack.getItem() instanceof BatteryBlockItem) {
|
||||
if (baseStack.isEmpty()) {
|
||||
baseStack = stack;
|
||||
} else {
|
||||
addedCapacity += BatteryBlockItem.getEnergyCapacity(stack);
|
||||
addedEnergy += BatteryBlockItem.getStoredEnergy(stack);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stack.getItem() instanceof BatteryItem) {
|
||||
addedCapacity += BatteryItem.getEnergyCapacity(stack);
|
||||
addedEnergy += BatteryItem.getStoredEnergy(stack);
|
||||
continue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (baseStack.isEmpty() || addedCapacity <= 0L) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Match(
|
||||
BatteryBlockItem.getEnergyCapacity(baseStack),
|
||||
BatteryBlockItem.getStoredEnergy(baseStack),
|
||||
PoweredItemEnergy.clampToInt(addedCapacity),
|
||||
PoweredItemEnergy.clampToInt(addedEnergy)
|
||||
);
|
||||
}
|
||||
|
||||
private record Match(int baseCapacity, int baseEnergy, int addedCapacity, int addedEnergy) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.trunksbomb.batteries.recipe;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.CraftingInput;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class BatteryTierUpgradeRecipe extends CustomRecipe {
|
||||
public BatteryTierUpgradeRecipe(CraftingBookCategory category) {
|
||||
super(category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CraftingInput input, @NonNull Level level) {
|
||||
return this.findMatch(input) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ItemStack assemble(CraftingInput input, HolderLookup.@NonNull Provider registries) {
|
||||
Match match = this.findMatch(input);
|
||||
if (match == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack result = match.baseStack().transmuteCopy(match.resultItem());
|
||||
int storedEnergy = BatteryItem.getStoredEnergy(match.baseStack());
|
||||
int capacity = BatteryItem.getEnergyCapacity(result);
|
||||
BatteryItem.createEnergyHandler(result);
|
||||
setStoredEnergy(result, Math.min(storedEnergy, capacity));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeSerializer<? extends CustomRecipe> getSerializer() {
|
||||
return Batteries.BATTERY_TIER_UPGRADE_RECIPE.get();
|
||||
}
|
||||
|
||||
public ItemStack getResultItem(HolderLookup.Provider registries) {
|
||||
return new ItemStack(Batteries.BATTERY1.get());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Match findMatch(CraftingInput input) {
|
||||
if (input.width() != 3 || input.height() != 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (matchesPattern(input, Items.REDSTONE, Items.GOLD_BLOCK, Batteries.BATTERY.get(), Items.REDSTONE_BLOCK)) {
|
||||
return new Match(input.getItem(4), Batteries.BATTERY1.get());
|
||||
}
|
||||
if (matchesPattern(input, Items.REDSTONE, Items.DIAMOND, Batteries.BATTERY1.get(), Items.REDSTONE_BLOCK)) {
|
||||
return new Match(input.getItem(4), Batteries.BATTERY2.get());
|
||||
}
|
||||
if (matchesPattern(input, Items.REDSTONE, Items.DIAMOND_BLOCK, Batteries.BATTERY2.get(), Items.REDSTONE_BLOCK)) {
|
||||
return new Match(input.getItem(4), Batteries.BATTERY3.get());
|
||||
}
|
||||
if (matchesPattern(input, Items.END_STONE, Items.DIAMOND_BLOCK, Batteries.BATTERY3.get(), Items.ENDER_PEARL)) {
|
||||
return new Match(input.getItem(4), Batteries.BATTERY_ENDER.get());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean matchesPattern(CraftingInput input, Item cornerItem, Item topBottomItem, Item centerItem, Item middleSideItem) {
|
||||
return input.getItem(0).is(cornerItem)
|
||||
&& input.getItem(1).is(topBottomItem)
|
||||
&& input.getItem(2).is(cornerItem)
|
||||
&& input.getItem(3).is(middleSideItem)
|
||||
&& input.getItem(4).is(centerItem)
|
||||
&& input.getItem(5).is(middleSideItem)
|
||||
&& input.getItem(6).is(cornerItem)
|
||||
&& input.getItem(7).is(topBottomItem)
|
||||
&& input.getItem(8).is(cornerItem);
|
||||
}
|
||||
|
||||
private static void setStoredEnergy(ItemStack stack, int energy) {
|
||||
net.minecraft.world.item.component.CustomData.update(net.minecraft.core.component.DataComponents.CUSTOM_DATA, stack,
|
||||
tag -> tag.putInt("energy", Math.max(0, energy)));
|
||||
}
|
||||
|
||||
private record Match(ItemStack baseStack, Item resultItem) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.trunksbomb.batteries.recipe;
|
||||
|
||||
import com.trunksbomb.batteries.Batteries;
|
||||
import com.trunksbomb.batteries.BatteriesConfig;
|
||||
import com.trunksbomb.batteries.item.BatteryItem;
|
||||
import com.trunksbomb.batteries.item.PoweredItem;
|
||||
import com.trunksbomb.batteries.item.PoweredItemEnergy;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.CraftingInput;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class PoweredGearUpgradeRecipe extends CustomRecipe {
|
||||
public PoweredGearUpgradeRecipe(CraftingBookCategory category) {
|
||||
super(category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CraftingInput input, @NonNull Level level) {
|
||||
return this.findMatch(input) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ItemStack assemble(CraftingInput input, HolderLookup.@NonNull Provider registries) {
|
||||
Match match = this.findMatch(input);
|
||||
if (match == null) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack result = match.baseStack().transmuteCopy(match.resultItem());
|
||||
int baseCapacity = match.basePowered() ? PoweredItemEnergy.getEnergyCapacity(match.baseStack()) : 0;
|
||||
int baseEnergy = match.basePowered() ? PoweredItemEnergy.getStoredEnergy(match.baseStack()) : 0;
|
||||
int newCapacity = PoweredItemEnergy.clampToInt((long) baseCapacity + match.addedCapacity());
|
||||
int newEnergy = PoweredItemEnergy.clampToInt((long) baseEnergy + match.addedEnergy());
|
||||
|
||||
PoweredItemEnergy.setEnergyCapacity(result, newCapacity);
|
||||
PoweredItemEnergy.setStoredEnergy(result, Math.min(newCapacity, newEnergy));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull RecipeSerializer<? extends CustomRecipe> getSerializer() {
|
||||
return Batteries.POWERED_GEAR_UPGRADE_RECIPE.get();
|
||||
}
|
||||
|
||||
public ItemStack getResultItem(HolderLookup.Provider registries) {
|
||||
return new ItemStack(Batteries.BATTERY_PICKAXE.get());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Match findMatch(CraftingInput input) {
|
||||
ItemStack baseStack = ItemStack.EMPTY;
|
||||
Item resultItem = null;
|
||||
boolean basePowered = false;
|
||||
long addedCapacity = 0L;
|
||||
long addedEnergy = 0L;
|
||||
int batteries = 0;
|
||||
|
||||
for (ItemStack stack : input.items()) {
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stack.getItem() instanceof BatteryItem) {
|
||||
batteries++;
|
||||
addedCapacity += BatteryItem.getEnergyCapacity(stack);
|
||||
addedEnergy += BatteryItem.getStoredEnergy(stack);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!baseStack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
resultItem = this.toPoweredResult(stack);
|
||||
if (resultItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
baseStack = stack;
|
||||
basePowered = stack.getItem() instanceof PoweredItem;
|
||||
}
|
||||
|
||||
if (baseStack.isEmpty() || resultItem == null || batteries == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Match(baseStack, resultItem, basePowered, PoweredItemEnergy.clampToInt(addedCapacity), PoweredItemEnergy.clampToInt(addedEnergy));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Item toPoweredResult(ItemStack stack) {
|
||||
Item item = stack.getItem();
|
||||
if (BatteriesConfig.poweredToolsEnabled() && item == Items.DIAMOND_PICKAXE) {
|
||||
return Batteries.BATTERY_PICKAXE.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredToolsEnabled() && item == Items.DIAMOND_AXE) {
|
||||
return Batteries.BATTERY_AXE.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredToolsEnabled() && item == Items.DIAMOND_SHOVEL) {
|
||||
return Batteries.BATTERY_SHOVEL.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredToolsEnabled() && item == Items.DIAMOND_HOE) {
|
||||
return Batteries.BATTERY_HOE.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredWeaponsEnabled() && item == Items.DIAMOND_SWORD) {
|
||||
return Batteries.BATTERY_SWORD.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredArmorEnabled() && item == Items.DIAMOND_HELMET) {
|
||||
return Batteries.BATTERY_HELMET.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredArmorEnabled() && item == Items.DIAMOND_CHESTPLATE) {
|
||||
return Batteries.BATTERY_CHESTPLATE.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredArmorEnabled() && item == Items.DIAMOND_LEGGINGS) {
|
||||
return Batteries.BATTERY_LEGGINGS.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredArmorEnabled() && item == Items.DIAMOND_BOOTS) {
|
||||
return Batteries.BATTERY_BOOTS.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredWeaponsEnabled() && item == Items.SHIELD) {
|
||||
return Batteries.BATTERY_SHIELD.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredWeaponsEnabled() && item == Items.BOW) {
|
||||
return Batteries.BATTERY_BOW.get();
|
||||
}
|
||||
if (BatteriesConfig.poweredToolsEnabled() && (item == Batteries.BATTERY_PICKAXE.get()
|
||||
|| item == Batteries.BATTERY_AXE.get()
|
||||
|| item == Batteries.BATTERY_SHOVEL.get()
|
||||
|| item == Batteries.BATTERY_HOE.get())) {
|
||||
return item;
|
||||
}
|
||||
if (BatteriesConfig.poweredWeaponsEnabled() && (item == Batteries.BATTERY_SWORD.get()
|
||||
|| item == Batteries.BATTERY_SHIELD.get()
|
||||
|| item == Batteries.BATTERY_BOW.get())) {
|
||||
return item;
|
||||
}
|
||||
if (BatteriesConfig.poweredArmorEnabled() && (item == Batteries.BATTERY_HELMET.get()
|
||||
|| item == Batteries.BATTERY_CHESTPLATE.get()
|
||||
|| item == Batteries.BATTERY_LEGGINGS.get()
|
||||
|| item == Batteries.BATTERY_BOOTS.get())) {
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private record Match(ItemStack baseStack, Item resultItem, boolean basePowered, int addedCapacity, int addedEnergy) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"pattern": [
|
||||
"rgr",
|
||||
"bib",
|
||||
"rgr"
|
||||
],
|
||||
"key": {
|
||||
"r": "minecraft:redstone",
|
||||
"g": "minecraft:gold_ingot",
|
||||
"i": "minecraft:iron_ingot",
|
||||
"b": "minecraft:redstone_block"
|
||||
},
|
||||
"result": {
|
||||
"id": "batteries:battery",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "batteries:battery_tier_upgrade",
|
||||
"category": "redstone"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "batteries:battery_tier_upgrade",
|
||||
"category": "redstone"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "batteries:battery_tier_upgrade",
|
||||
"category": "redstone"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"pattern": [
|
||||
"RGR",
|
||||
"LIL",
|
||||
"RGR"
|
||||
],
|
||||
"key": {
|
||||
"R": "minecraft:redstone_block",
|
||||
"L": "#minecraft:logs",
|
||||
"G": "minecraft:gold_ingot",
|
||||
"I": "minecraft:iron_block"
|
||||
},
|
||||
"result": {
|
||||
"id": "batteries:battery_block"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "batteries:battery_block_upgrade",
|
||||
"category": "redstone"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "batteries:battery_tier_upgrade",
|
||||
"category": "redstone"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"pattern": [
|
||||
"rgr",
|
||||
"bib",
|
||||
"rgr"
|
||||
],
|
||||
"key": {
|
||||
"r": "minecraft:white_concrete",
|
||||
"g": "minecraft:gold_ingot",
|
||||
"i": "minecraft:iron_ingot",
|
||||
"b": "minecraft:redstone_block"
|
||||
},
|
||||
"result": {
|
||||
"id": "batteries:charger",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"pattern": [
|
||||
"RGR",
|
||||
"LFL",
|
||||
"RGR"
|
||||
],
|
||||
"key": {
|
||||
"R": "minecraft:redstone_block",
|
||||
"G": "minecraft:gold_ingot",
|
||||
"L": "#minecraft:logs",
|
||||
"F": "minecraft:furnace"
|
||||
},
|
||||
"result": {
|
||||
"id": "batteries:coal_generator"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "redstone",
|
||||
"pattern": [
|
||||
"ede",
|
||||
"ece",
|
||||
"ede"
|
||||
],
|
||||
"key": {
|
||||
"e": "minecraft:ender_pearl",
|
||||
"d": "minecraft:diamond",
|
||||
"c": "batteries:charger"
|
||||
},
|
||||
"result": {
|
||||
"id": "batteries:ender_charger",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods.
|
||||
# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
|
||||
# The overall format is standard TOML format, v0.5.0.
|
||||
# Note that there are a couple of TOML lists in this file.
|
||||
# Find more information on toml format here: https://github.com/toml-lang/toml
|
||||
|
||||
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
||||
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
||||
license="${mod_license}"
|
||||
|
||||
# A URL to refer people to when problems occur with this mod
|
||||
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
|
||||
|
||||
# A list of mods - how many allowed here is determined by the individual mod loader
|
||||
[[mods]] #mandatory
|
||||
|
||||
# The modid of the mod
|
||||
modId="${mod_id}" #mandatory
|
||||
|
||||
# The version number of the mod
|
||||
version="${mod_version}" #mandatory
|
||||
|
||||
# A display name for the mod
|
||||
displayName="${mod_name}" #mandatory
|
||||
|
||||
# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/
|
||||
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
|
||||
|
||||
# A URL for the "homepage" for this mod, displayed in the mod UI
|
||||
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
|
||||
|
||||
# A file name (in the root of the mod JAR) containing a logo for display
|
||||
#logoFile="examplemod.png" #optional
|
||||
|
||||
# A text field displayed in the mod UI
|
||||
#credits="" #optional
|
||||
|
||||
# The authors of the mod, displayed in the mod UI (optional)
|
||||
authors="trunksbomb"
|
||||
|
||||
# The description text for the mod (multi line!) (#mandatory)
|
||||
description='''
|
||||
Adds portable batteries and chargers for NeoForge. This 1.21 branch currently includes an MVP battery item and charger block while the gameplay systems are being ported.
|
||||
'''
|
||||
|
||||
# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded.
|
||||
#[[mixins]]
|
||||
#config="${mod_id}.mixins.json"
|
||||
|
||||
# The [[accessTransformers]] block allows you to declare where your AT file is.
|
||||
# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg
|
||||
#[[accessTransformers]]
|
||||
#file="META-INF/accesstransformer.cfg"
|
||||
|
||||
# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json
|
||||
|
||||
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
|
||||
[[dependencies.${mod_id}]] #optional
|
||||
# the modid of the dependency
|
||||
modId="neoforge" #mandatory
|
||||
# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive).
|
||||
# 'required' requires the mod to exist, 'optional' does not
|
||||
# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning
|
||||
type="required" #mandatory
|
||||
# Optional field describing why the dependency is required or why it is incompatible
|
||||
# reason="..."
|
||||
# The version range of the dependency
|
||||
versionRange="[${neo_version},)" #mandatory
|
||||
# An ordering relationship for the dependency.
|
||||
# BEFORE - This mod is loaded BEFORE the dependency
|
||||
# AFTER - This mod is loaded AFTER the dependency
|
||||
ordering="NONE"
|
||||
# Side this dependency is applied on - BOTH, CLIENT, or SERVER
|
||||
side="BOTH"
|
||||
|
||||
# Here's another dependency
|
||||
[[dependencies.${mod_id}]]
|
||||
modId="minecraft"
|
||||
type="required"
|
||||
# This version range declares a minimum of the current minecraft version up to but not including the next major version
|
||||
versionRange="${minecraft_version_range}"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
|
||||
# Features are specific properties of the game environment, that you may want to declare you require. This example declares
|
||||
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't
|
||||
# stop your mod loading on the server for example.
|
||||
#[features.${mod_id}]
|
||||
#openGLVersion="[3.2,)"
|
||||
Reference in New Issue
Block a user