diff --git a/src/main/java/gregtech/api/GregTechAPI.java b/src/main/java/gregtech/api/GregTechAPI.java index 01845f8b047..4e3bc8bc69c 100644 --- a/src/main/java/gregtech/api/GregTechAPI.java +++ b/src/main/java/gregtech/api/GregTechAPI.java @@ -11,6 +11,7 @@ import gregtech.api.metatileentity.registry.MTEManager; import gregtech.api.modules.IModuleManager; import gregtech.api.network.INetworkHandler; +import gregtech.api.recipes.properties.RecipePropertyRegistry; import gregtech.api.sound.ISoundManager; import gregtech.api.unification.material.Material; import gregtech.api.unification.material.registry.IMaterialRegistryManager; @@ -56,6 +57,7 @@ public class GregTechAPI { public static MTEManager mteManager; /** GT's data migrations API */ public static final MigrationAPI MIGRATIONS = new MigrationAPI(); + public static final RecipePropertyRegistry RECIPE_PROPERTIES = new RecipePropertyRegistry(); /** Will be available at the Pre-Initialization stage */ private static boolean highTier; diff --git a/src/main/java/gregtech/api/block/IHeatingCoilBlockStats.java b/src/main/java/gregtech/api/block/IHeatingCoilBlockStats.java index 5f6e5c49c06..0e2b7427591 100644 --- a/src/main/java/gregtech/api/block/IHeatingCoilBlockStats.java +++ b/src/main/java/gregtech/api/block/IHeatingCoilBlockStats.java @@ -1,6 +1,6 @@ package gregtech.api.block; -import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.recipes.properties.impl.TemperatureProperty; import gregtech.api.unification.material.Material; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java index ced966ccc48..455ae3afd47 100644 --- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java @@ -18,9 +18,9 @@ import gregtech.api.recipes.logic.IParallelableRecipeLogic; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.CleanroomProperty; -import gregtech.api.recipes.recipeproperties.DimensionProperty; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; +import gregtech.api.recipes.properties.impl.CleanroomProperty; +import gregtech.api.recipes.properties.impl.DimensionProperty; import gregtech.api.util.GTLog; import gregtech.api.util.GTTransferUtils; import gregtech.api.util.GTUtility; @@ -441,9 +441,11 @@ protected boolean checkCleanroomRequirement(@NotNull Recipe recipe) { } protected boolean checkDimensionRequirement(@NotNull Recipe recipe) { - if (!recipe.hasProperty(DimensionProperty.getInstance())) return true; - return recipe.getProperty(DimensionProperty.getInstance(), DimensionProperty.DimensionPropertyList.EMPTY_LIST) - .checkDimension(this.getMetaTileEntity().getWorld().provider.getDimension()); + DimensionProperty.DimensionPropertyList list = recipe.getProperty(DimensionProperty.getInstance(), null); + if (list == null) { + return true; + } + return list.checkDimension(this.getMetaTileEntity().getWorld().provider.getDimension()); } /** @@ -682,7 +684,7 @@ protected static boolean areItemStacksEqual(@NotNull ItemStack stackA, @NotNull @NotNull IMultipleTankHandler importFluids) { calculateOverclock(recipe); - modifyOverclockPost(ocResult, recipe.getRecipePropertyStorage()); + modifyOverclockPost(ocResult, recipe.propertyStorage()); if (ocResult.parallel() > 1) { recipe = subTickOC(ocResult, recipe, importInventory, importFluids); @@ -814,7 +816,7 @@ protected boolean hasEnoughPower(long eut, int duration) { * @param ocResult The overclock result * @param storage the RecipePropertyStorage of the recipe being processed */ - protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull IRecipePropertyStorage storage) {} + protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull RecipePropertyStorage storage) {} /** * Calculates the overclocked Recipe's final duration and EU/t @@ -837,13 +839,13 @@ protected final void calculateOverclock(@NotNull Recipe recipe) { * @param ocResult the result of overclocking */ protected void performOverclocking(@NotNull Recipe recipe, @NotNull OCParams ocParams, @NotNull OCResult ocResult) { - modifyOverclockPre(ocParams, recipe.getRecipePropertyStorage()); + modifyOverclockPre(ocParams, recipe.propertyStorage()); if (ocParams.ocAmount() <= 0) { // number of OCs is <= 0, so do not overclock ocResult.init(ocParams.eut(), ocParams.duration()); } else { - runOverclockingLogic(ocParams, ocResult, recipe.getRecipePropertyStorage(), getMaximumOverclockVoltage()); + runOverclockingLogic(ocParams, ocResult, recipe.propertyStorage(), getMaximumOverclockVoltage()); } } @@ -873,7 +875,7 @@ protected int getNumberOfOCs(long recipeEUt) { * @param ocParams an array of [recipeEUt, recipeDuration, numberOfOCs] * @param storage the RecipePropertyStorage of the recipe being processed */ - protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePropertyStorage storage) {} + protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull RecipePropertyStorage storage) {} /** * Calls the desired overclocking logic to be run for the recipe. Performs the actual overclocking on the provided @@ -885,7 +887,7 @@ protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePr * @param maxVoltage the maximum voltage the recipe is allowed to be run at */ protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { standardOC(ocParams, ocResult, maxVoltage, getOverclockingDurationFactor(), getOverclockingVoltageFactor()); } diff --git a/src/main/java/gregtech/api/capability/impl/ComputationRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/ComputationRecipeLogic.java index 99284729017..67c3c03fbc9 100644 --- a/src/main/java/gregtech/api/capability/impl/ComputationRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/ComputationRecipeLogic.java @@ -4,8 +4,8 @@ import gregtech.api.capability.IOpticalComputationReceiver; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; import gregtech.api.recipes.Recipe; -import gregtech.api.recipes.recipeproperties.ComputationProperty; -import gregtech.api.recipes.recipeproperties.TotalComputationProperty; +import gregtech.api.recipes.properties.impl.ComputationProperty; +import gregtech.api.recipes.properties.impl.TotalComputationProperty; import net.minecraft.nbt.NBTTagCompound; @@ -49,11 +49,12 @@ public boolean checkRecipe(@NotNull Recipe recipe) { if (!super.checkRecipe(recipe)) { return false; } - if (!recipe.hasProperty(ComputationProperty.getInstance())) { + int recipeCWUt = recipe.getProperty(ComputationProperty.getInstance(), 0); + if (recipeCWUt == 0) { return true; } + IOpticalComputationProvider provider = getComputationProvider(); - int recipeCWUt = recipe.getProperty(ComputationProperty.getInstance(), 0); return provider.requestCWUt(recipeCWUt, true) >= recipeCWUt; } diff --git a/src/main/java/gregtech/api/capability/impl/FuelRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/FuelRecipeLogic.java index f70ca3fcde9..26871a55d3c 100644 --- a/src/main/java/gregtech/api/capability/impl/FuelRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/FuelRecipeLogic.java @@ -6,7 +6,7 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import org.jetbrains.annotations.NotNull; @@ -40,7 +40,7 @@ protected boolean hasEnoughPower(long eut, int duration) { @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { standardOC(ocParams, ocResult, maxVoltage, getOverclockingDurationFactor(), getOverclockingVoltageFactor()); } diff --git a/src/main/java/gregtech/api/capability/impl/HeatingCoilRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/HeatingCoilRecipeLogic.java index 1c97891e9bb..fc9bd216d55 100644 --- a/src/main/java/gregtech/api/capability/impl/HeatingCoilRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/HeatingCoilRecipeLogic.java @@ -5,8 +5,8 @@ import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; import gregtech.api.recipes.logic.OverclockingLogic; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; -import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.recipes.properties.RecipePropertyStorage; +import gregtech.api.recipes.properties.impl.TemperatureProperty; import org.jetbrains.annotations.NotNull; @@ -26,18 +26,18 @@ public HeatingCoilRecipeLogic(RecipeMapMultiblockController metaTileEntity) { } @Override - protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull RecipePropertyStorage storage) { super.modifyOverclockPre(ocParams, storage); // coil EU/t discount ocParams.setEut(OverclockingLogic.applyCoilEUtDiscount(ocParams.eut(), ((IHeatingCoil) metaTileEntity).getCurrentTemperature(), - storage.getRecipePropertyValue(TemperatureProperty.getInstance(), 0))); + storage.get(TemperatureProperty.getInstance(), 0))); } @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { heatingCoilOC(ocParams, ocResult, maxVoltage, ((IHeatingCoil) metaTileEntity).getCurrentTemperature(), - propertyStorage.getRecipePropertyValue(TemperatureProperty.getInstance(), 0)); + propertyStorage.get(TemperatureProperty.getInstance(), 0)); } } diff --git a/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java index 746593f366a..30c5e5410e3 100644 --- a/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java @@ -8,7 +8,7 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; import gregtech.api.util.TextFormattingUtil; @@ -29,7 +29,7 @@ public MultiblockFuelRecipeLogic(RecipeMapMultiblockController tileEntity) { } @Override - protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull RecipePropertyStorage storage) { // apply maintenance bonuses Tuple maintenanceValues = getMaintenanceValues(); @@ -40,7 +40,7 @@ protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePr } @Override - protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull RecipePropertyStorage storage) { // apply maintenance penalties Tuple maintenanceValues = getMaintenanceValues(); diff --git a/src/main/java/gregtech/api/capability/impl/MultiblockRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/MultiblockRecipeLogic.java index 200f92ca873..ff95f0fbb0c 100644 --- a/src/main/java/gregtech/api/capability/impl/MultiblockRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/MultiblockRecipeLogic.java @@ -12,7 +12,7 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; import gregtech.common.ConfigHolder; @@ -281,7 +281,7 @@ protected boolean prepareRecipeDistinct(Recipe recipe) { } @Override - protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull RecipePropertyStorage storage) { super.modifyOverclockPre(ocParams, storage); // apply maintenance bonuses @@ -295,13 +295,13 @@ protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePr @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { subTickParallelOC(ocParams, ocResult, maxVoltage, getOverclockingDurationFactor(), getOverclockingVoltageFactor()); } @Override - protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull RecipePropertyStorage storage) { super.modifyOverclockPost(ocResult, storage); // apply maintenance penalties diff --git a/src/main/java/gregtech/api/capability/impl/PrimitiveRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/PrimitiveRecipeLogic.java index d1893fa8f84..40e4f13ca43 100644 --- a/src/main/java/gregtech/api/capability/impl/PrimitiveRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/PrimitiveRecipeLogic.java @@ -5,7 +5,7 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import org.jetbrains.annotations.NotNull; @@ -50,7 +50,7 @@ public long getMaxVoltage() { @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { ocParams.setEut(1L); super.runOverclockingLogic(ocParams, ocResult, propertyStorage, maxVoltage); } diff --git a/src/main/java/gregtech/api/capability/impl/RecipeLogicEnergy.java b/src/main/java/gregtech/api/capability/impl/RecipeLogicEnergy.java index 4e62346dcf2..8460b2a7aa7 100644 --- a/src/main/java/gregtech/api/capability/impl/RecipeLogicEnergy.java +++ b/src/main/java/gregtech/api/capability/impl/RecipeLogicEnergy.java @@ -5,7 +5,7 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import org.jetbrains.annotations.NotNull; @@ -56,7 +56,7 @@ public long getMaxVoltage() { @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { subTickNonParallelOC(ocParams, ocResult, maxVoltage, getOverclockingDurationFactor(), getOverclockingVoltageFactor()); } diff --git a/src/main/java/gregtech/api/items/toolitem/ToolHelper.java b/src/main/java/gregtech/api/items/toolitem/ToolHelper.java index 23d3145ca9b..8b1a8ef147a 100644 --- a/src/main/java/gregtech/api/items/toolitem/ToolHelper.java +++ b/src/main/java/gregtech/api/items/toolitem/ToolHelper.java @@ -18,7 +18,13 @@ import gregtech.tools.enchants.EnchantmentHardHammer; import net.minecraft.advancements.CriteriaTriggers; -import net.minecraft.block.*; +import net.minecraft.block.Block; +import net.minecraft.block.BlockCommandBlock; +import net.minecraft.block.BlockLiquid; +import net.minecraft.block.BlockPane; +import net.minecraft.block.BlockRailBase; +import net.minecraft.block.BlockStructure; +import net.minecraft.block.BlockWeb; import net.minecraft.block.state.IBlockState; import net.minecraft.enchantment.EnchantmentDurability; import net.minecraft.enchantment.EnchantmentHelper; @@ -57,7 +63,11 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; import java.util.function.Supplier; import static gregtech.api.GTValues.LV; @@ -196,7 +206,7 @@ public static ItemStack getAndSetToolData(IGTTool tool, Material material, int m ItemStack stack = tool.getRaw(); GTUtility.getOrCreateNbtCompound(stack).setInteger(HIDE_FLAGS, 2); NBTTagCompound toolTag = getToolTag(stack); - toolTag.setString(MATERIAL_KEY, material.toString()); + toolTag.setString(MATERIAL_KEY, material.getRegistryName()); toolTag.setInteger(MAX_DURABILITY_KEY, maxDurability); toolTag.setInteger(HARVEST_LEVEL_KEY, harvestLevel); toolTag.setFloat(TOOL_SPEED_KEY, toolSpeed); diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java index 1185fb5ab97..24649f1d1f5 100644 --- a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java @@ -584,6 +584,9 @@ public final boolean onToolClick(EntityPlayer playerIn, @NotNull Set too if (toolClasses.contains(ToolClasses.HARD_HAMMER)) { return onHardHammerClick(playerIn, hand, gridSideHit, hitResult); } + if (toolClasses.contains(ToolClasses.WIRE_CUTTER)) { + return onWireCutterClick(playerIn, hand, gridSideHit, hitResult); + } return false; } @@ -664,6 +667,16 @@ public boolean onHardHammerClick(EntityPlayer playerIn, EnumHand hand, EnumFacin return true; } + /** + * Called when player clicks a wire cutter on specific side of this meta tile entity + * + * @return true if something happened, so the tool will get damaged and animation will be played + */ + public boolean onWireCutterClick(EntityPlayer playerIn, EnumHand hand, EnumFacing facing, + CuboidRayTraceResult hitResult) { + return false; + } + public void onLeftClick(EntityPlayer player, EnumFacing facing, CuboidRayTraceResult hitResult) { if (this instanceof IDataStickIntractable dsi) { ItemStack stack = player.getHeldItemMainhand(); diff --git a/src/main/java/gregtech/api/mui/GTGuiTextures.java b/src/main/java/gregtech/api/mui/GTGuiTextures.java index f17affee609..4f0345f49fc 100644 --- a/src/main/java/gregtech/api/mui/GTGuiTextures.java +++ b/src/main/java/gregtech/api/mui/GTGuiTextures.java @@ -206,6 +206,8 @@ public static class IDs { "textures/gui/widget/button_public_private.png", 18, 36, 18, 18, true); + public static final UITexture MENU_OVERLAY = fullImage("textures/gui/overlay/menu_overlay.png"); + // todo bronze/steel/primitive fluid slots? // SLOT OVERLAYS diff --git a/src/main/java/gregtech/api/mui/GTGuis.java b/src/main/java/gregtech/api/mui/GTGuis.java index b5599dd9cfd..614f6a08f1b 100644 --- a/src/main/java/gregtech/api/mui/GTGuis.java +++ b/src/main/java/gregtech/api/mui/GTGuis.java @@ -9,6 +9,7 @@ import net.minecraft.item.ItemStack; +import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.factory.GuiManager; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.utils.Alignment; @@ -62,7 +63,15 @@ public PopupPanel(@NotNull String name, int width, int height, boolean disableBe super(name); size(width, height).align(Alignment.Center); background(GTGuiTextures.BACKGROUND_POPUP); - child(ButtonWidget.panelCloseButton().top(5).right(5)); + child(ButtonWidget.panelCloseButton().top(5).right(5) + .onMousePressed(mouseButton -> { + if (mouseButton == 0 || mouseButton == 1) { + this.closeIfOpen(true); + Interactable.playButtonClickSound(); + return true; + } + return false; + })); this.disableBelow = disableBelow; this.closeOnOutsideClick = closeOnOutsideClick; } diff --git a/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java b/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java new file mode 100644 index 00000000000..b5be013da18 --- /dev/null +++ b/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java @@ -0,0 +1,224 @@ +package gregtech.api.mui.sync; + +import gregtech.api.util.GTUtility; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidTank; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandlerItem; + +import com.cleanroommc.modularui.network.NetworkUtils; +import com.cleanroommc.modularui.value.sync.SyncHandler; +import org.jetbrains.annotations.NotNull; + +public class GTFluidSyncHandler extends SyncHandler { + + private static final int TRY_CLICK_CONTAINER = 1; + + private final IFluidTank tank; + private boolean canDrainSlot = true; + private boolean canFillSlot = true; + + public GTFluidSyncHandler(IFluidTank tank) { + this.tank = tank; + } + + public FluidStack getFluid() { + return this.tank.getFluid(); + } + + public int getCapacity() { + return this.tank.getCapacity(); + } + + public GTFluidSyncHandler canDrainSlot(boolean canDrainSlot) { + this.canDrainSlot = canDrainSlot; + return this; + } + + public boolean canDrainSlot() { + return this.canDrainSlot; + } + + public GTFluidSyncHandler canFillSlot(boolean canFillSlot) { + this.canFillSlot = canFillSlot; + return this; + } + + public boolean canFillSlot() { + return this.canFillSlot; + } + + @Override + public void readOnClient(int id, PacketBuffer buf) { + if (id == TRY_CLICK_CONTAINER) { + replaceCursorItemStack(NetworkUtils.readItemStack(buf)); + } + } + + @Override + public void readOnServer(int id, PacketBuffer buf) { + if (id == TRY_CLICK_CONTAINER) { + var stack = tryClickContainer(buf.readBoolean()); + if (!stack.isEmpty()) + syncToClient(TRY_CLICK_CONTAINER, buffer -> NetworkUtils.writeItemStack(buffer, stack)); + } + } + + public ItemStack tryClickContainer(boolean tryFillAll) { + ItemStack playerHeldStack = getSyncManager().getCursorItem(); + if (playerHeldStack.isEmpty()) + return ItemStack.EMPTY; + + ItemStack useStack = GTUtility.copy(1, playerHeldStack); + IFluidHandlerItem fluidHandlerItem = useStack + .getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); + if (fluidHandlerItem == null) return ItemStack.EMPTY; + + FluidStack tankFluid = tank.getFluid(); + FluidStack heldFluid = fluidHandlerItem.drain(Integer.MAX_VALUE, false); + + // nothing to do, return + if (tankFluid == null && heldFluid == null) + return ItemStack.EMPTY; + + // tank is empty, try to fill tank + if (canFillSlot && tankFluid == null) { + return fillTankFromStack(fluidHandlerItem, heldFluid, tryFillAll); + + // hand is empty, try to drain tank + } else if (canDrainSlot && heldFluid == null) { + return drainTankFromStack(fluidHandlerItem, tankFluid, tryFillAll); + + // neither is empty but tank is not full, try to fill tank + } else if (canFillSlot && tank.getFluidAmount() < tank.getCapacity() && heldFluid != null) { + return fillTankFromStack(fluidHandlerItem, heldFluid, tryFillAll); + } + + return ItemStack.EMPTY; + } + + private ItemStack fillTankFromStack(IFluidHandlerItem fluidHandler, @NotNull FluidStack heldFluid, + boolean tryFillAll) { + ItemStack heldItem = getSyncManager().getCursorItem(); + if (heldItem.isEmpty()) return ItemStack.EMPTY; + + FluidStack currentFluid = tank.getFluid(); + // Fluid type does not match + if (currentFluid != null && !currentFluid.isFluidEqual(heldFluid)) return ItemStack.EMPTY; + + int freeSpace = tank.getCapacity() - tank.getFluidAmount(); + if (freeSpace <= 0) return ItemStack.EMPTY; + + ItemStack itemStackEmptied = ItemStack.EMPTY; + int fluidAmountTaken = 0; + + FluidStack drained = fluidHandler.drain(freeSpace, true); + if (drained != null && drained.amount > 0) { + itemStackEmptied = fluidHandler.getContainer(); + fluidAmountTaken = drained.amount; + } + if (itemStackEmptied == ItemStack.EMPTY) { + return ItemStack.EMPTY; + } + + // find out how many fills we can do + // same round down behavior as drain + int additional = tryFillAll ? Math.min(freeSpace / fluidAmountTaken, heldItem.getCount()) : 1; + FluidStack copiedFluidStack = heldFluid.copy(); + copiedFluidStack.amount = fluidAmountTaken * additional; + tank.fill(copiedFluidStack, true); + + itemStackEmptied.setCount(additional); + replaceCursorItemStack(itemStackEmptied); + playSound(heldFluid, true); + return itemStackEmptied; + } + + private ItemStack drainTankFromStack(IFluidHandlerItem fluidHandler, FluidStack tankFluid, boolean tryFillAll) { + ItemStack heldItem = getSyncManager().getCursorItem(); + if (heldItem.isEmpty()) return ItemStack.EMPTY; + + ItemStack fluidContainer = fluidHandler.getContainer(); + int filled = fluidHandler.fill(tankFluid, false); + if (filled > 0) { + tank.drain(filled, true); + fluidHandler.fill(tankFluid, true); + if (tryFillAll) { + // Determine how many more items we can fill. One item is already filled. + // Integer division means it will round down, so it will only fill equivalent fluid amounts. + // For example: + // Click with 3 cells, with 2500L of fluid in the tank. + // 2 cells will be filled, and 500L will be left behind in the tank. + int additional = Math.min(heldItem.getCount(), tankFluid.amount / filled) - 1; + tank.drain(filled * additional, true); + fluidContainer.grow(additional); + } + replaceCursorItemStack(fluidContainer); + playSound(tankFluid, false); + return fluidContainer; + } + return ItemStack.EMPTY; + } + + /** + * Replace the ItemStack on the player's cursor with the passed stack. Use to replace empty cells with filled, or + * filled cells with empty. If it is not fully emptied/filled, it will place the new items into the player inventory + * instead, and shrink the held stack by the appropriate amount. + */ + private void replaceCursorItemStack(ItemStack resultStack) { + int resultStackSize = resultStack.getMaxStackSize(); + ItemStack playerStack = getSyncManager().getCursorItem(); + + if (!getSyncManager().isClient()) + syncToClient(TRY_CLICK_CONTAINER, buffer -> NetworkUtils.writeItemStack(buffer, resultStack)); + + while (resultStack.getCount() > resultStackSize) { + playerStack.shrink(resultStackSize); + addItemToPlayerInventory(resultStack.splitStack(resultStackSize)); + } + if (playerStack.getCount() == resultStack.getCount()) { + // every item on the cursor is mutated, so leave it there + getSyncManager().setCursorItem(resultStack); + } else { + // some items not mutated. Mutated items go into the inventory/world. + playerStack.shrink(resultStack.getCount()); + getSyncManager().setCursorItem(playerStack); + addItemToPlayerInventory(resultStack); + } + } + + /** Place an item into the player's inventory, or drop it in-world as an item entity if it cannot fit. */ + private void addItemToPlayerInventory(ItemStack stack) { + if (stack == null) return; + var player = getSyncManager().getPlayer(); + + if (!player.inventory.addItemStackToInventory(stack) && !player.world.isRemote) { + EntityItem dropItem = player.entityDropItem(stack, 0); + if (dropItem != null) dropItem.setPickupDelay(0); + } + } + + /** + * Play the appropriate fluid interaction sound for the fluid.
+ * Must be called on server to work correctly + **/ + private void playSound(FluidStack fluid, boolean fill) { + if (fluid == null) return; + SoundEvent soundEvent; + if (fill) { + soundEvent = fluid.getFluid().getFillSound(fluid); + } else { + soundEvent = fluid.getFluid().getEmptySound(fluid); + } + EntityPlayer player = getSyncManager().getPlayer(); + player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, + soundEvent, SoundCategory.PLAYERS, 1.0F, 1.0F); + } +} diff --git a/src/main/java/gregtech/api/recipes/Recipe.java b/src/main/java/gregtech/api/recipes/Recipe.java index 923c463e0c4..ff495651ac5 100644 --- a/src/main/java/gregtech/api/recipes/Recipe.java +++ b/src/main/java/gregtech/api/recipes/Recipe.java @@ -8,9 +8,9 @@ import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; import gregtech.api.recipes.ingredients.GTRecipeInput; -import gregtech.api.recipes.recipeproperties.EmptyRecipePropertyStorage; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; -import gregtech.api.recipes.recipeproperties.RecipeProperty; +import gregtech.api.recipes.properties.RecipeProperty; +import gregtech.api.recipes.properties.RecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorageImpl; import gregtech.api.util.GTUtility; import gregtech.api.util.ItemStackHashStrategy; import gregtech.integration.groovy.GroovyScriptModule; @@ -26,15 +26,15 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Set; /** * Class that represent machine recipe. @@ -99,7 +99,7 @@ public static int getMaxChancedValue() { // TODO YEET private final boolean isCTRecipe; private final boolean groovyRecipe; - private final IRecipePropertyStorage recipePropertyStorage; + private final RecipePropertyStorage recipePropertyStorage; private final int hashCode; @@ -113,10 +113,9 @@ public Recipe(@NotNull List inputs, long EUt, boolean hidden, boolean isCTRecipe, - IRecipePropertyStorage recipePropertyStorage, + @NotNull RecipePropertyStorage recipePropertyStorage, @NotNull GTRecipeCategory recipeCategory) { - this.recipePropertyStorage = recipePropertyStorage == null ? EmptyRecipePropertyStorage.INSTANCE : - recipePropertyStorage; + this.recipePropertyStorage = recipePropertyStorage; this.inputs = GTRecipeInputCache.deduplicateInputs(inputs); if (outputs.isEmpty()) { this.outputs = Collections.emptyList(); @@ -742,40 +741,26 @@ public GTRecipeCategory getRecipeCategory() { /////////////////////////////////////////////////////////// // Property Helper Methods // /////////////////////////////////////////////////////////// - public T getProperty(RecipeProperty property, T defaultValue) { - return recipePropertyStorage.getRecipePropertyValue(property, defaultValue); - } - - public Object getPropertyRaw(String key) { - return recipePropertyStorage.getRawRecipePropertyValue(key); - } - - public Set, Object>> getPropertyValues() { - return recipePropertyStorage.getRecipeProperties(); - } - - public Set getPropertyKeys() { - return recipePropertyStorage.getRecipePropertyKeys(); - } - - public Set> getPropertyTypes() { - return recipePropertyStorage.getPropertyTypes(); - } - public boolean hasProperty(RecipeProperty property) { - return recipePropertyStorage.hasRecipeProperty(property); - } - - public int getPropertyCount() { - return recipePropertyStorage.getSize(); + /** + * @see RecipePropertyStorageImpl#get(RecipeProperty, Object) + */ + @Contract("_, !null -> !null") + public @Nullable T getProperty(@NotNull RecipeProperty property, @Nullable T defaultValue) { + return recipePropertyStorage.get(property, defaultValue); } - public int getUnhiddenPropertyCount() { - return (int) recipePropertyStorage.getRecipeProperties().stream() - .filter((property) -> !property.getKey().isHidden()).count(); + /** + * @see RecipePropertyStorageImpl#contains(RecipeProperty) + */ + public boolean hasProperty(@NotNull RecipeProperty property) { + return recipePropertyStorage.contains(property); } - public IRecipePropertyStorage getRecipePropertyStorage() { + /** + * @return the property storage + */ + public @NotNull RecipePropertyStorage propertyStorage() { return recipePropertyStorage; } } diff --git a/src/main/java/gregtech/api/recipes/RecipeBuilder.java b/src/main/java/gregtech/api/recipes/RecipeBuilder.java index b57b8c25db4..0a6f07ee6e7 100644 --- a/src/main/java/gregtech/api/recipes/RecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/RecipeBuilder.java @@ -16,11 +16,11 @@ import gregtech.api.recipes.ingredients.IntCircuitIngredient; import gregtech.api.recipes.ingredients.nbtmatch.NBTCondition; import gregtech.api.recipes.ingredients.nbtmatch.NBTMatcher; -import gregtech.api.recipes.recipeproperties.CleanroomProperty; -import gregtech.api.recipes.recipeproperties.DimensionProperty; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; -import gregtech.api.recipes.recipeproperties.RecipeProperty; -import gregtech.api.recipes.recipeproperties.RecipePropertyStorage; +import gregtech.api.recipes.properties.RecipeProperty; +import gregtech.api.recipes.properties.RecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorageImpl; +import gregtech.api.recipes.properties.impl.CleanroomProperty; +import gregtech.api.recipes.properties.impl.DimensionProperty; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.material.Material; import gregtech.api.unification.ore.OrePrefix; @@ -44,7 +44,6 @@ import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; import crafttweaker.CraftTweakerAPI; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntLists; import org.apache.commons.lang3.builder.ToStringBuilder; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.MustBeInvokedByOverriders; @@ -84,7 +83,7 @@ public class RecipeBuilder> { protected boolean isCTRecipe = false; protected int parallel = 0; protected EnumValidationResult recipeStatus = EnumValidationResult.VALID; - protected @Nullable IRecipePropertyStorage recipePropertyStorage = null; + protected RecipePropertyStorage recipePropertyStorage = RecipePropertyStorage.EMPTY; protected boolean recipePropertyStorageErrored = false; protected RecipeBuilder() { @@ -108,10 +107,7 @@ public RecipeBuilder(Recipe recipe, RecipeMap recipeMap) { this.EUt = recipe.getEUt(); this.hidden = recipe.isHidden(); this.category = recipe.getRecipeCategory(); - this.recipePropertyStorage = recipe.getRecipePropertyStorage().copy(); - if (this.recipePropertyStorage != null) { - this.recipePropertyStorage.freeze(false); - } + this.recipePropertyStorage = recipe.propertyStorage().copy(); } @SuppressWarnings("CopyConstructorMissesField") @@ -129,11 +125,7 @@ protected RecipeBuilder(RecipeBuilder recipeBuilder) { this.EUt = recipeBuilder.EUt; this.hidden = recipeBuilder.hidden; this.category = recipeBuilder.category; - this.recipePropertyStorage = recipeBuilder.recipePropertyStorage == null ? null : - recipeBuilder.recipePropertyStorage.copy(); - if (this.recipePropertyStorage != null) { - this.recipePropertyStorage = this.recipePropertyStorage.copy(); - } + this.recipePropertyStorage = recipeBuilder.recipePropertyStorage.copy(); } public R cleanroom(@Nullable CleanroomType cleanroom) { @@ -150,7 +142,7 @@ public R dimension(int dimensionID) { public R dimension(int dimensionID, boolean toBlackList) { DimensionProperty.DimensionPropertyList dimensionIDs = getCompleteDimensionIDs(); - if (dimensionIDs == DimensionProperty.DimensionPropertyList.EMPTY_LIST) { + if (dimensionIDs == null) { dimensionIDs = new DimensionProperty.DimensionPropertyList(); this.applyProperty(DimensionProperty.getInstance(), dimensionIDs); } @@ -158,29 +150,26 @@ public R dimension(int dimensionID, boolean toBlackList) { return (R) this; } - public DimensionProperty.DimensionPropertyList getCompleteDimensionIDs() { - return this.recipePropertyStorage == null ? DimensionProperty.DimensionPropertyList.EMPTY_LIST : - this.recipePropertyStorage.getRecipePropertyValue(DimensionProperty.getInstance(), - DimensionProperty.DimensionPropertyList.EMPTY_LIST); + public @Nullable DimensionProperty.DimensionPropertyList getCompleteDimensionIDs() { + return this.recipePropertyStorage.get(DimensionProperty.getInstance(), null); } - public IntList getDimensionIDs() { - return this.recipePropertyStorage == null ? IntLists.EMPTY_LIST : - this.recipePropertyStorage.getRecipePropertyValue(DimensionProperty.getInstance(), - DimensionProperty.DimensionPropertyList.EMPTY_LIST).whiteListDimensions; + public @NotNull IntList getDimensionIDs() { + return this.recipePropertyStorage.get(DimensionProperty.getInstance(), + DimensionProperty.DimensionPropertyList.EMPTY_LIST).whiteListDimensions; } - public IntList getBlockedDimensionIDs() { - return this.recipePropertyStorage == null ? IntLists.EMPTY_LIST : - this.recipePropertyStorage.getRecipePropertyValue(DimensionProperty.getInstance(), - DimensionProperty.DimensionPropertyList.EMPTY_LIST).whiteListDimensions; + public @NotNull IntList getBlockedDimensionIDs() { + return this.recipePropertyStorage.get(DimensionProperty.getInstance(), + DimensionProperty.DimensionPropertyList.EMPTY_LIST).blackListDimensions; } - public boolean applyProperty(@NotNull String key, @Nullable Object value) { + @MustBeInvokedByOverriders + public boolean applyPropertyCT(@NotNull String key, @NotNull Object value) { if (key.equals(DimensionProperty.KEY)) { if (value instanceof DimensionProperty.DimensionPropertyList list) { DimensionProperty.DimensionPropertyList dimensionIDs = getCompleteDimensionIDs(); - if (dimensionIDs == DimensionProperty.DimensionPropertyList.EMPTY_LIST) { + if (dimensionIDs == null) { dimensionIDs = new DimensionProperty.DimensionPropertyList(); this.applyProperty(DimensionProperty.getInstance(), dimensionIDs); } @@ -201,22 +190,16 @@ public boolean applyProperty(@NotNull String key, @Nullable Object value) { return false; } - public boolean applyProperty(@NotNull RecipeProperty property, @Nullable Object value) { - if (value == null) { - if (this.recipePropertyStorage != null) { - return this.recipePropertyStorage.remove(property); - } - } else { - if (this.recipePropertyStorage == null) { - this.recipePropertyStorage = new RecipePropertyStorage(); - } - boolean stored = this.recipePropertyStorage.store(property, value); - if (!stored) { - this.recipePropertyStorageErrored = true; - } - return stored; + public final boolean applyProperty(@NotNull RecipeProperty property, @NotNull Object value) { + if (this.recipePropertyStorage == RecipePropertyStorage.EMPTY) { + this.recipePropertyStorage = new RecipePropertyStorageImpl(); + } + + boolean stored = this.recipePropertyStorage.store(property, value); + if (!stored) { + this.recipePropertyStorageErrored = true; } - return true; + return stored; } public R input(GTRecipeInput input) { @@ -791,8 +774,8 @@ public void chancedOutputsMultiply(Recipe chancedOutputsFrom, int numberOfOperat */ public R append(Recipe recipe, int multiplier, boolean multiplyDuration) { - for (Map.Entry, Object> property : recipe.getPropertyValues()) { - this.applyProperty(property.getKey().getKey(), property.getValue()); + for (Map.Entry, Object> property : recipe.propertyStorage().entrySet()) { + this.applyPropertyCT(property.getKey().getKey(), property.getValue()); } // Create holders for the various parts of the new multiplied Recipe @@ -905,12 +888,9 @@ public R copy() { return (R) new RecipeBuilder<>(this); } - protected EnumValidationResult finalizeAndValidate() { - return recipePropertyStorageErrored ? EnumValidationResult.INVALID : validate(); - } - public ValidationResult build() { - return ValidationResult.newResult(finalizeAndValidate(), new Recipe(inputs, outputs, + EnumValidationResult result = recipePropertyStorageErrored ? EnumValidationResult.INVALID : validate(); + return ValidationResult.newResult(result, new Recipe(inputs, outputs, new ChancedOutputList<>(this.chancedOutputLogic, chancedOutputs), fluidInputs, fluidOutputs, new ChancedOutputList<>(this.chancedFluidOutputLogic, chancedFluidOutputs), @@ -956,9 +936,6 @@ protected EnumValidationResult validate() { if (recipeStatus == EnumValidationResult.INVALID) { GTLog.logger.error("Invalid recipe, read the errors above: {}", this); } - if (recipePropertyStorage != null) { - recipePropertyStorage.freeze(true); - } return recipeStatus; } @@ -1067,10 +1044,8 @@ public int getDuration() { return duration; } - @Nullable - public CleanroomType getCleanroom() { - return this.recipePropertyStorage == null ? null : - this.recipePropertyStorage.getRecipePropertyValue(CleanroomProperty.getInstance(), null); + public @Nullable CleanroomType getCleanroom() { + return this.recipePropertyStorage.get(CleanroomProperty.getInstance(), null); } @Override diff --git a/src/main/java/gregtech/api/recipes/builders/AssemblyLineRecipeBuilder.java b/src/main/java/gregtech/api/recipes/builders/AssemblyLineRecipeBuilder.java index 40a23a261ff..dc711dd0a0a 100644 --- a/src/main/java/gregtech/api/recipes/builders/AssemblyLineRecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/builders/AssemblyLineRecipeBuilder.java @@ -4,8 +4,8 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.ResearchProperty; -import gregtech.api.recipes.recipeproperties.ResearchPropertyData; +import gregtech.api.recipes.properties.impl.ResearchProperty; +import gregtech.api.recipes.properties.impl.ResearchPropertyData; import gregtech.api.util.AssemblyLineManager; import gregtech.api.util.EnumValidationResult; import gregtech.api.util.GTLog; @@ -14,7 +14,6 @@ import net.minecraft.item.ItemStack; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -45,14 +44,15 @@ public AssemblyLineRecipeBuilder copy() { } @Override - public boolean applyProperty(@NotNull String key, @Nullable Object value) { + public boolean applyPropertyCT(@NotNull String key, @NotNull Object value) { if (key.equals(ResearchProperty.KEY)) { if (value instanceof ItemStack itemStack) { scannerResearch(itemStack); return true; } + return false; } - return super.applyProperty(key, value); + return super.applyPropertyCT(key, value); } private boolean applyResearchProperty(ResearchPropertyData.ResearchEntry researchEntry) { @@ -70,15 +70,13 @@ private boolean applyResearchProperty(ResearchPropertyData.ResearchEntry researc return false; } - if (recipePropertyStorage != null && recipePropertyStorage.hasRecipeProperty(ResearchProperty.getInstance())) { - ResearchPropertyData property = recipePropertyStorage.getRecipePropertyValue(ResearchProperty.getInstance(), - null); - if (property == null) throw new IllegalStateException("Property storage has a null property"); + ResearchPropertyData property = recipePropertyStorage.get(ResearchProperty.getInstance(), null); + if (property != null) { property.add(researchEntry); return true; } - ResearchPropertyData property = new ResearchPropertyData(); + property = new ResearchPropertyData(); if (applyProperty(ResearchProperty.getInstance(), property)) { property.add(researchEntry); return true; diff --git a/src/main/java/gregtech/api/recipes/builders/BlastRecipeBuilder.java b/src/main/java/gregtech/api/recipes/builders/BlastRecipeBuilder.java index 8aa2bd236cc..c5bf92c5e16 100644 --- a/src/main/java/gregtech/api/recipes/builders/BlastRecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/builders/BlastRecipeBuilder.java @@ -3,7 +3,7 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.recipes.properties.impl.TemperatureProperty; import gregtech.api.util.EnumValidationResult; import gregtech.api.util.GTLog; @@ -28,12 +28,12 @@ public BlastRecipeBuilder copy() { } @Override - public boolean applyProperty(@NotNull String key, Object value) { + public boolean applyPropertyCT(@NotNull String key, @NotNull Object value) { if (key.equals(TemperatureProperty.KEY)) { this.blastFurnaceTemp(((Number) value).intValue()); return true; } - return super.applyProperty(key, value); + return super.applyPropertyCT(key, value); } public BlastRecipeBuilder blastFurnaceTemp(int blastFurnaceTemp) { @@ -47,8 +47,7 @@ public BlastRecipeBuilder blastFurnaceTemp(int blastFurnaceTemp) { } public int getBlastFurnaceTemp() { - return this.recipePropertyStorage == null ? 0 : - this.recipePropertyStorage.getRecipePropertyValue(TemperatureProperty.getInstance(), 0); + return this.recipePropertyStorage.get(TemperatureProperty.getInstance(), 0); } @Override diff --git a/src/main/java/gregtech/api/recipes/builders/ComputationRecipeBuilder.java b/src/main/java/gregtech/api/recipes/builders/ComputationRecipeBuilder.java index 2da0309972a..1d6c49a75d8 100644 --- a/src/main/java/gregtech/api/recipes/builders/ComputationRecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/builders/ComputationRecipeBuilder.java @@ -3,8 +3,8 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.ComputationProperty; -import gregtech.api.recipes.recipeproperties.TotalComputationProperty; +import gregtech.api.recipes.properties.impl.ComputationProperty; +import gregtech.api.recipes.properties.impl.TotalComputationProperty; import gregtech.api.util.EnumValidationResult; import gregtech.api.util.GTLog; @@ -28,7 +28,7 @@ public ComputationRecipeBuilder copy() { } @Override - public boolean applyProperty(@NotNull String key, Object value) { + public boolean applyPropertyCT(@NotNull String key, @NotNull Object value) { if (key.equals(ComputationProperty.KEY)) { this.CWUt(((Number) value).intValue()); return true; @@ -37,7 +37,7 @@ public boolean applyProperty(@NotNull String key, Object value) { this.totalCWU(((Number) value).intValue()); return true; } - return super.applyProperty(key, value); + return super.applyPropertyCT(key, value); } public ComputationRecipeBuilder CWUt(int cwut) { diff --git a/src/main/java/gregtech/api/recipes/builders/FusionRecipeBuilder.java b/src/main/java/gregtech/api/recipes/builders/FusionRecipeBuilder.java index a6f56e1420a..91622a9950c 100644 --- a/src/main/java/gregtech/api/recipes/builders/FusionRecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/builders/FusionRecipeBuilder.java @@ -3,7 +3,7 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.FusionEUToStartProperty; +import gregtech.api.recipes.properties.impl.FusionEUToStartProperty; import gregtech.api.util.EnumValidationResult; import gregtech.api.util.GTLog; @@ -28,12 +28,12 @@ public FusionRecipeBuilder copy() { } @Override - public boolean applyProperty(@NotNull String key, Object value) { + public boolean applyPropertyCT(@NotNull String key, @NotNull Object value) { if (key.equals(FusionEUToStartProperty.KEY)) { this.EUToStart(((Number) value).longValue()); return true; } - return super.applyProperty(key, value); + return super.applyPropertyCT(key, value); } public FusionRecipeBuilder EUToStart(long EUToStart) { @@ -46,8 +46,7 @@ public FusionRecipeBuilder EUToStart(long EUToStart) { } public long getEUToStart() { - return this.recipePropertyStorage == null ? 0L : - this.recipePropertyStorage.getRecipePropertyValue(FusionEUToStartProperty.getInstance(), 0L); + return this.recipePropertyStorage.get(FusionEUToStartProperty.getInstance(), 0L); } @Override diff --git a/src/main/java/gregtech/api/recipes/builders/ImplosionRecipeBuilder.java b/src/main/java/gregtech/api/recipes/builders/ImplosionRecipeBuilder.java index 3ada0e83415..ce078b9751b 100644 --- a/src/main/java/gregtech/api/recipes/builders/ImplosionRecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/builders/ImplosionRecipeBuilder.java @@ -4,10 +4,9 @@ import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.ingredients.GTRecipeItemInput; -import gregtech.api.recipes.recipeproperties.ImplosionExplosiveProperty; +import gregtech.api.recipes.properties.impl.ImplosionExplosiveProperty; import gregtech.api.util.EnumValidationResult; import gregtech.api.util.GTLog; -import gregtech.api.util.ValidationResult; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; @@ -34,61 +33,52 @@ public ImplosionRecipeBuilder copy() { } @Override - public boolean applyProperty(@NotNull String key, Object value) { + public boolean applyPropertyCT(@NotNull String key, @NotNull Object value) { if (key.equals(ImplosionExplosiveProperty.KEY)) { - if (value instanceof ItemStack) { - this.applyProperty(ImplosionExplosiveProperty.getInstance(), value); - } else { - this.applyProperty(ImplosionExplosiveProperty.getInstance(), new ItemStack(Blocks.TNT, (int) value)); + if (value instanceof ItemStack stack) { + return this.applyProperty(ImplosionExplosiveProperty.getInstance(), stack); + } else if (value instanceof Number number) { + return this.applyProperty(ImplosionExplosiveProperty.getInstance(), number.intValue()); } - return true; + return false; } - return super.applyProperty(key, value); + return super.applyPropertyCT(key, value); } @ZenMethod - public ImplosionRecipeBuilder explosivesAmount(int explosivesAmount) { - if (1 > explosivesAmount || explosivesAmount > 64) { - GTLog.logger.error("Amount of explosives should be from 1 to 64 inclusive", new Throwable()); - recipeStatus = EnumValidationResult.INVALID; - } - this.applyProperty(ImplosionExplosiveProperty.getInstance(), new ItemStack(Blocks.TNT, explosivesAmount)); - return this; + public ImplosionRecipeBuilder explosives(int amount) { + return explosives(new ItemStack(Blocks.TNT, amount)); } @ZenMethod - public ImplosionRecipeBuilder explosivesType(ItemStack explosivesType) { - if (1 > explosivesType.getCount() || explosivesType.getCount() > 64) { + public ImplosionRecipeBuilder explosives(@NotNull ItemStack explosive) { + if (explosive.isEmpty()) { + GTLog.logger.error("Cannot use empty explosives", new Throwable()); + this.recipeStatus = EnumValidationResult.INVALID; + return this; + } + + int count = explosive.getCount(); + if (count < 1 || count > 64) { GTLog.logger.error("Amount of explosives should be from 1 to 64 inclusive", new Throwable()); recipeStatus = EnumValidationResult.INVALID; + return this; } - this.applyProperty(ImplosionExplosiveProperty.getInstance(), explosivesType); - return this; - } - - public ItemStack getExplosivesType() { - if (this.recipePropertyStorage == null) { - return ItemStack.EMPTY; + if (this.applyProperty(ImplosionExplosiveProperty.getInstance(), explosive)) { + this.inputs.add(new GTRecipeItemInput(explosive)); } - return this.recipePropertyStorage.getRecipePropertyValue(ImplosionExplosiveProperty.getInstance(), - ItemStack.EMPTY); + return this; } - public ValidationResult build() { - ItemStack explosivesType = getExplosivesType(); - if (!explosivesType.isEmpty()) { - this.inputs.add(new GTRecipeItemInput(explosivesType)); - } else { - this.recipePropertyStorageErrored = true; - } - return super.build(); + public @NotNull ItemStack getExplosives() { + return this.recipePropertyStorage.get(ImplosionExplosiveProperty.getInstance(), ItemStack.EMPTY); } @Override public String toString() { return new ToStringBuilder(this) .appendSuper(super.toString()) - .append(ImplosionExplosiveProperty.getInstance().getKey(), getExplosivesType()) + .append(ImplosionExplosiveProperty.getInstance().getKey(), getExplosives()) .toString(); } } diff --git a/src/main/java/gregtech/api/recipes/builders/PrimitiveRecipeBuilder.java b/src/main/java/gregtech/api/recipes/builders/PrimitiveRecipeBuilder.java index 858a94d1177..ec0e8cf2202 100644 --- a/src/main/java/gregtech/api/recipes/builders/PrimitiveRecipeBuilder.java +++ b/src/main/java/gregtech/api/recipes/builders/PrimitiveRecipeBuilder.java @@ -3,7 +3,7 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.PrimitiveProperty; +import gregtech.api.recipes.properties.impl.PrimitiveProperty; import gregtech.api.util.ValidationResult; public class PrimitiveRecipeBuilder extends RecipeBuilder { diff --git a/src/main/java/gregtech/api/recipes/machines/RecipeMapAssemblyLine.java b/src/main/java/gregtech/api/recipes/machines/RecipeMapAssemblyLine.java index 5c23b88f6a2..d1d8301a3c7 100644 --- a/src/main/java/gregtech/api/recipes/machines/RecipeMapAssemblyLine.java +++ b/src/main/java/gregtech/api/recipes/machines/RecipeMapAssemblyLine.java @@ -3,8 +3,8 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.ResearchProperty; -import gregtech.api.recipes.recipeproperties.ResearchPropertyData; +import gregtech.api.recipes.properties.impl.ResearchProperty; +import gregtech.api.recipes.properties.impl.ResearchPropertyData; import gregtech.api.recipes.ui.RecipeMapUIFunction; import gregtech.core.sound.GTSoundEvents; @@ -32,15 +32,11 @@ public RecipeMapAssemblyLine(@NotNull String unlocalizedName, @NotNull R default @Override public boolean compileRecipe(Recipe recipe) { if (!super.compileRecipe(recipe)) return false; - if (recipe.hasProperty(ResearchProperty.getInstance())) { - ResearchPropertyData data = recipe.getProperty(ResearchProperty.getInstance(), null); - if (data != null) { - for (ResearchPropertyData.ResearchEntry entry : data) { - addDataStickEntry(entry.getResearchId(), recipe); - } - return true; + ResearchPropertyData data = recipe.getProperty(ResearchProperty.getInstance(), null); + if (data != null) { + for (ResearchPropertyData.ResearchEntry entry : data) { + addDataStickEntry(entry.researchId(), recipe); } - return false; } return true; } @@ -48,15 +44,11 @@ public boolean compileRecipe(Recipe recipe) { @Override public boolean removeRecipe(@NotNull Recipe recipe) { if (!super.removeRecipe(recipe)) return false; - if (recipe.hasProperty(ResearchProperty.getInstance())) { - ResearchPropertyData data = recipe.getProperty(ResearchProperty.getInstance(), null); - if (data != null) { - for (ResearchPropertyData.ResearchEntry entry : data) { - removeDataStickEntry(entry.getResearchId(), recipe); - } - return true; + ResearchPropertyData data = recipe.getProperty(ResearchProperty.getInstance(), null); + if (data != null) { + for (ResearchPropertyData.ResearchEntry entry : data) { + removeDataStickEntry(entry.researchId(), recipe); } - return false; } return true; } diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/RecipeProperty.java b/src/main/java/gregtech/api/recipes/properties/RecipeProperty.java similarity index 62% rename from src/main/java/gregtech/api/recipes/recipeproperties/RecipeProperty.java rename to src/main/java/gregtech/api/recipes/properties/RecipeProperty.java index 9dde81bb195..bb54c07603f 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/RecipeProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/RecipeProperty.java @@ -1,11 +1,13 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties; import net.minecraft.client.Minecraft; +import net.minecraft.nbt.NBTBase; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import org.jetbrains.annotations.NotNull; + import java.util.List; -import java.util.Objects; public abstract class RecipeProperty { @@ -17,6 +19,18 @@ protected RecipeProperty(String key, Class type) { this.type = type; } + /** + * @param value the value to serialize + * @return the serialized form of the value + */ + public abstract @NotNull NBTBase serialize(@NotNull Object value); + + /** + * @param nbt the nbt to deserialize + * @return the deserialized property value + */ + public abstract @NotNull Object deserialize(@NotNull NBTBase nbt); + @SideOnly(Side.CLIENT) public abstract void drawInfo(Minecraft minecraft, int x, int y, int color, Object value); @@ -28,19 +42,15 @@ public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value, @SideOnly(Side.CLIENT) public void getTooltipStrings(List tooltip, int mouseX, int mouseY, Object value) {} - public int getInfoHeight(Object value) { + public int getInfoHeight(@NotNull Object value) { return 10; // GTRecipeWrapper#LINE_HEIGHT } - public boolean isOfType(Class otherType) { - return this.type == otherType; - } - - public String getKey() { + public final @NotNull String getKey() { return key; } - public T castValue(Object value) { + protected final T castValue(@NotNull Object value) { return this.type.cast(value); } @@ -75,15 +85,20 @@ public boolean hideDuration() { } @Override - public boolean equals(Object o) { + public final boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - RecipeProperty that = (RecipeProperty) o; - return Objects.equals(type, that.type) && Objects.equals(key, that.key); + if (!(o instanceof RecipePropertythat)) return false; + + return type.equals(that.type) && getKey().equals(that.getKey()); + } + + @Override + public final int hashCode() { + return 31 * type.hashCode() + getKey().hashCode(); } @Override - public int hashCode() { - return Objects.hash(type, key); + public String toString() { + return "RecipeProperty{" + "key='" + key + "'}"; } } diff --git a/src/main/java/gregtech/api/recipes/properties/RecipePropertyRegistry.java b/src/main/java/gregtech/api/recipes/properties/RecipePropertyRegistry.java new file mode 100644 index 00000000000..13defc9f723 --- /dev/null +++ b/src/main/java/gregtech/api/recipes/properties/RecipePropertyRegistry.java @@ -0,0 +1,29 @@ +package gregtech.api.recipes.properties; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public final class RecipePropertyRegistry { + + private final Map> map = new Object2ReferenceOpenHashMap<>(); + + /** + * @param key the RecipeProperty's key + * @param property the property's instance + */ + public void register(@NotNull String key, @NotNull RecipeProperty property) { + if (map.containsKey(key)) { + throw new IllegalArgumentException("RecipeProperty is already registered: " + key); + } + map.put(key, property); + } + + @ApiStatus.Internal + public @Nullable RecipeProperty get(@NotNull String key) { + return map.get(key); + } +} diff --git a/src/main/java/gregtech/api/recipes/properties/RecipePropertyStorage.java b/src/main/java/gregtech/api/recipes/properties/RecipePropertyStorage.java new file mode 100644 index 00000000000..790143293dd --- /dev/null +++ b/src/main/java/gregtech/api/recipes/properties/RecipePropertyStorage.java @@ -0,0 +1,119 @@ +package gregtech.api.recipes.properties; + +import gregtech.api.util.GTLog; + +import net.minecraft.nbt.NBTTagCompound; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public interface RecipePropertyStorage { + + /** + * @param recipeProperty the property to store + * @param value the value to store + * @return if the store succeeds + */ + boolean store(@NotNull RecipeProperty recipeProperty, @NotNull Object value); + + /** + * @return a copy of this property storage + */ + @NotNull + RecipePropertyStorage copy(); + + /** + * @return number of stored properties + */ + int size(); + + /** + * @return all stored properties and values + */ + @NotNull + Set, Object>> entrySet(); + + /** + * @param recipeProperty the property to retrieve + * @param defaultValue default value if the property is not found + * @param the type of returned value + * @return value associated with the provided recipeProperty, otherwise the default + */ + @Contract("_, !null -> !null") + @Nullable T get(@NotNull RecipeProperty recipeProperty, @Nullable T defaultValue); + + /** + * @param recipeProperty the property to check + * @return if the property is in this storage + */ + boolean contains(@NotNull RecipeProperty recipeProperty); + + /** + * @return the recipe property values + */ + @UnmodifiableView + @NotNull + Set<@NotNull RecipeProperty> values(); + + @NotNull + NBTTagCompound serializeNBT(); + + void deserializeNBT(@NotNull NBTTagCompound nbt); + + RecipePropertyStorage EMPTY = new RecipePropertyStorage() { + + @Override + public boolean store(@NotNull RecipeProperty recipeProperty, @NotNull Object value) { + throw new UnsupportedOperationException("empty"); + } + + @Override + public @NotNull RecipePropertyStorage copy() { + return this; + } + + @Override + public int size() { + return 0; + } + + @Override + public @NotNull Set, Object>> entrySet() { + return Collections.emptySet(); + } + + @Override + public @Nullable T get(@NotNull RecipeProperty recipeProperty, @Nullable T defaultValue) { + return defaultValue; + } + + @Override + public boolean contains(@NotNull RecipeProperty recipeProperty) { + return false; + } + + @Override + public @UnmodifiableView @NotNull Set<@NotNull RecipeProperty> values() { + return Collections.emptySet(); + } + + @Override + public @NotNull NBTTagCompound serializeNBT() { + return new NBTTagCompound(); + } + + @Override + public void deserializeNBT(@NotNull NBTTagCompound nbt) { + if (!nbt.isEmpty()) { + GTLog.logger.warn("Tried to deserialize non-empty tag in RecipePropertyStorage.EMPTY: {}", nbt, + new Throwable()); + } + } + }; +} diff --git a/src/main/java/gregtech/api/recipes/properties/RecipePropertyStorageImpl.java b/src/main/java/gregtech/api/recipes/properties/RecipePropertyStorageImpl.java new file mode 100644 index 00000000000..ffc7ea21227 --- /dev/null +++ b/src/main/java/gregtech/api/recipes/properties/RecipePropertyStorageImpl.java @@ -0,0 +1,111 @@ +package gregtech.api.recipes.properties; + +import gregtech.api.GregTechAPI; +import gregtech.api.util.GTLog; + +import net.minecraft.nbt.NBTTagCompound; + +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Map; +import java.util.Set; + +public final class RecipePropertyStorageImpl implements RecipePropertyStorage { + + private final Map, Object> map; + + public RecipePropertyStorageImpl() { + this(new Object2ObjectArrayMap<>(1)); + } + + private RecipePropertyStorageImpl(@NotNull Map, Object> map) { + this.map = map; + } + + @Override + public boolean store(@NotNull RecipeProperty recipeProperty, @NotNull Object value) { + String key = recipeProperty.getKey(); + if (map.containsKey(recipeProperty)) { + GTLog.logger.warn("Duplicate recipe property added: {} -> {}", key, value, new Throwable()); + return false; + } + + try { + recipeProperty.castValue(value); + } catch (ClassCastException e) { + GTLog.logger.error("Provided incorrect value for RecipeProperty with key {}", key, e); + return false; + } + + map.put(recipeProperty, value); + return true; + } + + @Override + public @NotNull RecipePropertyStorage copy() { + return new RecipePropertyStorageImpl(new Object2ObjectArrayMap<>(this.map)); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public @NotNull Set, Object>> entrySet() { + return this.map.entrySet(); + } + + @Override + @Contract("_, !null -> !null") + public @Nullable T get(@NotNull RecipeProperty recipeProperty, @Nullable T defaultValue) { + var value = map.get(recipeProperty); + if (value == null) { + return defaultValue; + } + + return recipeProperty.castValue(value); + } + + @Override + public boolean contains(@NotNull RecipeProperty recipeProperty) { + return map.containsKey(recipeProperty); + } + + @Override + @UnmodifiableView + public @NotNull Set<@NotNull RecipeProperty> values() { + return map.keySet(); + } + + @Override + public @NotNull String toString() { + return "RecipePropertyStorage{" + map + '}'; + } + + @Override + public @NotNull NBTTagCompound serializeNBT() { + NBTTagCompound tag = new NBTTagCompound(); + for (var entry : map.entrySet()) { + var property = entry.getKey(); + tag.setTag(property.getKey(), property.serialize(entry.getValue())); + } + return tag; + } + + @Override + public void deserializeNBT(@NotNull NBTTagCompound nbt) { + for (var entry : nbt.tagMap.entrySet()) { + var property = GregTechAPI.RECIPE_PROPERTIES.get(entry.getKey()); + if (property == null) { + GTLog.logger.warn("Failed to read property with key {}", entry.getKey()); + continue; + } + map.put(property, property.deserialize(entry.getValue())); + } + } +} diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/CleanroomProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/CleanroomProperty.java similarity index 63% rename from src/main/java/gregtech/api/recipes/recipeproperties/CleanroomProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/CleanroomProperty.java index e048ffa5626..5329b868c04 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/CleanroomProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/CleanroomProperty.java @@ -1,15 +1,21 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; +import gregtech.api.GregTechAPI; import gregtech.api.metatileentity.multiblock.CleanroomType; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagString; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.jetbrains.annotations.NotNull; -public class CleanroomProperty extends RecipeProperty { +import java.util.Objects; + +public final class CleanroomProperty extends RecipeProperty { public static final String KEY = "cleanroom"; @@ -22,24 +28,27 @@ private CleanroomProperty() { public static CleanroomProperty getInstance() { if (INSTANCE == null) { INSTANCE = new CleanroomProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } @Override - @SideOnly(Side.CLIENT) - public void drawInfo(@NotNull Minecraft minecraft, int x, int y, int color, Object value) { - CleanroomType type = castValue(value); - if (type == null) return; + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagString(castValue(value).getName()); + } - minecraft.fontRenderer.drawString(I18n.format("gregtech.recipe.cleanroom", getName(type)), x, y, color); + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return Objects.requireNonNull(CleanroomType.getByName(((NBTTagString) nbt).getString())); } @Override - public int getInfoHeight(Object value) { + @SideOnly(Side.CLIENT) + public void drawInfo(@NotNull Minecraft minecraft, int x, int y, int color, Object value) { CleanroomType type = castValue(value); - if (type == null) return 0; - return super.getInfoHeight(value); + + minecraft.fontRenderer.drawString(I18n.format("gregtech.recipe.cleanroom", getName(type)), x, y, color); } @NotNull diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/ComputationProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/ComputationProperty.java similarity index 53% rename from src/main/java/gregtech/api/recipes/recipeproperties/ComputationProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/ComputationProperty.java index 5f2de1b653b..f57a6ed2ea7 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/ComputationProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/ComputationProperty.java @@ -1,27 +1,45 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; + +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagInt; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -public class ComputationProperty extends RecipeProperty { +import org.jetbrains.annotations.NotNull; + +public final class ComputationProperty extends RecipeProperty { public static final String KEY = "computation_per_tick"; private static ComputationProperty INSTANCE; - protected ComputationProperty() { + private ComputationProperty() { super(KEY, Integer.class); } public static ComputationProperty getInstance() { if (INSTANCE == null) { INSTANCE = new ComputationProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagInt(castValue(value)); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return ((NBTTagInt) nbt).getInt(); + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/DimensionProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/DimensionProperty.java similarity index 63% rename from src/main/java/gregtech/api/recipes/recipeproperties/DimensionProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/DimensionProperty.java index 3795db18aa1..721300af5d4 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/DimensionProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/DimensionProperty.java @@ -1,17 +1,22 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import gregtech.api.worldgen.config.WorldGenRegistry; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import org.jetbrains.annotations.NotNull; -public class DimensionProperty extends RecipeProperty { +public final class DimensionProperty extends RecipeProperty { public static final String KEY = "dimension"; @@ -22,11 +27,36 @@ private DimensionProperty() { } public static DimensionProperty getInstance() { - if (INSTANCE == null) + if (INSTANCE == null) { INSTANCE = new DimensionProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); + } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + DimensionPropertyList list = castValue(value); + NBTTagCompound tag = new NBTTagCompound(); + tag.setIntArray("whiteListDimensions", list.whiteListDimensions.toArray(new int[0])); + tag.setIntArray("blackListDimensions", list.blackListDimensions.toArray(new int[0])); + return tag; + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + NBTTagCompound tag = (NBTTagCompound) nbt; + DimensionPropertyList list = new DimensionPropertyList(); + for (int i : tag.getIntArray("whiteListDimensions")) { + list.add(i, false); + } + + for (int i : tag.getIntArray("blackListDimensions")) { + list.add(i, true); + } + return tag; + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { @@ -62,8 +92,8 @@ public static class DimensionPropertyList { public static DimensionPropertyList EMPTY_LIST = new DimensionPropertyList(); - public IntList whiteListDimensions = new IntArrayList(); - public IntList blackListDimensions = new IntArrayList(); + public final IntList whiteListDimensions = new IntArrayList(); + public final IntList blackListDimensions = new IntArrayList(); public void add(int key, boolean toBlacklist) { if (toBlacklist) { @@ -75,16 +105,13 @@ public void add(int key, boolean toBlacklist) { } } - public void merge(DimensionPropertyList list) { + public void merge(@NotNull DimensionPropertyList list) { this.whiteListDimensions.addAll(list.whiteListDimensions); this.blackListDimensions.addAll(list.blackListDimensions); } public boolean checkDimension(int dim) { - boolean valid = true; - if (this.blackListDimensions.size() > 0) valid = !this.blackListDimensions.contains(dim); - if (this.whiteListDimensions.size() > 0) valid = this.whiteListDimensions.contains(dim); - return valid; + return !blackListDimensions.contains(dim) && whiteListDimensions.contains(dim); } } } diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/FusionEUToStartProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/FusionEUToStartProperty.java similarity index 74% rename from src/main/java/gregtech/api/recipes/recipeproperties/FusionEUToStartProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/FusionEUToStartProperty.java index 758480a4816..13446abe8d1 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/FusionEUToStartProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/FusionEUToStartProperty.java @@ -1,19 +1,24 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import gregtech.api.util.TextFormattingUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagLong; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; import java.util.Map; import java.util.TreeMap; -public class FusionEUToStartProperty extends RecipeProperty { +public final class FusionEUToStartProperty extends RecipeProperty { public static final String KEY = "eu_to_start"; @@ -21,18 +26,29 @@ public class FusionEUToStartProperty extends RecipeProperty { private static FusionEUToStartProperty INSTANCE; - protected FusionEUToStartProperty() { + private FusionEUToStartProperty() { super(KEY, Long.class); } public static FusionEUToStartProperty getInstance() { if (INSTANCE == null) { INSTANCE = new FusionEUToStartProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagLong(castValue(value)); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return ((NBTTagLong) nbt).getLong(); + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/ImplosionExplosiveProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/ImplosionExplosiveProperty.java similarity index 55% rename from src/main/java/gregtech/api/recipes/recipeproperties/ImplosionExplosiveProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/ImplosionExplosiveProperty.java index fd88401041b..915fc5aa4e3 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/ImplosionExplosiveProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/ImplosionExplosiveProperty.java @@ -1,12 +1,19 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; + +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -public class ImplosionExplosiveProperty extends RecipeProperty { +import org.jetbrains.annotations.NotNull; + +public final class ImplosionExplosiveProperty extends RecipeProperty { public static final String KEY = "explosives"; @@ -19,16 +26,27 @@ private ImplosionExplosiveProperty() { public static ImplosionExplosiveProperty getInstance() { if (INSTANCE == null) { INSTANCE = new ImplosionExplosiveProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return castValue(value).serializeNBT(); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return new ItemStack((NBTTagCompound) nbt); + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { minecraft.fontRenderer.drawString(I18n.format("gregtech.recipe.explosive", - ((ItemStack) value).getDisplayName()), x, y, color); + castValue(value).getDisplayName()), x, y, color); } @Override diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/PrimitiveProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/PrimitiveProperty.java similarity index 55% rename from src/main/java/gregtech/api/recipes/recipeproperties/PrimitiveProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/PrimitiveProperty.java index 908127d3573..22b49f00bb8 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/PrimitiveProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/PrimitiveProperty.java @@ -1,13 +1,20 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; + +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagByte; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import org.jetbrains.annotations.NotNull; + /** * Simple Marker Property to tell JEI to not display Total EU and EU/t. */ -public class PrimitiveProperty extends RecipeProperty { +public final class PrimitiveProperty extends RecipeProperty { public static final String KEY = "primitive_property"; private static PrimitiveProperty INSTANCE; @@ -19,16 +26,27 @@ private PrimitiveProperty() { public static PrimitiveProperty getInstance() { if (INSTANCE == null) { INSTANCE = new PrimitiveProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagByte((byte) (castValue(value) ? 1 : 0)); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return ((NBTTagByte) nbt).getByte() == 1; + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) {} @Override - public int getInfoHeight(Object value) { + public int getInfoHeight(@NotNull Object value) { return 0; } diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/ResearchProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/ResearchProperty.java similarity index 50% rename from src/main/java/gregtech/api/recipes/recipeproperties/ResearchProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/ResearchProperty.java index ec4d6ab105e..15b538fa5d6 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/ResearchProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/ResearchProperty.java @@ -1,7 +1,12 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; + +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagList; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -21,10 +26,31 @@ private ResearchProperty() { public static ResearchProperty getInstance() { if (INSTANCE == null) { INSTANCE = new ResearchProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + NBTTagList list = new NBTTagList(); + for (var entry : castValue(value)) { + list.appendTag(entry.serializeNBT()); + } + return list; + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + NBTTagList list = (NBTTagList) nbt; + ResearchPropertyData data = new ResearchPropertyData(); + for (int i = 0; i < list.tagCount(); i++) { + data.add(ResearchPropertyData.ResearchEntry.deserializeFromNBT(list.getCompoundTagAt(i))); + } + + return data; + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(@NotNull Minecraft minecraft, int x, int y, int color, Object value) { diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/ResearchPropertyData.java b/src/main/java/gregtech/api/recipes/properties/impl/ResearchPropertyData.java similarity index 66% rename from src/main/java/gregtech/api/recipes/recipeproperties/ResearchPropertyData.java rename to src/main/java/gregtech/api/recipes/properties/impl/ResearchPropertyData.java index 4b199221d6c..95a5159aac0 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/ResearchPropertyData.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/ResearchPropertyData.java @@ -1,6 +1,7 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import org.jetbrains.annotations.NotNull; @@ -12,8 +13,6 @@ public final class ResearchPropertyData implements Iterable entries = new ArrayList<>(); - public ResearchPropertyData() {} - /** * @param entry the entry to add */ @@ -46,14 +45,23 @@ public ResearchEntry(@NotNull String researchId, @NotNull ItemStack dataItem) { this.dataItem = dataItem; } - @NotNull - public String getResearchId() { + public @NotNull String researchId() { return researchId; } - @NotNull - public ItemStack getDataItem() { + public @NotNull ItemStack dataItem() { return dataItem; } + + public @NotNull NBTTagCompound serializeNBT() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setString("researchId", researchId); + tag.setTag("dataItem", dataItem.serializeNBT()); + return tag; + } + + public static @NotNull ResearchEntry deserializeFromNBT(@NotNull NBTTagCompound tag) { + return new ResearchEntry(tag.getString("researchId"), new ItemStack(tag.getCompoundTag("dataItem"))); + } } } diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/ScanProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/ScanProperty.java similarity index 56% rename from src/main/java/gregtech/api/recipes/recipeproperties/ScanProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/ScanProperty.java index 8018561a710..4e80ca61c20 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/ScanProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/ScanProperty.java @@ -1,13 +1,18 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; + +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagByte; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.jetbrains.annotations.NotNull; -public class ScanProperty extends RecipeProperty { +public final class ScanProperty extends RecipeProperty { public static final String KEY = "scan"; @@ -21,10 +26,21 @@ private ScanProperty() { public static ScanProperty getInstance() { if (INSTANCE == null) { INSTANCE = new ScanProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagByte((byte) (castValue(value) ? 1 : 0)); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return ((NBTTagByte) nbt).getByte() == 1; + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/TemperatureProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/TemperatureProperty.java similarity index 79% rename from src/main/java/gregtech/api/recipes/recipeproperties/TemperatureProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/TemperatureProperty.java index c9c1681d9ae..3764158baed 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/TemperatureProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/TemperatureProperty.java @@ -1,9 +1,13 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import gregtech.api.unification.material.Material; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagInt; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -13,7 +17,7 @@ import java.util.Map; import java.util.TreeMap; -public class TemperatureProperty extends RecipeProperty { +public final class TemperatureProperty extends RecipeProperty { public static final String KEY = "temperature"; @@ -28,10 +32,21 @@ private TemperatureProperty() { public static TemperatureProperty getInstance() { if (INSTANCE == null) { INSTANCE = new TemperatureProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagInt(castValue(value)); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return ((NBTTagInt) nbt).getInt(); + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/TotalComputationProperty.java b/src/main/java/gregtech/api/recipes/properties/impl/TotalComputationProperty.java similarity index 56% rename from src/main/java/gregtech/api/recipes/recipeproperties/TotalComputationProperty.java rename to src/main/java/gregtech/api/recipes/properties/impl/TotalComputationProperty.java index a6247be32c4..0704e44cb46 100644 --- a/src/main/java/gregtech/api/recipes/recipeproperties/TotalComputationProperty.java +++ b/src/main/java/gregtech/api/recipes/properties/impl/TotalComputationProperty.java @@ -1,27 +1,45 @@ -package gregtech.api.recipes.recipeproperties; +package gregtech.api.recipes.properties.impl; + +import gregtech.api.GregTechAPI; +import gregtech.api.recipes.properties.RecipeProperty; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagInt; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -public class TotalComputationProperty extends RecipeProperty { +import org.jetbrains.annotations.NotNull; + +public final class TotalComputationProperty extends RecipeProperty { public static final String KEY = "total_computation"; private static TotalComputationProperty INSTANCE; - protected TotalComputationProperty() { + private TotalComputationProperty() { super(KEY, Integer.class); } public static TotalComputationProperty getInstance() { if (INSTANCE == null) { INSTANCE = new TotalComputationProperty(); + GregTechAPI.RECIPE_PROPERTIES.register(KEY, INSTANCE); } return INSTANCE; } + @Override + public @NotNull NBTBase serialize(@NotNull Object value) { + return new NBTTagInt(castValue(value)); + } + + @Override + public @NotNull Object deserialize(@NotNull NBTBase nbt) { + return ((NBTTagInt) nbt).getInt(); + } + @Override @SideOnly(Side.CLIENT) public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/DefaultProperty.java b/src/main/java/gregtech/api/recipes/recipeproperties/DefaultProperty.java deleted file mode 100644 index 1579297b2cd..00000000000 --- a/src/main/java/gregtech/api/recipes/recipeproperties/DefaultProperty.java +++ /dev/null @@ -1,19 +0,0 @@ -package gregtech.api.recipes.recipeproperties; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.resources.I18n; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public class DefaultProperty extends RecipeProperty { - - public DefaultProperty(String key, Class type) { - super(key, type); - } - - @SideOnly(Side.CLIENT) - public void drawInfo(Minecraft minecraft, int x, int y, int color, Object value) { - minecraft.fontRenderer.drawString(I18n.format("gregtech.recipe." + getKey(), - castValue(value)), x, y, color); - } -} diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/EmptyRecipePropertyStorage.java b/src/main/java/gregtech/api/recipes/recipeproperties/EmptyRecipePropertyStorage.java deleted file mode 100644 index 89f6bb201d6..00000000000 --- a/src/main/java/gregtech/api/recipes/recipeproperties/EmptyRecipePropertyStorage.java +++ /dev/null @@ -1,65 +0,0 @@ -package gregtech.api.recipes.recipeproperties; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -public final class EmptyRecipePropertyStorage implements IRecipePropertyStorage { - - public static final EmptyRecipePropertyStorage INSTANCE = new EmptyRecipePropertyStorage(); - - private EmptyRecipePropertyStorage() {} - - @Override - public boolean store(RecipeProperty recipeProperty, Object value) { - return false; - } - - @Override - public boolean remove(RecipeProperty recipeProperty) { - return false; - } - - @Override - public void freeze(boolean frozen) {} - - @Override - public IRecipePropertyStorage copy() { - return null; // Fresh for RecipeBuilder to handle - } - - @Override - public int getSize() { - return 0; - } - - @Override - public Set, Object>> getRecipeProperties() { - return Collections.emptySet(); - } - - @Override - public T getRecipePropertyValue(RecipeProperty recipeProperty, T defaultValue) { - return defaultValue; - } - - @Override - public boolean hasRecipeProperty(RecipeProperty recipeProperty) { - return false; - } - - @Override - public Set getRecipePropertyKeys() { - return Collections.emptySet(); - } - - @Override - public Set> getPropertyTypes() { - return Collections.emptySet(); - } - - @Override - public Object getRawRecipePropertyValue(String key) { - return null; - } -} diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/IRecipePropertyStorage.java b/src/main/java/gregtech/api/recipes/recipeproperties/IRecipePropertyStorage.java deleted file mode 100644 index 9781e2484ce..00000000000 --- a/src/main/java/gregtech/api/recipes/recipeproperties/IRecipePropertyStorage.java +++ /dev/null @@ -1,60 +0,0 @@ -package gregtech.api.recipes.recipeproperties; - -import java.util.Map; -import java.util.Set; - -public interface IRecipePropertyStorage { - - /** - * Stores new {@link RecipeProperty} with value - * - * @param recipeProperty {@link RecipeProperty} - * @param value value - * @return {@code true} if store succeeds; otherwise {@code false} - */ - boolean store(RecipeProperty recipeProperty, Object value); - - boolean remove(RecipeProperty recipeProperty); - - void freeze(boolean frozen); - - IRecipePropertyStorage copy(); - - /** - * Provides information how many {@link RecipeProperty} are stored - * - * @return number of stored {@link RecipeProperty} - */ - int getSize(); - - /** - * Provides all stored {@link RecipeProperty} - * - * @return all stored {@link RecipeProperty} and values - */ - Set, Object>> getRecipeProperties(); - - /** - * Provides casted value for one specific {@link RecipeProperty} if is stored or defaultValue - * - * @param recipeProperty {@link RecipeProperty} - * @param defaultValue Default value if recipeProperty is not found - * @param Type of returned value - * @return value tied with provided recipeProperty on success; otherwise defaultValue - */ - T getRecipePropertyValue(RecipeProperty recipeProperty, T defaultValue); - - boolean hasRecipeProperty(RecipeProperty recipeProperty); - - Set getRecipePropertyKeys(); - - Set> getPropertyTypes(); - - /** - * Provides un-casted value for one specific {@link RecipeProperty} searched by key - * - * @param key Key of stored {@link RecipeProperty} - * @return {@link Object} value on success; otherwise {@code null} - */ - Object getRawRecipePropertyValue(String key); -} diff --git a/src/main/java/gregtech/api/recipes/recipeproperties/RecipePropertyStorage.java b/src/main/java/gregtech/api/recipes/recipeproperties/RecipePropertyStorage.java deleted file mode 100644 index cf38c40ef3e..00000000000 --- a/src/main/java/gregtech/api/recipes/recipeproperties/RecipePropertyStorage.java +++ /dev/null @@ -1,134 +0,0 @@ -package gregtech.api.recipes.recipeproperties; - -import gregtech.api.util.GTLog; - -import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class RecipePropertyStorage implements IRecipePropertyStorage { - - private final Map, Object> recipeProperties; - - private boolean frozen = false; - - public RecipePropertyStorage() { - recipeProperties = new Object2ObjectArrayMap<>(1); - } - - private RecipePropertyStorage(Map, Object> recipeProperties) { - this(); - this.recipeProperties.putAll(recipeProperties); - } - - @Override - public boolean store(RecipeProperty recipeProperty, Object value) { - boolean success = true; - String key = recipeProperty.getKey(); - if (frozen) { - GTLog.logger.warn("Unable to add RecipeProperty with key {} as the storage is frozen", key); - success = false; - } - for (RecipeProperty existingRecipeProperty : recipeProperties.keySet()) { - if (existingRecipeProperty.getKey().equals(key)) { - GTLog.logger.warn("Unable to add RecipeProperty with key {} as it already exists", key); - success = false; - } - } - - if (value == null) { - GTLog.logger.warn("Provided value is null for RecipeProperty with key {}", key); - success = false; - } - - try { - recipeProperty.castValue(value); - } catch (ClassCastException e) { - GTLog.logger.warn("Provided incorrect value for RecipeProperty with key {}", key, e); - success = false; - } - - if (success) { - recipeProperties.put(recipeProperty, value); - } else { - GTLog.logger.warn("RecipePropertyStorage error found", new Throwable()); - } - - return success; - } - - @Override - public boolean remove(RecipeProperty recipeProperty) { - return this.recipeProperties.remove(recipeProperty) != null; - } - - @Override - public void freeze(boolean frozen) { - this.frozen = frozen; - } - - @Override - public IRecipePropertyStorage copy() { - return new RecipePropertyStorage(this.recipeProperties); - } - - @Override - public int getSize() { - return recipeProperties.size(); - } - - @Override - public Set, Object>> getRecipeProperties() { - return this.recipeProperties.entrySet(); - } - - @Override - public T getRecipePropertyValue(RecipeProperty recipeProperty, T defaultValue) { - Object value = recipeProperties.get(recipeProperty); - - if (value == null) { - return defaultValue; - } - - return recipeProperty.castValue(value); - } - - public boolean hasRecipeProperty(RecipeProperty recipeProperty) { - return recipeProperties.containsKey(recipeProperty); - } - - @Override - public Set getRecipePropertyKeys() { - HashSet keys = new HashSet<>(); - - recipeProperties.keySet().forEach(recipeProperty -> keys.add(recipeProperty.getKey())); - - return keys; - } - - @Override - public Set> getPropertyTypes() { - return recipeProperties.keySet(); - } - - @Override - public Object getRawRecipePropertyValue(String key) { - RecipeProperty recipeProperty = getRecipePropertyValue(key); - if (recipeProperty != null) { - return recipeProperties.get(recipeProperty); - } - - return null; - } - - private RecipeProperty getRecipePropertyValue(String key) { - for (RecipeProperty recipeProperty : recipeProperties.keySet()) { - if (recipeProperty.getKey().equals(key)) - return recipeProperty; - } - - return null; - } -} diff --git a/src/main/java/gregtech/api/recipes/ui/RecipeMapUI.java b/src/main/java/gregtech/api/recipes/ui/RecipeMapUI.java index a85fd546cae..a6d66d3e62e 100644 --- a/src/main/java/gregtech/api/recipes/ui/RecipeMapUI.java +++ b/src/main/java/gregtech/api/recipes/ui/RecipeMapUI.java @@ -283,8 +283,10 @@ public int getPropertyHeightShift() { int maxPropertyCount = 0; if (shouldShiftWidgets()) { for (Recipe recipe : recipeMap.getRecipeList()) { - if (recipe.getPropertyCount() > maxPropertyCount) - maxPropertyCount = recipe.getPropertyCount(); + int count = recipe.propertyStorage().size(); + if (count > maxPropertyCount) { + maxPropertyCount = count; + } } } return maxPropertyCount * 10; // GTRecipeWrapper#LINE_HEIGHT diff --git a/src/main/java/gregtech/api/util/AssemblyLineManager.java b/src/main/java/gregtech/api/util/AssemblyLineManager.java index a2652acfa84..7601c0e73cd 100644 --- a/src/main/java/gregtech/api/util/AssemblyLineManager.java +++ b/src/main/java/gregtech/api/util/AssemblyLineManager.java @@ -11,7 +11,7 @@ import gregtech.api.recipes.ingredients.nbtmatch.NBTMatcher; import gregtech.api.recipes.machines.IScannerRecipeMap; import gregtech.api.recipes.machines.RecipeMapScanner; -import gregtech.api.recipes.recipeproperties.ScanProperty; +import gregtech.api.recipes.properties.impl.ScanProperty; import gregtech.common.ConfigHolder; import gregtech.common.items.MetaItems; diff --git a/src/main/java/gregtech/api/util/FluidTankSwitchShim.java b/src/main/java/gregtech/api/util/FluidTankSwitchShim.java index e0210eee145..0e4c556da3a 100644 --- a/src/main/java/gregtech/api/util/FluidTankSwitchShim.java +++ b/src/main/java/gregtech/api/util/FluidTankSwitchShim.java @@ -11,14 +11,17 @@ // probably causes problems public class FluidTankSwitchShim implements IFluidTank, IFluidHandler { - IFluidTank tank; + @Nullable + private IFluidTank tank; + private static final FluidTankInfo NO_INFO = new FluidTankInfo(null, 0); + private static final IFluidTankProperties[] NO_PROPS = new IFluidTankProperties[0]; public FluidTankSwitchShim(IFluidTank tank) { changeTank(tank); } public void changeTank(IFluidTank tank) { - if (!(tank instanceof IFluidHandler)) { + if (tank != null && !(tank instanceof IFluidHandler)) { throw new IllegalArgumentException("Shim tank must be both IFluidTank and IFluidHandler!"); } this.tank = tank; @@ -27,43 +30,49 @@ public void changeTank(IFluidTank tank) { @Nullable @Override public FluidStack getFluid() { - return tank.getFluid(); + return tank == null ? null : tank.getFluid(); } @Override public int getFluidAmount() { - return tank.getFluidAmount(); + return tank == null ? 0 : tank.getFluidAmount(); } @Override public int getCapacity() { - return tank.getCapacity(); + return tank == null ? 0 : tank.getCapacity(); } @Override public FluidTankInfo getInfo() { - return tank.getInfo(); + return tank == null ? NO_INFO : tank.getInfo(); } @Override public IFluidTankProperties[] getTankProperties() { + if (tank == null) + return NO_PROPS; + return ((IFluidHandler) tank).getTankProperties(); } @Override public int fill(FluidStack resource, boolean doFill) { + if (tank == null) return 0; return ((IFluidHandler) tank).fill(resource, doFill); } @Nullable @Override public FluidStack drain(FluidStack resource, boolean doDrain) { + if (tank == null) return null; return ((IFluidHandler) tank).drain(resource, doDrain); } @Nullable @Override public FluidStack drain(int maxDrain, boolean doDrain) { + if (tank == null) return null; return tank.drain(maxDrain, doDrain); } } diff --git a/src/main/java/gregtech/api/util/VirtualTankRegistry.java b/src/main/java/gregtech/api/util/VirtualTankRegistry.java deleted file mode 100644 index f43809d7eb3..00000000000 --- a/src/main/java/gregtech/api/util/VirtualTankRegistry.java +++ /dev/null @@ -1,338 +0,0 @@ -package gregtech.api.util; - -import gregtech.api.GTValues; - -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.world.World; -import net.minecraft.world.storage.MapStorage; -import net.minecraft.world.storage.WorldSavedData; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.FluidTankInfo; -import net.minecraftforge.fluids.IFluidTank; -import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.fluids.capability.IFluidTankProperties; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class VirtualTankRegistry extends WorldSavedData { - - private static final int DEFAULT_CAPACITY = 64000; // 64B - private static final String DATA_ID = GTValues.MODID + ".vtank_data"; - - protected static Map> tankMap = new HashMap<>(); - - public VirtualTankRegistry() { - super(DATA_ID); - } - - // for some reason, MapStorage throws an error if this constructor is not present - @SuppressWarnings("unused") - public VirtualTankRegistry(String name) { - super(name); - } - - /** - * Retrieves a tank from the registry - * - * @param key The name of the tank - * @param uuid The uuid of the player the tank is private to, or null if the tank is public - * @return The tank object - */ - public static IFluidTank getTank(String key, UUID uuid) { - return tankMap.get(uuid).get(key); - } - - /** - * @return the internal Map of tanks. - * Do not use to modify the map! - */ - public static Map> getTankMap() { - return tankMap; - } - - /** - * Retrieves a tank from the registry, creating it if it does not exist - * - * @param key The name of the tank - * @param uuid The uuid of the player the tank is private to, or null if the tank is public - * @param capacity The initial capacity of the tank - * @return The tank object - */ - public static IFluidTank getTankCreate(String key, UUID uuid, int capacity) { - if (!tankMap.containsKey(uuid) || !tankMap.get(uuid).containsKey(key)) { - addTank(key, uuid, capacity); - } - return getTank(key, uuid); - } - - /** - * Retrieves a tank from the registry, creating it with {@link #DEFAULT_CAPACITY the default capacity} if it does - * not exist - * - * @param key The name of the tank - * @param uuid The uuid of the player the tank is private to, or null if the tank is public - * @return The tank object - */ - public static IFluidTank getTankCreate(String key, UUID uuid) { - return getTankCreate(key, uuid, DEFAULT_CAPACITY); - } - - /** - * Adds a tank to the registry - * - * @param key The name of the tank - * @param uuid The uuid of the player the tank is private to, or null if the tank is public - * @param capacity The initial capacity of the tank - */ - public static void addTank(String key, UUID uuid, int capacity) { - if (tankMap.containsKey(uuid) && tankMap.get(uuid).containsKey(key)) { - GTLog.logger.warn("Overwriting virtual tank " + key + "/" + (uuid == null ? "null" : uuid.toString()) + - ", this might cause fluid loss!"); - } else if (!tankMap.containsKey(uuid)) { - tankMap.put(uuid, new HashMap<>()); - } - tankMap.get(uuid).put(key, new VirtualTank(capacity)); - } - - /** - * Adds a tank to the registry with {@link #DEFAULT_CAPACITY the default capacity} - * - * @param key The name of the tank - * @param uuid The uuid of the player the tank is private to, or null if the tank is public - */ - public static void addTank(String key, UUID uuid) { - addTank(key, uuid, DEFAULT_CAPACITY); - } - - /** - * Removes a tank from the registry. Use with caution! - * - * @param key The name of the tank - * @param uuid The uuid of the player the tank is private to, or null if the tank is public - * @param removeFluid Whether to remove the tank if it has fluid in it - */ - public static void delTank(String key, UUID uuid, boolean removeFluid) { - if (tankMap.containsKey(uuid) && tankMap.get(uuid).containsKey(key)) { - if (removeFluid || tankMap.get(uuid).get(key).getFluidAmount() <= 0) { - tankMap.get(uuid).remove(key); - if (tankMap.get(uuid).size() == 0) { - tankMap.remove(uuid); - } - } - } else { - GTLog.logger.warn("Attempted to delete tank " + key + "/" + (uuid == null ? "null" : uuid.toString()) + - ", which does not exist!"); - } - } - - /** - * To be called on server stopped event - */ - public static void clearMaps() { - tankMap.clear(); - } - - @Override - public void readFromNBT(NBTTagCompound nbt) { - if (nbt.hasKey("Public")) { - NBTTagCompound publicTanks = nbt.getCompoundTag("Public"); - for (String key : publicTanks.getKeySet()) { - NBTTagCompound tankCompound = publicTanks.getCompoundTag(key); - VirtualTankRegistry.addTank(key, null, tankCompound.getInteger("Capacity")); - if (!tankCompound.hasKey("Empty")) { - VirtualTankRegistry.getTank(key, null).fill(FluidStack.loadFluidStackFromNBT(tankCompound), true); - } - } - } - if (nbt.hasKey("Private")) { - NBTTagCompound privateTankUUIDs = nbt.getCompoundTag("Private"); - for (String uuidStr : privateTankUUIDs.getKeySet()) { - UUID uuid = UUID.fromString(uuidStr); - NBTTagCompound privateTanks = privateTankUUIDs.getCompoundTag(uuidStr); - for (String key : privateTanks.getKeySet()) { - NBTTagCompound tankCompound = privateTanks.getCompoundTag(key); - VirtualTankRegistry.addTank(key, uuid, tankCompound.getInteger("Capacity")); - if (!tankCompound.hasKey("Empty")) { - VirtualTankRegistry.getTank(key, uuid).fill(FluidStack.loadFluidStackFromNBT(tankCompound), - true); - } - } - } - } - } - - @NotNull - @Override - public NBTTagCompound writeToNBT(NBTTagCompound compound) { - compound.setTag("Private", new NBTTagCompound()); - tankMap.forEach((uuid, map) -> { - NBTTagCompound mapCompound = new NBTTagCompound(); - map.forEach((key, tank) -> { - if (tank.getFluid() != null || tank.getCapacity() != DEFAULT_CAPACITY) { - NBTTagCompound tankCompound = new NBTTagCompound(); - tankCompound.setInteger("Capacity", tank.getCapacity()); - if (tank.getFluid() != null) { - tank.getFluid().writeToNBT(tankCompound); - } else { - tankCompound.setString("Empty", ""); - } - mapCompound.setTag(key, tankCompound); - } - }); - if (mapCompound.getSize() > 0) { - if (uuid == null) { - compound.setTag("Public", mapCompound); - } else { - compound.getCompoundTag("Private").setTag(uuid.toString(), mapCompound); - } - } - }); - return compound; - } - - @Override - public boolean isDirty() { - // can't think of a good way to mark dirty other than always - return true; - } - - /** - * To be called on world load event - */ - public static void initializeStorage(World world) { - MapStorage storage = world.getMapStorage(); - VirtualTankRegistry instance = (VirtualTankRegistry) storage.getOrLoadData(VirtualTankRegistry.class, DATA_ID); - - if (instance == null) { - instance = new VirtualTankRegistry(); - storage.setData(DATA_ID, instance); - } - } - - private static class VirtualTank implements IFluidTank, IFluidHandler { - - @Nullable - protected FluidStack fluid; - protected int capacity; - protected IFluidTankProperties[] tankProperties; - - public VirtualTank(int capacity) { - this.capacity = capacity; - } - - @Nullable - @Override - public FluidStack getFluid() { - return this.fluid; - } - - @Override - public int getFluidAmount() { - return this.fluid == null ? 0 : this.fluid.amount; - } - - @Override - public int getCapacity() { - return this.capacity; - } - - @Override - public FluidTankInfo getInfo() { - return new FluidTankInfo(this); - } - - @Override - public IFluidTankProperties[] getTankProperties() { - if (this.tankProperties == null) { - this.tankProperties = new IFluidTankProperties[] { new VirtualTankProperties(this) }; - } - return this.tankProperties; - } - - @Override - public int fill(FluidStack fluidStack, boolean doFill) { - if (fluidStack == null || fluidStack.amount <= 0 || - (this.fluid != null && !fluidStack.isFluidEqual(this.fluid))) - return 0; - - int fillAmt = Math.min(fluidStack.amount, this.capacity - this.getFluidAmount()); - if (doFill) { - if (this.fluid == null) { - this.fluid = new FluidStack(fluidStack, fillAmt); - } else { - this.fluid.amount += fillAmt; - } - } - return fillAmt; - } - - @Nullable - @Override - public FluidStack drain(FluidStack resource, boolean doDrain) { - return resource == null || !resource.isFluidEqual(this.fluid) ? null : drain(resource.amount, doDrain); - } - - @Nullable - @Override - public FluidStack drain(int amount, boolean doDrain) { - if (this.fluid == null || amount <= 0) - return null; - - int drainAmt = Math.min(this.getFluidAmount(), amount); - FluidStack drainedFluid = new FluidStack(fluid, drainAmt); - if (doDrain) { - this.fluid.amount -= drainAmt; - if (this.fluid.amount <= 0) { - this.fluid = null; - } - } - return drainedFluid; - } - - private static class VirtualTankProperties implements IFluidTankProperties { - - protected final VirtualTank tank; - - private VirtualTankProperties(VirtualTank tank) { - this.tank = tank; - } - - @Nullable - @Override - public FluidStack getContents() { - FluidStack contents = tank.getFluid(); - return contents == null ? null : contents.copy(); - } - - @Override - public int getCapacity() { - return tank.getCapacity(); - } - - @Override - public boolean canFill() { - return true; - } - - @Override - public boolean canDrain() { - return true; - } - - @Override - public boolean canFillFluidType(FluidStack fluidStack) { - return true; - } - - @Override - public boolean canDrainFluidType(FluidStack fluidStack) { - return true; - } - } - } -} diff --git a/src/main/java/gregtech/api/util/virtualregistry/EntryTypes.java b/src/main/java/gregtech/api/util/virtualregistry/EntryTypes.java new file mode 100644 index 00000000000..6324958b8c7 --- /dev/null +++ b/src/main/java/gregtech/api/util/virtualregistry/EntryTypes.java @@ -0,0 +1,66 @@ +package gregtech.api.util.virtualregistry; + +import gregtech.api.util.GTLog; +import gregtech.api.util.virtualregistry.entries.VirtualTank; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ResourceLocation; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.function.Supplier; + +import static gregtech.api.util.GTUtility.gregtechId; + +public final class EntryTypes { + + private static final Map> TYPES_MAP = new Object2ObjectOpenHashMap<>(); + public static final EntryTypes ENDER_FLUID = addEntryType(gregtechId("ender_fluid"), VirtualTank::new); + // ENDER_ITEM("ender_item", null), + // ENDER_ENERGY("ender_energy", null), + // ENDER_REDSTONE("ender_redstone", null); + private final ResourceLocation location; + private final Supplier factory; + + private EntryTypes(ResourceLocation location, Supplier supplier) { + this.location = location; + this.factory = supplier; + } + + public T createInstance(NBTTagCompound nbt) { + var entry = createInstance(); + entry.deserializeNBT(nbt); + return entry; + } + + public T createInstance() { + return factory.get(); + } + + @Override + public String toString() { + return this.location.toString(); + } + + @Nullable + public static EntryTypes fromString(String name) { + return TYPES_MAP.getOrDefault(gregtechId(name), null); + } + + @Nullable + public static EntryTypes fromLocation(String location) { + return TYPES_MAP.getOrDefault(new ResourceLocation(location), null); + } + + public static EntryTypes addEntryType(ResourceLocation location, Supplier supplier) { + var type = new EntryTypes<>(location, supplier); + if (!TYPES_MAP.containsKey(location)) { + TYPES_MAP.put(location, type); + } else { + GTLog.logger.warn("Entry \"{}\" is already registered!", location); + } + return type; + } +} diff --git a/src/main/java/gregtech/api/util/virtualregistry/VirtualEnderRegistry.java b/src/main/java/gregtech/api/util/virtualregistry/VirtualEnderRegistry.java new file mode 100644 index 00000000000..3d8ccd78224 --- /dev/null +++ b/src/main/java/gregtech/api/util/virtualregistry/VirtualEnderRegistry.java @@ -0,0 +1,175 @@ +package gregtech.api.util.virtualregistry; + +import gregtech.api.GTValues; +import gregtech.api.util.GTLog; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraft.world.storage.MapStorage; +import net.minecraft.world.storage.WorldSavedData; +import net.minecraftforge.fluids.IFluidTank; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Predicate; + +@SuppressWarnings("SameParameterValue") +public class VirtualEnderRegistry extends WorldSavedData { + + private static final String DATA_ID = GTValues.MODID + ".virtual_entry_data"; + private static final String OLD_DATA_ID = GTValues.MODID + ".vtank_data"; + private static final String PUBLIC_KEY = "Public"; + private static final String PRIVATE_KEY = "Private"; + private static final Map VIRTUAL_REGISTRIES = new HashMap<>(); + + public VirtualEnderRegistry(String name) { + super(name); + } + + public static T getEntry(@Nullable UUID owner, EntryTypes type, String name) { + return getRegistry(owner).getEntry(type, name); + } + + public static void addEntry(@Nullable UUID owner, String name, VirtualEntry entry) { + getRegistry(owner).addEntry(name, entry); + } + + public static boolean hasEntry(@Nullable UUID owner, EntryTypes type, String name) { + return getRegistry(owner).contains(type, name); + } + + public static @NotNull T getOrCreateEntry(@Nullable UUID owner, EntryTypes type, + String name) { + if (!hasEntry(owner, type, name)) + addEntry(owner, name, type.createInstance()); + + return getEntry(owner, type, name); + } + + /** + * Removes an entry from the registry. Use with caution! + * + * @param owner The uuid of the player the entry is private to, or null if the entry is public + * @param type Type of the registry to remove from + * @param name The name of the entry + */ + public static void deleteEntry(@Nullable UUID owner, EntryTypes type, String name) { + var registry = getRegistry(owner); + if (registry.contains(type, name)) { + registry.deleteEntry(type, name); + return; + } + GTLog.logger.warn("Attempted to delete {} entry {} of type {}, which does not exist", + owner == null ? "public" : String.format("private [%s]", owner), name, type); + } + + public static void deleteEntry(@Nullable UUID owner, EntryTypes type, String name, + Predicate shouldDelete) { + T entry = getEntry(owner, type, name); + if (entry != null && shouldDelete.test(entry)) + deleteEntry(owner, type, name); + } + + public static Set getEntryNames(UUID owner, EntryTypes type) { + return getRegistry(owner).getEntryNames(type); + } + + /** + * To be called on server stopped event + */ + public static void clearMaps() { + VIRTUAL_REGISTRIES.clear(); + } + + private static VirtualRegistryMap getRegistry(UUID owner) { + return VIRTUAL_REGISTRIES.computeIfAbsent(owner, key -> new VirtualRegistryMap()); + } + + // remove if tank app is removed + public static Map> createTankMap() { + Map> map = new HashMap<>(); + for (var uuid : VIRTUAL_REGISTRIES.keySet()) { + map.put(uuid, new HashMap<>()); + for (var name : getEntryNames(uuid, EntryTypes.ENDER_FLUID)) { + map.get(uuid).put(name, getEntry(uuid, EntryTypes.ENDER_FLUID, name)); + } + } + return map; + } + + @Override + public final void readFromNBT(NBTTagCompound nbt) { + if (nbt.hasKey(PUBLIC_KEY)) { + VIRTUAL_REGISTRIES.put(null, new VirtualRegistryMap(nbt.getCompoundTag(PUBLIC_KEY))); + } + if (nbt.hasKey(PRIVATE_KEY)) { + NBTTagCompound privateEntries = nbt.getCompoundTag(PRIVATE_KEY); + for (String owner : privateEntries.getKeySet()) { + var privateMap = privateEntries.getCompoundTag(owner); + VIRTUAL_REGISTRIES.put(UUID.fromString(owner), new VirtualRegistryMap(privateMap)); + } + } + } + + @NotNull + @Override + public final NBTTagCompound writeToNBT(@NotNull NBTTagCompound tag) { + var privateTag = new NBTTagCompound(); + for (var owner : VIRTUAL_REGISTRIES.keySet()) { + var mapTag = VIRTUAL_REGISTRIES.get(owner).serializeNBT(); + if (owner != null) { + privateTag.setTag(owner.toString(), mapTag); + } else { + tag.setTag(PUBLIC_KEY, mapTag); + } + } + tag.setTag(PRIVATE_KEY, privateTag); + return tag; + } + + @Override + public boolean isDirty() { + // can't think of a good way to mark dirty other than always + return true; + } + + /** + * To be called on world load event + */ + @SuppressWarnings("DataFlowIssue") + public static void initializeStorage(World world) { + MapStorage storage = world.getMapStorage(); + + VirtualEnderRegistry instance = (VirtualEnderRegistry) storage.getOrLoadData(VirtualEnderRegistry.class, + DATA_ID); + VirtualEnderRegistry old = (VirtualEnderRegistry) storage.getOrLoadData(VirtualEnderRegistry.class, + OLD_DATA_ID); + + if (instance == null) { + instance = new VirtualEnderRegistry(DATA_ID); + storage.setData(DATA_ID, instance); + } + + if (old != null) { + instance.readFromNBT(old.serializeNBT()); + var file = world.getSaveHandler().getMapFileFromName(OLD_DATA_ID); + var split = file.getName().split("\\."); + var stringBuilder = new StringBuilder(split[0]) + .append('.') + .append(split[1]) + .append(".backup") + .append('.') + .append(split[2]); + if (file.renameTo(new File(file.getParent(), stringBuilder.toString()))) { + file.deleteOnExit(); + GTLog.logger.warn("Moved Virtual Tank Data to new format, created backup!"); + } + } + } +} diff --git a/src/main/java/gregtech/api/util/virtualregistry/VirtualEntry.java b/src/main/java/gregtech/api/util/virtualregistry/VirtualEntry.java new file mode 100644 index 00000000000..2775069f0e3 --- /dev/null +++ b/src/main/java/gregtech/api/util/virtualregistry/VirtualEntry.java @@ -0,0 +1,79 @@ +package gregtech.api.util.virtualregistry; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.INBTSerializable; + +import org.jetbrains.annotations.NotNull; + +public abstract class VirtualEntry implements INBTSerializable { + + public static final String DEFAULT_COLOR = "FFFFFFFF"; + protected static final String COLOR_KEY = "color"; + protected static final String DESC_KEY = "description"; + + private int color = 0xFFFFFFFF; + private String colorStr = DEFAULT_COLOR; + private @NotNull String description = ""; + + public abstract EntryTypes getType(); + + public String getColorStr() { + return colorStr; + } + + public int getColor() { + return this.color; + } + + public void setColor(String color) { + this.color = parseColor(color); + this.colorStr = color.toUpperCase(); + } + + public void setColor(int color) { + setColor(Integer.toHexString(color)); + } + + private int parseColor(String s) { + // stupid java not having actual unsigned ints + long tmp = Long.parseLong(s, 16); + if (tmp > 0x7FFFFFFF) { + tmp -= 0x100000000L; + } + return (int) tmp; + } + + public @NotNull String getDescription() { + return this.description; + } + + public void setDescription(@NotNull String desc) { + this.description = desc; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VirtualEntry other)) return false; + return this.getType() == other.getType() && + this.color == other.color; + } + + @Override + public NBTTagCompound serializeNBT() { + var tag = new NBTTagCompound(); + tag.setString(COLOR_KEY, this.colorStr); + + if (description != null && !description.isEmpty()) + tag.setString(DESC_KEY, this.description); + + return tag; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + setColor(nbt.getString(COLOR_KEY)); + + if (nbt.hasKey(DESC_KEY)) + setDescription(nbt.getString(DESC_KEY)); + } +} diff --git a/src/main/java/gregtech/api/util/virtualregistry/VirtualRegistryMap.java b/src/main/java/gregtech/api/util/virtualregistry/VirtualRegistryMap.java new file mode 100644 index 00000000000..18a2b9a81e8 --- /dev/null +++ b/src/main/java/gregtech/api/util/virtualregistry/VirtualRegistryMap.java @@ -0,0 +1,87 @@ +package gregtech.api.util.virtualregistry; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.INBTSerializable; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class VirtualRegistryMap implements INBTSerializable { + + private final Map, Map> registryMap = new HashMap<>(); + + public VirtualRegistryMap(NBTTagCompound tag) { + deserializeNBT(tag); + } + + public VirtualRegistryMap() {} + + @SuppressWarnings("unchecked") + public @Nullable T getEntry(EntryTypes type, String name) { + if (!contains(type, name)) + return null; + + return (T) registryMap.get(type).get(name); + } + + public void addEntry(String name, VirtualEntry entry) { + registryMap.computeIfAbsent(entry.getType(), k -> new HashMap<>()) + .put(name, entry); + } + + public boolean contains(EntryTypes type, String name) { + if (!registryMap.containsKey(type)) + return false; + + return registryMap.get(type).containsKey(name); + } + + public void deleteEntry(EntryTypes type, String name) { + registryMap.get(type).remove(name); + } + + public void clear() { + registryMap.clear(); + } + + public Set getEntryNames(EntryTypes type) { + return registryMap.get(type).keySet(); + } + + @Override + public @NotNull NBTTagCompound serializeNBT() { + var tag = new NBTTagCompound(); + for (var type : registryMap.keySet()) { + var entriesTag = new NBTTagCompound(); + var entries = registryMap.get(type); + for (var name : entries.keySet()) { + entriesTag.setTag(name, entries.get(name).serializeNBT()); + } + tag.setTag(type.toString(), entriesTag); + } + return tag; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + for (var entryType : nbt.getKeySet()) { + EntryTypes type; + if (entryType.contains(":")) { + type = EntryTypes.fromLocation(entryType); + } else { + type = EntryTypes.fromString(entryType); + } + if (type == null) continue; + + var virtualEntries = nbt.getCompoundTag(entryType); + for (var name : virtualEntries.getKeySet()) { + var entry = virtualEntries.getCompoundTag(name); + addEntry(name, type.createInstance(entry)); + } + } + } +} diff --git a/src/main/java/gregtech/api/util/virtualregistry/entries/VirtualTank.java b/src/main/java/gregtech/api/util/virtualregistry/entries/VirtualTank.java new file mode 100644 index 00000000000..3ad02e5969c --- /dev/null +++ b/src/main/java/gregtech/api/util/virtualregistry/entries/VirtualTank.java @@ -0,0 +1,180 @@ +package gregtech.api.util.virtualregistry.entries; + +import gregtech.api.util.virtualregistry.EntryTypes; +import gregtech.api.util.virtualregistry.VirtualEntry; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; + +import org.jetbrains.annotations.Nullable; + +public class VirtualTank extends VirtualEntry implements IFluidTank, IFluidHandler { + + protected static final String CAPACITY_KEY = "capacity"; + protected static final String FLUID_KEY = "fluid"; + private static final int DEFAULT_CAPACITY = 64000; // 64B + + @Nullable + private FluidStack fluidStack = null; + private int capacity; + private final IFluidTankProperties[] props = new IFluidTankProperties[] { + createProperty(this) + }; + + public VirtualTank(int capacity) { + this.capacity = capacity; + } + + public VirtualTank() { + this(DEFAULT_CAPACITY); + } + + @Override + public EntryTypes getType() { + return EntryTypes.ENDER_FLUID; + } + + @Override + public FluidStack getFluid() { + return this.fluidStack; + } + + public void setFluid(FluidStack fluid) { + this.fluidStack = fluid; + } + + @Override + public int getFluidAmount() { + return fluidStack == null ? 0 : fluidStack.amount; + } + + @Override + public int getCapacity() { + return this.capacity; + } + + @Override + public FluidTankInfo getInfo() { + return new FluidTankInfo(this); + } + + @Override + public IFluidTankProperties[] getTankProperties() { + return this.props; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VirtualTank other)) return false; + if (this.fluidStack == null && other.fluidStack == null) + return super.equals(o); + if (this.fluidStack == null || other.fluidStack == null) + return false; + if (this.fluidStack.isFluidStackIdentical(other.fluidStack)) + return super.equals(o); + + return false; + } + + @Override + public NBTTagCompound serializeNBT() { + var tag = super.serializeNBT(); + tag.setInteger(CAPACITY_KEY, this.capacity); + + if (this.fluidStack != null) + tag.setTag(FLUID_KEY, this.fluidStack.writeToNBT(new NBTTagCompound())); + + return tag; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + super.deserializeNBT(nbt); + this.capacity = nbt.getInteger(CAPACITY_KEY); + + if (nbt.hasKey(FLUID_KEY)) + setFluid(FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag(FLUID_KEY))); + } + + @Override + public int fill(FluidStack fluidStack, boolean doFill) { + if (fluidStack == null || fluidStack.amount <= 0 || + (this.fluidStack != null && !fluidStack.isFluidEqual(this.fluidStack))) + return 0; + + int fillAmt = Math.min(fluidStack.amount, getCapacity() - this.getFluidAmount()); + + if (doFill) { + if (this.fluidStack == null) { + this.fluidStack = new FluidStack(fluidStack, fillAmt); + } else { + this.fluidStack.amount += fillAmt; + } + } + return fillAmt; + } + + @Nullable + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) { + return resource == null || !resource.isFluidEqual(this.fluidStack) ? null : drain(resource.amount, doDrain); + } + + @Nullable + @Override + public FluidStack drain(int amount, boolean doDrain) { + if (this.fluidStack == null || amount <= 0) + return null; + + int drainAmt = Math.min(this.getFluidAmount(), amount); + FluidStack drainedFluid = new FluidStack(this.fluidStack, drainAmt); + if (doDrain) { + this.fluidStack.amount -= drainAmt; + if (this.fluidStack.amount <= 0) { + this.fluidStack = null; + } + } + return drainedFluid; + } + + private static IFluidTankProperties createProperty(VirtualTank tank) { + return new IFluidTankProperties() { + + @Nullable + @Override + public FluidStack getContents() { + FluidStack contents = tank.getFluid(); + return contents == null ? null : contents.copy(); + } + + @Override + public int getCapacity() { + return tank.getCapacity(); + } + + @Override + public boolean canFill() { + return true; + } + + @Override + public boolean canDrain() { + return true; + } + + @Override + public boolean canFillFluidType(FluidStack fluidStack) { + return true; + } + + @Override + public boolean canDrainFluidType(FluidStack fluidStack) { + return true; + } + }; + } +} diff --git a/src/main/java/gregtech/common/CommonProxy.java b/src/main/java/gregtech/common/CommonProxy.java index 6817aaa7ce5..1a43a28aa62 100644 --- a/src/main/java/gregtech/common/CommonProxy.java +++ b/src/main/java/gregtech/common/CommonProxy.java @@ -10,7 +10,7 @@ import gregtech.api.recipes.GTRecipeInputCache; import gregtech.api.recipes.ModHandler; import gregtech.api.recipes.ingredients.GTRecipeOreInput; -import gregtech.api.recipes.recipeproperties.FusionEUToStartProperty; +import gregtech.api.recipes.properties.impl.FusionEUToStartProperty; import gregtech.api.terminal.TerminalRegistry; import gregtech.api.unification.material.Material; import gregtech.api.unification.material.info.MaterialFlags; diff --git a/src/main/java/gregtech/common/ConfigHolder.java b/src/main/java/gregtech/common/ConfigHolder.java index caf04defc48..7127a2ab688 100644 --- a/src/main/java/gregtech/common/ConfigHolder.java +++ b/src/main/java/gregtech/common/ConfigHolder.java @@ -35,7 +35,6 @@ public class ConfigHolder { // TODO move to ToolsModule config @Config.Comment("Config options for Tools and Armor") @Config.Name("Tool and Armor Options") - @Config.RequiresMcRestart public static ToolOptions tools = new ToolOptions(); @Config.Comment("Config options for World Generation features") @@ -641,34 +640,42 @@ public static class HeatEffectBloom { public static class ToolOptions { + @Config.RequiresMcRestart @Config.Name("NanoSaber Options") public NanoSaber nanoSaber = new NanoSaber(); + @Config.RequiresMcRestart @Config.Comment("NightVision Goggles Voltage Tier. Default: 1 (LV)") @Config.RangeInt(min = 0, max = 14) public int voltageTierNightVision = 1; + @Config.RequiresMcRestart @Config.Comment("NanoSuit Voltage Tier. Default: 3 (HV)") @Config.RangeInt(min = 0, max = 14) public int voltageTierNanoSuit = 3; + @Config.RequiresMcRestart @Config.Comment({ "Advanced NanoSuit Chestplate Voltage Tier.", "Default: 3 (HV)" }) @Config.RangeInt(min = 0, max = 14) public int voltageTierAdvNanoSuit = 3; + @Config.RequiresMcRestart @Config.Comment({ "QuarkTech Suit Voltage Tier.", "Default: 5 (IV)" }) @Config.RangeInt(min = 0, max = 14) @Config.SlidingOption public int voltageTierQuarkTech = 5; + @Config.RequiresMcRestart @Config.Comment({ "Advanced QuarkTech Suit Chestplate Voltage Tier.", "Default: 5 (LuV)" }) @Config.RangeInt(min = 0, max = 14) public int voltageTierAdvQuarkTech = 6; + @Config.RequiresMcRestart @Config.Comment({ "Electric Impeller Jetpack Voltage Tier.", "Default: 2 (MV)" }) @Config.RangeInt(min = 0, max = 14) public int voltageTierImpeller = 2; + @Config.RequiresMcRestart @Config.Comment({ "Advanced Electric Jetpack Voltage Tier.", "Default: 3 (HV)" }) @Config.RangeInt(min = 0, max = 14) public int voltageTierAdvImpeller = 3; @@ -680,6 +687,11 @@ public static class ToolOptions { @Config.Comment("Armor HUD Location") public ArmorHud armorHud = new ArmorHud(); + + @Config.Comment({ "How often items should be moved by a magnet", "Default: 10 ticks" }) + @Config.RangeInt(min = 1, max = 100) + @Config.SlidingOption + public int magnetDelay = 10; } public static class ArmorHud { diff --git a/src/main/java/gregtech/common/EventHandlers.java b/src/main/java/gregtech/common/EventHandlers.java index 8fa23091b99..ecbe6b16097 100644 --- a/src/main/java/gregtech/common/EventHandlers.java +++ b/src/main/java/gregtech/common/EventHandlers.java @@ -13,7 +13,7 @@ import gregtech.api.util.CapesRegistry; import gregtech.api.util.GTUtility; import gregtech.api.util.Mods; -import gregtech.api.util.VirtualTankRegistry; +import gregtech.api.util.virtualregistry.VirtualEnderRegistry; import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinSaveData; import gregtech.common.entities.EntityGTExplosive; import gregtech.common.items.MetaItems; @@ -327,7 +327,7 @@ public static void onPlayerTickClient(TickEvent.PlayerTickEvent event) { @SubscribeEvent public static void onWorldLoadEvent(WorldEvent.Load event) { - VirtualTankRegistry.initializeStorage(event.getWorld()); + VirtualEnderRegistry.initializeStorage(event.getWorld()); CapesRegistry.checkAdvancements(event.getWorld()); } diff --git a/src/main/java/gregtech/common/covers/CoverBehaviors.java b/src/main/java/gregtech/common/covers/CoverBehaviors.java index 78413327163..355fe3b0dff 100644 --- a/src/main/java/gregtech/common/covers/CoverBehaviors.java +++ b/src/main/java/gregtech/common/covers/CoverBehaviors.java @@ -8,6 +8,7 @@ import gregtech.api.util.GTLog; import gregtech.client.renderer.texture.Textures; import gregtech.common.covers.detector.*; +import gregtech.common.covers.ender.CoverEnderFluidLink; import gregtech.common.items.MetaItems; import gregtech.common.items.behaviors.CoverDigitalInterfaceWirelessPlaceBehaviour; diff --git a/src/main/java/gregtech/common/covers/CoverEnderFluidLink.java b/src/main/java/gregtech/common/covers/CoverEnderFluidLink.java deleted file mode 100644 index a674b6fbd54..00000000000 --- a/src/main/java/gregtech/common/covers/CoverEnderFluidLink.java +++ /dev/null @@ -1,348 +0,0 @@ -package gregtech.common.covers; - -import gregtech.api.capability.GregtechTileCapabilities; -import gregtech.api.capability.IControllable; -import gregtech.api.cover.CoverBase; -import gregtech.api.cover.CoverDefinition; -import gregtech.api.cover.CoverWithUI; -import gregtech.api.cover.CoverableView; -import gregtech.api.mui.GTGuiTextures; -import gregtech.api.mui.GTGuis; -import gregtech.api.util.FluidTankSwitchShim; -import gregtech.api.util.GTTransferUtils; -import gregtech.api.util.VirtualTankRegistry; -import gregtech.client.renderer.texture.Textures; -import gregtech.common.covers.filter.FluidFilterContainer; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ITickable; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler; - -import codechicken.lib.raytracer.CuboidRayTraceResult; -import codechicken.lib.render.CCRenderState; -import codechicken.lib.render.pipeline.IVertexOperation; -import codechicken.lib.vec.Cuboid6; -import codechicken.lib.vec.Matrix4; -import com.cleanroommc.modularui.api.drawable.IKey; -import com.cleanroommc.modularui.drawable.DynamicDrawable; -import com.cleanroommc.modularui.drawable.Rectangle; -import com.cleanroommc.modularui.factory.SidedPosGuiData; -import com.cleanroommc.modularui.screen.ModularPanel; -import com.cleanroommc.modularui.utils.Color; -import com.cleanroommc.modularui.value.sync.BooleanSyncValue; -import com.cleanroommc.modularui.value.sync.EnumSyncValue; -import com.cleanroommc.modularui.value.sync.FluidSlotSyncHandler; -import com.cleanroommc.modularui.value.sync.PanelSyncManager; -import com.cleanroommc.modularui.value.sync.StringSyncValue; -import com.cleanroommc.modularui.widgets.FluidSlot; -import com.cleanroommc.modularui.widgets.ToggleButton; -import com.cleanroommc.modularui.widgets.layout.Column; -import com.cleanroommc.modularui.widgets.layout.Row; -import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.UUID; -import java.util.regex.Pattern; - -public class CoverEnderFluidLink extends CoverBase implements CoverWithUI, ITickable, IControllable { - - public static final int TRANSFER_RATE = 8000; // mB/t - private static final Pattern COLOR_INPUT_PATTERN = Pattern.compile("[0-9a-fA-F]*"); - - protected CoverPump.PumpMode pumpMode = CoverPump.PumpMode.IMPORT; - private int color = 0xFFFFFFFF; - private UUID playerUUID = null; - private boolean isPrivate = false; - private boolean workingEnabled = true; - private boolean ioEnabled = false; - private String tempColorStr; - private boolean isColorTemp; - private final FluidTankSwitchShim linkedTank; - protected final FluidFilterContainer fluidFilter; - - protected CoverEnderFluidLink(@NotNull CoverDefinition definition, @NotNull CoverableView coverableView, - @NotNull EnumFacing attachedSide) { - super(definition, coverableView, attachedSide); - this.linkedTank = new FluidTankSwitchShim(VirtualTankRegistry.getTankCreate(makeTankName(), null)); - this.fluidFilter = new FluidFilterContainer(this); - } - - private String makeTankName() { - return "EFLink#" + Integer.toHexString(this.color).toUpperCase(); - } - - private UUID getTankUUID() { - return isPrivate ? playerUUID : null; - } - - public FluidFilterContainer getFluidFilterContainer() { - return this.fluidFilter; - } - - public boolean isIOEnabled() { - return this.ioEnabled; - } - - @Override - public boolean canAttach(@NotNull CoverableView coverable, @NotNull EnumFacing side) { - return coverable.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side); - } - - @Override - public void renderCover(@NotNull CCRenderState renderState, @NotNull Matrix4 translation, - IVertexOperation[] pipeline, @NotNull Cuboid6 plateBox, @NotNull BlockRenderLayer layer) { - Textures.ENDER_FLUID_LINK.renderSided(getAttachedSide(), plateBox, renderState, pipeline, translation); - } - - @Override - public @NotNull EnumActionResult onScrewdriverClick(@NotNull EntityPlayer playerIn, @NotNull EnumHand hand, - @NotNull CuboidRayTraceResult hitResult) { - if (!getWorld().isRemote) { - openUI((EntityPlayerMP) playerIn); - } - return EnumActionResult.SUCCESS; - } - - @Override - public void onAttachment(@NotNull CoverableView coverableView, @NotNull EnumFacing side, - @Nullable EntityPlayer player, @NotNull ItemStack itemStack) { - super.onAttachment(coverableView, side, player, itemStack); - if (player != null) { - this.playerUUID = player.getUniqueID(); - } - } - - @Override - public void onRemoval() { - dropInventoryContents(fluidFilter); - } - - @Override - public void update() { - if (workingEnabled && ioEnabled) { - transferFluids(); - } - } - - protected void transferFluids() { - IFluidHandler fluidHandler = getCoverableView().getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, - getAttachedSide()); - if (fluidHandler == null) return; - if (pumpMode == CoverPump.PumpMode.IMPORT) { - GTTransferUtils.transferFluids(fluidHandler, linkedTank, TRANSFER_RATE, fluidFilter::test); - } else if (pumpMode == CoverPump.PumpMode.EXPORT) { - GTTransferUtils.transferFluids(linkedTank, fluidHandler, TRANSFER_RATE, fluidFilter::test); - } - } - - public void setPumpMode(CoverPump.PumpMode pumpMode) { - this.pumpMode = pumpMode; - markDirty(); - } - - public CoverPump.PumpMode getPumpMode() { - return pumpMode; - } - - @Override - public void openUI(EntityPlayerMP player) { - CoverWithUI.super.openUI(player); - isColorTemp = false; - } - - @Override - public boolean usesMui2() { - return true; - } - - @Override - public ModularPanel buildUI(SidedPosGuiData guiData, PanelSyncManager guiSyncManager) { - var panel = GTGuis.createPanel(this, 176, 192); - - getFluidFilterContainer().setMaxTransferSize(1); - - return panel.child(CoverWithUI.createTitleRow(getPickItem())) - .child(createWidgets(panel, guiSyncManager)) - .bindPlayerInventory(); - } - - protected Column createWidgets(ModularPanel panel, PanelSyncManager syncManager) { - var isPrivate = new BooleanSyncValue(this::isPrivate, this::setPrivate); - isPrivate.updateCacheFromSource(true); - - var color = new StringSyncValue(this::getColorStr, this::updateColor); - color.updateCacheFromSource(true); - - var pumpMode = new EnumSyncValue<>(CoverPump.PumpMode.class, this::getPumpMode, this::setPumpMode); - syncManager.syncValue("pump_mode", pumpMode); - pumpMode.updateCacheFromSource(true); - - var ioEnabled = new BooleanSyncValue(this::isIOEnabled, this::setIoEnabled); - - var fluidTank = new FluidSlotSyncHandler(this.linkedTank); - fluidTank.updateCacheFromSource(true); - - return new Column().coverChildrenHeight().top(24) - .margin(7, 0).widthRel(1f) - .child(new Row().marginBottom(2) - .coverChildrenHeight() - .child(new ToggleButton() - .tooltip(tooltip -> tooltip.setAutoUpdate(true)) - .background(GTGuiTextures.PRIVATE_MODE_BUTTON[0]) - .hoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[0]) - .selectedBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1]) - .selectedHoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1]) - .tooltipBuilder(tooltip -> tooltip.addLine(IKey.lang(this.isPrivate ? - "cover.ender_fluid_link.private.tooltip.enabled" : - "cover.ender_fluid_link.private.tooltip.disabled"))) - .marginRight(2) - .value(isPrivate)) - .child(new DynamicDrawable(() -> new Rectangle() - .setColor(this.color) - .asIcon().size(16)) - .asWidget() - .background(GTGuiTextures.SLOT) - .size(18).marginRight(2)) - .child(new TextFieldWidget().height(18) - .value(color) - .setValidator(s -> { - if (s.length() != 8) { - return color.getStringValue(); - } - return s; - }) - .setPattern(COLOR_INPUT_PATTERN) - .widthRel(0.5f).marginRight(2)) - .child(new FluidSlot().size(18) - .syncHandler(fluidTank))) - .child(new Row().marginBottom(2) - .coverChildrenHeight() - .child(new ToggleButton() - .value(ioEnabled) - .overlay(IKey.dynamic(() -> IKey.lang(this.ioEnabled ? - "behaviour.soft_hammer.enabled" : - "behaviour.soft_hammer.disabled").get()) - .color(Color.WHITE.darker(1))) - .widthRel(0.6f) - .left(0))) - .child(getFluidFilterContainer().initUI(panel, syncManager)) - .child(new EnumRowBuilder<>(CoverPump.PumpMode.class) - .value(pumpMode) - .overlay(GTGuiTextures.CONVEYOR_MODE_OVERLAY) - .lang("cover.pump.mode") - .build()); - } - - public void updateColor(String str) { - if (str.length() == 8) { - isColorTemp = false; - // stupid java not having actual unsigned ints - long tmp = Long.parseLong(str, 16); - if (tmp > 0x7FFFFFFF) { - tmp -= 0x100000000L; - } - this.color = (int) tmp; - updateTankLink(); - } else { - tempColorStr = str; - isColorTemp = true; - } - } - - public String getColorStr() { - return isColorTemp ? tempColorStr : Integer.toHexString(this.color).toUpperCase(); - } - - public void updateTankLink() { - this.linkedTank.changeTank(VirtualTankRegistry.getTankCreate(makeTankName(), getTankUUID())); - markDirty(); - } - - @Override - public void writeToNBT(NBTTagCompound tagCompound) { - super.writeToNBT(tagCompound); - tagCompound.setInteger("Frequency", color); - tagCompound.setInteger("PumpMode", pumpMode.ordinal()); - tagCompound.setBoolean("WorkingAllowed", workingEnabled); - tagCompound.setBoolean("IOAllowed", ioEnabled); - tagCompound.setBoolean("Private", isPrivate); - tagCompound.setString("PlacedUUID", playerUUID.toString()); - tagCompound.setTag("Filter", fluidFilter.serializeNBT()); - } - - @Override - public void readFromNBT(NBTTagCompound tagCompound) { - super.readFromNBT(tagCompound); - this.color = tagCompound.getInteger("Frequency"); - this.pumpMode = CoverPump.PumpMode.values()[tagCompound.getInteger("PumpMode")]; - this.workingEnabled = tagCompound.getBoolean("WorkingAllowed"); - this.ioEnabled = tagCompound.getBoolean("IOAllowed"); - this.isPrivate = tagCompound.getBoolean("Private"); - this.playerUUID = UUID.fromString(tagCompound.getString("PlacedUUID")); - this.fluidFilter.deserializeNBT(tagCompound.getCompoundTag("Filter")); - updateTankLink(); - } - - @Override - public void writeInitialSyncData(PacketBuffer packetBuffer) { - packetBuffer.writeInt(this.color); - packetBuffer.writeString(this.playerUUID == null ? "null" : this.playerUUID.toString()); - } - - @Override - public void readInitialSyncData(PacketBuffer packetBuffer) { - this.color = packetBuffer.readInt(); - // does client even need uuid info? just in case - String uuidStr = packetBuffer.readString(36); - this.playerUUID = uuidStr.equals("null") ? null : UUID.fromString(uuidStr); - // client does not need the actual tank reference, the default one will do just fine - } - - @Override - public boolean isWorkingEnabled() { - return workingEnabled; - } - - @Override - public void setWorkingEnabled(boolean isActivationAllowed) { - this.workingEnabled = isActivationAllowed; - } - - public T getCapability(Capability capability, T defaultValue) { - if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { - return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(linkedTank); - } - if (capability == GregtechTileCapabilities.CAPABILITY_CONTROLLABLE) { - return GregtechTileCapabilities.CAPABILITY_CONTROLLABLE.cast(this); - } - return defaultValue; - } - - private boolean isIoEnabled() { - return ioEnabled; - } - - private void setIoEnabled(boolean ioEnabled) { - this.ioEnabled = ioEnabled; - } - - private boolean isPrivate() { - return isPrivate; - } - - private void setPrivate(boolean isPrivate) { - this.isPrivate = isPrivate; - updateTankLink(); - } -} diff --git a/src/main/java/gregtech/common/covers/ender/CoverAbstractEnderLink.java b/src/main/java/gregtech/common/covers/ender/CoverAbstractEnderLink.java new file mode 100644 index 00000000000..eb5c6a81d9d --- /dev/null +++ b/src/main/java/gregtech/common/covers/ender/CoverAbstractEnderLink.java @@ -0,0 +1,513 @@ +package gregtech.common.covers.ender; + +import gregtech.api.capability.GregtechDataCodes; +import gregtech.api.capability.IControllable; +import gregtech.api.cover.CoverBase; +import gregtech.api.cover.CoverDefinition; +import gregtech.api.cover.CoverWithUI; +import gregtech.api.cover.CoverableView; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuis; +import gregtech.api.util.virtualregistry.EntryTypes; +import gregtech.api.util.virtualregistry.VirtualEnderRegistry; +import gregtech.api.util.virtualregistry.VirtualEntry; +import gregtech.common.mui.widget.InteractableText; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ITickable; + +import codechicken.lib.raytracer.CuboidRayTraceResult; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.drawable.DynamicDrawable; +import com.cleanroommc.modularui.drawable.GuiTextures; +import com.cleanroommc.modularui.drawable.Rectangle; +import com.cleanroommc.modularui.factory.SidedPosGuiData; +import com.cleanroommc.modularui.network.NetworkUtils; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.value.sync.BooleanSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncHandler; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.StringSyncValue; +import com.cleanroommc.modularui.widgets.ButtonWidget; +import com.cleanroommc.modularui.widgets.ListWidget; +import com.cleanroommc.modularui.widgets.ToggleButton; +import com.cleanroommc.modularui.widgets.layout.Column; +import com.cleanroommc.modularui.widgets.layout.Row; +import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.regex.Pattern; + +@SuppressWarnings("SameParameterValue") +public abstract class CoverAbstractEnderLink extends CoverBase + implements CoverWithUI, ITickable, IControllable { + + protected static final Pattern COLOR_INPUT_PATTERN = Pattern.compile("[0-9a-fA-F]*"); + public static final int UPDATE_PRIVATE = GregtechDataCodes.assignId(); + + protected T activeEntry = null; + protected String color = VirtualEntry.DEFAULT_COLOR; + protected UUID playerUUID = null; + private boolean isPrivate = false; + private boolean workingEnabled = true; + private boolean ioEnabled = false; + + public CoverAbstractEnderLink(@NotNull CoverDefinition definition, @NotNull CoverableView coverableView, + @NotNull EnumFacing attachedSide) { + super(definition, coverableView, attachedSide); + updateLink(); + } + + protected void updateLink() { + this.activeEntry = VirtualEnderRegistry.getOrCreateEntry(getOwner(), getType(), createName()); + this.activeEntry.setColor(this.color); + markDirty(); + } + + protected abstract EntryTypes getType(); + + public String getColorStr() { + return this.color; + } + + protected final String createName() { + return identifier() + this.color; + } + + protected abstract String identifier(); + + protected final UUID getOwner() { + return isPrivate ? playerUUID : null; + } + + @Override + public void readCustomData(int discriminator, @NotNull PacketBuffer buf) { + super.readCustomData(discriminator, buf); + if (discriminator == UPDATE_PRIVATE) { + setPrivate(buf.readBoolean()); + updateLink(); + } + } + + @Override + public @NotNull EnumActionResult onScrewdriverClick(@NotNull EntityPlayer playerIn, @NotNull EnumHand hand, + @NotNull CuboidRayTraceResult hitResult) { + if (!getWorld().isRemote) { + openUI((EntityPlayerMP) playerIn); + } + return EnumActionResult.SUCCESS; + } + + @Override + public void onAttachment(@NotNull CoverableView coverableView, @NotNull EnumFacing side, + @Nullable EntityPlayer player, @NotNull ItemStack itemStack) { + super.onAttachment(coverableView, side, player, itemStack); + if (player != null) { + this.playerUUID = player.getUniqueID(); + } + } + + public void updateColor(String str) { + if (str.length() == 8) { + this.color = str.toUpperCase(); + updateLink(); + } + } + + @Override + public boolean usesMui2() { + return true; + } + + @Override + public ModularPanel buildUI(SidedPosGuiData guiData, PanelSyncManager guiSyncManager) { + var panel = GTGuis.createPanel(this, 176, 192); + + return panel.child(CoverWithUI.createTitleRow(getPickItem())) + .child(createWidgets(panel, guiSyncManager)) + .bindPlayerInventory(); + } + + protected Column createWidgets(ModularPanel panel, PanelSyncManager syncManager) { + var name = new StringSyncValue(this::getColorStr, this::updateColor); + + var entrySelectorSH = createEntrySelector(panel); + syncManager.syncValue("entry_selector", entrySelectorSH); + + return new Column().coverChildrenHeight().top(24) + .margin(7, 0).widthRel(1f) + .child(new Row().marginBottom(2) + .coverChildrenHeight() + .child(createPrivateButton()) + .child(createColorIcon()) + .child(new TextFieldWidget() + .height(18) + .value(name) + .setPattern(COLOR_INPUT_PATTERN) + .widthRel(0.5f) + .marginRight(2)) + .child(createEntrySlot()) + .child(new ButtonWidget<>() + .overlay(GTGuiTextures.MENU_OVERLAY) + .background(GTGuiTextures.MC_BUTTON) + .disableHoverBackground() + .addTooltipLine(IKey.lang("cover.generic.ender.open_selector")) + .onMousePressed(i -> { + if (entrySelectorSH.isPanelOpen()) { + entrySelectorSH.closePanel(); + } else { + entrySelectorSH.openPanel(); + } + Interactable.playButtonClickSound(); + return true; + }))) + .child(createIoRow()); + } + + protected abstract PanelSyncHandler createEntrySelector(ModularPanel panel); + + protected abstract IWidget createEntrySlot(); + + protected IWidget createColorIcon() { + // todo color selector popup panel + return new DynamicDrawable(() -> new Rectangle() + .setColor(this.activeEntry.getColor()) + .asIcon().size(16)) + .asWidget() + .background(GTGuiTextures.SLOT) + .size(18) + .marginRight(2); + } + + protected IWidget createPrivateButton() { + var isPrivate = new BooleanSyncValue(this::isPrivate, this::setPrivate); + isPrivate.updateCacheFromSource(true); + + return new ToggleButton() + .tooltip(tooltip -> tooltip.setAutoUpdate(true)) + .background(GTGuiTextures.PRIVATE_MODE_BUTTON[0]) + .hoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[0]) + .selectedBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1]) + .selectedHoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1]) + .tooltipBuilder(tooltip -> tooltip.addLine(IKey.lang(this.isPrivate ? + "cover.ender_fluid_link.private.tooltip.enabled" : + "cover.ender_fluid_link.private.tooltip.disabled"))) + .marginRight(2) + .value(isPrivate); + } + + protected IWidget createIoRow() { + var ioEnabled = new BooleanSyncValue(this::isIoEnabled, this::setIoEnabled); + + return new Row().marginBottom(2) + .coverChildrenHeight() + .child(new ToggleButton() + .value(ioEnabled) + .overlay(IKey.dynamic(() -> IKey.lang(this.ioEnabled ? + "behaviour.soft_hammer.enabled" : + "behaviour.soft_hammer.disabled").get()) + .color(Color.WHITE.darker(1))) + .widthRel(0.6f) + .left(0)); + } + + @Override + public boolean isWorkingEnabled() { + return workingEnabled; + } + + @Override + public void setWorkingEnabled(boolean isActivationAllowed) { + this.workingEnabled = isActivationAllowed; + } + + public boolean isIoEnabled() { + return ioEnabled; + } + + protected void setIoEnabled(boolean ioEnabled) { + this.ioEnabled = ioEnabled; + } + + private boolean isPrivate() { + return isPrivate; + } + + private void setPrivate(boolean isPrivate) { + this.isPrivate = isPrivate; + updateLink(); + writeCustomData(UPDATE_PRIVATE, buffer -> buffer.writeBoolean(this.isPrivate)); + } + + @Override + public void writeInitialSyncData(PacketBuffer packetBuffer) { + packetBuffer.writeString(this.playerUUID == null ? "null" : this.playerUUID.toString()); + } + + @Override + public void readInitialSyncData(PacketBuffer packetBuffer) { + // does client even need uuid info? just in case + String uuidStr = packetBuffer.readString(36); + this.playerUUID = uuidStr.equals("null") ? null : UUID.fromString(uuidStr); + } + + @Override + public void readFromNBT(@NotNull NBTTagCompound nbt) { + super.readFromNBT(nbt); + this.ioEnabled = nbt.getBoolean("IOAllowed"); + this.isPrivate = nbt.getBoolean("Private"); + this.workingEnabled = nbt.getBoolean("WorkingAllowed"); + this.playerUUID = UUID.fromString(nbt.getString("PlacedUUID")); + int color = nbt.getInteger("Frequency"); + this.color = Integer.toHexString(color).toUpperCase(); + updateLink(); + } + + @Override + public void writeToNBT(@NotNull NBTTagCompound nbt) { + super.writeToNBT(nbt); + nbt.setBoolean("IOAllowed", ioEnabled); + nbt.setBoolean("Private", isPrivate); + nbt.setBoolean("WorkingAllowed", workingEnabled); + nbt.setString("PlacedUUID", playerUUID.toString()); + nbt.setInteger("Frequency", activeEntry.getColor()); + } + + protected abstract class EntrySelectorSH extends PanelSyncHandler { + + private static final int TRACK_SUBPANELS = 3; + private static final int DELETE_ENTRY = 1; + private final EntryTypes type; + private final ModularPanel mainPanel; + private static final String PANEL_NAME = "entry_selector"; + private final Set opened = new HashSet<>(); + protected UUID playerUUID; + + protected EntrySelectorSH(ModularPanel mainPanel, EntryTypes type) { + super(mainPanel, EntrySelectorSH::defaultPanel); + this.type = type; + this.mainPanel = mainPanel; + } + + @Override + public void init(String key, PanelSyncManager syncManager) { + super.init(key, syncManager); + this.playerUUID = syncManager.getPlayer().getUniqueID(); + } + + private static ModularPanel defaultPanel(PanelSyncManager syncManager, PanelSyncHandler syncHandler) { + return GTGuis.createPopupPanel(PANEL_NAME, 168, 112); + } + + public UUID getPlayerUUID() { + return isPrivate ? playerUUID : null; + } + + @Override + public ModularPanel createUI(PanelSyncManager syncManager) { + List names = new ArrayList<>(VirtualEnderRegistry.getEntryNames(getPlayerUUID(), type)); + return super.createUI(syncManager) + .child(IKey.lang("cover.generic.ender.known_channels") + .color(UI_TITLE_COLOR).asWidget() + .top(6) + .left(4)) + .child(ListWidget.builder(names, name -> createRow(name, this.mainPanel, syncManager)) + .background(GTGuiTextures.DISPLAY.asIcon() + .width(168 - 8) + .height(112 - 20)) + .paddingTop(1) + .size(168 - 12, 112 - 24) + .left(4) + .bottom(6)); + } + + protected IWidget createRow(String name, ModularPanel mainPanel, PanelSyncManager syncManager) { + T entry = VirtualEnderRegistry.getEntry(getPlayerUUID(), this.type, name); + String key = String.format("entry#%s_description", entry.getColorStr()); + var entryDescriptionSH = new EntryDescriptionSH(mainPanel, key, entry); + syncManager.syncValue(key, isPrivate ? 1 : 0, entryDescriptionSH); + + return new Row() + .left(4) + .marginBottom(2) + .height(18) + .widthRel(0.98f) + .setEnabledIf(row -> VirtualEnderRegistry.hasEntry(getOwner(), this.type, name)) + .child(new Rectangle() + .setColor(entry.getColor()) + .asWidget() + .marginRight(4) + .size(16) + .background(GTGuiTextures.SLOT.asIcon().size(18)) + .top(1)) + .child(new InteractableText<>(entry, CoverAbstractEnderLink.this::updateColor) + .tooltip(tooltip -> tooltip.setAutoUpdate(true)) + .tooltipBuilder(tooltip -> { + String desc = entry.getDescription(); + if (!desc.isEmpty()) + tooltip.addLine(desc); + }) + .width(64) + .height(16) + .top(1) + .marginRight(4)) + .child(new ButtonWidget<>() + .overlay(GuiTextures.GEAR) + .addTooltipLine(IKey.lang("cover.generic.ender.set_description.tooltip")) + .onMousePressed(i -> { + // open entry settings + if (entryDescriptionSH.isPanelOpen()) { + entryDescriptionSH.closePanel(); + } else { + entryDescriptionSH.openPanel(); + } + Interactable.playButtonClickSound(); + return true; + })) + .child(createSlotWidget(entry)) + .child(new ButtonWidget<>() + .overlay(GTGuiTextures.BUTTON_CROSS) + .setEnabledIf(w -> !Objects.equals(entry.getColor(), activeEntry.getColor())) + .addTooltipLine(IKey.lang("cover.generic.ender.delete_entry")) + .onMousePressed(i -> { + // todo option to force delete, maybe as a popup? + deleteEntry(getPlayerUUID(), name); + syncToServer(1, buffer -> { + NetworkUtils.writeStringSafe(buffer, getPlayerUUID().toString()); + NetworkUtils.writeStringSafe(buffer, name); + }); + Interactable.playButtonClickSound(); + return true; + })); + } + + @Override + public void closePanel() { + var manager = getSyncManager().getModularSyncManager().getPanelSyncManager(PANEL_NAME); + for (var key : opened) { + if (manager.getSyncHandler(key) instanceof PanelSyncHandler psh) { + psh.closePanel(); + } + } + super.closePanel(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void closePanelInternal() { + var manager = getSyncManager().getModularSyncManager().getPanelSyncManager(PANEL_NAME); + for (var key : opened) { + if (manager.getSyncHandler(key) instanceof PanelSyncHandler psh) { + psh.closePanel(); + } + } + super.closePanelInternal(); + } + + @Override + public void readOnClient(int i, PacketBuffer packetBuffer) throws IOException { + if (i == TRACK_SUBPANELS) { + handleTracking(packetBuffer); + } + super.readOnClient(i, packetBuffer); + } + + @Override + public void readOnServer(int i, PacketBuffer packetBuffer) throws IOException { + if (i == TRACK_SUBPANELS) { + handleTracking(packetBuffer); + } + super.readOnServer(i, packetBuffer); + if (i == DELETE_ENTRY) { + UUID uuid = UUID.fromString(NetworkUtils.readStringSafe(packetBuffer)); + String name = NetworkUtils.readStringSafe(packetBuffer); + deleteEntry(uuid, name); + } + } + + private void handleTracking(PacketBuffer buffer) { + boolean add = buffer.readBoolean(); + String key = NetworkUtils.readStringSafe(buffer); + if (key != null) { + if (add) opened.add(key); + else opened.remove(key); + } + } + + private class EntryDescriptionSH extends PanelSyncHandler { + + /** + * Creates a PanelSyncHandler + * + * @param mainPanel the main panel of the current GUI + */ + public EntryDescriptionSH(ModularPanel mainPanel, String key, VirtualEntry entry) { + super(mainPanel, (syncManager, syncHandler) -> defaultPanel(syncHandler, key, entry)); + } + + private static ModularPanel defaultPanel(@NotNull PanelSyncHandler syncHandler, String key, + VirtualEntry entry) { + return GTGuis.createPopupPanel(key, 168, 36 + 6) + .child(IKey.lang("cover.generic.ender.set_description.title", entry.getColorStr()) + .color(UI_TITLE_COLOR) + .asWidget() + .left(4) + .top(6)) + .child(new TextFieldWidget() + .setTextColor(Color.WHITE.darker(1)) + .widthRel(0.95f) + .height(18) + .value(new StringSyncValue(entry::getDescription, string -> { + entry.setDescription(string); + if (syncHandler.isPanelOpen()) { + syncHandler.closePanel(); + } + })) + .alignX(0.5f) + .bottom(6)); + } + + @Override + public void openPanel() { + opened.add(getKey()); + EntrySelectorSH.this.sync(3, buffer -> { + buffer.writeBoolean(true); + NetworkUtils.writeStringSafe(buffer, getKey()); + }); + super.openPanel(); + } + + @Override + public void closePanel() { + opened.remove(getKey()); + EntrySelectorSH.this.sync(3, buffer -> { + buffer.writeBoolean(false); + NetworkUtils.writeStringSafe(buffer, getKey()); + }); + super.closePanel(); + } + } + + protected abstract IWidget createSlotWidget(T entry); + + protected abstract void deleteEntry(UUID player, String name); + } +} diff --git a/src/main/java/gregtech/common/covers/ender/CoverEnderFluidLink.java b/src/main/java/gregtech/common/covers/ender/CoverEnderFluidLink.java new file mode 100644 index 00000000000..27c338154b1 --- /dev/null +++ b/src/main/java/gregtech/common/covers/ender/CoverEnderFluidLink.java @@ -0,0 +1,193 @@ +package gregtech.common.covers.ender; + +import gregtech.api.capability.GregtechTileCapabilities; +import gregtech.api.capability.IControllable; +import gregtech.api.cover.CoverDefinition; +import gregtech.api.cover.CoverWithUI; +import gregtech.api.cover.CoverableView; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.util.FluidTankSwitchShim; +import gregtech.api.util.GTTransferUtils; +import gregtech.api.util.virtualregistry.EntryTypes; +import gregtech.api.util.virtualregistry.VirtualEnderRegistry; +import gregtech.api.util.virtualregistry.entries.VirtualTank; +import gregtech.client.renderer.texture.Textures; +import gregtech.common.covers.CoverPump; +import gregtech.common.covers.filter.FluidFilterContainer; +import gregtech.common.mui.widget.GTFluidSlot; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ITickable; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; + +import codechicken.lib.render.CCRenderState; +import codechicken.lib.render.pipeline.IVertexOperation; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.value.sync.EnumSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncHandler; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widgets.layout.Column; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class CoverEnderFluidLink extends CoverAbstractEnderLink + implements CoverWithUI, ITickable, IControllable { + + public static final int TRANSFER_RATE = 8000; // mB/t + + protected CoverPump.PumpMode pumpMode = CoverPump.PumpMode.IMPORT; + private final FluidTankSwitchShim linkedTank; + protected final FluidFilterContainer fluidFilter; + + public CoverEnderFluidLink(@NotNull CoverDefinition definition, @NotNull CoverableView coverableView, + @NotNull EnumFacing attachedSide) { + super(definition, coverableView, attachedSide); + this.linkedTank = new FluidTankSwitchShim(this.activeEntry); + this.fluidFilter = new FluidFilterContainer(this); + } + + @Override + protected void updateLink() { + super.updateLink(); + if (this.linkedTank != null) + this.linkedTank.changeTank(this.activeEntry); + } + + @Override + protected EntryTypes getType() { + return EntryTypes.ENDER_FLUID; + } + + @Override + protected String identifier() { + return "EFLink#"; + } + + public FluidFilterContainer getFluidFilterContainer() { + return this.fluidFilter; + } + + @Override + public boolean canAttach(@NotNull CoverableView coverable, @NotNull EnumFacing side) { + return coverable.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side); + } + + @Override + public void renderCover(@NotNull CCRenderState renderState, @NotNull Matrix4 translation, + IVertexOperation[] pipeline, @NotNull Cuboid6 plateBox, @NotNull BlockRenderLayer layer) { + Textures.ENDER_FLUID_LINK.renderSided(getAttachedSide(), plateBox, renderState, pipeline, translation); + } + + @Override + public void onRemoval() { + dropInventoryContents(fluidFilter); + } + + @Override + public void update() { + if (isWorkingEnabled() && isIoEnabled()) { + transferFluids(); + } + } + + protected void transferFluids() { + IFluidHandler fluidHandler = getCoverableView().getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, + getAttachedSide()); + if (fluidHandler == null) return; + if (pumpMode == CoverPump.PumpMode.IMPORT) { + GTTransferUtils.transferFluids(fluidHandler, activeEntry, TRANSFER_RATE, fluidFilter::test); + } else if (pumpMode == CoverPump.PumpMode.EXPORT) { + GTTransferUtils.transferFluids(activeEntry, fluidHandler, TRANSFER_RATE, fluidFilter::test); + } + } + + public void setPumpMode(CoverPump.PumpMode pumpMode) { + this.pumpMode = pumpMode; + markDirty(); + } + + public CoverPump.PumpMode getPumpMode() { + return pumpMode; + } + + @Override + protected PanelSyncHandler createEntrySelector(ModularPanel panel) { + return new EntrySelectorSH(panel, EntryTypes.ENDER_FLUID) { + + @Override + protected IWidget createSlotWidget(VirtualTank entry) { + var fluidTank = GTFluidSlot.sync(entry) + .canFillSlot(false) + .canDrainSlot(false); + + return new GTFluidSlot() + .size(18) + .background(GTGuiTextures.FLUID_SLOT) + .syncHandler(fluidTank) + .marginRight(2); + } + + @Override + protected void deleteEntry(UUID uuid, String name) { + VirtualEnderRegistry.deleteEntry(uuid, getType(), name, tank -> tank.getFluidAmount() == 0); + } + }; + } + + @Override + protected IWidget createEntrySlot() { + return new GTFluidSlot() + .size(18) + .background(GTGuiTextures.FLUID_SLOT) + .syncHandler(this.linkedTank) + .marginRight(2); + } + + protected Column createWidgets(ModularPanel panel, PanelSyncManager syncManager) { + getFluidFilterContainer().setMaxTransferSize(1); + + var pumpMode = new EnumSyncValue<>(CoverPump.PumpMode.class, this::getPumpMode, this::setPumpMode); + syncManager.syncValue("pump_mode", pumpMode); + pumpMode.updateCacheFromSource(true); + + return super.createWidgets(panel, syncManager) + .child(getFluidFilterContainer().initUI(panel, syncManager)) + .child(new EnumRowBuilder<>(CoverPump.PumpMode.class) + .value(pumpMode) + .overlay(GTGuiTextures.CONVEYOR_MODE_OVERLAY) + .lang("cover.pump.mode") + .build()); + } + + @Override + public void writeToNBT(NBTTagCompound tagCompound) { + super.writeToNBT(tagCompound); + tagCompound.setInteger("PumpMode", pumpMode.ordinal()); + tagCompound.setTag("Filter", fluidFilter.serializeNBT()); + } + + @Override + public void readFromNBT(NBTTagCompound tagCompound) { + super.readFromNBT(tagCompound); + this.pumpMode = CoverPump.PumpMode.values()[tagCompound.getInteger("PumpMode")]; + this.fluidFilter.deserializeNBT(tagCompound.getCompoundTag("Filter")); + } + + public T getCapability(Capability capability, T defaultValue) { + if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { + return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this.activeEntry); + } + if (capability == GregtechTileCapabilities.CAPABILITY_CONTROLLABLE) { + return GregtechTileCapabilities.CAPABILITY_CONTROLLABLE.cast(this); + } + return defaultValue; + } +} diff --git a/src/main/java/gregtech/common/items/behaviors/ItemMagnetBehavior.java b/src/main/java/gregtech/common/items/behaviors/ItemMagnetBehavior.java index f5ce22ba925..bd0adea08e7 100644 --- a/src/main/java/gregtech/common/items/behaviors/ItemMagnetBehavior.java +++ b/src/main/java/gregtech/common/items/behaviors/ItemMagnetBehavior.java @@ -6,6 +6,7 @@ import gregtech.api.items.metaitem.MetaItem; import gregtech.api.items.metaitem.stats.IItemBehaviour; import gregtech.api.util.Mods; +import gregtech.common.ConfigHolder; import gregtech.integration.baubles.BaublesModule; import net.minecraft.client.resources.I18n; @@ -81,7 +82,7 @@ private static boolean toggleActive(ItemStack stack) { public void onUpdate(ItemStack stack, Entity entity) { // Adapted logic from Draconic Evolution // https://github.com/Draconic-Inc/Draconic-Evolution/blob/1.12.2/src/main/java/com/brandon3055/draconicevolution/items/tools/Magnet.java - if (!entity.isSneaking() && entity.ticksExisted % 10 == 0 && isActive(stack) && + if (!entity.isSneaking() && entity.ticksExisted % ConfigHolder.tools.magnetDelay == 0 && isActive(stack) && entity instanceof EntityPlayer player) { World world = entity.getEntityWorld(); if (!drainEnergy(true, stack, energyDraw)) { diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityAssemblyLine.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityAssemblyLine.java index fb2de88df33..69b648b41cb 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityAssemblyLine.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityAssemblyLine.java @@ -14,7 +14,7 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.ingredients.GTRecipeInput; -import gregtech.api.recipes.recipeproperties.ResearchProperty; +import gregtech.api.recipes.properties.impl.ResearchProperty; import gregtech.api.util.GTUtility; import gregtech.api.util.RelativeDirection; import gregtech.client.particle.GTLaserBeamParticle; diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java index d71dca8cd0d..b1bd30b8574 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java @@ -12,7 +12,7 @@ import gregtech.api.pattern.PatternMatchContext; import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; import gregtech.api.util.TextComponentUtil; import gregtech.client.renderer.ICubeRenderer; @@ -150,7 +150,7 @@ public CrackingUnitWorkableHandler(RecipeMapMultiblockController tileEntity) { } @Override - protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull RecipePropertyStorage storage) { super.modifyOverclockPost(ocResult, storage); int coilTier = ((MetaTileEntityCrackingUnit) metaTileEntity).getCoilTier(); diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java index 78d9449eaf2..89c277d7c8d 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java @@ -17,7 +17,7 @@ import gregtech.api.pattern.PatternMatchContext; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMaps; -import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.recipes.properties.impl.TemperatureProperty; import gregtech.api.util.GTUtility; import gregtech.api.util.TextComponentUtil; import gregtech.api.util.TextFormattingUtil; diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java index 4b6b9db00d5..b9a918639f3 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java @@ -30,8 +30,8 @@ import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.logic.OCParams; -import gregtech.api.recipes.recipeproperties.FusionEUToStartProperty; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; +import gregtech.api.recipes.properties.impl.FusionEUToStartProperty; import gregtech.api.util.RelativeDirection; import gregtech.api.util.TextComponentUtil; import gregtech.api.util.TextFormattingUtil; @@ -637,13 +637,13 @@ public boolean checkRecipe(@NotNull Recipe recipe) { } @Override - protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPre(@NotNull OCParams ocParams, @NotNull RecipePropertyStorage storage) { super.modifyOverclockPre(ocParams, storage); // Limit the number of OCs to the difference in fusion reactor MK. // I.e., a MK2 reactor can overclock a MK1 recipe once, and a // MK3 reactor can overclock a MK2 recipe once, or a MK1 recipe twice. - long euToStart = storage.getRecipePropertyValue(FusionEUToStartProperty.getInstance(), 0L); + long euToStart = storage.get(FusionEUToStartProperty.getInstance(), 0L); int fusionTier = FusionEUToStartProperty.getFusionTier(euToStart); if (fusionTier != 0) fusionTier = MetaTileEntityFusionReactor.this.tier - fusionTier; ocParams.setOcAmount(Math.min(fusionTier, ocParams.ocAmount())); diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java index d25c923630d..71fca7ba8d4 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java @@ -17,7 +17,7 @@ import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; import gregtech.api.recipes.machines.RecipeMapFurnace; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; import gregtech.api.util.TextComponentUtil; import gregtech.api.util.TextFormattingUtil; @@ -208,7 +208,7 @@ public ParallelLogicType getParallelLogicType() { @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { standardOC(ocParams, ocResult, maxVoltage, getOverclockingDurationFactor(), getOverclockingVoltageFactor()); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java index 7a8fd7a9162..4759bb1dec7 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java @@ -24,7 +24,7 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; import gregtech.api.util.TextComponentUtil; import gregtech.api.util.TextFormattingUtil; @@ -417,7 +417,7 @@ protected int getNumberOfOCs(long recipeEUt) { @Override protected void runOverclockingLogic(@NotNull OCParams ocParams, @NotNull OCResult ocResult, - @NotNull IRecipePropertyStorage propertyStorage, long maxVoltage) { + @NotNull RecipePropertyStorage propertyStorage, long maxVoltage) { subTickNonParallelOC(ocParams, ocResult, maxVoltage, getOverclockingDurationFactor(), getOverclockingVoltageFactor()); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java index efda75b7dc6..b4715563582 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java @@ -12,7 +12,7 @@ import gregtech.api.pattern.PatternMatchContext; import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.logic.OCResult; -import gregtech.api.recipes.recipeproperties.IRecipePropertyStorage; +import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; import gregtech.api.util.TextComponentUtil; import gregtech.client.renderer.ICubeRenderer; @@ -173,7 +173,7 @@ public PyrolyseOvenWorkableHandler(RecipeMapMultiblockController tileEntity) { } @Override - protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull IRecipePropertyStorage storage) { + protected void modifyOverclockPost(@NotNull OCResult ocResult, @NotNull RecipePropertyStorage storage) { super.modifyOverclockPost(ocResult, storage); int coilTier = ((MetaTileEntityPyrolyseOven) metaTileEntity).getCoilTier(); diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityAEHostablePart.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityAEHostablePart.java index c71b4acf74e..fbe7e8ff674 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityAEHostablePart.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityAEHostablePart.java @@ -4,11 +4,14 @@ import gregtech.common.ConfigHolder; import gregtech.common.metatileentities.multi.multiblockpart.MetaTileEntityMultiblockNotifiablePart; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextComponentTranslation; import appeng.api.AEApi; import appeng.api.networking.GridFlags; @@ -24,6 +27,7 @@ import appeng.me.helpers.BaseActionSource; import appeng.me.helpers.IGridProxyable; import appeng.me.helpers.MachineSource; +import codechicken.lib.raytracer.CuboidRayTraceResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,12 +44,14 @@ public abstract class MetaTileEntityAEHostablePart> extend private AENetworkProxy aeProxy; private int meUpdateTick; protected boolean isOnline; + private boolean allowExtraConnections; public MetaTileEntityAEHostablePart(ResourceLocation metaTileEntityId, int tier, boolean isExportHatch, Class> storageChannel) { super(metaTileEntityId, tier, isExportHatch); this.meUpdateTick = 0; this.storageChannel = storageChannel; + this.allowExtraConnections = false; } @Override @@ -76,6 +82,7 @@ public void writeInitialSyncData(PacketBuffer buf) { } buf.writeInt(this.meUpdateTick); buf.writeBoolean(this.isOnline); + buf.writeBoolean(this.allowExtraConnections); } @Override @@ -95,6 +102,7 @@ public void receiveInitialSyncData(PacketBuffer buf) { } this.meUpdateTick = buf.readInt(); this.isOnline = buf.readBoolean(); + this.allowExtraConnections = buf.readBoolean(); } @Override @@ -112,7 +120,7 @@ public void receiveCustomData(int dataId, PacketBuffer buf) { @NotNull @Override public AECableType getCableConnectionType(@NotNull AEPartLocation part) { - if (part.getFacing() != this.frontFacing) { + if (part.getFacing() != this.frontFacing && !this.allowExtraConnections) { return AECableType.NONE; } return AECableType.SMART; @@ -133,9 +141,7 @@ public AENetworkProxy getProxy() { @Override public void setFrontFacing(EnumFacing frontFacing) { super.setFrontFacing(frontFacing); - if (this.aeProxy != null) { - this.aeProxy.setValidSides(EnumSet.of(this.getFrontFacing())); - } + updateConnectableSides(); } @Override @@ -174,7 +180,7 @@ private AENetworkProxy createProxy() { AENetworkProxy proxy = new AENetworkProxy(holder, "mte_proxy", this.getStackForm(), true); proxy.setFlags(GridFlags.REQUIRE_CHANNEL); proxy.setIdlePowerUsage(ConfigHolder.compat.ae2.meHatchEnergyUsage); - proxy.setValidSides(EnumSet.of(this.getFrontFacing())); + proxy.setValidSides(getConnectableSides()); return proxy; } return null; @@ -198,4 +204,42 @@ protected IMEMonitor getMonitor() { return null; } } + + public EnumSet getConnectableSides() { + return this.allowExtraConnections ? EnumSet.allOf(EnumFacing.class) : EnumSet.of(getFrontFacing()); + } + + public void updateConnectableSides() { + if (this.aeProxy != null) { + this.aeProxy.setValidSides(getConnectableSides()); + } + } + + @Override + public boolean onWireCutterClick(EntityPlayer playerIn, EnumHand hand, EnumFacing facing, + CuboidRayTraceResult hitResult) { + this.allowExtraConnections = !this.allowExtraConnections; + updateConnectableSides(); + + if (!getWorld().isRemote) { + playerIn.sendStatusMessage(new TextComponentTranslation(this.allowExtraConnections ? + "gregtech.machine.me.extra_connections.enabled" : "gregtech.machine.me.extra_connections.disabled"), + true); + } + + return true; + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound data) { + super.writeToNBT(data); + data.setBoolean("AllowExtraConnections", this.allowExtraConnections); + return data; + } + + @Override + public void readFromNBT(NBTTagCompound data) { + super.readFromNBT(data); + this.allowExtraConnections = data.getBoolean("AllowExtraConnections"); + } } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputBus.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputBus.java index 3d4fea9abc0..b1e53f22f4a 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputBus.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputBus.java @@ -320,6 +320,7 @@ public void addInformation(ItemStack stack, @Nullable World player, @NotNull Lis tooltip.add(I18n.format("gregtech.machine.me.item_import.tooltip")); tooltip.add(I18n.format("gregtech.machine.me_import_item_hatch.configs.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.copy_paste.tooltip")); + tooltip.add(I18n.format("gregtech.machine.me.extra_connections.tooltip")); tooltip.add(I18n.format("gregtech.universal.enabled")); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputHatch.java index 4e510077787..2bcdc10f542 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEInputHatch.java @@ -260,6 +260,7 @@ public void addInformation(ItemStack stack, @Nullable World player, @NotNull Lis tooltip.add(I18n.format("gregtech.machine.me.fluid_import.tooltip")); tooltip.add(I18n.format("gregtech.machine.me_import_fluid_hatch.configs.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.copy_paste.tooltip")); + tooltip.add(I18n.format("gregtech.machine.me.extra_connections.tooltip")); tooltip.add(I18n.format("gregtech.universal.enabled")); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputBus.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputBus.java index 853321014f9..9e008b857e3 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputBus.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputBus.java @@ -186,6 +186,7 @@ public void addInformation(ItemStack stack, @Nullable World player, @NotNull Lis tooltip.add(I18n.format("gregtech.machine.item_bus.export.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.item_export.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.item_export.tooltip.2")); + tooltip.add(I18n.format("gregtech.machine.me.extra_connections.tooltip")); tooltip.add(I18n.format("gregtech.universal.enabled")); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputHatch.java index f22c489909b..26a8fcc7947 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEOutputHatch.java @@ -188,6 +188,7 @@ public void addInformation(ItemStack stack, @Nullable World player, @NotNull Lis tooltip.add(I18n.format("gregtech.machine.fluid_hatch.export.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.fluid_export.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.fluid_export.tooltip.2")); + tooltip.add(I18n.format("gregtech.machine.me.extra_connections.tooltip")); tooltip.add(I18n.format("gregtech.universal.enabled")); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingBus.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingBus.java index f8bd7a5850a..640f7869617 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingBus.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingBus.java @@ -360,6 +360,7 @@ public void addInformation(ItemStack stack, @Nullable World player, @NotNull Lis tooltip.add(I18n.format("gregtech.machine.me_import_item_hatch.configs.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.copy_paste.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.stocking_item.tooltip.2")); + tooltip.add(I18n.format("gregtech.machine.me.extra_connections.tooltip")); tooltip.add(I18n.format("gregtech.universal.enabled")); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingHatch.java index fcc9e1e7d12..30e28eff54b 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/appeng/MetaTileEntityMEStockingHatch.java @@ -265,6 +265,7 @@ public void addInformation(ItemStack stack, @Nullable World player, @NotNull Lis tooltip.add(I18n.format("gregtech.machine.me_import_fluid_hatch.configs.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.copy_paste.tooltip")); tooltip.add(I18n.format("gregtech.machine.me.stocking_fluid.tooltip.2")); + tooltip.add(I18n.format("gregtech.machine.me.extra_connections.tooltip")); tooltip.add(I18n.format("gregtech.universal.enabled")); } diff --git a/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java b/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java new file mode 100644 index 00000000000..1b0ccff4ebe --- /dev/null +++ b/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java @@ -0,0 +1,142 @@ +package gregtech.common.mui.widget; + +import gregtech.api.GTValues; +import gregtech.api.mui.sync.GTFluidSyncHandler; +import gregtech.api.util.FluidTooltipUtil; +import gregtech.api.util.LocalizationUtils; +import gregtech.client.utils.TooltipHelper; + +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidTank; + +import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.drawable.GuiDraw; +import com.cleanroommc.modularui.drawable.TextRenderer; +import com.cleanroommc.modularui.integration.jei.JeiIngredientProvider; +import com.cleanroommc.modularui.screen.Tooltip; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetSlotTheme; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.NumberFormat; +import com.cleanroommc.modularui.widget.Widget; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class GTFluidSlot extends Widget implements Interactable, JeiIngredientProvider { + + private final TextRenderer textRenderer = new TextRenderer(); + private GTFluidSyncHandler syncHandler; + + public GTFluidSlot() { + tooltip().setAutoUpdate(true).setHasTitleMargin(true); + tooltipBuilder(tooltip -> { + if (!isSynced()) return; + var fluid = this.syncHandler.getFluid(); + if (fluid == null) return; + + tooltip.addLine(fluid.getLocalizedName()); + tooltip.addLine(IKey.lang("gregtech.fluid.amount", fluid.amount, this.syncHandler.getCapacity())); + + // Add various tooltips from the material + List formula = FluidTooltipUtil.getFluidTooltip(fluid); + if (formula != null) { + for (String s : formula) { + if (s.isEmpty()) continue; + tooltip.addLine(s); + } + } + + addIngotMolFluidTooltip(fluid, tooltip); + }); + } + + public static GTFluidSyncHandler sync(IFluidTank tank) { + return new GTFluidSyncHandler(tank); + } + + @Override + public void onInit() { + this.textRenderer.setShadow(true); + this.textRenderer.setScale(0.5f); + this.textRenderer.setColor(Color.WHITE.main); + } + + public GTFluidSlot syncHandler(IFluidTank fluidTank) { + return syncHandler(new GTFluidSyncHandler(fluidTank)); + } + + public GTFluidSlot syncHandler(GTFluidSyncHandler syncHandler) { + setSyncHandler(syncHandler); + this.syncHandler = syncHandler; + return this; + } + + @Override + public void draw(GuiContext context, WidgetTheme widgetTheme) { + FluidStack content = this.syncHandler.getFluid(); + if (content != null) { + GuiDraw.drawFluidTexture(content, 1, 1, getArea().w() - 2, getArea().h() - 2, 0); + + String s = NumberFormat.formatWithMaxDigits(getBaseUnitAmount(content.amount)) + getBaseUnit(); + this.textRenderer.setAlignment(Alignment.CenterRight, getArea().width - 1f); + this.textRenderer.setPos(0, 12); + this.textRenderer.draw(s); + } + if (isHovering()) { + GlStateManager.colorMask(true, true, true, false); + GuiDraw.drawRect(1, 1, getArea().w() - 2, getArea().h() - 2, + getWidgetTheme(context.getTheme()).getSlotHoverColor()); + GlStateManager.colorMask(true, true, true, true); + } + } + + protected double getBaseUnitAmount(double amount) { + return amount / 1000; + } + + protected String getBaseUnit() { + return "L"; + } + + @NotNull + @Override + public Result onMouseTapped(int mouseButton) { + if (this.syncHandler.canFillSlot() || this.syncHandler.canDrainSlot()) { + this.syncHandler.syncToServer(1, buffer -> buffer.writeBoolean(mouseButton == 0)); + Interactable.playButtonClickSound(); + return Result.SUCCESS; + } + return Result.IGNORE; + } + + @Override + public WidgetSlotTheme getWidgetTheme(ITheme theme) { + return theme.getFluidSlotTheme(); + } + + @Override + public @Nullable Object getIngredient() { + return this.syncHandler.getFluid(); + } + + public static void addIngotMolFluidTooltip(FluidStack fluidStack, Tooltip tooltip) { + // Add tooltip showing how many "ingot moles" (increments of 144) this fluid is if shift is held + if (TooltipHelper.isShiftDown() && fluidStack.amount > GTValues.L) { + int numIngots = fluidStack.amount / GTValues.L; + int extra = fluidStack.amount % GTValues.L; + String fluidAmount = String.format(" %,d L = %,d * %d L", fluidStack.amount, numIngots, GTValues.L); + if (extra != 0) { + fluidAmount += String.format(" + %d L", extra); + } + tooltip.addLine(TextFormatting.GRAY + LocalizationUtils.format("gregtech.gui.amount_raw") + fluidAmount); + } + } +} diff --git a/src/main/java/gregtech/common/mui/widget/InteractableText.java b/src/main/java/gregtech/common/mui/widget/InteractableText.java new file mode 100644 index 00000000000..eac6936059a --- /dev/null +++ b/src/main/java/gregtech/common/mui/widget/InteractableText.java @@ -0,0 +1,63 @@ +package gregtech.common.mui.widget; + +import gregtech.api.util.virtualregistry.VirtualEntry; + +import net.minecraft.network.PacketBuffer; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.network.NetworkUtils; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.value.sync.SyncHandler; +import com.cleanroommc.modularui.widgets.TextWidget; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +public class InteractableText extends TextWidget implements Interactable { + + private final T entry; + private final EntryColorSH syncHandler; + + public InteractableText(T entry, Consumer setter) { + super(IKey.str(entry.getColorStr()) + .alignment(Alignment.CenterLeft) + .color(Color.WHITE.darker(1))); + this.entry = entry; + this.syncHandler = new EntryColorSH(setter); + setSyncHandler(this.syncHandler); + } + + @NotNull + @Override + public Result onMousePressed(int mouseButton) { + Interactable.playButtonClickSound(); + this.syncHandler.setColor(this.entry.getColorStr()); + this.syncHandler.syncToServer(1, buf -> NetworkUtils.writeStringSafe(buf, this.entry.getColorStr())); + return Result.SUCCESS; + } + + private static class EntryColorSH extends SyncHandler { + + private final Consumer setter; + + private EntryColorSH(Consumer setter) { + this.setter = setter; + } + + public void setColor(String c) { + this.setter.accept(c); + } + + @Override + public void readOnClient(int id, PacketBuffer buf) {} + + @Override + public void readOnServer(int id, PacketBuffer buf) { + if (id == 1) { + setColor(NetworkUtils.readStringSafe(buf)); + } + } + } +} diff --git a/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java b/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java index b6a24a190b9..ed0e63eefac 100644 --- a/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java +++ b/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java @@ -12,7 +12,7 @@ import gregtech.api.terminal.os.TerminalTheme; import gregtech.api.terminal.os.menu.IMenuComponent; import gregtech.api.util.GTLog; -import gregtech.api.util.VirtualTankRegistry; +import gregtech.api.util.virtualregistry.VirtualEnderRegistry; import gregtech.common.terminal.component.SearchComponent; import net.minecraft.nbt.NBTTagCompound; @@ -70,9 +70,8 @@ public AbstractApplication initApp() { return this; } - private List> findVirtualTanks() { + private List> findVirtualTanks(Map> tankMap) { List> result = new LinkedList<>(); - Map> tankMap = VirtualTankRegistry.getTankMap(); for (UUID uuid : tankMap.keySet().stream().sorted(Comparator.nullsLast(UUID::compareTo)) .collect(Collectors.toList())) { if (uuid == null || uuid.equals(gui.entityPlayer.getUniqueID())) { @@ -93,9 +92,9 @@ public void detectAndSendChanges() { } private void refresh() { - Map> tankMap = VirtualTankRegistry.getTankMap(); + Map> tankMap = VirtualEnderRegistry.createTankMap(); Map, FluidStack> access = new HashMap<>(); - for (Pair virtualTankEntry : findVirtualTanks()) { + for (Pair virtualTankEntry : findVirtualTanks(tankMap)) { UUID uuid = virtualTankEntry.getKey(); String key = virtualTankEntry.getValue(); FluidStack fluidStack = tankMap.get(uuid).get(key).getFluid(); @@ -229,7 +228,8 @@ public List getMenuComponents() { @Override public String resultDisplay(Pair result) { - FluidStack fluidStack = VirtualTankRegistry.getTankMap().get(result.getKey()).get(result.getValue()).getFluid(); + FluidStack fluidStack = VirtualEnderRegistry.createTankMap().get(result.getKey()).get(result.getValue()) + .getFluid(); return String.format("Lock: %b, ID: %s, Fluid: %s", result.getKey() != null, result.getValue(), fluidStack == null ? "-" : fluidStack.getLocalizedName()); } diff --git a/src/main/java/gregtech/core/CoreModule.java b/src/main/java/gregtech/core/CoreModule.java index 5e80c4b3b31..aecbe907d48 100644 --- a/src/main/java/gregtech/core/CoreModule.java +++ b/src/main/java/gregtech/core/CoreModule.java @@ -20,7 +20,7 @@ import gregtech.api.mui.GTGuis; import gregtech.api.recipes.ModHandler; import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.recipes.properties.impl.TemperatureProperty; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.material.Materials; import gregtech.api.unification.material.event.MaterialEvent; @@ -29,9 +29,9 @@ import gregtech.api.unification.material.registry.MarkerMaterialRegistry; import gregtech.api.util.CapesRegistry; import gregtech.api.util.Mods; -import gregtech.api.util.VirtualTankRegistry; import gregtech.api.util.input.KeyBind; import gregtech.api.util.oreglob.OreGlob; +import gregtech.api.util.virtualregistry.VirtualEnderRegistry; import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinHandler; import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinSaveData; import gregtech.api.worldgen.config.WorldGenRegistry; @@ -347,7 +347,7 @@ public void serverStarted(FMLServerStartedEvent event) { @Override public void serverStopped(FMLServerStoppedEvent event) { - VirtualTankRegistry.clearMaps(); + VirtualEnderRegistry.clearMaps(); CapesRegistry.clearMaps(); } } diff --git a/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipe.java b/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipe.java index 728c190a0d3..ac71e2f0fbb 100644 --- a/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipe.java +++ b/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipe.java @@ -15,7 +15,6 @@ import stanhebben.zenscript.annotations.ZenGetter; import stanhebben.zenscript.annotations.ZenMethod; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -90,16 +89,6 @@ public boolean isHidden() { return this.backingRecipe.isHidden(); } - @ZenGetter("propertyKeys") - public List getPropertyKeys() { - return new ArrayList<>(this.backingRecipe.getPropertyKeys()); - } - - @ZenMethod - public Object getProperty(String key) { - return this.backingRecipe.getPropertyRaw(key); - } - @ZenMethod public boolean remove() { return this.recipeMap.removeRecipe(this.backingRecipe); diff --git a/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipeBuilder.java b/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipeBuilder.java index be74a57456c..b419cc4960b 100644 --- a/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipeBuilder.java +++ b/src/main/java/gregtech/integration/crafttweaker/recipe/CTRecipeBuilder.java @@ -220,7 +220,7 @@ public CTRecipeBuilder fluidOutputs(ILiquidStack... ingredients) { @ZenMethod public CTRecipeBuilder property(String key, int value) { - boolean applied = this.backingBuilder.applyProperty(key, value); + boolean applied = this.backingBuilder.applyPropertyCT(key, value); if (!applied) { throw new IllegalArgumentException("Property " + key + " cannot be applied to recipe type " + @@ -231,7 +231,7 @@ public CTRecipeBuilder property(String key, int value) { @ZenMethod public CTRecipeBuilder property(String key, String value) { - boolean applied = this.backingBuilder.applyProperty(key, value); + boolean applied = this.backingBuilder.applyPropertyCT(key, value); if (!applied) { throw new IllegalArgumentException("Property " + key + " cannot be applied to recipe type " + @@ -242,7 +242,7 @@ public CTRecipeBuilder property(String key, String value) { @ZenMethod public CTRecipeBuilder property(String key, boolean value) { - boolean applied = this.backingBuilder.applyProperty(key, value); + boolean applied = this.backingBuilder.applyPropertyCT(key, value); if (!applied) { throw new IllegalArgumentException("Property " + key + " cannot be applied to recipe type " + @@ -253,7 +253,7 @@ public CTRecipeBuilder property(String key, boolean value) { @ZenMethod public CTRecipeBuilder property(String key, long value) { - boolean applied = this.backingBuilder.applyProperty(key, value); + boolean applied = this.backingBuilder.applyPropertyCT(key, value); if (!applied) { throw new IllegalArgumentException("Property " + key + " cannot be applied to recipe type " + @@ -264,7 +264,7 @@ public CTRecipeBuilder property(String key, long value) { @ZenMethod public CTRecipeBuilder property(String key, float value) { - boolean applied = this.backingBuilder.applyProperty(key, value); + boolean applied = this.backingBuilder.applyPropertyCT(key, value); if (!applied) { throw new IllegalArgumentException("Property " + key + " cannot be applied to recipe type " + @@ -275,7 +275,7 @@ public CTRecipeBuilder property(String key, float value) { @ZenMethod public CTRecipeBuilder property(String key, IItemStack item) { - boolean applied = this.backingBuilder.applyProperty(key, CraftTweakerMC.getItemStack(item)); + boolean applied = this.backingBuilder.applyPropertyCT(key, CraftTweakerMC.getItemStack(item)); if (!applied) { throw new IllegalArgumentException("Property " + key + " cannot be applied to recipe type " + diff --git a/src/main/java/gregtech/integration/groovy/GroovyExpansions.java b/src/main/java/gregtech/integration/groovy/GroovyExpansions.java index ff00a9d1b5b..00a7906fb0e 100644 --- a/src/main/java/gregtech/integration/groovy/GroovyExpansions.java +++ b/src/main/java/gregtech/integration/groovy/GroovyExpansions.java @@ -18,7 +18,7 @@ public class GroovyExpansions { public static > RecipeBuilder property(RecipeBuilder builder, String key, Object value) { - if (!builder.applyProperty(key, value)) { + if (!builder.applyPropertyCT(key, value)) { GroovyLog.get().error("Failed to add property '{}' with '{}' to recipe", key, value); } return builder; diff --git a/src/main/java/gregtech/integration/jei/basic/MaterialTreeCategory.java b/src/main/java/gregtech/integration/jei/basic/MaterialTreeCategory.java index 16a26032f73..b3c8b6e10b2 100644 --- a/src/main/java/gregtech/integration/jei/basic/MaterialTreeCategory.java +++ b/src/main/java/gregtech/integration/jei/basic/MaterialTreeCategory.java @@ -2,7 +2,7 @@ import gregtech.api.GTValues; import gregtech.api.gui.GuiTextures; -import gregtech.api.recipes.recipeproperties.TemperatureProperty; +import gregtech.api.recipes.properties.impl.TemperatureProperty; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.material.Materials; import gregtech.api.unification.ore.OrePrefix; diff --git a/src/main/java/gregtech/integration/jei/recipe/GTRecipeWrapper.java b/src/main/java/gregtech/integration/jei/recipe/GTRecipeWrapper.java index f0fb97c1f1c..7dae031a66a 100644 --- a/src/main/java/gregtech/integration/jei/recipe/GTRecipeWrapper.java +++ b/src/main/java/gregtech/integration/jei/recipe/GTRecipeWrapper.java @@ -16,10 +16,10 @@ import gregtech.api.recipes.ingredients.GTRecipeInput; import gregtech.api.recipes.machines.IResearchRecipeMap; import gregtech.api.recipes.machines.IScannerRecipeMap; -import gregtech.api.recipes.recipeproperties.ComputationProperty; -import gregtech.api.recipes.recipeproperties.RecipeProperty; -import gregtech.api.recipes.recipeproperties.ScanProperty; -import gregtech.api.recipes.recipeproperties.TotalComputationProperty; +import gregtech.api.recipes.properties.RecipeProperty; +import gregtech.api.recipes.properties.impl.ComputationProperty; +import gregtech.api.recipes.properties.impl.ScanProperty; +import gregtech.api.recipes.properties.impl.TotalComputationProperty; import gregtech.api.util.AssemblyLineManager; import gregtech.api.util.ClipboardUtil; import gregtech.api.util.GTUtility; @@ -44,7 +44,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.function.BooleanSupplier; import java.util.stream.Collectors; @@ -239,7 +238,8 @@ public void addIngredientTooltips(@NotNull Collection tooltip, boolean n @Override public void drawInfo(@NotNull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { super.drawInfo(minecraft, recipeWidth, recipeHeight, mouseX, mouseY); - var properties = recipe.getPropertyTypes(); + var storage = recipe.propertyStorage(); + var properties = storage.values(); boolean drawTotalEU = properties.isEmpty() || properties.stream().noneMatch(RecipeProperty::hideTotalEU); boolean drawEUt = properties.isEmpty() || properties.stream().noneMatch(RecipeProperty::hideEUt); boolean drawDuration = properties.isEmpty() || properties.stream().noneMatch(RecipeProperty::hideDuration); @@ -249,15 +249,18 @@ public void drawInfo(@NotNull Minecraft minecraft, int recipeWidth, int recipeHe if (drawEUt) defaultLines++; if (drawDuration) defaultLines++; - int yPosition = recipeHeight - ((recipe.getUnhiddenPropertyCount() + defaultLines) * 10 - 3); + int unhiddenCount = (int) storage.entrySet().stream() + .filter((property) -> !property.getKey().isHidden()) + .count(); + int yPosition = recipeHeight - ((unhiddenCount + defaultLines) * 10 - 3); // Default entries if (drawTotalEU) { long eu = recipe.getEUt() * recipe.getDuration(); // sadly we still need a custom override here, since computation uses duration and EU/t very differently - if (recipe.hasProperty(TotalComputationProperty.getInstance()) && - recipe.hasProperty(ComputationProperty.getInstance())) { - int minimumCWUt = recipe.getProperty(ComputationProperty.getInstance(), 1); + if (storage.contains(TotalComputationProperty.getInstance()) && + storage.contains(ComputationProperty.getInstance())) { + int minimumCWUt = storage.get(ComputationProperty.getInstance(), 1); minecraft.fontRenderer.drawString(I18n.format("gregtech.recipe.max_eu", eu / minimumCWUt), 0, yPosition, 0x111111); } else { @@ -280,10 +283,10 @@ public void drawInfo(@NotNull Minecraft minecraft, int recipeWidth, int recipeHe } // Property custom entries - for (Map.Entry, Object> propertyEntry : recipe.getPropertyValues()) { + for (var propertyEntry : storage.entrySet()) { if (!propertyEntry.getKey().isHidden()) { RecipeProperty property = propertyEntry.getKey(); - Object value = propertyEntry.getValue(); + var value = propertyEntry.getValue(); property.drawInfo(minecraft, 0, yPosition += property.getInfoHeight(value), 0x111111, value, mouseX, mouseY); } @@ -294,7 +297,7 @@ public void drawInfo(@NotNull Minecraft minecraft, int recipeWidth, int recipeHe @Override public List getTooltipStrings(int mouseX, int mouseY) { List tooltips = new ArrayList<>(); - for (var entry : recipe.getPropertyValues()) { + for (var entry : recipe.propertyStorage().entrySet()) { if (!entry.getKey().isHidden()) { RecipeProperty property = entry.getKey(); Object value = entry.getValue(); diff --git a/src/main/java/gregtech/integration/jei/recipe/RecipeMapCategory.java b/src/main/java/gregtech/integration/jei/recipe/RecipeMapCategory.java index 52039a21b07..31dde8f7874 100644 --- a/src/main/java/gregtech/integration/jei/recipe/RecipeMapCategory.java +++ b/src/main/java/gregtech/integration/jei/recipe/RecipeMapCategory.java @@ -12,8 +12,8 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.category.GTRecipeCategory; -import gregtech.api.recipes.recipeproperties.ResearchProperty; -import gregtech.api.recipes.recipeproperties.ResearchPropertyData; +import gregtech.api.recipes.properties.impl.ResearchProperty; +import gregtech.api.recipes.properties.impl.ResearchPropertyData; import gregtech.api.util.AssemblyLineManager; import gregtech.api.util.GTUtility; import gregtech.api.util.LocalizationUtils; @@ -213,9 +213,9 @@ public void setRecipe(IRecipeLayout recipeLayout, @NotNull GTRecipeWrapper recip if (data != null) { List dataItems = new ArrayList<>(); for (ResearchPropertyData.ResearchEntry entry : data) { - ItemStack dataStick = entry.getDataItem().copy(); + ItemStack dataStick = entry.dataItem().copy(); AssemblyLineManager.writeResearchToNBT(GTUtility.getOrCreateNbtCompound(dataStick), - entry.getResearchId()); + entry.researchId()); dataItems.add(dataStick); } itemStackGroup.set(16, dataItems); diff --git a/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java b/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java index be85082485c..b05a8e3f7af 100644 --- a/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java +++ b/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java @@ -5,6 +5,7 @@ import gregtech.api.cover.CoverHolder; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.common.covers.*; +import gregtech.common.covers.ender.CoverEnderFluidLink; import gregtech.integration.opencomputers.InputValidator; import gregtech.integration.opencomputers.values.*; diff --git a/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java b/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java index 8f919385959..31c8b2ae10d 100644 --- a/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java +++ b/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java @@ -1,8 +1,8 @@ package gregtech.integration.opencomputers.values; import gregtech.api.cover.Cover; -import gregtech.common.covers.CoverEnderFluidLink; import gregtech.common.covers.CoverPump.PumpMode; +import gregtech.common.covers.ender.CoverEnderFluidLink; import gregtech.integration.opencomputers.InputValidator; import net.minecraft.util.EnumFacing; diff --git a/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java b/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java index 0aebf88036a..377ff7a51d7 100644 --- a/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java +++ b/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java @@ -6,6 +6,7 @@ import gregtech.api.cover.CoverHolder; import gregtech.api.util.TextFormattingUtil; import gregtech.common.covers.*; +import gregtech.common.covers.ender.CoverEnderFluidLink; import gregtech.common.covers.filter.*; import net.minecraft.entity.player.EntityPlayer; @@ -181,8 +182,8 @@ private static void fluidFilterInfo(@NotNull IProbeInfo probeInfo, @NotNull Cove * @param enderFluidLink the ender fluid link cover to get data from */ private static void enderFluidLinkInfo(@NotNull IProbeInfo probeInfo, @NotNull CoverEnderFluidLink enderFluidLink) { - transferRateText(probeInfo, enderFluidLink.getPumpMode(), " " + lang("cover.bucket.mode.milli_bucket_rate"), - enderFluidLink.isIOEnabled() ? CoverEnderFluidLink.TRANSFER_RATE : 0); + transferRateText(probeInfo, enderFluidLink.getPumpMode(), " " + lang("cover.ender_fluid_link.transfer_unit"), + enderFluidLink.isIoEnabled() ? CoverEnderFluidLink.TRANSFER_RATE : 0); fluidFilterText(probeInfo, enderFluidLink.getFluidFilterContainer().getFilter()); if (!enderFluidLink.getColorStr().isEmpty()) { diff --git a/src/main/java/gregtech/loaders/recipe/handlers/MaterialRecipeHandler.java b/src/main/java/gregtech/loaders/recipe/handlers/MaterialRecipeHandler.java index cbdf94add16..3b048b093c6 100644 --- a/src/main/java/gregtech/loaders/recipe/handlers/MaterialRecipeHandler.java +++ b/src/main/java/gregtech/loaders/recipe/handlers/MaterialRecipeHandler.java @@ -91,28 +91,28 @@ public static void processDust(OrePrefix dustPrefix, Material mat, DustProperty .inputs(GTUtility.copy(4, dustStack)) .outputs(GTUtility.copy(3, gemStack)) .chancedOutput(dust, Materials.DarkAsh, 2500, 0) - .explosivesType(new ItemStack(MetaBlocks.POWDERBARREL, 8)) + .explosives(new ItemStack(MetaBlocks.POWDERBARREL, 8)) .buildAndRegister(); RecipeMaps.IMPLOSION_RECIPES.recipeBuilder() .inputs(GTUtility.copy(4, dustStack)) .outputs(GTUtility.copy(3, gemStack)) .chancedOutput(dust, Materials.DarkAsh, 2500, 0) - .explosivesAmount(4) + .explosives(4) .buildAndRegister(); RecipeMaps.IMPLOSION_RECIPES.recipeBuilder() .inputs(GTUtility.copy(4, dustStack)) .outputs(GTUtility.copy(3, gemStack)) .chancedOutput(dust, Materials.DarkAsh, 2500, 0) - .explosivesType(MetaItems.DYNAMITE.getStackForm(2)) + .explosives(MetaItems.DYNAMITE.getStackForm(2)) .buildAndRegister(); RecipeMaps.IMPLOSION_RECIPES.recipeBuilder() .inputs(GTUtility.copy(4, dustStack)) .outputs(GTUtility.copy(3, gemStack)) .chancedOutput(dust, Materials.DarkAsh, 2500, 0) - .explosivesType(new ItemStack(MetaBlocks.ITNT)) + .explosives(new ItemStack(MetaBlocks.ITNT)) .buildAndRegister(); } diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang index 8576e93db24..5c78797a406 100644 --- a/src/main/resources/assets/gregtech/lang/en_us.lang +++ b/src/main/resources/assets/gregtech/lang/en_us.lang @@ -1358,6 +1358,13 @@ cover.ender_fluid_link.iomode.disabled=I/O Disabled cover.ender_fluid_link.private.tooltip.disabled=Switch to private tank mode\nPrivate mode uses the player who originally placed the cover cover.ender_fluid_link.private.tooltip.enabled=Switch to public tank mode cover.ender_fluid_link.incomplete_hex=Inputted color is incomplete!/nIt will be applied once complete (all 8 hex digits)/nClosing the gui will lose edits! +cover.ender_fluid_link.transfer_unit=L/t + +cover.generic.ender.known_channels=Known Channels +cover.generic.ender.open_selector=Open Entry Selector +cover.generic.ender.set_description.tooltip=Set Description +cover.generic.ender.set_description.title=Set Description [%s] +cover.generic.ender.delete_entry=Delete Entry cover.generic.advanced_detector.latched=Latched cover.generic.advanced_detector.continuous=Continuous @@ -5333,6 +5340,9 @@ gregtech.machine.me.import_copy_settings=Saved settings to Data Stick gregtech.machine.me.import_paste_settings=Applied settings from Data Stick gregtech.machine.me.item_import.data_stick.name=§oME Input Bus Configuration Data gregtech.machine.me.fluid_import.data_stick.name=§oME Input Hatch Configuration Data +gregtech.machine.me.extra_connections.enabled=Allow connections from all sides +gregtech.machine.me.extra_connections.disabled=Allow connection only on front face +gregtech.machine.me.extra_connections.tooltip=Right-click with wire cutters to allow connecting to AE2 on all sides # Universal tooltips gregtech.universal.tooltip.voltage_in=§aVoltage IN: §f%,d EU/t (%s§f) diff --git a/src/main/resources/assets/gregtech/lang/ru_ru.lang b/src/main/resources/assets/gregtech/lang/ru_ru.lang index f8bfe1a7604..32e4120f240 100644 --- a/src/main/resources/assets/gregtech/lang/ru_ru.lang +++ b/src/main/resources/assets/gregtech/lang/ru_ru.lang @@ -39,7 +39,7 @@ enchantment.disjunction=Разъединение gregtech.machine.steam_grinder.name=Паровой измельчитель gregtech.multiblock.steam_grinder.description=Многоблочный измельчитель в паровой эпохе. Требует как минимум 14 бронзовых машинных корпусов. Может использовать только паровые люки. gregtech.multiblock.steam.low_steam=Недостаточно пара для работы! -gregtech.multiblock.steam.steam_stored=Пар: %s / %s mB +gregtech.multiblock.steam.steam_stored=Пар: %s gregtech.machine.steam_hatch.name=Паровой люк gregtech.machine.steam.steam_hatch.tooltip=§eПринимает жидкости: §fПар gregtech.machine.steam_import_bus.name=Паровой входной люк @@ -1315,7 +1315,7 @@ gregtech.material.hydrogen=Водород gregtech.material.helium=Гелий gregtech.material.helium_3=Гелий-3 gregtech.material.indium=Индий -gregtech.material.iodine=Иод +gregtech.material.iodine=Йод gregtech.material.iridium=Иридий gregtech.material.iron=Железо gregtech.material.krypton=Криптон @@ -1783,9 +1783,9 @@ gregtech.material.wood=Дерево gregtech.material.treated_wood=Обработанное дерево gregtech.material.paper=Бумага gregtech.material.fish_oil=Рыбий жир -gregtech.material.ruby_slurry=Рубиновый шлам -gregtech.material.sapphire_slurry=Сапфирный шлам -gregtech.material.green_sapphire_slurry=Шлам зелёного сапфира +gregtech.material.ruby_slurry=Рубиновая суспензия +gregtech.material.sapphire_slurry=Сапфировая суспензия +gregtech.material.green_sapphire_slurry=Суспензия зелёного сапфира gregtech.material.dye_black=Черный краситель gregtech.material.dye_red=Красный краситель gregtech.material.dye_green=Зеленый краситель @@ -5038,7 +5038,7 @@ gregtech.multiblock.hpca.warning_structure_header=Подсказки по стр gregtech.multiblock.hpca.warning_multiple_bridges=- Множество мостов в структуре (не дают дополнительных преимущество) gregtech.multiblock.hpca.warning_no_computation=- Не проводятся вычисления gregtech.multiblock.hpca.warning_low_cooling=- Не достаточно хладогента -gregtech.command.usage=Применение: /gregtech +gregtech.command.usage=Применение: /gregtech gregtech.command.worldgen.usage=Применение: /gregtech worldgen gregtech.command.worldgen.reload.usage=Применение: /gregtech worldgen reload gregtech.command.worldgen.reload.success=Генератор мира успешно перезагрузился из настроек. @@ -5934,7 +5934,7 @@ gregtech.machine.me.stocking_item.tooltip=Извлекает предметы н gregtech.machine.me_import_item_hatch.configs.tooltip=Держит 16 предметов в наличии tile.gt_explosive.breaking_tooltip=При обычной добыче взрывается, добудьте с SHIFT, чтобы забрать обратно gregtech.machine.me.stocking_fluid.tooltip=Извлекает жидкости непосредственно из сети ME -gregtech.machine.me.copy_paste.tooltip=ЛКС с Флешкой для копирования, ПКМ для применения +gregtech.machine.me.copy_paste.tooltip=ЛКМ с Флешкой для копирования, ПКМ для применения gregtech.machine.me.import_copy_settings=Настройки сохранены в Флешку gregtech.machine.me.import_paste_settings=Настройки из Флешки применены gregtech.machine.me.fluid_import.data_stick.name=§oНастройки ME Накопительного жидкостного люка @@ -5951,7 +5951,7 @@ tile.gt_explosive.lighting_tooltip=Нельзя зажечь с помощью gregtech.multiblock.power_substation.average_in_hover=Средний приход EU/t в внутреннее хранилище вашей Силовой Подстанции gregtech.machine.me.stocking_item.tooltip.2=Авто-вытягивание из МЕ сети будет автоматически пополнять первые 16 предметов в МЕ сети, обновляясь раз в 5 сек. gregtech.machine.me.stocking_fluid.tooltip.2=Авто-вытягивание из МЕ сети будет автоматически пополнять первые 16 слотов жидкости в МЕ сети, обновляясь раз в 5 сек. -gregtech.gui.config_slot.auto_pull_managed=§4Отключено:§7 Управляется Авто-вытягивание +gregtech.gui.config_slot.auto_pull_managed=§4Отключено:§7 Управляется Авто-вытягиванием tile.itnt.drops_tooltip=Намного мощнее, чем TNT. Все взорванные блоки выпадают как предметы gregtech.multiblock.cleanroom.low_tier=Уровень вольтажа слишком низкий, требуется %s или выше! gregtech.gui.item_auto_input.tooltip.disabled=Авто. ввод предметов выключен @@ -5981,3 +5981,25 @@ gregtech.machine.me.stocking_auto_pull_enabled=Авто-вытягивание cover.machine_controller.this_cover=§cЭто улучшение cover.machine_controller.cover_not_controllable=§cНет управляемых улучшений cover.machine_controller.control=Управление: +gregtech.material.ilmenite_slag=Ильменитовый шлак +gregtech.material.zirconia=Оксид циркония +gregtech.command.datafix.usage=Применение: /gregtech datafix +gregtech.material.decalcified_bauxite_sludge=Декальцинированный шлам боксита +gregtech.material.zircon=Циркон +gregtech.material.zirconium_tetrachloride=Тетрахлорид циркония +gregtech.material.hafnia=Гафнии +gregtech.material.hafnium_tetrachloride=Тетрахлорид гафния +gregtech.material.zircaloy_4=Циркалой-4 +gregtech.material.inconel_718=Инконель-718 +gregtech.material.bauxite_sludge=Шлам боксита +gregtech.material.bauxite_slurry=Бокситовая суспензия +gregtech.material.cracked_bauxite_slurry=Бокситовая суспензия прошедшая крекинг +gregtech.material.bauxite_slag=Бокситовый шлак +gregtech.command.datafix.bqu.usage=Применение: /gregtech datafix bqu [confirm] +gregtech.command.datafix.bqu.backup=Скопируйте ваши сохранения и папку настроек BQu, а после перезапустите с 'confirm' аргументов +gregtech.command.datafix.bqu.start=Начинается перенос BQu Базы данных Квестов... +gregtech.command.datafix.bqu.complete=Закончен перенос Базы данных Квестов +gregtech.command.datafix.bqu.failed=Перенос Базы данных Квестов был произведен с ошибкой. Восстановите ваши сохранения! +behavior.tricorder.mte_owner=Владелец: %s +behavior.tricorder.mte_owner_offline=Владелец Не в сети или Не в этом мире! +behavior.tricorder.mte_owner_null=Это установлено не игроком! diff --git a/src/main/resources/assets/gregtech/lang/uk_UA.lang b/src/main/resources/assets/gregtech/lang/uk_UA.lang new file mode 100644 index 00000000000..46667cc7db0 --- /dev/null +++ b/src/main/resources/assets/gregtech/lang/uk_UA.lang @@ -0,0 +1,1090 @@ + + +death.attack.heat= +death.attack.chemical=%s потрапив у хімічний інцидент +death.attack.electric=%s був убитий струмом +death.attack.radiation=%s тепер світиться від радості +death.attack.turbine=%s засунув голову в турбіну +death.attack.explosion=%s вибухнув +death.attack.explosion.player=%s вибухнув з допомогою %s +death.attack.heat.player=%s був зварений живцем з допомогою %s +death.attack.shovel=%s було викопано з допомогою %s +death.attack.axe=%s було порубано на шматки з допомогою %s +death.attack.hammer=%s був розчавлений з допомогою %s +death.attack.mallet=%s був забитий до смерті з допомогою %s +death.attack.mining_hammer=%s був помилково прийнятий за руду %s +death.attack.wrench=%s вдарив %s гайковим ключем! +death.attack.frost=%s досліджував кріогенні технології +death.attack.mortar=%s було розтерто на порох з допомогою %s +death.attack.wire_cutter=кабель життєзабезпечення %s був перерізаний %s +death.attack.knife=%s був ніжно вдарений ножем з допомогою %s +death.attack.drill_lv=%s був пробурений при напрузі 32V з допомогою %s +death.attack.wrench_iv=До планів %s несподівано був доданий гайковий ключ %s +death.attack.buzzsaw=%s був розпиляний з допомогою %s +enchantment.disjunction=Роз'єднання +gregtech.machine.steam_grinder.name=Парова дробарка +death.attack.drill_hv=%s був пробурений при напрузі 512V з допомогою %s +death.attack.drill_iv=%s був пробурений при напрузі 8192V з допомогою %s +death.attack.chainsaw_lv=%s був розпиляний з допомогою %s +gregtech.multiblock.steam.low_steam=Не вистачає пари для роботи! +gregtech.machine.steam_hatch.name=Паровий люк +gregtech.multiblock.steam.steam_stored=Пар: %s +gregtech.machine.steam.steam_hatch.tooltip=§eМожливі рідини: §fПар +gregtech.machine.steam_import_bus.name=Парова вхідна шина +gregtech.machine.steam_export_bus.name=Парови вихідна шина +gregtech.machine.steam_oven.name=Парова мультиплавильна піч +gregtech.multiblock.require_steam_parts=Потребує парові люки та шини! +gregtech.multiblock.steam_.duration_modifier=Потребує в §f1.5x §7більше часу, не залежить від кількості предметів. +gregtech.top.energy_production=Виготовляє +gregtech.top.transform_down=Знижувати +gregtech.top.transform_input=Вхід: +gregtech.top.transform_output=Вихід: +gregtech.top.steam_heating_up=Нагрівання +gregtech.top.steam_no_water=Немає води +gregtech.top.convert_fe=Конвертація §cFE§r -> §eEU§r +gregtech.top.invalid_structure=Структура незавершена +gregtech.top.obstructed_structure=Структура заблокована +gregtech.top.maintenance_fixed=Технічне обслуговування непотрібне +gregtech.top.maintenance.wrench=Труба ослаблена +gregtech.top.maintenance.screwdriver=Гвинти ослаблені +gregtech.top.maintenance.hard_hammer=Обшивка має вм'ятини +gregtech.top.maintenance.crowbar=Щось там не повинно бути +gregtech.top.mode.export=Експортуємо +gregtech.top.unit.items=Предмети +gregtech.top.unit.fluid_milibuckets=МЛ +gregtech.top.unit.fluid_buckets=Л +gregtech.top.block_drops=Випадає +gregtech.top.ld_pipe_incomplete=Незавершений трубопровід +gregtech.top.ld_pipe_length=Довжина: +option.gregtech.converter=Енергетичні конвертори +option.gregtech.diode=Діоди +option.gregtech.energy=Енергосховища +option.gregtech.block_lamp=Лампи +option.gregtech.multi_recipemap=Режими роботи +option.gregtech.multiblock=Багатоблочні конструкції +option.gregtech.primitive_pump=Примітивний насос +option.gregtech.transformer=Трансформатори +gregtech.waila.progress_idle=Холостий +gregtech.waila.progress_sec=Прогрес: %d s / %d s +gregtech.waila.progress_computation=Розрахунок: %s / %s +gregtech.multiblock.title=Схема багатоблокової структури +gregtech.multiblock.assembly_line.description=Складальна лінія - це велика багатоблочна конструкція, що складається з 5 до 16 "шматочків". Теоретично, це велика складальна машина, що використовується для виготовлення покращених компонентів. +gregtech.multiblock.fusion_reactor.uv.description=Термоядерний реактор модель 3 - це велика багатоблочна конструкція, яка використовується для злиття елементів у більш важкі. Може використовувати лише енергетичні люки типу UV. З кожним люком буфер енергії збільшується на 40 млн EU, і має максимум у 640 млн EU. +gregtech.multiblock.fusion_reactor.heat=Тепло: %s +metaitem.coin.doge.name=Догкоін +metaitem.shape.empty.name=Порожня форма +metaitem.dynamite.name=Динаміт +metaitem.power_unit.lv.name=Блок живлення (LV) +metaitem.power_unit.mv.name=Блок живлення (MV) +metaitem.power_unit.hv.name=Блок живлення (HV) +metaitem.nano_saber.name=Наношабля +metaitem.nano_saber.tooltip=Ryujin no ken wo kurae! +metaitem.shape.mold.plate.tooltip=Форма для виготовлення пластин +metaitem.shape.mold.plate.name=Форма (Пластина) +metaitem.shape.mold.gear.tooltip=Форма для виготовлення шестерень +metaitem.shape.mold.gear.name=Форма (Шестерня) +metaitem.shape.mold.bottle.name=Форма (Пляшка) +metaitem.shape.mold.bottle.tooltip=Форма для виготовлення пляшок +metaitem.shape.mold.ingot.name=Форма (Злиток) +metaitem.shape.mold.ball.name=Форма (Куля) +metaitem.shape.mold.ball.tooltip=Форма для виготовлення куль +metaitem.shape.mold.block.name=Форма (Блок) +metaitem.shape.mold.nugget.name=Форма (самородки) +metaitem.shape.mold.cylinder.name=Форма (Циліндр) +metaitem.shape.mold.name.name=Форма (Назва) +metaitem.shape.mold.cylinder.tooltip=Форма для формування циліндрів +metaitem.shape.mold.gear.small.tooltip=Форма для виготовлення малих шестерень +metaitem.shape.mold.rotor.name=Форма (Ротор) +metaitem.shape.extruder.plate.name=Форма екструдера (пластина) +metaitem.shape.extruder.plate.tooltip=Форма екструдера для виготовлення пластин +metaitem.shape.extruder.bolt.name=Форма екструдера (Болт) +metaitem.shape.extruder.ring.tooltip=Форма екструдера для виготовлення кілець +metaitem.shape.extruder.cell.name=Форма екструдера (Капсула) +metaitem.shape.extruder.casing.name=Форма екструдера (Корпус) +metaitem.shape.extruder.pipe.tiny.name=Форма екструдера (Крихітна трубка) +metaitem.shape.extruder.pipe.small.name=Форма екструдера (маленька труба) +metaitem.shape.extruder.pipe.normal.tooltip=Форма екструдера для виготовлення труб +metaitem.shape.extruder.pipe.large.name=Форма екструдера (Велика труба) +metaitem.shape.extruder.pipe.huge.tooltip=Форма екструдера для виготовлення величезних труб +metaitem.shape.extruder.block.name=Форма екструдера (Блок) +metaitem.shape.extruder.block.tooltip=Форма екструдера для виготовлення блоків +metaitem.shape.extruder.shovel.tooltip=Форма екструдера для виготовлення лопат +metaitem.shape.extruder.hoe.name=Форма екструдера (Головка мотики) +metaitem.shape.extruder.hoe.tooltip=Форма екструдера для виготовлення мотик +metaitem.shape.extruder.saw.name=Форма екструдера (Пильний диск) +metaitem.shape.extruder.saw.tooltip=Форма екструдера для виготовлення пилок +metaitem.shape.extruder.gear.name=Форма екструдера (Шестерня) +death.attack.pickaxe=мізки %s було вибито з допомогою %s +death.attack.hoe=%s був проораний з допомогою %s +death.attack.wrench_hv=Труби %s були послаблені з допомогою %s +gregtech.multiblock.steam_grinder.description=Багатоблочний подрібнювач парової епохи. Для побудови потрібно щонайменше 14 бронзових корпусів. Не може використовувати звичайні вхідні/вихідні шини, або рідині люки. Може використовувати тільки парові люки. +gregtech.multiblock.steam_oven.description=Мультиплавильна піч парової епохи. Для побудови потрібно щонайменше 6 бронзових корпусів. Не може використовувати звичайні вхідні/вихідні шини, або рідині люки. Може використовувати тільки парові люки. Паровий люк має бути на першому рівні, та не має бути більше одного. +gregtech.top.working_disabled=Робота призупинена +gregtech.top.transform_up=Підвищує +gregtech.top.convert_eu=Конвертація §eEU§r -> §cFE§r +gregtech.top.valid_structure=Структура завершена +gregtech.top.maintenance.wire_cutter=Дроти перегоріли +gregtech.top.mode.import=Імпортуємо +gregtech.top.ld_pipe_no_network=Не знайдено жодного трубопроводу +gregtech.top.ld_pipe_connected=Трубопровід під'єднано +gregtech.top.ld_pipe_output=Вихід +gregtech.top.ld_pipe_input_endpoint=Кінцева точка входу: +gregtech.top.ld_pipe_output_endpoint=Кінцева точка виходу: +option.gregtech.controllable=Керовані машини +option.gregtech.maintenance=Проблеми з технічним обслуговуванням +option.gregtech.recipe_logic=Рецепти +option.gregtech.steam_boiler=Парові котли +gregtech.waila.energy_stored=Енергія: %d EU / %d EU +death.attack.spade=%s був розкопаний з допомогою %s +death.attack.screwdriver_lv=Гвинти %s були відкручені з допомогою %s +gregtech.top.energy_consumption=Використовує +gregtech.top.maintenance_broken=Технічне обслуговування потрібне +gregtech.top.maintenance.soft_mallet=Щось заклинило +gregtech.top.primitive_pump_production=Виробництво: +gregtech.top.filter.label=Фільтр: +gregtech.top.link_cover.color=Колір: +gregtech.top.ld_pipe_input=Вхід +option.gregtech.block_ore=Рудні блоки +option.gregtech.workable=Механізми +gregtech.waila.progress_tick=Прогрес: %d t / %d t +gregtech.machine.steam_bus.tooltip=Не працює з не паровими багатоблочними конструкціями +metaitem.shape.mold.name.tooltip=Форма для іменування предметів у формувальному пресі (перейменувати форму за допомогою ковадла) +metaitem.shape.mold.gear.small.name=Форма (Мала шестерня) +metaitem.shape.mold.rotor.tooltip=Форма для виготовлення роторів +metaitem.shape.extruder.rod.name=Форма екструдера (Cтержень) +metaitem.shape.extruder.ingot.tooltip=Форма екструдера для, зачекайте, хіба ми не можемо просто використовувати піч? +metaitem.shape.extruder.wire.tooltip=Форма екструдера для виготовлення дротів +metaitem.shape.extruder.pipe.tiny.tooltip=Форма екструдера для виготовлення крихітних труб +metaitem.shape.extruder.pipe.small.tooltip=Форма екструдера для виготовлення малих труб +metaitem.shape.extruder.pipe.normal.name=Форма екструдера (Труба) +metaitem.shape.extruder.pipe.large.tooltip=Форма екструдера для виготовлення великих труб +metaitem.shape.extruder.pipe.huge.name=Форма екструдера (Величезна труба) +metaitem.shape.extruder.sword.tooltip=Форма екструдера для виготовлення мечів +metaitem.shape.extruder.pickaxe.name=Форма екструдера (Головка кайла) +metaitem.shape.extruder.pickaxe.tooltip=Форма екструдера для виготовлення кайла +gregtech.multiblock.primitive_blast_furnace.bronze.description=Примітивна доменна піч (ПДП) це багатоблочна структура, що використовуєть для виготовлення сталі на початку гри. Хоча вона і не дуже швидка, вона забезпечить вам сталь для перших кроків. +gregtech.multiblock.coke_oven.description=Коксова піч - це багатоблочна конструкція, яка використовується для отриманя коксу та креозоту на початку гри. Вона не потребує палива і має внутрішній резервуар на 32 відра креозоту. Доступ до інвентарю можна отримати через люк коксової печі. +gregtech.multiblock.vacuum_freezer.description=Вакуумна морозильна камера - це багатоблочна структура, яка в більшості випадків використовується для охолоджування гарячих злитків до їхньоніх звичайних температур. Також може заморожувати інші речовини, до прикладу вода. +gregtech.multiblock.implosion_compressor.description=Імплохійний компресор - це багатоблочна структура яка використовує вибухівки щоб перетворити пил на відповідні дорогоцінні камені. +gregtech.multiblock.pyrolyse_oven.description=Піролізна піч - це багатоблочна конструкція, яка використовується для перетворення колод на деревне вугілля та креозот, або золу та тяжку нафту. +gregtech.multiblock.cracker.description=Крекінгова установка - це багатоблочна конструкція, яка використовується для перетворення легкого та важкаго палина на їхні потріскані варіанти. +gregtech.multiblock.large_combustion_engine.description=Великий двигун внутрішнього згоряння - це багатоблочна конструкція, яка діє як генератор внетрішнього згоряння для вироблення EV енергіє. +metaitem.shape.mold.anvil.name=Форма (Ковадло) +metaitem.shape.mold.anvil.tooltip=Форма для формування ковадла +metaitem.shape.extruder.rod.tooltip=Форма екструдера для виготовлення стержнів +metaitem.shape.extruder.bolt.tooltip=Форма екструдера для виготовлення болтів +metaitem.shape.extruder.ring.name=Форма екструдера (Кільце) +metaitem.shape.extruder.cell.tooltip=Форма екструдера для виготовлення капсул +metaitem.shape.extruder.ingot.name=Форма екструдера (злиток) +metaitem.shape.extruder.wire.name=Форма екструдера (Дріт) +metaitem.shape.extruder.casing.tooltip=Форма екструдера для виготовлення корпусів +metaitem.shape.extruder.sword.name=Форма екструдера (Лезо меча) +metaitem.shape.extruder.shovel.name=Форма екструдера (Головка лопати) +metaitem.shape.extruder.axe.name=Форма екструдера (Головка сокири) +metaitem.shape.extruder.axe.tooltip=Форма екструдера для виготовлення сокир +death.attack.file=%s був заокруглений до сметрі з допомогою %s +death.attack.drill_ev=%s був пробурений при напрузі 2048V з допомогою %s +death.attack.butchery_knife=%s був перетворений у фарш з допомогою %s +death.attack.drill_mv=%s був пробурений при напрузі 128V з допомогою %s +death.attack.wrench_lv=Труби %s були ослаблені з допомогою %s +gregtech.multiblock.large_turbine.description=Великі турбіни - це багатоблочна конструкція яка генерую електроенергію з пари, газів, і плазми шляхом обертання ротора турбіни. Вихід енергії залежить від ефективності ротора та поточної швидкості турбіни. У центрі конструкції використовуються корпуси коробки передач. +metaitem.shape.extruder.hammer.name=Форма екструдера (Молоткова головка) +metaitem.shape.extruder.hammer.tooltip=Форма екструдера для виготовлення молотків +metaitem.shape.extruder.file.tooltip=Форма екструдера для воготовлення напилка +metaitem.shape.extruder.file.name=Форма екструдера (Головка напилка) +metaitem.shape.extruder.gear.tooltip=Форма екструдера для виготовлення шестерень +metaitem.shape.extruder.bottle.name=Форма екструдера (Пляшка) +death.attack.scythe=Душу %s було викрадено з допомогою %s +gregtech.multiblock.extreme_combustion_engine.description=Екстремальний двигун внутрішнього згорання - це багатоблочна конструкція, яка діє як генератор згоряння для IV рівня напруги. +gregtech.multiblock.distillation_tower.description=Дистиляціна вежа - це багатоблочана конструкція, яка використовується для перегонки різних видів нафти та деяких побічних продуктів. Кожен шар починаючи з другого повинен мати рівно один вихідний люк. Нижній шар може виводити продукти і вводити рідини в будь-якому положенні. +gregtech.multiblock.electric_blast_furnace.description=Електрична доменна піч (ЕДП) - це багатоблочна конструкція, яка використовується для виплавки сплавів, готових металів та переробки сирих. Вона необхідна для отримання високоякісних сплавів і металів, таких як алюміній, нержавіюча сталь, титат і сплав наквади. +gregtech.multiblock.multi_furnace.description=Мультиплавильна піч - це багатоблочна конструкція, яка використовується для виплавлення великої кількості предметів. Різні рівні котушок забезпечують підвищення швидкості та енергоефективності. 32 - це базова кількість предметів, що виплавляються за операцію, яке можна збільшити, використовуючи котушки вищого рівня. +gregtech.multiblock.large_boiler.description=Великі котли - це багатоблочна структура, яка виробляє пар за допомогою енергії та води. Джерелом енергії є будь-яке тверде паливо з певним часом горіння або дизельне/напіврідке паливо. Може бути сповільнене заради зменшення кількості пари та потрібного палива. +gregtech.multiblock.fusion_reactor.luv.description=Термоядерний реактор модель 1 - це велика багатоблочна конструкція, яка використовується для злиття елементів у більш важкі. Може використовувати лише енергетичні люки типу LuV, ZPM, і UV. З кожним люком буфер енергії збільшується на 10 млн EU, і має максимум у 160 млн EU. +gregtech.multiblock.fusion_reactor.zpm.description=Термоядерний реактор модель 2 - це велика багатоблочна конструкція, яка використовується для злиття елементів у більш важкі. Може використовувати лише енергетичні люки типу ZPM і UV. З кожним люком буфер енергії збільшується на 20 млн EU, і має максимум у 320 млн EU. +gregtech.multiblock.large_chemical_reactor.description=Великий хімічний реактор виконує хімічні реакції зі 100%% енергоефективністю. Розгін збільшує швидкість і евергію в 4 рази. Багатоблочна конструкція потребує рівно 1 купронікелевий блок, який повинен бути розміщений поруч з копрусом ПТФЕ труби, розташованим в центрі. +metaitem.credit.platinum.tooltip=512 Кредитів +metaitem.credit.osmium.tooltip=4096 Кредити +metaitem.credit.naquadah.name=Наквадовий Кредит +metaitem.coin.gold.ancient.name=Старовинна золота монета +metaitem.coin.gold.ancient.tooltip=Знайдено в стародавніх руїнах +metaitem.power_unit.iv.name=Блок живлення (IV) +metaitem.shape.mold.casing.tooltip=Форма для виготовлення корпусів +metaitem.shape.mold.block.tooltip=Форма для виготовлення блоків +gregtech.multiblock.primitive_water_pump.description=Примітивна водяна помпа - це багатоблочна конструкція до парової ери, яка збирає води один раз на секунду, залежно в якому біома. Вона може використовувати помповий, ULV або LV вихідний люк, збільшуючи кількість води на рівень. Розраховується за формулою: коефіцієнь біома * множник люка +metaitem.credit.osmium.name=Осмійські Кредит +metaitem.credit.naquadah.tooltip=32768 Кредити +metaitem.credit.neutronium.name=Нейтронний кредит +metaitem.credit.neutronium.tooltip=262144 Кредити +metaitem.coin.chocolate.name=Шоколадна монетка +metaitem.coin.chocolate.tooltip=Загорнутий у золото +metaitem.shape.empty.tooltip=Сировина для виготовлення прес-форм та екструзійних форм +metaitem.power_unit.ev.name=Блок живлення (EV) +metaitem.shape.mold.credit.name=Форма (Монета) +metaitem.shape.mold.credit.tooltip=Безпечна форма для виготовлення монет (не загуби!) +metaitem.shape.mold.casing.name=Форма (Корпус) +metaitem.shape.mold.ingot.tooltip=Форма для виготовлення злитків +metaitem.shape.mold.nugget.tooltip=Форма для виготовлення самородків +gregtech.multiblock.primitive_water_pump.extra2=Множники люків:/n Люк насоса: 1x/n Вихідний люк ULV: 2x/n Вихідний люк LV: 4x/n/nПід час дощу в біомі насоса загальне виробництво води збільшується на 50%%. +gregtech.multiblock.processing_array.description=Оброблювальний масив об'єднує до 16 одноблочних машин в один мультиблок, що значно полегшує автоматизацію. +item.invalid.name=Некоректний предмет +fluid.empty=Порожньо +gregtech.tooltip.hold_shift=Утримуйте SHIFT, щоб дізнатися більше +gregtech.tooltip.hold_ctrl=Утримуйте CTRL, щоб дізнатися більше +gregtech.tooltip.tool_fluid_hold_shift=Утримуйте SHIFT, щоб відобразити інформацію про рідину та інструмент +metaitem.generic.fluid_container.tooltip=%,d/%,dЛ %s +metaitem.generic.electric_item.tooltip=%,d/%,d EU - Рівень %s +metaitem.electric.discharge_mode.enabled=§eУвімкнено режим розрядки +metaitem.dust.tooltip.purify=Киньте в Казан, щоб отримати чистий пил +metaitem.int_circuit.configuration=§aНалаштоване значення: §f%d§7 +metaitem.credit.copper.tooltip=0.125 Кредитів +metaitem.credit.silver.name=Срібний кредит +metaitem.credit.silver.tooltip=8 кредитів +metaitem.credit.gold.name=Золотий кредит +metaitem.credit.gold.tooltip=64 Кредитів +metaitem.credit.platinum.name=Платиновий кредит +metaitem.shape.extruder.rod_long.tooltip=Форма екструдера для виготовлення довгих стержнів +metaitem.shape.extruder.rod_long.name=Форма екструдера (Довгий стержень) +metaitem.spray.empty.name=Балончик (порожній) +metaitem.spray.empty.tooltip=Можна наповнювати спреями різних кольорів + +# Fluid Cells +metaitem.fluid_cell.empty=Порожньо +metaitem.fluid_cell.name=%s Капсула +metaitem.fluid_cell.universal.name=%s Універсальна капсула +metaitem.large_fluid_cell.steel.name=%s Сталева капсула +metaitem.fluid_cell.glass_vial.name=%s Пробірка +metaitem.large_fluid_cell.titanium.name=%s Титанова капсула +metaitem.spray.solvent.name=Балончик (розчинник) +metaitem.spray.can.dyes.orange.name=Балончик (помаранчевий) +metaitem.spray.can.dyes.purple.name=Балончик (фіолетовий) +metaitem.tool.matches.name=Сірник +metaitem.tool.lighter.platinum.name=Платинова запальничка +metaitem.battery.hull.hv.name=Великий корпус батареї +metaitem.battery.hull.ev.name=Малий корпус ванадієвої батареї +metaitem.battery.hull.zpm.name=Корпус середньої наквадівої батареї +metaitem.battery.hull.zpm.tooltip=Порожній §fZPM §7корпус батареї +metaitem.battery.hull.uv.name=Корпус великої наквадівої батареї +metaitem.battery.hull.uv.tooltip=Порожній §3UV §7корпус батареї +metaitem.battery.charge_time=§aЗберігає %,d%s електроенергії +metaitem.battery.charge_unit.minute=хв +metaitem.battery.charge_unit.hour=год +metaitem.battery.re.ulv.tantalum.name=Танталовий конденсатор +metaitem.battery.re.lv.cadmium.name=Маленька кадмієва батарея +metaitem.battery.re.lv.cadmium.tooltip=Багаторазова батарея +metaitem.battery.re.lv.lithium.name=Маленька літієва батарея +metaitem.battery.re.mv.cadmium.tooltip=Багаторазова батарея +metaitem.battery.re.mv.sodium.tooltip=Багаторазова батарея +metaitem.battery.re.hv.cadmium.name=Велика кадмієва батарея +metaitem.battery.re.hv.cadmium.tooltip=Багаторазова батарея +metaitem.battery.re.hv.lithium.name=Велика літієва батарея +metaitem.battery.re.hv.sodium.name=Велика натрієва батарея +metaitem.battery.ev.vanadium.name=Маленька ванадієва батарея +metaitem.battery.iv.vanadium.name=Середня ванадієва батарея +metaitem.battery.luv.vanadium.name=Велика ванадієва батарея +metaitem.battery.luv.vanadium.tooltip=Багаторазова батарея +metaitem.battery.uv.naquadria.name=Велика наквадрієва батарея +metaitem.energy_crystal.name=Енергетичний кристал +metaitem.energy.lapotronic_orb.tooltip=Багаторазова батарея +metaitem.energy.lapotronic_orb_cluster.name=Лапотронний кластер енергетичних куль +metaitem.energy.module.name=Енергетичний модуль +metaitem.energy.cluster.name=Енергетичний кластер +metaitem.max.battery.tooltip=Зарядіть це, щоб виграти Minecraft +metaitem.electric.motor.luv.name=Електродвигун LuV +metaitem.electric.motor.zpm.name=Електродвигун ZPM +metaitem.electric.motor.uev.name=Електродвигун UEV +metaitem.electric.motor.uxv.name=Електродвигун UXV +metaitem.electric.pump.ev.name=Електронасос EV +metaitem.electric.pump.iv.name=Електронасос IV +metaitem.electric.pump.uev.name=Електронасос UEV +metaitem.electric.pump.uxv.name=Електронасос UXV +metaitem.fluid.regulator.lv.name=Регулятор рідини LV +metaitem.fluid.regulator.mv.name=Регулятор рідини MV +metaitem.fluid.regulator.luv.name=Регулятор рідини LuV +metaitem.fluid.regulator.uv.name=Регулятор рідини UV +metaitem.conveyor.module.lv.name=Конвеєрний модуль LV +metaitem.conveyor.module.mv.name=Конвеєрний модуль MV +metaitem.conveyor.module.ev.name=Конвеєрний модуль EV +metaitem.conveyor.module.zpm.name=Конвеєрний модуль ZPM +metaitem.conveyor.module.uhv.name=Конвеєрний модуль UHV +metaitem.conveyor.module.uiv.name=Конвеєрний модуль UIV +metaitem.conveyor.module.uxv.name=Конвеєрний модуль UXV +metaitem.conveyor.module.opv.name=Конвеєрний модуль OpV +metaitem.electric.piston.lv.name=Електричний поршень LV +metaitem.electric.piston.mv.name=Електричний поршень MV +metaitem.electric.piston.ev.name=Електричний поршень EV +metaitem.electric.piston.iv.name=Електричний поршень IV +metaitem.electric.piston.luv.name=Електричний поршень LuV +metaitem.electric.piston.zpm.name=Електричний поршень ZPM +metaitem.electric.piston.uhv.name=Електричний поршень UHV +metaitem.electric.piston.uev.name=Електричний поршень UEV +metaitem.electric.piston.uiv.name=Електричний поршень UIV +metaitem.electric.piston.uxv.name=Електричний поршень UXV +metaitem.electric.piston.opv.name=Електричний поршень OpV +metaitem.robot.arm.mv.name=Роботизована рука MV +metaitem.robot.arm.hv.name=Роботизована рука HV +metaitem.robot.arm.iv.name=Роботизована рука IV +metaitem.robot.arm.luv.name=Роботизована рука LuV +metaitem.robot.arm.uv.name=Роботизована рука UV +metaitem.robot.arm.uhv.name=Роботизована рука UHV +metaitem.robot.arm.uiv.name=Роботизована рука UIV +metaitem.robot.arm.opv.name=Роботизована рука OpV +metaitem.field.generator.mv.name=Генератор поля MV +metaitem.field.generator.ev.name=Генератор поля EV +metaitem.field.generator.iv.name=Генератор поля IV +metaitem.field.generator.luv.name=Генератор поля LuV +metaitem.field.generator.zpm.name=Генератор поля ZPM +metaitem.field.generator.uhv.name=Генератор поля UHV +metaitem.field.generator.uev.name=Генератор поля UEV +metaitem.field.generator.uiv.name=Генератор поля UIV +metaitem.emitter.mv.name=Випромінювач MV +metaitem.emitter.ev.name=Випромінювач EV +metaitem.emitter.iv.name=Випромінювач IV +metaitem.emitter.luv.name=Випромінювач LuV +metaitem.emitter.zpm.name=Випромінювач ZPM +metaitem.emitter.uev.name=Випромінювач UEV +metaitem.emitter.uxv.name=Випромінювач UXV +metaitem.emitter.opv.name=Випромінювач OpV +metaitem.sensor.lv.name=Сенсор LV +metaitem.sensor.mv.name=Сенсор MV +metaitem.sensor.ev.name=Сенсор EV +metaitem.sensor.iv.name=Сенсор IV +metaitem.sensor.luv.name=Сенсор LuV +metaitem.sensor.zpm.name=Сенсор ZPM +metaitem.sensor.uv.name=Сенсор UV +metaitem.sensor.uhv.name=Сенсор UHV +metaitem.sensor.uev.name=Сенсор UEV +metaitem.sensor.uiv.name=Сенсор UIV +metaitem.sensor.opv.name=Сенсор OpV +metaitem.tool.datastick.name=Накопичувач даних +metaitem.tool.datastick.tooltip=Сховище для простих даних +metaitem.tool.dataorb.name=Сфера даних +metaitem.tool.datamodule.name=Модуль даних +metaitem.circuit.integrated.name=Запрограмована схема +metaitem.circuit.integrated.tooltip=ПКМ щоб відкрити графічний інтерфейс/n/nShift-ПКМ по машині/nзі схемою в руці/nщоб запрограмувати машину./n +metaitem.circuit.integrated.gui=Конфігурація схеми +item.glass.lens=Скляна лінза (Біла) +metaitem.glass_lens.orange.name=Скляна лінза (Помаранчева) +metaitem.glass_lens.magenta.name=Скляна лінза (Пурпурна) +metaitem.glass_lens.pink.name=Скляна лінза (Рожева) +metaitem.glass_lens.yellow.name=Скляна лінза (Жовта) +metaitem.glass_lens.light_gray.name=Скляна лінза (Світло-сіра) +metaitem.glass_lens.cyan.name=Скляна лінза (Блакитна) +metaitem.glass_lens.purple.name=Скляна лінза (Фіолетова) +metaitem.glass_lens.green.name=Скляна лінза (Зелена) +metaitem.glass_lens.red.name=Скляна лінза (Червона) +metaitem.glass_lens.black.name=Скляна лінза (Чорна) + +#CIRCUITS LOCALIZATION +metaitem.boule.silicon.name=Монокристалічний кремній +metaitem.boule.phosphorus.tooltip=Необроблена схема +metaitem.boule.naquadah.tooltip=Необроблена схема +metaitem.boule.neutronium.tooltip=Необроблена схема +metaitem.wafer.phosphorus.tooltip=Необроблена схема +metaitem.board.epoxy.tooltip=Покращена плата +metaitem.board.fiber_reinforced.tooltip=Екстремально гарна плата +metaitem.board.multilayer.fiber_reinforced.name=Багато рівнева посилена волокном друкована плата +metaitem.board.fiber_reinforced.name=Посилена волокном друкована плата +metaitem.board.multilayer.fiber_reinforced.tooltip=Відмінна плата +metaitem.board.wetware.name=Друкована плата зі системою життєзабезпечення +metaitem.board.wetware.tooltip=Плата, яка зберігає життя +metaitem.circuit_board.basic.name=Друкована плата +metaitem.circuit_board.basic.tooltip=Базова друкована плата +metaitem.circuit_board.good.name=Гарна друкована плата +metaitem.circuit_board.good.tooltip=Гарна друкована плата +metaitem.circuit_board.plastic.name=Пластикова друкована плата +metaitem.circuit_board.plastic.tooltip=Гарна друкована плата +metaitem.circuit_board.advanced.name=Покращена друкована плата +metaitem.circuit_board.advanced.tooltip=Покращена друкована плата +metaitem.circuit_board.extreme.name=Екстремально гарна друкована плата +metaitem.circuit_board.extreme.tooltip=Більш покращена друкована плата +metaitem.circuit_board.elite.tooltip=Відмінна друкована плата +metaitem.circuit.vacuum_tube.name=Вакумна лампа +metaitem.component.diode.name=Діод +metaitem.component.diode.tooltip=Базовий електричний компонент +metaitem.component.resistor.name=Резистор +metaitem.component.resistor.tooltip=Базовий електричний компонент +metaitem.component.transistor.name=Транзистор +metaitem.component.capacitor.name=Конденсатор +metaitem.component.glass.tube.name=Скляна трубка +metaitem.component.inductor.name=Індуктор +metaitem.component.inductor.tooltip=Мала котушка +metaitem.component.smd.diode.name=SMD Діод +metaitem.component.smd.capacitor.name=SMD Конденсатор +metaitem.component.smd.capacitor.tooltip=Електричний компонент +metaitem.component.smd.transistor.tooltip=Електричний компонент +metaitem.component.smd.resistor.name=SMD Резистор +metaitem.component.smd.resistor.tooltip=Електричний компонент +metaitem.component.smd.inductor.name=SMD Індуктор +metaitem.component.smd.inductor.tooltip=Електричний компонент +metaitem.component.advanced_smd.diode.name=Покращений SMD Діод +metaitem.component.advanced_smd.capacitor.name=Покращений SMD Конденсатор +metaitem.component.advanced_smd.capacitor.tooltip=Покращений електричний компонент +metaitem.component.advanced_smd.transistor.name=Покращений SMD Транзистор +metaitem.component.advanced_smd.resistor.name=Покращений SMD Резистор +metaitem.component.advanced_smd.resistor.tooltip=Покращений електричний компонент +metaitem.component.advanced_smd.inductor.name=Покращений SMD Індуктор +metaitem.component.advanced_smd.inductor.tooltip=Покращений електричний компонент +metaitem.wafer.advanced_system_on_chip.name=Пластина ASoC +metaitem.wafer.advanced_system_on_chip.tooltip=Необроблена покращена схема +metaitem.wafer.central_processing_unit.tooltip=Необроблений модуль обробки +metaitem.credit.cupronickel.name=Купронікелевий кредит +metaitem.credit.cupronickel.tooltip=1 Кредит +metaitem.electric.discharge_mode.tooltip=Використовуйте затиснувши кнопку Присісти для перемикання режиму розрядки +metaitem.crushed.tooltip.purify=Киньте в казан, щоб отримати очищену руду +metaitem.shape.extruder.rotor.name=Форма екструдера (Ротор) +metaitem.shape.extruder.rotor.tooltip=Форма екструдера для виготовлення роторів +metaitem.credit.copper.name=Мідний кредит +gregtech.multiblock.primitive_water_pump.extra1=Коефіцієнт біома:/n Океан, річка: 1000 л/с/n Болото: 800 л/с/n Джунглі: 350 л/с/n Засніжені: 300 л/с/n Рівнина, ліс: 250 л/с/n Тайга: 175 л/с/n Пляж: 170 Л/с/n Інше: 100 л/с +gregtech.multiblock.advanced_processing_array.description=Оброблювальний масив об'єднує до 64 одноблочних машин в один мультиблок, що значно полегшує автоматизацію. +gregtech.tooltip.fluid_pipe_hold_shift=Утримуйте SHIFT, щоб відобразити інформацію про рідину +metaitem.electric.discharge_mode.disabled=§eВимкнено режим розряду +metaitem.shape.extruder.bottle.tooltip=Форма екструдера для виготовлення пляшок +metaitem.shape.extruder.gear_small.tooltip=Форма екструдера для виготовлення малих шестерень +metaitem.shape.extruder.foil.name=Форма екструдера (Фольга) +metaitem.shape.extruder.gear_small.name=Форма екструдера (Мала шестерня) +metaitem.shape.extruder.foil.tooltip=Форма екструдера для виготовлення фольги з неметалів +metaitem.plant.ball.name=Кулька біомаси +metaitem.fluid_cell.universal.empty=Порожньо +metaitem.large_fluid_cell.aluminium.name=%s Алюмінієва капсула +metaitem.large_fluid_cell.stainless_steel.name=%s Капсула з нержавіючої сталі +metaitem.large_fluid_cell.tungstensteel.name=%s Вольфрамова капсула +metaitem.spray.can.dyes.white.name=Балончик (білий) +metaitem.spray.can.dyes.magenta.name=Балончик (пурпурний) +metaitem.spray.can.dyes.yellow.name=Балончик (жовтий) +metaitem.spray.can.dyes.lime.name=Балончик (лайм) +metaitem.spray.can.dyes.pink.name=Балончик (рожевий) +metaitem.spray.can.dyes.gray.name=Балончик (сірий) +metaitem.spray.can.dyes.silver.name=Балончик (світло-сірий) +metaitem.spray.can.dyes.cyan.name=Балончик (блакитний) +metaitem.spray.can.dyes.blue.name=Балончик (синій) +metaitem.spray.can.dyes.brown.name=Балончик (коричневий) +metaitem.spray.can.dyes.green.name=Балончик (зелений) +metaitem.spray.can.dyes.red.name=Балончик (червоний) +metaitem.spray.can.dyes.black.name=Балончик (чорний) +metaitem.tool.matchbox.name=Сірникова коробка +metaitem.tool.matchbox.tooltip=Це не автомобіль +metaitem.tool.lighter.platinum.tooltip=На ньому викарбувано відомого майстра жартів +metaitem.battery.hull.lv.tooltip=Порожній §7LV §7корпус батареї +metaitem.battery.hull.mv.tooltip=Порожній §bMV §7корпус батареї +metaitem.tool.lighter.invar.name=Запальничка + + +metaitem.battery.hull.lv.name=Малий корпус батареї +metaitem.battery.hull.mv.name=Середній корпус батареї +metaitem.battery.hull.hv.tooltip=Великий §6HV §7корпус батареї +metaitem.battery.hull.iv.name=Корпус середньої ванадієвої батареї +metaitem.battery.hull.iv.tooltip=Порожній §1IV §7корпус батареї +metaitem.battery.re.ulv.tantalum.tooltip=Багаторазова батарея +metaitem.battery.re.lv.sodium.tooltip=Багаторазова батарея +metaitem.battery.re.mv.cadmium.name=Середня кадмієва батарея +metaitem.battery.re.hv.lithium.tooltip=Багаторазова батарея +metaitem.battery.re.hv.sodium.tooltip=Багаторазова батарея +metaitem.battery.ev.vanadium.tooltip=Багаторазова батарея +metaitem.battery.iv.vanadium.tooltip=Багаторазова батарея +metaitem.battery.zpm.naquadria.name=Середня наквадрієва батарея +metaitem.battery.zpm.naquadria.tooltip=Багаторазова батарея +metaitem.battery.hull.ev.tooltip=Порожній §5EV §7корпус батареї +metaitem.battery.hull.luv.name=Великий ванадієвий корпус батареї +metaitem.battery.hull.luv.tooltip=Порожній §dLuV §7корпус батареї +metaitem.battery.charge_unit.second=сек +metaitem.battery.re.lv.lithium.tooltip=Багаторазова батарея +metaitem.battery.re.lv.sodium.name=Маленька натрієва батарея +metaitem.battery.re.mv.lithium.name=Середня літієва батарея +metaitem.battery.re.mv.lithium.tooltip=Багаторазова батарея +metaitem.battery.re.mv.sodium.name=Середня натрієва батарея +metaitem.battery.uv.naquadria.tooltip=Багаторазова батарея +metaitem.energy_crystal.tooltip=Багаторазова батарея +metaitem.lapotron_crystal.name=Лапотроний кристал +metaitem.lapotron_crystal.tooltip=Багаторазова батарея +metaitem.energy.lapotronic_orb_cluster.tooltip=Багаторазова батарея +metaitem.zpm.name=Модуль нульової точки +metaitem.energy.cluster.tooltip=Багаторазова батарея +metaitem.max.battery.name=Досконала батарея +metaitem.electric.motor.iv.name=Електродвигун IV +metaitem.energy.lapotronic_orb.name=Лапотронічна енергетична куля +metaitem.energy.module.tooltip=Багаторазова батарея +metaitem.fluid.regulator.tooltip=Обмежує §fРідини§7 до певної кількості, працює як §fПокращення§7. +metaitem.electric.pump.tooltip=Переводить §fРідини§7 з одинаковою швидкістю, працює як §fПокращення§7. +metaitem.electric.motor.lv.name=Електродвигун LV +metaitem.electric.motor.mv.name=Електродвигун MV +metaitem.electric.motor.hv.name=Електродвигун HV +metaitem.electric.motor.ev.name=Електродвигун EV +metaitem.electric.motor.uhv.name=Електродвигун UHV +metaitem.electric.motor.uiv.name=Електродвигун UIV +metaitem.electric.motor.opv.name=Електродвигун OpV +metaitem.electric.pump.lv.name=Електронасос LV +metaitem.electric.pump.hv.name=Електронасос HV +metaitem.electric.pump.uhv.name=Електронасос UHV +metaitem.fluid.regulator.hv.name=Регулятор рідини HV +metaitem.fluid.regulator.ev.name=Регулятор рідини EV +metaitem.conveyor.module.hv.name=Конвеєрний модуль HV +metaitem.conveyor.module.iv.name=Конвеєрний модуль IV +metaitem.conveyor.module.uev.name=Конвеєрний модуль UEV +metaitem.conveyor.module.tooltip=Передає §fПредмети§7 з одинаковою швидкістю, працює як §fПокращення§7. +metaitem.electric.piston.hv.name=Електричний поршень HV +metaitem.electric.piston.uv.name=Електричний поршень UV +metaitem.robot.arm.uxv.name=Роботизована рука UXV +metaitem.field.generator.lv.name=Генератор поля LV +metaitem.field.generator.uxv.name=Генератор поля UXV +metaitem.emitter.lv.name=Випромінювач LV +metaitem.electric.motor.uv.name=Електродвигун UV +metaitem.electric.pump.mv.name=Електронасос MV +metaitem.electric.pump.luv.name=Електронасос LuV +metaitem.electric.pump.zpm.name=Електронасос ZPM +metaitem.electric.pump.uv.name=Електронасос UV +metaitem.electric.pump.uiv.name=Електронасос UIV +metaitem.electric.pump.opv.name=Електронасос OpV +metaitem.fluid.regulator.iv.name=Регулятор рідини IV +metaitem.fluid.regulator.zpm.name=Регулятор рідини ZPM +metaitem.conveyor.module.luv.name=Конвеєрний модуль LuV +metaitem.conveyor.module.uv.name=Конвеєрний модуль UV +metaitem.robot.arm.lv.name=Роботизована рука LV +metaitem.robot.arm.ev.name=Роботизована рука EV +metaitem.robot.arm.zpm.name=Роботизована рука ZPM +metaitem.robot.arm.uev.name=Роботизована рука UEV +metaitem.robot.arm.tooltip=Обмежує §fПредмети§7 до певної кількості, працює як §fПокращення§7. +metaitem.field.generator.hv.name=Генератор поля HV +metaitem.field.generator.uv.name=Генератор поля UV +metaitem.field.generator.opv.name=Генератор поля OpV +metaitem.emitter.hv.name=Випромінювач HV +metaitem.emitter.uv.name=Випромінювач UV +metaitem.emitter.uiv.name=Випромінювач UIV +metaitem.tool.dataorb.tooltip=Сховище для складних даних +metaitem.tool.datamodule.tooltip=Сховище для неймовірно складних даних/n§cМоже бути прочитане лише банком даних +metaitem.emitter.uhv.name=Випромінювач UHV +metaitem.sensor.hv.name=Сенсор HV +metaitem.sensor.uxv.name=Сенсор UXV +metaitem.board.epoxy.name=Епоксидна друкована плата +metaitem.glass_lens.light_blue.name=Скляна лінза (Світло-блакитна) +metaitem.circuit.integrated.jei_description=JEI показує рецепти тільки для даної конфігурації.\n\nВи можете вибрати конфігурацію у графічному інтерфейсі схеми. +metaitem.spray.can.dyes.light_blue.name=Балончик (світло-синій) +metaitem.glass_lens.blue.name=Скляна лінза (Синя) +metaitem.boule.silicon.tooltip=Необроблена схема +metaitem.wafer.silicon.tooltip=Необроблена схема +metaitem.wafer.phosphorus.name=Пластина легована фосфором +metaitem.wafer.naquadah.name=Пластина легована наквадою +metaitem.boule.naquadah.name=Монокристалічний кремній легований наквадою +metaitem.board.plastic.name=Пластмасова друкована плата +metaitem.circuit_board.elite.name=Відмінна друкована плата +metaitem.circuit_board.wetware.tooltip=Плата, яка зберігає життя +metaitem.circuit.vacuum_tube.tooltip=Технічно це діод/n рівень §cULV +metaitem.component.transistor.tooltip=Базовий електричний компонент +metaitem.glass_lens.lime.name=Glass Lens (Лаймова) +metaitem.glass_lens.gray.name=Скляна лінза (Сіра) +metaitem.glass_lens.brown.name=Скляна лінза (Коричнева) +metaitem.boule.phosphorus.name=Монокристалічний кремній легований фосфором +metaitem.wafer.silicon.name=Кремнієва Пластина +metaitem.wafer.naquadah.tooltip=Необроблена схема +metaitem.wafer.neutronium.name=Пластина легована нейтронами +metaitem.boule.neutronium.name=Монокристалічний кремній легований нейтронами +metaitem.wafer.neutronium.tooltip=Необроблена схема +metaitem.board.coated.name=Друкована плата з покриттям +metaitem.board.coated.tooltip=Плата з покриттям +metaitem.board.phenolic.name=Фенольна друкована плата +metaitem.board.phenolic.tooltip=Гарна плата +metaitem.board.plastic.tooltip=Гарна плата +metaitem.component.capacitor.tooltip=Базовий електричний компонент +metaitem.component.smd.diode.tooltip=Електричний компонент +metaitem.component.smd.transistor.name=SMD Транзистор +metaitem.component.advanced_smd.diode.tooltip=Покращений електричний компонент +metaitem.component.advanced_smd.transistor.tooltip=Покращений електричний компонент +metaitem.wafer.highly_advanced_system_on_chip.tooltip=Необроблена високотехнологічна схема +metaitem.wafer.high_power_integrated_circuit.tooltip=Необроблена схема високої потужності +metaitem.wafer.central_processing_unit.name=Процесорна пластина +metaitem.petri_dish.name=Чашка Петрі +metaitem.duct_tape.name=Покращена армована клейка стрічка FAL-84 +metaitem.basic_tape.tooltip=Недостатньо міцна для вирішення механічних проблем +metaitem.gravistar.tooltip=Доскональна пекельна зірка +metaitem.carbon.mesh.name=Сітка з вуглецевого волокна +metaitem.carbon.plate.name=Вуглецева пластина +metaitem.minecart_wheels.steel.tooltip=Щоб все їхало за планом +metaitem.wafer.ultra_high_power_integrated_circuit.name=Пластина UHPIC +metaitem.wafer.ultra_high_power_integrated_circuit.tooltip=Необроблена схема надвисокої напруги +metaitem.wafer.nand_memory_chip.name=Пластина NAND +metaitem.wafer.nand_memory_chip.tooltip=Необроблений логічний вентиль +metaitem.wafer.ultra_low_power_integrated_circuit.name=Пластина ULPIC +metaitem.wafer.ultra_low_power_integrated_circuit.tooltip=Необроблена схема наднизької напруги +metaitem.wafer.low_power_integrated_circuit.name=Пластина LPIC +metaitem.wafer.low_power_integrated_circuit.tooltip=Необроблена схема низької напруги +metaitem.wafer.power_integrated_circuit.name=Пластина PIC +metaitem.wafer.power_integrated_circuit.tooltip=Необлена схема напруги +metaitem.wafer.nano_central_processing_unit.name=Пластина нанопроцесора +metaitem.wafer.nor_memory_chip.name=Пластина NOR +metaitem.wafer.nor_memory_chip.tooltip=Необроблений логічний вентиль +metaitem.wafer.random_access_memory.name=Пластина RAM +metaitem.wafer.random_access_memory.tooltip=Необроблена пам'ять +metaitem.wafer.system_on_chip.name=Пластина SoC +metaitem.wafer.simple_system_on_chip.name=Проста пластина SoC +metaitem.wafer.simple_system_on_chip.tooltip=Необроблена проста схема +metaitem.engraved.crystal_chip.name=Гравірований кришталевий чіп +metaitem.engraved.crystal_chip.tooltip=Потрібен для схем +metaitem.crystal.raw.name=Необроблений кришталевий чіп +metaitem.crystal.raw_chip.name=Частини необробленого криштального чіпа +metaitem.engraved.lapotron_chip.name=Гравірований лапотроний криштальний чіп +metaitem.crystal.central_processing_unit.name=Криштальний CPU +metaitem.crystal.central_processing_unit.tooltip=Криштальний модуль обробки +metaitem.crystal.system_on_chip.name=Криштальний SoC +metaitem.crystal.system_on_chip.tooltip=Криштальна система на чіпі +metaitem.plate.advanced_system_on_chip.name=ПСнЧ +metaitem.plate.highly_advanced_system_on_chip.name=ВТСнЧ +metaitem.plate.highly_advanced_system_on_chip.tooltip=Високотехнологічна система на чіпі +metaitem.plate.integrated_logic_circuit.name=Вбудована схема +metaitem.plate.integrated_logic_circuit.tooltip=Вбудована логічна схема +metaitem.plate.central_processing_unit.name=ЦП +metaitem.plate.central_processing_unit.tooltip=Центральний процесор +metaitem.plate.high_power_integrated_circuit.name=ВСВН +metaitem.plate.high_power_integrated_circuit.tooltip=Вбудована схема високої напруги +metaitem.plate.ultra_high_power_integrated_circuit.tooltip=Вбудована схема надвисокої напруги +metaitem.plate.ultra_high_power_integrated_circuit.name=ВСНВН +metaitem.plate.nand_memory_chip.name=NAND +metaitem.plate.nand_memory_chip.tooltip=Логічний вентиль NAND +metaitem.plate.nano_central_processing_unit.name=Нано ЦП +metaitem.plate.nano_central_processing_unit.tooltip=Нанопроцесор +metaitem.plate.nor_memory_chip.name=NOR +metaitem.plate.nor_memory_chip.tooltip=Логічний вентиль NOR +metaitem.circuit.good_electronic.name=Гарна електрична схема +metaitem.circuit.good_electronic.tooltip=Твоя друга схема/n§cРівень MV +metaitem.plate.ultra_low_power_integrated_circuit.tooltip=Вбудована схема наднизької напруги +metaitem.plate.low_power_integrated_circuit.name=ВСНН +metaitem.plate.ultra_low_power_integrated_circuit.name=ВСННН +metaitem.plate.low_power_integrated_circuit.tooltip=Вбудована схема низької напруги +metaitem.plate.power_integrated_circuit.name=ВСН +metaitem.plate.power_integrated_circuit.tooltip=Вбудована схема напруги +metaitem.plate.random_access_memory.name=RAM +metaitem.plate.random_access_memory.tooltip=Пам'ять з довільним доступом +metaitem.plate.system_on_chip.name=СнЧ +metaitem.plate.system_on_chip.tooltip=Система на чіпі +metaitem.plate.simple_system_on_chip.name=Проста СнЧ +metaitem.plate.simple_system_on_chip.tooltip=Проста система на чіпі + + +# T1: Electronic +metaitem.circuit.electronic.name=Проста електрична схема +metaitem.circuit.electronic.tooltip=Твоя перша схема/n§cРівень LV + +# T2: Integrated +metaitem.circuit.basic_integrated.name=Вбудована логічна схема +metaitem.circuit.basic_integrated.tooltip=Менша та більш потужна/n§6Рівень LV +metaitem.circuit.good_integrated.name=Гарна вбудована схема +metaitem.circuit.good_integrated.tooltip=Менша та більш потужна/n§6Рівень MV +metaitem.circuit.advanced_integrated.name=Покращена вбудована схема +metaitem.circuit.advanced_integrated.tooltip=Менша та більш потужна/n§6Рівень HV + +# Misc +metaitem.circuit.nand_chip.name=Чіп NAND +metaitem.circuit.nand_chip.tooltip=Передова проста схема/n§6Рівень ULV +metaitem.circuit.microprocessor.name=Мікропроцесор +metaitem.circuit.microprocessor.tooltip=Передова базова схема/n§eРівень LV + +# T3: Processor +metaitem.circuit.processor.name=Вбудований процесор +metaitem.circuit.processor.tooltip=Дивовижна швидкість обчислення!/n§eРівень MV +metaitem.circuit.assembly.name=Обчислювальний процесор +metaitem.circuit.assembly.tooltip=Дивовижна швидкість обчислення!/n§eРівень HV +metaitem.circuit.workstation.name=Суперкомп'ютер +metaitem.circuit.workstation.tooltip=Дивовижна швидкість обчислення!/n§eРівень EV +metaitem.circuit.mainframe.name=Основний каркас +metaitem.circuit.mainframe.tooltip=Дивовижна швидкість обчислення!/n§eРівень IV + +# T4: Nano +metaitem.circuit.nano_processor.name=Нанопроцесор +metaitem.circuit.nano_processor.tooltip=Менше, ніж будь-коли/n§bРівень HV +metaitem.circuit.nano_assembly.name=Обчислювальний нанопроцесор +metaitem.circuit.nano_assembly.tooltip=Менше, ніж будь-коли/n§bРівень EV +metaitem.circuit.nano_computer.name=Наносуперкомп'ютер +metaitem.circuit.nano_computer.tooltip=Менше, ніж будь-коли/n§bРівень IV +metaitem.circuit.nano_mainframe.name=Основний каркас нанопроцесора +metaitem.circuit.nano_mainframe.tooltip=Менше, ніж будь-коли/n§bРівень LuV + +# T5: Quantum +metaitem.circuit.quantum_processor.name=Квантовий процесор +metaitem.circuit.quantum_processor.tooltip=Квантові обчислення втілюються в життя!/n§aРівень EV +metaitem.circuit.quantum_assembly.name=Обчислювальний квантовий процесор +metaitem.circuit.quantum_assembly.tooltip=Квантові обчислення втілюються в життя!/n§aРівень IV +metaitem.circuit.quantum_computer.name=Квантовий суперкомп'ютер +metaitem.circuit.quantum_computer.tooltip=Квантові обчислення втілюються в життя!/n§aРівень LuV +metaitem.circuit.quantum_mainframe.name=Основний каркас квантового процесора +metaitem.circuit.quantum_mainframe.tooltip=Квантові обчислення втілюються в життя!/n§aРівень ZPM + +# T6: Crystal +metaitem.circuit.crystal_processor.name=Криштальний процесор +metaitem.circuit.crystal_processor.tooltip=Використання переваг гравіювання на кришталі/n§9Рівень IV +metaitem.circuit.crystal_assembly.name=Обчислювальний криштальний процесор +metaitem.circuit.crystal_assembly.tooltip=Використання переваг гравіювання на кришталі/n§9Рівень LuV +metaitem.circuit.crystal_computer.name=Криштальний суперкомп'ютер +metaitem.circuit.crystal_computer.tooltip=Використання переваг гравіювання на кришталі/n§9Рівень ZPM +metaitem.circuit.crystal_mainframe.name=Основний каркас криштального процесора +metaitem.circuit.crystal_mainframe.tooltip=Використання переваг гравіювання на кришталі/n§9Рівень UV + +# T7: Wetware +metaitem.circuit.wetware_processor.name=Органічний процесор +metaitem.circuit.wetware_processor.tooltip=У тебе таке відчуття, ніби воно спостерігає за тобою./n§4РівеньLuV +metaitem.circuit.wetware_assembly.name=Обчислювальний органічний процесор +metaitem.circuit.wetware_assembly.tooltip=Зможе запустити Minecraft/n§4Рівень ZPM +metaitem.circuit.wetware_computer.name=Органічний суперкомп'ютер +metaitem.circuit.wetware_mainframe.name=Основний каркас органічного процесора +metaitem.circuit.wetware_computer.tooltip=Ідеальне поєднання плоті та машини/n§4Рівень UV +metaitem.circuit.wetware_mainframe.tooltip=Потужне, але чи сможе воно запустити Crysis?/n§4Рівень UHV +metaitem.stem_cells.name=Cтовбурові клітини +metaitem.stem_cells.tooltip=Сирий інтелект +metaitem.processor.neuro.name=Нейроний модуль обробки +metaitem.processor.neuro.tooltip=Нейроний процесор +metaitem.petri_dish.tooltip=Для вирощування клітин +metaitem.bio_chaff.name=Біологічна маса +metaitem.carbon.fibers.name=Сире вуглецеве волокно +metaitem.neutron_reflector.name=Іридієвий нейтронний відбивач +metaitem.neutron_reflector.tooltip=Незламний +metaitem.duct_tape.tooltip=Якщо ви не можете виправити це за допомогою цього, використайте більше! +metaitem.basic_tape.name=Стрічка +metaitem.component.grinder.diamond.name=Алмазна шліфувальна головка +metaitem.component.grinder.tungsten.name=Вольфрамова шліфувальна головка +metaitem.minecart_wheels.iron.name=Залізні колеса вагонетки +metaitem.minecart_wheels.iron.tooltip=Щоб все їхало за планом +metaitem.minecart_wheels.steel.name=Стальні колеса вагонетки +metaitem.quantumeye.name=Квантове око +metaitem.quantumeye.tooltip=Вдосконалене око енду +metaitem.quantumstar.name=Квантова зірка +metaitem.quantumstar.tooltip=Вдосконалена пекельна зірка +metaitem.gravistar.name=Гравітаційна зірка +metaitem.item_filter.name=Фільтр предметів +metaitem.wafer.system_on_chip.tooltip=Необроблена базова схема +metaitem.wafer.nano_central_processing_unit.tooltip=Необроблема наносхема +metaitem.crystal.raw_chip.tooltip=Частини необробленого криштального процесора +metaitem.plate.advanced_system_on_chip.tooltip=Покращена система на чіпі +metaitem.crystal.raw.tooltip=Необроблений кришталевий процесор +metaitem.fluid_filter.name=Фільтр рідини +metaitem.ore_dictionary_filter.name=Фільтр руди +metaitem.fluid_filter.tooltip=Фільтрує §fРідини§7 працює як §fПокращення§7./nМожна виковистовувати для покращення §fЕлектронасоса§7 і §fРегулятора рідини§7. +metaitem.smart_item_filter.name=Розумний фільтр предметів +behaviour.filter_ui_manager=§fПКМ§7 для налаштувань, §fShift ПКМ§7 для очищення налаштувань +metaitem.cover.controller.name=Контролер машини +metaitem.cover.activity.detector.tooltip=Видає §fСтатус роботи§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7. +metaitem.cover.fluid.detector.tooltip=Видає §fКількість рідини§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7. +metaitem.cover.item.detector.tooltip=Видає §fКількість предметів§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7. +metaitem.cover.activity.detector.name=Датчик активності +metaitem.cover.fluid.detector.name=Датчик рідини +metaitem.cover.fluid.detector.advanced.name=Покращений датчик рідини +metaitem.cover.item.detector.name=Датчик предметів +metaitem.cover.item.detector.advanced.name=Покращений датчик предметів +metaitem.cover.fluid.voiding.name=Видалятор рідини +metaitem.cover.fluid.voiding.tooltip=Видаляє §fРідини§7, працює як §fПокращення§7./nАктивується за допомогою §fМ'якої киянки§7 після розміщення. +metaitem.cover.fluid.voiding.advanced.name=Покращення видалятор рідини +metaitem.cover.item.voiding.name=Видалятор предметів +metaitem.cover.item.voiding.tooltip=Видаляє §fПредмети§7 працює як §fПокращення§7./nАктивується за допомогою §fМ'якої киянки§7 після розміщення. +metaitem.cover.item.voiding.advanced.name=Покращений видалятор предметів +metaitem.cover.storage.name=Сховище (Покращення) +metaitem.cover.energy.detector.advanced.name=Покращений датчик енергії +metaitem.cover.maintenance.detector.tooltip=Видає §fПотребу в технічного обслуговування§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7. +metaitem.cover.facade.name=Фасад (%s) +metaitem.cover.screen.name=Комп'ютерний монітор +metaitem.cover.screen.tooltip=Показує §fДані§7, працює як §fПокращення§7. +metaitem.cover.crafting.name=Верстак (Покращення) +metaitem.cover.shutter.name=Затворний модуль (Покращення) +metaitem.cover.shutter.tooltip=§fБлокує передачу§7 через сторону на якій знаходиться, працює як §fПокращення§7. +metaitem.cover.solar.panel.ulv.name=Сонячна панель ультранизької напруги +metaitem.cover.solar.panel.mv.name=Сонячна панель середньої напруги (Покращення) +metaitem.cover.solar.panel.hv.name=Сонячна панель високої напруги (Покращення) +metaitem.cover.solar.panel.iv.name=Сонячна панель шаленої напруги (Покращення) +metaitem.cover.solar.panel.luv.name=Сонячна панель сміховинної напруги (Покращення) +metaitem.cover.solar.panel.uv.name=Сонячна панель кінцевої напруги (Покращення) +metaitem.cover.solar.panel.tooltip.1=Нехай Сонце буде з вами. +metaitem.cover.infinite_water.name=Нескінченне джерело води (Покращення) +metaitem.cover.infinite_water.tooltip.1=Наповнює контейнери §9Водою§7, працює як §fПокращення§7. +metaitem.cover.ender_fluid_link.name=Рідкий безтрубний зв'язок (Покращення) +metaitem.gelled_toluene.tooltip=Сира вибухівка +metaitem.bottle.purple.drink.name=Фіолетовий напій +metaitem.bottle.purple.drink.tooltip=Як щодо лимонаду? Або холодного чаю? А у мене є фіолетовий напій! +metaitem.dye.black.name=Чорний барвник +metaitem.dye.red.name=Червоний барвник +metaitem.dye.green.name=Зелений барвник +metaitem.dye.blue.name=Синій барвник +metaitem.dye.purple.name=Фіолетовий барвник +metaitem.dye.cyan.name=Блакитний барвник +metaitem.dye.silver.name=Світло-сірий барвник +metaitem.dye.gray.name=Сірий барвник +metaitem.dye.lime.name=Лаймовий барвник +metaitem.dye.magenta.name=Пурпурний барвник +metaitem.dye.orange.name=Помаранчевий барвник +metaitem.dye.white.name=Білий барвник +metaitem.tool_parts_box.name=Ящик для інструментів +metaitem.foam_sprayer.name=Розпилювач піни +metaitem.energium_dust.name=Енергітичний пил +metaitem.compressed.clay.name=Спресована глина +metaitem.compressed.fireclay.name=Спресована шамотна глина +metaitem.brick.fireclay.name=Цегла з шамотної глина +metaitem.brick.fireclay.tooltip=Термостійка +metaitem.brick.coke.name=Цегла для коксових печей +metaitem.wooden_form.empty.name=Порожня дерев'яна форма +item.gt.tool.replace_tool_head=Створіть нову головку інструменту, щоб замінити її +item.gt.tool.usable_as=Використовується як: §f%s +item.gt.tool.behavior.tree_felling=§4Лісоруб: §fРубка дерев +item.gt.tool.behavior.shield_disable=§cБрут: §fВідключає щити +item.gt.tool.behavior.ground_tilling=§eФермер: §fОбробляє землю +item.gt.tool.behavior.rail_rotation=§eЗалізничний інженер: §fОбертає рейки +item.gt.tool.behavior.block_rotation=§2Механік: §fОбертає блоки +gt.tool.class.sword=Меч +gt.tool.class.pickaxe=Кайло +gt.tool.class.shovel=Лопата +gt.tool.class.axe=Сокира +gt.tool.class.hoe=Мотика +gt.tool.class.saw=Пила +gt.tool.class.hammer=Молот +gt.tool.class.mallet=Киянка +gt.tool.class.wrench=Гайковий ключ +gt.tool.class.file=Напилок +gt.tool.class.crowbar=Лом +gt.tool.class.screwdriver=Викрутка +gt.tool.class.mortar=Ступка +gt.tool.class.scythe=Коса +gt.tool.class.shears=Ножиці +gt.tool.class.knife=Ніж +gt.tool.class.plunger=Вантуз +item.gt.tool.sword.name=Меч (%s) +item.gt.tool.pickaxe.name=Кайло (%s) +item.gt.tool.shovel.name=Лопата (%s) +item.gt.tool.axe.name=Сокира (%s) +item.gt.tool.hoe.name=Мотика (%s) +item.gt.tool.saw.name=Пила (%s) +item.gt.tool.hammer.name=Молот (%s) +item.gt.tool.mallet.name=М'яка киянка (%s) +item.gt.tool.mallet.tooltip=§8Зупиняє/запускає техніку +item.gt.tool.wrench.name=Гайковий ключ %s +item.gt.tool.file.name=Напилок (%s) +item.gt.tool.crowbar.name=Лом (%s) +item.gt.tool.crowbar.tooltip=§8Знімає покращення +item.gt.tool.screwdriver.name=Викрутка (%s) +item.gt.tool.screwdriver.tooltip=§8Налаштовує покращення та машини +item.gt.tool.mortar.name=Ступка (%s) +item.gt.tool.wire_cutter.name=Кусачки (%s) +item.gt.tool.butchery_knife.name=Тесак (%s) +item.gt.tool.butchery_knife.tooltip=§8Має низьку швидкість атаки +item.gt.tool.scythe.name=Коса (%s) +item.gt.tool.rolling_pin.name=Качалка (%s) +item.gt.tool.drill_lv.name=%s Бур (LV) +item.gt.tool.drill_hv.name=%s Бур (HV) +item.gt.tool.drill_ev.name=%s Бур (EV) +item.gt.tool.drill_iv.name=%s Бур (IV) +item.gt.tool.mining_hammer.name=%s Гірський молот +item.gt.tool.spade.tooltip=§8Видобуває велику площу за один раз (якщо тільки ви не присідаєте) +item.gt.tool.chainsaw_lv.name=%s Бензопила (LV) +item.gt.tool.chainsaw_mv.name=%s Бензопила (MV) +item.gt.tool.chainsaw_hv.name=%s Бензопила (HV) +item.gt.tool.wrench_lv.tooltip=§8Утримуйте ЛКМ, щоб демонтувати машини +item.gt.tool.wrench_iv.tooltip=§8Утримуйте ЛКМ, щоб демонтувати машини +item.gt.tool.buzzsaw.name=%s Циркулярна пила (LV) +item.gt.tool.screwdriver_lv.name=%s Викрутка (LV) +item.gt.tool.screwdriver_lv.tooltip=§8Налаштовує покращення та машини +item.gt.tool.plunger.name=%s Вантуз +item.gt.tool.plunger.tooltip=§8Видаляє рідини з машин +item.gt.tool.wire_cutter_lv.name=%s Кусачки (LV) +item.gt.tool.wire_cutter_hv.name=%s Кусачки (HV) +item.gt.tool.tooltip.general_uses=§bМіцність: %s +item.gt.tool.tooltip.attack_damage=§cШкода від атаки: %s +item.gt.tool.tooltip.attack_speed=§9Швидкість атаки: %s +item.gt.tool.tooltip.mining_speed=§dШвидкість видобутку: %s +item.gt.tool.tooltip.harvest_level_extra=§eРівень збору: %s §f(%s§f) +item.gt.tool.harvest_level.0=§8Дерево +item.gt.tool.harvest_level.2=§aЗалізо +item.gt.tool.harvest_level.3=§bАлмаз +item.gt.tool.harvest_level.4=§dУльтімет +item.gt.tool.harvest_level.5=§9Дюранієвий +item.gt.tool.harvest_level.6=§cНейтроній +item.gt.tool.tooltip.repair_info=Утримуйте клавішу SHIFT, щоб показати інформацію про ремонт +item.gt.tool.tooltip.repair_material=Ремонтується з допомогою: §a%s +item.gt.tool.aoe.rows=Ряди +item.gt.tool.aoe.columns=Колонки +item.gt.tool.aoe.layers=Шари +metaitem.turbine_rotor.name=%s Ротор турбіни +metaitem.turbine_rotor.tooltip=Ротори турбін для вашої електростанції +metaitem.clipboard.name=Блокнот +metaitem.clipboard.tooltip=Можна писати (без будь-якого інструменту для письма). Клацніть ПКМ на стіні, щоб розмістити, і клацніть Shift-ПКМ, щоб підібрати + + +metaitem.behavior.mode_switch.tooltip=SHIFT-ПКМ для змінення режиму +metaitem.behavior.mode_switch.mode_switched=§eРежим змінено на: %s +metaitem.behavior.mode_switch.current_mode=Режим: %s +metaitem.tool.tooltip.primary_material=§fМатеріал: §e%s +metaitem.tool.tooltip.durability=§fМіцність: §a%,d / %,d +metaitem.tool.tooltip.rotor.efficiency=Ефективність турбіни: §9%,d%% +metaitem.tool.tooltip.rotor.power=Потужність турбіни: §9%,d%% +metaitem.voltage_coil.ulv.name=Котушка (ULV) +metaitem.voltage_coil.ulv.tooltip=Примітивна котушка +metaitem.voltage_coil.lv.name=Котушка (LV) +metaitem.voltage_coil.lv.tooltip=Базова котушка +metaitem.voltage_coil.mv.name=Котушка середньої напруги +metaitem.voltage_coil.mv.tooltip=Гарна котушка +metaitem.voltage_coil.hv.name=Котушка високої напруги +metaitem.voltage_coil.hv.tooltip=Покращена котушка +metaitem.voltage_coil.ev.name=Котушка екстремальної напруги +metaitem.voltage_coil.ev.tooltip=Екстремально гарна котушка +metaitem.voltage_coil.iv.name=Котушка шаленої напруги +metaitem.voltage_coil.iv.tooltip=Відмінна котушка +metaitem.voltage_coil.luv.name=Котушка сміховинної напруги +metaitem.voltage_coil.luv.tooltip=Відмінна котушка V2 +metaitem.voltage_coil.zpm.name=Котушка (ZPM) +metaitem.voltage_coil.zpm.tooltip=Відмінна котушка V3 +metaitem.voltage_coil.uv.name=Котушка (UV) +metaitem.voltage_coil.uv.tooltip=Доскональна котушка +metaitem.voltage_coil.uhv.name=Котушка (UHV) +metaitem.voltage_coil.uhv.tooltip=Ідеальна котушка +metaitem.voltage_coil.uev.name=Котушка (UEV) +metaitem.voltage_coil.uev.tooltip=Нереальна котушка +metaitem.voltage_coil.uiv.name=Котушка (UIV) +metaitem.voltage_coil.uiv.tooltip=Бездоганна котушка V2 +metaitem.voltage_coil.uxv.name=Котушка (UXV) +metaitem.voltage_coil.uxv.tooltip=Бездоганна котушка V3 +metaitem.voltage_coil.opv.name=Котушка (OpV) +metaitem.voltage_coil.opv.tooltip=Бездоганна котушка V4 +metaitem.voltage_coil.max.name=Катушка (MAX) +metaitem.voltage_coil.max.tooltip=Максимальна котушка +metaitem.liquid_fuel_jetpack.name=Реактивний ранець на рідкому паливі +metaitem.liquid_fuel_jetpack.tooltip=Використовує паливо для тяги +metaitem.electric_jetpack.name=Реактивний ранець з електричним двигуном +metaitem.nightvision_goggles.name=Окуляри нічного бачення +metaitem.nms.leggings.name=NanoMuscle™ Штани +metaitem.nms.boots.name=NanoMuscle™ Чоботи +metaitem.nms.helmet.name=NanoMuscle™ Шолом +metaitem.nms.advanced_chestplate.name=Покращений NanoMuscle™ Нагрудник +metaitem.qts.chestplate.name=QuarkTech™ Нагрудник +metaitem.item_filter.tooltip=Фільтрує §fПредмети§7 працює як §fПокращення§7./nМожна виковистовувати для покращення §fКонвеєрного модуля§7 і §fРоботизованої руки§7. +metaitem.smart_item_filter.tooltip=Фільтрує §fПредмети§7 за допомогою §fРецептів машин§7 працює як §fПокращення§7./nМожна виковистовувати для покращення §fКонвеєрного модуля§7 і §fРоботизованої руки§7. +metaitem.qts.helmet.name=QuarkTech™ Шолом +metaitem.qts.advanced_chestplate.name=Покращений QuarkTech™ Нагрудник +metaitem.gravitation_engine.name=Модуль гравітаційного двигуна +metaitem.tool.multiblock_builder.name=Конструктор багатоблочних конструкцій +metaitem.tool.multiblock_builder.tooltip=Для налагодження та автоматичної збірки ваших багатоблочних конструкцій! +metaitem.ore_dictionary_filter.tooltip=Фільтрує §fПредмети§7 з допомогою §fСловника руд§7 працює як §fПокращення§7./nМожна виковистовувати для покращення §fКонвеєрного модуля§7 і §fРоботизованої руки§7. +metaarmor.nms.nightvision.enabled=NanoMuscle™ Костюм: Увімкнено нічне бачення +metaarmor.nms.nightvision.disabled=NanoMuscle™ Костюм: Вимкнено нічне бачення +metaitem.cover.controller.tooltip=§fВКЛ/ВИКЛ§7 машину працює як §fПокращення§7. +metaitem.cover.activity.detector_advanced.tooltip=Видає §fПрогрес роботи§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7. +metaitem.cover.item.detector.advanced.tooltip=Видає §fСтатус зберігання предметів§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7, з можливістю контролювати з допомогою §fRS-Latch§7. +metaitem.cover.energy.detector.advanced.tooltip=Дає §fRS-Latch§7 контроль за §fСтатусом енергії§7 за допомогою Редстоуну, працює як §fПокращення§7. +metaitem.cover.energy.detector.name=Датчик енергії +metaarmor.nms.nightvision.error=NanoMuscle™ Костюм: §cНедостатньо енергії! +metaarmor.qts.nightvision.enabled=QuarkTech™ Костюм: Увімкнено нічне бачення +metaitem.cover.fluid.detector.advanced.tooltip=Видає §fСтатус зберігання рідини§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7, з можливістю контролювати з допомогою §fRS-Latch§7. +metaitem.cover.energy.detector.tooltip=Видає §fКількість енергії§7 у вигляді сигналу Редстоуна, працює як §fПокращення§7. +metaitem.cover.fluid.voiding.advanced.tooltip=Видаляє §fРідини§7, з контролем кількості, працює як §fПокращення§7./nАктивується за допомогою §fМ'якої киянки§7 після розміщення. +metaarmor.qts.nightvision.disabled=QuarkTech™ Костюм: Вимкнено нічне бачення +metaarmor.jetpack.hover.enable=Реактивний ранець: Увімкнено режим зависання +metaarmor.jetpack.hover.disable=Реактивний ранець: Вимкнено режим зависання +metaarmor.jetpack.emergency_hover_mode=Аварійний режим зависання увімкнено! +metaarmor.nms.share.enable=NanoMuscle™ Костюм: Зарядку ввімкнено +metaarmor.nms.share.disable=NanoMuscle™ Костюм: Зарядку вимкнено +metaarmor.nms.share.error=NanoMuscle™ Костюм: §cНе вистачає енергії для зарядки! +metaarmor.qts.share.enable=QuarkTech™ Костюм: Зарядку ввімкнено +metaarmor.qts.share.disable=QuarkTech™ Костюм: Зарядку вимкнено +metaarmor.qts.share.error=QuarkTech™ Костюм: §cНе вистачає енергії для зарядки! +metaarmor.message.nightvision.enabled=§bНічне бачення: §aУвімкнено +metaarmor.message.nightvision.disabled=§bНічне бачення: §cВимкнено +metaarmor.message.nightvision.error=§cНедостатньо енергії! +metaarmor.tooltip.stepassist=Забезпечує крокову допомогу +metaarmor.tooltip.speed=Збільшує швидкість бігу +metaarmor.tooltip.jump=Збільшує висоту та дистанцію стрибків +metaarmor.tooltip.falldamage=Обнуляє шкоду від падіння +metaarmor.tooltip.potions=Обнуляє шкідливі ефекти +metaarmor.tooltip.burning=Обнуляє горіння +metaarmor.tooltip.breath=Поповнює шкалу підводого дихання +metaarmor.tooltip.autoeat=Поповнює запаси їжі, використовуючи їжу з інвентарю +metaarmor.hud.status.enabled=§aУВІМК +metaarmor.hud.status.disabled=§cВИМК +metaarmor.hud.energy_lvl=Рівень енергії: %s +metaarmor.hud.fuel_lvl=Рівень палива: %s +metaarmor.hud.hover_mode=Режим зависання: %s +mataarmor.hud.supply_mode=Режим постачання: %s +metaarmor.energy_share.error=Енергопостачання: §cНе вистачає потужності для зарядки приладів! +metaarmor.energy_share.enable=Енергопостачання: Увімкнено заряджання приладів +metaarmor.energy_share.disable=Енергопостачання: Вимкнено зарядку пристроїв +metaarmor.energy_share.tooltip=Режим постачання: %s +metaarmor.energy_share.tooltip.guide=Щоб змінити режим, клацніть SHIFT-ПКМ, утримуючи предмет +metaitem.record.sus.name=Музичний диск +metaitem.record.sus.tooltip=§7Leonz - Among Us Drip +metaitem.nan.certificate.name=Сертифікат про те, що ви більше не новачок +metaitem.nan.certificate.tooltip=Виклик прийнято! +metaitem.fertilizer.name=Добриво +metaitem.blacklight.name=Чорне світло +metaitem.blacklight.tooltip=Довгохвильове §dУльтрафіолетове§7 джерело світла +gui.widget.incrementButton.default_tooltip=Утримуйте Shift, Ctrl або обидві клавіші, щоб змінити кількість +gui.widget.recipeProgressWidget.default_tooltip=Показати рецепти +gregtech.recipe_memory_widget.tooltip.1=§7Клікніть лівою кнопкою миші, щоб автоматично внести цей рецепт до сітки крафтингу +cover.filter.blacklist.disabled=Білий список +cover.filter.blacklist.enabled=Чорний список +cover.ore_dictionary_filter.title=Фільтр руди за словником +cover.ore_dictionary_filter.match_all=Відповідає всім: %s +cover.ore_dictionary_filter.case_sensitive=Чутливість до регістру: %s +cover.ore_dictionary_filter.test_slot.info=Вставте елемент, щоб перевірити, чи відповідає він виразу фільтра +cover.ore_dictionary_filter.test_slot.matches=§a* %s +cover.ore_dictionary_filter.test_slot.matches_not=§c* %s +cover.ore_dictionary_filter.test_slot.no_oredict.matches=§a(Немає словника руд) +cover.ore_dictionary_filter.test_slot.no_oredict.matches_not=§c(Немає словника руд) +cover.ore_dictionary_filter.status.err=§c%s помилка(и) +cover.ore_dictionary_filter.status.err_warn=§c%s помилок та %s попереджень +cover.ore_dictionary_filter.status.warn=§7%s попереджень +cover.ore_dictionary_filter.status.no_issues=§aБез проблем +cover.ore_dictionary_filter.status.explain=Пояснення рудного фільтра: +cover.ore_dictionary_filter.button.case_sensitive.disabled=Нечутливий до регістру +cover.ore_dictionary_filter.button.case_sensitive.enabled=Чутливий до регістру +cover.ore_dictionary_filter.preview.next=... за яким слідує +cover.ore_dictionary_filter.preview.match='%s' +cover.ore_dictionary_filter.preview.match.not=не '%s' +cover.ore_dictionary_filter.preview.char=1 символ +cover.ore_dictionary_filter.preview.char.not=або більше ніж 1 символабо нічого +cover.ore_dictionary_filter.preview.chars=%s символів +cover.ore_dictionary_filter.preview.chars.not=або більше ніж або менше %s символів +cover.ore_dictionary_filter.preview.chars_or_more=%s чи більше символів +cover.ore_dictionary_filter.preview.chars_or_more.not=менше ніж %s символів +cover.ore_dictionary_filter.preview.group=не: +cover.ore_dictionary_filter.preview.or=один з... +cover.ore_dictionary_filter.preview.nor=будьщо, що не є одним із... +cover.ore_dictionary_filter.preview.or.entry= +cover.ore_dictionary_filter.preview.or.entry.start= +metaitem.cover.item.voiding.advanced.tooltip=Видаляє §fПредмети§7, з контролем кількості, працює як §fПокращення§7./nАктивується за допомогою §fМ'якої киянки§7 після розміщення. +metaitem.cover.storage.tooltip=Невеликий сховище для зберігання дрібниць +metaitem.cover.maintenance.detector.name=Датчик потреби технічного обслуговування +metaitem.cover.activity.detector_advanced.name=Покращений датчик активності +metaitem.cover.facade.tooltip=Декоративне оздоблення §fПокращення§7. +metaitem.cover.crafting.tooltip=§fПокращений верстак§7 на машині, працює як §fПокращення§7. +metaitem.cover.solar.panel.name=Сонячна панель (Покращення) +metaitem.cover.solar.panel.lv.name=Сонячна панель низької напруги (Покращення) +metaitem.cover.solar.panel.ev.name=Сонячна панель екстремальної напруги (Покращення) +metaitem.cover.solar.panel.zpm.name=Сонячна панель модуля нульової точки (Покращення) +metaitem.cover.solar.panel.tooltip.2=Виробляє §fЕнергію§7 з §eСонця§7, працює як §fПокращення§7. +metaitem.dye.pink.name=Рожевий барвник +metaitem.dye.yellow.name=Жовтий барвник +metaitem.dye.light_blue.name=Світло-синій барвник +metaitem.foam_sprayer.tooltip=Розпилює будівельну піну/nВикористовується на рамі для запінення з'єднаних рам/nПіна може бути кольоровою +item.gt.tool.behavior.grass_path=§eЛандшафтник: §fСтворює доріжки +item.gt.tool.behavior.plunger=§9Сантехнік: §fЗливає рідини +gt.tool.class.wirecutter=Кусачки +item.gt.tool.hammer.tooltip=§8Подрібнює блоки під час їх збирання +item.gt.tool.knife.name=Ніж (%s) +metaitem.cover.ender_fluid_link.tooltip=Передає §fРідини§7 за допомогою §fБезтрубового §dЕндер§f зв'яку§7, працює як §fПокращення§7. +metaitem.dye.brown.name=Коричневий барвник +metaitem.tool_parts_box.tooltip=Містить деякі частини інструментів/nПКМ для відкриття +metaitem.compressed.coke_clay.name=Спресована коксова глина +metaitem.wooden_form.brick.name=Дерев'яна форма для цегли +item.gt.tool.behavior.silk_ice=§bЛьодоруб: §fШовковий дотик для льоду +item.gt.tool.behavior.relocate_mining=§2Магніт: §fПритягує видобуті блоки +item.gt.tool.behavior.damage_boost=§4Посилення шкоди: §fДодаткова шкода проти %s +gt.tool.class.butchery_knife=Тесак +item.gt.tool.wrench.tooltip=§8Утримуйте ліву кнопку миші, щоб демонтувати машини +item.gt.tool.drill_mv.name=%s Бур (MV) +item.gt.tool.wire_cutter_iv.name=%s Кусачки (IV) + + +item.gt.tool.tooltip.crafting_uses=§aЗалишилося застосувань: %s +item.gt.tool.harvest_level.1=§7Камінь +item.gt.tool.mining_hammer.tooltip=§8Видобуває велику площу за один раз (якщо тільки ви не присідаєте) +item.gt.tool.wrench_lv.name= +item.gt.tool.wrench_hv.tooltip=§8Утримуйте ЛКМ, щоб демонтувати машини +item.gt.tool.buzzsaw.tooltip=§8Не підходить для добування блоків +item.gt.tool.tooltip.harvest_level=§eРівень збору: %s +metaitem.advanced_electric_jetpack.name=Покращений реактивний ранець +metaitem.nms.chestplate.name=NanoMuscle™ Нагрудник +metaitem.qts.leggings.name=QuarkTech™ Штани +metaarmor.qts.nightvision.error=QuarkTech™ Костюм: §cНедостатньо енергії! +gregtech.recipe_memory_widget.tooltip.2=§7Нажміть Shift, щоб заблокувати/розблокувати цей рецепт +metaitem.qts.boots.name=QuarkTech™ Чоботи +metaitem.tool.multiblock_builder.tooltip2=SHIFT-ПКМ по контролеру мультиблочної структури, щоб автоматично побудувати її diff --git a/src/main/resources/assets/gregtech/lang/zh_cn.lang b/src/main/resources/assets/gregtech/lang/zh_cn.lang index fb68f3d3bf2..f3bc63c1658 100644 --- a/src/main/resources/assets/gregtech/lang/zh_cn.lang +++ b/src/main/resources/assets/gregtech/lang/zh_cn.lang @@ -1830,6 +1830,14 @@ gregtech.material.enriched_naquadah_sulfate=硫酸富集硅岩 gregtech.material.naquadria_sulfate=硫酸超能硅岩 gregtech.material.pyrochlore=烧绿石 gregtech.material.rtm_alloy=钌钨钼合金 +gregtech.material.ilmenite_slag=钛铁矿渣 +gregtech.material.zircon=锆石 +gregtech.material.zirconia=氧化锆 +gregtech.material.zirconium_tetrachloride=四氯化锆 +gregtech.material.hafnia=二氧化铪 +gregtech.material.hafnium_tetrachloride=四氯化铪 +gregtech.material.zircaloy_4=锆-4合金 +gregtech.material.inconel_718=因科镍-718 # Organic Chemistry Materials @@ -2043,6 +2051,11 @@ gregtech.material.acidic_naquadria_solution=酸性超能硅岩溶液 gregtech.material.naquadria_waste=超能硅岩废液 gregtech.material.lapotron=兰波顿 gregtech.material.uu_matter=UU物质 +gregtech.material.bauxite_slurry=铝土浆液 +gregtech.material.cracked_bauxite_slurry=裂化铝土浆液 +gregtech.material.bauxite_sludge=铝土泥渣 +gregtech.material.decalcified_bauxite_sludge=脱钙铝土泥渣 +gregtech.material.bauxite_slag=铝土矿渣 # Second Degree Materials @@ -2403,7 +2416,7 @@ tile.optical_pipe_normal.name=光缆 tile.optical_pipe_normal.tooltip1=传递§f算力§7或§f研究数据§7 tile.laser_pipe_normal.name=激光传导线缆 -tile.laser_pipe_normal.tooltip1=无线损地传递能量,只限直线摆放 +tile.laser_pipe_normal.tooltip1=§f无损§7传递能量,仅限直线摆放 metaitem.prospector.lv.name=电动勘探扫描仪(LV) metaitem.prospector.hv.name=电动勘探扫描仪(HV) @@ -4792,7 +4805,7 @@ gregtech.machine.large_miner.ev.name=基础采矿场 gregtech.machine.large_miner.iv.name=进阶采矿场 gregtech.machine.large_miner.luv.name=进阶采矿场 II gregtech.machine.miner.multi.modes=具有精准采集模式与区块对齐模式。 -gregtech.machine.miner.multi.production=产出§f研磨机§7§f3x§7倍的粉碎矿石。 +gregtech.machine.miner.multi.production=它能产出§f三倍§7于§f研磨机§7的粉碎矿石。 gregtech.machine.miner.fluid_usage=每tick消耗§f%,d L§7的§f%s§7,超频时翻倍。 gregtech.machine.miner.multi.description=一台工作范围极广的多方块采矿机,能够提供巨量矿石。 gregtech.machine.miner.multi.needsfluid=钻井液不足! @@ -4811,7 +4824,7 @@ gregtech.machine.miner.chunkradius=区块半径:%d gregtech.machine.fluid_drilling_rig.mv.name=基础流体钻机 gregtech.machine.fluid_drilling_rig.hv.name=进阶流体钻机 gregtech.machine.fluid_drilling_rig.ev.name=进阶流体钻机 II -gregtech.machine.fluid_drilling_rig.description=钻取基岩之下的涓涓流体。 +gregtech.machine.fluid_drilling_rig.description=钻取基岩之下的漫漫流体。 gregtech.machine.fluid_drilling_rig.production=§e产量倍数:§f%dx,超频时为%fx gregtech.machine.fluid_drilling_rig.depletion=§b损耗率:§f%s%% gregtech.machine.fluid_drilling_rig.shows_depletion=显示流体矿脉信息 @@ -5149,9 +5162,9 @@ gregtech.maintenance.configurable_duration=处理耗时:%fx gregtech.maintenance.configurable_duration.unchanged_description=配方以正常速度运行。更改配置以更新。 gregtech.maintenance.configurable_duration.changed_description=配方处理速度现为正常速度(不计超频)的%f倍。 -gregtech.maintenance.configurable_time=故障几率:%fx -gregtech.maintenance.configurable_time.unchanged_description=故障的发生几率为正常值。更改配置以更新。 -gregtech.maintenance.configurable_time.changed_description=故障的发生几率现为正常值的%f倍。 +gregtech.maintenance.configurable_time=故障概率:%fx +gregtech.maintenance.configurable_time.unchanged_description=故障的发生概率为正常值。更改配置以更新。 +gregtech.maintenance.configurable_time.changed_description=故障的发生概率现为正常值的%f倍。 gregtech.maintenance.configurable.tooltip_basic=以更频繁的维护需求为代价,加快机器的处理速度 gregtech.maintenance.configurable.tooltip_more_info=按住SHIFT进行特殊交互 @@ -5177,7 +5190,7 @@ gregtech.machine.muffler_hatch.uxv.name=§eUXV§r消声仓 gregtech.machine.muffler_hatch.opv.name=§9OpV§r消声仓 gregtech.machine.muffler_hatch.max.name=§c§lMAX§r消声仓 -gregtech.muffler.recovery_tooltip=§b收集几率:§f%d%% +gregtech.muffler.recovery_tooltip=§b收集概率:§f%d%% gregtech.machine.pump_hatch.name=水泵输出仓 gregtech.machine.pump_hatch.tooltip=原始水泵专用流体输出口 @@ -5289,7 +5302,7 @@ gregtech.machine.laser_hatch.target.tooltip1=远距离接收能量 gregtech.machine.laser_hatch.tooltip2=§c激光传导线缆必须直线摆放!§7 gregtech.machine.fluid_tank.max_multiblock=多方块结构最大尺寸:%,dx%,dx%,d -gregtech.machine.fluid_tank.fluid=含有%s mB%s +gregtech.machine.fluid_tank.fluid=含有%s L%s # ME Parts gregtech.machine.me_import_item_bus.name=ME输入总线 @@ -5493,14 +5506,14 @@ fluid.spawnlocation.name=流体矿脉信息 gregtech.jei.fluid.vein_weight=矿脉权重:%d gregtech.jei.fluid.min_yield=最小产量:%d gregtech.jei.fluid.max_yield=最大产量:%d -gregtech.jei.fluid.depletion_chance=消耗几率:%d%% +gregtech.jei.fluid.depletion_chance=消耗概率:%d%% gregtech.jei.fluid.depletion_amount=消耗量:%d gregtech.jei.fluid.depleted_rate=殆尽后产量:%d gregtech.jei.fluid.dimension=维度: gregtech.jei.fluid.weight_hover=流体矿脉的生成权重。鼠标悬于图标可查看该流体在特定生物群系中特殊的生成权重 gregtech.jei.fluid.min_hover=流体矿脉所能具有的的最小产量 gregtech.jei.fluid.max_hover=流体矿脉所能具有的的最大产量 -gregtech.jei.fluid.dep_chance_hover=开采流体矿脉时消耗的几率 +gregtech.jei.fluid.dep_chance_hover=开采流体矿脉时消耗的概率 gregtech.jei.fluid.dep_amount_hover=消耗后消耗的量 gregtech.jei.fluid.dep_yield_hover=流体矿脉殆尽后能开采的最大流体量 @@ -5658,7 +5671,7 @@ gregtech.multiblock.large_combustion_engine.supply_liquid_oxygen_to_boost=提供 gregtech.multiblock.large_combustion_engine.obstructed=引擎进气口受阻! gregtech.multiblock.large_combustion_engine.obstructed.desc=引擎进气口前方必须有一格空气。 -gregtech.multiblock.turbine.fuel_amount=燃料量:%s mB(%s) +gregtech.multiblock.turbine.fuel_amount=燃料量:%s L(%s) gregtech.multiblock.turbine.fuel_needed=消耗量:%s / %s ticks gregtech.multiblock.turbine.rotor_speed=转子转速:%s gregtech.multiblock.turbine.rotor_rpm_unit_name=RPM @@ -5748,14 +5761,14 @@ gregtech.multiblock.hpca.warning_multiple_bridges=- 多个桥接组件(没有 gregtech.multiblock.hpca.warning_no_computation=- 没有计算组件 gregtech.multiblock.hpca.warning_low_cooling=- 冷却不足 gregtech.multiblock.hpca.info_max_computation=最大算力:%s -gregtech.multiblock.hpca.info_max_cooling_demand=冷却液需求:%s -gregtech.multiblock.hpca.info_max_cooling_available=冷却液可用:%s -gregtech.multiblock.hpca.info_max_coolant_required=冷却液还需:%s +gregtech.multiblock.hpca.info_max_cooling_demand=冷却需求:%s +gregtech.multiblock.hpca.info_max_cooling_available=冷却可用:%s +gregtech.multiblock.hpca.info_max_coolant_required=冷却液需求:%s L/t gregtech.multiblock.hpca.info_coolant_name=多氯联苯冷却液 gregtech.multiblock.hpca.info_bridging_enabled=桥接已启动 gregtech.multiblock.hpca.info_bridging_disabled=桥接已关闭 -gregtech.command.usage=用法:/gregtech +gregtech.command.usage=用法:/gregtech gregtech.command.worldgen.usage=用法:/gregtech worldgen gregtech.command.worldgen.reload.usage=用法:/gregtech worldgen reload gregtech.command.worldgen.reload.success=已从配置文件中重载世界生成设定。 @@ -5782,6 +5795,12 @@ gregtech.command.copy.copied_and_click=已复制至剪贴板。点击以再次 gregtech.command.copy.click_to_copy=点击以复制 gregtech.command.copy.copied_start=已复制[ gregtech.command.copy.copied_end=]至剪贴板 +gregtech.command.datafix.usage=用法:/gregtech datafix +gregtech.command.datafix.bqu.usage=用法:/gregtech datafix [confirm] +gregtech.command.datafix.bqu.backup=备份你的存档和BQu配置文件,然后使用“confirm”参数重新运行 +gregtech.command.datafix.bqu.start=开始迁移BQu任务数据库… +gregtech.command.datafix.bqu.complete=BQu任务数据库迁移完成 +gregtech.command.datafix.bqu.failed=BQu任务数据库迁移失败。恢复你的备份! gregtech.chat.cape=§5恭喜你:你刚刚解锁了一件新披风!查看终端应用程序“披风选择器”来使用它。§r diff --git a/src/main/resources/assets/gregtech/textures/gui/overlay/menu_overlay.png b/src/main/resources/assets/gregtech/textures/gui/overlay/menu_overlay.png new file mode 100644 index 00000000000..dc166fac7e4 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/overlay/menu_overlay.png differ diff --git a/src/test/java/gregtech/api/recipes/recipeproperties/RecipePropertyStorageTest.java b/src/test/java/gregtech/api/recipes/recipeproperties/RecipePropertyStorageTest.java deleted file mode 100644 index e3daea5e015..00000000000 --- a/src/test/java/gregtech/api/recipes/recipeproperties/RecipePropertyStorageTest.java +++ /dev/null @@ -1,138 +0,0 @@ -package gregtech.api.recipes.recipeproperties; - -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.hamcrest.CoreMatchers.is; - -public class RecipePropertyStorageTest { - - private static final String propInt1Key = "propInt1"; - - private static final DefaultProperty propInt1 = new DefaultProperty<>(propInt1Key, Integer.class); - private static final DefaultProperty propInt2 = new DefaultProperty<>("propInt2", Integer.class); - private static final DefaultProperty propInt1_2 = new DefaultProperty<>("propInt1", Integer.class); - private static final DefaultProperty wrongCast = new DefaultProperty<>("wrongCast", Integer.class); - - private RecipePropertyStorage storage; - - @BeforeEach - public void initTestStub() { - this.storage = new RecipePropertyStorage(); - } - - @Test - public void storing_unique_recipe_properties_succeeds() { - MatcherAssert.assertThat(storage.store(propInt1, 1), is(true)); - MatcherAssert.assertThat(storage.store(propInt2, 1), is(true)); - } - - @Test - public void storing_same_property_twice_fails() { - MatcherAssert.assertThat(storage.store(propInt1, 1), is(true)); - MatcherAssert.assertThat(storage.store(propInt1, 1), is(false)); - } - - @Test - public void storing_unique_properties_with_same_key_fails() { - MatcherAssert.assertThat(storage.store(propInt1, 1), is(true)); - MatcherAssert.assertThat(storage.store(propInt1_2, 1), is(false)); - } - - @Test - public void storing_property_with_wrong_cast_fails() { - MatcherAssert.assertThat(storage.store(wrongCast, "This is not int"), is(false)); - } - - @Test - public void storing_property_without_value_fails() { - MatcherAssert.assertThat(storage.store(propInt1, null), is(false)); - } - - @Test - public void get_size_returns_correct_value() { - storage.store(propInt1, 1); // succeeds - - MatcherAssert.assertThat(storage.getSize(), is(1)); - - storage.store(propInt2, 2); // succeeds - - MatcherAssert.assertThat(storage.getSize(), is(2)); - - storage.store(propInt1, 1); // fails - - MatcherAssert.assertThat(storage.getSize(), is(2)); - } - - @Test - public void get_recipe_properties_returns_correct_value() { - storage.store(propInt1, 1); // succeeds - storage.store(propInt2, 2); // succeeds - - Map, Object> map = new HashMap<>(); - map.put(propInt1, 1); - map.put(propInt2, 2); - Set, Object>> expectedProperties = map.entrySet(); - - Set, Object>> actualProperties = storage.getRecipeProperties(); - - MatcherAssert.assertThat(actualProperties.size(), is(2)); - MatcherAssert.assertThat( - actualProperties.containsAll(expectedProperties) && expectedProperties.containsAll(actualProperties), - is(true)); - } - - @Test - public void get_recipe_property_value_returns_correct_value_if_exists() { - final int expectedValue = 1; - storage.store(propInt1, expectedValue); // succeeds - - int actual = storage.getRecipePropertyValue(propInt1, 0); - - MatcherAssert.assertThat(actual, is(expectedValue)); - } - - @Test - public void get_recipe_property_value_returns_default_value_if_does_not_exists() { - final int expectedValue = 0; - storage.store(propInt1, 1); // succeeds - - int actual = storage.getRecipePropertyValue(propInt2, expectedValue); - - MatcherAssert.assertThat(actual, is(expectedValue)); - } - - @Test - // CT way - public void get_recipe_property_keys() { - storage.store(propInt1, 1); // succeeds - storage.store(propInt2, 2); // succeeds - - Set expectedKeys = new HashSet<>(); - expectedKeys.add(propInt1.getKey()); - expectedKeys.add(propInt2.getKey()); - - Set actualKeys = storage.getRecipePropertyKeys(); - - MatcherAssert.assertThat(expectedKeys.containsAll(actualKeys) && actualKeys.containsAll(expectedKeys), - is(true)); - } - - @Test - // CT way - public void get_raw_recipe_property_value_via_string_key() { - final int expectedValue = 1; - - storage.store(propInt1, expectedValue); // succeeds - - Object actualValue = storage.getRawRecipePropertyValue(propInt1.getKey()); - - MatcherAssert.assertThat(actualValue, is(expectedValue)); - } -}