diff --git a/README.md b/README.md index 2607f1a..683aa86 100644 --- a/README.md +++ b/README.md @@ -41,11 +41,18 @@ Here is a list of implemented feature and considered features to be implemented: - [x] deposit upgrade (b/a) - [x] restock upgrade (b/a) - [x] filter upgrade (b/a) -- [ ] magnet upgrade (b/a) -- [ ] void upgrade (b/a) +- [x] magnet upgrade (b/a) +- [x] void upgrade (b/a) +- [x] refill_upgrade (b/a) +- [x] compacting_upgrade (b/a) - [x] crafting upgrade -- [ ] everlasting upgrade -- [ ] jukebox upgrade (low priority) +- [x] everlasting upgrade +- [x] tool_swapper_upgrade (b/a) +- [x] jukebox upgrade (low priority) +- [x] tank_upgrade +- [x] pump_upgrade (b/a) +- [x] battery_upgrade +- [x] mob_catcher_upgrade (b/a) ## Disclaimer diff --git a/build.gradle.kts b/build.gradle.kts index 109bcb0..a0bc313 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,7 +20,7 @@ plugins { id("maven-publish") id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.7" id("eclipse") - id("com.gtnewhorizons.retrofuturagradle") version "1.3.27" + id("com.gtnewhorizons.retrofuturagradle") version "1.4.9" id("com.matthewprenger.cursegradle") version "1.4.0" } diff --git a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/capability/Capabilities.java b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/capability/Capabilities.java index c8e8822..1d78b26 100644 --- a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/capability/Capabilities.java +++ b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/capability/Capabilities.java @@ -1,6 +1,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability; import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.*; +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherUpgradeWrapper; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.CapabilityInject; import org.jetbrains.annotations.NotNull; @@ -44,6 +45,63 @@ public final class Capabilities { @CapabilityInject(AdvancedFilterUpgradeWrapper.class) public static final @NotNull Capability ADVANCED_FILTER_UPGRADE_WRAPPER_CAPABILITY = null; + @CapabilityInject(MagnetUpgradeWrapper.class) + public static final @NotNull Capability MAGNET_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedMagnetUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_MAGNET_UPGRADE_CAPABILITY = null; + + @CapabilityInject(VoidUpgradeWrapper.class) + public static final @NotNull Capability VOID_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedVoidUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_VOID_UPGRADE_CAPABILITY = null; + + @CapabilityInject(RefillUpgradeWrapper.class) + public static final @NotNull Capability REFILL_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedRefillUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_REFILL_UPGRADE_CAPABILITY = null; + + @CapabilityInject(CompactingUpgradeWrapper.class) + public static final @NotNull Capability COMPACTING_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedCompactingUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_COMPACTING_UPGRADE_CAPABILITY = null; + + @CapabilityInject(EverlastingUpgradeWrapper.class) + public static final @NotNull Capability EVERLASTING_UPGRADE_CAPABILITY = null; + + @CapabilityInject(ToolSwapperUpgradeWrapper.class) + public static final @NotNull Capability TOOL_SWAPPER_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedToolSwapperUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_TOOL_SWAPPER_UPGRADE_CAPABILITY = null; + + @CapabilityInject(TankUpgradeWrapper.class) + public static final @NotNull Capability TANK_UPGRADE_CAPABILITY = null; + + @CapabilityInject(JukeboxUpgradeWrapper.class) + public static final @NotNull Capability JUKEBOX_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedJukeboxUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_JUKEBOX_UPGRADE_CAPABILITY = null; + + @CapabilityInject(PumpUpgradeWrapper.class) + public static final @NotNull Capability PUMP_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AdvancedPumpUpgradeWrapper.class) + public static final @NotNull Capability ADVANCED_PUMP_UPGRADE_CAPABILITY = null; + + @CapabilityInject(BatteryUpgradeWrapper.class) + public static final @NotNull Capability BATTERY_UPGRADE_CAPABILITY = null; + + @CapabilityInject(AnvilUpgradeWrapper.class) + public static final @NotNull Capability ANVIL_UPGRADE_CAPABILITY = null; + + @CapabilityInject(MobCatcherUpgradeWrapper.class) + public static final @NotNull Capability MOB_CATCHER_UPGRADE_CAPABILITY = null; + // Abstract capabilities @CapabilityInject(UpgradeWrapper.class) public static final @NotNull Capability> UPGRADE_CAPABILITY = null; @@ -71,4 +129,37 @@ public final class Capabilities { @CapabilityInject(IFilterUpgrade.class) public static final @NotNull Capability IFILTER_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IMagnetUpgrade.class) + public static final @NotNull Capability IMAGNET_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IVoidUpgrade.class) + public static final @NotNull Capability IVOID_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IRefillUpgrade.class) + public static final @NotNull Capability IREFILL_UPGRADE_CAPABILITY = null; + + @CapabilityInject(ICompactingUpgrade.class) + public static final @NotNull Capability ICOMPACTING_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IEverlastingUpgrade.class) + public static final @NotNull Capability IEVERLASTING_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IToolSwapperUpgrade.class) + public static final @NotNull Capability ITOOL_SWAPPER_UPGRADE_CAPABILITY = null; + + @CapabilityInject(ITankUpgrade.class) + public static final @NotNull Capability ITANK_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IJukeboxUpgrade.class) + public static final @NotNull Capability IJUKEBOX_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IPumpUpgrade.class) + public static final @NotNull Capability IPUMP_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IBatteryUpgrade.class) + public static final @NotNull Capability IBATTERY_UPGRADE_CAPABILITY = null; + + @CapabilityInject(IAnvilUpgrade.class) + public static final @NotNull Capability IANVIL_UPGRADE_CAPABILITY = null; } diff --git a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/client/RenderStateHelper.java b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/client/RenderStateHelper.java new file mode 100644 index 0000000..6b6a4aa --- /dev/null +++ b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/client/RenderStateHelper.java @@ -0,0 +1,66 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client; + +import net.minecraft.client.renderer.GlStateManager; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; + +import java.nio.ByteBuffer; + +@SideOnly(Side.CLIENT) +public final class RenderStateHelper { + private static final ByteBuffer COLOR_MASK = BufferUtils.createByteBuffer(16); + + private RenderStateHelper() { + } + + public static void syncGlStateManagerCache() { + forceSetToggle(GL11.glIsEnabled(GL11.GL_DEPTH_TEST), GlStateManager::enableDepth, GlStateManager::disableDepth); + forceSetToggle(GL11.glIsEnabled(GL11.GL_BLEND), GlStateManager::enableBlend, GlStateManager::disableBlend); + forceSetToggle(GL11.glIsEnabled(GL11.GL_CULL_FACE), GlStateManager::enableCull, GlStateManager::disableCull); + forceSetToggle(GL11.glIsEnabled(GL11.GL_LIGHTING), GlStateManager::enableLighting, GlStateManager::disableLighting); + forceSetToggle(GL11.glIsEnabled(GL11.GL_ALPHA_TEST), GlStateManager::enableAlpha, GlStateManager::disableAlpha); + forceSetToggle(GL11.glIsEnabled(GL11.GL_FOG), GlStateManager::enableFog, GlStateManager::disableFog); + GlStateManager.setActiveTexture(GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE)); + forceSetToggle(GL11.glIsEnabled(GL11.GL_TEXTURE_2D), GlStateManager::enableTexture2D, GlStateManager::disableTexture2D); + forceSetToggle(GL11.glIsEnabled(GL12.GL_RESCALE_NORMAL), GlStateManager::enableRescaleNormal, GlStateManager::disableRescaleNormal); + + boolean depthMask = GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK); + GlStateManager.depthMask(!depthMask); + GlStateManager.depthMask(depthMask); + GlStateManager.depthFunc(GL11.glGetInteger(GL11.GL_DEPTH_FUNC)); + GlStateManager.alphaFunc(GL11.glGetInteger(GL11.GL_ALPHA_TEST_FUNC), GL11.glGetFloat(GL11.GL_ALPHA_TEST_REF)); + GlStateManager.tryBlendFuncSeparate( + GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), + GL11.glGetInteger(GL14.GL_BLEND_DST_RGB), + GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA), + GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA) + ); + syncColorMask(); + + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.color(0.0F, 0.0F, 0.0F, 0.0F); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.bindTexture(GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D)); + } + + private static void syncColorMask() { + COLOR_MASK.clear(); + GL11.glGetBoolean(GL11.GL_COLOR_WRITEMASK, COLOR_MASK); + GlStateManager.colorMask(COLOR_MASK.get(0) != 0, COLOR_MASK.get(1) != 0, COLOR_MASK.get(2) != 0, COLOR_MASK.get(3) != 0); + } + + private static void forceSetToggle(boolean desired, Runnable enable, Runnable disable) { + if (desired) { + disable.run(); + enable.run(); + } else { + enable.run(); + disable.run(); + } + } +} diff --git a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/EntityItemAccessor.java b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/EntityItemAccessor.java new file mode 100644 index 0000000..bc59f01 --- /dev/null +++ b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/EntityItemAccessor.java @@ -0,0 +1,15 @@ +package com.cleanroommc.retrosophisticatedbackpacks.mixin; + +import net.minecraft.entity.item.EntityItem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntityItem.class) +public interface EntityItemAccessor { + @Accessor("pickupDelay") + int rsb$getPickupDelay(); + + @Accessor("age") + void rsb$setAge(int age); + +} diff --git a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerAccessor.java b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerAccessor.java new file mode 100644 index 0000000..50e72f0 --- /dev/null +++ b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerAccessor.java @@ -0,0 +1,19 @@ +package com.cleanroommc.retrosophisticatedbackpacks.mixin; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(GuiContainer.class) +public interface GuiContainerAccessor { + @Accessor("guiLeft") + int getGuiLeft(); + + @Accessor("guiTop") + int getGuiTop(); + + @Invoker("getSlotAtPosition") + Slot rsb$getSlotAtPosition(int mouseX, int mouseY); +} diff --git a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerMixin.java b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerMixin.java index 42917e2..efd466a 100644 --- a/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerMixin.java +++ b/src/main/java/com/cleanroommc/retrosophisticatedbackpacks/mixin/GuiContainerMixin.java @@ -1,5 +1,6 @@ package com.cleanroommc.retrosophisticatedbackpacks.mixin; +import com.cleanroommc.retrosophisticatedbackpacks.handler.ClientGuiStashHandler; import com.cleanroommc.retrosophisticatedbackpacks.handler.KeyInputHandler; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiContainer; @@ -14,4 +15,11 @@ public class GuiContainerMixin extends GuiScreen { private void keyTyped(char typedChar, int keyCode, CallbackInfo info) { KeyInputHandler.onKeyInputInGuiScreen(keyCode); } + + @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) + private void rsb$mouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo info) { + if (ClientGuiStashHandler.handleMouseClicked((GuiContainer) (Object) this, mouseX, mouseY, mouseButton)) { + info.cancel(); + } + } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/RetroSophisticatedBackpacks.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/RetroSophisticatedBackpacks.kt index 80dcd4a..21d8423 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/RetroSophisticatedBackpacks.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/RetroSophisticatedBackpacks.kt @@ -59,7 +59,7 @@ object RetroSophisticatedBackpacks { appleCoreLoaded = Loader.isModLoaded("applecore") proxy.preInit(event) - + FMLInterModComms.sendFunctionMessage("theoneprobe", "getTheOneProbe", OneProbePlugin::class.java.name) } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackInventoryHelper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackInventoryHelper.kt index 5be8ba2..6b54038 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackInventoryHelper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackInventoryHelper.kt @@ -2,6 +2,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.backpack import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem import net.minecraft.entity.Entity import net.minecraft.inventory.IInventory @@ -40,7 +41,7 @@ object BackpackInventoryHelper { val isMemorizedSlot = wrapper.isSlotMemorized(i) val baseStack = wrapper.getStackInSlot(i) - val maxSize = baseStack.maxStackSize * wrapper.getTotalStackMultiplier() + val maxSize = wrapper.getStackLimit(baseStack) for (j in i + 1 until wrapper.backpackInventorySize()) { if (isMemorizedSlot != wrapper.isSlotMemorized(j) || wrapper.isSlotLocked(j)) @@ -123,6 +124,11 @@ object BackpackInventoryHelper { ) { for (i in 9 until playerInventory.slots) { var stack = playerInventory.getStackInSlot(i) + if (stack.isEmpty) + continue + + if (transferMatched && !backpackContainsOrMemory(wrapper, stack)) + continue if (stack.item is BackpackItem) { val currentBackpackWrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) @@ -134,14 +140,8 @@ object BackpackInventoryHelper { continue } - for (j in 0 until wrapper.backpackInventorySize()) { - stack = wrapper.backpackItemStackHandler.insertItemToMemorySlots(stack, false) - - if (transferMatched && wrapper.getStackInSlot(j).isEmpty) - continue - - stack = wrapper.insertItem(j, stack, false) - } + stack = if (transferMatched) insertIntoMatchingBackpackSlots(wrapper, stack) + else insertIntoBackpackMatchingFirst(wrapper, stack) playerInventory.setStackInSlot(i, stack) } @@ -154,19 +154,105 @@ object BackpackInventoryHelper { ) { for (i in 0 until wrapper.backpackInventorySize()) { var stack = wrapper.getStackInSlot(i) + if (stack.isEmpty) + continue - for (j in 9 until playerInventory.slots) { - if (transferMatched && playerInventory.getStackInSlot(j).isEmpty) - continue + if (transferMatched && !handlerContains(playerInventory, stack, 0)) + continue - stack = playerInventory.insertItem(j, stack, false) - } + stack = if (transferMatched) insertIntoMatchingHandlerSlots(playerInventory, stack, 0) + else insertIntoHandlerMatchingFirst(playerInventory, stack, 9) wrapper.backpackItemStackHandler.setStackInSlot(i, stack) } } + private fun insertIntoBackpackMatchingFirst(wrapper: BackpackWrapper, stack: ItemStack): ItemStack { + var stack = insertIntoMatchingBackpackSlots(wrapper, stack) + for (slot in 0 until wrapper.backpackInventorySize()) { + stack = wrapper.insertItem(slot, stack, false) + if (stack.isEmpty) + break + } + return stack + } + + private fun insertIntoMatchingBackpackSlots(wrapper: BackpackWrapper, stack: ItemStack): ItemStack { + var stack = stack + for (slot in 0 until wrapper.backpackInventorySize()) { + if (!matchesStackKey(wrapper.getStackInSlot(slot), stack)) + continue + + stack = wrapper.insertItem(slot, stack, false) + if (stack.isEmpty) + return stack + } + + for (slot in 0 until wrapper.backpackInventorySize()) { + if (!matchesMemorySlot(wrapper, slot, stack)) + continue + + stack = wrapper.insertItem(slot, stack, false) + if (stack.isEmpty) + return stack + } + + return stack + } + + private fun insertIntoHandlerMatchingFirst(handler: IItemHandlerModifiable, stack: ItemStack, firstSlot: Int): ItemStack { + var stack = insertIntoMatchingHandlerSlots(handler, stack, firstSlot) + for (slot in firstSlot until handler.slots) { + stack = handler.insertItem(slot, stack, false) + if (stack.isEmpty) + break + } + return stack + } + + private fun insertIntoMatchingHandlerSlots(handler: IItemHandlerModifiable, stack: ItemStack, firstSlot: Int): ItemStack { + var stack = stack + for (slot in firstSlot until handler.slots) { + if (!matchesStackKey(handler.getStackInSlot(slot), stack)) + continue + + stack = handler.insertItem(slot, stack, false) + if (stack.isEmpty) + break + } + return stack + } + + private fun backpackContainsOrMemory(wrapper: BackpackWrapper, stack: ItemStack): Boolean = + handlerContains(wrapper.backpackItemStackHandler, stack, 0) || + wrapper.backpackItemStackHandler.memorizedSlotStack.indices.any { matchesMemorySlot(wrapper, it, stack) } + + private fun handlerContains(handler: IItemHandler, stack: ItemStack, firstSlot: Int): Boolean { + for (slot in firstSlot until handler.slots) { + if (matchesStackKey(handler.getStackInSlot(slot), stack)) { + return true + } + } + return false + } + + private fun matchesStackKey(first: ItemStack, second: ItemStack): Boolean = + !first.isEmpty && !second.isEmpty && + ItemStack.areItemsEqual(first, second) && + ItemStack.areItemStackTagsEqual(first, second) + + private fun matchesMemorySlot(wrapper: BackpackWrapper, slot: Int, stack: ItemStack): Boolean { + val memoryStack = wrapper.backpackItemStackHandler.memorizedSlotStack[slot] + return !memoryStack.isEmpty && !stack.isEmpty && + if (wrapper.backpackItemStackHandler.memorizedSlotRespectNbtList[slot]) + ItemStack.areItemStacksEqual(stack, memoryStack) + else stack.isItemEqualIgnoreDurability(memoryStack) + } + fun attemptDepositOnTileEntity(wrapper: BackpackWrapper, destination: TileEntity, facing: EnumFacing): Boolean { + if (Config.isInteractionBlockDisallowed(destination.blockType)) { + return false + } val destination = getHandler(destination, facing) ?: return false return attemptDepositOnItemHandler(wrapper, destination) } @@ -204,6 +290,9 @@ object BackpackInventoryHelper { } fun attemptRestockFromTileEntity(wrapper: BackpackWrapper, source: TileEntity, facing: EnumFacing): Boolean { + if (Config.isInteractionBlockDisallowed(source.blockType)) { + return false + } val source = getHandler(source, facing) ?: return false return attemptRestockFromItemHandler(wrapper, source) } @@ -262,4 +351,4 @@ object BackpackInventoryHelper { return true } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackStashHelper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackStashHelper.kt new file mode 100644 index 0000000..b2ae49c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/BackpackStashHelper.kt @@ -0,0 +1,45 @@ +package com.cleanroommc.retrosophisticatedbackpacks.backpack + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import net.minecraft.item.ItemStack +import net.minecraftforge.items.ItemHandlerHelper + +object BackpackStashHelper { + enum class Result { + MATCH_AND_SPACE, + SPACE, + NO_SPACE + } + + fun getStashResult(backpackStack: ItemStack, stack: ItemStack): Result { + val wrapper = backpackStack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return Result.NO_SPACE + return getStashResult(wrapper, stack) + } + + fun getStashResult(wrapper: BackpackWrapper, stack: ItemStack): Result { + if (stack.isEmpty || wrapper.insertStack(stack.copy(), true, true).count == stack.count) { + return Result.NO_SPACE + } + return if (hasMatchingStack(wrapper, stack) || hasMatchingMemory(wrapper, stack)) Result.MATCH_AND_SPACE else Result.SPACE + } + + fun stash(wrapper: BackpackWrapper, stack: ItemStack, simulate: Boolean): ItemStack = + wrapper.insertStack(stack.copy(), simulate, true) + + private fun hasMatchingStack(wrapper: BackpackWrapper, stack: ItemStack): Boolean = + (0 until wrapper.slots).any { + val slotStack = wrapper.getStackInSlot(it) + !slotStack.isEmpty && ItemHandlerHelper.canItemStacksStack(slotStack, stack) + } + + private fun hasMatchingMemory(wrapper: BackpackWrapper, stack: ItemStack): Boolean = + (0 until wrapper.backpackInventorySize()).any { + val memoryStack = wrapper.getMemorizedStack(it) + !memoryStack.isEmpty && if (wrapper.isMemoryStackRespectNBT(it)) { + ItemStack.areItemStacksEqual(stack, memoryStack) + } else { + stack.isItemEqualIgnoreDurability(memoryStack) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/DisplaySide.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/DisplaySide.kt new file mode 100644 index 0000000..748f9e9 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/backpack/DisplaySide.kt @@ -0,0 +1,18 @@ +package com.cleanroommc.retrosophisticatedbackpacks.backpack + +enum class DisplaySide(val serializedName: String) { + FRONT("front"), + LEFT("left"), + RIGHT("right"); + + fun next(): DisplaySide = + entries[(ordinal + 1) % entries.size] + + fun previous(): DisplaySide = + entries[(ordinal + entries.size - 1) % entries.size] + + companion object { + fun fromName(name: String): DisplaySide = + entries.firstOrNull { it.serializedName == name } ?: FRONT + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/block/BackpackBlock.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/block/BackpackBlock.kt index 9604324..0fde1e6 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/block/BackpackBlock.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/block/BackpackBlock.kt @@ -2,7 +2,10 @@ package com.cleanroommc.retrosophisticatedbackpacks.block import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackTier +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.EverlastingUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.JukeboxUpgradeWrapper import com.cleanroommc.retrosophisticatedbackpacks.handler.RegistryHandler import com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity import com.cleanroommc.retrosophisticatedbackpacks.util.IModelRegister @@ -177,9 +180,17 @@ class BackpackBlock( val tileEntity = worldIn.getTileEntity(pos) as? BackpackTileEntity ?: return tileEntity.wrapper.deserializeNBT(backpackInventory.serializeNBT()) - if (stack.hasDisplayName()) tileEntity.setCustomName(stack.displayName) + + val (leftTank, rightTank) = tileEntity.wrapper.tankRenderSides() + worldIn.setBlockState( + pos, + state.withProperty(LEFT_TANK, leftTank) + .withProperty(RIGHT_TANK, rightTank) + .withProperty(BATTERY, tileEntity.wrapper.hasBatteryUpgrade()), + 3 + ) } override fun onBlockActivated( @@ -195,6 +206,11 @@ class BackpackBlock( ): Boolean { if (!worldIn.isRemote) { if (playerIn.isSneaking) { + val tileEntity = worldIn.getTileEntity(pos) as? BackpackTileEntity + if (tileEntity?.wrapper?.hasEverlastingUpgrade() == true) { + return true + } + tileEntity?.wrapper?.stopJukeboxUpgrades() worldIn.playSound(playerIn, pos, SoundEvents.BLOCK_CLOTH_BREAK, SoundCategory.BLOCKS, 1f, 0.5f) dropBlockAsItem(worldIn, pos, state, 0) worldIn.setBlockState(pos, net.minecraft.init.Blocks.AIR.defaultState) @@ -237,11 +253,13 @@ class BackpackBlock( fortune: Int ) { val tileEntity = world.getTileEntity(pos) as? BackpackTileEntity ?: return + if (tileEntity.wrapper.hasEverlastingUpgrade()) { + return + } val stack = ItemStack(Item.getItemFromBlock(this)) val tileEntityBackpackInventory = tileEntity.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return val stackBackpackInventory = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return stackBackpackInventory.deserializeNBT(tileEntityBackpackInventory.serializeNBT()) - if (tileEntity.hasCustomName()) stack.setStackDisplayName(tileEntity.name) @@ -260,6 +278,11 @@ class BackpackBlock( player: EntityPlayer, willHarvest: Boolean ): Boolean { + val tileEntity = world.getTileEntity(pos) as? BackpackTileEntity + if (tileEntity?.wrapper?.hasEverlastingUpgrade() == true) { + return false + } + tileEntity?.wrapper?.stopJukeboxUpgrades() if (willHarvest) return true return super.removedByPlayer(state, world, pos, player, false) } @@ -275,4 +298,15 @@ class BackpackBlock( super.harvestBlock(worldIn, player, pos, state, te, stack) worldIn.setBlockToAir(pos) } + + private fun BackpackWrapper.hasEverlastingUpgrade(): Boolean = + gatherCapabilityUpgrades(Capabilities.EVERLASTING_UPGRADE_CAPABILITY) + .filterIsInstance() + .isNotEmpty() + + private fun BackpackWrapper.stopJukeboxUpgrades() { + gatherCapabilityUpgrades(Capabilities.IJUKEBOX_UPGRADE_CAPABILITY) + .filterIsInstance() + .forEach { it.requestStop() } + } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackEnergyStorage.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackEnergyStorage.kt new file mode 100644 index 0000000..d4ba2de --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackEnergyStorage.kt @@ -0,0 +1,48 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability + +import net.minecraftforge.energy.IEnergyStorage + +class BackpackEnergyStorage(private val wrapper: BackpackWrapper) : IEnergyStorage { + private fun batteries() = + wrapper.gatherCapabilityUpgrades(Capabilities.IBATTERY_UPGRADE_CAPABILITY) + + override fun receiveEnergy(maxReceive: Int, simulate: Boolean): Int { + var remaining = maxReceive + var received = 0 + for (battery in batteries()) { + val moved = battery.receiveEnergy(wrapper, remaining, simulate) + received += moved + remaining -= moved + if (remaining <= 0) { + break + } + } + return received + } + + override fun extractEnergy(maxExtract: Int, simulate: Boolean): Int { + var remaining = maxExtract + var extracted = 0 + for (battery in batteries()) { + val moved = battery.extractEnergy(wrapper, remaining, simulate) + extracted += moved + remaining -= moved + if (remaining <= 0) { + break + } + } + return extracted + } + + override fun getEnergyStored(): Int = + batteries().fold(0) { acc, battery -> acc + battery.energyStored } + + override fun getMaxEnergyStored(): Int = + batteries().fold(0) { acc, battery -> acc + battery.getMaxEnergyStored(wrapper) } + + override fun canExtract(): Boolean = + batteries().any { it.canExtractEnergy } + + override fun canReceive(): Boolean = + batteries().any { it.canReceiveEnergy } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackFluidHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackFluidHandler.kt new file mode 100644 index 0000000..cdcfda1 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackFluidHandler.kt @@ -0,0 +1,55 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.TankUpgradeWrapper +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.capability.IFluidHandler +import net.minecraftforge.fluids.capability.IFluidTankProperties + +class BackpackFluidHandler(private val wrapper: BackpackWrapper) : IFluidHandler { + private fun tanks(): List = + wrapper.gatherCapabilityUpgrades(Capabilities.TANK_UPGRADE_CAPABILITY) + .filterIsInstance() + + override fun getTankProperties(): Array = + tanks().flatMap { it.getTankProperties(wrapper).toList() }.toTypedArray() + + override fun fill(resource: FluidStack?, doFill: Boolean): Int { + if (resource == null || resource.amount <= 0) { + return 0 + } + var filled = 0 + for (tank in tanks()) { + val toFill = FluidStack(resource.fluid, resource.amount - filled, resource.tag?.copy()) + filled += tank.fill(wrapper, toFill, doFill) + if (filled >= resource.amount) { + return resource.amount + } + } + return filled + } + + override fun drain(resource: FluidStack?, doDrain: Boolean): FluidStack? { + if (resource == null || resource.amount <= 0) { + return null + } + var drained = 0 + for (tank in tanks()) { + val stack = tank.drain(wrapper, FluidStack(resource.fluid, resource.amount - drained, resource.tag?.copy()), doDrain) ?: continue + drained += stack.amount + if (drained >= resource.amount) { + return FluidStack(resource.fluid, resource.amount, resource.tag?.copy()) + } + } + return if (drained > 0) FluidStack(resource.fluid, drained, resource.tag?.copy()) else null + } + + override fun drain(maxDrain: Int, doDrain: Boolean): FluidStack? { + for (tank in tanks()) { + val drained = tank.drain(wrapper, maxDrain, doDrain) + if (drained != null && drained.amount > 0) { + return drained + } + } + return null + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackFluidItemHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackFluidItemHandler.kt new file mode 100644 index 0000000..9cd734c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackFluidItemHandler.kt @@ -0,0 +1,27 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability + +import net.minecraft.item.ItemStack +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.capability.IFluidHandlerItem +import net.minecraftforge.fluids.capability.IFluidTankProperties + +class BackpackFluidItemHandler( + private val container: ItemStack, + wrapper: BackpackWrapper +) : IFluidHandlerItem { + private val delegate = BackpackFluidHandler(wrapper) + + override fun getContainer(): ItemStack = container + + override fun getTankProperties(): Array = + delegate.tankProperties + + override fun fill(resource: FluidStack?, doFill: Boolean): Int = + delegate.fill(resource, doFill) + + override fun drain(resource: FluidStack?, doDrain: Boolean): FluidStack? = + delegate.drain(resource, doDrain) + + override fun drain(maxDrain: Int, doDrain: Boolean): FluidStack? = + delegate.drain(maxDrain, doDrain) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackWrapper.kt index 94b7ad0..38f4bdd 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/BackpackWrapper.kt @@ -1,27 +1,57 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability +import com.cleanroommc.retrosophisticatedbackpacks.backpack.DisplaySide import com.cleanroommc.retrosophisticatedbackpacks.backpack.SortType +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.ICompactingUpgrade +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IMagnetUpgrade +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedCompactingUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedVoidUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.CompactingUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.VoidUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.CapturedMob +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherStorage +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.inventory.BackpackItemStackHandler +import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler import com.cleanroommc.retrosophisticatedbackpacks.inventory.UpgradeItemStackHandler import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem import com.cleanroommc.retrosophisticatedbackpacks.item.ExponentialStackUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.item.InceptionUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.item.StackUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.mixin.EntityItemAccessor import com.cleanroommc.retrosophisticatedbackpacks.util.BackpackItemStackHelper +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.item.EntityItem +import net.minecraft.init.SoundEvents +import net.minecraft.inventory.Container +import net.minecraft.inventory.InventoryCrafting +import net.minecraft.item.EnumDyeColor import net.minecraft.item.ItemStack +import net.minecraft.item.crafting.CraftingManager import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing +import net.minecraft.util.SoundCategory +import net.minecraft.util.math.AxisAlignedBB +import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.ITextComponent +import net.minecraft.util.text.TextComponentString +import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.world.World import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.fluids.capability.CapabilityFluidHandler +import net.minecraftforge.energy.CapabilityEnergy import net.minecraftforge.items.CapabilityItemHandler import net.minecraftforge.items.IItemHandler +import net.minecraftforge.items.ItemHandlerHelper import java.util.* class BackpackWrapper( var backpackInventorySize: () -> Int = { 27 }, var upgradeSlotsSize: () -> Int = { 1 }, var uuid: UUID = UUID.randomUUID(), + private val containerStack: ItemStack = ItemStack.EMPTY, ) : IItemHandler, ISidelessCapabilityProvider, INBTSerializable { companion object { private const val BACKPACK_INVENTORY_TAG = "BackpackInventory" @@ -31,12 +61,24 @@ class BackpackWrapper( private const val MAIN_COLOR_TAG = "MainColor" private const val ACCENT_COLOR_TAG = "AccentColor" + private const val CUSTOM_NAME_TAG = "CustomName" private const val MEMORY_STACK_ITEMS_TAG = "MemoryItems" private const val MEMORY_STACK_RESPECT_NBT_TAG = "MemoryRespectNBT" private const val SORT_TYPE_TAG = "SortType" private const val LOCKED_SLOTS_TAG = "LockedSlots" - + private const val MAIN_SETTINGS_TAG = "MainSettings" + private const val MAIN_SETTINGS_CONTEXT_TAG = "Context" + private const val MAIN_SETTINGS_SHIFT_CLICK_INTO_OPEN_TAB_TAG = "ShiftClickIntoOpenTab" + private const val MAIN_SETTINGS_KEEP_TAB_OPEN_TAG = "KeepTabOpen" + private const val MAIN_SETTINGS_KEEP_SEARCH_PHRASE_TAG = "KeepSearchPhrase" + private const val MAIN_SETTINGS_SEARCH_PHRASE_TAG = "SearchPhrase" + private const val MAIN_SETTINGS_ANOTHER_PLAYER_CAN_OPEN_TAG = "AnotherPlayerCanOpen" + private const val ITEM_DISPLAY_SETTINGS_TAG = "ItemDisplay" + private const val ITEM_DISPLAY_SLOTS_TAG = "Slots" + private const val ITEM_DISPLAY_ROTATIONS_TAG = "Rotations" + private const val ITEM_DISPLAY_COLOR_TAG = "Color" + private const val ITEM_DISPLAY_SIDE_TAG = "DisplaySide" private const val UUID_TAG = "UUID" const val DEFAULT_MAIN_COLOR: Int = -0x339ec6 @@ -45,11 +87,37 @@ class BackpackWrapper( var isCached: Boolean = false var backpackItemStackHandler = BackpackItemStackHandler(backpackInventorySize(), this) - var upgradeItemStackHandler = UpgradeItemStackHandler(upgradeSlotsSize()) + var upgradeItemStackHandler = UpgradeItemStackHandler(upgradeSlotsSize(), this) var sortType: SortType = SortType.BY_NAME var mainColor = DEFAULT_MAIN_COLOR var accentColor = DEFAULT_ACCENT_COLOR + var customName: String? = null + var isGuiInteractionInProgress = false + var settingsContext: SettingsContext = SettingsContext.PLAYER + var shiftClickIntoOpenTab = false + var keepTabOpen = true + var keepSearchPhrase = false + var searchPhrase = "" + var anotherPlayerCanOpen = Config.allowOpeningOtherPlayerBackpacks + var itemDisplayColor: EnumDyeColor = EnumDyeColor.RED + var itemDisplaySide: DisplaySide = DisplaySide.FRONT + private val itemDisplaySlots = linkedSetOf() + private val itemDisplayRotations = mutableMapOf() + private val slotsToCompact = mutableSetOf() + private var compactingSlotsInitialized = false + private val slotsToVoid = mutableSetOf() + val capturedMobs: MutableList = mutableListOf() + var capturedMobsColumns = 0 + private var updatingCapturedMobLayout = false + + enum class SettingsContext { + PLAYER, + STORAGE; + + fun next(): SettingsContext = + if (this == PLAYER) STORAGE else PLAYER + } fun isStackedByMultiplication(): Boolean = upgradeItemStackHandler.inventory.map(ItemStack::getItem).filterIsInstance().any() @@ -74,6 +142,12 @@ class BackpackWrapper( return stackUpgradeItems.fold(base) { acc, item -> func(acc, item.multiplier()) } } + fun getTotalStackMultiplier(stack: ItemStack): Int = + if (Config.canStackWithStackUpgrade(stack)) getTotalStackMultiplier() else 1 + + fun getStackLimit(stack: ItemStack): Int = + stack.maxStackSize * getTotalStackMultiplier(stack) + fun canAddStackUpgrade(newMultiplier: Int): Boolean { // Ensures no overflow for vanilla itemstack, no guarantee for modded itemstack val currentMultiplier = getTotalStackMultiplier() * 64 @@ -94,14 +168,14 @@ class BackpackWrapper( val newStackMultiplier = getTotalStackMultiplier() / oldMultiplier * newMultiplier for (stack in backpackItemStackHandler.inventory) { - if (stack.isEmpty) + if (stack.isEmpty || !Config.canStackWithStackUpgrade(stack)) continue if (stack.count > stack.maxStackSize * newStackMultiplier) return false } - return true + return canFitBatteryEnergyWithMultiplier(newStackMultiplier) } fun canAddExponentialStackUpgrade(): Boolean { @@ -119,14 +193,14 @@ class BackpackWrapper( val byAddMultiplier = getTotalStackMultiplier(false) for (stack in backpackItemStackHandler.inventory) { - if (stack.isEmpty) + if (stack.isEmpty || !Config.canStackWithStackUpgrade(stack)) continue if (stack.count > stack.maxStackSize * byAddMultiplier) return false } - return true + return canFitBatteryEnergyWithMultiplier(byAddMultiplier) } fun canNestBackpack(): Boolean = @@ -168,6 +242,354 @@ class BackpackWrapper( else filterUpgrades.any { it.canInsert(stack) } } + fun ensureCapturedMobLayoutCurrent() { + if (updatingCapturedMobLayout) + return + + updatingCapturedMobLayout = true + try { + MobCatcherStorage.ensureLayoutCurrent(this) + } finally { + updatingCapturedMobLayout = false + } + } + + fun isSlotBlockedByMobCatcher(slotIndex: Int): Boolean { + ensureCapturedMobLayoutCurrent() + return MobCatcherStorage.isSlotBlocked(this, slotIndex) + } + + fun onBeforeInsert(stack: ItemStack): ItemStack = + if (shouldVoid(stack, false, false)) ItemStack.EMPTY else stack + + fun onSlotChanged(slotIndex: Int, fromGui: Boolean = false) { + val stack = getStackInSlot(slotIndex) + if (!fromGui || shouldVoidInGui(slotIndex, stack)) { + slotsToVoid.add(slotIndex) + } + if (!fromGui || shouldCompactInGui()) { + slotsToCompact.add(slotIndex) + } + } + + fun onGuiSlotChanged(slotIndex: Int) { + onSlotChanged(slotIndex, true) + } + + fun shouldHandleSlotChangeFromGui(): Boolean = + isGuiInteractionInProgress + + fun insertStack(stack: ItemStack, simulate: Boolean, processInsertUpgrades: Boolean = false): ItemStack { + val stack = if (processInsertUpgrades) onBeforeInsert(stack) else stack + if (stack.isEmpty) { + return ItemStack.EMPTY + } + + var remaining = backpackItemStackHandler.insertItemToMemorySlots(stack, simulate) + for (slot in 0 until slots) { + if (remaining.isEmpty) { + return ItemStack.EMPTY + } + remaining = insertItem(slot, remaining, simulate) + } + return if (processInsertUpgrades) onInsertRemainder(remaining) else remaining + } + + fun onInsertRemainder(remaining: ItemStack): ItemStack = + if (shouldVoid(remaining, true, hasMatchingStack(remaining))) ItemStack.EMPTY else remaining + + fun extractMatching(filter: ItemStack, amount: Int, simulate: Boolean): ItemStack { + var remaining = amount + var extracted = ItemStack.EMPTY + + for (slot in 0 until slots) { + val stack = getStackInSlot(slot) + if (stack.isEmpty || !ItemHandlerHelper.canItemStacksStack(stack, filter)) { + continue + } + + val slotExtracted = extractItem(slot, minOf(stack.count, remaining), simulate) + if (extracted.isEmpty) { + extracted = slotExtracted.copy() + } else { + extracted.grow(slotExtracted.count) + } + remaining -= slotExtracted.count + + if (remaining <= 0) { + break + } + } + + return extracted + } + + fun tickUpgrades(player: EntityPlayer?, world: World, x: Double, y: Double, z: Double, pos: BlockPos = BlockPos(x, y, z)) { + if (world.totalWorldTime % 5L == 0L) { + if (player != null) { + gatherCapabilityUpgrades(Capabilities.IREFILL_UPGRADE_CAPABILITY).forEach { it.refill(player, this) } + } else if (gatherCapabilityUpgrades(Capabilities.IREFILL_UPGRADE_CAPABILITY).isNotEmpty()) { + val players = world.getEntitiesWithinAABB(EntityPlayer::class.java, AxisAlignedBB(pos).grow(3.0)) + players.forEach { nearbyPlayer -> + gatherCapabilityUpgrades(Capabilities.IREFILL_UPGRADE_CAPABILITY).forEach { it.refill(nearbyPlayer, this) } + } + } + } + + if (world.totalWorldTime % 10L == 0L) { + magnetItems(player, world, pos) + } + + gatherCapabilityUpgrades(Capabilities.ITANK_UPGRADE_CAPABILITY).forEach { it.tick(this, world) } + gatherCapabilityUpgrades(Capabilities.IBATTERY_UPGRADE_CAPABILITY).forEach { it.tick(this, world) } + gatherCapabilityUpgrades(Capabilities.IPUMP_UPGRADE_CAPABILITY).forEach { it.tick(player, this, world, pos) } + gatherCapabilityUpgrades(Capabilities.IJUKEBOX_UPGRADE_CAPABILITY) + .forEach { it.tick(world, pos) } + + val compactingUpgrades = gatherCapabilityUpgrades(Capabilities.ICOMPACTING_UPGRADE_CAPABILITY) + if (compactingUpgrades.isNotEmpty()) { + if (!compactingSlotsInitialized) { + slotsToCompact.addAll(0 until slots) + compactingSlotsInitialized = true + } + if (world.totalWorldTime % 5L == 0L && slotsToCompact.isNotEmpty()) { + compactingUpgrades.forEach { it.compact(this, world) } + slotsToCompact.clear() + } + } else { + compactingSlotsInitialized = false + slotsToCompact.clear() + } + + if (slotsToVoid.isNotEmpty()) { + for (slot in slotsToVoid.toSet()) { + val stack = getStackInSlot(slot) + if (!stack.isEmpty && shouldVoidInGui(slot, stack)) { + extractItem(slot, stack.count, false) + } + } + slotsToVoid.clear() + } + } + + fun compactChangedSlots( + world: World, + upgrade: ICompactingUpgrade, + shouldCompactThreeByThree: Boolean, + compactNonUncraftable: Boolean, + stackFilter: (ItemStack) -> Boolean + ) { + val slots = if (slotsToCompact.isEmpty()) (0 until this.slots).toSet() else slotsToCompact.toSet() + for (slot in slots) { + compactSlot(world, slot, shouldCompactThreeByThree, compactNonUncraftable, stackFilter) + } + } + + private fun magnetItems(player: EntityPlayer?, world: World, pos: BlockPos) { + val magnetUpgrades = gatherCapabilityUpgrades(Capabilities.IMAGNET_UPGRADE_CAPABILITY) + if (magnetUpgrades.isEmpty()) { + return + } + + val range = magnetUpgrades.map(IMagnetUpgrade::range).max() + val items = world.getEntitiesWithinAABB(EntityItem::class.java, AxisAlignedBB(pos).grow(range)) + + for (entityItem in items) { + if (entityItem.isDead || (entityItem as EntityItemAccessor).`rsb$getPickupDelay`() == 32767 || magnetUpgrades.none { it.canPickup(entityItem.item, this) }) { + continue + } + + val original = entityItem.item.copy() + val remaining = insertStack(original, false, true) + if (remaining.count != original.count) { + entityItem.item = remaining + player?.world?.playSound( + null, + player.posX, + player.posY, + player.posZ, + SoundEvents.ENTITY_ITEM_PICKUP, + SoundCategory.PLAYERS, + 0.2f, + (world.rand.nextFloat() - world.rand.nextFloat()) * 1.4f + 2.0f + ) + if (remaining.isEmpty) { + entityItem.setDead() + } else { + entityItem.setNoPickupDelay() + } + player?.onItemPickup(entityItem, original.count - remaining.count) + } + } + } + + private fun compactSlot( + world: World, + slot: Int, + shouldCompactThreeByThree: Boolean, + compactNonUncraftable: Boolean, + stackFilter: (ItemStack) -> Boolean + ) { + val stack = getStackInSlot(slot) + if (stack.isEmpty || !stackFilter(stack)) { + return + } + + if (shouldCompactThreeByThree && tryCompact(world, stack, 3, 3, compactNonUncraftable)) { + return + } + tryCompact(world, stack, 2, 2, compactNonUncraftable) + } + + private fun tryCompact(world: World, stack: ItemStack, width: Int, height: Int, compactNonUncraftable: Boolean): Boolean { + val count = width * height + val compactingResult = getCompactingResult(world, stack, width, height) + val result = compactingResult.result + if (result.isEmpty || (!compactNonUncraftable && !canUncompact(world, result, stack, count))) { + return false + } + + var compacted = false + val stacksToInsert = listOf(result) + compactingResult.remainingItems + while (extractMatching(stack, count, true).count == count && canInsertAll(stacksToInsert)) { + extractMatching(stack, count, false) + stacksToInsert.forEach { insertStack(it.copy(), false) } + compacted = true + } + return compacted + } + + private fun getCompactingResult(world: World, stack: ItemStack, width: Int, height: Int): CompactingResult { + val inventory = InventoryCrafting(DummyContainer, width, height) + for (slot in 0 until width * height) { + inventory.setInventorySlotContents(slot, ItemHandlerHelper.copyStackWithSize(stack, 1)) + } + val recipe = CraftingManager.findMatchingRecipe(inventory, world) ?: return CompactingResult.EMPTY + return CompactingResult( + recipe.getCraftingResult(inventory), + recipe.getRemainingItems(inventory).filterNot(ItemStack::isEmpty).map(ItemStack::copy) + ) + } + + private fun canInsertAll(stacks: List): Boolean { + val simulation = ExposedItemStackHandler(slots) + for (slot in 0 until slots) { + simulation.setStackInSlot(slot, getStackInSlot(slot).copy()) + } + + for (stack in stacks) { + if (!insertIntoSimulation(simulation, stack.copy()).isEmpty) { + return false + } + } + return true + } + + private fun insertIntoSimulation(simulation: ExposedItemStackHandler, stack: ItemStack): ItemStack { + var remaining = stack + for (slot in 0 until simulation.slots) { + if (isSlotBlockedByMobCatcher(slot)) { + continue + } + val existing = simulation.getStackInSlot(slot) + if (existing.isEmpty) { + val moved = minOf(remaining.count, getStackLimit(remaining)) + simulation.setStackInSlot(slot, ItemHandlerHelper.copyStackWithSize(remaining, moved)) + remaining = ItemHandlerHelper.copyStackWithSize(remaining, remaining.count - moved) + if (remaining.isEmpty) { + return ItemStack.EMPTY + } + continue + } + if (!ItemHandlerHelper.canItemStacksStack(existing, remaining)) { + continue + } + val moved = minOf(remaining.count, getStackLimit(existing) - existing.count) + if (moved > 0) { + existing.grow(moved) + remaining = ItemHandlerHelper.copyStackWithSize(remaining, remaining.count - moved) + if (remaining.isEmpty) { + return ItemStack.EMPTY + } + } + } + return remaining + } + + private fun canUncompact(world: World, result: ItemStack, input: ItemStack, expectedCount: Int): Boolean { + val inventory = InventoryCrafting(DummyContainer, 3, 3) + inventory.setInventorySlotContents(0, ItemHandlerHelper.copyStackWithSize(result, 1)) + val recipe = CraftingManager.findMatchingRecipe(inventory, world) ?: return false + val uncompactResult = recipe.getCraftingResult(inventory) + return ItemHandlerHelper.canItemStacksStack(uncompactResult, input) && uncompactResult.count >= expectedCount + } + + private fun shouldVoid(stack: ItemStack, storageFull: Boolean, hasMatchingStack: Boolean): Boolean { + if (stack.isEmpty) { + return false + } + + return gatherCapabilityUpgrades(Capabilities.IVOID_UPGRADE_CAPABILITY).any { + when (it) { + is VoidUpgradeWrapper -> it.shouldVoidOverflow(stack, storageFull, hasMatchingStack) + is AdvancedVoidUpgradeWrapper -> it.shouldVoidOverflow(stack, storageFull, hasMatchingStack) + else -> it.shouldVoid(stack) + } + } + } + + private fun shouldVoidInGui(slotIndex: Int, stack: ItemStack): Boolean = + gatherCapabilityUpgrades(Capabilities.IVOID_UPGRADE_CAPABILITY).any { + when (it) { + is VoidUpgradeWrapper -> it.shouldWorkInGui && it.shouldVoidOverflow(stack, false, hasMatchingStack(slotIndex, stack)) + is AdvancedVoidUpgradeWrapper -> it.shouldWorkInGui && it.shouldVoidOverflow(stack, false, hasMatchingStack(slotIndex, stack)) + else -> false + } + } + + private fun shouldCompactInGui(): Boolean = + gatherCapabilityUpgrades(Capabilities.ICOMPACTING_UPGRADE_CAPABILITY).any { + when (it) { + is CompactingUpgradeWrapper -> it.enabled && it.shouldWorkInGui + is AdvancedCompactingUpgradeWrapper -> it.enabled && it.shouldWorkInGui + else -> false + } + } + + private fun hasMatchingStack(stack: ItemStack): Boolean = + backpackItemStackHandler.inventory.any { !it.isEmpty && ItemHandlerHelper.canItemStacksStack(it, stack) } + + private fun hasMatchingStack(changedSlot: Int, stack: ItemStack): Boolean = + backpackItemStackHandler.inventory.withIndex() + .any { (slot, storedStack) -> slot != changedSlot && !storedStack.isEmpty && ItemHandlerHelper.canItemStacksStack(storedStack, stack) } + + fun matchesStorageContents(stack: ItemStack, matcher: (ItemStack, ItemStack) -> Boolean): Boolean = + (0 until slots).any { + val storedStack = getStackInSlot(it) + (!storedStack.isEmpty && matcher(stack, storedStack)) || matchesMemoryStack(it, stack, matcher) + } + + private fun matchesMemoryStack(slotIndex: Int, stack: ItemStack, matcher: (ItemStack, ItemStack) -> Boolean): Boolean { + val memoryStack = getMemorizedStack(slotIndex) + return !memoryStack.isEmpty && if (isMemoryStackRespectNBT(slotIndex)) { + stack.isItemEqual(memoryStack) && stack.tagCompound == memoryStack.tagCompound + } else { + matcher(stack, memoryStack) + } + } + + private fun isFull(): Boolean = + (0 until slots).all { + val stack = getStackInSlot(it) + !stack.isEmpty && stack.count >= getSlotLimit(it) + } + + private data class CompactingResult(val result: ItemStack, val remainingItems: List) { + companion object { + val EMPTY = CompactingResult(ItemStack.EMPTY, emptyList()) + } + } + fun canExtract(slotIndex: Int): Boolean { val stack = getStackInSlot(slotIndex) val filterUpgrades = gatherCapabilityUpgrades(Capabilities.IFILTER_UPGRADE_CAPABILITY) @@ -217,15 +639,136 @@ class BackpackWrapper( backpackItemStackHandler.sortLockedSlots[slotIndex] = locked } + fun toggleSettingsContext() { + settingsContext = settingsContext.next() + } + + fun toggleShiftClickIntoOpenTab() { + shiftClickIntoOpenTab = !shiftClickIntoOpenTab + } + + fun toggleKeepTabOpen() { + keepTabOpen = !keepTabOpen + } + + fun toggleKeepSearchPhrase() { + keepSearchPhrase = !keepSearchPhrase + if (!keepSearchPhrase) { + searchPhrase = "" + } + } + + fun toggleAnotherPlayerCanOpen() { + if (!Config.allowOpeningOtherPlayerBackpacks) { + anotherPlayerCanOpen = false + return + } + anotherPlayerCanOpen = !anotherPlayerCanOpen + } + + fun isItemDisplaySlotSelected(slotIndex: Int): Boolean = + !Config.itemDisplayDisabled && slotIndex in itemDisplaySlots + + fun selectItemDisplaySlot(slotIndex: Int) { + if (Config.itemDisplayDisabled || slotIndex !in 0 until backpackInventorySize() || slotIndex in itemDisplaySlots) { + return + } + if (itemDisplaySlots.size + 1 > 1) { + return + } + itemDisplaySlots.add(slotIndex) + } + + fun unselectItemDisplaySlot(slotIndex: Int) { + itemDisplaySlots.remove(slotIndex) + itemDisplayRotations.remove(slotIndex) + } + + fun getFirstItemDisplaySlot(): Int = + if (Config.itemDisplayDisabled) -1 else itemDisplaySlots.firstOrNull() ?: -1 + + fun getItemDisplaySlots(): Set = + if (Config.itemDisplayDisabled) emptySet() else itemDisplaySlots + + fun getItemDisplayRotation(slotIndex: Int): Int = + itemDisplayRotations[slotIndex] ?: 0 + + fun rotateItemDisplaySlot(slotIndex: Int, clockwise: Boolean) { + if (Config.itemDisplayDisabled || slotIndex !in itemDisplaySlots) { + return + } + itemDisplayRotations[slotIndex] = (getItemDisplayRotation(slotIndex) + if (clockwise) 45 else -45 + 360) % 360 + } + + fun getDisplayItem(): DisplayItem? { + if (Config.itemDisplayDisabled) { + return null + } + val slotIndex = getFirstItemDisplaySlot() + if (slotIndex !in 0 until backpackInventorySize()) { + return null + } + val stack = getStackInSlot(slotIndex).takeIf { !it.isEmpty } + ?: getMemorizedStack(slotIndex).takeIf { !it.isEmpty } + ?: return null + return DisplayItem(ItemHandlerHelper.copyStackWithSize(stack, 1), getItemDisplayRotation(slotIndex), itemDisplaySide) + } + + data class DisplayItem(val stack: ItemStack, val rotation: Int, val side: DisplaySide) + // This is only meant to used for bogosorter as RSB already implemented a sorting mechanism fun getSortableSlotIndexes(): List = - (0.. gatherCapabilityUpgrades(capability: Capability): List = + fun gatherCapabilityUpgrades(capability: Capability): List = upgradeItemStackHandler.inventory .mapNotNull { it.getCapability(capability, null) } - // Overrides + fun tankUpgradeSlots(): List = + upgradeItemStackHandler.inventory.mapIndexedNotNull { slot, upgrade -> + if (upgrade.getCapability(Capabilities.TANK_UPGRADE_CAPABILITY, null) != null) slot else null + } + + fun hasTankUpgrade(): Boolean = + tankUpgradeSlots().isNotEmpty() + + fun batteryUpgradeSlots(): List = + upgradeItemStackHandler.inventory.mapIndexedNotNull { slot, upgrade -> + if (upgrade.getCapability(Capabilities.BATTERY_UPGRADE_CAPABILITY, null) != null) slot else null + } + + fun hasBatteryUpgrade(): Boolean = + batteryUpgradeSlots().isNotEmpty() + + fun canAddBatteryUpgrade(): Boolean = + !hasBatteryUpgrade() + + fun hasMobCatcherUpgrade(): Boolean = + upgradeItemStackHandler.inventory.any { it.getCapability(Capabilities.MOB_CATCHER_UPGRADE_CAPABILITY, null) != null } + + fun canAddMobCatcherUpgrade(): Boolean = + !hasMobCatcherUpgrade() + + fun canFitBatteryEnergyWithMultiplier(stackMultiplier: Int): Boolean = + gatherCapabilityUpgrades(Capabilities.IBATTERY_UPGRADE_CAPABILITY) + .all { it.energyStored <= it.getMaxEnergyStored(this, stackMultiplier) } + + fun tankRenderSides(): Pair { + val tankSlots = tankUpgradeSlots() + return (tankSlots.size > 1) to tankSlots.isNotEmpty() + } + + private object DummyContainer : Container() { + override fun canInteractWith(playerIn: EntityPlayer): Boolean = false + } override fun getSlots(): Int = backpackItemStackHandler.slots @@ -234,17 +777,35 @@ class BackpackWrapper( backpackItemStackHandler.getStackInSlot(index) override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack = - backpackItemStackHandler.insertItem(slot, stack, simulate) + if (isSlotBlockedByMobCatcher(slot)) stack else backpackItemStackHandler.insertItem(slot, stack, simulate) override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack = - backpackItemStackHandler.extractItem(slot, amount, simulate) + if (isSlotBlockedByMobCatcher(slot)) ItemStack.EMPTY else backpackItemStackHandler.extractItem(slot, amount, simulate) override fun getSlotLimit(slot: Int): Int = - backpackItemStackHandler.getSlotLimit(slot) + if (isSlotBlockedByMobCatcher(slot)) 0 else backpackItemStackHandler.getSlotLimit(slot) override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = capability == Capabilities.BACKPACK_CAPABILITY || - capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY + capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY || + capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && Config.itemFluidHandlerEnabled && hasTankUpgrade() || + capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY && + Config.itemFluidHandlerEnabled && hasTankUpgrade() && !containerStack.isEmpty || + capability == CapabilityEnergy.ENERGY && hasBatteryUpgrade() + + @Suppress("UNCHECKED_CAST") + override fun getCapability(capability: Capability, facing: EnumFacing?): T? = + when { + capability == Capabilities.BACKPACK_CAPABILITY -> this as T + capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY -> this as T + capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && + Config.itemFluidHandlerEnabled && hasTankUpgrade() -> BackpackFluidHandler(this) as T + capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY && + Config.itemFluidHandlerEnabled && hasTankUpgrade() && !containerStack.isEmpty -> + BackpackFluidItemHandler(containerStack, this) as T + capability == CapabilityEnergy.ENERGY && hasBatteryUpgrade() -> BackpackEnergyStorage(this) as T + else -> null + } override fun serializeNBT(): NBTTagCompound { val nbt = NBTTagCompound() @@ -260,6 +821,8 @@ class BackpackWrapper( nbt.setInteger(MAIN_COLOR_TAG, mainColor) nbt.setInteger(ACCENT_COLOR_TAG, accentColor) + customName?.let { nbt.setString(CUSTOM_NAME_TAG, it) } + // Settings val memoryNbt = NBTTagCompound() BackpackItemStackHelper.saveAllSlotsExtended(memoryNbt, backpackItemStackHandler.memorizedSlotStack) @@ -275,6 +838,29 @@ class BackpackWrapper( backpackItemStackHandler.sortLockedSlots.map { if (it) 1 else 0 }.map(Int::toByte).toByteArray() ) + val mainSettingsNbt = NBTTagCompound() + mainSettingsNbt.setByte(MAIN_SETTINGS_CONTEXT_TAG, settingsContext.ordinal.toByte()) + mainSettingsNbt.setBoolean(MAIN_SETTINGS_SHIFT_CLICK_INTO_OPEN_TAB_TAG, shiftClickIntoOpenTab) + mainSettingsNbt.setBoolean(MAIN_SETTINGS_KEEP_TAB_OPEN_TAG, keepTabOpen) + mainSettingsNbt.setBoolean(MAIN_SETTINGS_KEEP_SEARCH_PHRASE_TAG, keepSearchPhrase) + if (keepSearchPhrase && searchPhrase.isNotEmpty()) { + mainSettingsNbt.setString(MAIN_SETTINGS_SEARCH_PHRASE_TAG, searchPhrase) + } + mainSettingsNbt.setBoolean(MAIN_SETTINGS_ANOTHER_PLAYER_CAN_OPEN_TAG, anotherPlayerCanOpen) + nbt.setTag(MAIN_SETTINGS_TAG, mainSettingsNbt) + + val itemDisplayNbt = NBTTagCompound() + itemDisplayNbt.setIntArray(ITEM_DISPLAY_SLOTS_TAG, itemDisplaySlots.toIntArray()) + val rotationsNbt = NBTTagCompound() + itemDisplayRotations.forEach { (slot, rotation) -> rotationsNbt.setInteger(slot.toString(), rotation) } + itemDisplayNbt.setTag(ITEM_DISPLAY_ROTATIONS_TAG, rotationsNbt) + itemDisplayNbt.setByte(ITEM_DISPLAY_COLOR_TAG, itemDisplayColor.ordinal.toByte()) + itemDisplayNbt.setString(ITEM_DISPLAY_SIDE_TAG, itemDisplaySide.serializedName) + nbt.setTag(ITEM_DISPLAY_SETTINGS_TAG, itemDisplayNbt) + ensureCapturedMobLayoutCurrent() + nbt.setTag(MobCatcherStorage.CAPTURED_MOBS_TAG, MobCatcherStorage.serialize(capturedMobs)) + nbt.setInteger(MobCatcherStorage.CAPTURED_MOBS_COLUMNS_TAG, capturedMobsColumns) + nbt.setUniqueId(UUID_TAG, uuid) return nbt } @@ -288,11 +874,14 @@ class BackpackWrapper( uuid = nbt.getUniqueId(UUID_TAG)!! backpackItemStackHandler = BackpackItemStackHandler(backpackInventorySize(), this) - upgradeItemStackHandler = UpgradeItemStackHandler(upgradeSlotsSize()) + upgradeItemStackHandler = UpgradeItemStackHandler(upgradeSlotsSize(), this) mainColor = nbt.getInteger(MAIN_COLOR_TAG) accentColor = nbt.getInteger(ACCENT_COLOR_TAG) + if (nbt.hasKey(CUSTOM_NAME_TAG)) + customName = nbt.getString(CUSTOM_NAME_TAG) + if (nbt.hasKey(BACKPACK_INVENTORY_TAG)) BackpackItemStackHelper.loadAllItemsExtended( nbt.getCompoundTag(BACKPACK_INVENTORY_TAG), @@ -319,6 +908,52 @@ class BackpackWrapper( setSlotLocked(index, b.toInt() != 0) } + if (nbt.hasKey(MAIN_SETTINGS_TAG)) { + val mainSettingsNbt = nbt.getCompoundTag(MAIN_SETTINGS_TAG) + settingsContext = SettingsContext.entries.getOrElse(mainSettingsNbt.getByte(MAIN_SETTINGS_CONTEXT_TAG).toInt()) { + SettingsContext.PLAYER + } + shiftClickIntoOpenTab = mainSettingsNbt.getBoolean(MAIN_SETTINGS_SHIFT_CLICK_INTO_OPEN_TAB_TAG) + keepTabOpen = if (mainSettingsNbt.hasKey(MAIN_SETTINGS_KEEP_TAB_OPEN_TAG)) + mainSettingsNbt.getBoolean(MAIN_SETTINGS_KEEP_TAB_OPEN_TAG) + else true + keepSearchPhrase = mainSettingsNbt.getBoolean(MAIN_SETTINGS_KEEP_SEARCH_PHRASE_TAG) + searchPhrase = + if (keepSearchPhrase && mainSettingsNbt.hasKey(MAIN_SETTINGS_SEARCH_PHRASE_TAG)) + mainSettingsNbt.getString(MAIN_SETTINGS_SEARCH_PHRASE_TAG) + else "" + anotherPlayerCanOpen = Config.allowOpeningOtherPlayerBackpacks && + (!mainSettingsNbt.hasKey(MAIN_SETTINGS_ANOTHER_PLAYER_CAN_OPEN_TAG) || + mainSettingsNbt.getBoolean(MAIN_SETTINGS_ANOTHER_PLAYER_CAN_OPEN_TAG)) + } + + itemDisplaySlots.clear() + itemDisplayRotations.clear() + if (nbt.hasKey(ITEM_DISPLAY_SETTINGS_TAG)) { + val itemDisplayNbt = nbt.getCompoundTag(ITEM_DISPLAY_SETTINGS_TAG) + itemDisplayNbt.getIntArray(ITEM_DISPLAY_SLOTS_TAG) + .filter { it in 0 until backpackInventorySize() } + .forEach(itemDisplaySlots::add) + val rotationsNbt = itemDisplayNbt.getCompoundTag(ITEM_DISPLAY_ROTATIONS_TAG) + rotationsNbt.keySet.forEach { key -> + key.toIntOrNull() + ?.takeIf { it in itemDisplaySlots } + ?.let { itemDisplayRotations[it] = rotationsNbt.getInteger(key) } + } + itemDisplayColor = EnumDyeColor.entries.getOrElse(itemDisplayNbt.getByte(ITEM_DISPLAY_COLOR_TAG).toInt()) { + EnumDyeColor.RED + } + itemDisplaySide = DisplaySide.fromName(itemDisplayNbt.getString(ITEM_DISPLAY_SIDE_TAG)) + } + + capturedMobs.clear() + capturedMobs.addAll(MobCatcherStorage.deserialize(nbt.getTagList(MobCatcherStorage.CAPTURED_MOBS_TAG, 10))) + capturedMobsColumns = if (nbt.hasKey(MobCatcherStorage.CAPTURED_MOBS_COLUMNS_TAG)) { + nbt.getInteger(MobCatcherStorage.CAPTURED_MOBS_COLUMNS_TAG) + } else { + MobCatcherStorage.getColumns(this) + } + sortType = SortType.entries[nbt.getByte(SORT_TYPE_TAG).toInt()] } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedDepositUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedDepositUpgradeWrapper.kt index 7424b65..45b696c 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedDepositUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedDepositUpgradeWrapper.kt @@ -1,13 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.DepositUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class AdvancedDepositUpgradeWrapper : AdvancedUpgradeWrapper(), IDepositUpgrade { +class AdvancedDepositUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedDepositUpgrade.filterSlots, Config.advancedDepositUpgrade.slotsInRow), + IDepositUpgrade { override val settingsLangKey: String = "gui.advanced_deposit_settings".asTranslationKey() override fun canDeposit(stack: ItemStack): Boolean = @@ -17,4 +20,4 @@ class AdvancedDepositUpgradeWrapper : AdvancedUpgradeWrapper capability == Capabilities.ADVANCED_DEPOSIT_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) || super.hasCapability(capability, facing) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFeedingUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFeedingUpgradeWrapper.kt index 800a51e..2353a53 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFeedingUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFeedingUpgradeWrapper.kt @@ -1,22 +1,21 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade -import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackDataFixer import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler import com.cleanroommc.retrosophisticatedbackpacks.item.FeedingUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.BackpackItemStackHelper import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey -import net.minecraft.item.ItemFood import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -import net.minecraftforge.fml.common.Loader import net.minecraftforge.items.IItemHandler -import squeek.applecore.api.AppleCoreAPI -class AdvancedFeedingUpgradeWrapper : AdvancedUpgradeWrapper(), IFeedingUpgrade { +class AdvancedFeedingUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedFeedingUpgrade.filterSlots, Config.advancedFeedingUpgrade.slotsInRow), + IFeedingUpgrade { companion object { private const val HUNGER_FEEDING_STRATEGY_TAG = "HungerFeedingStrategy" private const val HURT_FEEDING_STRATEGY_TAG = "HurtFeedingStrategy" @@ -24,7 +23,7 @@ class AdvancedFeedingUpgradeWrapper : AdvancedUpgradeWrapper override val settingsLangKey: String = "gui.advanced_feeding_settings".asTranslationKey() - override val filterItems: ExposedItemStackHandler = object : ExposedItemStackHandler(16) { + override val filterItems: ExposedItemStackHandler = object : ExposedItemStackHandler(Config.advancedFeedingUpgrade.filterSlots) { override fun isItemValid(slot: Int, stack: ItemStack): Boolean = IFeedingUpgrade.isValidFood(stack) } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFilterUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFilterUpgradeWrapper.kt index 6b427ee..76c5213 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFilterUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedFilterUpgradeWrapper.kt @@ -1,6 +1,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.FilterUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack @@ -8,8 +9,10 @@ import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class AdvancedFilterUpgradeWrapper : AdvancedUpgradeWrapper(), IFilterUpgrade { - override val settingsLangKey: String = "gui.advanced_filter_upgrade".asTranslationKey() +class AdvancedFilterUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedFilterUpgrade.filterSlots, Config.advancedFilterUpgrade.slotsInRow), + IFilterUpgrade { + override val settingsLangKey: String = "gui.advanced_filter_settings".asTranslationKey() override var filterWay: IFilterUpgrade.FilterWayType = IFilterUpgrade.FilterWayType.IN_OUT override fun canInsert(stack: ItemStack): Boolean { @@ -39,6 +42,7 @@ class AdvancedFilterUpgradeWrapper : AdvancedUpgradeWrapper() override fun deserializeNBT(nbt: NBTTagCompound) { super.deserializeNBT(nbt) - filterWay = IFilterUpgrade.FilterWayType.entries[nbt.getByte(IFilterUpgrade.FILTER_WAY_TAG).toInt()] + if (nbt.hasKey(IFilterUpgrade.FILTER_WAY_TAG)) + filterWay = IFilterUpgrade.FilterWayType.entries.getOrElse(nbt.getByte(IFilterUpgrade.FILTER_WAY_TAG).toInt()) { filterWay } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedPickupUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedPickupUpgradeWrapper.kt index 20ebc0e..04db38d 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedPickupUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedPickupUpgradeWrapper.kt @@ -1,13 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.PickupUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class AdvancedPickupUpgradeWrapper : AdvancedUpgradeWrapper(), IPickupUpgrade { +class AdvancedPickupUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedPickupUpgrade.filterSlots, Config.advancedPickupUpgrade.slotsInRow), + IPickupUpgrade { override val settingsLangKey: String = "gui.advanced_pickup_settings".asTranslationKey() override fun canPickup(stack: ItemStack): Boolean = @@ -17,4 +20,4 @@ class AdvancedPickupUpgradeWrapper : AdvancedUpgradeWrapper() capability == Capabilities.ADVANCED_PICKUP_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) || super.hasCapability(capability, facing) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedRestockUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedRestockUpgradeWrapper.kt index a0fa35a..d2cfbd1 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedRestockUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedRestockUpgradeWrapper.kt @@ -1,13 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.RestockUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class AdvancedRestockUpgradeWrapper : AdvancedUpgradeWrapper(), IRestockUpgrade { +class AdvancedRestockUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedRestockUpgrade.filterSlots, Config.advancedRestockUpgrade.slotsInRow), + IRestockUpgrade { override val settingsLangKey: String = "gui.advanced_restock_settings".asTranslationKey() override fun canRestock(stack: ItemStack): Boolean = @@ -17,4 +20,4 @@ class AdvancedRestockUpgradeWrapper : AdvancedUpgradeWrapper capability == Capabilities.ADVANCED_RESTOCK_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) || super.hasCapability(capability, facing) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedUpgradeWrapper.kt index b9323a9..5e59b91 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AdvancedUpgradeWrapper.kt @@ -10,10 +10,11 @@ import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.util.Constants -abstract class AdvancedUpgradeWrapper : UpgradeWrapper(), IToggleable, IAdvancedFilterable where T : UpgradeItem { +abstract class AdvancedUpgradeWrapper(filterSlots: Int = 16, override val slotsInRow: Int = 4) : + UpgradeWrapper(), IToggleable, IAdvancedFilterable where T : UpgradeItem { override var enabled = true override var filterType = IBasicFilterable.FilterType.WHITELIST - override val filterItems: ExposedItemStackHandler = ExposedItemStackHandler(16) + override val filterItems: ExposedItemStackHandler = ExposedItemStackHandler(filterSlots) override var matchType = IAdvancedFilterable.MatchType.ITEM override var oreDictEntries = mutableListOf() override var ignoreDurability = true @@ -47,16 +48,25 @@ abstract class AdvancedUpgradeWrapper : UpgradeWrapper(), IToggleable, IAd override fun deserializeNBT(nbt: NBTTagCompound) { super.deserializeNBT(nbt) - enabled = nbt.getBoolean(IToggleable.ENABLED_TAG) - filterItems.deserializeNBT(nbt.getCompoundTag(IBasicFilterable.FILTER_ITEMS_TAG)) - filterType = IBasicFilterable.FilterType.entries[nbt.getByte(IBasicFilterable.FILTER_TYPE_TAG).toInt()] - matchType = IAdvancedFilterable.MatchType.entries[nbt.getByte(IAdvancedFilterable.MATCH_TYPE_TAG).toInt()] - ignoreDurability = nbt.getBoolean(IAdvancedFilterable.IGNORE_DURABILITY_TAG) - ignoreNBT = nbt.getBoolean(IAdvancedFilterable.IGNORE_NBT_TAG) + if (nbt.hasKey(IToggleable.ENABLED_TAG)) + enabled = nbt.getBoolean(IToggleable.ENABLED_TAG) + if (nbt.hasKey(IBasicFilterable.FILTER_ITEMS_TAG)) + filterItems.deserializeNBT(nbt.getCompoundTag(IBasicFilterable.FILTER_ITEMS_TAG)) + if (nbt.hasKey(IBasicFilterable.FILTER_TYPE_TAG)) + filterType = IBasicFilterable.FilterType.entries.getOrElse(nbt.getByte(IBasicFilterable.FILTER_TYPE_TAG).toInt()) { filterType } + if (nbt.hasKey(IAdvancedFilterable.MATCH_TYPE_TAG)) + matchType = IAdvancedFilterable.MatchType.entries.getOrElse(nbt.getByte(IAdvancedFilterable.MATCH_TYPE_TAG).toInt()) { matchType } + if (nbt.hasKey(IAdvancedFilterable.IGNORE_DURABILITY_TAG)) + ignoreDurability = nbt.getBoolean(IAdvancedFilterable.IGNORE_DURABILITY_TAG) + if (nbt.hasKey(IAdvancedFilterable.IGNORE_NBT_TAG)) + ignoreNBT = nbt.getBoolean(IAdvancedFilterable.IGNORE_NBT_TAG) + if (!nbt.hasKey(IAdvancedFilterable.ORE_DICT_LIST_TAG)) + return val oreDictList = nbt.getTagList(IAdvancedFilterable.ORE_DICT_LIST_TAG, Constants.NBT.TAG_STRING) + oreDictEntries.clear() for (stringNBT in oreDictList) oreDictEntries.add((stringNBT as NBTTagString).string) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AnvilUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AnvilUpgradeWrapper.kt new file mode 100644 index 0000000..6c8a441 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/AnvilUpgradeWrapper.kt @@ -0,0 +1,118 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler +import com.cleanroommc.retrosophisticatedbackpacks.item.AnvilUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.ContainerRepair +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.items.IItemHandler + +class AnvilUpgradeWrapper : UpgradeWrapper(), IAnvilUpgrade { + companion object { + private const val INVENTORY_TAG = "Inventory" + private const val SHIFT_CLICK_TAG = "ShiftClickIntoStorage" + private const val ITEM_NAME_TAG = "ItemName" + private const val MAXIMUM_COST_TAG = "MaximumCost" + private const val MATERIAL_COST_TAG = "MaterialCost" + private const val RESULT_TAG = "Result" + } + + override val settingsLangKey = "gui.anvil_settings".asTranslationKey() + override var shouldShiftClickIntoStorage = true + override var itemName = "" + override var maximumCost = 0 + private set + override var materialCost = 0 + private set + private var result = ItemStack.EMPTY + private val inventory = object : ExposedItemStackHandler(2) { + override fun onContentsChanged(slot: Int) { + result = ItemStack.EMPTY + maximumCost = 0 + materialCost = 0 + } + } + + override fun getInventory(): IItemHandler = + inventory + + override fun updateRepairOutput(player: EntityPlayer, world: World): ItemStack { + val container = createContainer(player, world) + maximumCost = container.maximumCost + materialCost = container.materialCost + result = container.getSlot(2).stack.copy() + return result.copy() + } + + override fun canTakeResult(player: EntityPlayer): Boolean = + !result.isEmpty && maximumCost > 0 && (player.capabilities.isCreativeMode || player.experienceLevel >= maximumCost) + + override fun takeResult(player: EntityPlayer, world: World): ItemStack { + updateRepairOutput(player, world) + if (!canTakeResult(player)) { + return ItemStack.EMPTY + } + val container = createContainer(player, world) + val resultSlot = container.getSlot(2) + val taken = resultSlot.stack.copy() + resultSlot.onTake(player, taken.copy()) + inventory.setStackInSlot(0, container.getSlot(0).stack.copy()) + inventory.setStackInSlot(1, container.getSlot(1).stack.copy()) + result = ItemStack.EMPTY + maximumCost = 0 + materialCost = 0 + return taken + } + + private fun createContainer(player: EntityPlayer, world: World): ContainerRepair { + val container = ContainerRepair(player.inventory, world, BlockPos(player), player) + container.getSlot(0).putStack(inventory.getStackInSlot(0).copy()) + container.getSlot(1).putStack(inventory.getStackInSlot(1).copy()) + container.updateItemName(itemName) + container.updateRepairOutput() + return container + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setTag(INVENTORY_TAG, inventory.serializeNBT()) + nbt.setBoolean(SHIFT_CLICK_TAG, shouldShiftClickIntoStorage) + nbt.setString(ITEM_NAME_TAG, itemName) + nbt.setInteger(MAXIMUM_COST_TAG, maximumCost) + nbt.setInteger(MATERIAL_COST_TAG, materialCost) + if (!result.isEmpty) { + nbt.setTag(RESULT_TAG, result.writeToNBT(NBTTagCompound())) + } + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(INVENTORY_TAG)) + inventory.deserializeNBT(nbt.getCompoundTag(INVENTORY_TAG)) + if (nbt.hasKey(SHIFT_CLICK_TAG)) + shouldShiftClickIntoStorage = nbt.getBoolean(SHIFT_CLICK_TAG) + if (nbt.hasKey(ITEM_NAME_TAG)) + itemName = nbt.getString(ITEM_NAME_TAG) + if (nbt.hasKey(MAXIMUM_COST_TAG)) + maximumCost = nbt.getInteger(MAXIMUM_COST_TAG) + if (nbt.hasKey(MATERIAL_COST_TAG)) + materialCost = nbt.getInteger(MATERIAL_COST_TAG) + if (nbt.hasKey(RESULT_TAG)) + result = ItemStack(nbt.getCompoundTag(RESULT_TAG)) + else if (nbt.hasKey(INVENTORY_TAG)) + result = ItemStack.EMPTY + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ANVIL_UPGRADE_CAPABILITY || + capability == Capabilities.IANVIL_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BasicUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BasicUpgradeWrapper.kt index cb0a143..f42f32f 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BasicUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BasicUpgradeWrapper.kt @@ -7,10 +7,11 @@ import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -abstract class BasicUpgradeWrapper : UpgradeWrapper(), IToggleable, IBasicFilterable where T : UpgradeItem { +abstract class BasicUpgradeWrapper(filterSlots: Int = 9, override val slotsInRow: Int = 3) : + UpgradeWrapper(), IToggleable, IBasicFilterable where T : UpgradeItem { override var enabled = true override var filterType = IBasicFilterable.FilterType.WHITELIST - override val filterItems = ExposedItemStackHandler(9) + override val filterItems = ExposedItemStackHandler(filterSlots) override fun checkFilter(stack: ItemStack): Boolean = enabled && super.checkFilter(stack) @@ -30,8 +31,11 @@ abstract class BasicUpgradeWrapper : UpgradeWrapper(), IToggleable, IBasic override fun deserializeNBT(nbt: NBTTagCompound) { super.deserializeNBT(nbt) - enabled = nbt.getBoolean(IToggleable.ENABLED_TAG) - filterItems.deserializeNBT(nbt.getCompoundTag(IBasicFilterable.FILTER_ITEMS_TAG)) - filterType = IBasicFilterable.FilterType.entries[nbt.getByte(IBasicFilterable.FILTER_TYPE_TAG).toInt()] + if (nbt.hasKey(IToggleable.ENABLED_TAG)) + enabled = nbt.getBoolean(IToggleable.ENABLED_TAG) + if (nbt.hasKey(IBasicFilterable.FILTER_ITEMS_TAG)) + filterItems.deserializeNBT(nbt.getCompoundTag(IBasicFilterable.FILTER_ITEMS_TAG)) + if (nbt.hasKey(IBasicFilterable.FILTER_TYPE_TAG)) + filterType = IBasicFilterable.FilterType.entries.getOrElse(nbt.getByte(IBasicFilterable.FILTER_TYPE_TAG).toInt()) { filterType } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BatteryUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BatteryUpgradeWrapper.kt new file mode 100644 index 0000000..ab15c7d --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/BatteryUpgradeWrapper.kt @@ -0,0 +1,138 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler +import com.cleanroommc.retrosophisticatedbackpacks.item.BatteryUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.energy.CapabilityEnergy +import net.minecraftforge.items.IItemHandler + +class BatteryUpgradeWrapper : UpgradeWrapper(), IBatteryUpgrade { + companion object { + private const val ENERGY_TAG = "Energy" + private const val INVENTORY_TAG = "Inventory" + private const val INPUT_SLOT = 0 + private const val OUTPUT_SLOT = 1 + } + + override val settingsLangKey = "gui.battery_settings".asTranslationKey() + override var energyStored = 0 + private set + override val canExtractEnergy = true + override val canReceiveEnergy = true + + private val inventory = object : ExposedItemStackHandler(2) { + override fun getSlotLimit(slot: Int): Int = 1 + + override fun isItemValid(slot: Int, stack: ItemStack): Boolean = + stack.isEmpty || when (slot) { + INPUT_SLOT -> isValidEnergyItem(stack, output = false) + OUTPUT_SLOT -> isValidEnergyItem(stack, output = true) + else -> false + } + } + + override fun getMaxEnergyStored(wrapper: BackpackWrapper): Int = + getMaxEnergyStored(wrapper, wrapper.getTotalStackMultiplier()) + + override fun getMaxEnergyStored(wrapper: BackpackWrapper, stackMultiplier: Int): Int = + (getSlotRows(wrapper) * Config.batteryUpgrade.energyPerSlotRow * getAdjustedStackMultiplier(stackMultiplier)).toInt() + + override fun receiveEnergy(wrapper: BackpackWrapper, maxReceive: Int, simulate: Boolean): Int { + val accepted = minOf(maxReceive, getMaxInOut(wrapper), getMaxEnergyStored(wrapper) - energyStored) + if (!simulate && accepted > 0) { + energyStored += accepted + } + return accepted.coerceAtLeast(0) + } + + override fun extractEnergy(wrapper: BackpackWrapper, maxExtract: Int, simulate: Boolean): Int { + val extracted = minOf(maxExtract, getMaxInOut(wrapper), energyStored) + if (!simulate && extracted > 0) { + energyStored -= extracted + } + return extracted.coerceAtLeast(0) + } + + override fun tick(wrapper: BackpackWrapper, world: World) { + if (world.isRemote) { + return + } + if (energyStored < getMaxEnergyStored(wrapper)) { + receiveFromContainer(wrapper) + } + if (energyStored > 0) { + extractToContainer(wrapper) + } + } + + override fun getInventory(): IItemHandler = + inventory + + private fun receiveFromContainer(wrapper: BackpackWrapper) { + val stack = inventory.getStackInSlot(INPUT_SLOT) + val energyStorage = stack.getCapability(CapabilityEnergy.ENERGY, null) ?: return + val toReceive = receiveEnergy(wrapper, getMaxInOut(wrapper), true) + val extracted = energyStorage.extractEnergy(toReceive, true) + if (extracted <= 0) { + return + } + energyStorage.extractEnergy(extracted, false) + receiveEnergy(wrapper, extracted, false) + inventory.setStackInSlot(INPUT_SLOT, stack) + } + + private fun extractToContainer(wrapper: BackpackWrapper) { + val stack = inventory.getStackInSlot(OUTPUT_SLOT) + val energyStorage = stack.getCapability(CapabilityEnergy.ENERGY, null) ?: return + val toExtract = extractEnergy(wrapper, getMaxInOut(wrapper), true) + val received = energyStorage.receiveEnergy(toExtract, true) + if (received <= 0) { + return + } + energyStorage.receiveEnergy(received, false) + extractEnergy(wrapper, received, false) + inventory.setStackInSlot(OUTPUT_SLOT, stack) + } + + private fun isValidEnergyItem(stack: ItemStack, output: Boolean): Boolean { + val energyStorage = stack.getCapability(CapabilityEnergy.ENERGY, null) ?: return false + return if (output) energyStorage.canReceive() else energyStorage.canExtract() && energyStorage.energyStored > 0 + } + + private fun getMaxInOut(wrapper: BackpackWrapper): Int = + maxOf(1, (getSlotRows(wrapper) * Config.batteryUpgrade.maxInputOutput * getAdjustedStackMultiplier(wrapper.getTotalStackMultiplier())).toInt()) + + private fun getSlotRows(wrapper: BackpackWrapper): Int = + maxOf(1, (wrapper.backpackInventorySize() + 8) / 9) + + private fun getAdjustedStackMultiplier(stackMultiplier: Int): Double = + 1.0 + Config.batteryUpgrade.stackMultiplierRatio * (stackMultiplier - 1) + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setInteger(ENERGY_TAG, energyStored) + nbt.setTag(INVENTORY_TAG, inventory.serializeNBT()) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(ENERGY_TAG)) + energyStored = nbt.getInteger(ENERGY_TAG) + if (nbt.hasKey(INVENTORY_TAG)) + inventory.deserializeNBT(nbt.getCompoundTag(INVENTORY_TAG)) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.BATTERY_UPGRADE_CAPABILITY || + capability == Capabilities.IBATTERY_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CompactingUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CompactingUpgradeWrapper.kt new file mode 100644 index 0000000..bdc278c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CompactingUpgradeWrapper.kt @@ -0,0 +1,107 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.UpgradeFilterUtils.matchesAllowEmpty +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.item.CompactingUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability + +open class CompactingUpgradeWrapper : + BasicUpgradeWrapper(Config.compactingUpgrade.filterSlots, Config.compactingUpgrade.slotsInRow), + ICompactingUpgrade { + companion object { + private const val COMPACT_NON_UNCRAFTABLE_TAG = "CompactNonUncraftable" + private const val SHOULD_WORK_IN_GUI_TAG = "ShouldWorkInGui" + } + + override val settingsLangKey = "gui.compacting_settings".asTranslationKey() + var compactNonUncraftable = false + var shouldWorkInGui = false + + override fun compact(wrapper: BackpackWrapper, world: World) { + if (enabled) { + wrapper.compactChangedSlots(world, this, false, compactNonUncraftable) { matchesAllowEmpty(it) } + } + } + + fun toggleCompactNonUncraftable() { + compactNonUncraftable = !compactNonUncraftable + } + + fun toggleWorkInGui() { + shouldWorkInGui = !shouldWorkInGui + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setBoolean(COMPACT_NON_UNCRAFTABLE_TAG, compactNonUncraftable) + nbt.setBoolean(SHOULD_WORK_IN_GUI_TAG, shouldWorkInGui) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(COMPACT_NON_UNCRAFTABLE_TAG)) + compactNonUncraftable = nbt.getBoolean(COMPACT_NON_UNCRAFTABLE_TAG) + if (nbt.hasKey(SHOULD_WORK_IN_GUI_TAG)) + shouldWorkInGui = nbt.getBoolean(SHOULD_WORK_IN_GUI_TAG) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.COMPACTING_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} + +class AdvancedCompactingUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedCompactingUpgrade.filterSlots, Config.advancedCompactingUpgrade.slotsInRow), + ICompactingUpgrade { + companion object { + private const val COMPACT_NON_UNCRAFTABLE_TAG = "CompactNonUncraftable" + private const val SHOULD_WORK_IN_GUI_TAG = "ShouldWorkInGui" + } + + override val settingsLangKey = "gui.advanced_compacting_settings".asTranslationKey() + var compactNonUncraftable = false + var shouldWorkInGui = false + + override fun compact(wrapper: BackpackWrapper, world: World) { + if (enabled) { + wrapper.compactChangedSlots(world, this, true, compactNonUncraftable) { matchesAllowEmpty(it) } + } + } + + fun toggleCompactNonUncraftable() { + compactNonUncraftable = !compactNonUncraftable + } + + fun toggleWorkInGui() { + shouldWorkInGui = !shouldWorkInGui + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setBoolean(COMPACT_NON_UNCRAFTABLE_TAG, compactNonUncraftable) + nbt.setBoolean(SHOULD_WORK_IN_GUI_TAG, shouldWorkInGui) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(COMPACT_NON_UNCRAFTABLE_TAG)) + compactNonUncraftable = nbt.getBoolean(COMPACT_NON_UNCRAFTABLE_TAG) + if (nbt.hasKey(SHOULD_WORK_IN_GUI_TAG)) + shouldWorkInGui = nbt.getBoolean(SHOULD_WORK_IN_GUI_TAG) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_COMPACTING_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CraftingUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CraftingUpgradeWrapper.kt index 59e0973..444c413 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CraftingUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/CraftingUpgradeWrapper.kt @@ -32,8 +32,10 @@ class CraftingUpgradeWrapper() : UpgradeWrapper() { override fun deserializeNBT(nbt: NBTTagCompound) { super.deserializeNBT(nbt) - craftingDestination = CraftingDestination.entries[nbt.getByte(CRAFTING_DEST_TAG).toInt()] - craftMatrix.deserializeNBT(nbt.getCompoundTag(MATRIX_TAG)) + if (nbt.hasKey(CRAFTING_DEST_TAG)) + craftingDestination = CraftingDestination.entries.getOrElse(nbt.getByte(CRAFTING_DEST_TAG).toInt()) { craftingDestination } + if (nbt.hasKey(MATRIX_TAG)) + craftMatrix.deserializeNBT(nbt.getCompoundTag(MATRIX_TAG)) } enum class CraftingDestination { diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DepositUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DepositUpgradeWrapper.kt index c793039..042b349 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DepositUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DepositUpgradeWrapper.kt @@ -1,13 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.DepositUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class DepositUpgradeWrapper : BasicUpgradeWrapper(), IDepositUpgrade { +class DepositUpgradeWrapper : + BasicUpgradeWrapper(Config.depositUpgrade.filterSlots, Config.depositUpgrade.slotsInRow), + IDepositUpgrade { override val settingsLangKey: String = "gui.deposit_settings".asTranslationKey() override fun canDeposit(stack: ItemStack): Boolean = @@ -17,4 +20,4 @@ class DepositUpgradeWrapper : BasicUpgradeWrapper(), IDeposi capability == Capabilities.DEPOSIT_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) || super.hasCapability(capability, facing) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DiscHandlerRegistry.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DiscHandlerRegistry.kt new file mode 100644 index 0000000..e333059 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/DiscHandlerRegistry.kt @@ -0,0 +1,91 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import net.minecraft.init.Items +import net.minecraft.item.Item +import net.minecraft.item.ItemRecord +import net.minecraft.item.ItemStack +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import java.util.Collections + +interface IDiscHandler { + fun supports(stack: ItemStack): Boolean + fun playDisc(world: World, pos: BlockPos, stack: ItemStack): Boolean + fun stopDisc(world: World, pos: BlockPos) { + world.playEvent(null, DiscHandlerRegistry.RECORD_PLAY_EVENT, pos, 0) + } + + fun getMusicLengthInTicks(stack: ItemStack, world: World): Long? +} + +object DiscHandlerRegistry { + const val RECORD_PLAY_EVENT = 1010 + + private val handlers = mutableListOf(VanillaDiscHandler) + + @JvmStatic + fun getHandlers(): List = Collections.unmodifiableList(handlers) + + @JvmStatic + fun registerHandler(handler: IDiscHandler) { + handlers.add(handler) + } + + @JvmStatic + fun findHandler(stack: ItemStack): IDiscHandler? = + if (stack.isEmpty) null else handlers.firstOrNull { it.supports(stack) } + + @JvmStatic + fun isSupported(stack: ItemStack): Boolean = findHandler(stack) != null + + @JvmStatic + fun playDisc(world: World, pos: BlockPos, stack: ItemStack): Boolean = + findHandler(stack)?.playDisc(world, pos, stack) == true + + @JvmStatic + fun stopDisc(world: World, pos: BlockPos) { + handlers.firstOrNull()?.stopDisc(world, pos) + ?: world.playEvent(null, RECORD_PLAY_EVENT, pos, 0) + } + + @JvmStatic + fun stopDisc(world: World, pos: BlockPos, stack: ItemStack) { + findHandler(stack)?.stopDisc(world, pos) ?: stopDisc(world, pos) + } + + @JvmStatic + fun getMusicLengthInTicks(stack: ItemStack, world: World): Long? = + findHandler(stack)?.getMusicLengthInTicks(stack, world) +} + +object VanillaDiscHandler : IDiscHandler { + private const val DEFAULT_DISC_LENGTH = 3600L + + private val vanillaDiscLengths = mapOf( + Items.RECORD_13 to 1780L, + Items.RECORD_CAT to 3700L, + Items.RECORD_BLOCKS to 6900L, + Items.RECORD_CHIRP to 3700L, + Items.RECORD_FAR to 3480L, + Items.RECORD_MALL to 3940L, + Items.RECORD_MELLOHI to 1920L, + Items.RECORD_STAL to 3000L, + Items.RECORD_STRAD to 3760L, + Items.RECORD_WARD to 5020L, + Items.RECORD_11 to 1420L, + Items.RECORD_WAIT to 4760L + ) + + override fun supports(stack: ItemStack): Boolean = stack.item is ItemRecord + + override fun playDisc(world: World, pos: BlockPos, stack: ItemStack): Boolean { + if (stack.item !is ItemRecord) { + return false + } + world.playEvent(null, DiscHandlerRegistry.RECORD_PLAY_EVENT, pos, Item.getIdFromItem(stack.item)) + return true + } + + override fun getMusicLengthInTicks(stack: ItemStack, world: World): Long? = + if (supports(stack)) vanillaDiscLengths[stack.item] ?: DEFAULT_DISC_LENGTH else null +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/EverlastingUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/EverlastingUpgradeWrapper.kt new file mode 100644 index 0000000..5426c78 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/EverlastingUpgradeWrapper.kt @@ -0,0 +1,24 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.item.EverlastingUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability + +class EverlastingUpgradeWrapper : UpgradeWrapper(), IEverlastingUpgrade { + override val settingsLangKey = "gui.everlasting_settings".asTranslationKey() + + override fun serializeNBT(): NBTTagCompound = + super.serializeNBT() + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.EVERLASTING_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FeedingUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FeedingUpgradeWrapper.kt index 262ff4c..1b921cc 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FeedingUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FeedingUpgradeWrapper.kt @@ -2,6 +2,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackDataFixer import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler import com.cleanroommc.retrosophisticatedbackpacks.item.FeedingUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.BackpackItemStackHelper @@ -12,10 +13,12 @@ import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.items.IItemHandler -class FeedingUpgradeWrapper : BasicUpgradeWrapper(), IFeedingUpgrade { +class FeedingUpgradeWrapper : + BasicUpgradeWrapper(Config.feedingUpgrade.filterSlots, Config.feedingUpgrade.slotsInRow), + IFeedingUpgrade { override val settingsLangKey: String = "gui.feeding_settings".asTranslationKey() - override val filterItems: ExposedItemStackHandler = object : ExposedItemStackHandler(9) { + override val filterItems: ExposedItemStackHandler = object : ExposedItemStackHandler(Config.feedingUpgrade.filterSlots) { override fun isItemValid(slot: Int, stack: ItemStack): Boolean = IFeedingUpgrade.isValidFood(stack) } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FilterUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FilterUpgradeWrapper.kt index a921ef4..1e62893 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FilterUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/FilterUpgradeWrapper.kt @@ -1,6 +1,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.FilterUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack @@ -8,7 +9,9 @@ import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class FilterUpgradeWrapper : BasicUpgradeWrapper(), IFilterUpgrade { +class FilterUpgradeWrapper : + BasicUpgradeWrapper(Config.filterUpgrade.filterSlots, Config.filterUpgrade.slotsInRow), + IFilterUpgrade { override val settingsLangKey: String = "gui.filter_settings".asTranslationKey() override var filterWay: IFilterUpgrade.FilterWayType = IFilterUpgrade.FilterWayType.IN_OUT @@ -39,6 +42,7 @@ class FilterUpgradeWrapper : BasicUpgradeWrapper(), IFilterUp override fun deserializeNBT(nbt: NBTTagCompound) { super.deserializeNBT(nbt) - filterWay = IFilterUpgrade.FilterWayType.entries[nbt.getByte(IFilterUpgrade.FILTER_WAY_TAG).toInt()] + if (nbt.hasKey(IFilterUpgrade.FILTER_WAY_TAG)) + filterWay = IFilterUpgrade.FilterWayType.entries.getOrElse(nbt.getByte(IFilterUpgrade.FILTER_WAY_TAG).toInt()) { filterWay } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAdvancedFilterable.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAdvancedFilterable.kt index 56a2206..c809bc5 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAdvancedFilterable.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAdvancedFilterable.kt @@ -27,7 +27,7 @@ interface IAdvancedFilterable : IBasicFilterable { } private fun matchItem(stack: ItemStack): Boolean { - val filterResult = BooleanArray(16) + val filterResult = BooleanArray(filterItems.slots) for ((i, filterStack) in filterItems.inventory.withIndex()) { if (filterStack.item != stack.item) @@ -43,10 +43,16 @@ interface IAdvancedFilterable : IBasicFilterable { } private fun matchMod(stack: ItemStack): Boolean { - val filterResult = BooleanArray(16) + val filterResult = BooleanArray(filterItems.slots) for ((i, filterStack) in filterItems.inventory.withIndex()) { - filterResult[i] = stack.item.registryName?.namespace == filterStack.item.registryName?.namespace + if (filterStack.isEmpty) + continue + + val modMatches = stack.item.registryName?.namespace == filterStack.item.registryName?.namespace + val damageMatches = ignoreDurability || stack.itemDamage == filterStack.itemDamage + val nbtMatches = ignoreNBT || stack.tagCompound == filterStack.tagCompound + filterResult[i] = modMatches && damageMatches && nbtMatches } return when (filterType) { @@ -106,6 +112,8 @@ interface IAdvancedFilterable : IBasicFilterable { object Impl : IAdvancedFilterable { override val filterItems: ExposedItemStackHandler get() = ExposedItemStackHandler(0) + override val slotsInRow: Int + get() = 1 override var filterType: IBasicFilterable.FilterType get() = IBasicFilterable.Impl.filterType set(_) {} @@ -126,4 +134,4 @@ interface IAdvancedFilterable : IBasicFilterable { get() = false set(_) {} } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAnvilUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAnvilUpgrade.kt new file mode 100644 index 0000000..3f4c375 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IAnvilUpgrade.kt @@ -0,0 +1,40 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.items.IItemHandler + +interface IAnvilUpgrade : ISidelessCapabilityProvider, INBTSerializable { + var shouldShiftClickIntoStorage: Boolean + var itemName: String + val maximumCost: Int + val materialCost: Int + + fun getInventory(): IItemHandler + fun updateRepairOutput(player: EntityPlayer, world: World): ItemStack + fun canTakeResult(player: EntityPlayer): Boolean + fun takeResult(player: EntityPlayer, world: World): ItemStack + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IANVIL_UPGRADE_CAPABILITY + + object Impl : IAnvilUpgrade { + override var shouldShiftClickIntoStorage = false + override var itemName = "" + override val maximumCost = 0 + override val materialCost = 0 + override fun getInventory(): IItemHandler = net.minecraftforge.items.ItemStackHandler(0) + override fun updateRepairOutput(player: EntityPlayer, world: World): ItemStack = ItemStack.EMPTY + override fun canTakeResult(player: EntityPlayer): Boolean = false + override fun takeResult(player: EntityPlayer, world: World): ItemStack = ItemStack.EMPTY + override fun serializeNBT(): NBTTagCompound = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBasicFilterable.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBasicFilterable.kt index 0bb0003..7fd472d 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBasicFilterable.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBasicFilterable.kt @@ -14,6 +14,7 @@ interface IBasicFilterable : ISidelessCapabilityProvider { } val filterItems: ExposedItemStackHandler + val slotsInRow: Int var filterType: FilterType fun checkFilter(stack: ItemStack): Boolean = when (filterType) { @@ -32,6 +33,8 @@ interface IBasicFilterable : ISidelessCapabilityProvider { object Impl : IBasicFilterable { override val filterItems: ExposedItemStackHandler get() = ExposedItemStackHandler(0) + override val slotsInRow: Int + get() = 1 override var filterType: FilterType get() = FilterType.WHITELIST set(_) {} @@ -39,4 +42,4 @@ interface IBasicFilterable : ISidelessCapabilityProvider { override fun checkFilter(itemStack: ItemStack): Boolean = false } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBatteryUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBatteryUpgrade.kt new file mode 100644 index 0000000..356343b --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IBatteryUpgrade.kt @@ -0,0 +1,41 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.items.IItemHandler + +interface IBatteryUpgrade : ISidelessCapabilityProvider, INBTSerializable { + val energyStored: Int + val canExtractEnergy: Boolean + val canReceiveEnergy: Boolean + + fun getMaxEnergyStored(wrapper: BackpackWrapper): Int + fun getMaxEnergyStored(wrapper: BackpackWrapper, stackMultiplier: Int): Int + fun receiveEnergy(wrapper: BackpackWrapper, maxReceive: Int, simulate: Boolean): Int + fun extractEnergy(wrapper: BackpackWrapper, maxExtract: Int, simulate: Boolean): Int + fun tick(wrapper: BackpackWrapper, world: World) + fun getInventory(): IItemHandler + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IBATTERY_UPGRADE_CAPABILITY + + object Impl : IBatteryUpgrade { + override val energyStored = 0 + override val canExtractEnergy = false + override val canReceiveEnergy = false + override fun getMaxEnergyStored(wrapper: BackpackWrapper): Int = 0 + override fun getMaxEnergyStored(wrapper: BackpackWrapper, stackMultiplier: Int): Int = 0 + override fun receiveEnergy(wrapper: BackpackWrapper, maxReceive: Int, simulate: Boolean): Int = 0 + override fun extractEnergy(wrapper: BackpackWrapper, maxExtract: Int, simulate: Boolean): Int = 0 + override fun tick(wrapper: BackpackWrapper, world: World) {} + override fun getInventory(): IItemHandler = net.minecraftforge.items.ItemStackHandler(0) + override fun serializeNBT(): NBTTagCompound = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ICompactingUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ICompactingUpgrade.kt new file mode 100644 index 0000000..01665d6 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ICompactingUpgrade.kt @@ -0,0 +1,17 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable + +sealed interface ICompactingUpgrade : ISidelessCapabilityProvider, INBTSerializable { + fun compact(wrapper: BackpackWrapper, world: World) + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ICOMPACTING_UPGRADE_CAPABILITY +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IContentsFilterable.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IContentsFilterable.kt new file mode 100644 index 0000000..9bf993f --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IContentsFilterable.kt @@ -0,0 +1,18 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +interface IContentsFilterable : IBasicFilterable { + companion object { + const val FILTER_BY_STORAGE_TAG = "FilterByStorage" + } + + var contentsFilterType: ContentsFilterType + + enum class ContentsFilterType { + ALLOW, + BLOCK, + STORAGE; + + fun next(): ContentsFilterType = + entries[(ordinal + 1) % entries.size] + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IEverlastingUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IEverlastingUpgrade.kt new file mode 100644 index 0000000..d86f80c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IEverlastingUpgrade.kt @@ -0,0 +1,18 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable + +interface IEverlastingUpgrade : ISidelessCapabilityProvider, INBTSerializable { + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IEVERLASTING_UPGRADE_CAPABILITY + + object Impl : IEverlastingUpgrade { + override fun serializeNBT(): NBTTagCompound = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IJukeboxUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IJukeboxUpgrade.kt new file mode 100644 index 0000000..b4763c9 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IJukeboxUpgrade.kt @@ -0,0 +1,36 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.entity.Entity +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.items.IItemHandler + +interface IJukeboxUpgrade : ISidelessCapabilityProvider, INBTSerializable { + val discInventory: IItemHandler + + fun play(world: World, pos: BlockPos) + fun play(entity: Entity) + fun stop(world: World, pos: BlockPos) + fun next() + fun previous() + fun tick(world: World, pos: BlockPos?) + + object Impl : IJukeboxUpgrade { + override val discInventory: IItemHandler = net.minecraftforge.items.wrapper.EmptyHandler.INSTANCE + override fun play(world: World, pos: BlockPos) {} + override fun play(entity: Entity) {} + override fun stop(world: World, pos: BlockPos) {} + override fun next() {} + override fun previous() {} + override fun tick(world: World, pos: BlockPos?) {} + override fun serializeNBT() = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?) = false + override fun getCapability(capability: Capability, facing: EnumFacing?): T? = null + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IMagnetUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IMagnetUpgrade.kt new file mode 100644 index 0000000..f558f81 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IMagnetUpgrade.kt @@ -0,0 +1,20 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.item.ItemStack +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable +import net.minecraft.nbt.NBTTagCompound + +sealed interface IMagnetUpgrade : ISidelessCapabilityProvider, INBTSerializable { + val range: Double + var pickupItems: Boolean + + fun canPickup(stack: ItemStack, backpackWrapper: BackpackWrapper): Boolean + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IMAGNET_UPGRADE_CAPABILITY +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IPumpUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IPumpUpgrade.kt new file mode 100644 index 0000000..bb85250 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IPumpUpgrade.kt @@ -0,0 +1,43 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.fluids.FluidStack + +interface IPumpUpgrade : ISidelessCapabilityProvider, INBTSerializable { + var enabled: Boolean + var isInput: Boolean + var interactWithHand: Boolean + var interactWithWorld: Boolean + var interactWithFluidHandlers: Boolean + val fluidFilters: List + + fun tick(player: EntityPlayer?, wrapper: BackpackWrapper, world: World, pos: BlockPos) + fun setFluidFilter(slot: Int, fluid: FluidStack?) + fun fluidMatches(fluid: FluidStack): Boolean + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IPUMP_UPGRADE_CAPABILITY + + object Impl : IPumpUpgrade { + override var enabled = false + override var isInput = true + override var interactWithHand = false + override var interactWithWorld = false + override var interactWithFluidHandlers = false + override val fluidFilters: List = emptyList() + override fun tick(player: EntityPlayer?, wrapper: BackpackWrapper, world: World, pos: BlockPos) {} + override fun setFluidFilter(slot: Int, fluid: FluidStack?) {} + override fun fluidMatches(fluid: FluidStack): Boolean = false + override fun serializeNBT(): NBTTagCompound = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IRefillUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IRefillUpgrade.kt new file mode 100644 index 0000000..c1ec718 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IRefillUpgrade.kt @@ -0,0 +1,17 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable + +sealed interface IRefillUpgrade : ISidelessCapabilityProvider, INBTSerializable { + fun refill(player: EntityPlayer, wrapper: BackpackWrapper) + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IREFILL_UPGRADE_CAPABILITY +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ITankUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ITankUpgrade.kt new file mode 100644 index 0000000..3dd77d7 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ITankUpgrade.kt @@ -0,0 +1,41 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.items.IItemHandler + +interface ITankUpgrade : ISidelessCapabilityProvider, INBTSerializable { + val tankCapacity: Int + fun getTankCapacity(wrapper: BackpackWrapper): Int = tankCapacity + fun getFluid(): FluidStack? + fun fill(wrapper: BackpackWrapper, resource: FluidStack, doFill: Boolean, ignoreInOutLimit: Boolean = false): Int + fun drain(wrapper: BackpackWrapper, maxDrain: Int, doDrain: Boolean, ignoreInOutLimit: Boolean = false): FluidStack? + fun drain(wrapper: BackpackWrapper, resource: FluidStack, doDrain: Boolean, ignoreInOutLimit: Boolean = false): FluidStack? + fun tick(wrapper: BackpackWrapper, world: World) + fun getInventory(): IItemHandler + fun interactWithCursorStack(player: EntityPlayer, wrapper: BackpackWrapper) + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ITANK_UPGRADE_CAPABILITY + + object Impl : ITankUpgrade { + override val tankCapacity = 0 + override fun getFluid(): FluidStack? = null + override fun fill(wrapper: BackpackWrapper, resource: FluidStack, doFill: Boolean, ignoreInOutLimit: Boolean): Int = 0 + override fun drain(wrapper: BackpackWrapper, maxDrain: Int, doDrain: Boolean, ignoreInOutLimit: Boolean): FluidStack? = null + override fun drain(wrapper: BackpackWrapper, resource: FluidStack, doDrain: Boolean, ignoreInOutLimit: Boolean): FluidStack? = null + override fun tick(wrapper: BackpackWrapper, world: World) {} + override fun getInventory(): IItemHandler = net.minecraftforge.items.ItemStackHandler(0) + override fun interactWithCursorStack(player: EntityPlayer, wrapper: BackpackWrapper) {} + override fun serializeNBT(): NBTTagCompound = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IToolSwapperUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IToolSwapperUpgrade.kt new file mode 100644 index 0000000..16ef9ec --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IToolSwapperUpgrade.kt @@ -0,0 +1,31 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.block.state.IBlockState +import net.minecraft.entity.Entity +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable + +interface IToolSwapperUpgrade : ISidelessCapabilityProvider, INBTSerializable { + fun onBlockClick(player: EntityPlayer, wrapper: BackpackWrapper, pos: BlockPos, state: IBlockState): Boolean = false + fun onAttackEntity(player: EntityPlayer, wrapper: BackpackWrapper): Boolean = false + fun canProcessBlockInteract(): Boolean = false + fun onBlockInteract(player: EntityPlayer, wrapper: BackpackWrapper, world: World, pos: BlockPos, state: IBlockState): Boolean = false + fun canProcessEntityInteract(): Boolean = false + fun onEntityInteract(player: EntityPlayer, wrapper: BackpackWrapper, entity: Entity): Boolean = false + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ITOOL_SWAPPER_UPGRADE_CAPABILITY + + object Impl : IToolSwapperUpgrade { + override fun serializeNBT(): NBTTagCompound = NBTTagCompound() + override fun deserializeNBT(nbt: NBTTagCompound) {} + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IVoidUpgrade.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IVoidUpgrade.kt new file mode 100644 index 0000000..92688c3 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/IVoidUpgrade.kt @@ -0,0 +1,16 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.ISidelessCapabilityProvider +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.INBTSerializable + +sealed interface IVoidUpgrade : ISidelessCapabilityProvider, INBTSerializable { + fun shouldVoid(stack: ItemStack): Boolean + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.IVOID_UPGRADE_CAPABILITY +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/JukeboxUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/JukeboxUpgradeWrapper.kt new file mode 100644 index 0000000..0a2448e --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/JukeboxUpgradeWrapper.kt @@ -0,0 +1,270 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler +import com.cleanroommc.retrosophisticatedbackpacks.item.JukeboxUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.entity.Entity +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.items.IItemHandler +import java.util.Collections +import java.util.LinkedList + +open class JukeboxUpgradeWrapper(private val slots: Int = 1) : UpgradeWrapper(), IJukeboxUpgrade { + companion object { + private const val INVENTORY_TAG = "Inventory" + private const val IS_PLAYING_TAG = "IsPlaying" + private const val ACTIVE_SLOT_TAG = "ActiveSlot" + private const val SHUFFLE_TAG = "Shuffle" + private const val REPEAT_MODE_TAG = "RepeatMode" + private const val FINISH_TIME_TAG = "FinishTime" + + private const val DEFAULT_DISC_LENGTH = 3600L + } + + override val settingsLangKey = "gui.jukebox_settings".asTranslationKey() + private var isPlaying = false + private var activeSlot = -1 + var shuffleEnabled = false + var repeatMode = RepeatMode.NO + private var finishTime = 0L + private var worldPlaying: World? = null + private var posPlaying: BlockPos? = null + private val playlist = LinkedList() + private val history = LinkedList() + + override val discInventory: IItemHandler = object : ExposedItemStackHandler(slots) { + override fun isItemValid(slot: Int, stack: ItemStack): Boolean = + stack.isEmpty || DiscHandlerRegistry.isSupported(stack) + + override fun onContentsChanged(slot: Int) { + super.onContentsChanged(slot) + if (isPlaying && slot == activeSlot) { + requestStop() + } + initPlaylist(excludeActive = true) + } + } + + override fun play(world: World, pos: BlockPos) { + if (world.isRemote || isPlaying) { + return + } + worldPlaying = world + posPlaying = pos + playNext() + } + + override fun play(entity: Entity) { + play(entity.world, BlockPos(entity)) + } + + override fun stop(world: World, pos: BlockPos) { + if (world.isRemote) { + return + } + DiscHandlerRegistry.stopDisc(world, pos, getDisc()) + setPlaying(false) + playlist.clear() + history.clear() + } + + override fun next() { + if (!isPlaying) { + return + } + playNext() + } + + override fun previous() { + if (!isPlaying || history.isEmpty()) { + return + } + if (activeSlot >= 0) { + playlist.addFirst(activeSlot) + } + activeSlot = history.pollLast() + playDisc() + } + + override fun tick(world: World, pos: BlockPos?) { + if (world.isRemote || !isPlaying) { + return + } + if (pos != null && (activeSlot == -1 || worldPlaying == null || posPlaying == null)) { + worldPlaying = world + posPlaying = pos + } + if (isPlaying && activeSlot == -1) { + playNext() + return + } + if (finishTime > 0L && world.totalWorldTime >= finishTime) { + onDiscFinished() + } + } + + fun toggleShuffle() { + shuffleEnabled = !shuffleEnabled + initPlaylist(excludeActive = true) + } + + fun cycleRepeatMode() { + repeatMode = repeatMode.next() + } + + fun isPlaying(): Boolean = isPlaying + + fun requestPlay() { + if (isPlaying) { + return + } + val world = worldPlaying + val pos = posPlaying + if (world != null && pos != null) { + play(world, pos) + } else { + setPlaying(true) + } + } + + fun requestStop() { + worldPlaying?.let { world -> + posPlaying?.let { pos -> stop(world, pos) } + } ?: run { + setPlaying(false) + playlist.clear() + history.clear() + } + } + + override fun onBeforeRemoved() { + requestStop() + } + + private fun playNext(startOverIfAtEnd: Boolean = true) { + if (playlist.isEmpty() && startOverIfAtEnd) { + initPlaylist(excludeActive = false) + } + val nextSlot = playlist.poll() ?: return + if (activeSlot >= 0) { + history.add(activeSlot) + while (history.size > slots) { + history.poll() + } + } + activeSlot = nextSlot + playDisc() + } + + private fun onDiscFinished() { + when (repeatMode) { + RepeatMode.ONE -> playDisc() + RepeatMode.ALL -> playNext() + RepeatMode.NO -> playNext(startOverIfAtEnd = false) + } + } + + private fun playDisc() { + val world = worldPlaying ?: return + val pos = posPlaying ?: return + val disc = getDisc() + if (world.isRemote || disc.isEmpty || !DiscHandlerRegistry.isSupported(disc)) { + setPlaying(false) + return + } + DiscHandlerRegistry.stopDisc(world, pos, disc) + if (!DiscHandlerRegistry.playDisc(world, pos, disc)) { + setPlaying(false) + return + } + finishTime = world.totalWorldTime + getDiscLength(disc, world) + setPlaying(true) + } + + fun getDisc(): ItemStack = + if (activeSlot in 0 until slots) discInventory.getStackInSlot(activeSlot) else ItemStack.EMPTY + + fun getDiscSlotActive(): Int = activeSlot + + fun getDiscFinishTime(): Long = finishTime + + private fun setPlaying(playing: Boolean) { + isPlaying = playing + if (!playing) { + activeSlot = -1 + finishTime = 0L + } + } + + private fun initPlaylist(excludeActive: Boolean) { + playlist.clear() + for (slot in 0 until slots) { + val disc = discInventory.getStackInSlot(slot) + if (DiscHandlerRegistry.isSupported(disc) && (!excludeActive || !isPlaying || slot != activeSlot)) { + playlist.add(slot) + } + } + if (shuffleEnabled) { + Collections.shuffle(playlist) + } + } + + private fun getDiscLength(stack: ItemStack, world: World): Long = + DiscHandlerRegistry.getMusicLengthInTicks(stack, world) ?: DEFAULT_DISC_LENGTH + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setTag(INVENTORY_TAG, (discInventory as ExposedItemStackHandler).serializeNBT()) + nbt.setBoolean(IS_PLAYING_TAG, isPlaying) + nbt.setInteger(ACTIVE_SLOT_TAG, activeSlot) + nbt.setBoolean(SHUFFLE_TAG, shuffleEnabled) + nbt.setString(REPEAT_MODE_TAG, repeatMode.name) + nbt.setLong(FINISH_TIME_TAG, finishTime) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(INVENTORY_TAG)) + (discInventory as ExposedItemStackHandler).deserializeNBT(nbt.getCompoundTag(INVENTORY_TAG)) + if (nbt.hasKey(IS_PLAYING_TAG)) + isPlaying = nbt.getBoolean(IS_PLAYING_TAG) + if (nbt.hasKey(ACTIVE_SLOT_TAG)) + activeSlot = nbt.getInteger(ACTIVE_SLOT_TAG) + if (nbt.hasKey(SHUFFLE_TAG)) + shuffleEnabled = nbt.getBoolean(SHUFFLE_TAG) + if (nbt.hasKey(REPEAT_MODE_TAG)) + repeatMode = runCatching { RepeatMode.valueOf(nbt.getString(REPEAT_MODE_TAG)) }.getOrDefault(repeatMode) + if (nbt.hasKey(FINISH_TIME_TAG)) + finishTime = nbt.getLong(FINISH_TIME_TAG) + initPlaylist(excludeActive = true) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.JUKEBOX_UPGRADE_CAPABILITY || + capability == Capabilities.IJUKEBOX_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} + +class AdvancedJukeboxUpgradeWrapper : JukeboxUpgradeWrapper(Config.advancedJukeboxUpgrade.numberOfSlots) { + override val settingsLangKey = "gui.advanced_jukebox_settings".asTranslationKey() + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_JUKEBOX_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} + +enum class RepeatMode { + ALL, + ONE, + NO; + + fun next(): RepeatMode = entries[(ordinal + 1) % entries.size] +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/MagnetUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/MagnetUpgradeWrapper.kt new file mode 100644 index 0000000..4cd324e --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/MagnetUpgradeWrapper.kt @@ -0,0 +1,158 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.UpgradeFilterUtils.matchesAllowEmpty +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.item.MagnetUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability + +open class MagnetUpgradeWrapper( + filterSlots: Int = Config.magnetUpgrade.filterSlots, + slotsInRow: Int = Config.magnetUpgrade.slotsInRow, + override val range: Double = Config.magnetUpgrade.magnetRange.toDouble() +) : + BasicUpgradeWrapper(filterSlots, slotsInRow), IMagnetUpgrade, IContentsFilterable { + companion object { + private const val PICKUP_ITEMS_TAG = "PickupItems" + } + + override val settingsLangKey = "gui.magnet_settings".asTranslationKey() + override var pickupItems = true + private var filterByStorage = false + override var contentsFilterType: IContentsFilterable.ContentsFilterType + get() = when { + filterByStorage -> IContentsFilterable.ContentsFilterType.STORAGE + filterType == IBasicFilterable.FilterType.WHITELIST -> IContentsFilterable.ContentsFilterType.ALLOW + else -> IContentsFilterable.ContentsFilterType.BLOCK + } + set(value) { + filterByStorage = value == IContentsFilterable.ContentsFilterType.STORAGE + when (value) { + IContentsFilterable.ContentsFilterType.ALLOW -> filterType = IBasicFilterable.FilterType.WHITELIST + IContentsFilterable.ContentsFilterType.BLOCK -> filterType = IBasicFilterable.FilterType.BLACKLIST + IContentsFilterable.ContentsFilterType.STORAGE -> {} + } + } + + init { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + + override fun canPickup(stack: ItemStack, backpackWrapper: BackpackWrapper): Boolean = + enabled && pickupItems && when (contentsFilterType) { + IContentsFilterable.ContentsFilterType.STORAGE -> + backpackWrapper.matchesStorageContents(stack) { candidate, stored -> + ItemStack.areItemsEqualIgnoreDurability(candidate, stored) + } + else -> matchesAllowEmpty(stack) + } + + fun togglePickupItems() { + pickupItems = !pickupItems + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setBoolean(PICKUP_ITEMS_TAG, pickupItems) + nbt.setBoolean(IContentsFilterable.FILTER_BY_STORAGE_TAG, filterByStorage) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + pickupItems = !nbt.hasKey(PICKUP_ITEMS_TAG) || nbt.getBoolean(PICKUP_ITEMS_TAG) + if (nbt.hasKey(IContentsFilterable.FILTER_BY_STORAGE_TAG)) + filterByStorage = nbt.getBoolean(IContentsFilterable.FILTER_BY_STORAGE_TAG) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.MAGNET_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} + +class AdvancedMagnetUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedMagnetUpgrade.filterSlots, Config.advancedMagnetUpgrade.slotsInRow), + IMagnetUpgrade, IContentsFilterable { + companion object { + private const val PICKUP_ITEMS_TAG = "PickupItems" + } + + override val settingsLangKey = "gui.advanced_magnet_settings".asTranslationKey() + override val range = Config.advancedMagnetUpgrade.magnetRange.toDouble() + override var pickupItems = true + private var filterByStorage = false + override var contentsFilterType: IContentsFilterable.ContentsFilterType + get() = when { + filterByStorage -> IContentsFilterable.ContentsFilterType.STORAGE + filterType == IBasicFilterable.FilterType.WHITELIST -> IContentsFilterable.ContentsFilterType.ALLOW + else -> IContentsFilterable.ContentsFilterType.BLOCK + } + set(value) { + val normalized = if (value == IContentsFilterable.ContentsFilterType.STORAGE && + matchType == IAdvancedFilterable.MatchType.ORE_DICT + ) IContentsFilterable.ContentsFilterType.ALLOW else value + filterByStorage = normalized == IContentsFilterable.ContentsFilterType.STORAGE + when (normalized) { + IContentsFilterable.ContentsFilterType.ALLOW -> filterType = IBasicFilterable.FilterType.WHITELIST + IContentsFilterable.ContentsFilterType.BLOCK -> filterType = IBasicFilterable.FilterType.BLACKLIST + IContentsFilterable.ContentsFilterType.STORAGE -> {} + } + } + + init { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + + override fun canPickup(stack: ItemStack, backpackWrapper: BackpackWrapper): Boolean = + enabled && pickupItems && when (contentsFilterType) { + IContentsFilterable.ContentsFilterType.STORAGE -> + backpackWrapper.matchesStorageContents(stack, ::matchesStorageStack) + else -> matchesAllowEmpty(stack) + } + + private fun matchesStorageStack(candidate: ItemStack, stored: ItemStack): Boolean = + when (matchType) { + IAdvancedFilterable.MatchType.ITEM -> { + val itemMatches = if (ignoreDurability) ItemStack.areItemsEqualIgnoreDurability(candidate, stored) + else candidate.isItemEqual(stored) + itemMatches && (ignoreNBT || candidate.tagCompound == stored.tagCompound) + } + IAdvancedFilterable.MatchType.MOD -> + candidate.item.registryName?.namespace == stored.item.registryName?.namespace && + (ignoreDurability || candidate.itemDamage == stored.itemDamage) && + (ignoreNBT || candidate.tagCompound == stored.tagCompound) + IAdvancedFilterable.MatchType.ORE_DICT -> false + } + + fun togglePickupItems() { + pickupItems = !pickupItems + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setBoolean(PICKUP_ITEMS_TAG, pickupItems) + nbt.setBoolean(IContentsFilterable.FILTER_BY_STORAGE_TAG, filterByStorage) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + pickupItems = !nbt.hasKey(PICKUP_ITEMS_TAG) || nbt.getBoolean(PICKUP_ITEMS_TAG) + if (nbt.hasKey(IContentsFilterable.FILTER_BY_STORAGE_TAG)) + filterByStorage = nbt.getBoolean(IContentsFilterable.FILTER_BY_STORAGE_TAG) + if (matchType == IAdvancedFilterable.MatchType.ORE_DICT && filterByStorage) { + contentsFilterType = IContentsFilterable.ContentsFilterType.ALLOW + } + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_MAGNET_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PickupUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PickupUpgradeWrapper.kt index e1950e6..63be82a 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PickupUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PickupUpgradeWrapper.kt @@ -1,13 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.PickupUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class PickupUpgradeWrapper : BasicUpgradeWrapper(), IPickupUpgrade { +class PickupUpgradeWrapper : + BasicUpgradeWrapper(Config.pickupUpgrade.filterSlots, Config.pickupUpgrade.slotsInRow), + IPickupUpgrade { override val settingsLangKey: String = "gui.pickup_settings".asTranslationKey() override fun canPickup(stack: ItemStack): Boolean = @@ -17,4 +20,4 @@ class PickupUpgradeWrapper : BasicUpgradeWrapper(), IPickupUp capability == Capabilities.PICKUP_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) || super.hasCapability(capability, facing) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PumpUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PumpUpgradeWrapper.kt new file mode 100644 index 0000000..5003312 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/PumpUpgradeWrapper.kt @@ -0,0 +1,285 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackFluidHandler +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem +import com.cleanroommc.retrosophisticatedbackpacks.item.PumpUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.block.BlockLiquid +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.util.EnumHand +import net.minecraft.util.math.AxisAlignedBB +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.fluids.Fluid +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.FluidUtil +import net.minecraftforge.fluids.capability.CapabilityFluidHandler +import net.minecraftforge.fluids.capability.IFluidHandler +import java.util.LinkedList + +open class PumpUpgradeWrapper( + private val filterSlots: Int = 0, + interactWithHandDefault: Boolean = false, + interactWithWorldDefault: Boolean = false, + interactWithFluidHandlersDefault: Boolean = true +) : UpgradeWrapper(), IPumpUpgrade, IToggleable { + companion object { + private const val ENABLED_TAG = "Enabled" + private const val INPUT_TAG = "Input" + private const val HAND_TAG = "InteractWithHand" + private const val WORLD_TAG = "InteractWithWorld" + private const val FLUID_HANDLERS_TAG = "InteractWithFluidHandlers" + private const val COOLDOWN_UNTIL_TAG = "CooldownUntil" + private const val LAST_HAND_ACTION_TAG = "LastHandAction" + private const val FILTER_TAG = "FluidFilter" + private const val DID_NOTHING_COOLDOWN = 40L + private const val HAND_COOLDOWN = 3L + private const val WORLD_COOLDOWN = 20L + private const val FLUID_HANDLER_COOLDOWN = 20L + private const val PLAYER_SEARCH_RANGE = 3.0 + private const val WORLD_RANGE = 4 + } + + override val settingsLangKey = "gui.pump_settings".asTranslationKey() + override var enabled = true + override var isInput = true + override var interactWithHand = interactWithHandDefault + override var interactWithWorld = interactWithWorldDefault + override var interactWithFluidHandlers = interactWithFluidHandlersDefault + override val fluidFilters: MutableList = MutableList(filterSlots) { null } + private var cooldownUntil = 0L + private var lastHandAction = -1L + + override fun tick(player: EntityPlayer?, wrapper: BackpackWrapper, world: World, pos: BlockPos) { + if (!enabled || world.isRemote || world.totalWorldTime < cooldownUntil || !wrapper.hasTankUpgrade()) { + return + } + val storage = BackpackFluidHandler(wrapper) + cooldownUntil = world.totalWorldTime + tick(storage, player, wrapper, world, pos) + } + + private fun tick(storage: IFluidHandler, player: EntityPlayer?, wrapper: BackpackWrapper, world: World, pos: BlockPos): Long { + if (interactWithHand && handlePlayers(player, world, pos, storage)) { + lastHandAction = world.totalWorldTime + return HAND_COOLDOWN + } + if (interactWithWorld) { + val cooldown = if (isInput) fillFromBlockInRange(world, pos, storage) else placeFluidInWorld(world, pos, storage) + if (cooldown != null) { + return cooldown + } + } + if (interactWithFluidHandlers && interactWithAttachedFluidHandlers(world, pos, storage, wrapper)) { + return FLUID_HANDLER_COOLDOWN + } + return if (lastHandAction + HAND_COOLDOWN * 10 > world.totalWorldTime) HAND_COOLDOWN else DID_NOTHING_COOLDOWN + } + + override fun setFluidFilter(slot: Int, fluid: FluidStack?) { + if (slot !in fluidFilters.indices) { + return + } + fluidFilters[slot] = fluid?.copy()?.also { it.amount = Fluid.BUCKET_VOLUME } + } + + override fun fluidMatches(fluid: FluidStack): Boolean = + fluidFilters.isEmpty() || fluidFilters.filterNotNull().let { filters -> + filters.isEmpty() || filters.any { it.isFluidEqual(fluid) } + } + + private fun handlePlayers(player: EntityPlayer?, world: World, pos: BlockPos, storage: IFluidHandler): Boolean { + if (player != null) { + return handlePlayer(player, storage) + } + val players = world.getEntitiesWithinAABB( + EntityPlayer::class.java, + AxisAlignedBB( + pos.x - PLAYER_SEARCH_RANGE, pos.y - PLAYER_SEARCH_RANGE, pos.z - PLAYER_SEARCH_RANGE, + pos.x + 1 + PLAYER_SEARCH_RANGE, pos.y + 1 + PLAYER_SEARCH_RANGE, pos.z + 1 + PLAYER_SEARCH_RANGE + ) + ) + return players.any { handlePlayer(it, storage) } + } + + private fun handlePlayer(player: EntityPlayer, storage: IFluidHandler): Boolean = + handleHand(player, EnumHand.MAIN_HAND, storage) || handleHand(player, EnumHand.OFF_HAND, storage) + + private fun handleHand(player: EntityPlayer, hand: EnumHand, storage: IFluidHandler): Boolean { + val stack = player.getHeldItem(hand) + if (stack.isEmpty || stack.count != 1) { + return false + } + if (stack.item is BackpackItem) { + return false + } + val itemHandler = FluidUtil.getFluidHandler(stack.copy()) ?: return false + val moved = if (isInput) fillFromFluidHandler(itemHandler, storage, Fluid.BUCKET_VOLUME) + else fillFluidHandler(itemHandler, storage, Fluid.BUCKET_VOLUME) + if (moved) { + player.setHeldItem(hand, itemHandler.container) + } + return moved + } + + private fun interactWithAttachedFluidHandlers(world: World, pos: BlockPos, storage: IFluidHandler, wrapper: BackpackWrapper): Boolean { + for (side in EnumFacing.values()) { + val te = world.getTileEntity(pos.offset(side)) ?: continue + val fluidHandler = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side.opposite) ?: continue + if (if (isInput) fillFromFluidHandler(fluidHandler, storage, getMaxInOut(wrapper)) + else fillFluidHandler(fluidHandler, storage, getMaxInOut(wrapper)) + ) { + return true + } + } + return false + } + + private fun fillFromBlockInRange(world: World, basePos: BlockPos, storage: IFluidHandler): Long? { + val queue = LinkedList() + val searched = mutableSetOf() + queue.add(basePos) + searched.add(basePos) + while (queue.isNotEmpty()) { + val pos = queue.poll() + if (fillFromBlock(world, pos, storage)) { + return maxOf(1L, Math.sqrt(distanceSq(basePos, pos).toDouble()).toLong()) * WORLD_COOLDOWN + } + for (side in EnumFacing.values()) { + val next = pos.offset(side) + if (searched.add(next) && distanceSq(basePos, next) < WORLD_RANGE * WORLD_RANGE) { + queue.add(next) + } + } + } + return null + } + + private fun fillFromBlock(world: World, pos: BlockPos, storage: IFluidHandler): Boolean { + val fluidHandler = FluidUtil.getFluidHandler(world, pos, null) ?: return false + return fillFromFluidHandler(fluidHandler, storage, Fluid.BUCKET_VOLUME) + } + + private fun placeFluidInWorld(world: World, pos: BlockPos, storage: IFluidHandler): Long? { + for (side in EnumFacing.values()) { + if (side == EnumFacing.UP) { + continue + } + val offsetPos = pos.offset(side) + if (!isValidForFluidPlacement(world, offsetPos)) { + continue + } + for (tank in storage.tankProperties) { + val fluid = tank.contents ?: continue + if (fluid.amount <= 0 || !fluidMatches(fluid)) { + continue + } + val resource = FluidStack(fluid.fluid, minOf(Fluid.BUCKET_VOLUME, fluid.amount), fluid.tag?.copy()) + if (FluidUtil.tryPlaceFluid(null, world, offsetPos, storage, resource)) { + return WORLD_COOLDOWN + } + } + } + return null + } + + private fun isValidForFluidPlacement(world: World, pos: BlockPos): Boolean { + val state = world.getBlockState(pos) + return world.isAirBlock(pos) || state.material.isLiquid && state.getValue(BlockLiquid.LEVEL) != 0 + } + + private fun fillFromFluidHandler(source: IFluidHandler, storage: IFluidHandler, maxDrain: Int): Boolean { + val contained = source.drain(maxDrain, false) ?: return false + if (contained.amount <= 0 || !fluidMatches(contained)) { + return false + } + return FluidUtil.tryFluidTransfer(storage, source, contained, true) != null + } + + private fun fillFluidHandler(destination: IFluidHandler, storage: IFluidHandler, maxFill: Int): Boolean { + for (tank in storage.tankProperties) { + val fluid = tank.contents ?: continue + if (fluid.amount <= 0 || !fluidMatches(fluid)) { + continue + } + val resource = FluidStack(fluid.fluid, minOf(maxFill, fluid.amount), fluid.tag?.copy()) + if (FluidUtil.tryFluidTransfer(destination, storage, resource, true) != null) { + return true + } + } + return false + } + + private fun getMaxInOut(wrapper: BackpackWrapper): Int = + maxOf(Fluid.BUCKET_VOLUME, (getSlotRows(wrapper) * Config.pumpUpgrade.maxInputOutput * getAdjustedStackMultiplier(wrapper)).toInt()) + + private fun getSlotRows(wrapper: BackpackWrapper): Int = + maxOf(1, (wrapper.backpackInventorySize() + 8) / 9) + + private fun getAdjustedStackMultiplier(wrapper: BackpackWrapper): Double = + 1.0 + Config.pumpUpgrade.stackMultiplierRatio * (wrapper.getTotalStackMultiplier() - 1) + + private fun distanceSq(first: BlockPos, second: BlockPos): Int { + val dx = first.x - second.x + val dy = first.y - second.y + val dz = first.z - second.z + return dx * dx + dy * dy + dz * dz + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setBoolean(ENABLED_TAG, enabled) + nbt.setBoolean(INPUT_TAG, isInput) + nbt.setBoolean(HAND_TAG, interactWithHand) + nbt.setBoolean(WORLD_TAG, interactWithWorld) + nbt.setBoolean(FLUID_HANDLERS_TAG, interactWithFluidHandlers) + nbt.setLong(COOLDOWN_UNTIL_TAG, cooldownUntil) + nbt.setLong(LAST_HAND_ACTION_TAG, lastHandAction) + fluidFilters.forEachIndexed { index, fluid -> + fluid?.let { nbt.setTag("$FILTER_TAG$index", it.writeToNBT(NBTTagCompound())) } + } + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + enabled = if (nbt.hasKey(ENABLED_TAG)) nbt.getBoolean(ENABLED_TAG) else enabled + isInput = if (nbt.hasKey(INPUT_TAG)) nbt.getBoolean(INPUT_TAG) else isInput + interactWithHand = if (nbt.hasKey(HAND_TAG)) nbt.getBoolean(HAND_TAG) else interactWithHand + interactWithWorld = if (nbt.hasKey(WORLD_TAG)) nbt.getBoolean(WORLD_TAG) else interactWithWorld + interactWithFluidHandlers = if (nbt.hasKey(FLUID_HANDLERS_TAG)) nbt.getBoolean(FLUID_HANDLERS_TAG) else interactWithFluidHandlers + if (nbt.hasKey(COOLDOWN_UNTIL_TAG)) + cooldownUntil = nbt.getLong(COOLDOWN_UNTIL_TAG) + if (nbt.hasKey(LAST_HAND_ACTION_TAG)) + lastHandAction = nbt.getLong(LAST_HAND_ACTION_TAG) + for (slot in fluidFilters.indices) { + fluidFilters[slot] = if (nbt.hasKey("$FILTER_TAG$slot")) { + FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("$FILTER_TAG$slot")) + } else null + } + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.PUMP_UPGRADE_CAPABILITY || + capability == Capabilities.IPUMP_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} + +class AdvancedPumpUpgradeWrapper : PumpUpgradeWrapper( + filterSlots = Config.pumpUpgrade.filterSlots, + interactWithHandDefault = true, + interactWithWorldDefault = false, + interactWithFluidHandlersDefault = true +) { + override val settingsLangKey = "gui.advanced_pump_settings".asTranslationKey() + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_PUMP_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RefillUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RefillUpgradeWrapper.kt new file mode 100644 index 0000000..25f082a --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RefillUpgradeWrapper.kt @@ -0,0 +1,304 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.item.RefillUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagList +import net.minecraft.util.EnumFacing +import net.minecraft.util.EnumHand +import net.minecraft.util.text.TextFormatting +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.Constants +import net.minecraftforge.items.ItemHandlerHelper + +open class RefillUpgradeWrapper( + filterSlots: Int = Config.refillUpgrade.filterSlots, + slotsInRow: Int = Config.refillUpgrade.slotsInRow +) : + BasicUpgradeWrapper(filterSlots, slotsInRow), IRefillUpgrade { + override val settingsLangKey = "gui.refill_settings".asTranslationKey() + + init { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + + override fun refill(player: EntityPlayer, wrapper: BackpackWrapper) { + if (!enabled) { + return + } + + for ((filterSlot, filter) in filterItems.inventory.withIndex()) { + if (!filter.isEmpty) { + refillFilter(player, wrapper, filter, getTargetSlot(filterSlot)) + } + } + } + + open fun getTargetSlot(filterSlot: Int): TargetSlot = + TargetSlot.ANY + + protected fun refillFilter(player: EntityPlayer, wrapper: BackpackWrapper, filter: ItemStack, targetSlot: TargetSlot) { + var missingCount = targetSlot.getMissingCount(player, filter) + val carried = player.inventory.itemStack + if (ItemHandlerHelper.canItemStacksStack(carried, filter)) { + missingCount -= minOf(missingCount, carried.count) + } + if (missingCount <= 0) { + return + } + + val extracted = wrapper.extractMatching(filter, missingCount, true) + if (extracted.isEmpty) { + return + } + + val remaining = targetSlot.insert(player, extracted) + val moved = extracted.count - remaining.count + + if (moved > 0) { + wrapper.extractMatching(filter, moved, false) + } + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.REFILL_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) + + enum class TargetSlot { + ANY { + override fun getMissingCount(player: EntityPlayer, filter: ItemStack): Int { + var count = filter.maxStackSize + for (slot in 0 until player.inventory.mainInventory.size) { + val stack = player.inventory.getStackInSlot(slot) + if (ItemHandlerHelper.canItemStacksStack(stack, filter)) { + count -= minOf(stack.count, count) + if (count <= 0) { + return 0 + } + } + } + return count + } + + override fun insert(player: EntityPlayer, stack: ItemStack): ItemStack { + val remaining = stack.copy() + for (slot in 0 until player.inventory.mainInventory.size) { + val target = player.inventory.getStackInSlot(slot) + if (!ItemHandlerHelper.canItemStacksStack(target, remaining)) { + continue + } + val moved = minOf(remaining.count, target.maxStackSize - target.count) + if (moved > 0) { + target.grow(moved) + remaining.shrink(moved) + } + if (remaining.isEmpty) { + return ItemStack.EMPTY + } + } + for (slot in 0 until player.inventory.mainInventory.size) { + if (!player.inventory.getStackInSlot(slot).isEmpty) { + continue + } + val moved = minOf(remaining.count, remaining.maxStackSize) + player.inventory.setInventorySlotContents(slot, ItemHandlerHelper.copyStackWithSize(remaining, moved)) + remaining.shrink(moved) + if (remaining.isEmpty) { + return ItemStack.EMPTY + } + } + return remaining + } + }, + MAIN_HAND { + override fun getMissingCount(player: EntityPlayer, filter: ItemStack): Int = + getSlotMissingCount(player.heldItemMainhand, filter) + + override fun insert(player: EntityPlayer, stack: ItemStack): ItemStack = + refillSlot(player.heldItemMainhand, stack) { player.inventory.setInventorySlotContents(player.inventory.currentItem, it) } + }, + OFF_HAND { + override fun getMissingCount(player: EntityPlayer, filter: ItemStack): Int = + getSlotMissingCount(player.heldItemOffhand, filter) + + override fun insert(player: EntityPlayer, stack: ItemStack): ItemStack = + refillSlot(player.heldItemOffhand, stack) { player.setHeldItem(net.minecraft.util.EnumHand.OFF_HAND, it) } + }, + HOTBAR_1, HOTBAR_2, HOTBAR_3, HOTBAR_4, HOTBAR_5, HOTBAR_6, HOTBAR_7, HOTBAR_8, HOTBAR_9; + + fun next(): TargetSlot = + entries[(ordinal + 1) % entries.size] + + fun previous(): TargetSlot = + entries[Math.floorMod(ordinal - 1, entries.size)] + + fun acronym(): String = when (this) { + ANY -> "A" + MAIN_HAND -> "M" + OFF_HAND -> "O" + else -> (ordinal - HOTBAR_1.ordinal + 1).toString() + } + + fun descriptionKey(): String = when (this) { + ANY -> "gui.refill_target_any".asTranslationKey() + MAIN_HAND -> "gui.refill_target_main_hand".asTranslationKey() + OFF_HAND -> "gui.refill_target_off_hand".asTranslationKey() + else -> "gui.refill_target_hotbar_${descriptionArg()}".asTranslationKey() + } + + fun descriptionArg(): String = + (ordinal - HOTBAR_1.ordinal + 1).toString() + + fun descriptionColor(): TextFormatting = + TextFormatting.DARK_GREEN + + open fun getMissingCount(player: EntityPlayer, filter: ItemStack): Int { + val slot = ordinal - HOTBAR_1.ordinal + return getSlotMissingCount(player.inventory.getStackInSlot(slot), filter) + } + + open fun insert(player: EntityPlayer, stack: ItemStack): ItemStack { + val slot = ordinal - HOTBAR_1.ordinal + return refillSlot(player.inventory.getStackInSlot(slot), stack) { + player.inventory.setInventorySlotContents(slot, it) + } + } + + companion object { + private fun getSlotMissingCount(stack: ItemStack, filter: ItemStack): Int = + if (stack.isEmpty) filter.maxStackSize + else if (ItemHandlerHelper.canItemStacksStack(stack, filter)) stack.maxStackSize - stack.count + else 0 + + private fun refillSlot(stack: ItemStack, toAdd: ItemStack, setter: (ItemStack) -> Unit): ItemStack { + if (stack.isEmpty) { + setter(toAdd.copy()) + return ItemStack.EMPTY + } + if (!ItemHandlerHelper.canItemStacksStack(stack, toAdd)) { + return toAdd + } + val moved = minOf(toAdd.count, stack.maxStackSize - stack.count) + stack.grow(moved) + return if (moved == toAdd.count) ItemStack.EMPTY else ItemHandlerHelper.copyStackWithSize(toAdd, toAdd.count - moved) + } + } + } +} + +class AdvancedRefillUpgradeWrapper : + RefillUpgradeWrapper(Config.advancedRefillUpgrade.filterSlots, Config.advancedRefillUpgrade.slotsInRow) { + companion object { + private const val TARGET_SLOTS_TAG = "TargetSlots" + private const val SLOT_TAG = "Slot" + private const val TARGET_TAG = "Target" + } + + override val settingsLangKey = "gui.advanced_refill_settings".asTranslationKey() + private val targetSlots = mutableMapOf() + + override fun getTargetSlot(filterSlot: Int): TargetSlot = + targetSlots[filterSlot] ?: TargetSlot.ANY + + fun setTargetSlot(filterSlot: Int, targetSlot: TargetSlot) { + targetSlots[filterSlot] = targetSlot + } + + fun pickBlock(player: EntityPlayer, wrapper: BackpackWrapper, pickedStack: ItemStack): Boolean { + if (!enabled || pickedStack.isEmpty) { + return false + } + + val extracted = wrapper.extractMatching(pickedStack, pickedStack.maxStackSize, true) + if (extracted.isEmpty || !moveHeldStackAway(player, wrapper)) { + return false + } + + player.setHeldItem(EnumHand.MAIN_HAND, wrapper.extractMatching(pickedStack, extracted.count, false)) + player.inventoryContainer.detectAndSendChanges() + return true + } + + private fun moveHeldStackAway(player: EntityPlayer, wrapper: BackpackWrapper): Boolean { + val held = player.heldItemMainhand + if (held.isEmpty) { + return true + } + + if (wrapper.insertStack(held.copy(), true).isEmpty) { + wrapper.insertStack(held.copy(), false) + player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY) + return true + } + + val remaining = held.copy() + val currentSlot = player.inventory.currentItem + for (slot in 0 until player.inventory.mainInventory.size) { + if (slot == currentSlot) { + continue + } + val target = player.inventory.getStackInSlot(slot) + if (!ItemHandlerHelper.canItemStacksStack(target, remaining)) { + continue + } + val moved = minOf(remaining.count, target.maxStackSize - target.count) + if (moved > 0) { + target.grow(moved) + remaining.shrink(moved) + } + if (remaining.isEmpty) { + break + } + } + for (slot in 0 until player.inventory.mainInventory.size) { + if (remaining.isEmpty) { + break + } + if (slot != currentSlot && player.inventory.getStackInSlot(slot).isEmpty) { + player.inventory.setInventorySlotContents(slot, remaining.copy()) + remaining.count = 0 + } + } + + if (!remaining.isEmpty) { + return false + } + + player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY) + return true + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + val list = NBTTagList() + for ((slot, target) in targetSlots) { + val entry = NBTTagCompound() + entry.setInteger(SLOT_TAG, slot) + entry.setByte(TARGET_TAG, target.ordinal.toByte()) + list.appendTag(entry) + } + nbt.setTag(TARGET_SLOTS_TAG, list) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + targetSlots.clear() + val list = nbt.getTagList(TARGET_SLOTS_TAG, Constants.NBT.TAG_COMPOUND) + for (entry in list) { + val compound = entry as NBTTagCompound + val targetOrdinal = compound.getByte(TARGET_TAG).toInt() + targetSlots[compound.getInteger(SLOT_TAG)] = TargetSlot.entries.getOrElse(targetOrdinal) { TargetSlot.ANY } + } + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_REFILL_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RestockUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RestockUpgradeWrapper.kt index 44884c2..029c052 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RestockUpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/RestockUpgradeWrapper.kt @@ -1,13 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.RestockUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.item.ItemStack import net.minecraft.util.EnumFacing import net.minecraftforge.common.capabilities.Capability -class RestockUpgradeWrapper : BasicUpgradeWrapper(), IRestockUpgrade { +class RestockUpgradeWrapper : + BasicUpgradeWrapper(Config.restockUpgrade.filterSlots, Config.restockUpgrade.slotsInRow), + IRestockUpgrade { override val settingsLangKey: String = "gui.restock_settings".asTranslationKey() override fun canRestock(stack: ItemStack): Boolean = @@ -17,4 +20,4 @@ class RestockUpgradeWrapper : BasicUpgradeWrapper(), IRestoc capability == Capabilities.RESTOCK_UPGRADE_CAPABILITY || super.hasCapability(capability, facing) || super.hasCapability(capability, facing) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/TankUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/TankUpgradeWrapper.kt new file mode 100644 index 0000000..c79f3f9 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/TankUpgradeWrapper.kt @@ -0,0 +1,319 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.inventory.ExposedItemStackHandler +import com.cleanroommc.retrosophisticatedbackpacks.item.TankUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraft.world.World +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.FluidUtil +import net.minecraftforge.fluids.capability.IFluidHandlerItem +import net.minecraftforge.fluids.capability.IFluidTankProperties +import net.minecraftforge.fluids.capability.FluidTankProperties +import net.minecraftforge.items.IItemHandler +import net.minecraftforge.items.ItemHandlerHelper + +class TankUpgradeWrapper : UpgradeWrapper(), ITankUpgrade { + companion object { + private const val FLUID_TAG = "Fluid" + private const val INVENTORY_TAG = "Inventory" + private const val INPUT_SLOT = 0 + private const val OUTPUT_SLOT = 1 + private const val INPUT_RESULT_SLOT = 2 + private const val OUTPUT_RESULT_SLOT = 3 + private const val BUCKET = 1000 + } + + override val settingsLangKey = "gui.tank_settings".asTranslationKey() + override val tankCapacity = Config.tankUpgrade.capacityPerSlotRow * 3 + private var fluid: FluidStack? = null + private var nextContainerActionTime = 0L + private val inventory = object : ExposedItemStackHandler(4) { + override fun isItemValid(slot: Int, stack: ItemStack): Boolean = + slot == INPUT_RESULT_SLOT || slot == OUTPUT_RESULT_SLOT || stack.isEmpty || FluidUtil.getFluidHandler(stack) != null + + override fun onContentsChanged(slot: Int) { + super.onContentsChanged(slot) + } + } + + override fun getFluid(): FluidStack? = + fluid?.copy() + + override fun getTankCapacity(wrapper: BackpackWrapper): Int = + maxOf(BUCKET, (getSlotRows(wrapper) * Config.tankUpgrade.capacityPerSlotRow * getAdjustedStackMultiplier(wrapper)).toInt()) + + private fun getMaxInOut(wrapper: BackpackWrapper): Int = + maxOf(BUCKET, (getSlotRows(wrapper) * Config.tankUpgrade.maxInputOutput * getAdjustedStackMultiplier(wrapper)).toInt()) + + private fun getSlotRows(wrapper: BackpackWrapper): Int = + maxOf(1, (wrapper.backpackInventorySize() + 8) / 9) + + private fun getAdjustedStackMultiplier(wrapper: BackpackWrapper): Double = + 1.0 + Config.tankUpgrade.stackMultiplierRatio * (wrapper.getTotalStackMultiplier() - 1) + + override fun fill(wrapper: BackpackWrapper, resource: FluidStack, doFill: Boolean, ignoreInOutLimit: Boolean): Int { + val current = fluid + if (resource.amount <= 0 || current != null && !current.isFluidEqual(resource)) { + return 0 + } + + val accepted = minOf(resource.amount, getTankCapacity(wrapper) - (current?.amount ?: 0), if (ignoreInOutLimit) Int.MAX_VALUE else getMaxInOut(wrapper)) + if (doFill && accepted > 0) { + fluid = if (current == null) FluidStack(resource.fluid, accepted, resource.tag?.copy()) + else current.also { it.amount += accepted } + } + return accepted + } + + override fun drain(wrapper: BackpackWrapper, maxDrain: Int, doDrain: Boolean, ignoreInOutLimit: Boolean): FluidStack? { + val current = fluid ?: return null + val drained = minOf(maxDrain, current.amount, if (ignoreInOutLimit) Int.MAX_VALUE else getMaxInOut(wrapper)) + if (drained <= 0) { + return null + } + + val result = FluidStack(current.fluid, drained, current.tag?.copy()) + if (doDrain) { + current.amount -= drained + if (current.amount <= 0) { + fluid = null + } + } + return result + } + + override fun drain(wrapper: BackpackWrapper, resource: FluidStack, doDrain: Boolean, ignoreInOutLimit: Boolean): FluidStack? { + val current = fluid ?: return null + if (!current.isFluidEqual(resource)) { + return null + } + return drain(wrapper, resource.amount, doDrain, ignoreInOutLimit) + } + + override fun tick(wrapper: BackpackWrapper, world: World) { + if (world.totalWorldTime < nextContainerActionTime) { + return + } + var didSomething = tryDrainInput(wrapper) + didSomething = tryFillOutput(wrapper) || didSomething + if (didSomething) { + nextContainerActionTime = + world.totalWorldTime + Config.tankUpgrade.autoFillDrainContainerCooldown.coerceAtLeast(1) + } + } + + override fun getInventory(): IItemHandler = + inventory + + override fun interactWithCursorStack(player: EntityPlayer, wrapper: BackpackWrapper) { + val cursor = player.inventory.itemStack + if (cursor.isEmpty) { + return + } + val handler = FluidUtil.getFluidHandler(cursor.copy()) ?: return + val current = fluid + if (current != null && fillHandler(wrapper, handler, false)) { + player.inventory.itemStack = handler.container + } else if (drainHandler(wrapper, handler, false)) { + player.inventory.itemStack = handler.container + } + } + + private fun tryDrainInput(wrapper: BackpackWrapper): Boolean { + val stack = inventory.getStackInSlot(INPUT_SLOT) + if (stack.isEmpty) { + return false + } + val single = ItemHandlerHelper.copyStackWithSize(stack, 1) + if (stack.count > 1 && !drainStack(wrapper, single, true, true, {})) { + return false + } + return drainStack(wrapper, single, true, false) { container -> + if (stack.count > 1) { + stack.shrink(1) + if (stack.isEmpty) { + inventory.setStackInSlot(INPUT_SLOT, ItemStack.EMPTY) + } + } else { + inventory.setStackInSlot(INPUT_SLOT, container) + } + } + } + + private fun tryFillOutput(wrapper: BackpackWrapper): Boolean { + val stack = inventory.getStackInSlot(OUTPUT_SLOT) + if (stack.isEmpty) { + return false + } + val single = ItemHandlerHelper.copyStackWithSize(stack, 1) + if (stack.count > 1 && !fillStack(wrapper, single, true, true, {})) { + return false + } + return fillStack(wrapper, single, true, false) { container -> + if (stack.count > 1) { + stack.shrink(1) + if (stack.isEmpty) { + inventory.setStackInSlot(OUTPUT_SLOT, ItemStack.EMPTY) + } + } else { + inventory.setStackInSlot(OUTPUT_SLOT, container) + } + } + } + + private fun drainStack( + wrapper: BackpackWrapper, + stack: ItemStack, + moveResult: Boolean, + requireFullDrain: Boolean, + updateContainerStack: (ItemStack) -> Unit + ): Boolean = + FluidUtil.getFluidHandler(stack)?.let { drainHandler(wrapper, it, moveResult, requireFullDrain, updateContainerStack) } ?: false + + private fun fillStack( + wrapper: BackpackWrapper, + stack: ItemStack, + moveResult: Boolean, + requireFullFill: Boolean, + updateContainerStack: (ItemStack) -> Unit + ): Boolean = + FluidUtil.getFluidHandler(stack)?.let { fillHandler(wrapper, it, moveResult, requireFullFill, updateContainerStack) } ?: false + + private fun drainHandler( + wrapper: BackpackWrapper, + handler: IFluidHandlerItem, + moveResult: Boolean, + requireFullDrain: Boolean = false, + updateContainerStack: (ItemStack) -> Unit = {} + ): Boolean { + val current = fluid + val toDrain = if (current == null) BUCKET else minOf(BUCKET, getTankCapacity(wrapper) - current.amount) + val extracted = if (current == null) handler.drain(toDrain, false) else handler.drain(FluidStack(current.fluid, toDrain, current.tag?.copy()), false) + if (extracted == null || extracted.amount <= 0 || fill(wrapper, extracted, false) <= 0) { + return false + } + if (requireFullDrain && fill(wrapper, extracted, false) != firstTankCapacity(handler)) { + return false + } + if (moveResult) { + val preview = FluidUtil.getFluidHandler(handler.container.copy()) ?: return false + val filled = fill(wrapper, extracted, false) + preview.drain(FluidStack(extracted.fluid, filled, extracted.tag?.copy()), true) + val movesToResult = hasNoMatchingFluid(preview) + if (requireFullDrain && !movesToResult) { + return false + } + if (movesToResult && !inventory.insertItem(INPUT_RESULT_SLOT, preview.container, true).isEmpty) { + return false + } + } + if (requireFullDrain) { + return true + } + val filled = fill(wrapper, extracted, true) + handler.drain(FluidStack(extracted.fluid, filled, extracted.tag?.copy()), true) + val resultHandler = FluidUtil.getFluidHandler(handler.container.copy()) + if (moveResult && (resultHandler?.let(::hasNoMatchingFluid) ?: true)) { + updateContainerStack(ItemStack.EMPTY) + inventory.insertItem(INPUT_RESULT_SLOT, handler.container, false) + } else { + updateContainerStack(handler.container) + } + return true + } + + private fun fillHandler( + wrapper: BackpackWrapper, + handler: IFluidHandlerItem, + moveResult: Boolean, + requireFullFill: Boolean = false, + updateContainerStack: (ItemStack) -> Unit = {} + ): Boolean { + val current = fluid ?: return false + val filled = handler.fill(FluidStack(current.fluid, minOf(BUCKET, current.amount), current.tag?.copy()), false) + if (filled <= 0 || drain(wrapper, filled, false) == null) { + return false + } + if (requireFullFill && drain(wrapper, filled, false)?.amount != firstTankCapacity(handler)) { + return false + } + if (moveResult) { + val preview = FluidUtil.getFluidHandler(handler.container.copy()) ?: return false + val drained = drain(wrapper, filled, false) ?: return false + preview.fill(drained, true) + val movesToResult = matchingTankIsFull(preview) + if (requireFullFill && !movesToResult) { + return false + } + if (movesToResult && !inventory.insertItem(OUTPUT_RESULT_SLOT, preview.container, true).isEmpty) { + return false + } + } + if (requireFullFill) { + return true + } + val drained = drain(wrapper, filled, true) ?: return false + handler.fill(drained, true) + val resultHandler = FluidUtil.getFluidHandler(handler.container.copy()) + if (moveResult && resultHandler?.let(::matchingTankIsFull) == true) { + updateContainerStack(ItemStack.EMPTY) + inventory.insertItem(OUTPUT_RESULT_SLOT, handler.container, false) + } else { + updateContainerStack(handler.container) + } + return true + } + + private fun hasNoMatchingFluid(handler: IFluidHandlerItem): Boolean = + handler.tankProperties.all { it.contents?.amount ?: 0 <= 0 } + + private fun firstTankCapacity(handler: IFluidHandlerItem): Int = + handler.tankProperties.firstOrNull()?.capacity ?: 0 + + private fun matchingTankIsFull(handler: IFluidHandlerItem): Boolean { + val current = fluid + if (current == null) { + return handler.tankProperties.all { property -> + val contents = property.contents + contents != null && contents.amount >= property.capacity + } + } + return handler.tankProperties.all { property -> + val contents = property.contents + contents == null || !contents.isFluidEqual(current) || contents.amount >= property.capacity + } + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + fluid?.let { nbt.setTag(FLUID_TAG, it.writeToNBT(NBTTagCompound())) } + nbt.setTag(INVENTORY_TAG, inventory.serializeNBT()) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(FLUID_TAG)) + fluid = FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag(FLUID_TAG)) + else if (nbt.hasKey(INVENTORY_TAG)) + fluid = null + if (nbt.hasKey(INVENTORY_TAG)) + inventory.deserializeNBT(nbt.getCompoundTag(INVENTORY_TAG)) + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.TANK_UPGRADE_CAPABILITY || + capability == Capabilities.ITANK_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) + + fun getTankProperties(wrapper: BackpackWrapper): Array = + arrayOf(FluidTankProperties(getFluid(), getTankCapacity(wrapper), true, true)) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ToolSwapperUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ToolSwapperUpgradeWrapper.kt new file mode 100644 index 0000000..1265a8e --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/ToolSwapperUpgradeWrapper.kt @@ -0,0 +1,386 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.UpgradeFilterUtils.matchesAllowEmpty +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem +import com.cleanroommc.retrosophisticatedbackpacks.item.ToolSwapperUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.block.Block +import net.minecraft.block.material.Material +import net.minecraft.block.state.IBlockState +import net.minecraft.entity.Entity +import net.minecraft.entity.SharedMonsterAttributes +import net.minecraft.entity.passive.EntityAnimal +import net.minecraft.entity.passive.EntityCow +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.init.Blocks +import net.minecraft.init.Items +import net.minecraft.item.ItemAxe +import net.minecraft.item.ItemHoe +import net.minecraft.item.ItemPickaxe +import net.minecraft.item.ItemSpade +import net.minecraft.item.ItemShears +import net.minecraft.item.ItemStack +import net.minecraft.item.ItemSword +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.NBTTagString +import net.minecraft.util.EnumFacing +import net.minecraft.util.EnumHand +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import net.minecraftforge.common.IShearable +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.util.Constants +import java.util.LinkedList + +open class ToolSwapperUpgradeWrapper( + private val hasSettingsTab: Boolean = false, + private val swapToolOnKeyPress: Boolean = false, +) : BasicUpgradeWrapper(Config.toolSwapperUpgrade.filterSlots, Config.toolSwapperUpgrade.slotsInRow), + IToolSwapperUpgrade, IAdvancedFilterable { + companion object { + private const val SHOULD_SWAP_WEAPON_TAG = "ShouldSwapWeapon" + private const val TOOL_SWAP_MODE_TAG = "ToolSwapMode" + } + + protected open val exposesAdvancedFilter = false + override val settingsLangKey = "gui.tool_swapper_settings".asTranslationKey() + var shouldSwapWeapon = true + var toolSwapMode = ToolSwapMode.ANY + override var matchType = IAdvancedFilterable.MatchType.ITEM + override var oreDictEntries = mutableListOf() + override var ignoreDurability = true + override var ignoreNBT = true + private var lastMinedBlock: Block = Blocks.AIR + private var toolCacheFor: String? = null + private val toolCache = LinkedList() + + init { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + + override fun checkFilter(stack: ItemStack): Boolean = + if (exposesAdvancedFilter) enabled && super.checkFilter(stack) + else super.checkFilter(stack) + + override fun onBlockClick(player: EntityPlayer, wrapper: BackpackWrapper, pos: BlockPos, state: IBlockState): Boolean { + if (!enabled || player.isCreative || player.isSpectator || toolSwapMode == ToolSwapMode.NO_SWAP || state.material == Material.AIR) { + return false + } + + val held = player.heldItemMainhand + if (held.item is BackpackItem || (toolSwapMode == ToolSwapMode.ONLY_TOOLS && isSword(held)) || (!isSword(held) && isNotTool(held)) || !matchesAllowEmpty(held)) { + return false + } + + val heldSpeed = if (isGoodAtBreaking(player, pos, state, held)) { + if (lastMinedBlock == state.block || state.getBlockHardness(player.world, pos) == 0f) { + return true + } + held.getDestroySpeed(state) + } else 0f + lastMinedBlock = state.block + val selectedSlot = findBestToolSlot(wrapper, player, pos, state, heldSpeed) ?: return false + return swapMainHandWithBackpackSlot(player, wrapper, selectedSlot) + } + + override fun onAttackEntity(player: EntityPlayer, wrapper: BackpackWrapper): Boolean { + if (!enabled || !shouldSwapWeapon) { + return false + } + + val held = player.heldItemMainhand + if (isSword(held)) { + return true + } + if (held.item is BackpackItem || isNotTool(held) || !matchesAllowEmpty(held)) { + return false + } + + val selectedSlot = findBestWeaponSlot(wrapper, player, held) ?: return false + return swapMainHandWithBackpackSlot(player, wrapper, selectedSlot) + } + + override fun canProcessBlockInteract(): Boolean = swapToolOnKeyPress + + override fun onBlockInteract(player: EntityPlayer, wrapper: BackpackWrapper, world: World, pos: BlockPos, state: IBlockState): Boolean { + if (!enabled || !swapToolOnKeyPress || player.heldItemMainhand.item is BackpackItem) { + return false + } + return tryToSwapTool(player, wrapper, state.block.registryName?.toString()) { + itemWorksOnBlock(world, pos, state, it) + } + } + + override fun canProcessEntityInteract(): Boolean = swapToolOnKeyPress + + override fun onEntityInteract(player: EntityPlayer, wrapper: BackpackWrapper, entity: Entity): Boolean { + if (!enabled || !swapToolOnKeyPress || player.heldItemMainhand.item is BackpackItem) { + return false + } + return tryToSwapTool(player, wrapper, entity.javaClass.name) { itemWorksOnEntity(entity, it) } + } + + private fun findBestToolSlot(wrapper: BackpackWrapper, player: EntityPlayer, pos: BlockPos, state: IBlockState, heldSpeed: Float): Int? { + var bestSlot: Int? = null + var bestSpeed = heldSpeed + for (slot in 0 until wrapper.slots) { + val stack = wrapper.getStackInSlot(slot) + if (stack.isEmpty || !matchesAllowEmpty(stack)) { + continue + } + if (!canHarvestDropsWith(state, stack)) continue + val destroyProgress = getDestroyProgressWith(player, pos, state, stack) + val speed = stack.getDestroySpeed(state) + if (speed <= 1.5f && destroyProgress < 1f) continue + if (destroyProgress >= 1f) { + return slot + } + if (speed > bestSpeed) { + bestSpeed = speed + bestSlot = slot + } + } + return bestSlot + } + + private fun findBestWeaponSlot(wrapper: BackpackWrapper, player: EntityPlayer, held: ItemStack): Int? { + var bestSwordSlot: Int? = null + var bestSwordDamage = if (isSword(held)) getAttackDamage(held, player) else 0.0 + var bestAxeSlot: Int? = null + var bestAxeDamage = if (isAxe(held)) getAttackDamage(held, player) else 0.0 + for (slot in 0 until wrapper.slots) { + val stack = wrapper.getStackInSlot(slot) + if (stack.isEmpty || !matchesAllowEmpty(stack)) { + continue + } + val damage = getAttackDamage(stack, player) + if (isSword(stack)) { + if (damage > bestSwordDamage) { + bestSwordDamage = damage + bestSwordSlot = slot + } + } else if (isAxe(stack) && damage > bestAxeDamage) { + bestAxeDamage = damage + bestAxeSlot = slot + } + } + return bestSwordSlot ?: bestAxeSlot + } + + private fun tryToSwapTool( + player: EntityPlayer, + wrapper: BackpackWrapper, + targetRegistryName: String?, + predicate: (ItemStack) -> Boolean + ): Boolean { + if (toolCacheFor != targetRegistryName) { + toolCache.clear() + toolCacheFor = targetRegistryName + } + + val held = player.heldItemMainhand + if (!held.isEmpty && predicate(held) && toolCache.none { isSameTool(it, held) }) { + toolCache.offer(held.copy().also { it.count = 1 }) + } + + val selectedSlot = findToolToSwapSlot(wrapper, predicate) ?: return false + val selectedTool = wrapper.getStackInSlot(selectedSlot).copy().also { it.count = 1 } + if (!swapMainHandWithBackpackSlot(player, wrapper, selectedSlot)) { + return false + } + toolCache.offer(selectedTool) + return true + } + + private fun findToolToSwapSlot(wrapper: BackpackWrapper, predicate: (ItemStack) -> Boolean): Int? { + val alreadyGivenBefore = mutableListOf() + for (slot in 0 until wrapper.slots) { + val stack = wrapper.getStackInSlot(slot) + if (!stack.isEmpty && matchesAllowEmpty(stack) && predicate(stack)) { + if (toolCache.none { isSameTool(it, stack) }) { + return slot + } + alreadyGivenBefore.add(stack.copy().also { it.count = 1 }) + } + } + + while (toolCache.peek() != null) { + val cached = toolCache.poll() + if (alreadyGivenBefore.any { isSameTool(it, cached) }) { + return findSlotWithSameTool(wrapper, cached, predicate) + } + } + return null + } + + private fun findSlotWithSameTool(wrapper: BackpackWrapper, tool: ItemStack, predicate: (ItemStack) -> Boolean): Int? { + for (slot in 0 until wrapper.slots) { + val stack = wrapper.getStackInSlot(slot) + if (!stack.isEmpty && matchesAllowEmpty(stack) && predicate(stack) && isSameTool(stack, tool)) { + return slot + } + } + return null + } + + private fun isSameTool(first: ItemStack, second: ItemStack): Boolean = + !first.isEmpty && !second.isEmpty && first.item == second.item + + private fun swapMainHandWithBackpackSlot(player: EntityPlayer, wrapper: BackpackWrapper, slot: Int): Boolean { + val held = player.heldItemMainhand + val selectedStack = wrapper.getStackInSlot(slot) + val tool = wrapper.extractItem(slot, 1, true) + if (tool.isEmpty) { + return false + } + val canStoreHeld = held.isEmpty || + wrapper.insertStack(held.copy(), true).isEmpty || + (selectedStack.count == 1 && wrapper.backpackItemStackHandler.isItemValid(slot, held)) + if (!canStoreHeld) return false + + val extractedTool = wrapper.extractItem(slot, 1, false) + if (extractedTool.isEmpty) return false + + player.setHeldItem(EnumHand.MAIN_HAND, extractedTool) + player.inventoryContainer.detectAndSendChanges() + if (!held.isEmpty) { + val remaining = wrapper.insertStack(held.copy(), false) + if (!remaining.isEmpty) { + wrapper.insertItem(slot, remaining, false) + } + } + return true + } + + private fun isGoodAtBreaking(player: EntityPlayer, pos: BlockPos, state: IBlockState, stack: ItemStack): Boolean = + canHarvestDropsWith(state, stack) && (stack.getDestroySpeed(state) > 1.5f || getDestroyProgressWith(player, pos, state, stack) >= 1f) + + private fun canHarvestDropsWith(state: IBlockState, stack: ItemStack): Boolean = + state.material.isToolNotRequired || stack.canHarvestBlock(state) + + private fun getDestroyProgressWith(player: EntityPlayer, pos: BlockPos, state: IBlockState, stack: ItemStack): Float { + val held = player.heldItemMainhand + player.setHeldItem(EnumHand.MAIN_HAND, stack) + return try { + state.getPlayerRelativeBlockHardness(player, player.world, pos) + } finally { + player.setHeldItem(EnumHand.MAIN_HAND, held) + } + } + + private fun isNotTool(stack: ItemStack): Boolean = + stack.isEmpty || !isTool(stack) + + private fun isTool(stack: ItemStack): Boolean = + stack.item is ItemAxe || stack.item is ItemHoe || stack.item is ItemPickaxe || stack.item is ItemSpade || + stack.item is ItemShears || stack.item.getToolClasses(stack).isNotEmpty() || + stack.getDestroySpeed(Blocks.STONE.defaultState) > 1f || + stack.getDestroySpeed(Blocks.DIRT.defaultState) > 1f || + stack.getDestroySpeed(Blocks.LOG.defaultState) > 1f + + private fun isSword(stack: ItemStack): Boolean = + stack.item is ItemSword + + private fun isAxe(stack: ItemStack): Boolean = + stack.item is ItemAxe || "axe" in stack.item.getToolClasses(stack) + + private fun getAttackDamage(stack: ItemStack, player: EntityPlayer): Double { + if (stack.isEmpty) { + return player.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).baseValue + } + var damage = 0.0 + stack.getAttributeModifiers(net.minecraft.inventory.EntityEquipmentSlot.MAINHAND).get(SharedMonsterAttributes.ATTACK_DAMAGE.name) + .forEach { damage += it.amount } + return damage + } + + private fun itemWorksOnBlock(world: World, pos: BlockPos, state: IBlockState, stack: ItemStack): Boolean { + if (stack.item is ItemShears && state.block is IShearable) { + return (state.block as IShearable).isShearable(stack, world, pos) + } + if (stack.item is ItemSpade && state.block == Blocks.GRASS && world.isAirBlock(pos.up())) { + return true + } + val harvestTool = state.block.getHarvestTool(state) + return harvestTool != null && harvestTool in stack.item.getToolClasses(stack) + } + + private fun itemWorksOnEntity(entity: Entity, stack: ItemStack): Boolean = + stack.item is ItemShears && entity is IShearable && entity.isShearable(stack, entity.world, entity.position) || + entity is EntityCow && stack.item == Items.BUCKET || + entity is EntityAnimal && stack.item == Items.LEAD + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setBoolean(SHOULD_SWAP_WEAPON_TAG, shouldSwapWeapon) + nbt.setByte(TOOL_SWAP_MODE_TAG, toolSwapMode.ordinal.toByte()) + if (exposesAdvancedFilter) { + nbt.setByte(IAdvancedFilterable.MATCH_TYPE_TAG, matchType.ordinal.toByte()) + nbt.setBoolean(IAdvancedFilterable.IGNORE_DURABILITY_TAG, ignoreDurability) + nbt.setBoolean(IAdvancedFilterable.IGNORE_NBT_TAG, ignoreNBT) + val oreDictList = NBTTagList() + for (entry in oreDictEntries) { + oreDictList.appendTag(NBTTagString(entry)) + } + nbt.setTag(IAdvancedFilterable.ORE_DICT_LIST_TAG, oreDictList) + } + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (filterItems.inventory.all(ItemStack::isEmpty) && filterType == IBasicFilterable.FilterType.WHITELIST) { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + shouldSwapWeapon = !nbt.hasKey(SHOULD_SWAP_WEAPON_TAG) || nbt.getBoolean(SHOULD_SWAP_WEAPON_TAG) + if (nbt.hasKey(TOOL_SWAP_MODE_TAG)) { + toolSwapMode = ToolSwapMode.entries.getOrElse(nbt.getByte(TOOL_SWAP_MODE_TAG).toInt()) { ToolSwapMode.ANY } + } + if (nbt.hasKey(IAdvancedFilterable.MATCH_TYPE_TAG)) { + matchType = IAdvancedFilterable.MatchType.entries.getOrElse(nbt.getByte(IAdvancedFilterable.MATCH_TYPE_TAG).toInt()) { matchType } + } + if (nbt.hasKey(IAdvancedFilterable.IGNORE_DURABILITY_TAG)) { + ignoreDurability = nbt.getBoolean(IAdvancedFilterable.IGNORE_DURABILITY_TAG) + } + if (nbt.hasKey(IAdvancedFilterable.IGNORE_NBT_TAG)) { + ignoreNBT = nbt.getBoolean(IAdvancedFilterable.IGNORE_NBT_TAG) + } + if (nbt.hasKey(IAdvancedFilterable.ORE_DICT_LIST_TAG)) { + val oreDictList = nbt.getTagList(IAdvancedFilterable.ORE_DICT_LIST_TAG, Constants.NBT.TAG_STRING) + oreDictEntries.clear() + for (stringNBT in oreDictList) { + oreDictEntries.add((stringNBT as NBTTagString).string) + } + } + } + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.TOOL_SWAPPER_UPGRADE_CAPABILITY || + exposesAdvancedFilter && capability == Capabilities.ADVANCED_FILTERABLE_CAPABILITY || + capability == Capabilities.ITOOL_SWAPPER_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) + + fun hasSettingsTab(): Boolean = hasSettingsTab +} + +class AdvancedToolSwapperUpgradeWrapper : ToolSwapperUpgradeWrapper(true, true) { + override val exposesAdvancedFilter = true + override val settingsLangKey = "gui.advanced_tool_swapper_settings".asTranslationKey() + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_TOOL_SWAPPER_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} + +enum class ToolSwapMode { + ANY, + ONLY_TOOLS, + NO_SWAP; + + fun next(): ToolSwapMode = + entries[(ordinal + 1) % entries.size] +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeFilterUtils.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeFilterUtils.kt new file mode 100644 index 0000000..56ea6db --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeFilterUtils.kt @@ -0,0 +1,13 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import net.minecraft.item.ItemStack + +object UpgradeFilterUtils { + fun IBasicFilterable.matchesAllowEmpty(stack: ItemStack): Boolean { + if (filterItems.inventory.all(ItemStack::isEmpty)) { + return filterType == IBasicFilterable.FilterType.BLACKLIST + } + + return checkFilter(stack) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeWrapper.kt index 57457a3..d392537 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeWrapper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/UpgradeWrapper.kt @@ -20,6 +20,8 @@ abstract class UpgradeWrapper : INBTSerializable, ISidelessCa abstract val settingsLangKey: String + open fun onBeforeRemoved() {} + override fun serializeNBT(): NBTTagCompound { val nbt = NBTTagCompound() nbt.setBoolean(TAB_STATE_TAG, isTabOpened) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/VoidUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/VoidUpgradeWrapper.kt new file mode 100644 index 0000000..1081ee6 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/VoidUpgradeWrapper.kt @@ -0,0 +1,131 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.UpgradeFilterUtils.matchesAllowEmpty +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.item.VoidUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability + +class VoidUpgradeWrapper : + BasicUpgradeWrapper(Config.voidUpgrade.filterSlots, Config.voidUpgrade.slotsInRow), + IVoidUpgrade { + companion object { + private const val VOID_TYPE_TAG = "VoidType" + private const val SHOULD_WORK_IN_GUI_TAG = "ShouldWorkInGui" + } + + override val settingsLangKey = "gui.void_settings".asTranslationKey() + var voidType = normalizeVoidType(VoidType.ALWAYS) + set(value) { + field = normalizeVoidType(value) + } + var shouldWorkInGui = false + + init { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + + override fun shouldVoid(stack: ItemStack): Boolean = + enabled && voidType == VoidType.ALWAYS && matchesAllowEmpty(stack) + + fun toggleWorkInGui() { + shouldWorkInGui = !shouldWorkInGui + } + + fun shouldVoidOverflow(stack: ItemStack, storageFull: Boolean, hasMatchingStack: Boolean): Boolean = + enabled && matchesAllowEmpty(stack) && when (voidType) { + VoidType.ALWAYS -> true + VoidType.SLOT_OVERFLOW -> hasMatchingStack + VoidType.STORAGE_OVERFLOW -> storageFull + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setByte(VOID_TYPE_TAG, voidType.ordinal.toByte()) + nbt.setBoolean(SHOULD_WORK_IN_GUI_TAG, shouldWorkInGui) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(VOID_TYPE_TAG)) + voidType = VoidType.entries.getOrElse(nbt.getByte(VOID_TYPE_TAG).toInt()) { voidType } + if (nbt.hasKey(SHOULD_WORK_IN_GUI_TAG)) + shouldWorkInGui = nbt.getBoolean(SHOULD_WORK_IN_GUI_TAG) + } + + private fun normalizeVoidType(type: VoidType): VoidType = + if (!Config.voidUpgrade.voidAlwaysEnabled && type == VoidType.ALWAYS) VoidType.SLOT_OVERFLOW else type + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.VOID_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} + +class AdvancedVoidUpgradeWrapper : + AdvancedUpgradeWrapper(Config.advancedVoidUpgrade.filterSlots, Config.advancedVoidUpgrade.slotsInRow), + IVoidUpgrade { + companion object { + private const val VOID_TYPE_TAG = "VoidType" + private const val SHOULD_WORK_IN_GUI_TAG = "ShouldWorkInGui" + } + + override val settingsLangKey = "gui.advanced_void_settings".asTranslationKey() + var voidType = normalizeVoidType(VoidType.ALWAYS) + set(value) { + field = normalizeVoidType(value) + } + var shouldWorkInGui = false + + init { + filterType = IBasicFilterable.FilterType.BLACKLIST + } + + override fun shouldVoid(stack: ItemStack): Boolean = + enabled && voidType == VoidType.ALWAYS && matchesAllowEmpty(stack) + + fun toggleWorkInGui() { + shouldWorkInGui = !shouldWorkInGui + } + + fun shouldVoidOverflow(stack: ItemStack, storageFull: Boolean, hasMatchingStack: Boolean): Boolean = + enabled && matchesAllowEmpty(stack) && when (voidType) { + VoidType.ALWAYS -> true + VoidType.SLOT_OVERFLOW -> hasMatchingStack + VoidType.STORAGE_OVERFLOW -> storageFull + } + + override fun serializeNBT(): NBTTagCompound { + val nbt = super.serializeNBT() + nbt.setByte(VOID_TYPE_TAG, voidType.ordinal.toByte()) + nbt.setBoolean(SHOULD_WORK_IN_GUI_TAG, shouldWorkInGui) + return nbt + } + + override fun deserializeNBT(nbt: NBTTagCompound) { + super.deserializeNBT(nbt) + if (nbt.hasKey(VOID_TYPE_TAG)) + voidType = VoidType.entries.getOrElse(nbt.getByte(VOID_TYPE_TAG).toInt()) { voidType } + if (nbt.hasKey(SHOULD_WORK_IN_GUI_TAG)) + shouldWorkInGui = nbt.getBoolean(SHOULD_WORK_IN_GUI_TAG) + } + + private fun normalizeVoidType(type: VoidType): VoidType = + if (!Config.advancedVoidUpgrade.voidAlwaysEnabled && type == VoidType.ALWAYS) VoidType.SLOT_OVERFLOW else type + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.ADVANCED_VOID_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) || + super.hasCapability(capability, facing) +} + +enum class VoidType { + ALWAYS, + SLOT_OVERFLOW, + STORAGE_OVERFLOW; +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/CapturedMob.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/CapturedMob.kt new file mode 100644 index 0000000..3c6e966 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/CapturedMob.kt @@ -0,0 +1,30 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher + +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.ResourceLocation +import java.util.UUID + +data class CapturedMob( + val id: UUID, + val entityType: ResourceLocation, + val entityNbt: NBTTagCompound, + val slot: Int, + val width: Int, + val height: Int, + val slotCost: Int, + val hostile: Boolean, + val displayName: String, + val currentHealth: Int, + val maxHealth: Int +) { + fun occupiesSlot(inventorySlot: Int, columns: Int): Boolean { + if (columns <= 0 || inventorySlot < 0) { + return false + } + val originX = slot % columns + val originY = slot / columns + val slotX = inventorySlot % columns + val slotY = inventorySlot / columns + return slotX >= originX && slotX < originX + width && slotY >= originY && slotY < originY + height + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/CapturedMobFootprint.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/CapturedMobFootprint.kt new file mode 100644 index 0000000..a47706c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/CapturedMobFootprint.kt @@ -0,0 +1,6 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher + +data class CapturedMobFootprint(val width: Int, val height: Int) { + val area: Int + get() = width * height +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherHandler.kt new file mode 100644 index 0000000..44aa863 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherHandler.kt @@ -0,0 +1,288 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackContainer +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.handler.CapabilityHandler +import com.cleanroommc.retrosophisticatedbackpacks.handler.NetworkHandler +import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem +import com.cleanroommc.retrosophisticatedbackpacks.network.S2CMobCatcherContentsPacket +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.block.Block +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityFlying +import net.minecraft.entity.EntityList +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.IEntityOwnable +import net.minecraft.entity.SharedMonsterAttributes +import net.minecraft.entity.boss.EntityDragon +import net.minecraft.entity.boss.EntityWither +import net.minecraft.entity.monster.IMob +import net.minecraft.entity.passive.EntityWaterMob +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.init.SoundEvents +import net.minecraft.inventory.IInventory +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumActionResult +import net.minecraft.util.EnumFacing +import net.minecraft.util.ResourceLocation +import net.minecraft.util.SoundCategory +import net.minecraft.util.math.AxisAlignedBB +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.RayTraceResult +import net.minecraft.util.math.Vec3d +import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.world.World +import java.util.UUID +import kotlin.math.ceil + +object MobCatcherHandler { + private const val RELEASE_REACH = 5.0 + + fun tryCapture(player: EntityPlayer, entity: EntityLivingBase): EnumActionResult { + val stack = player.heldItemMainhand + if (stack.item !is BackpackItem) { + return EnumActionResult.PASS + } + val backpackWrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return EnumActionResult.PASS + val upgradeWrapper = getBestUpgrade(backpackWrapper) ?: return EnumActionResult.PASS + if (player.world.isRemote) { + return EnumActionResult.SUCCESS + } + + val result = capture(player as EntityPlayerMP, backpackWrapper, entity, upgradeWrapper.isAdvanced) + player.sendStatusMessage(TextComponentTranslation(result.messageKey.asTranslationKey(), *result.args), true) + return if (result.success) EnumActionResult.SUCCESS else EnumActionResult.FAIL + } + + private fun capture( + player: EntityPlayerMP, + backpackWrapper: BackpackWrapper, + entity: EntityLivingBase, + advanced: Boolean + ): CaptureResult { + getEligibilityError(player, entity, advanced)?.let { return fail(player, it) } + + val hostile = isHostile(entity) + val slotCost = getSlotCost(entity, hostile) + val maxSlotCost = if (advanced) Config.mobCatcherUpgrade.advancedMaxSlotCost else Config.mobCatcherUpgrade.basicMaxSlotCost + if (slotCost > maxSlotCost) { + return fail(player, "gui.status.mob_catcher_too_large", slotCost, maxSlotCost) + } + + val footprint = MobCatcherStorage.getFootprint(entity, slotCost) + val slot = MobCatcherStorage.findEmptyRectangle(backpackWrapper, footprint) + ?: return fail(player, "gui.status.mob_catcher_no_space", footprint.width, footprint.height) + val entityType = EntityList.getKey(entity) + ?: return fail(player, "gui.status.mob_catcher_invalid_entity") + val entityTag = NBTTagCompound() + entity.writeToNBT(entityTag) + entityTag.setString("id", entityType.toString()) + entityTag.removeTag("UUIDMost") + entityTag.removeTag("UUIDLeast") + + val capturedMob = CapturedMob( + UUID.randomUUID(), + entityType, + entityTag, + slot, + footprint.width, + footprint.height, + slotCost, + hostile, + getCapturedMobDisplayName(entity), + ceil(entity.health.toDouble()).toInt(), + ceil(getEffectiveMaxHealth(entity)).toInt() + ) + MobCatcherStorage.addCapturedMob(backpackWrapper, capturedMob) + entity.setDead() + syncBackpack(player, backpackWrapper) + playMobCatcherSound(player, true, 0.7f) + return CaptureResult(true, "gui.status.mob_catcher_captured", arrayOf(capturedMob.displayName)) + } + + private fun getEligibilityError(player: EntityPlayerMP, entity: EntityLivingBase, advanced: Boolean): String? { + if (entity is EntityPlayer) { + return "gui.status.mob_catcher_players_blocked" + } + if (entity is EntityDragon || entity is EntityWither) { + return "gui.status.mob_catcher_boss_blocked" + } + if (entity.isRiding || entity.isBeingRidden) { + return "gui.status.mob_catcher_passengers_blocked" + } + val entityType = EntityList.getKey(entity) + if (entityType == null || entityType in configuredEntityTypes(Config.mobCatcherUpgrade.entityBlockList)) { + return "gui.status.mob_catcher_blocklisted" + } + if (entity is IEntityOwnable && entity.ownerId != null && entity.ownerId != player.uniqueID) { + return "gui.status.mob_catcher_not_owner" + } + if (Config.mobCatcherUpgrade.disallowInventoryEntities && entity is IInventory) { + return "gui.status.mob_catcher_inventory_blocked" + } + if (!advanced && isHostile(entity)) { + return "gui.status.mob_catcher_hostile_needs_advanced" + } + return null + } + + fun release(player: EntityPlayerMP, capturedMobId: UUID) { + val container = player.openContainer as? BackpackContainer ?: return + val backpackWrapper = container.backpackWrapper + val capturedMob = MobCatcherStorage.getCapturedMob(backpackWrapper, capturedMobId) ?: return + val entity = createEntity(player.world, capturedMob) as? EntityLivingBase ?: run { + player.sendStatusMessage(TextComponentTranslation("gui.status.mob_catcher_release_failed".asTranslationKey()), true) + return + } + val target = getReleasePosition(player, entity) ?: run { + player.sendStatusMessage(TextComponentTranslation("gui.status.mob_catcher_no_release_space".asTranslationKey()), true) + playMobCatcherSound(player, false, 0.8f) + return + } + entity.setLocationAndAngles(target.x, target.y, target.z, player.rotationYaw, 0f) + entity.setUniqueId(UUID.randomUUID()) + if (!player.world.spawnEntity(entity)) { + player.sendStatusMessage(TextComponentTranslation("gui.status.mob_catcher_release_failed".asTranslationKey()), true) + return + } + MobCatcherStorage.removeCapturedMob(backpackWrapper, capturedMobId) + syncBackpack(player, backpackWrapper) + player.sendStatusMessage(TextComponentTranslation("gui.status.mob_catcher_released".asTranslationKey(), capturedMob.displayName), true) + playMobCatcherSound(player, true, 1.2f) + } + + private fun createEntity(world: World, capturedMob: CapturedMob): Entity? { + val entity = EntityList.createEntityByIDFromName(capturedMob.entityType, world) ?: return null + entity.readFromNBT(capturedMob.entityNbt) + return entity + } + + private fun getReleasePosition(player: EntityPlayerMP, entity: EntityLivingBase): Vec3d? { + val eye = player.getPositionEyes(1f) + val look = player.lookVec + val hit = player.world.rayTraceBlocks(eye, eye.add(look.scale(RELEASE_REACH)), false, true, false) + if (hit != null && hit.typeOfHit == RayTraceResult.Type.BLOCK) { + getValidReleasePosition(player, entity, hit.blockPos.offset(hit.sideHit))?.let { return it } + getValidReleasePosition(player, entity, hit.blockPos.up())?.let { return it } + } + + var horizontal = Vec3d(look.x, 0.0, look.z) + if (horizontal.lengthSquared() < 1.0E-4) { + horizontal = Vec3d.fromPitchYaw(0f, player.rotationYaw) + } + val direction = horizontal.normalize() + val maxFallbackDistance = maxOf( + 2, + minOf(RELEASE_REACH.toInt(), ceil(entity.width / 2.0 + player.width / 2.0 + 1.0).toInt()) + ) + for (distance in 1..maxFallbackDistance) { + val candidate = player.positionVector.add(direction.scale(distance.toDouble())) + getValidReleasePosition(player, entity, BlockPos(candidate.x, player.posY, candidate.z))?.let { return it } + } + return null + } + + private fun getValidReleasePosition(player: EntityPlayerMP, entity: EntityLivingBase, spawnPos: BlockPos): Vec3d? { + val pos = getReleasePositionOnGround(player, entity, spawnPos) ?: return null + val bounds = AxisAlignedBB( + pos.x - entity.width / 2.0, + pos.y, + pos.z - entity.width / 2.0, + pos.x + entity.width / 2.0, + pos.y + entity.height, + pos.z + entity.width / 2.0 + ) + return if (player.world.getCollisionBoxes(entity, bounds).isEmpty() && + player.world.checkNoEntityCollision(bounds, entity) + ) pos else null + } + + private fun getReleasePositionOnGround(player: EntityPlayerMP, entity: EntityLivingBase, spawnPos: BlockPos): Vec3d? { + if (canReleaseWithoutGround(entity)) { + return Vec3d(spawnPos.x + 0.5, spawnPos.y.toDouble(), spawnPos.z + 0.5) + } + val groundPos = spawnPos.down() + val bounds = player.world.getBlockState(groundPos).getCollisionBoundingBox(player.world, groundPos) + if (bounds == null || bounds == Block.NULL_AABB) { + return null + } + return Vec3d(spawnPos.x + 0.5, groundPos.y + bounds.maxY, spawnPos.z + 0.5) + } + + private fun canReleaseWithoutGround(entity: EntityLivingBase): Boolean = + entity is EntityFlying || entity is EntityWaterMob || entity is net.minecraft.entity.passive.EntityFlying || entity.hasNoGravity() + + fun getBestUpgrade(backpackWrapper: BackpackWrapper): MobCatcherUpgradeWrapper? { + val upgrades = backpackWrapper.gatherCapabilityUpgrades(Capabilities.MOB_CATCHER_UPGRADE_CAPABILITY) + return upgrades.firstOrNull { it.isAdvanced } ?: upgrades.firstOrNull() + } + + fun isHostile(entity: EntityLivingBase): Boolean { + val entityType = EntityList.getKey(entity) ?: return entity is IMob + if (entityType in configuredEntityTypes(Config.mobCatcherUpgrade.passiveOverrides)) { + return false + } + return entityType in configuredEntityTypes(Config.mobCatcherUpgrade.hostileOverrides) || entity is IMob + } + + fun getSlotCost(entity: EntityLivingBase, hostile: Boolean): Int { + val maxHealth = getEffectiveMaxHealth(entity) + val currentHealth = maxOf(0.0, entity.health.toDouble()) + val baseCost = maxHealth / 2.0 + minOf(currentHealth, maxHealth) / 2.0 + val multiplier = if (hostile) Config.mobCatcherUpgrade.hostileMultiplier else Config.mobCatcherUpgrade.animalMultiplier + return maxOf(1, ceil(baseCost * multiplier).toInt()) + } + + fun getEffectiveMaxHealth(entity: EntityLivingBase): Double = + maxOf( + 1.0, + entity.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).attributeValue, + entity.maxHealth.toDouble(), + entity.health.toDouble() + ) + + private fun getCapturedMobDisplayName(entity: EntityLivingBase): String = + if (entity.hasCustomName()) entity.customNameTag else entity.displayName.unformattedText + + private fun configuredEntityTypes(configuredEntityTypes: Array): Set = + configuredEntityTypes.mapNotNull { + try { + ResourceLocation(it) + } catch (_: RuntimeException) { + null + } + }.toSet() + + private fun fail(player: EntityPlayerMP, messageKey: String, vararg args: Any): CaptureResult { + playMobCatcherSound(player, false, 0.8f) + return CaptureResult(false, messageKey, args) + } + + private fun playMobCatcherSound(player: EntityPlayerMP, success: Boolean, basePitch: Float) { + val sound = if (success) SoundEvents.ENTITY_ITEM_PICKUP else SoundEvents.BLOCK_NOTE_BASS + val pitch = basePitch + (player.rng.nextFloat() - 0.5f) * 0.16f + player.world.playSound(null, player.position, sound, SoundCategory.PLAYERS, 0.7f, pitch) + } + + private fun syncBackpack(player: EntityPlayerMP, backpackWrapper: BackpackWrapper) { + player.openContainer.detectAndSendChanges() + player.inventoryContainer.detectAndSendChanges() + CapabilityHandler.updateBackpackInventory(backpackWrapper) + syncCapturedMobsToViewers(player, backpackWrapper) + } + + private fun syncCapturedMobsToViewers(player: EntityPlayerMP, backpackWrapper: BackpackWrapper) { + player.server.playerList.players + .filter { + it == player || (it.openContainer as? BackpackContainer)?.backpackWrapper?.uuid == backpackWrapper.uuid + } + .forEach { + NetworkHandler.INSTANCE.sendTo(S2CMobCatcherContentsPacket(backpackWrapper), it) + } + } + + private data class CaptureResult(val success: Boolean, val messageKey: String, val args: Array = emptyArray()) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherStorage.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherStorage.kt new file mode 100644 index 0000000..dd31813 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherStorage.kt @@ -0,0 +1,553 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher + +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.EntityList +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagList +import net.minecraft.util.ResourceLocation +import net.minecraftforge.items.ItemHandlerHelper +import kotlin.math.abs +import kotlin.math.ceil +import kotlin.math.ln + +object MobCatcherStorage { + const val CAPTURED_MOBS_TAG = "capturedMobs" + const val CAPTURED_MOBS_COLUMNS_TAG = "capturedMobsColumns" + + private const val ID_TAG = "id" + private const val ENTITY_TYPE_TAG = "entityType" + private const val ENTITY_NBT_TAG = "entityNbt" + private const val SLOT_TAG = "slot" + private const val WIDTH_TAG = "width" + private const val HEIGHT_TAG = "height" + private const val SLOT_COST_TAG = "slotCost" + private const val HOSTILE_TAG = "hostile" + private const val DISPLAY_NAME_TAG = "displayName" + private const val CURRENT_HEALTH_TAG = "currentHealth" + private const val MAX_HEALTH_TAG = "maxHealth" + private const val TAG_COMPOUND = 10 + private const val FOOTPRINT_ASPECT_WIDENING = 1.4 + private const val FOOTPRINT_ASPECT_WEIGHT = 10.0 + private const val FOOTPRINT_OVERFILL_WEIGHT = 0.75 + private const val FOOTPRINT_SCORE_EPSILON = 0.001 + private const val MOB_PART_PREFIX = "mob:" + private const val STACK_PART_PREFIX = "stack:" + private const val FIXED_PART_PREFIX = "fixed:" + + fun getCapturedMobs(backpackWrapper: BackpackWrapper): List = + backpackWrapper.capturedMobs + + fun setCapturedMobs(backpackWrapper: BackpackWrapper, capturedMobs: List) { + backpackWrapper.capturedMobs.clear() + backpackWrapper.capturedMobs.addAll(capturedMobs) + backpackWrapper.capturedMobsColumns = getColumns(backpackWrapper) + } + + fun addCapturedMob(backpackWrapper: BackpackWrapper, capturedMob: CapturedMob) { + backpackWrapper.capturedMobs.add(capturedMob) + backpackWrapper.capturedMobsColumns = getColumns(backpackWrapper) + } + + fun removeCapturedMob(backpackWrapper: BackpackWrapper, capturedMobId: java.util.UUID): Boolean { + val removed = backpackWrapper.capturedMobs.removeIf { it.id == capturedMobId } + if (removed && backpackWrapper.capturedMobs.isEmpty()) { + backpackWrapper.capturedMobsColumns = getColumns(backpackWrapper) + } + return removed + } + + fun getCapturedMob(backpackWrapper: BackpackWrapper, capturedMobId: java.util.UUID): CapturedMob? = + backpackWrapper.capturedMobs.firstOrNull { it.id == capturedMobId } + + fun getCapturedMobsTag(backpackWrapper: BackpackWrapper): NBTTagCompound { + backpackWrapper.ensureCapturedMobLayoutCurrent() + return NBTTagCompound().also { + it.setTag(CAPTURED_MOBS_TAG, serialize(getCapturedMobs(backpackWrapper))) + it.setInteger(CAPTURED_MOBS_COLUMNS_TAG, backpackWrapper.capturedMobsColumns) + } + } + + fun applyCapturedMobsTag(backpackWrapper: BackpackWrapper, nbt: NBTTagCompound) { + backpackWrapper.capturedMobs.clear() + backpackWrapper.capturedMobs.addAll(deserializeCapturedMobsTag(nbt)) + backpackWrapper.capturedMobsColumns = if (nbt.hasKey(CAPTURED_MOBS_COLUMNS_TAG)) { + nbt.getInteger(CAPTURED_MOBS_COLUMNS_TAG) + } else { + getColumns(backpackWrapper) + } + backpackWrapper.ensureCapturedMobLayoutCurrent() + } + + fun isSlotBlocked(backpackWrapper: BackpackWrapper, slot: Int): Boolean { + val columns = getColumns(backpackWrapper) + return getCapturedMobs(backpackWrapper).any { it.occupiesSlot(slot, columns) } + } + + fun canFitBasicTier(backpackWrapper: BackpackWrapper, maxSlotCost: Int): Boolean = + getCapturedMobs(backpackWrapper).all { !it.hostile && it.slotCost <= maxSlotCost } + + fun canFitWithAdditionalInventoryControls(backpackWrapper: BackpackWrapper, additionalControls: Int): Boolean { + if (additionalControls <= 0) { + return true + } + backpackWrapper.ensureCapturedMobLayoutCurrent() + val currentColumns = getColumns(backpackWrapper) + return canFitLayout(backpackWrapper, (currentColumns - additionalControls * 2).coerceAtLeast(1), currentColumns) + } + + fun findEmptyRectangle(backpackWrapper: BackpackWrapper, footprint: CapturedMobFootprint): Int? { + backpackWrapper.ensureCapturedMobLayoutCurrent() + val columns = getColumns(backpackWrapper) + val rows = ceil(backpackWrapper.backpackInventorySize().toDouble() / columns).toInt() + val capturedMobs = getCapturedMobs(backpackWrapper) + return findEmptyRectangle(backpackWrapper, footprint, columns, rows, capturedMobs) + } + + fun ensureLayoutCurrent(backpackWrapper: BackpackWrapper) { + val currentColumns = getColumns(backpackWrapper) + val previousColumns = backpackWrapper.capturedMobsColumns.takeIf { it > 0 } ?: currentColumns + val capturedMobs = getCapturedMobs(backpackWrapper) + if (capturedMobs.isEmpty()) { + backpackWrapper.capturedMobsColumns = currentColumns + return + } + if (previousColumns == currentColumns) { + return + } + + val compact = currentColumns > previousColumns + val fitResult = fitInventoryLayout( + getInventoryLayoutParts(backpackWrapper, capturedMobs, previousColumns, currentColumns), + backpackWrapper.backpackInventorySize(), + currentColumns, + compact + ) + if (!fitResult.fits) { + return + } + + applyInventoryLayout(backpackWrapper, fitResult) + backpackWrapper.capturedMobsColumns = currentColumns + } + + private fun canFitLayout(backpackWrapper: BackpackWrapper, targetColumns: Int, previousColumns: Int): Boolean { + if (targetColumns == previousColumns) { + return true + } + val capturedMobs = getCapturedMobs(backpackWrapper) + if (capturedMobs.isEmpty()) { + return true + } + return fitInventoryLayout( + getInventoryLayoutParts(backpackWrapper, capturedMobs, previousColumns, targetColumns), + backpackWrapper.backpackInventorySize(), + targetColumns, + compact = targetColumns > previousColumns + ).fits + } + + private fun getInventoryLayoutParts( + backpackWrapper: BackpackWrapper, + capturedMobs: List, + previousColumns: Int, + currentColumns: Int + ): List { + val parts = mutableListOf() + for (slot in 0 until backpackWrapper.backpackInventorySize()) { + val mobAtOrigin = capturedMobs.firstOrNull { it.slot == slot } + if (mobAtOrigin != null) { + parts += LayoutPart( + MOB_PART_PREFIX + mobAtOrigin.id, + getTargetSlot(mobAtOrigin.slot, previousColumns, currentColumns), + mobAtOrigin.width, + mobAtOrigin.height, + getOccupiedSlots(mobAtOrigin, previousColumns, backpackWrapper.backpackInventorySize()) + ) + continue + } + if (capturedMobs.any { it.occupiesSlot(slot, previousColumns) }) { + continue + } + + val stack = backpackWrapper.backpackItemStackHandler.getStackInSlot(slot) + if (stack.isEmpty) { + continue + } + val fixed = backpackWrapper.isSlotLocked(slot) || backpackWrapper.isSlotMemorized(slot) + parts += LayoutPart( + (if (fixed) FIXED_PART_PREFIX else STACK_PART_PREFIX) + slot, + slot, + 1, + 1, + setOf(slot) + ) + } + return parts + } + + private fun applyInventoryLayout(backpackWrapper: BackpackWrapper, fitResult: LayoutFitResult) { + val stackMoves = fitResult.fittedSlots.mapNotNull { (partId, targetSlot) -> + if (!partId.startsWith(STACK_PART_PREFIX)) { + return@mapNotNull null + } + val sourceSlot = partId.removePrefix(STACK_PART_PREFIX).toIntOrNull() ?: return@mapNotNull null + if (sourceSlot == targetSlot || + sourceSlot !in 0 until backpackWrapper.backpackInventorySize() || + targetSlot !in 0 until backpackWrapper.backpackInventorySize() + ) { + return@mapNotNull null + } + val stack = backpackWrapper.backpackItemStackHandler.getStackInSlot(sourceSlot) + if (stack.isEmpty) null else StackMove(sourceSlot, targetSlot, stack.copy()) + } + + if (stackMoves.isNotEmpty()) { + val sourceSlots = stackMoves.mapTo(mutableSetOf()) { it.sourceSlot } + val targetSlots = stackMoves.mapTo(mutableSetOf()) { it.targetSlot } + if (targetSlots.any { !backpackWrapper.backpackItemStackHandler.getStackInSlot(it).isEmpty && it !in sourceSlots }) { + return + } + stackMoves.forEach { backpackWrapper.backpackItemStackHandler.setStackInSlot(it.sourceSlot, net.minecraft.item.ItemStack.EMPTY) } + stackMoves.forEach { + backpackWrapper.backpackItemStackHandler.setStackInSlot( + it.targetSlot, + ItemHandlerHelper.copyStackWithSize(it.stack, it.stack.count) + ) + } + } + + val fittedMobs = getCapturedMobs(backpackWrapper).map { capturedMob -> + fitResult.fittedSlots[MOB_PART_PREFIX + capturedMob.id]?.let { capturedMob.copy(slot = it) } ?: capturedMob + } + backpackWrapper.capturedMobs.clear() + backpackWrapper.capturedMobs.addAll(fittedMobs) + } + + private fun findEmptyRectangle( + backpackWrapper: BackpackWrapper, + footprint: CapturedMobFootprint, + columns: Int, + rows: Int, + capturedMobs: List + ): Int? { + for (y in 0..rows - footprint.height) { + for (x in 0..columns - footprint.width) { + val slot = y * columns + x + if (isRectangleEmpty(backpackWrapper, slot, footprint, columns, capturedMobs)) { + return slot + } + } + } + return null + } + + private fun isRectangleEmpty( + backpackWrapper: BackpackWrapper, + slot: Int, + footprint: CapturedMobFootprint, + columns: Int, + capturedMobs: List + ): Boolean { + for (y in 0 until footprint.height) { + for (x in 0 until footprint.width) { + val checkedSlot = slot + y * columns + x + if (checkedSlot !in 0 until backpackWrapper.backpackInventorySize()) { + return false + } + if (!backpackWrapper.getStackInSlot(checkedSlot).isEmpty) { + return false + } + if (capturedMobs.any { it.occupiesSlot(checkedSlot, columns) }) { + return false + } + } + } + return true + } + + fun getFootprint(entity: EntityLivingBase, slotCost: Int): CapturedMobFootprint { + val entityWidth = maxOf(entity.width.toDouble(), 0.25) + val entityHeight = maxOf(entity.height.toDouble(), 0.25) + val targetAspect = entityWidth / entityHeight * FOOTPRINT_ASPECT_WIDENING + val maxSlotCost = maxOf(1, slotCost) + var best = CapturedMobFootprint(1, maxSlotCost) + var bestScore = Double.MAX_VALUE + var bestAspectError = Double.MAX_VALUE + var bestOverfill = Int.MAX_VALUE + + for (width in 1..maxSlotCost) { + for (height in 1..maxSlotCost) { + val area = width * height + if (area < slotCost) { + continue + } + val aspect = width.toDouble() / height + val aspectError = abs(ln(aspect / targetAspect)) + val overfill = area - slotCost + val score = aspectError * FOOTPRINT_ASPECT_WEIGHT + overfill * FOOTPRINT_OVERFILL_WEIGHT + if (isBetterFootprint( + score, + aspectError, + overfill, + width, + height, + bestScore, + bestAspectError, + bestOverfill, + best + ) + ) { + best = CapturedMobFootprint(width, height) + bestScore = score + bestAspectError = aspectError + bestOverfill = overfill + } + } + } + return best + } + + private fun isBetterFootprint( + score: Double, + aspectError: Double, + overfill: Int, + width: Int, + height: Int, + bestScore: Double, + bestAspectError: Double, + bestOverfill: Int, + best: CapturedMobFootprint + ): Boolean { + if (score < bestScore - FOOTPRINT_SCORE_EPSILON) return true + if (score > bestScore + FOOTPRINT_SCORE_EPSILON) return false + if (aspectError < bestAspectError - FOOTPRINT_SCORE_EPSILON) return true + if (aspectError > bestAspectError + FOOTPRINT_SCORE_EPSILON) return false + if (width != best.width) return width > best.width + if (height != best.height) return height < best.height + return overfill < bestOverfill + } + + fun getColumns(backpackWrapper: BackpackWrapper): Int { + val backgroundColumns = if (backpackWrapper.backpackInventorySize() > 81) 12 else 9 + val columnsTaken = (backpackWrapper.tankUpgradeSlots().take(2).size + backpackWrapper.batteryUpgradeSlots().take(1).size) * 2 + return (backgroundColumns - columnsTaken).coerceAtLeast(1) + } + + private fun getTargetSlot(slot: Int, columns: Int, targetColumns: Int): Int = + slot / columns * targetColumns + slot % columns + + private fun getOccupiedSlots(capturedMob: CapturedMob, columns: Int, inventorySlots: Int): Set { + val occupiedSlots = mutableSetOf() + for (y in 0 until capturedMob.height) { + for (x in 0 until capturedMob.width) { + val slot = capturedMob.slot + y * columns + x + if (slot < inventorySlots) { + occupiedSlots += slot + } + } + } + return occupiedSlots + } + + private fun fitInventoryLayout( + parts: List, + targetSlots: Int, + targetColumns: Int, + compact: Boolean + ): LayoutFitResult { + val result = fitInventoryLayoutInternal(parts, targetSlots, targetColumns, compact, compact) + if (compact || result.fits) { + return result + } + + val compactResult = fitInventoryLayoutInternal(parts, targetSlots, targetColumns, compact = true, preserveStacks = false) + if (compactResult.fits) { + return compactResult + } + + val reorderedCompactResult = fitInventoryLayoutInternal( + orderPartsForCompaction(parts), + targetSlots, + targetColumns, + compact = true, + preserveStacks = false, + fillGapsWithStacks = true + ) + return if (reorderedCompactResult.fits) reorderedCompactResult else compactResult + } + + private fun orderPartsForCompaction(parts: List): List = + parts.sortedWith( + compareBy { compactionPriority(it) } + .thenByDescending { it.width * it.height } + .thenBy { it.firstSlot } + ) + + private fun compactionPriority(part: LayoutPart): Int = + when { + part.id.startsWith(FIXED_PART_PREFIX) -> 0 + part.id.startsWith(STACK_PART_PREFIX) -> 2 + else -> 1 + } + + private fun fitInventoryLayoutInternal( + parts: List, + targetSlots: Int, + targetColumns: Int, + compact: Boolean, + preserveStacks: Boolean, + fillGapsWithStacks: Boolean = false + ): LayoutFitResult { + val occupiedSlots = mutableSetOf() + val fittedSlots = mutableMapOf() + var nextSlot = 0 + + for ((partIndex, part) in parts.withIndex()) { + val fittedSlot = findNextFit( + part, + if (fillGapsWithStacks && part.id.startsWith(STACK_PART_PREFIX)) 0 else nextSlot, + targetSlots, + targetColumns, + occupiedSlots, + compact, + preserveStacks + ) + if (fittedSlot < 0) { + return LayoutFitResult( + false, + fittedSlots, + parts.drop(partIndex).flatMapTo(mutableSetOf()) { it.sourceSlots } + ) + } + + occupy(part, fittedSlot, targetColumns, occupiedSlots) + fittedSlots[part.id] = fittedSlot + nextSlot = if (!compact && fittedSlot == part.firstSlot) { + maxOf(nextSlot, getSlotAfterPart(part, fittedSlot, targetColumns)) + } else { + fittedSlot + part.width + } + } + + return LayoutFitResult(true, fittedSlots) + } + + private fun findNextFit( + part: LayoutPart, + nextSlot: Int, + targetSlots: Int, + targetColumns: Int, + occupiedSlots: Set, + compact: Boolean, + preserveStacks: Boolean + ): Int { + if (part.id.startsWith(FIXED_PART_PREFIX)) { + return if (fits(part, part.firstSlot, targetSlots, targetColumns, occupiedSlots)) part.firstSlot else -1 + } + if (shouldPreserveFirstSlot(part, compact, preserveStacks) && + part.firstSlot < targetSlots && + fits(part, part.firstSlot, targetSlots, targetColumns, occupiedSlots) + ) { + return part.firstSlot + } + + val startSlot = if (compact || part.firstSlot >= targetSlots) nextSlot else maxOf(nextSlot, part.firstSlot) + for (slot in startSlot until targetSlots) { + if (fits(part, slot, targetSlots, targetColumns, occupiedSlots)) { + return slot + } + } + return -1 + } + + private fun shouldPreserveFirstSlot(part: LayoutPart, compact: Boolean, preserveStacks: Boolean): Boolean = + !compact || preserveStacks && part.id.startsWith(STACK_PART_PREFIX) + + private fun fits(part: LayoutPart, slot: Int, targetSlots: Int, targetColumns: Int, occupiedSlots: Set): Boolean { + val x = slot % targetColumns + if (x + part.width > targetColumns) { + return false + } + for (y in 0 until part.height) { + for (partX in 0 until part.width) { + val checkedSlot = slot + y * targetColumns + partX + if (checkedSlot >= targetSlots || checkedSlot in occupiedSlots) { + return false + } + } + } + return true + } + + private fun occupy(part: LayoutPart, slot: Int, targetColumns: Int, occupiedSlots: MutableSet) { + for (y in 0 until part.height) { + for (x in 0 until part.width) { + occupiedSlots += slot + y * targetColumns + x + } + } + } + + private fun getSlotAfterPart(part: LayoutPart, slot: Int, targetColumns: Int): Int = + slot + (part.height - 1) * targetColumns + part.width + + fun serialize(capturedMobs: List): NBTTagList = + NBTTagList().also { list -> capturedMobs.sortedBy { it.slot }.forEach { list.appendTag(serialize(it)) } } + + fun deserialize(list: NBTTagList): List = + (0 until list.tagCount()).mapNotNull { deserialize(list.getCompoundTagAt(it)) } + + private fun serialize(capturedMob: CapturedMob): NBTTagCompound = + NBTTagCompound().also { tag -> + tag.setUniqueId(ID_TAG, capturedMob.id) + tag.setString(ENTITY_TYPE_TAG, capturedMob.entityType.toString()) + tag.setTag(ENTITY_NBT_TAG, capturedMob.entityNbt.copy()) + tag.setInteger(SLOT_TAG, capturedMob.slot) + tag.setInteger(WIDTH_TAG, capturedMob.width) + tag.setInteger(HEIGHT_TAG, capturedMob.height) + tag.setInteger(SLOT_COST_TAG, capturedMob.slotCost) + tag.setBoolean(HOSTILE_TAG, capturedMob.hostile) + tag.setString(DISPLAY_NAME_TAG, capturedMob.displayName) + tag.setInteger(CURRENT_HEALTH_TAG, capturedMob.currentHealth) + tag.setInteger(MAX_HEALTH_TAG, capturedMob.maxHealth) + } + + private fun deserialize(tag: NBTTagCompound): CapturedMob? { + val entityType = ResourceLocation(tag.getString(ENTITY_TYPE_TAG)) + if (!EntityList.isRegistered(entityType)) { + return null + } + return CapturedMob( + tag.getUniqueId(ID_TAG) ?: return null, + entityType, + tag.getCompoundTag(ENTITY_NBT_TAG), + tag.getInteger(SLOT_TAG), + tag.getInteger(WIDTH_TAG).coerceAtLeast(1), + tag.getInteger(HEIGHT_TAG).coerceAtLeast(1), + tag.getInteger(SLOT_COST_TAG).coerceAtLeast(1), + tag.getBoolean(HOSTILE_TAG), + tag.getString(DISPLAY_NAME_TAG), + tag.getInteger(CURRENT_HEALTH_TAG), + tag.getInteger(MAX_HEALTH_TAG).coerceAtLeast(1) + ) + } + + fun deserializeCapturedMobsTag(nbt: NBTTagCompound): List = + deserialize(nbt.getTagList(CAPTURED_MOBS_TAG, TAG_COMPOUND)) + + private data class LayoutPart( + val id: String, + val firstSlot: Int, + val width: Int, + val height: Int, + val sourceSlots: Set + ) + + private data class LayoutFitResult( + val fits: Boolean, + val fittedSlots: Map, + val errorSlots: Set = emptySet() + ) + + private data class StackMove(val sourceSlot: Int, val targetSlot: Int, val stack: net.minecraft.item.ItemStack) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherUpgradeWrapper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherUpgradeWrapper.kt new file mode 100644 index 0000000..c6d637c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/capability/upgrade/mobcatcher/MobCatcherUpgradeWrapper.kt @@ -0,0 +1,19 @@ +package com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.UpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.item.MobCatcherUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.capabilities.Capability + +class MobCatcherUpgradeWrapper(private val advanced: Boolean = false) : UpgradeWrapper() { + override val settingsLangKey: String = "" + + val isAdvanced: Boolean + get() = advanced + + override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = + capability == Capabilities.MOB_CATCHER_UPGRADE_CAPABILITY || + super.hasCapability(capability, facing) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackBlockEntityRenderer.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackBlockEntityRenderer.kt new file mode 100644 index 0000000..73ba51c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackBlockEntityRenderer.kt @@ -0,0 +1,46 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client + +import com.cleanroommc.retrosophisticatedbackpacks.block.BackpackBlock +import com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.util.EnumFacing +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly + +@SideOnly(Side.CLIENT) +class BackpackBlockEntityRenderer : TileEntitySpecialRenderer() { + override fun render( + te: BackpackTileEntity, + x: Double, + y: Double, + z: Double, + partialTicks: Float, + destroyStage: Int, + alpha: Float + ) { + if (te.wrapper.getDisplayItem() == null) { + return + } + + val state = te.world.getBlockState(te.pos) + if (state.block !is BackpackBlock) { + return + } + + GlStateManager.pushMatrix() + GlStateManager.translate(x + 0.5, y, z + 0.5) + GlStateManager.rotate(facingRotation(state.getValue(BackpackBlock.FACING)), 0f, 1f, 0f) + BackpackDisplayItemRenderer.render(te.wrapper) + GlStateManager.popMatrix() + } + + private fun facingRotation(facing: EnumFacing): Float = + when (facing) { + EnumFacing.NORTH -> 180f + EnumFacing.SOUTH -> 0f + EnumFacing.WEST -> 90f + EnumFacing.EAST -> -90f + else -> 0f + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDisplayItemRenderer.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDisplayItemRenderer.kt new file mode 100644 index 0000000..fa91367 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDisplayItemRenderer.kt @@ -0,0 +1,77 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client + +import com.cleanroommc.retrosophisticatedbackpacks.backpack.DisplaySide +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.RenderHelper +import net.minecraft.client.renderer.block.model.ItemCameraTransforms +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly +import org.lwjgl.opengl.GL11 + +@SideOnly(Side.CLIENT) +object BackpackDisplayItemRenderer { + private const val MODEL_CENTER = 0.5 + private const val CENTER_Y = 0.6 + private const val FRONT_Z = -0.314 + private const val SIDE_X = 0.455 + private const val SCALE = 0.5f + + fun render( + wrapper: BackpackWrapper, + modelScale: Double = 1.0, + itemScale: Float = 1.0f, + originAtCenter: Boolean = true + ) { + val displayItem = wrapper.getDisplayItem() ?: return + + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) + GlStateManager.pushMatrix() + try { + applySideTransform(displayItem.side, modelScale, originAtCenter) + GlStateManager.rotate(displayItem.rotation.toFloat(), 0f, 0f, 1f) + val scale = SCALE * itemScale + GlStateManager.scale(scale, scale, scale) + GlStateManager.color(1f, 1f, 1f, 1f) + GlStateManager.enableRescaleNormal() + GlStateManager.enableDepth() + GlStateManager.enableBlend() + GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA) + RenderHelper.enableStandardItemLighting() + Minecraft.getMinecraft().renderItem.renderItem(displayItem.stack, ItemCameraTransforms.TransformType.FIXED) + } finally { + RenderHelper.disableStandardItemLighting() + GlStateManager.disableRescaleNormal() + GlStateManager.color(1f, 1f, 1f, 1f) + GlStateManager.popMatrix() + GL11.glPopAttrib() + RenderStateHelper.syncGlStateManagerCache() + } + } + + private fun applySideTransform(side: DisplaySide, modelScale: Double, originAtCenter: Boolean) { + val invScale = 1.0 / modelScale + fun x(relative: Double) = + if (originAtCenter) relative * invScale else MODEL_CENTER + relative * invScale + + fun z(relative: Double) = + if (originAtCenter) relative * invScale else MODEL_CENTER + relative * invScale + + when (side) { + DisplaySide.FRONT -> { + GlStateManager.translate(x(0.0), CENTER_Y * invScale, z(FRONT_Z)) + } + + DisplaySide.LEFT -> { + GlStateManager.translate(x(SIDE_X), CENTER_Y * invScale, z(0.0)) + GlStateManager.rotate(-90f, 0f, 1f, 0f) + } + + DisplaySide.RIGHT -> { + GlStateManager.translate(x(-SIDE_X), CENTER_Y * invScale, z(0.0)) + GlStateManager.rotate(90f, 0f, 1f, 0f) + } + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDynamicModel.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDynamicModel.kt index 98530fa..4581e59 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDynamicModel.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackDynamicModel.kt @@ -4,6 +4,7 @@ import com.cleanroommc.retrosophisticatedbackpacks.Tags import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackTier import com.cleanroommc.retrosophisticatedbackpacks.block.BackpackBlock import com.cleanroommc.retrosophisticatedbackpacks.block.Blocks +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities import com.google.common.collect.ImmutableMap import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.block.model.* @@ -32,7 +33,7 @@ class BackpackDynamicModel private constructor( ) : IModel { companion object { private val BACKPACK_MODELS_RESOURCE_LOCATION = ModelPart.entries.filter { - it == ModelPart.BASE || it.name.endsWith("POUCH") + it == ModelPart.BASE || it.name.endsWith("POUCH") || it.name.endsWith("TANK") }.associateBy({ it }, { val path = "block/backpack_${it.name.lowercase(Locale.ENGLISH)}" @@ -41,6 +42,7 @@ class BackpackDynamicModel private constructor( private val BACKPACK_CLOTH_RESOURCE_LOCATION = ResourceLocation(Tags.MOD_ID, "block/backpack_cloth") private val BACKPACK_BORDER_RESOURCE_LOCATION = ResourceLocation(Tags.MOD_ID, "block/backpack_border") + private val BACKPACK_MODULES_RESOURCE_LOCATION = ResourceLocation(Tags.MOD_ID, "block/backpack_modules") private val BACKPACK_CLIP_RESOURCE_LOCATIONS = BackpackTier.entries.associateBy( { it }, { ResourceLocation(Tags.MOD_ID, "block/${it.registryName}_clips") } @@ -48,6 +50,7 @@ class BackpackDynamicModel private constructor( private val BACKPACK_TEXTURE_RESOURCE_LOCATIONS = listOf( BACKPACK_CLOTH_RESOURCE_LOCATION, BACKPACK_BORDER_RESOURCE_LOCATION, + BACKPACK_MODULES_RESOURCE_LOCATION, *BACKPACK_CLIP_RESOURCE_LOCATIONS.values.toTypedArray() ) } @@ -125,6 +128,12 @@ class BackpackDynamicModel private constructor( ret.addAll(models[ModelPart.FRONT_POUCH]!!.getQuads(state, side, rand)) ret.addAll(models[ModelPart.LEFT_POUCH]!!.getQuads(state, side, rand)) ret.addAll(models[ModelPart.RIGHT_POUCH]!!.getQuads(state, side, rand)) + if (state?.getValue(BackpackBlock.LEFT_TANK) ?: tankLeft) { + ret.addAll(models[ModelPart.LEFT_TANK]!!.getQuads(state, side, rand)) + } + if (state?.getValue(BackpackBlock.RIGHT_TANK) ?: tankRight) { + ret.addAll(models[ModelPart.RIGHT_TANK]!!.getQuads(state, side, rand)) + } if (state != null) { val facing = state.getValue(BackpackBlock.FACING) @@ -142,7 +151,7 @@ class BackpackDynamicModel private constructor( true override fun isBuiltInRenderer(): Boolean = - false + true override fun getParticleTexture(): TextureAtlasSprite = models[ModelPart.BASE]!!.particleTexture @@ -164,6 +173,11 @@ class BackpackDynamicModel private constructor( backpackModel.tankLeft = false backpackModel.tankRight = false backpackModel.battery = false + stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null)?.let { + val (left, right) = it.tankRenderSides() + backpackModel.tankLeft = left + backpackModel.tankRight = right + } return backpackModel } @@ -196,4 +210,4 @@ class BackpackDynamicModel private constructor( RIGHT_POUCH, RIGHT_TANK, } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackItemStackRenderer.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackItemStackRenderer.kt index bd9dac4..b1fa3fe 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackItemStackRenderer.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/BackpackItemStackRenderer.kt @@ -1,5 +1,6 @@ package com.cleanroommc.retrosophisticatedbackpacks.client +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities import net.minecraft.client.Minecraft import net.minecraft.client.renderer.tileentity.TileEntityItemStackRenderer import net.minecraft.item.ItemStack @@ -11,9 +12,12 @@ class BackpackItemStackRenderer : TileEntityItemStackRenderer() { private val mc = Minecraft.getMinecraft() override fun renderByItem(itemStackIn: ItemStack, partialTicks: Float) { - mc.itemRenderer val model = mc.renderItem.getItemModelWithOverrides(itemStackIn, null, null) - mc.renderItem.renderItem(itemStackIn, model) + mc.renderItem.renderModel(model, itemStackIn) + + itemStackIn.getCapability(Capabilities.BACKPACK_CAPABILITY, null)?.let { + BackpackDisplayItemRenderer.render(it, itemScale = 0.7f, originAtCenter = false) + } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackPanel.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackPanel.kt index 2ae4a7a..e70e3ca 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackPanel.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackPanel.kt @@ -1,9 +1,9 @@ package com.cleanroommc.retrosophisticatedbackpacks.client.gui -import com.cleanroommc.modularui.api.IPanelHandler +import com.cleanroommc.modularui.api.drawable.IDrawable import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.layout.IViewportStack import com.cleanroommc.modularui.api.widget.Interactable -import com.cleanroommc.modularui.drawable.AdaptableUITexture import com.cleanroommc.modularui.drawable.ItemDrawable import com.cleanroommc.modularui.drawable.UITexture import com.cleanroommc.modularui.drawable.text.StringKey @@ -11,20 +11,20 @@ import com.cleanroommc.modularui.screen.ModularPanel import com.cleanroommc.modularui.screen.RichTooltip import com.cleanroommc.modularui.screen.viewport.ModularGuiContext import com.cleanroommc.modularui.theme.WidgetTheme +import com.cleanroommc.modularui.theme.WidgetThemeEntry import com.cleanroommc.modularui.value.sync.PanelSyncManager -import com.cleanroommc.modularui.widgets.ButtonWidget +import com.cleanroommc.modularui.widget.Widget import com.cleanroommc.modularui.widgets.SlotGroupWidget import com.cleanroommc.modularui.widgets.TextWidget import com.cleanroommc.modularui.widgets.slot.ItemSlot import com.cleanroommc.modularui.widgets.slot.SlotGroup -import com.cleanroommc.retrosophisticatedbackpacks.Tags import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackInventoryHelper import com.cleanroommc.retrosophisticatedbackpacks.backpack.SortType import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.* import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.* -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.BackpackSlot +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.NoBackgroundItemSlot import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade.* import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackContainer import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiData @@ -33,6 +33,7 @@ import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.LockedPlayerS import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlot import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularUpgradeSlot import com.cleanroommc.retrosophisticatedbackpacks.config.ClientConfig +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.UpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSH import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSlotSH @@ -40,28 +41,41 @@ import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH import com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.ceilDiv +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.setEnabledIfAndEnabled +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.gui.ScaledResolution import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.util.ITooltipFlag +import net.minecraft.util.text.TextFormatting import net.minecraftforge.fml.common.Loader import net.minecraftforge.items.wrapper.PlayerInvWrapper import net.minecraftforge.items.wrapper.PlayerMainInvWrapper +import java.util.Locale import kotlin.math.min class BackpackPanel( internal val player: EntityPlayer, internal val tileEntity: BackpackTileEntity?, internal val syncManager: PanelSyncManager, - internal val backpackWrapper: BackpackWrapper + internal val backpackWrapper: BackpackWrapper, + private val openedBackpackSlotIndex: Int? = null, + private val backpackName: String? = null ) : ModularPanel("backpack_gui") { companion object { private const val SLOT_SIZE = 18 - private val LAYERED_TAB_TEXTURE = UITexture.builder() - .location(Tags.MOD_ID, "gui/gui_controls") - .imageSize(256, 256) - .xy(132, 0, 124, 256) - .adaptable(4) - .tiled() - .build() as AdaptableUITexture + private const val HEIGHT_WITHOUT_STORAGE_SLOTS = 114 + private const val STORAGE_INVENTORY_X = 7 + private const val STORAGE_INVENTORY_Y = 17 + private const val SEARCH_BOX_MIN_WIDTH = 10 + private const val SEARCH_BOX_HEIGHT = 10 + private const val SEARCH_BOX_UNFOCUSED_COLOR = 0xBBBBBB + private const val SEARCH_BOX_ANIMATION_MS = 200L + internal const val DISABLED_SLOT_X_POS = -2000 + internal const val VISIBLE_BACKPACK_ROWS = 5 + internal const val INVENTORY_CONTROL_COLUMNS = 2 private val SORT_TYPE_VARIANTS = listOf( CyclicVariantButtonWidget.Variant( IKey.lang("gui.sort_by_name".asTranslationKey()), @@ -89,35 +103,230 @@ class BackpackPanel( width: Int, height: Int, backpackSlotIndex: Int? = null, + backpackName: String? = null, ): BackpackPanel { - val panel = - BackpackPanel(player, tileEntity, syncManager, wrapper) - .size(width, height) as BackpackPanel + val panel = BackpackPanel(player, tileEntity, syncManager, wrapper, backpackSlotIndex, backpackName) + panel.background(IDrawable.EMPTY) syncManager.bindPlayerInventory(player) - panel.bindPlayerInventory() + panel.recalculateLayout() + panel.size(panel.panelWidth, panel.panelHeight) return panel } } val upgradeSlotWidgets = mutableListOf() - val upgradeSlotGroupWidget = UpgradeSlotGroupWidget(this, backpackWrapper.upgradeSlotsSize()) + var upgradeSlotGroupWidget = UpgradeSlotGroupWidget(this, backpackWrapper.upgradeSlotsSize()) + private set val tabWidgets = mutableListOf() - val rowSize = if (backpackWrapper.backpackInventorySize() > 81) 12 else 9 - val colSize = backpackWrapper.backpackInventorySize().ceilDiv(rowSize) - - val backpackSyncHandler: BackpackSH = BackpackSH(PlayerMainInvWrapper(player.inventory), backpackWrapper) + var tankInventoryControlCount = 0 + private set + var batteryInventoryControlCount = 0 + private set + var inventoryColumnsTaken = 0 + private set + var backgroundRowSize = 0 + private set + var rowSize = 0 + private set + var colSize = 0 + private set + var visibleColSize = 0 + private set + var backpackSlotsWidth = 0 + private set + var inventoryScrollbarWidth = 0 + private set + var inventoryAreaWidth = 0 + private set + var storageInventoryHeight = 0 + private set + private var playerInventoryXOffset = 0 + private var storageBackgroundTexture: UITexture = RSBTextures.STORAGE_BACKGROUND_9 + val playerInventoryLabelX: Int + get() = 8 + playerInventoryXOffset + val playerInventoryLabelY: Int + get() = panelHeight - 94 + private val playerInventorySlotsY: Int + get() = playerInventoryLabelY + 11 + val panelWidth: Int + get() = 14 + backgroundRowSize * SLOT_SIZE + inventoryScrollbarWidth + val panelHeight: Int + get() = HEIGHT_WITHOUT_STORAGE_SLOTS + visibleColSize * SLOT_SIZE + + val backpackSyncHandler: BackpackSH = BackpackSH(PlayerMainInvWrapper(player.inventory), backpackWrapper, tileEntity) val backpackSlotSyncHandlers: Array val upgradeSlotSyncHandlers: Array val upgradeSlotGroups: Array - val settingPanel: IPanelHandler var isMemorySettingTabOpened: Boolean = false var shouldMemorizeRespectNBT: Boolean = false var isSortingSettingTabOpened: Boolean = false + var isBackpackSettingTabOpened: Boolean = false + var isItemDisplaySettingTabOpened: Boolean = false + var currentItemDisplaySelectedSlot: Int = -1 + private set + internal val isSlotSettingTabOpened: Boolean + get() = isMemorySettingTabOpened || isSortingSettingTabOpened || isItemDisplaySettingTabOpened + private lateinit var backpackSettingTabWidget: TabWidget + private lateinit var memorySettingTabWidget: TabWidget + private lateinit var sortingSettingTabWidget: TabWidget + private lateinit var itemDisplaySettingTabWidget: TabWidget + private var rebuildWidgetsQueued = false + private var lastUpgradeStructureSignature = emptyList() + private var reopenBackpackQueued = false + private var lastScaledHeight = 0 + private val searchSlotDisplayIndices = IntArray(backpackWrapper.backpackInventorySize()) { it } + private var searchVisibleSlots = backpackWrapper.backpackInventorySize() + private var lastSearchLayoutKey = "" + var searchLayoutVersion = 0 + private set + var isSettingMode: Boolean = false + set(value) { + if (field == value) + return + + field = value + if (value) { + closeUpgradeTabs(syncToServer = true) + tabWidgets.forEach { it.isEnabled = false } + closeSettingTabs() + } else { + closeSettingTabs() + updateUpgradeWidgets() + } + updateSearchLayout(force = true) + scheduleResize() + } + + private val searchTerms: List + get() = backpackWrapper.searchPhrase.trim() + .split(Regex("\\s+")) + .filter(String::isNotEmpty) + + override fun onUpdate() { + super.onUpdate() + refreshLayoutIfScreenHeightChanged() + updateSearchLayout() + if (!rebuildWidgetsQueued) + queueRebuildIfUpgradeStructureChanged() + if (reopenBackpackQueued) { + reopenBackpackQueued = false + rebuildWidgetsQueued = false + upgradeSlotSyncHandlers.firstOrNull()?.syncToServer(UpgradeSlotSH.UPDATE_REOPEN_BACKPACK) {} + return + } + if (rebuildWidgetsQueued) { + rebuildWidgetsQueued = false + rebuildWidgets() + } + } + + fun refreshUpgradeWidgetsAfterSlotChange() { + if (!isValid) + return + + if (!queueRebuildIfUpgradeStructureChanged() && !rebuildWidgetsQueued) + updateUpgradeWidgets() + } + + private fun queueRebuildIfUpgradeStructureChanged(): Boolean { + if (!isValid) + return false + + val signature = upgradeStructureSignature() + if (signature == lastUpgradeStructureSignature) + return false + + lastUpgradeStructureSignature = signature + rebuildWidgetsQueued = true + if (syncManager.isClient && lastUpgradeStructureSignature.isNotEmpty()) + reopenBackpackQueued = true + return true + } + + fun rebuildWidgets() { + rebuildWidgetsQueued = false + reopenBackpackQueued = false + if (isValid) { + removeAll() + } + + recalculateLayout() + size(panelWidth, panelHeight) + updateSearchLayout(force = true) + upgradeSlotWidgets.clear() + tabWidgets.clear() + upgradeSlotGroupWidget = UpgradeSlotGroupWidget(this, backpackWrapper.upgradeSlotsSize()) + currentItemDisplaySelectedSlot = -1 + + addPlayerInventoryWidgets() + addBackpackInventorySlots() + addUpgradeSlots() + addSettingTab() + addUpgradeTabs() + addTexts(player) + addSortingButtons() + addSearchBox() + addTransferButtons() + closeSettingTabs() + lastUpgradeStructureSignature = upgradeStructureSignature() + updateUpgradeWidgets() + scheduleResize() + } + + private fun upgradeStructureSignature(): List = + (0 until backpackWrapper.upgradeSlotsSize()).map { slotIndex -> + val stack = backpackWrapper.upgradeItemStackHandler.getStackInSlot(slotIndex) + if (stack.isEmpty) "empty" + else stack.item.registryName?.toString() ?: stack.item.javaClass.name + } + + private fun recalculateLayout() { + tankInventoryControlCount = min(backpackWrapper.tankUpgradeSlots().size, 2) + batteryInventoryControlCount = min(backpackWrapper.batteryUpgradeSlots().size, 1) + inventoryColumnsTaken = (tankInventoryControlCount + batteryInventoryControlCount) * INVENTORY_CONTROL_COLUMNS + backgroundRowSize = if (backpackWrapper.backpackInventorySize() > 81) 12 else 9 + rowSize = (backgroundRowSize - inventoryColumnsTaken).coerceAtLeast(1) + colSize = backpackWrapper.backpackInventorySize().ceilDiv(rowSize) + visibleColSize = min(colSize, maxVisibleBackpackRows()) + backpackSlotsWidth = rowSize * SLOT_SIZE + inventoryScrollbarWidth = if (colSize > visibleColSize) BackpackInventoryScrollWidget.SCROLLBAR_WIDTH else 0 + inventoryAreaWidth = backgroundRowSize * SLOT_SIZE + inventoryScrollbarWidth + storageInventoryHeight = visibleColSize * SLOT_SIZE + playerInventoryXOffset = + when { + backgroundRowSize > 9 && inventoryScrollbarWidth > 0 -> 30 + backgroundRowSize > 9 -> 27 + inventoryScrollbarWidth > 0 -> 3 + else -> 0 + } + storageBackgroundTexture = + when { + backgroundRowSize > 9 && inventoryScrollbarWidth > 0 -> RSBTextures.STORAGE_BACKGROUND_12_WIDER + backgroundRowSize > 9 -> RSBTextures.STORAGE_BACKGROUND_12 + inventoryScrollbarWidth > 0 -> RSBTextures.STORAGE_BACKGROUND_9_WIDER + else -> RSBTextures.STORAGE_BACKGROUND_9 + } + } + + private fun refreshLayoutIfScreenHeightChanged() { + val scaledHeight = ScaledResolution(Minecraft.getMinecraft()).scaledHeight + if (scaledHeight == lastScaledHeight) + return + + lastScaledHeight = scaledHeight + rebuildWidgetsQueued = true + } + + private fun maxVisibleBackpackRows(): Int = + ((ScaledResolution(Minecraft.getMinecraft()).scaledHeight - HEIGHT_WITHOUT_STORAGE_SLOTS) / SLOT_SIZE) + .coerceAtLeast(1) init { + recalculateLayout() + lastScaledHeight = ScaledResolution(Minecraft.getMinecraft()).scaledHeight syncManager.syncValue("backpack_wrapper", backpackSyncHandler) // Backpack slots @@ -138,11 +347,12 @@ class BackpackPanel( backpackWrapper, it ).slotGroup("upgrade_inventory") - val syncHandler = UpgradeSlotSH(upgradeSlot) - val index = it - upgradeSlot.changeListener { lastStack, _, isClient, init -> + val syncHandler = UpgradeSlotSH(upgradeSlot) { + refreshUpgradeWidgetsAfterSlotChange() + } + upgradeSlot.changeListener { _, _, isClient, _ -> if (isClient) - updateUpgradeWidgets(index, lastStack) + refreshUpgradeWidgetsAfterSlotChange() } syncManager.syncValue("upgrades", it, syncHandler) @@ -156,15 +366,17 @@ class BackpackPanel( UpgradeSlotUpdateGroup(this, backpackWrapper, it) } - settingPanel = syncManager.syncedPanel("setting_panel", true) { syncManager, syncHandler -> - BackpackSettingPanel(this) - } } fun getBackpackContainer(): BackpackContainer { return syncManager.container as BackpackContainer } + override fun onInit() { + super.onInit() + updateUpgradeWidgets() + } + // Currently only main hand slot will be locked if it's the backpack being opened internal fun modifyPlayerSlot( syncManager: PanelSyncManager, @@ -201,18 +413,17 @@ class BackpackPanel( } } .setEnabledIf { - !settingPanel.isPanelOpen + !isSettingMode } .top(4) .right(rightAnchor) .size(12) - val sortButton = ButtonWidget() + val sortButton = TransferButtonWidget(RSBTextures.SOLID_UP_ARROW_ICON, RSBTextures.SOLID_UP_ARROW_ICON) .top(4) .right(rightAnchor + 14) .size(12) - .overlay(RSBTextures.SOLID_UP_ARROW_ICON) .setEnabledIf { - !settingPanel.isPanelOpen + !isSettingMode } .onMousePressed { mouseButton -> if (mouseButton == 0) { @@ -236,18 +447,15 @@ class BackpackPanel( } internal fun addTransferButtons() { - val rightAnchor = if (Loader.isModLoaded("bogosorter")) { - if (backpackWrapper.backpackInventorySize() > 81) 57 - else 30 - } else 7 + val transferButtonsShiftX = if (Loader.isModLoaded("bogosorter")) -23 else 0 val transferToBackpackButton = TransferButtonWidget(RSBTextures.DOT_UP_ARROW_ICON, RSBTextures.SOLID_UP_ARROW_ICON) - .top(17 + colSize * 18) - .right(rightAnchor) + .top(playerInventoryLabelY - 2) + .left(playerInventoryLabelX + 137 + transferButtonsShiftX) .size(12) .setEnabledIf { - !settingPanel.isPanelOpen + !isSettingMode } .onMousePressed { mouseButton -> if (mouseButton == 0) { @@ -276,11 +484,11 @@ class BackpackPanel( } val transferToPlayerButton = TransferButtonWidget(RSBTextures.DOT_DOWN_ARROW_ICON, RSBTextures.SOLID_DOWN_ARROW_ICON) - .top(17 + colSize * 18) - .right(rightAnchor + 14) + .top(playerInventoryLabelY - 2) + .left(playerInventoryLabelX + 149 + transferButtonsShiftX) .size(12) .setEnabledIf { - !settingPanel.isPanelOpen + !isSettingMode } .onMousePressed { mouseButton -> if (mouseButton == 0) { @@ -312,28 +520,354 @@ class BackpackPanel( .child(transferToBackpackButton) } + internal fun addSearchBox() { + val rightAnchor = if (Loader.isModLoaded("bogosorter")) 30 else 7 + val sortButtonLeft = panelWidth - (rightAnchor + 14) - 12 + val left = 7 + val width = (sortButtonLeft - 1 - left).coerceAtLeast(SEARCH_BOX_MIN_WIDTH) + if (width <= 0) { + return + } + + child( + SearchBoxWidget() + .top(5) + .left(left) + .size(width, SEARCH_BOX_HEIGHT) + .setEnabledIf { !isSettingMode } + ) + } + + internal fun addPlayerInventoryWidgets() { + child( + SlotGroupWidget.playerInventory(0, false) { _, _ -> + NoBackgroundItemSlot() + } + .disableSortButtons() + .pos(playerInventoryLabelX - 1, playerInventorySlotsY) + ) + } + + internal fun hasSearchPhrase(): Boolean = + searchTerms.isNotEmpty() + + internal fun isSearchViewActive(): Boolean = + !isSettingMode && hasSearchPhrase() + + internal fun updateSearchLayout(force: Boolean = false) { + val key = if (isSearchViewActive()) + "${isSettingMode}|${backpackWrapper.searchPhrase}|${searchInventorySignature()}" + else "${isSettingMode}|${backpackWrapper.searchPhrase}|${backpackWrapper.backpackInventorySize()}" + if (!force && key == lastSearchLayoutKey) { + return + } + + lastSearchLayoutKey = key + searchVisibleSlots = 0 + + if (!isSearchViewActive()) { + for (slotIndex in searchSlotDisplayIndices.indices) { + searchSlotDisplayIndices[slotIndex] = slotIndex + } + searchVisibleSlots = backpackWrapper.backpackInventorySize() + } else { + for (slotIndex in searchSlotDisplayIndices.indices) { + val stack = backpackWrapper.getStackInSlot(slotIndex) + searchSlotDisplayIndices[slotIndex] = + if (!backpackWrapper.isSlotBlockedByMobCatcher(slotIndex) && !stack.isEmpty && matchesSearch(stack)) + searchVisibleSlots++ + else DISABLED_SLOT_X_POS + } + } + + searchLayoutVersion++ + } + + internal fun isSearchSlotVisible(slotIndex: Int): Boolean = + !isSearchViewActive() || searchSlotDisplayIndices.getOrNull(slotIndex)?.let { it >= 0 } == true + + internal fun searchSlotX(slotIndex: Int): Int { + val displayIndex = searchSlotDisplayIndices.getOrNull(slotIndex) ?: return DISABLED_SLOT_X_POS + return if (displayIndex < 0) DISABLED_SLOT_X_POS else displayIndex % rowSize * SLOT_SIZE + } + + internal fun searchSlotY(slotIndex: Int): Int { + val displayIndex = searchSlotDisplayIndices.getOrNull(slotIndex) ?: return 0 + return if (displayIndex < 0) 0 else displayIndex / rowSize * SLOT_SIZE + } + + internal fun searchVisibleSlotCount(): Int = + if (isSearchViewActive()) searchVisibleSlots else backpackWrapper.backpackInventorySize() + + internal fun searchDisplayRows(): Int = + if (isSearchViewActive()) searchVisibleSlots.coerceAtLeast(1).ceilDiv(rowSize).coerceAtLeast(visibleColSize) + else colSize + + private fun searchInventorySignature(): String = + buildString { + append(backpackWrapper.backpackInventorySize()) + for (slotIndex in 0 until backpackWrapper.backpackInventorySize()) { + val stack = backpackWrapper.getStackInSlot(slotIndex) + append('|') + if (stack.isEmpty) { + append("empty") + } else { + append(stack.item.registryName) + .append(':') + .append(stack.metadata) + .append(':') + .append(stack.count) + } + append(':') + .append(backpackWrapper.isSlotBlockedByMobCatcher(slotIndex)) + } + } + + internal fun matchesSearch(stack: ItemStack): Boolean { + val terms = searchTerms + if (terms.isEmpty()) { + return true + } + if (stack.isEmpty) { + return false + } + + val displayName = stack.displayName.lowercase(Locale.ROOT) + val registryName = stack.item.registryName + val modId = registryName?.namespace?.lowercase(Locale.ROOT) ?: "" + val tooltipLines by lazy { + stack.getTooltip(player, ITooltipFlag.TooltipFlags.NORMAL) + .joinToString("\n") { TextFormatting.getTextWithoutFormattingCodes(it) ?: it } + .lowercase(Locale.ROOT) + } + + return terms.all { rawTerm -> + val term = rawTerm.lowercase(Locale.ROOT) + when { + term.startsWith("@") -> modId.contains(term.drop(1)) + term.startsWith("#") -> tooltipLines.contains(term.drop(1)) + else -> displayName.contains(term) + } + } + } + + private fun setSearchPhrase(phrase: String) { + val trimmed = phrase.take(50) + if (backpackWrapper.searchPhrase == trimmed) { + return + } + backpackWrapper.searchPhrase = trimmed + updateSearchLayout(force = true) + backpackSyncHandler.syncToServer(BackpackSH.UPDATE_SEARCH_PHRASE) { + it.writeString(trimmed) + } + } + + private inner class SearchBoxWidget : VanillaTextFieldWidget(1, SEARCH_BOX_MIN_WIDTH - 2, 8) { + private var lastSyncedText = backpackWrapper.searchPhrase + private var lastFocusChangeTime = Minecraft.getSystemTime() + private var currentWidth = -1 + private var currentTheme = WidgetTheme.getDefault().theme + + init { + textField.setMaxStringLength(50) + textField.setText(backpackWrapper.searchPhrase) + textField.setTextColor(if (backpackWrapper.searchPhrase.isEmpty()) SEARCH_BOX_UNFOCUSED_COLOR else 0xFFFFFF) + textField.setDisabledTextColour(SEARCH_BOX_UNFOCUSED_COLOR) + tooltipBuilder { + it.addLine(IKey.lang("gui.search".asTranslationKey())) + .addLine(IKey.lang("gui.search_detail".asTranslationKey()).style(IKey.GRAY)) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + } + + override fun isInside(stack: IViewportStack, mx: Int, my: Int, absolute: Boolean): Boolean { + if (isSettingMode) { + return false + } + val x = if (absolute) stack.unTransformX(mx.toFloat(), my.toFloat()) else mx + val y = if (absolute) stack.unTransformY(mx.toFloat(), my.toFloat()) else my + return y >= 0 && y < area.height && x >= visualX() && x < area.width + } + + override fun drawBackground(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) {} + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + currentTheme = widgetTheme.theme + if (isSettingMode) { + return + } + GlStateManager.color(1f, 1f, 1f, 1f) + val visualWidth = visualWidth() + val visualX = visualX() + Gui.drawRect(visualX, 0, visualX + visualWidth, area.height, 0xFF777777.toInt()) + if (!isFocused() && textField.text.isEmpty()) { + RSBTextures.SEARCH_ICON.draw( + context, + visualX, + 0, + SEARCH_BOX_MIN_WIDTH, + SEARCH_BOX_HEIGHT, + currentTheme + ) + } else { + drawTextField() + } + GlStateManager.color(1f, 1f, 1f, 1f) + } + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (isSettingMode) { + return Interactable.Result.IGNORE + } + val mouseX = context.mouseX + val mouseY = context.mouseY + if (mouseY < 0 || mouseY >= area.height || mouseX < visualX() || mouseX >= area.width) { + return Interactable.Result.IGNORE + } + if (mouseButton == 1) { + onTextChanged("") + return Interactable.Result.SUCCESS + } + if (mouseButton != 0) { + return Interactable.Result.STOP + } + return super.onMousePressed(mouseButton) + } + + override fun onUpdate() { + super.onUpdate() + if (isSettingMode) { + if (isFocused()) { + context.removeFocus() + } + return + } + animateWidth() + if (backpackWrapper.searchPhrase != lastSyncedText && backpackWrapper.searchPhrase != textField.text) { + lastSyncedText = backpackWrapper.searchPhrase + textField.setText(lastSyncedText) + } + } + + override fun onTextChanged(text: String) { + val trimmed = text.take(50) + if (textField.text != trimmed) { + textField.setText(trimmed) + } + if (trimmed == lastSyncedText) { + return + } + lastSyncedText = trimmed + setSearchPhrase(lastSyncedText) + } + + override fun onFocusChanged(focused: Boolean) { + lastFocusChangeTime = Minecraft.getSystemTime() + textField.setTextColor(if (focused) 0xFFFFFF else SEARCH_BOX_UNFOCUSED_COLOR) + } + + private fun visualWidth(): Int { + animateWidth() + return currentWidth.coerceIn(SEARCH_BOX_MIN_WIDTH, area.width) + } + + private fun visualX(): Int = area.width - visualWidth() + + override fun textFieldX(): Int = visualX() + 1 + + override fun textFieldY(): Int = 1 + + override fun textFieldWidth(): Int = visualWidth() - 6 + + override fun textFieldHeight(): Int = SEARCH_BOX_HEIGHT + + override fun mouseXForTextField(): Int = context.mouseX + + override fun mouseYForTextField(): Int = context.mouseY + + private fun animateWidth() { + val target = if (isFocused() || textField.text.isNotEmpty()) area.width else SEARCH_BOX_MIN_WIDTH + if (currentWidth < 0) { + currentWidth = target + return + } + if (currentWidth == target) { + return + } + val elapsed = (Minecraft.getSystemTime() - lastFocusChangeTime).coerceAtLeast(0L) + val ratio = (elapsed.toFloat() / SEARCH_BOX_ANIMATION_MS).coerceIn(0f, 1f) + val eased = if (ratio < 0.5f) 4f * ratio * ratio * ratio else 1f - Math.pow((-2f * ratio + 2f).toDouble(), 3.0).toFloat() / 2f + currentWidth = if (target == area.width) { + (SEARCH_BOX_MIN_WIDTH + (area.width - SEARCH_BOX_MIN_WIDTH) * eased).toInt() + } else { + (area.width - (area.width - SEARCH_BOX_MIN_WIDTH) * eased).toInt() + }.coerceIn(SEARCH_BOX_MIN_WIDTH, area.width) + } + } + internal fun addBackpackInventorySlots() { - val backpackSlotGroupWidget = SlotGroupWidget().name("backpack_inventory") - backpackSlotGroupWidget.coverChildren().leftRel(0.5F).top(17) + val inventoryArea = SlotGroupWidget().disableSortButtons() + .size(inventoryAreaWidth, storageInventoryHeight) + .pos(STORAGE_INVENTORY_X, STORAGE_INVENTORY_Y) + .setEnabledIf { !isSettingMode || isSlotSettingTabOpened } + + inventoryArea.child( + if (inventoryScrollbarWidth > 0) BackpackInventoryScrollWidget(this).pos(0, 0) + else BackpackInventoryScrollWidget.createSlots(this, visibleColSize).pos(0, 0) + ) - for (i in 0 until backpackWrapper.backpackInventorySize()) { - val itemSlot = BackpackSlot(this, backpackWrapper) - .syncHandler("backpack", i) - .pos(i % rowSize * SLOT_SIZE, i / rowSize * SLOT_SIZE) - .name("slot_${i}") + val tankSlots = backpackWrapper.tankUpgradeSlots().take(tankInventoryControlCount) + var controlIndex = 0 + for ((index, slot) in tankSlots.withIndex()) { + if (backpackWrapper.upgradeItemStackHandler.inventory[slot] + .getCapability(Capabilities.TANK_UPGRADE_CAPABILITY, null) == null + ) continue + inventoryArea.child( + TankInventoryControlWidget( + upgradeSlotSyncHandlers[slot], + slot, + backpackWrapper, + storageInventoryHeight + ) + .pos(backpackSlotsWidth + inventoryScrollbarWidth + controlIndex * TankInventoryControlWidget.WIDTH, 0) + .name("tank_inventory_control_$slot") + .setEnabledIf { !isSettingMode && !isSearchViewActive() } + ) + controlIndex++ + } - backpackSlotGroupWidget.child(itemSlot) + val batterySlots = backpackWrapper.batteryUpgradeSlots().take(batteryInventoryControlCount) + for (slot in batterySlots) { + if (backpackWrapper.upgradeItemStackHandler.inventory[slot] + .getCapability(Capabilities.BATTERY_UPGRADE_CAPABILITY, null) == null + ) continue + inventoryArea.child( + BatteryInventoryControlWidget( + slot, + backpackWrapper, + storageInventoryHeight + ) + .pos(backpackSlotsWidth + inventoryScrollbarWidth + controlIndex * BatteryInventoryControlWidget.WIDTH, 0) + .name("battery_inventory_control_$slot") + .setEnabledIf { !isSettingMode && !isSearchViewActive() } + ) + controlIndex++ } - child(backpackSlotGroupWidget) + child(inventoryArea) } internal fun addUpgradeSlots() { upgradeSlotGroupWidget.name("upgrade_inventory") - upgradeSlotGroupWidget.size(23, 10 + backpackWrapper.upgradeSlotsSize() * 18).left(-21) + upgradeSlotGroupWidget.size(25, 13 + backpackWrapper.upgradeSlotsSize() * 16).left(-21) + upgradeSlotGroupWidget.setEnabledIf { !isSettingMode } for (i in 0 until backpackWrapper.upgradeSlotsSize()) { - val itemSlot = ItemSlot().syncHandler("upgrades", i).pos(5, 5 + i * 18).name("slot_${i}") + val itemSlot = NoBackgroundItemSlot(RSBTextures.EMPTY_UPGRADE_SLOT) + .syncHandler("upgrades", i) + .pos(5, 5 + i * 16) + .name("slot_${i}") upgradeSlotWidgets.add(itemSlot) upgradeSlotGroupWidget.child(itemSlot) @@ -343,12 +877,90 @@ class BackpackPanel( } internal fun addSettingTab() { - child(SettingTabWidget()) + val backToBackpackTab = BackToBackpackTabWidget() + .setEnabledIfAndEnabled({ isSettingMode }, false) + + backpackSettingTabWidget = TabWidget(2).name("backpack_setting_tab") + backpackSettingTabWidget.isEnabled = false + backpackSettingTabWidget.expandedWidget = BackpackMainSettingsWidget(this, backpackSettingTabWidget) + backpackSettingTabWidget.tabIcon = RSBTextures.BACKPACK_SETTINGS_ICON + backpackSettingTabWidget.tooltipDynamic { + it.clearText() + .addLine(IKey.lang("gui.backpack_settings.tooltip".asTranslationKey())) + .addLine( + IKey.lang( + if (backpackSettingTabWidget.showExpanded) + "gui.backpack_settings.tooltip_open_detail".asTranslationKey() + else "gui.backpack_settings.tooltip_detail".asTranslationKey() + ).style(IKey.GRAY) + ) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + sortingSettingTabWidget = TabWidget(3).name("sorting_setting_tab") + sortingSettingTabWidget.isEnabled = false + sortingSettingTabWidget.expandedWidget = SortingSettingWidget(this, sortingSettingTabWidget) + sortingSettingTabWidget.tabIcon = RSBTextures.NO_SORT_ICON + sortingSettingTabWidget.tooltipDynamic { + it.clearText() + .addLine(IKey.lang("gui.sorting_settings.tooltip".asTranslationKey())) + .addLine( + IKey.lang( + if (sortingSettingTabWidget.showExpanded) + "gui.sorting_settings.tooltip_open_detail".asTranslationKey() + else "gui.sorting_settings.tooltip_detail".asTranslationKey() + ).style(IKey.GRAY) + ) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + memorySettingTabWidget = TabWidget(4).name("memory_setting_tab") + memorySettingTabWidget.isEnabled = false + memorySettingTabWidget.expandedWidget = MemorySettingWidget(this, memorySettingTabWidget) + memorySettingTabWidget.tabIcon = RSBTextures.BRAIN_ICON + memorySettingTabWidget.tooltipDynamic { + it.clearText() + .addLine(IKey.lang("gui.memory_settings.tooltip".asTranslationKey())) + .addLine( + IKey.lang( + if (memorySettingTabWidget.showExpanded) + "gui.memory_settings.tooltip_open_detail".asTranslationKey() + else "gui.memory_settings.tooltip_detail".asTranslationKey() + ).style(IKey.GRAY) + ) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + itemDisplaySettingTabWidget = TabWidget(5).name("item_display_setting_tab") + itemDisplaySettingTabWidget.isEnabled = false + itemDisplaySettingTabWidget.expandedWidget = ItemDisplaySettingsWidget(this, itemDisplaySettingTabWidget) + itemDisplaySettingTabWidget.tabIcon = RSBTextures.ITEM_DISPLAY_SETTINGS_ICON + itemDisplaySettingTabWidget.tooltipDynamic { + it.clearText() + .addLine(IKey.lang("gui.item_display_settings.tooltip".asTranslationKey())) + .addLine( + IKey.lang( + if (itemDisplaySettingTabWidget.showExpanded) + "gui.item_display_settings.tooltip_open_detail".asTranslationKey() + else "gui.item_display_settings.tooltip_detail".asTranslationKey() + ).style(IKey.GRAY) + ) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + child(SettingTabWidget().setEnabledIf { !isSettingMode }) + if (!Config.itemDisplayDisabled) { + child(itemDisplaySettingTabWidget) + } + child(memorySettingTabWidget) + .child(sortingSettingTabWidget) + .child(backpackSettingTabWidget) + .child(backToBackpackTab) } internal fun addUpgradeTabs() { for (i in 0 until backpackWrapper.upgradeSlotsSize()) { - val tab = TabWidget(i + 1).name("upgrade_tab_${i}") + val tab = TabWidget(i + 2).name("upgrade_tab_${i}") tab.isEnabled = false tabWidgets.add(tab) @@ -360,16 +972,147 @@ class BackpackPanel( } } - internal fun addTexts(player: EntityPlayer, backpackName: String) { - child(TextWidget(StringKey(backpackName)).pos(8, 6)) - child(TextWidget(StringKey(player.inventory.displayName.formattedText)).pos(8, 18 + colSize * 18)) + internal fun addTexts(player: EntityPlayer) { + val titleWidget = TextWidget(StringKey(backpackName ?: backpackWrapper.getDisplayName().formattedText)) + .pos(8, 6) + .setEnabledIf { !isSettingMode } + val settingsTitleWidget = IKey.lang("gui.settings".asTranslationKey()).asWidget() + .pos(8, 6) + .setEnabledIf { isSettingMode } + settingsTitleWidget.isEnabled = false + child(titleWidget) + child(settingsTitleWidget) + child(TextWidget(StringKey(player.inventory.displayName.formattedText)).pos(playerInventoryLabelX, playerInventoryLabelY)) + } + + fun openMemorySettings(tabWidget: TabWidget, open: Boolean) { + if (!isSettingMode) + return + + memorySettingTabWidget.showExpanded = open + isMemorySettingTabOpened = open + shouldMemorizeRespectNBT = + open && (memorySettingTabWidget.expandedWidget as? MemorySettingWidget)?.isRespectNBT() == true + + if (open) + closeOtherSettingTabs(memorySettingTabWidget) + updateSettingTabEnabledStates(memorySettingTabWidget, open) + } + + fun openSortingSettings(tabWidget: TabWidget, open: Boolean) { + if (!isSettingMode) + return + + sortingSettingTabWidget.showExpanded = open + isSortingSettingTabOpened = open + + if (open) + closeOtherSettingTabs(sortingSettingTabWidget) + updateSettingTabEnabledStates(sortingSettingTabWidget, open) + } + + fun openBackpackSettings(tabWidget: TabWidget, open: Boolean) { + if (!isSettingMode) + return + + backpackSettingTabWidget.showExpanded = open + isBackpackSettingTabOpened = open + + if (open) + closeOtherSettingTabs(backpackSettingTabWidget) + updateSettingTabEnabledStates(backpackSettingTabWidget, open) + } + + fun openItemDisplaySettings(tabWidget: TabWidget, open: Boolean) { + if (!isSettingMode || Config.itemDisplayDisabled) + return + + itemDisplaySettingTabWidget.showExpanded = open + isItemDisplaySettingTabOpened = open + currentItemDisplaySelectedSlot = if (open) backpackWrapper.getFirstItemDisplaySlot() else -1 + + if (open) + closeOtherSettingTabs(itemDisplaySettingTabWidget) + updateSettingTabEnabledStates(itemDisplaySettingTabWidget, open) + } + + fun setCurrentItemDisplaySelectedSlot(slotIndex: Int) { + currentItemDisplaySelectedSlot = slotIndex + } + + private fun closeSettingTabs() { + if (!this::memorySettingTabWidget.isInitialized || !this::sortingSettingTabWidget.isInitialized || + !this::backpackSettingTabWidget.isInitialized || !this::itemDisplaySettingTabWidget.isInitialized) + return + + backpackSettingTabWidget.showExpanded = false + memorySettingTabWidget.showExpanded = false + sortingSettingTabWidget.showExpanded = false + itemDisplaySettingTabWidget.showExpanded = false + backpackSettingTabWidget.isEnabled = isSettingMode + memorySettingTabWidget.isEnabled = isSettingMode + sortingSettingTabWidget.isEnabled = isSettingMode + itemDisplaySettingTabWidget.isEnabled = isSettingMode && !Config.itemDisplayDisabled + isBackpackSettingTabOpened = false + isMemorySettingTabOpened = false + shouldMemorizeRespectNBT = false + isSortingSettingTabOpened = false + isItemDisplaySettingTabOpened = false + currentItemDisplaySelectedSlot = -1 + } + + private fun closeOtherSettingTabs(openTab: TabWidget) { + if (openTab != backpackSettingTabWidget) { + backpackSettingTabWidget.showExpanded = false + isBackpackSettingTabOpened = false + } + if (openTab != sortingSettingTabWidget) { + sortingSettingTabWidget.showExpanded = false + isSortingSettingTabOpened = false + } + if (openTab != memorySettingTabWidget) { + memorySettingTabWidget.showExpanded = false + isMemorySettingTabOpened = false + shouldMemorizeRespectNBT = false + } + if (openTab != itemDisplaySettingTabWidget) { + itemDisplaySettingTabWidget.showExpanded = false + isItemDisplaySettingTabOpened = false + currentItemDisplaySelectedSlot = -1 + } + } + + private fun updateSettingTabEnabledStates(openTab: TabWidget, open: Boolean) { + listOf(backpackSettingTabWidget, sortingSettingTabWidget, memorySettingTabWidget, itemDisplaySettingTabWidget) + .forEach { it.isEnabled = !open || it == openTab } + if (Config.itemDisplayDisabled) { + itemDisplaySettingTabWidget.isEnabled = false + } + } + + private fun closeUpgradeTabs(syncToServer: Boolean) { + for (slotIndex in 0 until backpackWrapper.upgradeSlotsSize()) { + val wrapper = backpackWrapper.upgradeItemStackHandler.getStackInSlot(slotIndex) + .getCapability(Capabilities.UPGRADE_CAPABILITY, null) ?: continue + + if (!wrapper.isTabOpened) + continue + + wrapper.isTabOpened = false + tabWidgets.getOrNull(slotIndex)?.showExpanded = false + if (syncToServer) { + upgradeSlotSyncHandlers[slotIndex].syncToServer(UpgradeSlotSH.UPDATE_UPGRADE_TAB_STATE) { + it.writeBoolean(false) + } + } + } } private inline fun , reified U : UpgradeWrapper<*>> updateAndCheckRecreation( widget: ExpandedTabWidget?, wrapper: U ): Boolean { - if (widget is V) { + if (widget is V && widget::class == V::class && widget.wrapper::class == wrapper::class) { widget.wrapper = wrapper return false } @@ -380,21 +1123,39 @@ class BackpackPanel( widget: ExpandedTabWidget?, wrapper: Any ): Boolean { - if (widget is V) { + if (widget is V && widget::class == V::class) { return !widget.consumePossibleWrapper(wrapper) } return true } + private fun clearUpgradeTab(tabWidget: TabWidget) { + tabWidget.showExpanded = false + tabWidget.isEnabled = false + tabWidget.expandedWidget = null + tabWidget.tabIcon = null + tabWidget.tooltip().reset() + } + + + private fun updateUpgradeWidgets() { + if (!isValid) + return + + if (isSettingMode) { + tabWidgets.forEach { + clearUpgradeTab(it) + } + syncToggles() + scheduleResize() + return + } - private fun updateUpgradeWidgets(index: Int?, lastStack: ItemStack?) { var tabIndex = 0 var openedTabIndex: Int? = null - resetTabState() - - for ((slotIndex, slotWidget) in upgradeSlotWidgets.withIndex()) { - val stack: ItemStack = slotWidget.slot.stack + for (slotIndex in 0 until backpackWrapper.upgradeSlotsSize()) { + val stack: ItemStack = backpackWrapper.upgradeItemStackHandler.getStackInSlot(slotIndex) val item = stack.item if (!(item is UpgradeItem && item.hasTab)) @@ -408,38 +1169,39 @@ class BackpackPanel( upgradeSlotSyncHandlers[slotIndex].syncToServer(UpgradeSlotSH.UPDATE_UPGRADE_TAB_STATE) { it.writeBoolean(false) } - - return + continue } openedTabIndex = slotIndex } } // Shifted forward to account for settings tab. - var tabDisplayIndex = 1 + var tabDisplayIndex = 2 // Sync all tabs to their corresponding upgrade for (slotIndex in 0 until backpackWrapper.upgradeSlotsSize()) { - val slot = upgradeSlotWidgets[slotIndex] - val stack: ItemStack = slot.slot.stack + val stack: ItemStack = backpackWrapper.upgradeItemStackHandler.getStackInSlot(slotIndex) val item = stack.item val tabWidget = tabWidgets[tabIndex] if (!(item is UpgradeItem && item.hasTab)) { - tabWidget.isEnabled = false - tabWidget.expandedWidget = null + clearUpgradeTab(tabWidget) tabIndex++ continue } val upgradeSlotGroup = upgradeSlotGroups[slotIndex] - val wrapper: UpgradeWrapper<*> = stack.getCapability(Capabilities.UPGRADE_CAPABILITY, null) ?: continue + val wrapper: UpgradeWrapper<*> = stack.getCapability(Capabilities.UPGRADE_CAPABILITY, null) ?: run { + clearUpgradeTab(tabWidget) + tabIndex++ + continue + } tabWidget.showExpanded = wrapper.isTabOpened tabWidget.isEnabled = true // Ensure correct tab position tabWidget.tabOrder = tabDisplayIndex - tabWidget.tabIcon = ItemDrawable(slot.slot.stack) + tabWidget.tabIcon = ItemDrawable(stack) tabWidget.tooltip { it.clearText() .addLine(IKey.str(item.getItemStackDisplayName(stack))) @@ -497,6 +1259,164 @@ class BackpackPanel( tabWidget.expandedWidget = FilterUpgradeWidget(slotIndex, wrapper) } + is AdvancedMagnetUpgradeWrapper -> { + upgradeSlotGroup.updateAdvancedFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedMagnetUpgradeWidget(slotIndex, wrapper, stack) + } + + is MagnetUpgradeWrapper -> { + upgradeSlotGroup.updateFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = MagnetUpgradeWidget(slotIndex, wrapper, stack) + } + + is AdvancedVoidUpgradeWrapper -> { + upgradeSlotGroup.updateAdvancedFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedVoidUpgradeWidget(slotIndex, wrapper, stack) + } + + is VoidUpgradeWrapper -> { + upgradeSlotGroup.updateFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = VoidUpgradeWidget(slotIndex, wrapper, stack) + } + + is AdvancedRefillUpgradeWrapper -> { + upgradeSlotGroup.updateLargeBasicFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedRefillUpgradeWidget(slotIndex, wrapper, stack) + } + + is RefillUpgradeWrapper -> { + upgradeSlotGroup.updateFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = RefillUpgradeWidget(slotIndex, wrapper, stack) + } + + is AdvancedCompactingUpgradeWrapper -> { + upgradeSlotGroup.updateAdvancedFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedCompactingUpgradeWidget(slotIndex, wrapper, stack) + } + + is CompactingUpgradeWrapper -> { + upgradeSlotGroup.updateFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = CompactingUpgradeWidget(slotIndex, wrapper, stack) + } + + is AdvancedJukeboxUpgradeWrapper -> { + upgradeSlotGroup.updateJukeboxDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedJukeboxUpgradeWidget(slotIndex, wrapper, stack) + } + + is JukeboxUpgradeWrapper -> { + upgradeSlotGroup.updateJukeboxDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = JukeboxUpgradeWidget(slotIndex, wrapper, stack) + } + + is AdvancedToolSwapperUpgradeWrapper -> { + upgradeSlotGroup.updateAdvancedFilterDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedToolSwapperUpgradeWidget(slotIndex, wrapper, stack) + } + + is TankUpgradeWrapper -> { + upgradeSlotGroup.updateTankDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = TankUpgradeWidget(slotIndex, wrapper, stack) + } + + is AdvancedPumpUpgradeWrapper -> { + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AdvancedPumpUpgradeWidget(slotIndex, wrapper, stack) + } + + is PumpUpgradeWrapper -> { + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = PumpUpgradeWidget(slotIndex, wrapper, stack) + } + + is BatteryUpgradeWrapper -> { + upgradeSlotGroup.updateBatteryDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = BatteryUpgradeWidget(slotIndex, wrapper, stack) + } + + is AnvilUpgradeWrapper -> { + upgradeSlotGroup.updateAnvilDelegate(wrapper) + if (updateAndCheckRecreation( + tabWidget.expandedWidget, + wrapper + ) + ) + tabWidget.expandedWidget = AnvilUpgradeWidget(slotIndex, wrapper, stack) + } + is IAdvancedFilterable -> { upgradeSlotGroup.updateAdvancedFilterDelegate(wrapper) if (updateAndCheckRecreation>(tabWidget.expandedWidget, wrapper)) @@ -520,7 +1440,6 @@ class BackpackPanel( } } - context.recipeViewerSettings.addExclusionArea(tabWidget.expandedWidget) tabIndex++ tabDisplayIndex++ } @@ -542,17 +1461,9 @@ class BackpackPanel( scheduleResize() } - private fun resetTabState() { - for (tabWidget in tabWidgets) { - if (tabWidget.expandedWidget != null) { - context.recipeViewerSettings.removeExclusionArea(tabWidget.expandedWidget) - } - } - } - private fun disableUnusedTabWidgets(startTabIndex: Int) { for (i in startTabIndex until backpackWrapper.upgradeSlotsSize()) { - tabWidgets[i].isEnabled = false + clearUpgradeTab(tabWidgets[i]) } } @@ -565,6 +1476,7 @@ class BackpackPanel( toggleWidget.isToggleEnabled = wrapper.enabled toggleWidget.isEnabled = true } else { + toggleWidget.isToggleEnabled = false toggleWidget.isEnabled = false } } @@ -573,11 +1485,53 @@ class BackpackPanel( override fun shouldAnimate(): Boolean = ClientConfig.enableAnimation && super.shouldAnimate() - override fun postDraw(context: ModularGuiContext, transformed: Boolean) { - super.postDraw(context, transformed) + override fun drawBackground(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + renderStorageBackground(widgetTheme.theme) + } + + private fun renderStorageBackground(theme: WidgetTheme) { + val slotsTopBottomHeight = min(storageInventoryHeight / 2, 150) + var yOffset = 0 + + storageBackgroundTexture.drawSubArea( + 0f, + 0f, + area.width.toFloat(), + (STORAGE_INVENTORY_Y + slotsTopBottomHeight).toFloat(), + 0f, + 0f, + area.width / 256f, + (STORAGE_INVENTORY_Y + slotsTopBottomHeight) / 256f, + theme + ) - // Nasty hack to draw over upgrade tabs - LAYERED_TAB_TEXTURE.draw(context, area.width - 6, 0, 6, area.height, WidgetTheme.getDefault().theme) + if (storageInventoryHeight / 2 > 150) { + val middleHeight = (storageInventoryHeight / 2 - 150) * 2 + storageBackgroundTexture.drawSubArea( + 0f, + (STORAGE_INVENTORY_Y + slotsTopBottomHeight).toFloat(), + area.width.toFloat(), + middleHeight.toFloat(), + 0f, + STORAGE_INVENTORY_Y / 256f, + area.width / 256f, + (STORAGE_INVENTORY_Y + middleHeight) / 256f, + theme + ) + yOffset = middleHeight + } + + storageBackgroundTexture.drawSubArea( + 0f, + (yOffset + STORAGE_INVENTORY_Y + slotsTopBottomHeight).toFloat(), + area.width.toFloat(), + (97 + slotsTopBottomHeight).toFloat(), + 0f, + (256 - (97 + slotsTopBottomHeight)) / 256f, + area.width / 256f, + 1f, + theme + ) } fun getOpenCraftingUpgradeSlot(): Int? { diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackSettingPanel.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackSettingPanel.kt deleted file mode 100644 index 240c3de..0000000 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/BackpackSettingPanel.kt +++ /dev/null @@ -1,111 +0,0 @@ -package com.cleanroommc.retrosophisticatedbackpacks.client.gui - -import com.cleanroommc.modularui.api.drawable.IKey -import com.cleanroommc.modularui.drawable.AdaptableUITexture -import com.cleanroommc.modularui.drawable.UITexture -import com.cleanroommc.modularui.screen.ModularPanel -import com.cleanroommc.modularui.screen.ModularScreen -import com.cleanroommc.modularui.screen.RichTooltip -import com.cleanroommc.modularui.screen.viewport.ModularGuiContext -import com.cleanroommc.modularui.theme.WidgetTheme -import com.cleanroommc.modularui.widgets.TextWidget -import com.cleanroommc.modularui.widgets.layout.Column -import com.cleanroommc.retrosophisticatedbackpacks.Tags -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.ExpandDirection -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.MemorySettingWidget -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.SortingSettingWidget -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.TabWidget -import com.cleanroommc.retrosophisticatedbackpacks.config.ClientConfig -import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey - -class BackpackSettingPanel(private val parent: BackpackPanel) : ModularPanel("backpack_settings") { - companion object { - private const val HEIGHT: Int = 95 - private val LAYERED_TAB_TEXTURE = UITexture.builder() - .location(Tags.MOD_ID, "gui/gui_controls") - .imageSize(256, 256) - .xy(128, 0, 124, 256) - .adaptable(4) - .tiled() - .build() as AdaptableUITexture - } - - private val memoryTab: TabWidget - private val sortTab: TabWidget - - init { - size(parent.area.width, HEIGHT) - .relative(parent) - .bottom(0) - - memoryTab = TabWidget(0, expandDirection = ExpandDirection.LEFT) - .tooltipStatic { - it.addLine(IKey.lang("gui.memory_settings".asTranslationKey())) - .pos(RichTooltip.Pos.NEXT_TO_MOUSE) - } - memoryTab.expandedWidget = MemorySettingWidget(parent, this, memoryTab) - memoryTab.tabIcon = RSBTextures.BRAIN_ICON - - sortTab = TabWidget(1, expandDirection = ExpandDirection.LEFT) - .tooltipStatic { - it.addLine(IKey.lang("gui.sorting_settings".asTranslationKey())) - .pos(RichTooltip.Pos.NEXT_TO_MOUSE) - } - sortTab.expandedWidget = SortingSettingWidget(parent, this, sortTab) - sortTab.tabIcon = RSBTextures.NO_SORT_ICON - - val grid = Column() - .size(parent.area.width - 14, HEIGHT - 14) - .margin(7) - .child(TextWidget(IKey.lang("gui.configuration_tab".asTranslationKey())).leftRel(0.5f)) - - child(grid) - .child(memoryTab) - .child(sortTab) - } - - internal fun updateTabState(fromIndex: Int) { - memoryTab.isEnabled = true - sortTab.isEnabled = true - - when (fromIndex) { - 0 -> { - sortTab.showExpanded = false - parent.isSortingSettingTabOpened = false - sortTab.isEnabled = !memoryTab.showExpanded - } - - 1 -> { - memoryTab.showExpanded = false - parent.isMemorySettingTabOpened = false - } - } - } - - override fun shouldAnimate(): Boolean = - ClientConfig.enableAnimation && super.shouldAnimate() - - override fun isDraggable(): Boolean = - false - - override fun onOpen(screen: ModularScreen) { - super.onOpen(screen) - parent.isMemorySettingTabOpened = memoryTab.showExpanded - parent.shouldMemorizeRespectNBT = (memoryTab.expandedWidget as MemorySettingWidget).isRespectNBT() - parent.isSortingSettingTabOpened = sortTab.showExpanded - } - - override fun onClose() { - super.onClose() - parent.isMemorySettingTabOpened = false - parent.shouldMemorizeRespectNBT = false - parent.isSortingSettingTabOpened = false - } - - override fun postDraw(context: ModularGuiContext, transformed: Boolean) { - super.postDraw(context, transformed) - - // Nasty hack to draw over upgrade tabs - LAYERED_TAB_TEXTURE.draw(context, 0, 0, 6, area.height, WidgetTheme.getDefault().theme) - } -} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/RSBTextures.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/RSBTextures.kt index 76acc9f..461020a 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/RSBTextures.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/RSBTextures.kt @@ -22,6 +22,7 @@ object RSBTextures { val MATCH_DURABILITY_ICON = icon("consider_duration", 0, 16) val IGNORE_DURABILITY_ICON = icon("ignore_duration", 16, 16) + val MATCH_BACKPACK_CONTENTS_ICON = icon("match_backpack_contents", 80, 16) val HALF_HEART_ICON = icon("half_heart", 96, 16) val IGNORE_HALF_HEART_ICON = icon("ignore_half_heart", 112, 16) @@ -32,15 +33,52 @@ object RSBTextures { val IN_OUT_ICON = icon("in_out", 0, 32) val IN_ICON = icon("in", 16, 32) val OUT_ICON = icon("out", 32, 32) + val PUMP_INPUT_ICON = icon("pump_input", 144, 0) + val PUMP_OUTPUT_ICON = icon("pump_output", 160, 0) + val PUMP_WORLD_ICON = icon("pump_world", 176, 0) + val PUMP_NO_WORLD_ICON = icon("pump_no_world", 192, 0) + val PUMP_HAND_ICON = icon("pump_hand", 208, 0) + val PUMP_NO_HAND_ICON = icon("pump_no_hand", 224, 0) + val PUMP_FLUID_HANDLER_ICON = icon("pump_fluid_handler", 0, 112) + val PUMP_NO_FLUID_HANDLER_ICON = icon("pump_no_fluid_handler", 16, 112) val ADD_ICON = icon("add", 96, 32) val REMOVE_ICON = icon("remove", 112, 32) val BRAIN_ICON = icon("brain", 128, 32) + val WORK_IN_GUI_ON_ICON = icon("works_in_gui", 0, 48) + val WORK_IN_GUI_OFF_ICON = icon("only_automatic", 16, 48) + val COMPACT_ANYTHING_ICON = icon("compact_anything", 80, 32) + val COMPACT_ONLY_UNCRAFTABLE_ICON = icon("compact_only_uncraftable", 80, 48) + val MAGNET_PICKUP_ITEMS_ICON = icon("pickup_items", 128, 48) + val MAGNET_NO_PICKUP_ITEMS_ICON = icon("do_not_pickup_items", 144, 48) + val VOID_ALWAYS_ICON = icon("void_always", 208, 16) + val VOID_SLOT_OVERFLOW_ICON = icon("void_slot_overflow", 224, 16) + val VOID_STORAGE_OVERFLOW_ICON = icon("void_storage_overflow", 112, 96) + val JUKEBOX_STOP_ICON = icon("jukebox_stop", 0, 64) + val JUKEBOX_PLAY_ICON = icon("jukebox_play", 16, 64) + val JUKEBOX_SHUFFLE_ON_ICON = icon("jukebox_shuffle_on", 96, 80) + val JUKEBOX_SHUFFLE_OFF_ICON = icon("jukebox_shuffle_off", 112, 80) + val JUKEBOX_REPEAT_ALL_ICON = icon("jukebox_repeat_all", 128, 80) + val JUKEBOX_REPEAT_ONE_ICON = icon("jukebox_repeat_one", 144, 80) + val JUKEBOX_NO_REPEAT_ICON = icon("jukebox_no_repeat", 160, 80) + val JUKEBOX_NEXT_ICON = icon("jukebox_next", 32, 96) + val JUKEBOX_PREVIOUS_ICON = icon("jukebox_previous", 48, 96) + val TOOL_SWAPPER_SWAP_WEAPON_ICON = icon("tool_swapper_swap_weapon", 32, 64) + val TOOL_SWAPPER_DO_NOT_SWAP_WEAPON_ICON = icon("tool_swapper_do_not_swap_weapon", 48, 64) + val TOOL_SWAPPER_SWAP_TOOLS_ICON = icon("tool_swapper_swap_tools", 64, 64) + val TOOL_SWAPPER_ONLY_TOOLS_ICON = icon("tool_swapper_only_tools", 80, 64) + val TOOL_SWAPPER_NO_SWAP_ICON = icon("tool_swapper_no_swap", 96, 64) + val ANVIL_NAME_BACKGROUND = controlIcon(28, 99, 100, 16) + val ANVIL_NAME_BACKGROUND_DISABLED = controlIcon(28, 115, 100, 16) + val ANVIL_PLUS_SIGN = controlIcon(113, 203, 13, 13) + val ANVIL_ARROW = controlIcon(56, 221, 14, 15) + val ANVIL_RED_CROSS = controlIcon(113, 216, 15, 15) val ONE_IN_FOUR_SLOT_ICON = icon("one_in_four_slot", 0, 80) val ALL_FOUR_SLOT_ICON = icon("all_in_four_slot", 16, 80) val NO_SORT_ICON = icon("no_sort", 32, 80) val NONE_FOUR_SLOT_ICON = icon("none_in_four_slot", 48, 80) + val BACK_TO_BACKPACK_ICON = icon("back_to_backpack", 64, 80) val SETTING_ICON = icon("setting", 16, 96) @@ -62,6 +100,12 @@ object RSBTextures { val LEFT_ARROW_ICON = icon("left_arrow", 32, 48) val DOWN_ARROW_ICON = icon("down_arrow", 48, 48) + val BACKPACK_SETTINGS_ICON = icon("backpack_settings", 64, 48) + val ITEM_DISPLAY_SETTINGS_ICON = icon("item_display_settings", 112, 64) + val ITEM_DISPLAY_ROTATE_ICON = icon("item_display_rotate", 128, 64) + val DISPLAY_SIDE_FRONT_ICON = icon("display_side_front", 144, 64) + val DISPLAY_SIDE_LEFT_ICON = icon("display_side_left", 160, 64) + val DISPLAY_SIDE_RIGHT_ICON = icon("display_side_right", 176, 64) val STANDARD_BUTTON = UITexture.builder() .location(Tags.MOD_ID, "gui/gui_controls.png") @@ -73,6 +117,22 @@ object RSBTextures { .imageSize(256, 256) .xy(47, 0, 18, 18) .build() + val SMALL_BUTTON = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(29, 18, 12, 12) + .build() + val SMALL_BUTTON_HOVERED = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(41, 18, 12, 12) + .build() + val CONTEXT_BUTTON_LEFT = buttonPiece(29, 0, 16, 18) + val CONTEXT_BUTTON_LEFT_HOVERED = buttonPiece(47, 0, 16, 18) + val CONTEXT_BUTTON_MIDDLE = buttonPiece(31, 0, 14, 18) + val CONTEXT_BUTTON_MIDDLE_HOVERED = buttonPiece(49, 0, 14, 18) + val CONTEXT_BUTTON_RIGHT = buttonPiece(31, 0, 16, 18) + val CONTEXT_BUTTON_RIGHT_HOVERED = buttonPiece(49, 0, 16, 18) val BIG_SLOT_TEXTURE = UITexture.builder() .location(Tags.MOD_ID, "gui/gui_controls.png") @@ -85,6 +145,74 @@ object RSBTextures { .imageSize(256, 256) .xy(97, 209, 16, 16) .build() + val TANK_ARROW = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(97, 216, 15, 8) + .build() + val MEMORIZED_SLOT_OVERLAY = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(77, 0, 16, 16) + .build() + val SLOT_SELECTION = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(93, 0, 24, 24) + .build() + val SLOT_BACKGROUND = UITexture.builder() + .location(Tags.MOD_ID, "gui/slots_background.png") + .imageSize(256, 256) + .xy(0, 0, 18, 18) + .build() + val SLOTS_BACKGROUND = UITexture.fullImage(Tags.MOD_ID, "gui/slots_background.png") + val STORAGE_BACKGROUND_9 = UITexture.fullImage(Tags.MOD_ID, "gui/storage_background_9.png") + val STORAGE_BACKGROUND_9_WIDER = UITexture.fullImage(Tags.MOD_ID, "gui/storage_background_9_wider.png") + val STORAGE_BACKGROUND_12 = UITexture.fullImage(Tags.MOD_ID, "gui/storage_background_12.png") + val STORAGE_BACKGROUND_12_WIDER = UITexture.fullImage(Tags.MOD_ID, "gui/storage_background_12_wider.png") + val EMPTY_TANK_INPUT_SLOT = UITexture.fullImage(Tags.MOD_ID, "item/empty_tank_input_slot.png") + val EMPTY_TANK_OUTPUT_SLOT = UITexture.fullImage(Tags.MOD_ID, "item/empty_tank_output_slot.png") + val EMPTY_BATTERY_INPUT_SLOT = UITexture.fullImage(Tags.MOD_ID, "item/empty_battery_input_slot.png") + val EMPTY_BATTERY_OUTPUT_SLOT = UITexture.fullImage(Tags.MOD_ID, "item/empty_battery_output_slot.png") + val EMPTY_UPGRADE_SLOT = UITexture.fullImage(Tags.MOD_ID, "item/empty_upgrade_slot.png") + + val TANK_OVERLAY = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(47, 30, 16, 18) + .build() + val BATTERY_OVERLAY = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(47, 56, 16, 18) + .build() + val BATTERY_CONNECTION_TOP = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(47, 48, 16, 4) + .build() + val BATTERY_CONNECTION_BOTTOM = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(47, 52, 16, 4) + .build() + val BATTERY_CHARGE = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(47, 74, 16, 6) + .build() + val BAR_BACKGROUND_TOP = barBackground(29, 30) + val BAR_BACKGROUND_MIDDLE = barBackground(29, 48) + val BAR_BACKGROUND_BOTTOM = barBackground(29, 66) + val SHIFT_CLICK_OPEN_TAB_ON = icon("shift_click_open_tab_on", 80, 32) + val SHIFT_CLICK_OPEN_TAB_OFF = icon("shift_click_open_tab_off", 64, 96) + val KEEP_TAB_OPEN_ON = icon("keep_tab_open_on", 80, 80) + val KEEP_TAB_OPEN_OFF = icon("keep_tab_open_off", 80, 96) + val KEEP_SEARCH_PHRASE_ON = icon("keep_search_phrase_on", 208, 32) + val KEEP_SEARCH_PHRASE_OFF = icon("keep_search_phrase_off", 224, 32) + val SEARCH_ICON = icon("search", 208, 32) + val ANOTHER_PLAYER_CAN_OPEN_ON = icon("another_player_can_open_on", 176, 32) + val ANOTHER_PLAYER_CAN_OPEN_OFF = icon("another_player_can_open_off", 192, 32) private fun icon(name: String, x: Int, y: Int, w: Int = 16, h: Int = 16): UITexture = UITexture.builder() @@ -93,4 +221,25 @@ object RSBTextures { .xy(x, y, w, h) .name(name) .build() + + private fun barBackground(x: Int, y: Int): UITexture = + UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(x, y, 18, 18) + .build() + + private fun buttonPiece(x: Int, y: Int, w: Int, h: Int): UITexture = + UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(x, y, w, h) + .build() + + private fun controlIcon(x: Int, y: Int, w: Int = 16, h: Int = 16): UITexture = + UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(x, y, w, h) + .build() } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/UpgradeSlotUpdateGroup.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/UpgradeSlotUpdateGroup.kt index 2c6da7a..77e1aee 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/UpgradeSlotUpdateGroup.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/UpgradeSlotUpdateGroup.kt @@ -8,6 +8,10 @@ import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.CraftingUpgradeWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IAdvancedFilterable import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IBasicFilterable +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AnvilUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.BatteryUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.JukeboxUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.TankUpgradeWrapper import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.CraftingSlotInfo import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.IndexedModularCraftingSlot import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularFilterSlot @@ -39,6 +43,18 @@ class UpgradeSlotUpdateGroup( val craftingInfo: CraftingSlotInfo + var jukeboxDiscStackHandler = DelegatedStackHandlerSH(wrapper, slotIndex, 12) + val jukeboxDiscSlots: Array + + var tankStackHandler = DelegatedStackHandlerSH(wrapper, slotIndex, 4) + val tankSlots: Array + + var batteryStackHandler = DelegatedStackHandlerSH(wrapper, slotIndex, 2) + val batterySlots: Array + + var anvilStackHandler = DelegatedStackHandlerSH(wrapper, slotIndex, 2) + val anvilSlots: Array + init { val syncManager = panel.syncManager @@ -139,6 +155,57 @@ class UpgradeSlotUpdateGroup( craftingInfo = CraftingSlotInfo(craftingMatrixSlots, craftingOutputSlot) syncManager.registerSlotGroup(SlotGroup("crafting_result_$slotIndex", 1, false)) + + syncManager.syncValue("jukebox_disc_delegation_$slotIndex", jukeboxDiscStackHandler) + jukeboxDiscSlots = Array(12) { + val slot = ModularSlot(jukeboxDiscStackHandler.delegatedStackHandler, it) + slot.slotGroup("jukebox_discs_$slotIndex") + + syncManager.syncValue( + "jukebox_discs_$slotIndex", + it, + ItemSlotSH(slot) + ) + + slot + } + syncManager.registerSlotGroup(SlotGroup("jukebox_discs_$slotIndex", 4, false)) + + syncManager.syncValue("tank_delegation_$slotIndex", tankStackHandler) + tankSlots = Array(4) { + val slot = ModularSlot(tankStackHandler.delegatedStackHandler, it) + slot.slotGroup("tank_$slotIndex") + if (it >= 2) { + slot.accessibility(false, true) + } + + syncManager.syncValue( + "tank_$slotIndex", + it, + ItemSlotSH(slot) + ) + + slot + } + syncManager.registerSlotGroup(SlotGroup("tank_$slotIndex", 2, false)) + + syncManager.syncValue("battery_delegation_$slotIndex", batteryStackHandler) + batterySlots = Array(2) { + val slot = ModularSlot(batteryStackHandler.delegatedStackHandler, it) + slot.slotGroup("battery_$slotIndex") + syncManager.syncValue("battery_$slotIndex", it, ItemSlotSH(slot)) + slot + } + syncManager.registerSlotGroup(SlotGroup("battery_$slotIndex", 2, false)) + + syncManager.syncValue("anvil_delegation_$slotIndex", anvilStackHandler) + anvilSlots = Array(2) { + val slot = ModularSlot(anvilStackHandler.delegatedStackHandler, it) + slot.slotGroup("anvil_$slotIndex") + syncManager.syncValue("anvil_$slotIndex", it, ItemSlotSH(slot)) + slot + } + syncManager.registerSlotGroup(SlotGroup("anvil_$slotIndex", 2, false)) } fun updateFilterDelegate(wrapper: IBasicFilterable) { @@ -151,9 +218,33 @@ class UpgradeSlotUpdateGroup( advancedCommonFilterStackHandler.syncToServer(DelegatedStackHandlerSH.UPDATE_FILTERABLE) } + fun updateLargeBasicFilterDelegate(wrapper: IBasicFilterable) { + advancedCommonFilterStackHandler.setDelegatedStackHandler(wrapper::filterItems) + advancedCommonFilterStackHandler.syncToServer(DelegatedStackHandlerSH.UPDATE_FILTERABLE) + } + fun updateCraftingDelegate(wrapper: CraftingUpgradeWrapper) { craftingStackHandler.setDelegatedStackHandler(wrapper::craftMatrix) craftingStackHandler.syncToServer(DelegatedCraftingStackHandlerSH.UPDATE_CRAFTING) } + fun updateJukeboxDelegate(wrapper: JukeboxUpgradeWrapper) { + jukeboxDiscStackHandler.setDelegatedStackHandler(wrapper::discInventory) + jukeboxDiscStackHandler.syncToServer(DelegatedStackHandlerSH.UPDATE_JUKEBOX) + } + + fun updateTankDelegate(wrapper: TankUpgradeWrapper) { + tankStackHandler.setDelegatedStackHandler(wrapper::getInventory) + tankStackHandler.syncToServer(DelegatedStackHandlerSH.UPDATE_TANK) + } + + fun updateBatteryDelegate(wrapper: BatteryUpgradeWrapper) { + batteryStackHandler.setDelegatedStackHandler(wrapper::getInventory) + batteryStackHandler.syncToServer(DelegatedStackHandlerSH.UPDATE_BATTERY) + } + + fun updateAnvilDelegate(wrapper: AnvilUpgradeWrapper) { + anvilStackHandler.setDelegatedStackHandler(wrapper::getInventory) + anvilStackHandler.syncToServer(DelegatedStackHandlerSH.UPDATE_ANVIL) + } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackToBackpackTabWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackToBackpackTabWidget.kt new file mode 100644 index 0000000..5704a8e --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackToBackpackTabWidget.kt @@ -0,0 +1,59 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.drawable.GuiTextures +import com.cleanroommc.modularui.drawable.TabTexture +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault + +class BackToBackpackTabWidget : Widget(), Interactable { + companion object { + val TAB_TEXTURE: TabTexture = GuiTextures.TAB_RIGHT + } + + init { + size(TAB_TEXTURE.width, TAB_TEXTURE.height) + .right(-TAB_TEXTURE.width + 4) + .top(TabWidget.TAB_TOP_OFFSET) + .background(TAB_TEXTURE.get(-1, false)) + .tooltipStatic { + it.addLine(IKey.lang("gui.back_to_backpack.tooltip".asTranslationKey())) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + } + + override fun onInit() { + context.recipeViewerSettings.addExclusionArea(this) + } + + override fun dispose() { + if (isValid) + context.recipeViewerSettings.removeExclusionArea(this) + super.dispose() + } + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (!isEnabled) + return Interactable.Result.STOP + + if (mouseButton == 0) { + Interactable.playButtonClickSound() + (panel as BackpackPanel).isSettingMode = false + return Interactable.Result.SUCCESS + } + + return Interactable.Result.IGNORE + } + + override fun draw(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.draw(context, widgetTheme) + RSBTextures.BACK_TO_BACKPACK_ICON.draw(context, 8, 6, 16, 16, widgetTheme.getThemeOrDefault()) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackpackInventoryScrollWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackpackInventoryScrollWidget.kt new file mode 100644 index 0000000..d9e219c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackpackInventoryScrollWidget.kt @@ -0,0 +1,93 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.widget.ScrollWidget +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.modularui.widget.scroll.VerticalScrollData +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.SlotGroupWidget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.BackpackSlot + +class BackpackInventoryScrollWidget(panel: BackpackPanel) : + ScrollWidget(VerticalScrollData(false, SCROLLBAR_WIDTH)) { + private val panel = panel + private var lastSearchLayoutVersion = panel.searchLayoutVersion + + init { + val scrollData = scrollArea.scrollY + scrollData.scrollSize = panel.colSize * SLOT_SIZE + scrollData.scrollSpeed = SLOT_SIZE + + size(panel.backpackSlotsWidth + panel.inventoryScrollbarWidth, panel.visibleColSize * SLOT_SIZE) + + child(createSlots(panel, panel.colSize)) + } + + override fun onUpdate() { + super.onUpdate() + val scrollData = scrollArea.scrollY + scrollData.scrollSize = panel.searchDisplayRows() * SLOT_SIZE + if (lastSearchLayoutVersion != panel.searchLayoutVersion) { + lastSearchLayoutVersion = panel.searchLayoutVersion + scrollData.scrollTo(scrollArea, 0) + } else { + scrollData.clamp(scrollArea) + } + } + + companion object { + const val SCROLLBAR_WIDTH = 6 + private const val SLOT_SIZE = 18 + + fun createSlots(panel: BackpackPanel, visibleRows: Int): SlotGroupWidget { + val slots = SlotGroupWidget().name("backpack_inventory").disableSortButtons() + slots.size(panel.backpackSlotsWidth, visibleRows * SLOT_SIZE) + slots.child(BackpackSlotBackgroundWidget(panel, visibleRows)) + for (i in 0 until panel.backpackWrapper.backpackInventorySize()) { + slots.child( + BackpackSlot(panel, panel.backpackWrapper) + .syncHandler("backpack", i) + .pos(i % panel.rowSize * SLOT_SIZE, i / panel.rowSize * SLOT_SIZE) + .name("slot_$i") + ) + } + slots.child( + MobCatcherInventoryControlWidget(panel) + .pos(0, 0) + .name("mob_catcher_inventory_control") + .setEnabledIf { !panel.isSettingMode && !panel.isSearchViewActive() } + ) + return slots + } + } + + private class BackpackSlotBackgroundWidget(private val panel: BackpackPanel, rows: Int) : + Widget() { + init { + size(panel.backpackSlotsWidth, rows * SLOT_SIZE) + } + + override fun canHover(): Boolean = false + + override fun canHoverThrough(): Boolean = true + + override fun canClickThrough(): Boolean = true + + override fun drawBackground(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + val theme = widgetTheme.theme + panel.updateSearchLayout() + for (i in 0 until panel.searchVisibleSlotCount()) { + RSBTextures.SLOT_BACKGROUND.draw( + context, + i % panel.rowSize * SLOT_SIZE, + i / panel.rowSize * SLOT_SIZE, + SLOT_SIZE, + SLOT_SIZE, + theme + ) + } + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackpackMainSettingsWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackpackMainSettingsWidget.kt new file mode 100644 index 0000000..c35d479 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BackpackMainSettingsWidget.kt @@ -0,0 +1,165 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.modularui.widgets.ButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.client.Minecraft +import net.minecraft.client.resources.I18n + +class BackpackMainSettingsWidget( + private val panel: BackpackPanel, + private val parentTabWidget: TabWidget +) : ExpandedTabWidget( + 3, + RSBTextures.BACKPACK_SETTINGS_ICON, + "gui.backpack_settings".asTranslationKey(), + width = if (Config.allowOpeningOtherPlayerBackpacks) 81 else 75, + tabHeight = 70, + expandDirection = ExpandDirection.RIGHT +) { + private val contextButton = ContextButtonWidget(panel.backpackWrapper) + .pos(4, 24) + .onMousePressed { + if (it == 0) { + panel.backpackWrapper.toggleSettingsContext() + panel.backpackSyncHandler.syncToServer(BackpackSH.UPDATE_TOGGLE_SETTINGS_CONTEXT) {} + Interactable.playButtonClickSound() + true + } else false + } + .tooltipAutoUpdate(true) + .tooltipDynamic { + val key = if (panel.backpackWrapper.settingsContext == BackpackWrapper.SettingsContext.PLAYER) + "gui.settings_button.context_player.tooltip" + else "gui.settings_button.context_backpack.tooltip" + it.addLine(IKey.lang(key.asTranslationKey())) + .addLine(IKey.lang("${key}_detail".asTranslationKey()).style(IKey.GRAY)) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + private val shiftClickButton = toggleButton( + 4, + 46, + { panel.backpackWrapper.shiftClickIntoOpenTab }, + RSBTextures.SHIFT_CLICK_OPEN_TAB_ON, + RSBTextures.SHIFT_CLICK_OPEN_TAB_OFF, + "shift_click_open_tab", + BackpackSH.UPDATE_TOGGLE_SHIFT_CLICK_INTO_OPEN_TAB, + panel.backpackWrapper::toggleShiftClickIntoOpenTab + ) + private val keepTabOpenButton = toggleButton( + 22, + 46, + { panel.backpackWrapper.keepTabOpen }, + RSBTextures.KEEP_TAB_OPEN_ON, + RSBTextures.KEEP_TAB_OPEN_OFF, + "keep_tab_open", + BackpackSH.UPDATE_TOGGLE_KEEP_TAB_OPEN, + panel.backpackWrapper::toggleKeepTabOpen + ) + private val keepSearchPhraseButton = toggleButton( + 40, + 46, + { panel.backpackWrapper.keepSearchPhrase }, + RSBTextures.KEEP_SEARCH_PHRASE_ON, + RSBTextures.KEEP_SEARCH_PHRASE_OFF, + "keep_search_phrase", + BackpackSH.UPDATE_TOGGLE_KEEP_SEARCH_PHRASE, + panel.backpackWrapper::toggleKeepSearchPhrase + ) + private val otherPlayerButton = toggleButton( + 58, + 46, + { panel.backpackWrapper.anotherPlayerCanOpen }, + RSBTextures.ANOTHER_PLAYER_CAN_OPEN_ON, + RSBTextures.ANOTHER_PLAYER_CAN_OPEN_OFF, + "another_player_can_open", + BackpackSH.UPDATE_TOGGLE_ANOTHER_PLAYER_CAN_OPEN, + panel.backpackWrapper::toggleAnotherPlayerCanOpen + ) + + init { + child(contextButton) + .child(shiftClickButton) + .child(keepTabOpenButton) + .child(keepSearchPhraseButton) + if (Config.allowOpeningOtherPlayerBackpacks) { + child(otherPlayerButton) + } + } + + override fun updateTabState() { + panel.openBackpackSettings(parentTabWidget, !parentTabWidget.showExpanded) + } + + private fun toggleButton( + x: Int, + y: Int, + state: () -> Boolean, + onIcon: IDrawable, + offIcon: IDrawable, + tooltipName: String, + syncId: Int, + toggle: () -> Unit + ): DynamicIconButtonWidget = + DynamicIconButtonWidget({ if (state()) onIcon else offIcon }) + .pos(x, y) + .size(18) + .onMousePressed { + if (it == 0) { + toggle() + panel.backpackSyncHandler.syncToServer(syncId) {} + Interactable.playButtonClickSound() + true + } else false + } + .tooltipAutoUpdate(true) + .tooltipDynamic { + val stateName = if (state()) "on" else "off" + it.addLine( + IKey.lang("gui.settings_button.$tooltipName.$stateName".asTranslationKey()) + ).addLine( + IKey.lang("gui.settings_button.$tooltipName.$stateName.tooltip".asTranslationKey()).style(IKey.GRAY) + ).pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + private class ContextButtonWidget(private val wrapper: BackpackWrapper) : ButtonWidget() { + init { + size(62, 18) + } + + override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + val theme = widgetTheme.getThemeOrDefault() + val left = if (isHovering) RSBTextures.CONTEXT_BUTTON_LEFT_HOVERED else RSBTextures.CONTEXT_BUTTON_LEFT + val middle = + if (isHovering) RSBTextures.CONTEXT_BUTTON_MIDDLE_HOVERED else RSBTextures.CONTEXT_BUTTON_MIDDLE + val right = if (isHovering) RSBTextures.CONTEXT_BUTTON_RIGHT_HOVERED else RSBTextures.CONTEXT_BUTTON_RIGHT + left.draw(context, 0, 0, 16, 18, theme) + middle.draw(context, 16, 0, 14, 18, theme) + middle.draw(context, 30, 0, 14, 18, theme) + right.draw(context, 44, 0, 16, 18, theme) + } + + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.drawOverlay(context, widgetTheme) + val textKey = if (wrapper.settingsContext == BackpackWrapper.SettingsContext.PLAYER) + "gui.settings_button.context_player" + else "gui.settings_button.context_backpack" + val text = I18n.format(textKey.asTranslationKey()) + val font = Minecraft.getMinecraft().fontRenderer + font.drawStringWithShadow(text, (area.width - font.getStringWidth(text)) / 2f, 5f, 0xFFFFFF) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BatteryInventoryControlWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BatteryInventoryControlWidget.kt new file mode 100644 index 0000000..d4c6643 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/BatteryInventoryControlWidget.kt @@ -0,0 +1,109 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.BatteryUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault + +class BatteryInventoryControlWidget( + private val upgradeSlot: Int, + private val backpackWrapper: BackpackWrapper, + height: Int +) : Widget() { + companion object { + const val WIDTH = 36 + private const val BATTERY_LEFT = 9 + private const val BATTERY_WIDTH = 18 + private const val OVERLAY_LEFT = BATTERY_LEFT + 1 + private const val OVERLAY_WIDTH = 16 + private const val CHARGE_SEGMENT_HEIGHT = 6 + private const val TOP_BAR_COLOR = 0xFF1A1A + private const val BOTTOM_BAR_COLOR = 0xFFFF40 + } + + init { + size(WIDTH, height) + tooltipAutoUpdate(true) + tooltipDynamic { + val wrapper = currentWrapper() + it.addLine(IKey.str("${wrapper?.energyStored ?: 0}/${wrapper?.getMaxEnergyStored(backpackWrapper) ?: 0} FE")) + it.pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + val theme = widgetTheme.getThemeOrDefault() + val height = area.height + RSBTextures.BAR_BACKGROUND_TOP.draw(context, BATTERY_LEFT, 0, BATTERY_WIDTH, if (height < 36) height / 2 else 18, theme) + var yOffset = 18 + repeat((height - 36).coerceAtLeast(0) / 18) { + RSBTextures.BAR_BACKGROUND_MIDDLE.draw(context, BATTERY_LEFT, yOffset, BATTERY_WIDTH, 18, theme) + yOffset += 18 + } + RSBTextures.BAR_BACKGROUND_BOTTOM.draw( + context, + BATTERY_LEFT, + if (height < 36) height / 2 else yOffset, + BATTERY_WIDTH, + if (height < 36) height / 2 else 18, + theme + ) + yOffset = 0 + repeat(height / 18) { + RSBTextures.BATTERY_OVERLAY.draw(context, OVERLAY_LEFT, yOffset, OVERLAY_WIDTH, 18, theme) + yOffset += 18 + } + renderCharge(context, currentWrapper(), height, theme) + RSBTextures.BATTERY_CONNECTION_TOP.draw(context, OVERLAY_LEFT, 0, OVERLAY_WIDTH, 4, theme) + RSBTextures.BATTERY_CONNECTION_BOTTOM.draw(context, OVERLAY_LEFT, height - 4, OVERLAY_WIDTH, 4, theme) + } + + private fun currentWrapper(): BatteryUpgradeWrapper? = + backpackWrapper.upgradeItemStackHandler.inventory[upgradeSlot] + .getCapability(Capabilities.BATTERY_UPGRADE_CAPABILITY, null) + + private fun renderCharge( + context: ModularGuiContext, + wrapper: BatteryUpgradeWrapper?, + height: Int, + theme: com.cleanroommc.modularui.theme.WidgetTheme + ) { + val max = wrapper?.getMaxEnergyStored(backpackWrapper) ?: 0 + if (wrapper == null || max <= 0 || wrapper.energyStored <= 0) { + return + } + val numberOfSegments = height / CHARGE_SEGMENT_HEIGHT + val displayLevel = (numberOfSegments * (wrapper.energyStored.toFloat() / max)).toInt() + if (displayLevel <= 0) { + return + } + + val topRed = TOP_BAR_COLOR shr 16 and 255 + val topGreen = TOP_BAR_COLOR shr 8 and 255 + val topBlue = TOP_BAR_COLOR and 255 + val bottomRed = BOTTOM_BAR_COLOR shr 16 and 255 + val bottomGreen = BOTTOM_BAR_COLOR shr 8 and 255 + val bottomBlue = BOTTOM_BAR_COLOR and 255 + + for (segmentIndex in 0 until displayLevel) { + val percentage = if (numberOfSegments <= 1) 0f else segmentIndex.toFloat() / (numberOfSegments - 1) + val red = (bottomRed * (1 - percentage) + topRed * percentage).toInt() + val green = (bottomGreen * (1 - percentage) + topGreen * percentage).toInt() + val blue = (bottomBlue * (1 - percentage) + topBlue * percentage).toInt() + RSBTextures.BATTERY_CHARGE.withColorOverride((255 shl 24) or (red shl 16) or (green shl 8) or blue).draw( + context, + OVERLAY_LEFT, + height - (segmentIndex + 1) * CHARGE_SEGMENT_HEIGHT, + OVERLAY_WIDTH, + CHARGE_SEGMENT_HEIGHT, + theme + ) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/CyclicVariantButtonWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/CyclicVariantButtonWidget.kt index a519dd4..401cd10 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/CyclicVariantButtonWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/CyclicVariantButtonWidget.kt @@ -9,6 +9,7 @@ import com.cleanroommc.modularui.widgets.ButtonWidget import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.client.renderer.GlStateManager class CyclicVariantButtonWidget( private val variants: List, @@ -26,6 +27,11 @@ class CyclicVariantButtonWidget( private set var inEffect: Boolean = true + fun selectIndex(index: Int) { + this.index = index.coerceIn(0, variants.lastIndex) + markTooltipDirty() + } + init { size(buttonWidth, buttonHeight) .onMousePressed { @@ -35,8 +41,12 @@ class CyclicVariantButtonWidget( mousePressedUpdater(this.index) markTooltipDirty() true - }.tooltipDynamic { + }.tooltipAutoUpdate(true) + .tooltipDynamic { it.addLine(variants[this.index].name) + for (detailLine in variants[this.index].detailLines) { + it.addLine(detailLine) + } if (!inEffect) { it.addLine(IKey.lang("gui.not_in_effect".asTranslationKey()).style(IKey.RED)) @@ -46,15 +56,18 @@ class CyclicVariantButtonWidget( } } - override fun draw(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { - if (hasCustomTexture) { - if (isHovering) { - hoveredTexture.draw(context, 0, 0, buttonWidth, buttonHeight, widgetTheme.getThemeOrDefault()) - } else { - notHoveredTexture.draw(context, 0, 0, buttonWidth, buttonHeight, widgetTheme.getThemeOrDefault()) + override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + if (hasCustomTexture || buttonWidth != 20 || buttonHeight != 20) { + val texture = when { + buttonWidth == 12 && buttonHeight == 12 && isHovering -> RSBTextures.SMALL_BUTTON_HOVERED + buttonWidth == 12 && buttonHeight == 12 -> RSBTextures.SMALL_BUTTON + isHovering -> hoveredTexture + else -> notHoveredTexture } + texture.draw(context, 0, 0, buttonWidth, buttonHeight, widgetTheme.getThemeOrDefault()) + } else { + super.drawBackground(context, widgetTheme) } - super.draw(context, widgetTheme) } override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { @@ -62,9 +75,11 @@ class CyclicVariantButtonWidget( val drawable = variants[index].drawable context?.let { + GlStateManager.color(1f, 1f, 1f, 1f) drawable.draw(context, iconOffset, iconOffset, iconSize, iconSize, widgetTheme.getThemeOrDefault()) + GlStateManager.color(1f, 1f, 1f, 1f) } } - data class Variant(val name: IKey, val drawable: IDrawable) -} \ No newline at end of file + data class Variant(val name: IKey, val drawable: IDrawable, val detailLines: List = emptyList()) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/DynamicIconButtonWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/DynamicIconButtonWidget.kt new file mode 100644 index 0000000..0ee1543 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/DynamicIconButtonWidget.kt @@ -0,0 +1,27 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.ButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.client.renderer.GlStateManager + +class DynamicIconButtonWidget( + private val icon: () -> IDrawable, + private val iconOffset: Int = 1, + private val iconSize: Int = 16 +) : ButtonWidget() { + override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + val theme = widgetTheme.getThemeOrDefault() + if (isHovering) RSBTextures.STANDARD_BUTTON_HOVERED.draw(context, 0, 0, 18, 18, theme) + else RSBTextures.STANDARD_BUTTON.draw(context, 0, 0, 18, 18, theme) + } + + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + GlStateManager.color(1f, 1f, 1f, 1f) + icon().draw(context, iconOffset, iconOffset, iconSize, iconSize, widgetTheme.getThemeOrDefault()) + GlStateManager.color(1f, 1f, 1f, 1f) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ExpandedTabWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ExpandedTabWidget.kt index 74aef4a..527de44 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ExpandedTabWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ExpandedTabWidget.kt @@ -17,6 +17,7 @@ abstract class ExpandedTabWidget( delegatedIcon: IDrawable, titleKey: String, width: Int = 75, + tabHeight: Int = coveredTabSize * 30, private val expandDirection: ExpandDirection = ExpandDirection.RIGHT ) : ParentWidget() { companion object { @@ -42,8 +43,8 @@ abstract class ExpandedTabWidget( right(0) upperTabRow - .child(Widget().width(4).name("placeholder")) - .child(titleKeyWidget) + .width(width - 3) + .child(titleKeyWidget.expanded()) .child(phantomTabWidget) } @@ -58,27 +59,37 @@ abstract class ExpandedTabWidget( } width(width) - .height(coveredTabSize * 30) + .height(tabHeight) .background(TAB_TEXTURE) - .child(upperTabRow) + child(upperTabRow) } abstract fun updateTabState() + override fun onInit() { + context.recipeViewerSettings.addExclusionArea(this) + } + + override fun dispose() { + if (isValid) + context.recipeViewerSettings.removeExclusionArea(this) + super.dispose() + } + protected inner class PhantomTabWidget(tabIcon: Widget<*>) : SingleChildWidget(), Interactable { init { - size(32, 28) + size(24, 28) tabIcon.size(16).top(6) when (expandDirection) { ExpandDirection.LEFT -> { right(0) - tabIcon.right(8) + tabIcon.right(4) } ExpandDirection.RIGHT -> { - tabIcon.left(8) + tabIcon.left(4) } } @@ -98,4 +109,4 @@ abstract class ExpandedTabWidget( return Interactable.Result.STOP } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ItemDisplaySettingsWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ItemDisplaySettingsWidget.kt new file mode 100644 index 0000000..91a13f0 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/ItemDisplaySettingsWidget.kt @@ -0,0 +1,125 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.ButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.backpack.DisplaySide +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSH +import com.cleanroommc.retrosophisticatedbackpacks.util.DyeColorUtils +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.gui.Gui +import net.minecraft.item.EnumDyeColor + +class ItemDisplaySettingsWidget( + private val panel: BackpackPanel, + private val parentTabWidget: TabWidget +) : ExpandedTabWidget( + 2, + RSBTextures.ITEM_DISPLAY_SETTINGS_ICON, + "gui.item_display_settings".asTranslationKey(), + width = 75, + tabHeight = 48, + expandDirection = ExpandDirection.RIGHT +) { + private val rotateButton = DynamicIconButtonWidget({ RSBTextures.ITEM_DISPLAY_ROTATE_ICON }) + .pos(3, 24) + .size(18) + .onMousePressed { + val slot = panel.currentItemDisplaySelectedSlot + if (slot < 0) { + return@onMousePressed false + } + val clockwise = it != 1 + panel.backpackWrapper.rotateItemDisplaySlot(slot, clockwise) + panel.backpackSyncHandler.syncToServer(BackpackSH.UPDATE_ITEM_DISPLAY_ROTATION) { buf -> + buf.writeInt(slot) + buf.writeBoolean(clockwise) + } + Interactable.playButtonClickSound() + true + } + .tooltipStatic { + it.addLine(IKey.lang("gui.settings_button.rotate".asTranslationKey())) + .addLine(IKey.lang("gui.settings_button.rotate_detail".asTranslationKey()).style(IKey.GRAY)) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + private val colorButton = ColorToggleButton() + .pos(21, 24) + .onMousePressed { + if (it != 0 && it != 1) { + return@onMousePressed false + } + val colors = EnumDyeColor.entries + val next = colors[(panel.backpackWrapper.itemDisplayColor.ordinal + if (it == 0) 1 else colors.size - 1) % colors.size] + panel.backpackWrapper.itemDisplayColor = next + panel.backpackSyncHandler.syncToServer(BackpackSH.UPDATE_ITEM_DISPLAY_COLOR) { buf -> + buf.writeEnumValue(next) + } + Interactable.playButtonClickSound() + true + } + .tooltipAutoUpdate(true) + .tooltipDynamic { + it.addLine(IKey.lang("gui.settings_button.item_display_color".asTranslationKey())) + .addLine(IKey.lang("gui.settings_button.item_display_color_detail".asTranslationKey()).style(IKey.GRAY)) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + private val sideButton = DynamicIconButtonWidget({ + when (panel.backpackWrapper.itemDisplaySide) { + DisplaySide.FRONT -> RSBTextures.DISPLAY_SIDE_FRONT_ICON + DisplaySide.LEFT -> RSBTextures.DISPLAY_SIDE_LEFT_ICON + DisplaySide.RIGHT -> RSBTextures.DISPLAY_SIDE_RIGHT_ICON + } + }) + .pos(39, 24) + .size(18) + .onMousePressed { + if (it != 0 && it != 1) { + return@onMousePressed false + } + val next = if (it == 0) panel.backpackWrapper.itemDisplaySide.next() + else panel.backpackWrapper.itemDisplaySide.previous() + panel.backpackWrapper.itemDisplaySide = next + panel.backpackSyncHandler.syncToServer(BackpackSH.UPDATE_ITEM_DISPLAY_SIDE) { buf -> + buf.writeEnumValue(next) + } + Interactable.playButtonClickSound() + true + } + .tooltipAutoUpdate(true) + .tooltipDynamic { + it.addLine( + IKey.lang("gui.settings_button.display_side_${panel.backpackWrapper.itemDisplaySide.serializedName}".asTranslationKey()) + ).addLine(IKey.lang("gui.settings_button.display_side_detail".asTranslationKey()).style(IKey.GRAY)) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + init { + child(rotateButton) + .child(colorButton) + .child(sideButton) + } + + override fun updateTabState() { + panel.openItemDisplaySettings(parentTabWidget, !parentTabWidget.showExpanded) + } + + private inner class ColorToggleButton : ButtonWidget() { + init { + size(18, 18) + } + + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.drawOverlay(context, widgetTheme) + val color = DyeColorUtils.colorValue(this@ItemDisplaySettingsWidget.panel.backpackWrapper.itemDisplayColor) + Gui.drawRect(4, 4, 14, 14, color or -0x1000000) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MemorySettingWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MemorySettingWidget.kt index b803760..276b1f4 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MemorySettingWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MemorySettingWidget.kt @@ -3,9 +3,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets import com.cleanroommc.modularui.api.drawable.IKey import com.cleanroommc.modularui.screen.RichTooltip import com.cleanroommc.modularui.widgets.ButtonWidget -import com.cleanroommc.modularui.widgets.layout.Row import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackSettingPanel import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSlotSH import com.cleanroommc.retrosophisticatedbackpacks.util.Utils @@ -13,14 +11,14 @@ import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey class MemorySettingWidget( private val panel: BackpackPanel, - private val settingPanel: BackpackSettingPanel, private val parentTabWidget: TabWidget ) : ExpandedTabWidget( 2, RSBTextures.BRAIN_ICON, "gui.memory_settings".asTranslationKey(), width = 75, - expandDirection = ExpandDirection.LEFT + tabHeight = 48, + expandDirection = ExpandDirection.RIGHT ) { companion object { private val RESPECT_NBT_VARIANTS = listOf( @@ -35,14 +33,9 @@ class MemorySettingWidget( ) } - private val buttonRow: Row = Row() - .leftRel(0.5f) - .height(20) - .coverChildrenWidth() - .childPadding(2) as Row - private val memorizeAllButton: ButtonWidget<*> = ButtonWidget() - .size(20) + .pos(3, 24) + .size(18) .overlay(RSBTextures.ALL_FOUR_SLOT_ICON) .onMousePressed { if (it == 0) { @@ -70,7 +63,8 @@ class MemorySettingWidget( } private val unmemorizeAllButton: ButtonWidget<*> = ButtonWidget() - .size(20) + .pos(21, 24) + .size(18) .overlay(RSBTextures.NONE_FOUR_SLOT_ICON) .onMousePressed { if (it == 0) { @@ -96,25 +90,20 @@ class MemorySettingWidget( } private val respectNBTButton: CyclicVariantButtonWidget = CyclicVariantButtonWidget( RESPECT_NBT_VARIANTS - ) { - this@MemorySettingWidget.panel.shouldMemorizeRespectNBT = it != 0 - } + ) { this@MemorySettingWidget.panel.shouldMemorizeRespectNBT = it != 0 } + .pos(39, 24) + .size(18) init { - buttonRow.top(28) - .child(memorizeAllButton) + child(memorizeAllButton) .child(unmemorizeAllButton) .child(respectNBTButton) - - child(buttonRow) } fun isRespectNBT(): Boolean = respectNBTButton.index != 0 override fun updateTabState() { - parentTabWidget.showExpanded = !parentTabWidget.showExpanded - panel.isMemorySettingTabOpened = parentTabWidget.showExpanded - settingPanel.updateTabState(0) + panel.openMemorySettings(parentTabWidget, !parentTabWidget.showExpanded) } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MobCatcherInventoryControlWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MobCatcherInventoryControlWidget.kt new file mode 100644 index 0000000..8fd9e88 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/MobCatcherInventoryControlWidget.kt @@ -0,0 +1,536 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.layout.IViewportStack +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.drawable.GuiDraw +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.retrosophisticatedbackpacks.Tags +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.CapturedMob +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherStorage +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.handler.NetworkHandler +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SMobCatcherReleasePacket +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.renderer.RenderHelper +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.EntityList +import net.minecraft.util.ResourceLocation +import net.minecraft.util.text.TextFormatting +import org.lwjgl.opengl.GL11 +import kotlin.math.floor +import kotlin.math.max +import kotlin.math.min +import kotlin.math.sqrt + +class MobCatcherInventoryControlWidget(private val panel: BackpackPanel) : + Widget(), Interactable { + companion object { + private const val SLOT_SIZE = 18 + private const val CAPTURED_MOB_SLOT_OFFSET = 1 + private val GUI_CONTROLS = ResourceLocation(Tags.MOD_ID, "textures/gui/gui_controls.png") + private const val GUI_CONTROLS_TEXTURE_WIDTH = 256 + private const val GUI_CONTROLS_TEXTURE_HEIGHT = 256 + private const val CAPTURED_MOB_BACKGROUND_U = 29 + private const val CAPTURED_MOB_BACKGROUND_V = 30 + private const val CAPTURED_MOB_BACKGROUND_WIDTH = 18 + private const val CAPTURED_MOB_BACKGROUND_HEIGHT = 54 + private const val CAPTURED_MOB_BACKGROUND_COLOR = 0xFF2B2B2B.toInt() + private const val CAPTURED_MOB_BACKGROUND_LAYER_COLOR = 0x184A4A4A + private const val BODY_YAW_RANGE = 50f + private const val HEAD_STATIC_YAW_RANGE = 24f + private const val HEAD_IDLE_YAW_AMPLITUDE = 33f + private const val HEAD_STATIC_PITCH_RANGE = 6f + private const val HEAD_IDLE_PITCH_AMPLITUDE = 10f + private const val HEAD_IDLE_MIN_CYCLE_TICKS = 340f + private const val HEAD_IDLE_CYCLE_VARIATION_TICKS = 180f + private const val HEAD_IDLE_MOVE_TICKS = 30f + private const val HEAD_IDLE_HOLD_TICKS = 15f + } + + private val renderEntities = mutableMapOf() + + init { + size(panel.backpackSlotsWidth, panel.colSize * SLOT_SIZE) + tooltipAutoUpdate(true) + tooltipDynamic { tooltip -> + val hovered = hoveredCapturedMob(getContext().mouseX, getContext().mouseY) ?: return@tooltipDynamic + val entity = renderEntity(hovered) + tooltip.addLine(IKey.str(tooltipDisplayName(hovered, entity))) + tooltip.addLine( + IKey.lang("gui.mob_catcher.click_to_release".asTranslationKey()) + .style(TextFormatting.GRAY, TextFormatting.ITALIC) + ) + tooltip.addLine( + IKey.str( + "${TextFormatting.RED}\u2665 ${hovered.currentHealth}/${hovered.maxHealth}" + ) + ) + tooltip.pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + } + + override fun isInside(stack: IViewportStack, mx: Int, my: Int, absolute: Boolean): Boolean { + if (panel.isSearchViewActive()) + return false + + val x = if (absolute) stack.unTransformX(mx.toFloat(), my.toFloat()) else mx + val y = if (absolute) stack.unTransformY(mx.toFloat(), my.toFloat()) else my + return hoveredCapturedMob(x, y) != null + } + + override fun canHoverThrough(): Boolean = true + + override fun canClickThrough(): Boolean = true + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (panel.isSearchViewActive()) + return Interactable.Result.IGNORE + + if (mouseButton != 0) { + return Interactable.Result.STOP + } + val capturedMob = hoveredCapturedMob(context.mouseX, context.mouseY) ?: return Interactable.Result.IGNORE + NetworkHandler.INSTANCE.sendToServer(C2SMobCatcherReleasePacket(capturedMob.id)) + Interactable.playButtonClickSound() + return Interactable.Result.SUCCESS + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + if (panel.isSearchViewActive()) + return + + panel.backpackWrapper.ensureCapturedMobLayoutCurrent() + val theme = widgetTheme.theme + val hoveredMob = hoveredCapturedMob(context.mouseX, context.mouseY) + val capturedMobs = MobCatcherStorage.getCapturedMobs(panel.backpackWrapper) + .filter { isValidBackpackSlot(it.slot) } + .map { capturedMob -> + CapturedMobRenderInfo( + capturedMob, + slotX(capturedMob.slot), + slotY(capturedMob.slot), + capturedMob.width * SLOT_SIZE, + capturedMob.height * SLOT_SIZE + ) + } + + for (renderInfo in capturedMobs) { + renderCapturedMobArea(renderInfo.x, renderInfo.y, renderInfo.capturedMob.width, renderInfo.capturedMob.height) + } + for (renderInfo in capturedMobs) { + renderEntity(renderInfo.capturedMob)?.let { entity -> + val state = EntityRenderState.capture(entity) + try { + prepareEntityForRender(entity, renderInfo.capturedMob, context) + val scale = getRenderScale(entity, renderInfo.width, renderInfo.height) + renderEntityInInventory( + renderInfo.x + renderInfo.width / 2, + getRenderBottomY(entity, renderInfo.y, renderInfo.height, scale), + scale, + entity, + context.partialTicks + ) + } finally { + state.restore(entity) + } + } + } + capturedMobs.firstOrNull { hoveredMob?.id == it.capturedMob.id }?.let { + RSBTextures.SOLID_DOWN_ARROW_ICON.draw(context, it.x + it.width - 14, it.y + it.height - 14, 12, 12, theme) + } + } + + private fun renderCapturedMobArea(x: Int, y: Int, widthSlots: Int, heightSlots: Int) { + val backgroundX = x - 1 + val backgroundY = y - 1 + val width = widthSlots * SLOT_SIZE + val height = heightSlots * SLOT_SIZE + renderTiledControlBackground( + backgroundX, + backgroundY, + width, + height, + CAPTURED_MOB_BACKGROUND_U, + CAPTURED_MOB_BACKGROUND_V, + CAPTURED_MOB_BACKGROUND_WIDTH, + CAPTURED_MOB_BACKGROUND_HEIGHT + ) + renderCapturedMobBackgroundLayers(backgroundX + 1, backgroundY + 1, width - 2, height - 2) + } + + private fun renderCapturedMobBackgroundLayers(x: Int, y: Int, width: Int, height: Int) { + GuiDraw.drawRect(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), CAPTURED_MOB_BACKGROUND_COLOR) + val layers = max(1, min(5, min(width, height) / 5)) + for (layer in 0 until layers) { + val insetX = 1 + layer * width / (layers * 3) + val insetY = 1 + layer * height / (layers * 3) + val alpha = 24 + layer * 12 + val color = (alpha shl 24) or (CAPTURED_MOB_BACKGROUND_LAYER_COLOR and 0x00FFFFFF) + GuiDraw.drawRect( + (x + insetX).toFloat(), + (y + insetY).toFloat(), + (width - insetX * 2).toFloat(), + (height - insetY * 2).toFloat(), + color + ) + } + restoreTexturedGuiState() + } + + private fun restoreTexturedGuiState() { + GlStateManager.enableTexture2D() + GlStateManager.enableAlpha() + GlStateManager.disableBlend() + GlStateManager.color(1f, 1f, 1f, 1f) + } + + private fun renderTiledControlBackground( + x: Int, + y: Int, + width: Int, + height: Int, + u: Int, + v: Int, + textureWidth: Int, + textureHeight: Int + ) { + val leftWidth = 1 + val topHeight = 1 + val rightWidth = min(1, width - leftWidth) + val bottomHeight = min(1, height - topHeight) + val sourceRightU = u + textureWidth - rightWidth + val sourceBottomV = v + textureHeight - bottomHeight + val centerWidth = width - leftWidth - rightWidth + val centerHeight = height - topHeight - bottomHeight + val sourceCenterWidth = textureWidth - leftWidth - rightWidth + val sourceCenterHeight = textureHeight - topHeight - bottomHeight + + GlStateManager.color(1f, 1f, 1f, 1f) + drawGuiControlsTexture(x, y, leftWidth, topHeight, u, v) + drawGuiControlsTexture(x + leftWidth + centerWidth, y, rightWidth, topHeight, sourceRightU, v) + drawGuiControlsTexture(x, y + topHeight + centerHeight, leftWidth, bottomHeight, u, sourceBottomV) + drawGuiControlsTexture( + x + leftWidth + centerWidth, + y + topHeight + centerHeight, + rightWidth, + bottomHeight, + sourceRightU, + sourceBottomV + ) + renderTiledTexture(x + leftWidth, y, centerWidth, topHeight, u + leftWidth, v, sourceCenterWidth, topHeight) + renderTiledTexture( + x + leftWidth, + y + topHeight + centerHeight, + centerWidth, + bottomHeight, + u + leftWidth, + sourceBottomV, + sourceCenterWidth, + bottomHeight + ) + renderTiledTexture(x, y + topHeight, leftWidth, centerHeight, u, v + topHeight, leftWidth, sourceCenterHeight) + renderTiledTexture( + x + leftWidth + centerWidth, + y + topHeight, + rightWidth, + centerHeight, + sourceRightU, + v + topHeight, + rightWidth, + sourceCenterHeight + ) + renderTiledTexture( + x + leftWidth, + y + topHeight, + centerWidth, + centerHeight, + u + leftWidth, + v + topHeight, + sourceCenterWidth, + sourceCenterHeight + ) + } + + private fun renderTiledTexture( + x: Int, + y: Int, + width: Int, + height: Int, + u: Int, + v: Int, + textureWidth: Int, + textureHeight: Int + ) { + if (width <= 0 || height <= 0 || textureWidth <= 0 || textureHeight <= 0) { + return + } + var renderedY = 0 + while (renderedY < height) { + val chunkHeight = min(textureHeight, height - renderedY) + var renderedX = 0 + while (renderedX < width) { + val chunkWidth = min(textureWidth, width - renderedX) + drawGuiControlsTexture(x + renderedX, y + renderedY, chunkWidth, chunkHeight, u, v) + renderedX += chunkWidth + } + renderedY += chunkHeight + } + } + + private fun drawGuiControlsTexture(x: Int, y: Int, width: Int, height: Int, u: Int, v: Int) { + if (width <= 0 || height <= 0) { + return + } + GuiDraw.drawTexture( + GUI_CONTROLS, + x.toFloat(), + y.toFloat(), + width.toFloat(), + height.toFloat(), + u, + v, + GUI_CONTROLS_TEXTURE_WIDTH, + GUI_CONTROLS_TEXTURE_HEIGHT + ) + } + + private fun renderEntityInInventory(posX: Int, posY: Float, scale: Int, entity: EntityLivingBase, partialTicks: Float) { + val renderManager = Minecraft.getMinecraft().renderManager + val previousViewY = renderManager.playerViewY + val previousShadow = renderManager.isRenderShadow + val previousBrightnessX = OpenGlHelper.lastBrightnessX + val previousBrightnessY = OpenGlHelper.lastBrightnessY + restoreTexturedGuiState() + GlStateManager.enableColorMaterial() + GlStateManager.enableDepth() + GlStateManager.pushMatrix() + try { + GlStateManager.translate(posX.toFloat(), posY, 50f) + GlStateManager.scale((-scale).toFloat(), scale.toFloat(), scale.toFloat()) + GlStateManager.rotate(180f, 0f, 0f, 1f) + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 220f, 220f) + enableFrontEntityLighting() + GlStateManager.disableCull() + renderManager.setPlayerViewY(180f) + renderManager.setRenderShadow(false) + renderManager.renderEntity(entity, 0.0, 0.0, 0.0, 0f, partialTicks, false) + } finally { + renderManager.setRenderShadow(previousShadow) + renderManager.setPlayerViewY(previousViewY) + GlStateManager.popMatrix() + GlStateManager.enableCull() + RenderHelper.disableStandardItemLighting() + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, previousBrightnessX, previousBrightnessY) + GlStateManager.disableRescaleNormal() + GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit) + GlStateManager.disableTexture2D() + GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit) + GlStateManager.disableDepth() + restoreTexturedGuiState() + } + } + + private fun enableFrontEntityLighting() { + GlStateManager.enableLighting() + GlStateManager.enableLight(0) + GlStateManager.enableLight(1) + GlStateManager.enableColorMaterial() + GlStateManager.colorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE) + setFrontEntityLight(GL11.GL_LIGHT0, 0f, 0.65f, 1f, 0.55f) + setFrontEntityLight(GL11.GL_LIGHT1, 0f, -0.25f, 1f, 0.35f) + GlStateManager.shadeModel(GL11.GL_FLAT) + GlStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, RenderHelper.setColorBuffer(0.32f, 0.32f, 0.32f, 1f)) + } + + private fun setFrontEntityLight(light: Int, x: Float, y: Float, z: Float, diffuse: Float) { + val length = sqrt((x * x + y * y + z * z).toDouble()).toFloat().takeIf { it > 0f } ?: 1f + GlStateManager.glLight(light, GL11.GL_POSITION, RenderHelper.setColorBuffer(x / length, y / length, z / length, 0f)) + GlStateManager.glLight(light, GL11.GL_DIFFUSE, RenderHelper.setColorBuffer(diffuse, diffuse, diffuse, 1f)) + GlStateManager.glLight(light, GL11.GL_AMBIENT, RenderHelper.setColorBuffer(0f, 0f, 0f, 1f)) + GlStateManager.glLight(light, GL11.GL_SPECULAR, RenderHelper.setColorBuffer(0f, 0f, 0f, 1f)) + } + + private fun hoveredCapturedMob(mouseX: Int, mouseY: Int): CapturedMob? { + panel.backpackWrapper.ensureCapturedMobLayoutCurrent() + return MobCatcherStorage.getCapturedMobs(panel.backpackWrapper).firstOrNull { capturedMob -> + if (!isValidBackpackSlot(capturedMob.slot)) { + return@firstOrNull false + } + val x = slotX(capturedMob.slot) - 1 + val y = slotY(capturedMob.slot) - 1 + mouseX >= x && mouseX < x + capturedMob.width * SLOT_SIZE && + mouseY >= y && mouseY < y + capturedMob.height * SLOT_SIZE + } + } + + private fun isValidBackpackSlot(slot: Int): Boolean = + slot in 0 until panel.backpackWrapper.backpackInventorySize() + + private fun slotX(slot: Int): Int = slot % panel.rowSize * SLOT_SIZE + CAPTURED_MOB_SLOT_OFFSET + + private fun slotY(slot: Int): Int = slot / panel.rowSize * SLOT_SIZE + CAPTURED_MOB_SLOT_OFFSET + + private fun renderEntity(capturedMob: CapturedMob): EntityLivingBase? { + renderEntities[capturedMob.id]?.let { return it } + val world = Minecraft.getMinecraft().world ?: return null + val entity = EntityList.createEntityByIDFromName(capturedMob.entityType, world) as? EntityLivingBase ?: return null + entity.readFromNBT(capturedMob.entityNbt.copy() as net.minecraft.nbt.NBTTagCompound) + renderEntities[capturedMob.id] = entity + return entity + } + + private fun tooltipDisplayName(capturedMob: CapturedMob, entity: EntityLivingBase?): String = + when { + entity == null -> capturedMob.displayName + entity.hasCustomName() -> entity.customNameTag + else -> entity.displayName.unformattedText + } + + private fun prepareEntityForRender(entity: EntityLivingBase, capturedMob: CapturedMob, context: ModularGuiContext) { + val renderTime = renderTime(capturedMob, context) + resetCapturedEntityVisualState(entity) + entity.setLocationAndAngles(0.0, 0.0, 0.0, 0f, 0f) + val bodyRot = (uuidFloat(capturedMob.id, 0) - 0.5f) * BODY_YAW_RANGE + val headOffset = -HEAD_STATIC_YAW_RANGE / 2f + uuidFloat(capturedMob.id, 1) * HEAD_STATIC_YAW_RANGE + + idlePoseOffset(capturedMob.id, renderTime, 0, HEAD_IDLE_YAW_AMPLITUDE) + val pitch = -HEAD_STATIC_PITCH_RANGE / 2f + uuidFloat(capturedMob.id, 2) * HEAD_STATIC_PITCH_RANGE + + idlePoseOffset(capturedMob.id, renderTime, 1, HEAD_IDLE_PITCH_AMPLITUDE) + entity.ticksExisted = renderTime.toInt() + entity.renderYawOffset = bodyRot + entity.prevRenderYawOffset = bodyRot + entity.rotationYaw = bodyRot + entity.prevRotationYaw = bodyRot + entity.rotationYawHead = bodyRot + headOffset + entity.prevRotationYawHead = entity.rotationYawHead + entity.rotationPitch = pitch + entity.prevRotationPitch = pitch + } + + private fun resetCapturedEntityVisualState(entity: EntityLivingBase) { + entity.hurtResistantTime = 0 + entity.hurtTime = 0 + entity.maxHurtTime = 0 + entity.deathTime = 0 + entity.attackedAtYaw = 0f + entity.limbSwing = 0f + entity.limbSwingAmount = 0f + entity.prevLimbSwingAmount = 0f + } + + private fun renderTime(capturedMob: CapturedMob, context: ModularGuiContext): Float { + val player = Minecraft.getMinecraft().player + val baseTime = player?.ticksExisted?.toFloat() ?: context.tick.toFloat() + return baseTime + context.partialTicks + uuidFloat(capturedMob.id, 3) * 200f + } + + private fun idlePoseOffset(uuid: java.util.UUID, renderTime: Float, salt: Int, amplitude: Float): Float { + val cycleLength = HEAD_IDLE_MIN_CYCLE_TICKS + uuidFloat(uuid, salt + 4) * HEAD_IDLE_CYCLE_VARIATION_TICKS + val cycle = floor(renderTime / cycleLength).toInt() + val phase = renderTime - cycle * cycleLength + val target = (cycleFloat(uuid, cycle, salt) - 0.5f) * 2f * amplitude + return when { + phase < HEAD_IDLE_MOVE_TICKS -> smoothStep(phase / HEAD_IDLE_MOVE_TICKS) * target + phase < HEAD_IDLE_MOVE_TICKS + HEAD_IDLE_HOLD_TICKS -> target + phase < HEAD_IDLE_MOVE_TICKS * 2f + HEAD_IDLE_HOLD_TICKS -> + (1f - smoothStep((phase - HEAD_IDLE_MOVE_TICKS - HEAD_IDLE_HOLD_TICKS) / HEAD_IDLE_MOVE_TICKS)) * target + else -> 0f + } + } + + private fun smoothStep(value: Float): Float = + value * value * (3f - 2f * value) + + private fun cycleFloat(uuid: java.util.UUID, cycle: Int, salt: Int): Float = + Math.floorMod(uuid.hashCode() * 31 + cycle * 131 + salt * 17, 1000) / 999f + + private fun uuidFloat(uuid: java.util.UUID, salt: Int): Float = + Math.floorMod(uuid.hashCode() + salt * 31, 1000) / 999f + + private fun getRenderScale(entity: EntityLivingBase, width: Int, height: Int): Int { + val entityWidth = max(entity.width, 0.25f) + val entityHeight = max(entity.height, 0.25f) + return max(8, (min((width - 6) / entityWidth, (height - 6) / entityHeight) * 0.5625f).toInt()) + } + + private fun getRenderBottomY(entity: EntityLivingBase, y: Int, height: Int, scale: Int): Float = + y + height / 2f + entity.height * scale / 2f + + private data class EntityRenderState( + val renderYawOffset: Float, + val prevRenderYawOffset: Float, + val rotationYaw: Float, + val prevRotationYaw: Float, + val rotationYawHead: Float, + val prevRotationYawHead: Float, + val rotationPitch: Float, + val prevRotationPitch: Float, + val ticksExisted: Int, + val hurtResistantTime: Int, + val hurtTime: Int, + val maxHurtTime: Int, + val deathTime: Int, + val attackedAtYaw: Float, + val limbSwing: Float, + val limbSwingAmount: Float, + val prevLimbSwingAmount: Float + ) { + fun restore(entity: EntityLivingBase) { + entity.renderYawOffset = renderYawOffset + entity.prevRenderYawOffset = prevRenderYawOffset + entity.rotationYaw = rotationYaw + entity.prevRotationYaw = prevRotationYaw + entity.rotationYawHead = rotationYawHead + entity.prevRotationYawHead = prevRotationYawHead + entity.rotationPitch = rotationPitch + entity.prevRotationPitch = prevRotationPitch + entity.ticksExisted = ticksExisted + entity.hurtResistantTime = hurtResistantTime + entity.hurtTime = hurtTime + entity.maxHurtTime = maxHurtTime + entity.deathTime = deathTime + entity.attackedAtYaw = attackedAtYaw + entity.limbSwing = limbSwing + entity.limbSwingAmount = limbSwingAmount + entity.prevLimbSwingAmount = prevLimbSwingAmount + } + + companion object { + fun capture(entity: EntityLivingBase): EntityRenderState = + EntityRenderState( + entity.renderYawOffset, + entity.prevRenderYawOffset, + entity.rotationYaw, + entity.prevRotationYaw, + entity.rotationYawHead, + entity.prevRotationYawHead, + entity.rotationPitch, + entity.prevRotationPitch, + entity.ticksExisted, + entity.hurtResistantTime, + entity.hurtTime, + entity.maxHurtTime, + entity.deathTime, + entity.attackedAtYaw, + entity.limbSwing, + entity.limbSwingAmount, + entity.prevLimbSwingAmount + ) + } + } + + private data class CapturedMobRenderInfo( + val capturedMob: CapturedMob, + val x: Int, + val y: Int, + val width: Int, + val height: Int + ) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SettingTabWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SettingTabWidget.kt index a6e4d30..1a5aabf 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SettingTabWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SettingTabWidget.kt @@ -21,7 +21,7 @@ class SettingTabWidget : Widget(), Interactable { init { size(TAB_TEXTURE.width, TAB_TEXTURE.height) .right(-TAB_TEXTURE.width + 4) - .top(0) + .top(TabWidget.TAB_TOP_OFFSET) .background(TAB_TEXTURE.get(-1, false)) .tooltipStatic { it.addLine(IKey.lang("gui.settings".asTranslationKey())) @@ -33,16 +33,18 @@ class SettingTabWidget : Widget(), Interactable { context.recipeViewerSettings.addExclusionArea(this) } + override fun dispose() { + if (isValid) + context.recipeViewerSettings.removeExclusionArea(this) + super.dispose() + } + override fun onMousePressed(mouseButton: Int): Interactable.Result { if (mouseButton == 0) { val panel = panel as BackpackPanel Interactable.playButtonClickSound() - if (panel.settingPanel.isPanelOpen) { - panel.settingPanel.closePanel() - } else { - panel.settingPanel.openPanel() - } + panel.isSettingMode = true return Interactable.Result.SUCCESS } @@ -55,4 +57,4 @@ class SettingTabWidget : Widget(), Interactable { RSBTextures.SETTING_ICON.draw(context, 8, 6, 16, 16, widgetTheme.getThemeOrDefault()) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SortingSettingWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SortingSettingWidget.kt index 15a07ab..e122101 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SortingSettingWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/SortingSettingWidget.kt @@ -3,10 +3,8 @@ package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets import com.cleanroommc.modularui.api.drawable.IKey import com.cleanroommc.modularui.screen.RichTooltip import com.cleanroommc.modularui.widgets.ButtonWidget -import com.cleanroommc.modularui.widgets.layout.Row import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel -import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackSettingPanel import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSlotSH import com.cleanroommc.retrosophisticatedbackpacks.util.Utils @@ -14,23 +12,18 @@ import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey class SortingSettingWidget( private val panel: BackpackPanel, - private val settingPanel: BackpackSettingPanel, private val parentTabWidget: TabWidget ) : ExpandedTabWidget( 2, RSBTextures.NO_SORT_ICON, "gui.sorting_settings".asTranslationKey(), width = 75, - expandDirection = ExpandDirection.LEFT + tabHeight = 48, + expandDirection = ExpandDirection.RIGHT ) { - private val buttonRow: Row = Row() - .leftRel(0.5f) - .height(20) - .coverChildrenWidth() - .childPadding(2) as Row - private val lockAllButton: ButtonWidget<*> = ButtonWidget() - .size(20) + .pos(3, 24) + .size(18) .overlay(RSBTextures.ALL_FOUR_SLOT_ICON) .onMousePressed { if (it == 0) { @@ -57,7 +50,8 @@ class SortingSettingWidget( } private val unlockAllButton: ButtonWidget<*> = ButtonWidget() - .size(20) + .pos(21, 24) + .size(18) .overlay(RSBTextures.NONE_FOUR_SLOT_ICON) .onMousePressed { if (it == 0) { @@ -83,16 +77,11 @@ class SortingSettingWidget( } init { - buttonRow.top(28) - .child(lockAllButton) + child(lockAllButton) .child(unlockAllButton) - - child(buttonRow) } override fun updateTabState() { - parentTabWidget.showExpanded = !parentTabWidget.showExpanded - panel.isSortingSettingTabOpened = parentTabWidget.showExpanded - settingPanel.updateTabState(1) + panel.openSortingSettings(parentTabWidget, !parentTabWidget.showExpanded) } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TabWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TabWidget.kt index 5293acc..46166aa 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TabWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TabWidget.kt @@ -10,6 +10,7 @@ import com.cleanroommc.modularui.widget.SingleChildWidget import com.cleanroommc.modularui.widget.sizer.Unit import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.setEnabledIfAndEnabled +import net.minecraft.client.renderer.GlStateManager class TabWidget( private val tabIndex: Int, @@ -19,6 +20,8 @@ class TabWidget( SingleChildWidget(), Interactable { companion object { val TAB_TEXTURE: TabTexture = GuiTextures.TAB_RIGHT + const val TAB_TOP_OFFSET = 0 + const val TAB_VERTICAL_SPACE = 1 } var showExpanded = false @@ -27,6 +30,7 @@ class TabWidget( expandedWidget?.isEnabled = value field = value + markTooltipDirty() } var expandedWidget: ExpandedTabWidget? = null @@ -43,12 +47,16 @@ class TabWidget( field = value } var tabIcon: IDrawable? = null + var onToggle: ((Boolean) -> Unit)? = null init { - size(TAB_TEXTURE.width, TAB_TEXTURE.height).top({ tabOrder * 30.0 }, Unit.Measure.PIXEL) + size(TAB_TEXTURE.width, TAB_TEXTURE.height) + .top({ + (TAB_TOP_OFFSET + (tabOrder - 1).coerceAtLeast(0) * (TAB_TEXTURE.height + TAB_VERTICAL_SPACE)).toDouble() + }, Unit.Measure.PIXEL) when (expandDirection) { - ExpandDirection.LEFT -> left(-TAB_TEXTURE.width + 4) + ExpandDirection.LEFT -> left(-TAB_TEXTURE.width - 2) ExpandDirection.RIGHT -> right(-TAB_TEXTURE.width + 4) } } @@ -57,12 +65,19 @@ class TabWidget( context.recipeViewerSettings.addExclusionArea(this) } + override fun dispose() { + if (isValid) + context.recipeViewerSettings.removeExclusionArea(this) + super.dispose() + } + override fun onMousePressed(mouseButton: Int): Interactable.Result { if (!isEnabled || expandedWidget == null) return Interactable.Result.STOP if (mouseButton == 0) { expandedWidget?.updateTabState() + onToggle?.invoke(showExpanded) Interactable.playButtonClickSound() return Interactable.Result.SUCCESS } @@ -76,7 +91,9 @@ class TabWidget( if (showExpanded) return + GlStateManager.color(1f, 1f, 1f, 1f) tabIcon?.draw(context, 8, 6, 16, 16, widgetTheme.getThemeOrDefault()) + GlStateManager.color(1f, 1f, 1f, 1f) } override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { @@ -95,4 +112,4 @@ class TabWidget( .drawAtZero(context, TAB_TEXTURE.width, TAB_TEXTURE.height, widgetTheme.getThemeOrDefault()) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TankInventoryControlWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TankInventoryControlWidget.kt new file mode 100644 index 0000000..9a9c69d --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TankInventoryControlWidget.kt @@ -0,0 +1,101 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.drawable.GuiDraw +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.TankUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraftforge.fluids.FluidStack + +class TankInventoryControlWidget( + private val slotSyncHandler: UpgradeSlotSH, + private val upgradeSlot: Int, + private val backpackWrapper: BackpackWrapper, + height: Int +) : Widget(), Interactable { + companion object { + const val WIDTH = 36 + private const val TANK_LEFT = 9 + private const val TANK_WIDTH = 18 + private const val OVERLAY_LEFT = TANK_LEFT + 1 + private const val OVERLAY_WIDTH = 16 + } + + init { + size(WIDTH, height) + tooltipAutoUpdate(true) + tooltipDynamic { tooltip -> + val wrapper = currentWrapper() + wrapper?.getFluid()?.let { tooltip.addLine(IKey.str(it.localizedName)) } + tooltip.addLine(IKey.str("${wrapper?.getFluid()?.amount ?: 0}/${wrapper?.getTankCapacity(backpackWrapper) ?: 0} mB")) + tooltip.pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + val theme = widgetTheme.getThemeOrDefault() + val height = area.height + RSBTextures.BAR_BACKGROUND_TOP.draw(context, TANK_LEFT, 0, TANK_WIDTH, if (height < 36) height / 2 else 18, theme) + + var yOffset = 18 + repeat((height - 36).coerceAtLeast(0) / 18) { + RSBTextures.BAR_BACKGROUND_MIDDLE.draw(context, TANK_LEFT, yOffset, TANK_WIDTH, 18, theme) + yOffset += 18 + } + + RSBTextures.BAR_BACKGROUND_BOTTOM.draw( + context, + TANK_LEFT, + if (height < 36) height / 2 else yOffset, + TANK_WIDTH, + if (height < 36) height / 2 else 18, + theme + ) + + renderFluid(context, currentWrapper(), height) + + yOffset = 0 + repeat(height / 18) { + RSBTextures.TANK_OVERLAY.draw(context, OVERLAY_LEFT, yOffset, OVERLAY_WIDTH, 18, theme) + yOffset += 18 + } + } + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (mouseButton != 0 && mouseButton != 1) { + return Interactable.Result.IGNORE + } + slotSyncHandler.syncToServer(UpgradeSlotSH.UPDATE_TANK_CLICK) {} + Interactable.playButtonClickSound() + return Interactable.Result.SUCCESS + } + + private fun currentWrapper(): TankUpgradeWrapper? = + backpackWrapper.upgradeItemStackHandler.inventory[upgradeSlot] + .getCapability(Capabilities.TANK_UPGRADE_CAPABILITY, null) + + private fun renderFluid(context: ModularGuiContext, wrapper: TankUpgradeWrapper?, height: Int) { + val fluid = wrapper?.getFluid() + if (fluid == null || fluid.amount <= 0) { + return + } + val displayLevel = ((height - 2) * (fluid.amount.toFloat() / wrapper.getTankCapacity(backpackWrapper))).toInt() + .coerceIn(1, height - 2) + GuiDraw.drawFluidTexture( + fluid, + (TANK_LEFT + 1).toFloat(), + (height - 1 - displayLevel).toFloat(), + OVERLAY_WIDTH.toFloat(), + displayLevel.toFloat(), + context.currentDrawingZ.toFloat() + ) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TransferButtonWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TransferButtonWidget.kt index 4ba1be8..980931b 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TransferButtonWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/TransferButtonWidget.kt @@ -5,17 +5,22 @@ import com.cleanroommc.modularui.api.widget.Interactable import com.cleanroommc.modularui.screen.viewport.ModularGuiContext import com.cleanroommc.modularui.theme.WidgetThemeEntry import com.cleanroommc.modularui.widgets.ButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.client.renderer.GlStateManager class TransferButtonWidget(private val matchedIcon: IDrawable, private val allIcon: IDrawable) : ButtonWidget() { - override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { - super.drawOverlay(context, widgetTheme) + override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + val theme = widgetTheme.getThemeOrDefault() + if (isHovering) RSBTextures.SMALL_BUTTON_HOVERED.draw(context, 0, 0, 12, 12, theme) + else RSBTextures.SMALL_BUTTON.draw(context, 0, 0, 12, 12, theme) + } - if (Interactable.hasShiftDown()) { - allIcon.drawAtZero(context, area, widgetTheme.getThemeOrDefault()) - } else { - matchedIcon.drawAtZero(context, area, widgetTheme.getThemeOrDefault()) - } + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + GlStateManager.color(1f, 1f, 1f, 1f) + val icon = if (Interactable.hasShiftDown()) allIcon else matchedIcon + icon.draw(context, 0, 0, 12, 12, widgetTheme.getThemeOrDefault()) + GlStateManager.color(1f, 1f, 1f, 1f) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/UpgradeSlotGroupWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/UpgradeSlotGroupWidget.kt index 089ae6a..77f59ba 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/UpgradeSlotGroupWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/UpgradeSlotGroupWidget.kt @@ -2,11 +2,9 @@ package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets import com.cleanroommc.modularui.api.value.ISyncOrValue import com.cleanroommc.modularui.api.widget.Interactable -import com.cleanroommc.modularui.drawable.GuiDraw import com.cleanroommc.modularui.drawable.UITexture import com.cleanroommc.modularui.screen.viewport.ModularGuiContext import com.cleanroommc.modularui.theme.WidgetThemeEntry -import com.cleanroommc.modularui.utils.Color import com.cleanroommc.modularui.widget.Widget import com.cleanroommc.modularui.widgets.SlotGroupWidget import com.cleanroommc.retrosophisticatedbackpacks.Tags @@ -18,18 +16,16 @@ import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault import net.minecraft.item.ItemStack -class UpgradeSlotGroupWidget(panel: BackpackPanel, private val slotSize: Int) : SlotGroupWidget() { +class UpgradeSlotGroupWidget(private val panel: BackpackPanel, private val slotSize: Int) : SlotGroupWidget() { companion object { + private val GUI_CONTROLS = + UITexture.fullImage(Tags.MOD_ID, "gui/gui_controls.png") private val UPPER_TAB_TEXTURE = UITexture.builder().location(Tags.MOD_ID, "gui/gui_controls.png").imageSize(256, 256) - .xy(0, 0, 25, 5).build() - private val SLOT_SURROUNDING_TEXTURE = - UITexture.builder().location(Tags.MOD_ID, "gui/gui_controls.png").imageSize(256, 256) - .xy(0, 5, 25, 18).build() + .xy(0, 0, 26, 4).build() private val LOWER_TAB_TEXTURE = UITexture.builder().location(Tags.MOD_ID, "gui/gui_controls.png").imageSize(256, 256) - .xy(0, 199, 25, 5).build() - private val SLOT_HOVERING_COLOR = Color.withAlpha(Color.WHITE.main, 0x50) + .xy(0, 198, 25, 6).build() } val toggleWidgets: List @@ -51,18 +47,31 @@ class UpgradeSlotGroupWidget(panel: BackpackPanel, private val slotSize: Int) : context.recipeViewerSettings.addExclusionArea(this) } - override fun draw(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { - super.draw(context, widgetTheme) - var y = 5 - - UPPER_TAB_TEXTURE.draw(context, 0, 0, 25, 5, widgetTheme.getThemeOrDefault()) + override fun dispose() { + if (isValid) + context.recipeViewerSettings.removeExclusionArea(this) + super.dispose() + } - for (i in 0 until slotSize) { - SLOT_SURROUNDING_TEXTURE.draw(context, 0, y, 25, 18, widgetTheme.getThemeOrDefault()) - y += 18 - } + override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.drawBackground(context, widgetTheme) + + val heightWithoutBottom = 6 + slotSize * 16 + + UPPER_TAB_TEXTURE.draw(context, 0, 0, 26, 4, widgetTheme.getThemeOrDefault()) + GUI_CONTROLS.drawSubArea( + 0f, + 4f, + 25f, + (heightWithoutBottom - 4).toFloat(), + 0f, + 4f / 256f, + 25f / 256f, + heightWithoutBottom / 256f, + widgetTheme.getThemeOrDefault() + ) + LOWER_TAB_TEXTURE.draw(context, 0, heightWithoutBottom, 25, 6, widgetTheme.getThemeOrDefault()) - LOWER_TAB_TEXTURE.draw(context, 0, y, 25, 5, widgetTheme.getThemeOrDefault()) } class UpgradeToggleWidget(private val panel: BackpackPanel, private val slotIndex: Int) : @@ -74,7 +83,22 @@ class UpgradeSlotGroupWidget(panel: BackpackPanel, private val slotSize: Int) : private val BACKGROUND_TAB_TEXTURE = UITexture.builder() .location(Tags.MOD_ID, "gui/gui_controls.png") .imageSize(256, 256) - .xy(0, 204, WIDTH, HEIGHT) + .xy(0, 204, 7, HEIGHT) + .build() + private val CONNECTED_BACKGROUND_TAB_TEXTURE = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(0, 205, 7, HEIGHT - 1) + .build() + private val SWITCH_BACKGROUND_TEXTURE = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(65, 0, 6, 12) + .build() + private val SWITCH_HOVERED_BACKGROUND_TEXTURE = UITexture.builder() + .location(Tags.MOD_ID, "gui/gui_controls.png") + .imageSize(256, 256) + .xy(71, 0, 6, 12) .build() } @@ -82,7 +106,7 @@ class UpgradeSlotGroupWidget(panel: BackpackPanel, private val slotSize: Int) : private var slotSyncHandler: UpgradeSlotSH? = null init { - size(WIDTH, HEIGHT).left(-4).top(slotIndex * 18 + 4) + size(WIDTH, HEIGHT).left(-4).top(slotIndex * 16 + 5) isEnabled = false val wrapper = getWrapper() @@ -117,7 +141,7 @@ class UpgradeSlotGroupWidget(panel: BackpackPanel, private val slotSize: Int) : super.drawOverlay(context, widgetTheme) if (isHovering) - GuiDraw.drawRect(4f, 4f, 4f, 10f, SLOT_HOVERING_COLOR) + SWITCH_HOVERED_BACKGROUND_TEXTURE.draw(context, 3, 3, 6, 12, widgetTheme.getThemeOrDefault()) if (isToggleEnabled) RSBTextures.TOGGLE_ENABLE_ICON.draw(context, 4, 4, 4, 10, widgetTheme.getThemeOrDefault()) @@ -128,7 +152,14 @@ class UpgradeSlotGroupWidget(panel: BackpackPanel, private val slotSize: Int) : override fun drawBackground(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { super.drawBackground(context, widgetTheme) - BACKGROUND_TAB_TEXTURE.draw(context, 0, 0, WIDTH, HEIGHT, widgetTheme.getThemeOrDefault()) + val previousHasSwitch = slotIndex > 0 && + panel.backpackWrapper.upgradeItemStackHandler.getStackInSlot(slotIndex - 1) + .getCapability(Capabilities.TOGGLEABLE_CAPABILITY, null) != null + if (previousHasSwitch) + CONNECTED_BACKGROUND_TAB_TEXTURE.draw(context, 0, 1, 7, HEIGHT - 1, widgetTheme.getThemeOrDefault()) + else + BACKGROUND_TAB_TEXTURE.draw(context, 0, 0, 7, HEIGHT, widgetTheme.getThemeOrDefault()) + SWITCH_BACKGROUND_TEXTURE.draw(context, 3, 3, 6, 12, widgetTheme.getThemeOrDefault()) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/VanillaTextFieldWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/VanillaTextFieldWidget.kt new file mode 100644 index 0000000..6ec8f81 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/VanillaTextFieldWidget.kt @@ -0,0 +1,146 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.api.widget.IFocusedWidget +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.widget.Widget +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiTextField +import org.lwjgl.input.Keyboard + +abstract class VanillaTextFieldWidget>( + textY: Int, + textWidth: Int, + textHeight: Int +) : Widget(), Interactable, IFocusedWidget { + companion object { + private var focusedTextField: VanillaTextFieldWidget<*>? = null + + fun handleCommittedCharacter(character: Char): Boolean { + val focused = focusedTextField ?: return false + if (!focused.isFocused()) { + focusedTextField = null + return false + } + return focused.typeCharacter(character, 0) + } + } + + protected val textField = GuiTextField(0, Minecraft.getMinecraft().fontRenderer, 0, textY, textWidth, textHeight) + + init { + background(IDrawable.EMPTY) + textField.setEnableBackgroundDrawing(false) + textField.setCanLoseFocus(false) + } + + protected var text: String + get() = textField.text + set(value) { + if (textField.text != value) { + textField.setText(value) + } + } + + protected open fun textFieldX(): Int = 0 + + protected open fun textFieldY(): Int = 0 + + protected open fun textFieldWidth(): Int = area.width + + protected open fun textFieldHeight(): Int = area.height + + protected open fun mouseXForTextField(): Int = context.mouseX + + protected open fun mouseYForTextField(): Int = context.mouseY + + protected open fun onTextChanged(text: String) {} + + protected open fun onEditingFinished() {} + + protected open fun onFocusChanged(focused: Boolean) {} + + protected fun setTextColor(color: Int) { + textField.setTextColor(color) + } + + protected fun setDisabledTextColor(color: Int) { + textField.setDisabledTextColour(color) + } + + protected fun setMaxStringLength(length: Int) { + textField.setMaxStringLength(length) + } + + protected fun drawTextField() { + updateTextFieldBounds() + textField.drawTextBox() + } + + protected fun updateTextFieldBounds() { + textField.x = textFieldX() + textField.y = textFieldY() + textField.width = textFieldWidth().coerceAtLeast(1) + textField.height = textFieldHeight().coerceAtLeast(1) + } + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (mouseButton != 0) { + return Interactable.Result.STOP + } + updateTextFieldBounds() + textField.setFocused(true) + textField.mouseClicked(mouseXForTextField(), mouseYForTextField(), mouseButton) + return Interactable.Result.SUCCESS + } + + override fun onKeyPressed(character: Char, keyCode: Int): Interactable.Result { + if (!isFocused()) { + return Interactable.Result.IGNORE + } + if (keyCode == Keyboard.KEY_ESCAPE || keyCode == Keyboard.KEY_RETURN || keyCode == Keyboard.KEY_NUMPADENTER) { + context.removeFocus() + return Interactable.Result.SUCCESS + } + + return if (typeCharacter(character, keyCode) || keyCode != Keyboard.KEY_TAB) { + Interactable.Result.SUCCESS + } else { + Interactable.Result.IGNORE + } + } + + private fun typeCharacter(character: Char, keyCode: Int): Boolean { + val oldText = textField.text + val handled = textField.textboxKeyTyped(character, keyCode) + if (textField.text != oldText) { + onTextChanged(textField.text) + } + return handled + } + + override fun onUpdate() { + super.onUpdate() + textField.updateCursorCounter() + } + + override fun isFocused(): Boolean = context.isFocused(this) + + override fun onFocus(context: ModularGuiContext) { + focusedTextField = this + textField.setFocused(true) + onFocusChanged(true) + Keyboard.enableRepeatEvents(true) + } + + override fun onRemoveFocus(context: ModularGuiContext) { + if (focusedTextField === this) { + focusedTextField = null + } + textField.setFocused(false) + onFocusChanged(false) + Keyboard.enableRepeatEvents(false) + onEditingFinished() + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/BackpackSlot.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/BackpackSlot.kt index 36b8f66..f44401e 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/BackpackSlot.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/BackpackSlot.kt @@ -12,14 +12,19 @@ import com.cleanroommc.modularui.theme.WidgetThemeEntry import com.cleanroommc.modularui.utils.Color import com.cleanroommc.modularui.utils.NumberFormat import com.cleanroommc.modularui.utils.Platform -import com.cleanroommc.modularui.widgets.slot.ItemSlot import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlot +import com.cleanroommc.retrosophisticatedbackpacks.handler.ClientGuiStashHandler +import com.cleanroommc.retrosophisticatedbackpacks.handler.NetworkHandler +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SStashToBackpackPacket +import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSH import com.cleanroommc.retrosophisticatedbackpacks.sync.BackpackSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.DyeColorUtils import com.cleanroommc.retrosophisticatedbackpacks.util.Utils import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.Minecraft import net.minecraft.client.gui.Gui import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.client.renderer.GlStateManager @@ -31,7 +36,7 @@ import net.minecraft.util.text.TextFormatting import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly -class BackpackSlot(private val panel: BackpackPanel, private val wrapper: BackpackWrapper) : ItemSlot() { +class BackpackSlot(private val panel: BackpackPanel, private val wrapper: BackpackWrapper) : NoBackgroundItemSlot() { companion object { val DECIMAL_TWO: NumberFormat.Params = NumberFormat.AMOUNT_TEXT.copyToBuilder() .maxLength(2) @@ -40,13 +45,27 @@ class BackpackSlot(private val panel: BackpackPanel, private val wrapper: Backpa } private val isInSettingMode: Boolean - get() = panel.settingPanel.isPanelOpen + get() = panel.isSettingMode private val isInMemorySettingMode: Boolean get() = panel.isMemorySettingTabOpened private val isInSortSettingMode: Boolean get() = panel.isSortingSettingTabOpened + private val isInItemDisplaySettingMode: Boolean + get() = panel.isItemDisplaySettingTabOpened + private val isBlockedByCapturedMob: Boolean + get() = wrapper.isSlotBlockedByMobCatcher(slot.slotIndex) + private val isHiddenBySearch: Boolean + get() = !panel.isSearchSlotVisible(slot.slotIndex) + + override fun onUpdate() { + super.onUpdate() + updateSearchPosition() + } override fun buildTooltip(stack: ItemStack, tooltip: RichTooltip) { + if (isBlockedByCapturedMob || isHiddenBySearch) + return + val memorizedStack = wrapper.getMemorizedStack(slot.slotIndex) if (stack.isEmpty && memorizedStack.isEmpty) @@ -97,81 +116,189 @@ class BackpackSlot(private val panel: BackpackPanel, private val wrapper: Backpa } override fun onMousePressed(mouseButton: Int): Interactable.Result = - if (isInMemorySettingMode) { - val isMemorySet = wrapper.isSlotMemorized(slot.slotIndex) - - if (isMemorySet && mouseButton == 1) { - wrapper.unsetMemoryStack(slot.slotIndex) - syncHandler.syncToServer(BackpackSlotSH.UPDATE_UNSET_MEMORY_STACK) - Utils.invalidateSortingContext() - Interactable.Result.SUCCESS - } else if (!isMemorySet && mouseButton == 0) { - wrapper.setMemoryStack(slot.slotIndex, panel.shouldMemorizeRespectNBT) - syncHandler.syncToServer(BackpackSlotSH.UPDATE_SET_MEMORY_STACK) { - it.writeBoolean(panel.shouldMemorizeRespectNBT) - } - Utils.invalidateSortingContext() - Interactable.Result.SUCCESS - } else Interactable.Result.STOP - } else if (isInSortSettingMode) { - val isSlotLocked = wrapper.isSlotLocked(slot.slotIndex) - - if (isSlotLocked && mouseButton == 1) { - wrapper.setSlotLocked(slot.slotIndex, false) - syncHandler.syncToServer(BackpackSlotSH.UPDATE_UNSET_SLOT_LOCK) - Utils.invalidateSortingContext() - Interactable.Result.SUCCESS - } else if (!isSlotLocked && mouseButton == 0) { - wrapper.setSlotLocked(slot.slotIndex, true) - syncHandler.syncToServer(BackpackSlotSH.UPDATE_SET_SLOT_LOCK) - Utils.invalidateSortingContext() - Interactable.Result.SUCCESS - } else Interactable.Result.STOP - } else if (isInSettingMode) { - Interactable.Result.STOP - } else { - super.onMousePressed(mouseButton) + when { + isBlockedByCapturedMob -> Interactable.Result.STOP + isHiddenBySearch -> Interactable.Result.STOP + isInItemDisplaySettingMode -> handleItemDisplaySlotClick(mouseButton) + isInMemorySettingMode -> handleMemorySlotClick(mouseButton) + isInSortSettingMode -> handleSortSlotClick(mouseButton) + isInSettingMode -> Interactable.Result.STOP + mouseButton == 1 && handleStashClick() -> Interactable.Result.SUCCESS + else -> super.onMousePressed(mouseButton) } override fun onMouseRelease(mouseButton: Int): Boolean = - if (isInSettingMode) true + if (isBlockedByCapturedMob || isHiddenBySearch || isInSettingMode) true else super.onMouseRelease(mouseButton) override fun onMouseDrag(mouseButton: Int, timeSinceClick: Long) { - if (isInSettingMode) + if (isBlockedByCapturedMob || isHiddenBySearch) { + return + } + if (isInMemorySettingMode) { + handleMemorySlotClick(mouseButton) + return + } + if (isInSortSettingMode) { + handleSortSlotClick(mouseButton) + return + } + if (isInItemDisplaySettingMode) { + handleItemDisplaySlotClick(mouseButton) return + } + if (isInSettingMode) { + return + } super.onMouseDrag(mouseButton, timeSinceClick) } + private fun handleMemorySlotClick(mouseButton: Int): Interactable.Result { + val isMemorySet = wrapper.isSlotMemorized(slot.slotIndex) + + return if (isMemorySet && mouseButton == 1) { + wrapper.unsetMemoryStack(slot.slotIndex) + syncHandler.syncToServer(BackpackSlotSH.UPDATE_UNSET_MEMORY_STACK) + Utils.invalidateSortingContext() + Interactable.Result.SUCCESS + } else if (!isMemorySet && mouseButton == 0) { + wrapper.setMemoryStack(slot.slotIndex, panel.shouldMemorizeRespectNBT) + syncHandler.syncToServer(BackpackSlotSH.UPDATE_SET_MEMORY_STACK) { + it.writeBoolean(panel.shouldMemorizeRespectNBT) + } + Utils.invalidateSortingContext() + Interactable.Result.SUCCESS + } else Interactable.Result.STOP + } + + private fun handleItemDisplaySlotClick(mouseButton: Int): Interactable.Result { + val slotIndex = slot.slotIndex + return when { + mouseButton == 0 && !wrapper.isItemDisplaySlotSelected(slotIndex) -> { + wrapper.selectItemDisplaySlot(slotIndex) + if (wrapper.isItemDisplaySlotSelected(slotIndex)) { + panel.setCurrentItemDisplaySelectedSlot(slotIndex) + panel.backpackSyncHandler.syncToServer(BackpackSH.UPDATE_ITEM_DISPLAY_SLOT) { + it.writeInt(slotIndex) + it.writeBoolean(true) + } + Interactable.Result.SUCCESS + } else Interactable.Result.STOP + } + + mouseButton == 1 && wrapper.isItemDisplaySlotSelected(slotIndex) -> { + wrapper.unselectItemDisplaySlot(slotIndex) + panel.setCurrentItemDisplaySelectedSlot(wrapper.getFirstItemDisplaySlot()) + panel.backpackSyncHandler.syncToServer(BackpackSH.UPDATE_ITEM_DISPLAY_SLOT) { + it.writeInt(slotIndex) + it.writeBoolean(false) + } + Interactable.Result.SUCCESS + } + + else -> Interactable.Result.STOP + } + } + + private fun handleSortSlotClick(mouseButton: Int): Interactable.Result { + val isSlotLocked = wrapper.isSlotLocked(slot.slotIndex) + + return if (isSlotLocked && mouseButton == 1) { + wrapper.setSlotLocked(slot.slotIndex, false) + syncHandler.syncToServer(BackpackSlotSH.UPDATE_UNSET_SLOT_LOCK) + Utils.invalidateSortingContext() + Interactable.Result.SUCCESS + } else if (!isSlotLocked && mouseButton == 0) { + wrapper.setSlotLocked(slot.slotIndex, true) + syncHandler.syncToServer(BackpackSlotSH.UPDATE_SET_SLOT_LOCK) + Utils.invalidateSortingContext() + Interactable.Result.SUCCESS + } else Interactable.Result.STOP + } + @SideOnly(Side.CLIENT) override fun draw(context: ModularGuiContext?, widgetThemeEntry: WidgetThemeEntry<*>?) { context?.let { - val widgetTheme = widgetThemeEntry?.theme ?: WidgetTheme.getDefault().theme - if (wrapper.isSlotLocked(slot.slotIndex)) - drawLockedSlot(it, widgetTheme) + updateSearchPosition() + if (isBlockedByCapturedMob) + return + val widgetTheme = widgetThemeEntry?.theme ?: WidgetTheme.getDefault().theme - if (isInSettingMode) drawSettingStack(context, widgetTheme) - else { + if (isInSettingMode) { + if (panel.isSlotSettingTabOpened) + drawSettingStack(context, widgetTheme) + drawSettingOverlays(it, widgetTheme) + } else { val slot = slot as? ModularBackpackSlot ?: return val memoryStack = slot.getMemoryStack() + if (wrapper.isSlotLocked(slot.slotIndex)) + drawLockedSlot(it, widgetTheme) + + if (isHiddenBySearch) + return + super.draw(context, widgetThemeEntry) - if (slot.stack.isEmpty && !memoryStack.isEmpty) + if (slot.stack.isEmpty && !memoryStack.isEmpty) { drawMemoryStack(memoryStack, context, widgetTheme) + drawMemorizedSlotOverlay(context, widgetTheme) + } + drawStashSign() } } } + private fun updateSearchPosition() { + if (!isSynced) + return + + val slotIndex = slot.slotIndex + val x = panel.searchSlotX(slotIndex) + val y = panel.searchSlotY(slotIndex) + if (area.rx == x && area.ry == y) + return + + area.rx = x + area.ry = y + } + + private fun handleStashClick(): Boolean { + val player = Minecraft.getMinecraft().player ?: return false + val carried = player.inventory.itemStack + if (carried.isEmpty) { + return false + } + val action = ClientGuiStashHandler.getStashActionForSlot(player, slot, carried) ?: return false + NetworkHandler.INSTANCE.sendToServer(C2SStashToBackpackPacket(slot.slotNumber, action)) + return true + } + + @SideOnly(Side.CLIENT) + private fun drawStashSign() { + val player = Minecraft.getMinecraft().player ?: return + val carried = player.inventory.itemStack + if (carried.isEmpty) { + return + } + val (sign, result) = ClientGuiStashHandler.getStashResultForSlot(player, slot, carried) ?: return + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow( + sign, + if (sign == "+") 10f else 1f, + if (sign == "+") 8f else 0f, + ClientGuiStashHandler.color(result) + ) + } + @SideOnly(Side.CLIENT) private fun drawSettingStack(context: ModularGuiContext, widgetTheme: WidgetTheme) { val slot = slot as? ModularBackpackSlot ?: return val memoryStack = slot.getMemoryStack() val guiScreen = screen.screenWrapper.guiScreen check(guiScreen is GuiContainer) { "The gui must be an instance of GuiContainer if it contains slots!" } - val guiContainer = guiScreen as GuiContainer + val guiContainer = guiScreen val renderItem = (guiScreen as GuiScreenAccessor).itemRender // makes sure items of different layers don't interfere with each other visually @@ -199,11 +326,6 @@ class BackpackSlot(private val panel: BackpackPanel, private val wrapper: Backpa NEAAnimationHandler.endHoverScale() } - if (!memoryStack.isEmpty) { - GlStateManager.depthFunc(516) - Gui.drawRect(1, 1, 17, 17, Color.argb(139, 139, 139, 128)) - GlStateManager.depthFunc(515) - } RenderHelper.enableStandardItemLighting() GlStateManager.disableLighting() @@ -233,11 +355,6 @@ class BackpackSlot(private val panel: BackpackPanel, private val wrapper: Backpa renderItem.renderItemIntoGUI(itemstack, 1, 1) Platform.endDrawItem() - if (!memoryStack.isEmpty) { - GlStateManager.depthFunc(516) - Gui.drawRect(1, 1, 17, 17, Color.argb(139, 139, 139, 128)) - GlStateManager.depthFunc(515) - } RenderHelper.enableStandardItemLighting() GlStateManager.disableLighting() @@ -245,6 +362,25 @@ class BackpackSlot(private val panel: BackpackPanel, private val wrapper: Backpa renderItem.zLevel = 0f } + @SideOnly(Side.CLIENT) + private fun drawSettingOverlays(context: ModularGuiContext, widgetTheme: WidgetTheme) { + if (wrapper.isSlotLocked(slot.slotIndex)) + drawLockedSlot(context, widgetTheme) + if (wrapper.isSlotMemorized(slot.slotIndex)) + drawMemorizedSlotOverlay(context, widgetTheme) + if (wrapper.isItemDisplaySlotSelected(slot.slotIndex)) + drawItemDisplayOverlay(context, widgetTheme) + } + + @SideOnly(Side.CLIENT) + private fun drawMemorizedSlotOverlay(context: ModularGuiContext, widgetTheme: WidgetTheme) { + GlStateManager.disableDepth() + GlStateManager.enableBlend() + RSBTextures.MEMORIZED_SLOT_OVERLAY.draw(context, 1, 1, 16, 16, widgetTheme) + GlStateManager.disableBlend() + GlStateManager.enableDepth() + } + @SideOnly(Side.CLIENT) private fun drawLockedSlot(context: ModularGuiContext, widgetTheme: WidgetTheme) { RSBTextures.NO_SORT_ICON.draw(context, 1, 1, 16, 16, widgetTheme) @@ -252,4 +388,19 @@ class BackpackSlot(private val panel: BackpackPanel, private val wrapper: Backpa Gui.drawRect(1, 1, 17, 17, Color.argb(139, 139, 139, 128)) GlStateManager.depthFunc(515) } + + @SideOnly(Side.CLIENT) + private fun drawItemDisplayOverlay(context: ModularGuiContext, widgetTheme: WidgetTheme) { + val color = DyeColorUtils.colorValue(wrapper.itemDisplayColor) + GlStateManager.disableDepth() + GlStateManager.enableBlend() + Gui.drawRect(1, 1, 17, 17, color and 0x00FFFFFF or (80 shl 24)) + if (panel.currentItemDisplaySelectedSlot == slot.slotIndex) { + GlStateManager.colorMask(true, true, true, false) + RSBTextures.SLOT_SELECTION.draw(context, -3, -3, 24, 24, widgetTheme) + GlStateManager.colorMask(true, true, true, true) + } + GlStateManager.disableBlend() + GlStateManager.enableDepth() + } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/NoBackgroundItemSlot.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/NoBackgroundItemSlot.kt new file mode 100644 index 0000000..fd670c3 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/slot/NoBackgroundItemSlot.kt @@ -0,0 +1,20 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.slot.ItemSlot +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault + +open class NoBackgroundItemSlot(private val emptyOverlay: IDrawable? = null) : ItemSlot() { + init { + background(IDrawable.EMPTY) + } + + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.drawOverlay(context, widgetTheme) + if (emptyOverlay != null && isSynced && slot.stack.isEmpty) { + emptyOverlay.draw(context, 1, 1, 16, 16, widgetTheme.getThemeOrDefault()) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedExpandedTabWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedExpandedTabWidget.kt index 4e28eb0..56b358b 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedExpandedTabWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedExpandedTabWidget.kt @@ -14,13 +14,18 @@ open class AdvancedExpandedTabWidget( filterSyncKey: String = "adv_common_filter", coveredTabSize: Int = 5, width: Int = 100, + contentX: Int = 8, + contentY: Int = 28, + contentWidth: Int = 88, + contentPadding: Int = 2, + filterWidth: Int = 88, ) : ExpandedUpgradeTabWidget(slotIndex, wrap, coveredTabSize, delegatedIconStack, titleKey, width) where T : IAdvancedFilterable, T : UpgradeWrapper<*> { protected val startingRow: Row = Row() .height(0) .name("starting_row") as Row protected val filterWidget: AdvancedFilterWidget = AdvancedFilterWidget(slotIndex, wrap, filterSyncKey) - .width(88) + .width(filterWidth) .coverChildrenHeight() .name("adv_filter_widget") @@ -31,9 +36,9 @@ open class AdvancedExpandedTabWidget( init { val column = Column() - .pos(8, 28) - .width(88) - .childPadding(2) + .pos(contentX, contentY) + .width(contentWidth) + .childPadding(contentPadding) .child(startingRow) .child(filterWidget) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedFilterWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedFilterWidget.kt index 472d496..085061b 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedFilterWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AdvancedFilterWidget.kt @@ -21,6 +21,7 @@ import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget import com.cleanroommc.retrosophisticatedbackpacks.Tags import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IAdvancedFilterable import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IBasicFilterable +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IContentsFilterable import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.client.gui.drawable.Outline import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget @@ -40,6 +41,11 @@ class AdvancedFilterWidget( CyclicVariantButtonWidget.Variant(IKey.lang("gui.whitelist".asTranslationKey()), RSBTextures.CHECK_ICON), CyclicVariantButtonWidget.Variant(IKey.lang("gui.blacklist".asTranslationKey()), RSBTextures.CROSS_ICON), ) + private val CONTENTS_FILTER_TYPE_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant(IKey.lang("gui.allow".asTranslationKey()), RSBTextures.CHECK_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.block".asTranslationKey()), RSBTextures.CROSS_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.match_backpack_contents".asTranslationKey()), RSBTextures.MATCH_BACKPACK_CONTENTS_ICON), + ) private val MATCH_TYPE_VARIANTS = listOf( CyclicVariantButtonWidget.Variant( @@ -99,18 +105,34 @@ class AdvancedFilterWidget( syncHandler("upgrades", slotIndex) filterTypeButton = CyclicVariantButtonWidget( - FILTER_TYPE_VARIANTS, - filterableWrapper.filterType.ordinal + if (filterableWrapper is IContentsFilterable) CONTENTS_FILTER_TYPE_VARIANTS else FILTER_TYPE_VARIANTS, + filterButtonIndex(), + iconOffset = 1, + buttonWidth = 18, + buttonHeight = 18, + hasCustomTexture = true ) { index -> - filterableWrapper.filterType = IBasicFilterable.FilterType.entries[index] - updateWrapper() + updateFilterType(index) } matchTypeButton = CyclicVariantButtonWidget( MATCH_TYPE_VARIANTS, - filterableWrapper.matchType.ordinal + filterableWrapper.matchType.ordinal, + iconOffset = 1, + buttonWidth = 18, + buttonHeight = 18, + hasCustomTexture = true ) { filterableWrapper.matchType = IAdvancedFilterable.MatchType.entries[it] + if (filterableWrapper.matchType == IAdvancedFilterable.MatchType.ORE_DICT) { + (filterableWrapper as? IContentsFilterable)?.let { contentsFilterable -> + if (contentsFilterable.contentsFilterType == IContentsFilterable.ContentsFilterType.STORAGE) { + contentsFilterable.contentsFilterType = IContentsFilterable.ContentsFilterType.ALLOW + filterTypeButton.selectIndex(contentsFilterable.contentsFilterType.ordinal) + syncContentsFilterType(contentsFilterable.contentsFilterType) + } + } + } updateWrapper() } @@ -118,7 +140,11 @@ class AdvancedFilterWidget( ignoreDurabilityButton = CyclicVariantButtonWidget( IGNORE_DURABILITY_VARIANTS, - if (filterableWrapper.ignoreDurability) 1 else 0 + if (filterableWrapper.ignoreDurability) 1 else 0, + iconOffset = 1, + buttonWidth = 18, + buttonHeight = 18, + hasCustomTexture = true ) { filterableWrapper.ignoreDurability = it == 1 updateWrapper() @@ -127,30 +153,39 @@ class AdvancedFilterWidget( ignoreNBTButton = CyclicVariantButtonWidget( IGNORE_NBT_VARIANTS, - if (filterableWrapper.ignoreNBT) 1 else 0 + if (filterableWrapper.ignoreNBT) 1 else 0, + iconOffset = 1, + buttonWidth = 18, + buttonHeight = 18, + hasCustomTexture = true ) { filterableWrapper.ignoreNBT = it == 1 updateWrapper() } - ignoreDurabilityButton.inEffect = inEffect + ignoreNBTButton.inEffect = inEffect + + val filterSlotCount = filterableWrapper.filterItems.slots + val slotsInRow = if (filterSlotCount > 0) filterableWrapper.slotsInRow.coerceIn(1, filterSlotCount) else 1 + val filterRows = if (filterSlotCount > 0) (filterSlotCount + slotsInRow - 1) / slotsInRow else 1 + val filterWidth = slotsInRow * 18 + val filterHeight = filterRows * 18 // Buttons val buttonRow = Row() - .leftRel(0.5f) - .size(88, 20) - .childPadding(2) + .size(filterWidth, 18) + .childPadding(0) val itemBasedConfigButtonRow = Row() - .childPadding(2) - .size(44, 20) - .left(44) + .childPadding(0) + .size(36, 18) + .left(36) .child(ignoreDurabilityButton) .child(ignoreNBTButton) .setEnabledIfAndEnabled { filterableWrapper.matchType == IAdvancedFilterable.MatchType.ITEM } .name("item_based_button_list") val addOreDictEntryButton = ButtonWidget() - .size(20, 20) + .size(18, 18) .overlay(RSBTextures.ADD_ICON) .onMousePressed { val oreName = oreDictTextField.text @@ -159,7 +194,7 @@ class AdvancedFilterWidget( return@onMousePressed false filterableWrapper.oreDictEntries.add(oreName) - oreDictList.child(OreDictEntryWidget(this, oreName, 77)) + oreDictList.child(OreDictEntryWidget(this, oreName, filterWidth - 11)) oreDictTextField.text = "" updateWrapper() oreDictList.scheduleResize() @@ -173,7 +208,7 @@ class AdvancedFilterWidget( .name("add_ore_dict_button") val removeOreDictEntryButton = ButtonWidget() - .size(20, 20) + .size(18, 18) .overlay(RSBTextures.REMOVE_ICON) .onMousePressed { val focusedOreDictEntry = focusedOreDictEntry @@ -193,9 +228,9 @@ class AdvancedFilterWidget( } val oreDictBasedConfigButtonRow = Row() - .size(44, 20) - .childPadding(2) - .left(44) + .size(36, 18) + .childPadding(0) + .left(36) .child(addOreDictEntryButton) .child(removeOreDictEntryButton) .setEnabledIfAndEnabled { filterableWrapper.matchType == IAdvancedFilterable.MatchType.ORE_DICT } @@ -210,29 +245,31 @@ class AdvancedFilterWidget( // Item-based configuration widgets val slotGroup = SlotGroupWidget().name("${syncKey}s") - slotGroup.coverChildren().leftRel(0.5f) + slotGroup.coverChildren() slotGroup.disableSortButtons() + slotGroup.setEnabledIfAndEnabled { + (filterableWrapper as? IContentsFilterable)?.contentsFilterType != IContentsFilterable.ContentsFilterType.STORAGE + } filterSlots = mutableListOf() - for (i in 0 until 16) { + for (i in 0 until filterSlotCount) { val slot = - PhantomItemSlot().syncHandler("${syncKey}_$slotIndex", i).pos(i % 4 * 18, i / 4 * 18) as PhantomItemSlot + PhantomItemSlot().syncHandler("${syncKey}_$slotIndex", i).pos(i % slotsInRow * 18, i / slotsInRow * 18) as PhantomItemSlot filterSlots.add(slot) slotGroup.child(slot) } itemBasedConfigurationGroup = Column() - .size(88, 85) - .leftRel(0.5f) - .top(24) + .size(filterWidth, filterHeight) + .top(21) .child(slotGroup) .setEnabledIfAndEnabled { filterableWrapper.matchType != IAdvancedFilterable.MatchType.ORE_DICT } .name("item_based_config_group") as Column // Ore-dict-based configuration widgets oreDictTextField = TextFieldWidget() - .size(88, 15) + .size(filterWidth, 15) .leftRel(0.5f) .bottom(3) .tooltipDynamic { @@ -261,15 +298,14 @@ class AdvancedFilterWidget( .tooltipAutoUpdate(true) oreDictList = OreDictRegexListWidget() - .size(82, 65) + .size(filterWidth - 6, maxOf(18, filterHeight - 20)) for (entry in filterableWrapper.oreDictEntries) - oreDictList.child(OreDictEntryWidget(this, entry, 77)) + oreDictList.child(OreDictEntryWidget(this, entry, filterWidth - 11)) oreDictBasedConfigurationGroup = Column() - .size(88, 85) - .leftRel(0.5f) - .top(24) + .size(filterWidth, filterHeight) + .top(21) .child(oreDictList) .child(oreDictTextField) .setEnabledIfAndEnabled { filterableWrapper.matchType == IAdvancedFilterable.MatchType.ORE_DICT } @@ -280,6 +316,34 @@ class AdvancedFilterWidget( .child(oreDictBasedConfigurationGroup) } + private fun filterButtonIndex(): Int = + (filterableWrapper as? IContentsFilterable)?.contentsFilterType?.ordinal ?: filterableWrapper.filterType.ordinal + + private fun updateFilterType(index: Int) { + val contentsFilterable = filterableWrapper as? IContentsFilterable + if (contentsFilterable != null) { + var filterType = IContentsFilterable.ContentsFilterType.entries[index] + if (filterableWrapper.matchType == IAdvancedFilterable.MatchType.ORE_DICT && + filterType == IContentsFilterable.ContentsFilterType.STORAGE + ) { + filterType = filterType.next() + filterTypeButton.selectIndex(filterType.ordinal) + } + contentsFilterable.contentsFilterType = filterType + syncContentsFilterType(filterType) + return + } + + filterableWrapper.filterType = IBasicFilterable.FilterType.entries[index] + updateWrapper() + } + + private fun syncContentsFilterType(filterType: IContentsFilterable.ContentsFilterType) { + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_CONTENTS_FILTERABLE) { + it.writeEnumValue(filterType) + } + } + private fun updateWrapper() { slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_ADVANCED_FILTERABLE) { it.writeEnumValue(filterableWrapper.filterType) @@ -391,4 +455,4 @@ class AdvancedFilterWidget( } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AnvilUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AnvilUpgradeWidget.kt new file mode 100644 index 0000000..1e6ceb1 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/AnvilUpgradeWidget.kt @@ -0,0 +1,152 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.drawable.ItemDrawable +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.modularui.widgets.SlotGroupWidget +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AnvilUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.VanillaTextFieldWidget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.NoBackgroundItemSlot +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.resources.I18n +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack + +class AnvilUpgradeWidget( + slotIndex: Int, + wrapper: AnvilUpgradeWrapper, + stack: ItemStack +) : ExpandedUpgradeTabWidget(slotIndex, wrapper, 5, stack, wrapper.settingsLangKey, width = 103) { + init { + size(103, 92) + child(AnvilNameField().pos(4, 24).size(90, 14)) + val slots = SlotGroupWidget().name("anvil_$slotIndex").disableSortButtons() + slots.size(56, 18).pos(3, 42) + slots.child(NoBackgroundItemSlot().syncHandler("anvil_$slotIndex", 0).pos(0, 0)) + slots.child(NoBackgroundItemSlot().syncHandler("anvil_$slotIndex", 1).pos(38, 0)) + child(slots) + child(AnvilResultWidget().pos(79, 42)) + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + val player = context.mc.player + val result = if (player == null) ItemStack.EMPTY else wrapper.updateRepairOutput(player, player.world) + val theme = widgetTheme.theme + val textFieldBackground = + if (wrapper.getInventory().getStackInSlot(0).isEmpty) RSBTextures.ANVIL_NAME_BACKGROUND_DISABLED + else RSBTextures.ANVIL_NAME_BACKGROUND + textFieldBackground.draw(context, 3, 23, 94, 16, theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 3, 42, 18, 18, widgetTheme.theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 41, 42, 18, 18, widgetTheme.theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 79, 42, 18, 18, widgetTheme.theme) + RSBTextures.ANVIL_PLUS_SIGN.draw(context, 25, 45, 13, 13, theme) + RSBTextures.ANVIL_ARROW.draw(context, 62, 44, 14, 15, theme) + if (!wrapper.getInventory().getStackInSlot(0).isEmpty && result.isEmpty) { + RSBTextures.ANVIL_RED_CROSS.draw(context, 62, 44, 15, 15, theme) + } + drawCost(player, result) + super.draw(context, widgetTheme) + } + + private fun drawCost(player: EntityPlayer?, result: ItemStack) { + val cost = wrapper.maximumCost + if (cost <= 0) { + return + } + val tooExpensive = player != null && cost >= 40 && !player.capabilities.isCreativeMode + val text = when { + tooExpensive -> I18n.format("container.repair.expensive") + result.isEmpty -> return + else -> I18n.format("container.repair.cost", cost) + } + val color = if (tooExpensive || player?.let { !wrapper.canTakeResult(it) } == true) 16736352 else 8453920 + val font = Minecraft.getMinecraft().fontRenderer + val x = 3 + val y = 62 + val maxWidth = 94 + val lines = font.listFormattedStringToWidth(text, maxWidth) + Gui.drawRect(x, y, x + maxWidth, y + lines.size * 12, 1325400064) + var yOffset = 0 + for (line in lines) { + font.drawStringWithShadow(line, x + 2 + (maxWidth - font.getStringWidth(line)) / 2f, y + 2 + yOffset.toFloat(), color) + yOffset += 12 + } + } + + private inner class AnvilNameField : VanillaTextFieldWidget(3, 90, 12) { + init { + textField.setMaxStringLength(50) + textField.setText(wrapper.itemName) + textField.setTextColor(-1) + textField.setDisabledTextColour(-1) + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + drawTextField() + } + + override fun onUpdate() { + super.onUpdate() + if (!isFocused() && textField.text != wrapper.itemName) { + textField.setText(wrapper.itemName) + } + } + + override fun onTextChanged(text: String) { + wrapper.itemName = text + } + + override fun onEditingFinished() { + wrapper.itemName = textField.text + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_ANVIL_ITEM_NAME) { it.writeString(wrapper.itemName) } + } + } + + private inner class AnvilResultWidget : Widget(), Interactable { + init { + size(18) + tooltipDynamic { + val player = context.mc.player + val result = if (player == null) ItemStack.EMPTY else wrapper.updateRepairOutput(player, player.world) + if (result.isEmpty) { + it.addLine(IKey.lang("gui.anvil_no_result".asTranslationKey())) + } else { + it.addLine(IKey.str(result.displayName)) + } + it.pos(RichTooltip.Pos.NEXT_TO_MOUSE) + }.tooltipAutoUpdate(true) + } + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (mouseButton != 0) { + return Interactable.Result.IGNORE + } + val player = context.mc.player ?: return Interactable.Result.IGNORE + if (wrapper.updateRepairOutput(player, player.world).isEmpty || !wrapper.canTakeResult(player)) { + return Interactable.Result.IGNORE + } + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_ANVIL_TAKE_RESULT) {} + Interactable.playButtonClickSound() + return Interactable.Result.SUCCESS + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + val player = context.mc.player ?: return + val result = wrapper.updateRepairOutput(player, player.world) + if (!result.isEmpty) { + ItemDrawable(result).draw(context, 1, 1, 16, 16, widgetTheme.getThemeOrDefault()) + } + super.draw(context, widgetTheme) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicExpandedTabWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicExpandedTabWidget.kt index f427393..26cfbd7 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicExpandedTabWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicExpandedTabWidget.kt @@ -2,8 +2,10 @@ package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade import com.cleanroommc.modularui.widgets.layout.Column import com.cleanroommc.modularui.widgets.layout.Row +import com.cleanroommc.modularui.widgets.slot.PhantomItemSlot import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IBasicFilterable import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.UpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH import net.minecraft.item.ItemStack open class BasicExpandedTabWidget( @@ -14,13 +16,20 @@ open class BasicExpandedTabWidget( filterSyncKey: String = "common_filter", coveredTabSize: Int = 4, width: Int = 75, + contentX: Int = 8, + contentY: Int = 28, + contentWidth: Int = 64, + contentPadding: Int = 2, + filterWidth: Int = 64, + showFilterButton: Boolean = true, + slotFactory: (Int, () -> UpgradeSlotSH?) -> PhantomItemSlot = { _, _ -> PhantomItemSlot() }, ) : ExpandedUpgradeTabWidget(slotIndex, wrap, coveredTabSize, delegatedIconStack, titleKey, width) where T : IBasicFilterable, T : UpgradeWrapper<*> { protected val startingRow: Row = Row() .height(0) .name("starting_row") as Row - protected val filterWidget: BasicFilterWidget = BasicFilterWidget(wrap, slotIndex, filterSyncKey) - .width(64) + protected val filterWidget: BasicFilterWidget = BasicFilterWidget(wrap, slotIndex, filterSyncKey, showFilterButton, slotFactory) + .width(filterWidth) .coverChildrenHeight() .name("filter_widget") @@ -31,12 +40,22 @@ open class BasicExpandedTabWidget( init { val column = Column() - .pos(8, 28) - .width(64) - .childPadding(2) + .pos(contentX, contentY) + .width(contentWidth) + .childPadding(contentPadding) .child(startingRow) .child(filterWidget) child(column) } -} \ No newline at end of file +} + +internal fun filterTabWidth(slotsInRow: Int): Int = + maxOf(75, 3 + slotsInRow.coerceAtLeast(1) * 18 + 6) + +internal fun filterTabSize(filterSlots: Int, slotsInRow: Int, hasTopButtonRow: Boolean = true, hasFilterButtonRow: Boolean = true): Int { + val columns = slotsInRow.coerceAtLeast(1) + val rows = ((filterSlots.coerceAtLeast(1) + columns - 1) / columns).coerceAtLeast(1) + val bottom = 24 + (if (hasTopButtonRow) 20 else 0) + (if (hasFilterButtonRow) 21 else 0) + rows * 18 + 6 + return ((bottom + 29) / 30).coerceAtLeast(3) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicFilterWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicFilterWidget.kt index 92954e0..429436a 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicFilterWidget.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BasicFilterWidget.kt @@ -6,15 +6,19 @@ import com.cleanroommc.modularui.widget.ParentWidget import com.cleanroommc.modularui.widgets.SlotGroupWidget import com.cleanroommc.modularui.widgets.slot.PhantomItemSlot import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IBasicFilterable +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IContentsFilterable import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.setEnabledIfAndEnabled class BasicFilterWidget( var filterableWrapper: IBasicFilterable, slotIndex: Int, - syncKey: String = "common_filter" + syncKey: String = "common_filter", + private val showFilterButton: Boolean = true, + private val slotFactory: (Int, () -> UpgradeSlotSH?) -> PhantomItemSlot = { _, _ -> PhantomItemSlot() } ) : ParentWidget() { companion object { @@ -22,6 +26,11 @@ class BasicFilterWidget( CyclicVariantButtonWidget.Variant(IKey.lang("gui.whitelist".asTranslationKey()), RSBTextures.CHECK_ICON), CyclicVariantButtonWidget.Variant(IKey.lang("gui.blacklist".asTranslationKey()), RSBTextures.CROSS_ICON), ) + private val CONTENTS_FILTER_TYPE_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant(IKey.lang("gui.allow".asTranslationKey()), RSBTextures.CHECK_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.block".asTranslationKey()), RSBTextures.CROSS_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.match_backpack_contents".asTranslationKey()), RSBTextures.MATCH_BACKPACK_CONTENTS_ICON), + ) } private val filterTypeButton: CyclicVariantButtonWidget @@ -33,31 +42,60 @@ class BasicFilterWidget( syncHandler("upgrades", slotIndex) filterTypeButton = CyclicVariantButtonWidget( - FILTER_TYPE_VARIANTS, - filterableWrapper.filterType.ordinal + if (filterableWrapper is IContentsFilterable) CONTENTS_FILTER_TYPE_VARIANTS else FILTER_TYPE_VARIANTS, + filterButtonIndex(), + iconOffset = 1, + buttonWidth = 18, + buttonHeight = 18, + hasCustomTexture = true ) { index -> - filterableWrapper.filterType = IBasicFilterable.FilterType.entries[index] - slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_BASIC_FILTERABLE) { - it.writeEnumValue(filterableWrapper.filterType) - } + updateFilterType(index) } - .size(20, 20) + .size(18, 18) val slotGroup = SlotGroupWidget().name("${syncKey}s") - slotGroup.coverChildren().top(26) + slotGroup.coverChildren().top(if (showFilterButton) 21 else 0) slotGroup.disableSortButtons() + slotGroup.setEnabledIfAndEnabled { + (filterableWrapper as? IContentsFilterable)?.contentsFilterType != IContentsFilterable.ContentsFilterType.STORAGE + } filterSlots = mutableListOf() - for (i in 0 until 9) { + val filterSlotCount = filterableWrapper.filterItems.slots + val slotsInRow = if (filterSlotCount > 0) filterableWrapper.slotsInRow.coerceIn(1, filterSlotCount) else 1 + for (i in 0 until filterSlotCount) { val slot = - PhantomItemSlot().syncHandler("${syncKey}_$slotIndex", i).pos(i % 3 * 18, i / 3 * 18) as PhantomItemSlot + slotFactory(i) { slotSyncHandler }.syncHandler("${syncKey}_$slotIndex", i) + .pos(i % slotsInRow * 18, i / slotsInRow * 18) as PhantomItemSlot filterSlots.add(slot) slotGroup.child(slot) } - child(filterTypeButton) - .child(slotGroup) + if (showFilterButton) { + child(filterTypeButton) + } + child(slotGroup) + } + + private fun filterButtonIndex(): Int = + (filterableWrapper as? IContentsFilterable)?.contentsFilterType?.ordinal ?: filterableWrapper.filterType.ordinal + + private fun updateFilterType(index: Int) { + val contentsFilterable = filterableWrapper as? IContentsFilterable + if (contentsFilterable != null) { + val filterType = IContentsFilterable.ContentsFilterType.entries[index] + contentsFilterable.contentsFilterType = filterType + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_CONTENTS_FILTERABLE) { + it.writeEnumValue(filterType) + } + return + } + + filterableWrapper.filterType = IBasicFilterable.FilterType.entries[index] + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_BASIC_FILTERABLE) { + it.writeEnumValue(filterableWrapper.filterType) + } } override fun isValidSyncOrValue(syncHandler: ISyncOrValue): Boolean { @@ -65,4 +103,4 @@ class BasicFilterWidget( slotSyncHandler = syncHandler return slotSyncHandler != null } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BatteryUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BatteryUpgradeWidget.kt new file mode 100644 index 0000000..3163bda --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/BatteryUpgradeWidget.kt @@ -0,0 +1,30 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.SlotGroupWidget +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.BatteryUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.NoBackgroundItemSlot +import net.minecraft.item.ItemStack + +class BatteryUpgradeWidget( + slotIndex: Int, + wrapper: BatteryUpgradeWrapper, + stack: ItemStack +) : ExpandedUpgradeTabWidget(slotIndex, wrapper, 3, stack, wrapper.settingsLangKey, width = 48) { + init { + size(48, 48) + val slots = SlotGroupWidget().name("battery_$slotIndex").disableSortButtons() + slots.size(42, 18).pos(3, 24) + slots.child(NoBackgroundItemSlot(RSBTextures.EMPTY_BATTERY_INPUT_SLOT).syncHandler("battery_$slotIndex", 0).pos(0, 0)) + slots.child(NoBackgroundItemSlot(RSBTextures.EMPTY_BATTERY_OUTPUT_SLOT).syncHandler("battery_$slotIndex", 1).pos(21, 0)) + child(slots) + } + + override fun drawBackground(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + super.drawBackground(context, widgetTheme) + RSBTextures.SLOT_BACKGROUND.draw(context, 3, 24, 18, 18, widgetTheme.theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 24, 24, 18, 18, widgetTheme.theme) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/CompactingUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/CompactingUpgradeWidget.kt new file mode 100644 index 0000000..0e384e6 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/CompactingUpgradeWidget.kt @@ -0,0 +1,83 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedCompactingUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.CompactingUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack + +class CompactingUpgradeWidget(slotIndex: Int, wrapper: CompactingUpgradeWrapper, stack: ItemStack) : + BasicExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createCompactModeButton(wrapper.compactNonUncraftable)) + .child(createWorkInGuiButton(wrapper.shouldWorkInGui) { + wrapper.shouldWorkInGui = !wrapper.shouldWorkInGui + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_COMPACT_WORK_IN_GUI) {} + }) + } + + private fun createCompactModeButton(compactNonUncraftable: Boolean): CyclicVariantButtonWidget = + tabIconButton(COMPACT_MODE_VARIANTS, if (compactNonUncraftable) 1 else 0) { + wrapper.compactNonUncraftable = !wrapper.compactNonUncraftable + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_COMPACT_NON_UNCRAFTABLE) {} + } +} + +class AdvancedCompactingUpgradeWidget(slotIndex: Int, wrapper: AdvancedCompactingUpgradeWrapper, stack: ItemStack) : + AdvancedExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createCompactModeButton(wrapper.compactNonUncraftable)) + .child(createWorkInGuiButton(wrapper.shouldWorkInGui) { + wrapper.shouldWorkInGui = !wrapper.shouldWorkInGui + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_COMPACT_WORK_IN_GUI) {} + }) + } + + private fun createCompactModeButton(compactNonUncraftable: Boolean): CyclicVariantButtonWidget = + tabIconButton(COMPACT_MODE_VARIANTS, if (compactNonUncraftable) 1 else 0) { + wrapper.compactNonUncraftable = !wrapper.compactNonUncraftable + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_COMPACT_NON_UNCRAFTABLE) {} + } +} + +private val COMPACT_MODE_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.compact_only_uncraftable".asTranslationKey()), + RSBTextures.COMPACT_ONLY_UNCRAFTABLE_ICON + ), + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.compact_anything".asTranslationKey()), + RSBTextures.COMPACT_ANYTHING_ICON + ), +) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/JukeboxUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/JukeboxUpgradeWidget.kt new file mode 100644 index 0000000..b52bd34 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/JukeboxUpgradeWidget.kt @@ -0,0 +1,189 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.SlotGroupWidget +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedJukeboxUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.DiscHandlerRegistry +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.JukeboxUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.RepeatMode +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.DynamicIconButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.NoBackgroundItemSlot +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.item.ItemStack + +class JukeboxUpgradeWidget( + slotIndex: Int, + wrapper: JukeboxUpgradeWrapper, + stack: ItemStack +) : BaseJukeboxUpgradeWidget(slotIndex, wrapper, stack, 1, 4, 3) { + init { + size(TAB_WIDTH, 70) + addDiscSlots(slotIndex) + val bottomSlotY = getBottomSlotY() + child(commandButton(UpgradeSlotSH.UPDATE_JUKEBOX_STOP, "gui.jukebox_stop", RSBTextures.JUKEBOX_STOP_ICON).pos(3, bottomSlotY + BUTTON_PADDING)) + child(commandButton(UpgradeSlotSH.UPDATE_JUKEBOX_PLAY, "gui.jukebox_play", RSBTextures.JUKEBOX_PLAY_ICON).pos(21, bottomSlotY + BUTTON_PADDING)) + } +} + +class AdvancedJukeboxUpgradeWidget( + slotIndex: Int, + wrapper: AdvancedJukeboxUpgradeWrapper, + stack: ItemStack +) : BaseJukeboxUpgradeWidget( + slotIndex, + wrapper, + stack, + wrapper.discInventory.slots, + Config.advancedJukeboxUpgrade.slotsInRow.coerceIn(1, wrapper.discInventory.slots), + 5 +) { + init { + size(TAB_WIDTH, 124) + addDiscSlots(slotIndex) + val bottomSlotY = getBottomSlotY() + child(commandButton(UpgradeSlotSH.UPDATE_JUKEBOX_PREVIOUS, "gui.jukebox_previous", RSBTextures.JUKEBOX_PREVIOUS_ICON).pos(3, bottomSlotY + BUTTON_PADDING)) + child(commandButton(UpgradeSlotSH.UPDATE_JUKEBOX_STOP, "gui.jukebox_stop", RSBTextures.JUKEBOX_STOP_ICON).pos(21, bottomSlotY + BUTTON_PADDING)) + child(commandButton(UpgradeSlotSH.UPDATE_JUKEBOX_PLAY, "gui.jukebox_play", RSBTextures.JUKEBOX_PLAY_ICON).pos(39, bottomSlotY + BUTTON_PADDING)) + child(commandButton(UpgradeSlotSH.UPDATE_JUKEBOX_NEXT, "gui.jukebox_next", RSBTextures.JUKEBOX_NEXT_ICON).pos(57, bottomSlotY + BUTTON_PADDING)) + child(shuffleButton().pos(12, bottomSlotY + BUTTON_PADDING + 20)) + child(repeatButton().pos(48, bottomSlotY + BUTTON_PADDING + 20)) + } + + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.drawOverlay(context, widgetTheme) + val activeSlot = wrapper.getDiscSlotActive() + if (context == null || widgetTheme == null || activeSlot !in 0 until slotCount) { + return + } + val remainingProgress = getPlaybackRemainingProgress() + if (remainingProgress <= 0f) { + return + } + val slotX = 4 + activeSlot % slotsInRow * SLOT_SIZE + val slotY = TOP_Y + 1 + activeSlot / slotsInRow * SLOT_SIZE + val progressOver = 16 - (16 * remainingProgress).toInt() + GlStateManager.disableDepth() + Gui.drawRect(slotX + progressOver, slotY, slotX + 16, slotY + 16, PLAYBACK_OVERLAY_COLOR) + GlStateManager.enableDepth() + } + + private fun getPlaybackRemainingProgress(): Float { + val world = Minecraft.getMinecraft().world ?: return 0f + val length = DiscHandlerRegistry.getMusicLengthInTicks(wrapper.getDisc(), world) ?: return 0f + if (length <= 0L) { + return 0f + } + return ((wrapper.getDiscFinishTime() - world.totalWorldTime).toFloat() / length).coerceIn(0f, 1f) + } +} + +abstract class BaseJukeboxUpgradeWidget( + slotIndex: Int, + wrapper: U, + stack: ItemStack, + protected val slotCount: Int, + protected val slotsInRow: Int, + coveredTabSize: Int +) : ExpandedUpgradeTabWidget(slotIndex, wrapper, coveredTabSize, stack, wrapper.settingsLangKey, width = TAB_WIDTH) { + protected fun addDiscSlots(slotIndex: Int) { + val discs = SlotGroupWidget().name("jukebox_discs_$slotIndex").disableSortButtons() + discs.size(slotsInRow * SLOT_SIZE, getSlotRows() * SLOT_SIZE).pos(3, TOP_Y) + repeat(slotCount) { + discs.child(NoBackgroundItemSlot().syncHandler("jukebox_discs_$slotIndex", it).pos(it % slotsInRow * SLOT_SIZE, it / slotsInRow * SLOT_SIZE)) + } + child(discs) + } + + protected fun commandButton(syncId: Int, langKey: String, icon: IDrawable): DynamicIconButtonWidget = + DynamicIconButtonWidget({ icon }) + .size(BUTTON_SIZE) + .onMousePressed { + if (it != 0) { + false + } else { + slotSyncHandler?.syncToServer(syncId) {} + Interactable.playButtonClickSound() + true + } + } + .tooltipStatic { + it.addLine(IKey.lang(langKey.asTranslationKey())).pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } + + protected fun shuffleButton(): CyclicVariantButtonWidget = + CyclicVariantButtonWidget( + SHUFFLE_VARIANTS, + if (wrapper.shuffleEnabled) 1 else 0, + iconOffset = 1, + iconSize = 16, + buttonWidth = BUTTON_SIZE, + buttonHeight = BUTTON_SIZE + ) { + wrapper.toggleShuffle() + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_JUKEBOX_SHUFFLE) {} + } + + protected fun repeatButton(): CyclicVariantButtonWidget = + CyclicVariantButtonWidget( + REPEAT_VARIANTS, + wrapper.repeatMode.ordinal, + iconOffset = 1, + iconSize = 16, + buttonWidth = BUTTON_SIZE, + buttonHeight = BUTTON_SIZE + ) { + wrapper.repeatMode = RepeatMode.entries[it] + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_JUKEBOX_REPEAT_MODE) { packet -> + packet.writeEnumValue(wrapper.repeatMode) + } + } + + override fun drawBackground(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + super.drawBackground(context, widgetTheme) + repeat(slotCount) { + RSBTextures.SLOT_BACKGROUND.draw( + context, + 3 + it % slotsInRow * SLOT_SIZE, + TOP_Y + it / slotsInRow * SLOT_SIZE, + SLOT_SIZE, + SLOT_SIZE, + widgetTheme.getThemeOrDefault() + ) + } + } + + protected fun getBottomSlotY(): Int = TOP_Y + getSlotRows() * SLOT_SIZE + + private fun getSlotRows(): Int = (slotCount + slotsInRow - 1) / slotsInRow +} + +private const val TOP_Y = 24 +private const val TAB_WIDTH = 80 +private const val BUTTON_SIZE = 18 +private const val SLOT_SIZE = 18 +private const val BUTTON_PADDING = 3 +private const val PLAYBACK_OVERLAY_COLOR = 0x5500CC00 + +private val SHUFFLE_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant(IKey.lang("gui.jukebox_shuffle_disabled".asTranslationKey()), RSBTextures.JUKEBOX_SHUFFLE_OFF_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.jukebox_shuffle_enabled".asTranslationKey()), RSBTextures.JUKEBOX_SHUFFLE_ON_ICON), +) + +private val REPEAT_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant(IKey.lang("gui.jukebox_repeat_all".asTranslationKey()), RSBTextures.JUKEBOX_REPEAT_ALL_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.jukebox_repeat_one".asTranslationKey()), RSBTextures.JUKEBOX_REPEAT_ONE_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.jukebox_repeat_no".asTranslationKey()), RSBTextures.JUKEBOX_NO_REPEAT_ICON), +) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/MagnetUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/MagnetUpgradeWidget.kt new file mode 100644 index 0000000..5e61e0a --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/MagnetUpgradeWidget.kt @@ -0,0 +1,69 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedMagnetUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.MagnetUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack + +class MagnetUpgradeWidget(slotIndex: Int, wrapper: MagnetUpgradeWrapper, stack: ItemStack) : + BasicExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createPickupItemsButton(wrapper.pickupItems)) + } + + private fun createPickupItemsButton(pickupItems: Boolean): CyclicVariantButtonWidget = + tabIconButton(PICKUP_ITEMS_VARIANTS, if (pickupItems) 0 else 1) { + wrapper.pickupItems = !wrapper.pickupItems + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_MAGNET_PICKUP_ITEMS) {} + } +} + +class AdvancedMagnetUpgradeWidget(slotIndex: Int, wrapper: AdvancedMagnetUpgradeWrapper, stack: ItemStack) : + AdvancedExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createPickupItemsButton(wrapper.pickupItems)) + } + + private fun createPickupItemsButton(pickupItems: Boolean): CyclicVariantButtonWidget = + tabIconButton(PICKUP_ITEMS_VARIANTS, if (pickupItems) 0 else 1) { + wrapper.pickupItems = !wrapper.pickupItems + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_MAGNET_PICKUP_ITEMS) {} + } +} + +private val PICKUP_ITEMS_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant(IKey.lang("gui.pickup_items".asTranslationKey()), RSBTextures.MAGNET_PICKUP_ITEMS_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.do_not_pickup_items".asTranslationKey()), RSBTextures.MAGNET_NO_PICKUP_ITEMS_ICON), +) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/PumpUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/PumpUpgradeWidget.kt new file mode 100644 index 0000000..4ee869a --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/PumpUpgradeWidget.kt @@ -0,0 +1,112 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IDrawable +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.api.widget.Interactable +import com.cleanroommc.modularui.drawable.GuiDraw +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widget.Widget +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.PumpUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.DynamicIconButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.getThemeOrDefault +import net.minecraft.item.ItemStack + +open class PumpUpgradeWidget( + slotIndex: Int, + wrapper: PumpUpgradeWrapper, + stack: ItemStack +) : ExpandedUpgradeTabWidget(slotIndex, wrapper, 3, stack, wrapper.settingsLangKey, width = 48) { + init { + size(48, 50) + child(toggleButton({ wrapper.isInput }, RSBTextures.PUMP_INPUT_ICON, RSBTextures.PUMP_OUTPUT_ICON, "gui.pump_input", "gui.pump_output") { + wrapper.isInput = !wrapper.isInput + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_PUMP_INPUT) { it.writeBoolean(wrapper.isInput) } + }.pos(3, 24)) + } + + protected fun toggleButton( + state: () -> Boolean, + enabledIcon: IDrawable, + disabledIcon: IDrawable, + enabledTooltip: String, + disabledTooltip: String, + action: () -> Unit + ): DynamicIconButtonWidget = + DynamicIconButtonWidget({ if (state()) enabledIcon else disabledIcon }) + .size(18) + .onMousePressed { + if (it != 0) false else { + action() + Interactable.playButtonClickSound() + true + } + } + .tooltipAutoUpdate(true) + .tooltipDynamic { + val key = if (state()) enabledTooltip else disabledTooltip + it.addLine(IKey.lang(key.asTranslationKey())) + .addLine(IKey.lang("${key}_detail".asTranslationKey()).style(IKey.GRAY)) + .pos(RichTooltip.Pos.NEXT_TO_MOUSE) + } +} + +class AdvancedPumpUpgradeWidget(slotIndex: Int, wrapper: PumpUpgradeWrapper, stack: ItemStack) : + PumpUpgradeWidget(slotIndex, wrapper, stack) { + init { + size(84, 82) + width(84) + child(toggleButton({ wrapper.interactWithFluidHandlers }, RSBTextures.PUMP_FLUID_HANDLER_ICON, RSBTextures.PUMP_NO_FLUID_HANDLER_ICON, "gui.pump_fluid_handlers", "gui.pump_no_fluid_handlers") { + wrapper.interactWithFluidHandlers = !wrapper.interactWithFluidHandlers + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_PUMP_FLUID_HANDLERS) {} + }.pos(21, 24)) + child(toggleButton({ wrapper.interactWithWorld }, RSBTextures.PUMP_WORLD_ICON, RSBTextures.PUMP_NO_WORLD_ICON, "gui.pump_world", "gui.pump_no_world") { + wrapper.interactWithWorld = !wrapper.interactWithWorld + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_PUMP_WORLD) {} + }.pos(39, 24)) + child(toggleButton({ wrapper.interactWithHand }, RSBTextures.PUMP_HAND_ICON, RSBTextures.PUMP_NO_HAND_ICON, "gui.pump_hand", "gui.pump_no_hand") { + wrapper.interactWithHand = !wrapper.interactWithHand + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_PUMP_HAND) {} + }.pos(57, 24)) + for (slot in wrapper.fluidFilters.indices) { + child(FluidFilterSlotWidget(slot, wrapper).pos(3 + slot * 18, 44)) + } + } + + private inner class FluidFilterSlotWidget( + private val filterSlot: Int, + private val pumpWrapper: PumpUpgradeWrapper + ) : Widget(), Interactable { + init { + size(18) + tooltipDynamic { + val fluid = pumpWrapper.fluidFilters.getOrNull(filterSlot) + it.addLine(if (fluid == null) IKey.lang("gui.none".asTranslationKey()) else IKey.str(fluid.localizedName)) + it.addLine(IKey.lang("gui.pump_fluid_filter_detail".asTranslationKey()).style(IKey.GRAY)) + it.pos(RichTooltip.Pos.NEXT_TO_MOUSE) + }.tooltipAutoUpdate(true) + } + + override fun onMousePressed(mouseButton: Int): Interactable.Result { + if (mouseButton != 0 && mouseButton != 1) { + return Interactable.Result.IGNORE + } + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_PUMP_FLUID_FILTER) { it.writeInt(filterSlot) } + Interactable.playButtonClickSound() + return Interactable.Result.SUCCESS + } + + override fun draw(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + RSBTextures.SLOT_BACKGROUND.draw(context, 0, 0, 18, 18, widgetTheme.getThemeOrDefault()) + val fluid = pumpWrapper.fluidFilters.getOrNull(filterSlot) + if (fluid != null) { + GuiDraw.drawFluidTexture(fluid, 1f, 1f, 16f, 16f, context.currentDrawingZ.toFloat()) + } + super.draw(context, widgetTheme) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/RefillUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/RefillUpgradeWidget.kt new file mode 100644 index 0000000..402014e --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/RefillUpgradeWidget.kt @@ -0,0 +1,114 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.UpOrDown +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.modularui.screen.RichTooltip +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.slot.PhantomItemSlot +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedRefillUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.RefillUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.resources.I18n +import net.minecraft.item.ItemStack +import net.minecraft.util.text.TextFormatting + +class RefillUpgradeWidget(slotIndex: Int, wrapper: RefillUpgradeWrapper, stack: ItemStack) : + BasicExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = refillCoveredTabSize(wrapper), + width = refillTabWidth(wrapper), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18, + showFilterButton = false + ) { + init { + startingRow.height(0) + } +} + +class AdvancedRefillUpgradeWidget(slotIndex: Int, wrapper: AdvancedRefillUpgradeWrapper, stack: ItemStack) : + BasicExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + filterSyncKey = "adv_common_filter", + coveredTabSize = refillCoveredTabSize(wrapper), + width = refillTabWidth(wrapper), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18, + showFilterButton = false, + slotFactory = { filterSlot, syncHandler -> RefillTargetSlot(wrapper, filterSlot, syncHandler) } + ) { + init { + startingRow.height(0) + } +} + +private fun refillTabWidth(wrapper: RefillUpgradeWrapper): Int = + maxOf(75, 3 + wrapper.slotsInRow * 18 + 6) + +private fun refillCoveredTabSize(wrapper: RefillUpgradeWrapper): Int { + val slotsInRow = wrapper.slotsInRow.coerceAtLeast(1) + val rows = (wrapper.filterItems.slots + slotsInRow - 1) / slotsInRow + return ((24 + rows * 18 + 6 + 29) / 30).coerceAtLeast(3) +} + +private class RefillTargetSlot( + private val wrapper: AdvancedRefillUpgradeWrapper, + private val filterSlot: Int, + private val upgradeSyncHandler: () -> UpgradeSlotSH? +) : PhantomItemSlot() { + override fun onMouseScroll(scrollDirection: UpOrDown, amount: Int): Boolean { + if (slot.stack.isEmpty) { + return super.onMouseScroll(scrollDirection, amount) + } + + val targetSlot = if (scrollDirection.isUp) wrapper.getTargetSlot(filterSlot).next() + else wrapper.getTargetSlot(filterSlot).previous() + wrapper.setTargetSlot(filterSlot, targetSlot) + upgradeSyncHandler()?.syncToServer(UpgradeSlotSH.UPDATE_REFILL_TARGET_SLOT) { + it.writeInt(filterSlot) + it.writeEnumValue(targetSlot) + } + markTooltipDirty() + return true + } + + override fun drawOverlay(context: ModularGuiContext?, widgetTheme: WidgetThemeEntry<*>?) { + super.drawOverlay(context, widgetTheme) + if (!isSynced || slot.stack.isEmpty) { + return + } + + GlStateManager.disableLighting() + GlStateManager.disableDepth() + Minecraft.getMinecraft().fontRenderer.drawString(wrapper.getTargetSlot(filterSlot).acronym(), 10, 2, 0x55FF55) + GlStateManager.enableDepth() + } + + override fun buildTooltip(stack: ItemStack, tooltip: RichTooltip) { + super.buildTooltip(stack, tooltip) + if (stack.isEmpty) { + return + } + + val targetSlot = wrapper.getTargetSlot(filterSlot) + val targetDescription = I18n.format(targetSlot.descriptionKey()) + tooltip.addLine(IKey.str(I18n.format("gui.refill_target_tooltip".asTranslationKey(), targetDescription)).style(targetSlot.descriptionColor())) + .addLine(IKey.lang("gui.refill_scroll_tooltip".asTranslationKey()).style(TextFormatting.DARK_GRAY, TextFormatting.ITALIC)) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/TankUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/TankUpgradeWidget.kt new file mode 100644 index 0000000..44059be --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/TankUpgradeWidget.kt @@ -0,0 +1,34 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext +import com.cleanroommc.modularui.theme.WidgetThemeEntry +import com.cleanroommc.modularui.widgets.SlotGroupWidget +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.TankUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.slot.NoBackgroundItemSlot +import net.minecraft.item.ItemStack + +class TankUpgradeWidget(slotIndex: Int, wrapper: TankUpgradeWrapper, stack: ItemStack) : + ExpandedUpgradeTabWidget(slotIndex, wrapper, 3, stack, wrapper.settingsLangKey, width = 48) { + init { + size(48, 80) + + val slots = SlotGroupWidget().name("tank_$slotIndex").disableSortButtons() + slots.size(42, 50).pos(3, 24) + slots.child(NoBackgroundItemSlot(RSBTextures.EMPTY_TANK_INPUT_SLOT).syncHandler("tank_$slotIndex", 0).pos(0, 0)) + slots.child(NoBackgroundItemSlot(RSBTextures.EMPTY_TANK_OUTPUT_SLOT).syncHandler("tank_$slotIndex", 1).pos(21, 0)) + slots.child(NoBackgroundItemSlot().syncHandler("tank_$slotIndex", 2).pos(0, 32)) + slots.child(NoBackgroundItemSlot().syncHandler("tank_$slotIndex", 3).pos(21, 32)) + child(slots) + } + + override fun drawBackground(context: ModularGuiContext, widgetTheme: WidgetThemeEntry<*>) { + super.drawBackground(context, widgetTheme) + RSBTextures.SLOT_BACKGROUND.draw(context, 3, 24, 18, 18, widgetTheme.theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 24, 24, 18, 18, widgetTheme.theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 3, 56, 18, 18, widgetTheme.theme) + RSBTextures.SLOT_BACKGROUND.draw(context, 24, 56, 18, 18, widgetTheme.theme) + RSBTextures.TANK_ARROW.draw(context, 4, 45, 15, 8, widgetTheme.theme) + RSBTextures.TANK_ARROW.draw(context, 25, 45, 15, 8, widgetTheme.theme) + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/ToolSwapperUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/ToolSwapperUpgradeWidget.kt new file mode 100644 index 0000000..9c1d6e6 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/ToolSwapperUpgradeWidget.kt @@ -0,0 +1,77 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedToolSwapperUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.ToolSwapMode +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack + +class AdvancedToolSwapperUpgradeWidget(slotIndex: Int, wrapper: AdvancedToolSwapperUpgradeWrapper, stack: ItemStack) : + AdvancedExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createSwapWeaponButton()) + .child(createToolSwapModeButton()) + } + + private fun createSwapWeaponButton(): CyclicVariantButtonWidget = + tabIconButton(SWAP_WEAPON_VARIANTS, if (wrapper.shouldSwapWeapon) 1 else 0) { + wrapper.shouldSwapWeapon = !wrapper.shouldSwapWeapon + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_TOOL_SWAPPER_SWAP_WEAPON) {} + } + + private fun createToolSwapModeButton(): CyclicVariantButtonWidget = + tabIconButton(TOOL_SWAP_MODE_VARIANTS, wrapper.toolSwapMode.ordinal) { + wrapper.toolSwapMode = ToolSwapMode.entries[it] + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_TOOL_SWAPPER_MODE) { + it.writeEnumValue(wrapper.toolSwapMode) + } + } +} + +private val SWAP_WEAPON_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.tool_swapper_swap_weapon_disabled".asTranslationKey()), + RSBTextures.TOOL_SWAPPER_DO_NOT_SWAP_WEAPON_ICON, + listOf(IKey.lang("gui.tool_swapper_swap_weapon_disabled.detail".asTranslationKey()).style(IKey.GRAY)) + ), + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.tool_swapper_swap_weapon_enabled".asTranslationKey()), + RSBTextures.TOOL_SWAPPER_SWAP_WEAPON_ICON, + listOf(IKey.lang("gui.tool_swapper_swap_weapon_enabled.detail".asTranslationKey()).style(IKey.GRAY)) + ), +) + +private val TOOL_SWAP_MODE_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.tool_swapper_any".asTranslationKey()), + RSBTextures.TOOL_SWAPPER_SWAP_TOOLS_ICON, + listOf(IKey.lang("gui.tool_swapper_any.detail".asTranslationKey()).style(IKey.GRAY)) + ), + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.tool_swapper_only_tools".asTranslationKey()), + RSBTextures.TOOL_SWAPPER_ONLY_TOOLS_ICON, + listOf(IKey.lang("gui.tool_swapper_only_tools.detail".asTranslationKey()).style(IKey.GRAY)) + ), + CyclicVariantButtonWidget.Variant( + IKey.lang("gui.tool_swapper_no_swap".asTranslationKey()), + RSBTextures.TOOL_SWAPPER_NO_SWAP_ICON, + listOf(IKey.lang("gui.tool_swapper_no_swap.detail".asTranslationKey()).style(IKey.GRAY)) + ), +) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/VoidUpgradeWidget.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/VoidUpgradeWidget.kt new file mode 100644 index 0000000..554c9a4 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/client/gui/widgets/upgrade/VoidUpgradeWidget.kt @@ -0,0 +1,129 @@ +package com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.upgrade + +import com.cleanroommc.modularui.api.drawable.IKey +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedVoidUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.VoidType +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.VoidUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.RSBTextures +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.CyclicVariantButtonWidget +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.sync.UpgradeSlotSH +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.item.ItemStack +import net.minecraft.util.text.TextFormatting + +class VoidUpgradeWidget(slotIndex: Int, wrapper: VoidUpgradeWrapper, stack: ItemStack) : + BasicExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createWorkInGuiButton(wrapper.shouldWorkInGui) { + wrapper.shouldWorkInGui = !wrapper.shouldWorkInGui + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_VOID_WORK_IN_GUI) {} + }) + .child(createVoidTypeButton(wrapper.voidType, Config.voidUpgrade.voidAlwaysEnabled)) + } + + private fun createVoidTypeButton(current: VoidType, alwaysEnabled: Boolean): CyclicVariantButtonWidget { + val voidTypes = allowedVoidTypes(alwaysEnabled) + return tabIconButton(voidTypes.map(::voidVariant), voidTypes.indexOf(current).coerceAtLeast(0)) { index -> + wrapper.voidType = voidTypes[index] + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_VOID_TYPE) { + it.writeEnumValue(voidTypes[index]) + } + } + } +} + +class AdvancedVoidUpgradeWidget(slotIndex: Int, wrapper: AdvancedVoidUpgradeWrapper, stack: ItemStack) : + AdvancedExpandedTabWidget( + slotIndex, + wrapper, + stack, + wrapper.settingsLangKey, + coveredTabSize = filterTabSize(wrapper.filterItems.slots, wrapper.slotsInRow), + width = filterTabWidth(wrapper.slotsInRow), + contentX = 3, + contentY = 24, + contentWidth = wrapper.slotsInRow * 18, + contentPadding = 0, + filterWidth = wrapper.slotsInRow * 18 + ) { + init { + startingRow + .height(20) + .child(createWorkInGuiButton(wrapper.shouldWorkInGui) { + wrapper.shouldWorkInGui = !wrapper.shouldWorkInGui + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_VOID_WORK_IN_GUI) {} + }) + .child(createVoidTypeButton(wrapper.voidType, Config.advancedVoidUpgrade.voidAlwaysEnabled)) + } + + private fun createVoidTypeButton(current: VoidType, alwaysEnabled: Boolean): CyclicVariantButtonWidget { + val voidTypes = allowedVoidTypes(alwaysEnabled) + return tabIconButton(voidTypes.map(::voidVariant), voidTypes.indexOf(current).coerceAtLeast(0)) { index -> + wrapper.voidType = voidTypes[index] + slotSyncHandler?.syncToServer(UpgradeSlotSH.UPDATE_VOID_TYPE) { + it.writeEnumValue(voidTypes[index]) + } + } + } +} + +internal fun createWorkInGuiButton(shouldWorkInGui: Boolean, toggle: () -> Unit): CyclicVariantButtonWidget = + tabIconButton(WORK_IN_GUI_VARIANTS, if (shouldWorkInGui) 1 else 0) { + toggle() + } + +internal fun tabIconButton( + variants: List, + index: Int, + updater: CyclicVariantButtonWidget.(Int) -> Unit +): CyclicVariantButtonWidget = + CyclicVariantButtonWidget( + variants, + index, + iconOffset = 1, + buttonWidth = 18, + buttonHeight = 18, + hasCustomTexture = true, + mousePressedUpdater = updater + ) + +private fun allowedVoidTypes(alwaysEnabled: Boolean): List = + if (alwaysEnabled) VoidType.entries else listOf(VoidType.SLOT_OVERFLOW, VoidType.STORAGE_OVERFLOW) + +private fun voidVariant(type: VoidType): CyclicVariantButtonWidget.Variant = + when (type) { + VoidType.ALWAYS -> CyclicVariantButtonWidget.Variant( + IKey.lang("gui.void_always".asTranslationKey()), + RSBTextures.VOID_ALWAYS_ICON + ) + VoidType.SLOT_OVERFLOW -> CyclicVariantButtonWidget.Variant( + IKey.lang("gui.void_slot_overflow".asTranslationKey()), + RSBTextures.VOID_SLOT_OVERFLOW_ICON, + listOf(IKey.lang("gui.void_slot_overflow_detail".asTranslationKey()).style(TextFormatting.GRAY)) + ) + VoidType.STORAGE_OVERFLOW -> CyclicVariantButtonWidget.Variant( + IKey.lang("gui.void_storage_overflow".asTranslationKey()), + RSBTextures.VOID_STORAGE_OVERFLOW_ICON, + listOf(IKey.lang("gui.void_storage_overflow_detail".asTranslationKey()).style(TextFormatting.GRAY)) + ) + } + +private val WORK_IN_GUI_VARIANTS = listOf( + CyclicVariantButtonWidget.Variant(IKey.lang("gui.only_automatic".asTranslationKey()), RSBTextures.WORK_IN_GUI_OFF_ICON), + CyclicVariantButtonWidget.Variant(IKey.lang("gui.works_in_gui".asTranslationKey()), RSBTextures.WORK_IN_GUI_ON_ICON), +) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackContainer.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackContainer.kt index 513e771..85f2ba4 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackContainer.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackContainer.kt @@ -3,6 +3,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.common.gui import com.cleanroommc.bogosorter.api.ISlot import com.cleanroommc.bogosorter.api.ISortingContextBuilder import com.cleanroommc.modularui.ModularUI +import com.cleanroommc.modularui.factory.TileEntityGuiFactory import com.cleanroommc.modularui.screen.ModularContainer import com.cleanroommc.modularui.utils.Platform import com.cleanroommc.modularui.widgets.slot.ModularSlot @@ -12,6 +13,7 @@ import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.IndexedInvent import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.IndexedModularCraftingSlot import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlot import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlotWrapper +import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.inventory.ClickType @@ -19,11 +21,20 @@ import net.minecraft.inventory.Container import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.item.crafting.CraftingManager +import net.minecraft.network.play.server.SPacketSetSlot +import net.minecraft.util.math.BlockPos import net.minecraftforge.fml.common.Optional import net.minecraftforge.items.ItemHandlerHelper import kotlin.math.min -class BackpackContainer(private val wrapper: BackpackWrapper, private val backpackSlotIndex: Int?) : +class BackpackContainer( + val backpackWrapper: BackpackWrapper, + private val backpackSlotIndex: Int?, + private val backpackInventoryType: PlayerInventoryGuiData.InventoryType? = null, + private val backpackSourceSlotIndex: Int? = backpackSlotIndex, + private val backpackTargetEntity: EntityLivingBase? = null, + private val tilePos: BlockPos? = null +) : ModularContainer() { companion object { private val DROP_TO_WORLD: Int = -999 @@ -31,6 +42,29 @@ class BackpackContainer(private val wrapper: BackpackWrapper, private val backpa private const val RIGHT_MOUSE: Int = 1 } + private val wrapper: BackpackWrapper + get() = backpackWrapper + + fun reopenBackpackGui(player: EntityPlayerMP) { + val carriedStack = player.inventory.itemStack.copy() + player.inventory.itemStack = ItemStack.EMPTY + try { + when { + tilePos != null -> TileEntityGuiFactory.INSTANCE.open(player, tilePos) + backpackInventoryType != null && backpackSourceSlotIndex != null -> + PlayerInventoryGuiFactory.open( + backpackTargetEntity ?: player, + player, + backpackInventoryType, + backpackSourceSlotIndex + ) + } + } finally { + player.inventory.itemStack = carriedStack + player.connection.sendPacket(SPacketSetSlot(-1, -1, carriedStack)) + } + } + /** * Internal hash table used to store wrapped crafting matrices. Intended for use alongside craftingSlotInstances, to connect an instance of wrapper and slot together. */ @@ -67,87 +101,96 @@ class BackpackContainer(private val wrapper: BackpackWrapper, private val backpa } override fun slotClick(slotId: Int, mouseButton: Int, clickTypeIn: ClickType, player: EntityPlayer): ItemStack { - val playerInventory = player.inventory - val heldStack = playerInventory.itemStack - - if (clickTypeIn == ClickType.PICKUP && - (mouseButton == LEFT_MOUSE || mouseButton == RIGHT_MOUSE) && - (slotId != DROP_TO_WORLD && slotId >= 0) - ) { - val clickedSlot = getSlot(slotId) - val slotStack = clickedSlot.stack - - if (clickedSlot is ModularBackpackSlot && !slotStack.isEmpty && heldStack.isEmpty) { - val s = min(slotStack.count, clickedSlot.getItemStackLimit(slotStack)) - val toRemove = if (mouseButton == LEFT_MOUSE) s else (s + 1) / 2 - playerInventory.itemStack = slotStack.splitStack(toRemove) - clickedSlot.putStack(slotStack) - clickedSlot.onTake(player, playerInventory.itemStack) - clickedSlot.onSlotChanged() - detectAndSendChanges() - return ItemStack.EMPTY - } - } else if (clickTypeIn == ClickType.PICKUP_ALL && slotId >= 0) { - val clickedSlot = getSlot(slotId) - val slotStack = clickedSlot.stack - val maxStackSize = clickedSlot.getItemStackLimit(slotStack) + if (slotId >= inventorySlots.size) { + return ItemStack.EMPTY + } + + backpackWrapper.isGuiInteractionInProgress = true + try { + val playerInventory = player.inventory + val heldStack = playerInventory.itemStack - if (!heldStack.isEmpty && - (clickedSlot == null || !clickedSlot.hasStack || !clickedSlot.canTakeStack(player)) + if (clickTypeIn == ClickType.PICKUP && + (mouseButton == LEFT_MOUSE || mouseButton == RIGHT_MOUSE) && + (slotId != DROP_TO_WORLD && slotId >= 0) ) { - val i = if (mouseButton == 0) 0 else inventorySlots.size - 1 - val j = if (mouseButton == 0) 1 else -1 + val clickedSlot = getSlot(slotId) + val slotStack = clickedSlot.stack + + if (clickedSlot is ModularBackpackSlot && !slotStack.isEmpty && heldStack.isEmpty) { + val s = min(slotStack.count, clickedSlot.getItemStackLimit(slotStack)) + val toRemove = if (mouseButton == LEFT_MOUSE) s else (s + 1) / 2 + playerInventory.itemStack = slotStack.splitStack(toRemove) + clickedSlot.putStack(slotStack) + clickedSlot.onTake(player, playerInventory.itemStack) + clickedSlot.onSlotChanged() + detectAndSendChanges() + return ItemStack.EMPTY + } + } else if (clickTypeIn == ClickType.PICKUP_ALL && slotId >= 0) { + val clickedSlot = getSlot(slotId) + val slotStack = clickedSlot.stack + val maxStackSize = clickedSlot.getItemStackLimit(slotStack) - for (k in 0..1) { - var l = i + if (!heldStack.isEmpty && + (clickedSlot == null || !clickedSlot.hasStack || !clickedSlot.canTakeStack(player)) + ) { + val i = if (mouseButton == 0) 0 else inventorySlots.size - 1 + val j = if (mouseButton == 0) 1 else -1 - while (l >= 0 && l < inventorySlots.size && heldStack.count < maxStackSize) { - val slot1 = inventorySlots[l] + for (k in 0..1) { + var l = i - if (slot1 is ModularSlot && slot1.isPhantom) { - l += j - continue - } + while (l >= 0 && l < inventorySlots.size && heldStack.count < maxStackSize) { + val slot1 = inventorySlots[l] - if (slot1.hasStack && Container.canAddItemToSlot(slot1, heldStack, true) && - slot1.canTakeStack(player) && canMergeSlot(heldStack, slot1) - ) { - val itemstack2 = slot1.stack + if (slot1 is ModularSlot && slot1.isPhantom) { + l += j + continue + } - if (k != 0 || itemstack2.count != maxStackSize) { - val i1 = min((maxStackSize - heldStack.count), itemstack2.count) - val itemstack3 = slot1.decrStackSize(i1) - heldStack.grow(i1) + if (slot1.hasStack && Container.canAddItemToSlot(slot1, heldStack, true) && + slot1.canTakeStack(player) && canMergeSlot(heldStack, slot1) + ) { + val itemstack2 = slot1.stack - if (itemstack3.isEmpty) { - slot1.putStack(ItemStack.EMPTY) - } + if (k != 0 || itemstack2.count != maxStackSize) { + val i1 = min((maxStackSize - heldStack.count), itemstack2.count) + val itemstack3 = slot1.decrStackSize(i1) + heldStack.grow(i1) + + if (itemstack3.isEmpty) { + slot1.putStack(ItemStack.EMPTY) + } - slot1.onTake(player, itemstack3) + slot1.onTake(player, itemstack3) + } } + l += j } - l += j } } - } - detectAndSendChanges() - return ItemStack.EMPTY - } else if (clickTypeIn == ClickType.CLONE && player.capabilities.isCreativeMode && - playerInventory.itemStack.isEmpty && slotId >= 0 - ) { - val slot = getSlot(slotId) + detectAndSendChanges() + return ItemStack.EMPTY + } else if (clickTypeIn == ClickType.CLONE && player.capabilities.isCreativeMode && + playerInventory.itemStack.isEmpty && slotId >= 0 + ) { + val slot = getSlot(slotId) - if (slot != null && slot.hasStack) - playerInventory.itemStack = slot.stack.copy() + if (slot != null && slot.hasStack) + playerInventory.itemStack = slot.stack.copy() - return ItemStack.EMPTY - } else if (clickTypeIn == ClickType.SWAP && mouseButton >= 0 && mouseButton < 9 && backpackSlotIndex == mouseButton) { - // Prevents swapping opened backpack when backpack is in player inventory - return ItemStack.EMPTY - } + return ItemStack.EMPTY + } else if (clickTypeIn == ClickType.SWAP && mouseButton >= 0 && mouseButton < 9 && backpackSlotIndex == mouseButton) { + // Prevents swapping opened backpack when backpack is in player inventory + return ItemStack.EMPTY + } - return super.slotClick(slotId, mouseButton, clickTypeIn, player) + return super.slotClick(slotId, mouseButton, clickTypeIn, player) + } finally { + backpackWrapper.isGuiInteractionInProgress = false + } } override fun transferItem(fromSlot: ModularSlot, fromStack: ItemStack): ItemStack { diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackGuiHolder.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackGuiHolder.kt index 4a437cf..d1642bd 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackGuiHolder.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/BackpackGuiHolder.kt @@ -7,44 +7,50 @@ import com.cleanroommc.modularui.screen.UISettings import com.cleanroommc.modularui.value.sync.PanelSyncManager import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.client.gui.BackpackPanel +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.BackpackInventoryScrollWidget import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiData.InventoryType import com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.ceilDiv +import kotlin.math.min import net.minecraft.entity.player.EntityPlayer sealed class BackpackGuiHolder(protected val backpackWrapper: BackpackWrapper) { companion object { private const val SLOT_SIZE = 18 + private const val HEIGHT_WITHOUT_STORAGE_SLOTS = 114 } - protected val rowSize = if (backpackWrapper.backpackInventorySize() > 81) 12 else 9 + protected val tankInventoryControlCount = min(backpackWrapper.tankUpgradeSlots().size, 2) + protected val batteryInventoryControlCount = min(backpackWrapper.batteryUpgradeSlots().size, 1) + protected val inventoryColumnsTaken = + (tankInventoryControlCount + batteryInventoryControlCount) * BackpackPanel.INVENTORY_CONTROL_COLUMNS + protected val backgroundRowSize = if (backpackWrapper.backpackInventorySize() > 81) 12 else 9 + protected val rowSize = (backgroundRowSize - inventoryColumnsTaken).coerceAtLeast(1) protected val colSize = backpackWrapper.backpackInventorySize().ceilDiv(rowSize) + protected val visibleColSize = min(colSize, BackpackPanel.VISIBLE_BACKPACK_ROWS) + protected val scrollbarWidth = if (colSize > visibleColSize) BackpackInventoryScrollWidget.SCROLLBAR_WIDTH else 0 protected fun createPanel( syncManager: PanelSyncManager, player: EntityPlayer, tileEntity: BackpackTileEntity?, inventoryType: InventoryType? = null, - slotIndex: Int? = null + slotIndex: Int? = null, + backpackName: String? = null ): BackpackPanel = BackpackPanel.defaultPanel( syncManager, player, tileEntity, backpackWrapper, - 14 + rowSize * SLOT_SIZE, - 112 + colSize * SLOT_SIZE, + 14 + backgroundRowSize * SLOT_SIZE + scrollbarWidth, + HEIGHT_WITHOUT_STORAGE_SLOTS + visibleColSize * SLOT_SIZE, inventoryType?.let { if (it == InventoryType.PLAYER_INVENTORY) slotIndex else null }, + backpackName, ) - protected fun addCommonWidgets(panel: BackpackPanel, player: EntityPlayer, backpackName: String) { - panel.addSortingButtons() - panel.addTransferButtons() - panel.addBackpackInventorySlots() - panel.addUpgradeSlots() - panel.addSettingTab() - panel.addUpgradeTabs() - panel.addTexts(player, backpackName) + protected fun addCommonWidgets(panel: BackpackPanel, player: EntityPlayer) { + panel.rebuildWidgets() } class TileEntityGuiHolder(backpackWrapper: BackpackWrapper) : BackpackGuiHolder(backpackWrapper), @@ -55,8 +61,8 @@ sealed class BackpackGuiHolder(protected val backpackWrapper: BackpackWrapper) { uiSettings: UISettings ): ModularPanel { val tileEntity = data.world.getTileEntity(data.blockPos) as BackpackTileEntity - val panel = createPanel(syncManager, data.player, tileEntity) - addCommonWidgets(panel, data.player, tileEntity.displayName.formattedText) + val panel = createPanel(syncManager, data.player, tileEntity, backpackName = tileEntity.displayName.formattedText) + addCommonWidgets(panel, data.player) return panel } } @@ -68,9 +74,9 @@ sealed class BackpackGuiHolder(protected val backpackWrapper: BackpackWrapper) { syncManager: PanelSyncManager, uiSettings: UISettings ): ModularPanel { - val panel = createPanel(syncManager, data.player, null, data.inventoryType, data.slotIndex) - addCommonWidgets(panel, data.player, data.usedItemStack.displayName) + val panel = createPanel(syncManager, data.player, null, data.inventoryType, data.slotIndex, data.usedItemStack.displayName) panel.modifyPlayerSlot(syncManager, data.inventoryType, data.slotIndex, data.player) + addCommonWidgets(panel, data.player) return panel } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/PlayerInventoryGuiData.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/PlayerInventoryGuiData.kt index fff3002..7ab7fc2 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/PlayerInventoryGuiData.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/PlayerInventoryGuiData.kt @@ -26,7 +26,7 @@ class PlayerInventoryGuiData( } } else { val chestStack = targetEntity.getItemStackFromSlot(EntityEquipmentSlot.CHEST) - + if (chestStack.item !is BackpackItem) ItemStack.EMPTY else chestStack } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularBackpackSlot.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularBackpackSlot.kt index 39cb5bd..2ab38d4 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularBackpackSlot.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularBackpackSlot.kt @@ -12,8 +12,11 @@ class ModularBackpackSlot( wrapper.getMemorizedStack(slotIndex) override fun getSlotStackLimit(): Int = - Int.MAX_VALUE + if (wrapper.isSlotBlockedByMobCatcher(slotIndex)) 0 else Int.MAX_VALUE override fun getItemStackLimit(stack: ItemStack): Int = - stack.maxStackSize * wrapper.getTotalStackMultiplier() -} \ No newline at end of file + if (wrapper.isSlotBlockedByMobCatcher(slotIndex)) 0 else wrapper.getStackLimit(stack) + + override fun isItemValid(stack: ItemStack): Boolean = + !wrapper.isSlotBlockedByMobCatcher(slotIndex) && super.isItemValid(stack) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularUpgradeSlot.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularUpgradeSlot.kt index cf5900b..2ebf60a 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularUpgradeSlot.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/common/gui/slot/ModularUpgradeSlot.kt @@ -7,6 +7,11 @@ import com.cleanroommc.retrosophisticatedbackpacks.item.ExponentialStackUpgradeI import com.cleanroommc.retrosophisticatedbackpacks.item.InceptionUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.item.StackUpgradeItem import com.cleanroommc.retrosophisticatedbackpacks.item.UpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.item.BatteryUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.item.MobCatcherUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.item.TankUpgradeItem +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherStorage +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack @@ -16,7 +21,7 @@ class ModularUpgradeSlot( index: Int, ) : ModularSlot(wrapper.upgradeItemStackHandler, index) { override fun canTakeStack(playerIn: EntityPlayer): Boolean { - if (panel.settingPanel.isPanelOpen) + if (panel.isSettingMode) return false val originalUpgradeItem = stack.item @@ -42,15 +47,50 @@ class ModularUpgradeSlot( else true } + if (originalUpgradeItem is MobCatcherUpgradeItem) { + if (wrapper.capturedMobs.isEmpty()) { + return true + } + return newUpgradeItem is MobCatcherUpgradeItem && + (newUpgradeItem.advanced || MobCatcherStorage.canFitBasicTier(wrapper, Config.mobCatcherUpgrade.basicMaxSlotCost)) + } + return true } override fun getItemStackLimit(stack: ItemStack): Int = 1 - override fun isItemValid(stack: ItemStack): Boolean = when (val item = stack.item) { - is StackUpgradeItem -> wrapper.canAddStackUpgrade(item.multiplier()) - is ExponentialStackUpgradeItem -> wrapper.canAddExponentialStackUpgrade() - else -> item is UpgradeItem + override fun isItemValid(stack: ItemStack): Boolean { + val item = stack.item as? UpgradeItem ?: return false + if (!canFitConfiguredUpgradeLimit(item)) { + return false + } + return when (item) { + is StackUpgradeItem -> wrapper.canAddStackUpgrade(item.multiplier()) + is ExponentialStackUpgradeItem -> wrapper.canAddExponentialStackUpgrade() + is TankUpgradeItem -> MobCatcherStorage.canFitWithAdditionalInventoryControls( + wrapper, + if (this.stack.item is TankUpgradeItem || wrapper.tankUpgradeSlots().size >= 2) 0 else 1 + ) + is BatteryUpgradeItem -> (this.stack.item is BatteryUpgradeItem || wrapper.canAddBatteryUpgrade()) && + MobCatcherStorage.canFitWithAdditionalInventoryControls( + wrapper, + if (this.stack.item is BatteryUpgradeItem || wrapper.hasBatteryUpgrade()) 0 else 1 + ) + is MobCatcherUpgradeItem -> this.stack.item is MobCatcherUpgradeItem || wrapper.canAddMobCatcherUpgrade() + else -> true + } + } + + private fun canFitConfiguredUpgradeLimit(item: UpgradeItem): Boolean { + val (limitKey, max) = Config.getUpgradeLimit(item) ?: return true + val currentItem = stack.item as? UpgradeItem + val currentCount = wrapper.upgradeItemStackHandler.inventory.count { + val upgrade = it.item as? UpgradeItem ?: return@count false + Config.matchesUpgradeLimit(upgrade, limitKey) + } + val replacingSameLimit = currentItem != null && Config.matchesUpgradeLimit(currentItem, limitKey) + return currentCount - (if (replacingSameLimit) 1 else 0) < max } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/config/Config.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/config/Config.kt index 4c687af..60d8999 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/config/Config.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/config/Config.kt @@ -1,13 +1,52 @@ package com.cleanroommc.retrosophisticatedbackpacks.config import com.cleanroommc.retrosophisticatedbackpacks.Tags +import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem +import com.cleanroommc.retrosophisticatedbackpacks.item.UpgradeItem +import net.minecraft.block.Block +import net.minecraft.item.ItemStack import net.minecraftforge.common.config.Config +import net.minecraftforge.items.CapabilityItemHandler @Config(modid = Tags.MOD_ID, name = "${Tags.MOD_ID}_general") object Config { @JvmField - @Config.Comment("Items that cannot be stored in backpack") + @Config.Comment("List of items that are not allowed to be put in backpacks - e.g. \"minecraft:shulker_box\"") + var disallowedItems = arrayOf("minecraft:shulker_box") + + @JvmField + @Config.Comment("Determines if container items are able to fit in backpacks") + var containerItemsDisallowed = false + + @JvmField + @Config.Comment("List of blocks that inventory interaction upgrades can't interact with - e.g. \"minecraft:shulker_box\"") + var noInteractionBlocks = arrayOf("minecraft:shulker_box") + + @JvmField + @Config.Comment("List of blocks that are not allowed to connect to backpacks - e.g. \"refinedstorage:external_storage\"") + var noConnectionBlocks = arrayOf() + + @JvmField + @Config.Comment("If true, disallows all blocks from connecting to backpacks") + var allBlockConnectionsDisallowed = false + + @JvmField + @Config.Comment("Turns on/off item fluid handler of backpack in its item form") + var itemFluidHandlerEnabled = true + + @JvmField + @Config.Comment("Determines whether player can right click on backpack that another player is wearing to open it") + var allowOpeningOtherPlayerBackpacks = true + + @JvmField + @Config.Comment("Allows disabling item display settings") @Config.RequiresMcRestart + var itemDisplayDisabled = false + + @JvmField + @Config.Comment("Allows disabling logic that dedupes backpacks with the same UUID in players' inventory") + var tickDedupeLogicDisabled = false + var blacklistedItems = arrayOf() @JvmField @@ -46,14 +85,127 @@ object Config { @JvmField val stackUpgrade = StackUpgradeConfig() + @JvmField + val compactingUpgrade = FilteredUpgradeConfig(9, 3) + + @JvmField + val advancedCompactingUpgrade = FilteredUpgradeConfig(16, 4) + + @JvmField + val depositUpgrade = FilteredUpgradeConfig(9, 3) + + @JvmField + val advancedDepositUpgrade = FilteredUpgradeConfig(16, 4) + + @JvmField + val feedingUpgrade = FilteredUpgradeConfig(9, 3) + + @JvmField + val advancedFeedingUpgrade = FilteredUpgradeConfig(16, 4) + + @JvmField + val filterUpgrade = FilteredUpgradeConfig(9, 3) + + @JvmField + val advancedFilterUpgrade = FilteredUpgradeConfig(16, 4) + + @JvmField + val magnetUpgrade = MagnetUpgradeConfig(9, 3, 3) + + @JvmField + val advancedMagnetUpgrade = MagnetUpgradeConfig(16, 4, 5) + + @JvmField + val pickupUpgrade = FilteredUpgradeConfig(9, 3) + + @JvmField + val advancedPickupUpgrade = FilteredUpgradeConfig(16, 4) + + @JvmField + val refillUpgrade = FilteredUpgradeConfig(6, 3) + + @JvmField + val advancedRefillUpgrade = FilteredUpgradeConfig(12, 4) + + @JvmField + val restockUpgrade = FilteredUpgradeConfig(9, 3) + + @JvmField + val advancedRestockUpgrade = FilteredUpgradeConfig(16, 4) + + @JvmField + val voidUpgrade = VoidUpgradeConfig(9, 3) + + @JvmField + val advancedVoidUpgrade = VoidUpgradeConfig(16, 4) + + @JvmField + val toolSwapperUpgrade = FilteredUpgradeConfig(8, 4) + + @JvmField + val tankUpgrade = TankUpgradeConfig() + + @JvmField + val batteryUpgrade = BatteryUpgradeConfig() + + @JvmField + val pumpUpgrade = PumpUpgradeConfig() + + @JvmField + val advancedJukeboxUpgrade = JukeboxUpgradeConfig(12) + + @JvmField + val mobCatcherUpgrade = MobCatcherUpgradeConfig() + + @JvmField + val maxUpgradesPerStorage = MaxUpgradesPerStorageConfig() + + fun isItemDisallowed(stack: ItemStack): Boolean { + val registryName = stack.item.registryName?.toString() ?: return false + if (registryName in disallowedItems) { + return true + } + return containerItemsDisallowed && + stack.item !is BackpackItem && + stack.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null) + } + + fun canStackWithStackUpgrade(stack: ItemStack): Boolean { + val registryName = stack.item.registryName?.toString() ?: return true + return registryName !in stackUpgrade.nonStackableItems + } + + fun isInteractionBlockDisallowed(block: Block): Boolean = + block.registryName?.toString() in noInteractionBlocks + + fun isConnectionBlockDisallowed(block: Block): Boolean = + allBlockConnectionsDisallowed || block.registryName?.toString() in noConnectionBlocks + + fun getUpgradeLimit(upgradeItem: UpgradeItem): Pair? { + val limits = maxUpgradesPerStorage.maxUpgradesPerStorage.mapNotNull { + val parts = it.split("|", limit = 2) + val max = parts.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null + parts[0] to max + }.toMap() + val registryPath = upgradeItem.registryName?.path + if (registryPath != null && registryPath in limits) { + return registryPath to limits.getValue(registryPath) + } + val group = upgradeItem.upgradeGroup + return if (group != null && group in limits) group to limits.getValue(group) else null + } + + fun matchesUpgradeLimit(upgradeItem: UpgradeItem, limitKey: String): Boolean = + upgradeItem.registryName?.path == limitKey || upgradeItem.upgradeGroup == limitKey + class LeatherBackpackConfig { @JvmField @Config.RequiresMcRestart - var slots = 27 + var inventorySlotCount = 27 @JvmField @Config.RequiresMcRestart - var upgradeSlots = 1 + var upgradeSlotCount = 1 @JvmField var spawnChanceOnMob = 0.05f @@ -65,11 +217,11 @@ object Config { class IronBackpackConfig { @JvmField @Config.RequiresMcRestart - var slots = 54 + var inventorySlotCount = 54 @JvmField @Config.RequiresMcRestart - var upgradeSlots = 2 + var upgradeSlotCount = 2 @JvmField var spawnChanceOnMob = 0.04f @@ -81,11 +233,11 @@ object Config { class GoldBackpackConfig { @JvmField @Config.RequiresMcRestart - var slots = 81 + var inventorySlotCount = 81 @JvmField @Config.RequiresMcRestart - var upgradeSlots = 3 + var upgradeSlotCount = 3 @JvmField var spawnChanceOnMob = 0.03f @@ -97,11 +249,11 @@ object Config { class DiamondBackpackConfig { @JvmField @Config.RequiresMcRestart - var slots = 108 + var inventorySlotCount = 108 @JvmField @Config.RequiresMcRestart - var upgradeSlots = 5 + var upgradeSlotCount = 5 @JvmField var spawnChanceOnMob = 0.02f @@ -113,11 +265,11 @@ object Config { class ObsidianBackpackConfig { @JvmField @Config.RequiresMcRestart - var slots = 120 + var inventorySlotCount = 120 @JvmField @Config.RequiresMcRestart - var upgradeSlots = 7 + var upgradeSlotCount = 7 @JvmField var spawnChanceOnMob = 0.01f @@ -146,5 +298,135 @@ object Config { @JvmField @Config.RequiresMcRestart var obsidianMultiplier = 32 + + @JvmField + var nonStackableItems = arrayOf( + "minecraft:shulker_box", + "minecraft:white_shulker_box", + "minecraft:orange_shulker_box", + "minecraft:magenta_shulker_box", + "minecraft:light_blue_shulker_box", + "minecraft:yellow_shulker_box", + "minecraft:lime_shulker_box", + "minecraft:pink_shulker_box", + "minecraft:gray_shulker_box", + "minecraft:silver_shulker_box", + "minecraft:cyan_shulker_box", + "minecraft:purple_shulker_box", + "minecraft:blue_shulker_box", + "minecraft:brown_shulker_box", + "minecraft:green_shulker_box", + "minecraft:red_shulker_box", + "minecraft:black_shulker_box" + ) + } + + open class FilteredUpgradeConfig(defaultFilterSlots: Int, defaultSlotsInRow: Int) { + @JvmField + var filterSlots = defaultFilterSlots + + @JvmField + var slotsInRow = defaultSlotsInRow + } + + class MagnetUpgradeConfig(defaultFilterSlots: Int, defaultSlotsInRow: Int, defaultMagnetRange: Int) { + @JvmField + var filterSlots = defaultFilterSlots + + @JvmField + var slotsInRow = defaultSlotsInRow + + @JvmField + var magnetRange = defaultMagnetRange + } + + class VoidUpgradeConfig(defaultFilterSlots: Int, defaultSlotsInRow: Int) { + @JvmField + var filterSlots = defaultFilterSlots + + @JvmField + var slotsInRow = defaultSlotsInRow + + @JvmField + var voidAlwaysEnabled = true + } + + class TankUpgradeConfig { + @JvmField + var capacityPerSlotRow = 4000 + + @JvmField + var stackMultiplierRatio = 1.0 + + @JvmField + var autoFillDrainContainerCooldown = 20 + + @JvmField + var maxInputOutput = 20 + } + + class BatteryUpgradeConfig { + @JvmField + var energyPerSlotRow = 10000 + + @JvmField + var stackMultiplierRatio = 1.0 + + @JvmField + var maxInputOutput = 20 + } + + class PumpUpgradeConfig { + @JvmField + var filterSlots = 4 + + @JvmField + var maxInputOutput = 20 + + @JvmField + var stackMultiplierRatio = 1.0 + } + + class JukeboxUpgradeConfig(defaultNumberOfSlots: Int) { + @JvmField + var numberOfSlots = defaultNumberOfSlots + + @JvmField + var slotsInRow = 4 + } + + class MobCatcherUpgradeConfig { + @JvmField + var basicMaxSlotCost = 18 + + @JvmField + var advancedMaxSlotCost = 72 + + @JvmField + var animalMultiplier = 1.0 + + @JvmField + var hostileMultiplier = 2.0 + + @JvmField + var disallowInventoryEntities = false + + @JvmField + var entityBlockList = arrayOf("minecraft:wither") + + @JvmField + var hostileOverrides = arrayOf("minecraft:enderman") + + @JvmField + var passiveOverrides = arrayOf("minecraft:villager") + } + + class MaxUpgradesPerStorageConfig { + @JvmField + var maxUpgradesPerStorage = arrayOf( + "stack|3", + "cooking|1", + "jukebox|1" + ) } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/BackpackTooltipHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/BackpackTooltipHandler.kt new file mode 100644 index 0000000..aa9f47c --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/BackpackTooltipHandler.kt @@ -0,0 +1,421 @@ +package com.cleanroommc.retrosophisticatedbackpacks.handler + +import com.cleanroommc.modularui.drawable.GuiDraw +import com.cleanroommc.retrosophisticatedbackpacks.Tags +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackEnergyStorage +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackFluidHandler +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.RenderHelper +import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.vertex.DefaultVertexFormats +import net.minecraft.client.resources.I18n +import net.minecraft.item.ItemStack +import net.minecraft.util.ResourceLocation +import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.util.text.TextFormatting +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.client.event.RenderTooltipEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols +import java.util.Locale + +@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(modid = Tags.MOD_ID, value = [Side.CLIENT]) +object BackpackTooltipHandler { + private const val MAX_STACKS_ON_LINE = 9 + private const val STACK_WIDTH = 18 + private const val GAUGE_SCALE = 0.65f + private const val GAUGE_RENDER_HEIGHT = 10 + private const val GAUGE_TEXTURE_WIDTH = 54 + private const val GAUGE_TEXTURE_HEIGHT = 18 + private const val GAUGE_TEXT_LEFT = 41 + private const val ROW_HEIGHT = 20 + private const val TITLE_HEIGHT = 10 + private const val COUNT_PADDING = 2 + private const val CHARGE_SEGMENT_HEIGHT = 6 + private const val GUI_CONTROLS_SIZE = 256f + private const val GUI_CONTROLS = Tags.MOD_ID + ":textures/gui/gui_controls.png" + private const val TANK_MARKER = "\u00A70\u00A71\u00A72\u00A73" + private const val BATTERY_MARKER = "\u00A70\u00A71\u00A72\u00A74" + private const val UPGRADES_MARKER = "\u00A70\u00A71\u00A72\u00A75" + private const val ITEMS_MARKER = "\u00A70\u00A71\u00A72\u00A76" + private const val TOP_BAR_COLOR = 0xFF1A1A + private const val BOTTOM_BAR_COLOR = 0xFFFF40 + private val GUI_CONTROLS_LOCATION = ResourceLocation(GUI_CONTROLS) + private val TWO_DIGIT_FORMAT = DecimalFormat("#.00", DecimalFormatSymbols(Locale.ROOT)) + private val ONE_DIGIT_FORMAT = DecimalFormat("##.0", DecimalFormatSymbols(Locale.ROOT)) + private val SUFFIXES = arrayOf("k", "m", "b") + + fun shouldShowContentsTooltip(): Boolean { + val player = Minecraft.getMinecraft().player + return GuiScreen.isShiftKeyDown() || player != null && !player.inventory.itemStack.isEmpty + } + + fun addTooltipLines(wrapper: BackpackWrapper, tooltip: MutableList) { + val data = TooltipData.of(wrapper) + data.summaryLines().forEach(tooltip::add) + data.blocks.forEach { block -> + repeat(block.placeholderRows) { row -> + tooltip.add(if (row == 0) block.marker else block.placeholderLine(data.width)) + } + } + } + + @SubscribeEvent + @JvmStatic + fun onTooltipPostText(event: RenderTooltipEvent.PostText) { + if (event.stack.item !is BackpackItem || !shouldShowContentsTooltip()) { + return + } + val wrapper = event.stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return + val data = TooltipData.of(wrapper) + data.blocks.forEach { block -> + val lineIndex = event.lines.indexOfFirst { it.contains(block.marker) } + if (lineIndex >= 0) { + block.render(data, event.x, lineTop(event.y, lineIndex)) + } + } + } + + private fun lineTop(tooltipY: Int, lineIndex: Int): Int = + tooltipY + if (lineIndex == 0) 0 else 12 + (lineIndex - 1) * 10 + + private fun renderGaugeTitle(title: String, leftX: Int, topY: Int) { + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow( + TextFormatting.YELLOW.toString() + title, + leftX.toFloat(), + topY.toFloat(), + -1 + ) + } + + private inline fun renderRotatedGauge(leftX: Int, topY: Int, render: () -> Unit) { + GlStateManager.pushMatrix() + GlStateManager.translate(leftX.toFloat(), topY + GAUGE_TEXTURE_HEIGHT * GAUGE_SCALE, 0f) + GlStateManager.scale(GAUGE_SCALE, GAUGE_SCALE, 1f) + GlStateManager.rotate(-90f, 0f, 0f, 1f) + render() + GlStateManager.popMatrix() + } + + private fun renderVerticalGaugeBackground() { + val mc = Minecraft.getMinecraft() + mc.textureManager.bindTexture(GUI_CONTROLS_LOCATION) + GlStateManager.color(1f, 1f, 1f, 1f) + drawTexturedRect(0, 0, 29, 30, 18, 18) + drawTexturedRect(0, 18, 29, 48, 18, 18) + drawTexturedRect(0, 36, 29, 66, 18, 18) + } + + private fun renderVerticalGaugeOverlay(overlayU: Int, overlayV: Int) { + val mc = Minecraft.getMinecraft() + mc.textureManager.bindTexture(GUI_CONTROLS_LOCATION) + GlStateManager.color(1f, 1f, 1f, 1f) + repeat(GAUGE_TEXTURE_WIDTH / 18) { + drawTexturedRect(1, it * 18, overlayU, overlayV, 16, 18) + } + } + + private fun renderTank(data: TooltipData, leftX: Int, topY: Int) { + val mc = Minecraft.getMinecraft() + val title = I18n.format("tooltip.backpack.fluid_title".asTranslationKey()) + renderGaugeTitle(title, leftX, topY) + data.tanks.forEachIndexed { index, tank -> + val gaugeY = topY + TITLE_HEIGHT + index * (GAUGE_RENDER_HEIGHT + 2) + val fluid = tank.fluid + renderRotatedGauge(leftX, gaugeY) { + renderVerticalGaugeBackground() + if (fluid != null && fluid.amount > 0) { + val displayLevel = ((GAUGE_TEXTURE_WIDTH - 2) * (fluid.amount.toFloat() / tank.capacity.coerceAtLeast(1))).toInt() + .coerceIn(1, GAUGE_TEXTURE_WIDTH - 2) + GuiDraw.drawFluidTexture( + fluid, + 1f, + 1f, + 16f, + displayLevel.toFloat(), + 300f + ) + } + renderVerticalGaugeOverlay(47, 30) + } + mc.fontRenderer.drawStringWithShadow( + if (fluid == null || fluid.amount <= 0) { + TextFormatting.BLUE.toString() + I18n.format("tooltip.backpack.fluid_empty".asTranslationKey()) + } else { + TextFormatting.BLUE.toString() + TextComponentTranslation( + "tooltip.backpack.fluid".asTranslationKey(), + TextFormatting.WHITE.toString() + abbreviate(fluid.amount), + TextFormatting.BLUE.toString() + fluid.localizedName + ).formattedText + }, + (leftX + GAUGE_TEXT_LEFT).toFloat(), + (gaugeY + 2).toFloat(), + -1 + ) + } + } + + private fun renderBattery(data: TooltipData, leftX: Int, topY: Int) { + val mc = Minecraft.getMinecraft() + renderGaugeTitle(I18n.format("tooltip.backpack.energy_title".asTranslationKey()), leftX, topY) + val gaugeY = topY + TITLE_HEIGHT + renderRotatedGauge(leftX, gaugeY) { + renderVerticalGaugeBackground() + renderVerticalGaugeOverlay(47, 56) + renderVerticalBatteryCharge(data.energyStored, data.energyCapacity) + mc.textureManager.bindTexture(GUI_CONTROLS_LOCATION) + GlStateManager.color(1f, 1f, 1f, 1f) + drawTexturedRect(1, 0, 47, 48, 16, 4) + drawTexturedRect(1, GAUGE_TEXTURE_WIDTH - 4, 47, 52, 16, 4) + } + mc.fontRenderer.drawStringWithShadow( + TextFormatting.RED.toString() + TextComponentTranslation( + "tooltip.backpack.energy".asTranslationKey(), + TextFormatting.WHITE.toString() + abbreviate(data.energyStored) + ).formattedText, + (leftX + GAUGE_TEXT_LEFT).toFloat(), + (gaugeY + 2).toFloat(), + -1 + ) + } + + private fun renderContentsTitle(title: String, leftX: Int, topY: Int) { + val mc = Minecraft.getMinecraft() + mc.fontRenderer.drawStringWithShadow(TextFormatting.YELLOW.toString() + title, leftX.toFloat(), topY.toFloat(), -1) + } + + private fun renderUpgradesBlock(data: TooltipData, leftX: Int, topY: Int) { + renderContentsTitle(I18n.format("tooltip.backpack.upgrades_title".asTranslationKey()), leftX, topY) + renderUpgrades(data.upgrades, leftX, topY + TITLE_HEIGHT) + } + + private fun renderItemsBlock(data: TooltipData, leftX: Int, topY: Int) { + renderContentsTitle(I18n.format("tooltip.backpack.inventory_title".asTranslationKey()), leftX, topY) + renderItems(data.items, leftX, topY + TITLE_HEIGHT) + } + + private fun renderUpgrades(stacks: List, leftX: Int, topY: Int) { + val mc = Minecraft.getMinecraft() + withItemLighting { + stacks.forEachIndexed { index, stack -> + val x = leftX + index * STACK_WIDTH + mc.renderItem.renderItemAndEffectIntoGUI(stack, x, topY) + } + } + } + + private fun renderItems(stacks: List, leftX: Int, topY: Int) { + val mc = Minecraft.getMinecraft() + withItemLighting { + var x = leftX + stacks.forEachIndexed { index, stack -> + if (index % MAX_STACKS_ON_LINE == 0) { + x = leftX + } + val y = topY + index / MAX_STACKS_ON_LINE * ROW_HEIGHT + val count = abbreviate(stack.count) + val width = maxOf(mc.fontRenderer.getStringWidth(count) + COUNT_PADDING, STACK_WIDTH) + val offset = width - STACK_WIDTH + mc.renderItem.renderItemAndEffectIntoGUI(stack, x + offset, y) + mc.renderItem.renderItemOverlayIntoGUI(mc.fontRenderer, stack, x + offset, y, count) + x += width + } + } + } + + private inline fun withItemLighting(render: () -> Unit) { + val mc = Minecraft.getMinecraft() + RenderHelper.enableGUIStandardItemLighting() + GlStateManager.enableDepth() + GlStateManager.enableRescaleNormal() + mc.renderItem.zLevel = 300f + render() + mc.renderItem.zLevel = 0f + GlStateManager.disableLighting() + GlStateManager.disableDepth() + } + + private fun renderVerticalBatteryCharge(energyStored: Int, maxEnergyStored: Int) { + if (maxEnergyStored <= 0 || energyStored <= 0) { + return + } + val mc = Minecraft.getMinecraft() + mc.textureManager.bindTexture(GUI_CONTROLS_LOCATION) + GlStateManager.enableTexture2D() + GlStateManager.enableBlend() + val numberOfSegments = GAUGE_TEXTURE_WIDTH / CHARGE_SEGMENT_HEIGHT + val displayLevel = (numberOfSegments * (energyStored.toFloat() / maxEnergyStored)).toInt() + for (segmentIndex in 0 until displayLevel) { + val percentage = if (numberOfSegments <= 1) 0f else segmentIndex.toFloat() / (numberOfSegments - 1) + GlStateManager.color( + colorChannel(BOTTOM_BAR_COLOR, 16, percentage, TOP_BAR_COLOR) / 255f, + colorChannel(BOTTOM_BAR_COLOR, 8, percentage, TOP_BAR_COLOR) / 255f, + colorChannel(BOTTOM_BAR_COLOR, 0, percentage, TOP_BAR_COLOR) / 255f, + 1f + ) + drawTexturedRect(1, GAUGE_TEXTURE_WIDTH - (segmentIndex + 1) * CHARGE_SEGMENT_HEIGHT, 47, 74, 16, CHARGE_SEGMENT_HEIGHT) + } + GlStateManager.color(1f, 1f, 1f, 1f) + } + + private fun colorChannel(bottom: Int, shift: Int, percentage: Float, top: Int): Int { + val bottomChannel = bottom shr shift and 255 + val topChannel = top shr shift and 255 + return (bottomChannel * (1 - percentage) + topChannel * percentage).toInt() + } + + private fun drawTexturedRect(x: Int, y: Int, u: Int, v: Int, width: Int, height: Int) { + val minU = u / GUI_CONTROLS_SIZE + val maxU = (u + width) / GUI_CONTROLS_SIZE + val minV = v / GUI_CONTROLS_SIZE + val maxV = (v + height) / GUI_CONTROLS_SIZE + drawSprite(x, y, width, height, minU, maxU, minV, maxV) + } + + private fun drawSprite(x: Int, y: Int, width: Int, height: Int, minU: Float, maxU: Float, minV: Float, maxV: Float) { + val tessellator = Tessellator.getInstance() + val buffer = tessellator.buffer + buffer.begin(7, DefaultVertexFormats.POSITION_TEX) + buffer.pos(x.toDouble(), (y + height).toDouble(), 300.0).tex(minU.toDouble(), maxV.toDouble()).endVertex() + buffer.pos((x + width).toDouble(), (y + height).toDouble(), 300.0).tex(maxU.toDouble(), maxV.toDouble()).endVertex() + buffer.pos((x + width).toDouble(), y.toDouble(), 300.0).tex(maxU.toDouble(), minV.toDouble()).endVertex() + buffer.pos(x.toDouble(), y.toDouble(), 300.0).tex(minU.toDouble(), minV.toDouble()).endVertex() + tessellator.draw() + } + + private data class TooltipData( + val items: List, + val upgrades: List, + val summary: List, + val tanks: List, + val energyStored: Int, + val energyCapacity: Int + ) { + val blocks: List = + buildList { + if (tanks.isNotEmpty()) add(TooltipBlock.Tank(tanks.size)) + if (energyCapacity > 0) add(TooltipBlock.Battery) + if (upgrades.isNotEmpty()) add(TooltipBlock.Upgrades) + if (items.isNotEmpty()) add(TooltipBlock.Items(items.size)) + } + + fun summaryLines(): List = + if (summary.isEmpty() && blocks.isEmpty()) { + listOf(TextFormatting.YELLOW.toString() + I18n.format("tooltip.backpack.empty".asTranslationKey())) + } else summary + + val width: Int by lazy { + val mc = Minecraft.getMinecraft() + val lineWidth = summaryLines().maxOfOrNull(mc.fontRenderer::getStringWidth) ?: 0 + maxOf(lineWidth, stackWidth(upgrades), stackWidth(items), gaugeWidth()) + } + + private fun stackWidth(stacks: List): Int = + stacks.take(MAX_STACKS_ON_LINE).sumOf { stack -> + maxOf(Minecraft.getMinecraft().fontRenderer.getStringWidth(abbreviate(stack.count)) + COUNT_PADDING, STACK_WIDTH) + } + + private fun gaugeWidth(): Int { + val mc = Minecraft.getMinecraft() + val tankTextWidth = tanks.maxOfOrNull { + val fluid = it.fluid + if (fluid == null || fluid.amount <= 0) mc.fontRenderer.getStringWidth(I18n.format("tooltip.backpack.fluid_empty".asTranslationKey())) + else mc.fontRenderer.getStringWidth(I18n.format("tooltip.backpack.fluid".asTranslationKey(), abbreviate(fluid.amount), fluid.localizedName)) + } ?: 0 + val energyTextWidth = if (energyCapacity > 0) mc.fontRenderer.getStringWidth(I18n.format("tooltip.backpack.energy".asTranslationKey(), abbreviate(energyStored))) else 0 + return GAUGE_TEXT_LEFT + maxOf(tankTextWidth, energyTextWidth, 0) + } + + companion object { + fun of(wrapper: BackpackWrapper): TooltipData { + val items = compactItems(wrapper.backpackItemStackHandler.inventory.filterNot(ItemStack::isEmpty)) + val upgrades = wrapper.upgradeItemStackHandler.inventory.filterNot(ItemStack::isEmpty).map(ItemStack::copy) + val summary = mutableListOf() + addMultiplierTooltip(wrapper, summary) + val tanks = tanks(wrapper) + val energy = BackpackEnergyStorage(wrapper) + val energyCapacity = if (wrapper.hasBatteryUpgrade()) energy.maxEnergyStored else 0 + return TooltipData(items, upgrades, summary, tanks, energy.energyStored, energyCapacity) + } + + private fun compactItems(stacks: List): List = + stacks.fold(mutableListOf()) { compacted, stack -> + val matching = compacted.firstOrNull { ItemStack.areItemsEqual(it, stack) && ItemStack.areItemStackTagsEqual(it, stack) } + if (matching == null) { + compacted.add(stack.copy()) + } else { + matching.count += stack.count + } + compacted + }.sortedByDescending(ItemStack::getCount) + + private fun addMultiplierTooltip(wrapper: BackpackWrapper, tooltip: MutableList) { + val multiplier = wrapper.getTotalStackMultiplier() + if (multiplier > 1) { + tooltip.add( + TextFormatting.GREEN.toString() + TextComponentTranslation( + "tooltip.backpack.stack_multiplier".asTranslationKey(), + TextFormatting.WHITE.toString() + multiplier + ).formattedText + ) + } + } + + private fun tanks(wrapper: BackpackWrapper): List = + if (!wrapper.hasTankUpgrade()) emptyList() + else BackpackFluidHandler(wrapper).tankProperties.map { TankTooltipInfo(it.contents?.copy(), it.capacity) } + } + } + + private data class TankTooltipInfo(val fluid: FluidStack?, val capacity: Int) + + private sealed class TooltipBlock( + val marker: String, + private val height: Int, + private val renderer: (TooltipData, Int, Int) -> Unit + ) { + val placeholderRows: Int = (height + 9) / 10 + + fun placeholderLine(width: Int): String = + " ".repeat((width + 3) / 4) + + fun render(data: TooltipData, x: Int, y: Int) { + renderer(data, x, y) + } + + class Tank(tankCount: Int) : TooltipBlock(TANK_MARKER, TITLE_HEIGHT + tankCount * (GAUGE_RENDER_HEIGHT + 2) + 2, ::renderTank) + object Battery : TooltipBlock(BATTERY_MARKER, TITLE_HEIGHT + GAUGE_RENDER_HEIGHT + 2, ::renderBattery) + object Upgrades : TooltipBlock(UPGRADES_MARKER, TITLE_HEIGHT + ROW_HEIGHT + 2, ::renderUpgradesBlock) + class Items(itemCount: Int) : TooltipBlock(ITEMS_MARKER, TITLE_HEIGHT + ((itemCount + MAX_STACKS_ON_LINE - 1) / MAX_STACKS_ON_LINE) * ROW_HEIGHT + 2, ::renderItemsBlock) + } + + private fun abbreviate(count: Int, maxCharacters: Int = 4): String { + val digits = count.toString().length + if (digits <= maxCharacters) { + return String.format(Locale.ROOT, "%,d", count) + } + val thousandsExponent = (digits - maxCharacters) / 3 + 1 + val suffix = SUFFIXES.getOrElse(thousandsExponent - 1) { "b" } + val divisionResult = count / Math.pow(1000.0, thousandsExponent.toDouble()) + val wholeDigits = digits - thousandsExponent * 3 + val precisionDigits = maxCharacters - 1 - wholeDigits + val numberPart = when { + wholeDigits > 3 || precisionDigits == 0 -> String.format(Locale.ROOT, "%,d", divisionResult.toInt()) + precisionDigits == 2 -> TWO_DIGIT_FORMAT.format(divisionResult) + precisionDigits == 1 -> ONE_DIGIT_FORMAT.format(divisionResult) + else -> divisionResult.toInt().toString() + } + return numberPart + suffix + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/CapabilityHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/CapabilityHandler.kt index 7229c69..82e5d7c 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/CapabilityHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/CapabilityHandler.kt @@ -3,6 +3,8 @@ package com.cleanroommc.retrosophisticatedbackpacks.handler import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.* +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import it.unimi.dsi.fastutil.objects.Object2ObjectMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import net.minecraft.nbt.NBTBase @@ -102,6 +104,119 @@ object CapabilityHandler { ::AdvancedFilterUpgradeWrapper ) + instance.register( + MagnetUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::MagnetUpgradeWrapper + ) + + instance.register( + AdvancedMagnetUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedMagnetUpgradeWrapper + ) + + instance.register( + VoidUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::VoidUpgradeWrapper + ) + + instance.register( + AdvancedVoidUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedVoidUpgradeWrapper + ) + + instance.register( + RefillUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::RefillUpgradeWrapper + ) + + instance.register( + AdvancedRefillUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedRefillUpgradeWrapper + ) + + instance.register( + CompactingUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::CompactingUpgradeWrapper + ) + + instance.register( + AdvancedCompactingUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedCompactingUpgradeWrapper + ) + + instance.register( + EverlastingUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::EverlastingUpgradeWrapper + ) + + instance.register( + ToolSwapperUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::ToolSwapperUpgradeWrapper + ) + + instance.register( + AdvancedToolSwapperUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedToolSwapperUpgradeWrapper + ) + + instance.register( + TankUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::TankUpgradeWrapper + ) + + instance.register( + JukeboxUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::JukeboxUpgradeWrapper + ) + + instance.register( + AdvancedJukeboxUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedJukeboxUpgradeWrapper + ) + + instance.register( + PumpUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::PumpUpgradeWrapper + ) + + instance.register( + AdvancedPumpUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AdvancedPumpUpgradeWrapper + ) + + instance.register( + BatteryUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::BatteryUpgradeWrapper + ) + + instance.register( + AnvilUpgradeWrapper::class.java, + CapabilityStorageProvider(), + ::AnvilUpgradeWrapper + ) + + instance.register( + MobCatcherUpgradeWrapper::class.java, + CapabilityStorageProvider() + ) { MobCatcherUpgradeWrapper() } + // Interfaces instance.register( UpgradeWrapper::class.java, @@ -152,9 +267,73 @@ object CapabilityHandler { NOPCapabilityStorage(), ::FilterUpgradeWrapper ) + + instance.register( + IMagnetUpgrade::class.java, + NOPCapabilityStorage(), + ::MagnetUpgradeWrapper + ) + + instance.register( + IVoidUpgrade::class.java, + NOPCapabilityStorage(), + ::VoidUpgradeWrapper + ) + + instance.register( + IRefillUpgrade::class.java, + NOPCapabilityStorage(), + ::RefillUpgradeWrapper + ) + + instance.register( + ICompactingUpgrade::class.java, + NOPCapabilityStorage(), + ::CompactingUpgradeWrapper + ) + + instance.register( + IEverlastingUpgrade::class.java, + NOPCapabilityStorage(), + ::EverlastingUpgradeWrapper + ) + + instance.register( + IToolSwapperUpgrade::class.java, + NOPCapabilityStorage(), + ::ToolSwapperUpgradeWrapper + ) + + instance.register( + ITankUpgrade::class.java, + NOPCapabilityStorage() + ) { TankUpgradeWrapper() } + + instance.register( + IJukeboxUpgrade::class.java, + NOPCapabilityStorage() + ) { JukeboxUpgradeWrapper() } + + instance.register( + IPumpUpgrade::class.java, + NOPCapabilityStorage() + ) { PumpUpgradeWrapper() } + + instance.register( + IBatteryUpgrade::class.java, + NOPCapabilityStorage() + ) { BatteryUpgradeWrapper() } + + instance.register( + IAnvilUpgrade::class.java, + NOPCapabilityStorage() + ) { AnvilUpgradeWrapper() } } fun cacheBackpackInventory(backpackWrapper: BackpackWrapper) { + if (Config.tickDedupeLogicDisabled) { + return + } if (BACKPACK_INVENTORY_CACHE.containsKey(backpackWrapper.uuid)) { backpackWrapper.isCached = true return @@ -224,4 +403,4 @@ object CapabilityHandler { instance.deserializeNBT(nbt as NBTTagCompound) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/ClientGuiStashHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/ClientGuiStashHandler.kt new file mode 100644 index 0000000..3de569e --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/ClientGuiStashHandler.kt @@ -0,0 +1,170 @@ +package com.cleanroommc.retrosophisticatedbackpacks.handler + +import com.cleanroommc.retrosophisticatedbackpacks.Tags +import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackStashHelper +import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackStashHelper.Result +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlot +import com.cleanroommc.retrosophisticatedbackpacks.mixin.GuiContainerAccessor +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SStashToBackpackPacket +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.RenderHelper +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.client.gui.inventory.GuiContainerCreative +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.Slot +import net.minecraft.item.ItemStack +import net.minecraftforge.client.event.GuiContainerEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly + +@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(modid = Tags.MOD_ID, value = [Side.CLIENT]) +object ClientGuiStashHandler { + private const val MATCH_AND_SPACE_COLOR = 0x55FF55 + private const val SPACE_COLOR = 0xFFFF55 + + @SubscribeEvent + @JvmStatic + fun onDrawForeground(event: GuiContainerEvent.DrawForeground) { + val screen = event.guiContainer + if (screen is GuiContainerCreative) { + return + } + + val player = Minecraft.getMinecraft().player ?: return + val carried = player.inventory.itemStack + if (carried.isEmpty) { + return + } + + for (slot in screen.inventorySlots.inventorySlots) { + if (slot is ModularBackpackSlot || !slot.isEnabled || slot.xPos < -100 || slot.yPos < -100) { + continue + } + drawStashSign(player, slot, carried) + } + } + + @JvmStatic + fun handleMouseClicked(screen: GuiContainer, mouseX: Int, mouseY: Int, mouseButton: Int): Boolean { + if (screen is GuiContainerCreative || mouseButton != 1) { + return false + } + + val player = Minecraft.getMinecraft().player ?: return false + val carried = player.inventory.itemStack + if (carried.isEmpty) { + return false + } + + val slot = (screen as GuiContainerAccessor).`rsb$getSlotAtPosition`(mouseX, mouseY) ?: return false + val action = getStashAction(player, slot, carried) ?: return false + NetworkHandler.INSTANCE.sendToServer(C2SStashToBackpackPacket(slot.slotNumber, action)) + return true + } + + @JvmStatic + fun getStashActionForSlot(player: EntityPlayer, slot: Slot, carried: ItemStack): C2SStashToBackpackPacket.Action? = + getStashAction(player, slot, carried) + + @JvmStatic + fun getStashResultForSlot(player: EntityPlayer, slot: Slot, carried: ItemStack): Pair? { + val slotStack = slot.stack + if (!slotStack.isEmpty && slotStack.count == 1 && slot.canTakeStack(player)) { + val result = BackpackStashHelper.getStashResult(slotStack, carried) + if (result != Result.NO_SPACE) { + return "+" to result + } + } + + if (!slotStack.isEmpty && carried.count == 1 && slot.canTakeStack(player) && + carried.getCapability(Capabilities.BACKPACK_CAPABILITY, null) != null + ) { + val result = BackpackStashHelper.getStashResult(carried, slotStack) + if (result != Result.NO_SPACE) { + return "-" to result + } + } + + return null + } + + private fun drawStashSign( + player: EntityPlayer, + slot: Slot, + carried: ItemStack + ) { + val slotStack = slot.stack + val plusResult = if (!slotStack.isEmpty && slotStack.count == 1 && slot.canTakeStack(player)) { + BackpackStashHelper.getStashResult(slotStack, carried) + } else { + Result.NO_SPACE + } + + if (plusResult != Result.NO_SPACE) { + drawStashText( + "+", + (slot.xPos + 10).toFloat(), + (slot.yPos + 8).toFloat(), + color(plusResult) + ) + return + } + + if (slotStack.isEmpty || !slot.canTakeStack(player) || carried.count != 1 || + carried.getCapability(Capabilities.BACKPACK_CAPABILITY, null) == null + ) { + return + } + + val minusResult = BackpackStashHelper.getStashResult(carried, slotStack) + if (minusResult == Result.NO_SPACE) { + return + } + + drawStashText( + "-", + (slot.xPos + 1).toFloat(), + slot.yPos.toFloat(), + color(minusResult) + ) + } + + private fun drawStashText(text: String, x: Float, y: Float, color: Int) { + GlStateManager.disableLighting() + GlStateManager.disableDepth() + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(text, x, y, color) + GlStateManager.enableDepth() + RenderHelper.enableGUIStandardItemLighting() + } + + private fun getStashAction( + player: EntityPlayer, + slot: Slot, + carried: ItemStack + ): C2SStashToBackpackPacket.Action? { + val slotStack = slot.stack + if (!slotStack.isEmpty && slotStack.count == 1 && slot.canTakeStack(player) && + BackpackStashHelper.getStashResult(slotStack, carried) != Result.NO_SPACE + ) { + return C2SStashToBackpackPacket.Action.CARRIED_TO_SLOT_BACKPACK + } + + if (!slotStack.isEmpty && carried.count == 1 && slot.canTakeStack(player) && + carried.getCapability(Capabilities.BACKPACK_CAPABILITY, null) != null && + BackpackStashHelper.getStashResult(carried, slotStack) != Result.NO_SPACE + ) { + return C2SStashToBackpackPacket.Action.SLOT_TO_CARRIED_BACKPACK + } + + return null + } + + @JvmStatic + fun color(result: Result): Int = + if (result == Result.MATCH_AND_SPACE) MATCH_AND_SPACE_COLOR else SPACE_COLOR +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/ClientImeInputHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/ClientImeInputHandler.kt new file mode 100644 index 0000000..871f7d4 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/ClientImeInputHandler.kt @@ -0,0 +1,29 @@ +package com.cleanroommc.retrosophisticatedbackpacks.handler + +import com.cleanroommc.modularui.api.IMuiScreen +import com.cleanroommc.retrosophisticatedbackpacks.Tags +import com.cleanroommc.retrosophisticatedbackpacks.client.gui.widgets.VanillaTextFieldWidget +import net.minecraftforge.client.event.GuiScreenEvent +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly +import org.lwjgl.input.Keyboard + +@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(modid = Tags.MOD_ID, value = [Side.CLIENT]) +object ClientImeInputHandler { + @SubscribeEvent(priority = EventPriority.LOWEST, receiveCanceled = true) + @JvmStatic + fun onKeyboardInput(event: GuiScreenEvent.KeyboardInputEvent.Pre) { + if (event.gui !is IMuiScreen || Keyboard.getEventKey() != 0 || Keyboard.getEventKeyState()) { + return + } + + val character = Keyboard.getEventCharacter() + if (character >= ' ' && VanillaTextFieldWidget.handleCommittedCharacter(character)) { + event.isCanceled = true + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/EntityEventHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/EntityEventHandler.kt index 47131d0..07f5a18 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/EntityEventHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/EntityEventHandler.kt @@ -4,12 +4,17 @@ import baubles.api.BaublesApi import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks import com.cleanroommc.retrosophisticatedbackpacks.Tags import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackInventoryHelper +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.EverlastingUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherHandler import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiData import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiFactory import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem import com.cleanroommc.retrosophisticatedbackpacks.item.Items +import com.cleanroommc.retrosophisticatedbackpacks.mixin.EntityItemAccessor +import net.minecraft.block.material.Material import net.minecraft.entity.EntityList import net.minecraft.entity.EntityLiving import net.minecraft.entity.EntityLivingBase @@ -19,9 +24,14 @@ import net.minecraft.inventory.EntityEquipmentSlot import net.minecraft.item.ItemStack import net.minecraft.util.EnumActionResult import net.minecraft.util.SoundCategory +import net.minecraft.util.math.BlockPos +import net.minecraftforge.event.entity.EntityJoinWorldEvent +import net.minecraftforge.event.entity.item.ItemExpireEvent import net.minecraftforge.event.entity.living.LivingSpawnEvent import net.minecraftforge.event.entity.player.EntityItemPickupEvent +import net.minecraftforge.event.entity.player.AttackEntityEvent import net.minecraftforge.event.entity.player.PlayerInteractEvent +import net.minecraftforge.event.world.ExplosionEvent import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.items.IItemHandler @@ -105,12 +115,7 @@ object EntityEventHandler { if (!wrapper.canPickupItem(stack)) continue - var slotIndex = 0 - while (!stack.isEmpty && slotIndex < wrapper.slots) { - stack = wrapper.backpackItemStackHandler.prioritizedInsertion(slotIndex, stack, false) - - slotIndex++ - } + stack = wrapper.insertStack(stack, false, true) if (stack.isEmpty) break @@ -119,6 +124,90 @@ object EntityEventHandler { return stack } + @SubscribeEvent + @JvmStatic + fun onItemExpire(event: ItemExpireEvent) { + val stack = event.entityItem.item + val wrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return + if (wrapper.hasEverlastingUpgrade()) { + event.extraLife = Int.MAX_VALUE + event.isCanceled = true + } + } + + @SubscribeEvent + @JvmStatic + fun onEntityJoinWorld(event: EntityJoinWorldEvent) { + val entity = event.entity as? EntityItem ?: return + if (entity.item.getCapability(Capabilities.BACKPACK_CAPABILITY, null)?.hasEverlastingUpgrade() == true) { + keepEverlastingItemAlive(entity) + } + } + + @SubscribeEvent + @JvmStatic + fun onWorldTick(event: net.minecraftforge.fml.common.gameevent.TickEvent.WorldTickEvent) { + if (event.phase != net.minecraftforge.fml.common.gameevent.TickEvent.Phase.END || event.world.isRemote || event.world.totalWorldTime % 20L != 0L) { + return + } + event.world.loadedEntityList.asSequence() + .filterIsInstance() + .filter { it.item.getCapability(Capabilities.BACKPACK_CAPABILITY, null)?.hasEverlastingUpgrade() == true } + .forEach { entity -> + keepEverlastingItemAlive(entity) + if (entity.posY < 0) { + entity.setPosition(entity.posX, 1.0, entity.posZ) + entity.motionY = 0.2 + } + val material = entity.world.getBlockState(BlockPos(entity)).material + if (material == Material.WATER || material == Material.LAVA) { + entity.motionY = 0.08 + entity.fallDistance = 0f + } + } + } + + @SubscribeEvent + @JvmStatic + fun onExplosionDetonate(event: ExplosionEvent.Detonate) { + event.affectedBlocks.removeIf { pos -> + val tile = event.world.getTileEntity(pos) as? com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity + tile?.wrapper?.hasEverlastingUpgrade() == true + } + event.affectedEntities.removeIf { entity -> + entity is EntityItem && entity.item.getCapability(Capabilities.BACKPACK_CAPABILITY, null)?.hasEverlastingUpgrade() == true + } + } + + @SubscribeEvent + @JvmStatic + fun onLeftClickBlock(event: PlayerInteractEvent.LeftClickBlock) { + if (event.world.isRemote) { + return + } + val state = event.world.getBlockState(event.pos) + if (forEachBackpack(event.entityPlayer) { wrapper -> + wrapper.gatherCapabilityUpgrades(Capabilities.ITOOL_SWAPPER_UPGRADE_CAPABILITY) + .any { it.onBlockClick(event.entityPlayer, wrapper, event.pos, state) } + }) { + event.entityPlayer.inventoryContainer.detectAndSendChanges() + } + } + + @SubscribeEvent + @JvmStatic + fun onAttackEntity(event: AttackEntityEvent) { + if (event.entityPlayer.world.isRemote) { + return + } + if (forEachBackpack(event.entityPlayer) { wrapper -> + wrapper.gatherCapabilityUpgrades(Capabilities.ITOOL_SWAPPER_UPGRADE_CAPABILITY) + .any { it.onAttackEntity(event.entityPlayer, wrapper) } + }) { + event.entityPlayer.inventoryContainer.detectAndSendChanges() + } + } + @SubscribeEvent @JvmStatic fun onPlayerInteract(event: PlayerInteractEvent.EntityInteract) { @@ -128,6 +217,14 @@ object EntityEventHandler { if (stack.item is BackpackItem) { if (player.isSneaking) { + if (entity is EntityLivingBase) { + val captureResult = MobCatcherHandler.tryCapture(player, entity) + if (captureResult != EnumActionResult.PASS) { + event.isCanceled = true + event.cancellationResult = captureResult + return + } + } val wrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return var transferred = BackpackInventoryHelper.attemptDepositOnEntity(wrapper, entity) @@ -149,6 +246,7 @@ object EntityEventHandler { } } } + } @SubscribeEvent @@ -174,8 +272,8 @@ object EntityEventHandler { val localY = hitVec.y val isClickingBackpack = - localX in -0.3..0.3 && // Width of the backpack - localY in 0.7..1.5 && // Height of the backpack on the body + localX in -0.3..0.3 && + localY in 0.7..1.5 && localZ in -0.5..-0.15 if (!isClickingBackpack) return @@ -217,11 +315,7 @@ object EntityEventHandler { val threshold = spawnChance + (difficulty * 0.01f) if (tierRoll <= threshold) { - val backpackStack = ItemStack(backpack, 1) - - // TODO: Add disc upgrade here :) - - entity.setItemStackToSlot(EntityEquipmentSlot.CHEST, backpackStack) + entity.setItemStackToSlot(EntityEquipmentSlot.CHEST, ItemStack(backpack, 1)) entity.setDropChance(EntityEquipmentSlot.CHEST, dropChance) return @@ -231,5 +325,32 @@ object EntityEventHandler { } } + private fun BackpackWrapper.hasEverlastingUpgrade(): Boolean = + gatherCapabilityUpgrades(Capabilities.EVERLASTING_UPGRADE_CAPABILITY) + .filterIsInstance() + .isNotEmpty() + + private fun keepEverlastingItemAlive(entity: EntityItem) { + entity.lifespan = Int.MAX_VALUE + entity.setEntityInvulnerable(true) + (entity as EntityItemAccessor).`rsb$setAge`(0) + } + + private fun forEachBackpack(player: net.minecraft.entity.player.EntityPlayer, action: (BackpackWrapper) -> Boolean): Boolean { + if (forEachBackpackIn(InvWrapper(player.inventory), action)) { + return true + } + return RetroSophisticatedBackpacks.baublesLoaded && forEachBackpackIn(BaublesApi.getBaublesHandler(player), action) + } + + private fun forEachBackpackIn(inventory: IItemHandler, action: (BackpackWrapper) -> Boolean): Boolean { + for (slot in 0 until inventory.slots) { + val wrapper = inventory.getStackInSlot(slot).getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: continue + if (action(wrapper)) { + return true + } + } + return false + } private data class BackpackChances(val backpack: BackpackItem, val spawnChance: Float, val dropChance: Float) } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/KeyInputHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/KeyInputHandler.kt index f4ababc..6d52376 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/KeyInputHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/KeyInputHandler.kt @@ -6,15 +6,25 @@ import com.cleanroommc.modularui.screen.ClientScreenHandler import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks import com.cleanroommc.retrosophisticatedbackpacks.Tags import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedRefillUpgradeWrapper import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiData +import com.cleanroommc.retrosophisticatedbackpacks.item.BackpackItem import com.cleanroommc.retrosophisticatedbackpacks.network.C2SOpenBackpackPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SRefillBlockPickPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SToolSwapBlockPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SToolSwapEntityPacket import com.cleanroommc.retrosophisticatedbackpacks.proxy.RSBProxy +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack +import net.minecraft.util.math.RayTraceResult +import net.minecraft.util.text.TextComponentTranslation import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.InputEvent +import net.minecraftforge.items.IItemHandler import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly @@ -66,6 +76,96 @@ object KeyInputHandler { ) } } + + if (RSBProxy.ClientProxy.TOOL_SWAP_KEYBIND.isPressed) { + sendToolSwapPacket(mc) + } + + if (mc.gameSettings.keyBindPickBlock.isPressed) { + sendRefillBlockPickPacket(mc) + } + } + + @SubscribeEvent + @JvmStatic + fun onMouseInput(event: InputEvent.MouseInputEvent) { + val mc = Minecraft.getMinecraft() + if (RSBProxy.ClientProxy.TOOL_SWAP_KEYBIND.isPressed) { + sendToolSwapPacket(mc) + } + if (mc.gameSettings.keyBindPickBlock.isPressed) { + sendRefillBlockPickPacket(mc) + } + } + + private fun sendToolSwapPacket(mc: Minecraft) { + val player = mc.player ?: return + val target = mc.objectMouseOver ?: return + if (player.heldItemMainhand.item is BackpackItem) { + player.sendStatusMessage(TextComponentTranslation("gui.status.unable_to_swap_tool_for_backpack".asTranslationKey()), true) + return + } + + when (target.typeOfHit) { + RayTraceResult.Type.BLOCK -> NetworkHandler.INSTANCE.sendToServer(C2SToolSwapBlockPacket(target.blockPos)) + RayTraceResult.Type.ENTITY -> target.entityHit?.let { NetworkHandler.INSTANCE.sendToServer(C2SToolSwapEntityPacket(it.entityId)) } + else -> Unit + } + } + + private fun sendRefillBlockPickPacket(mc: Minecraft) { + val player = mc.player ?: return + val target = mc.objectMouseOver ?: return + if (player.isCreative || target.typeOfHit != RayTraceResult.Type.BLOCK || !hasAdvancedRefillBackpack(player.inventory) && + (!RetroSophisticatedBackpacks.baublesLoaded || !hasAdvancedRefillBackpack(BaublesApi.getBaublesHandler(player)))) { + return + } + + val pos = target.blockPos + val state = mc.world.getBlockState(pos) + if (state.block.isAir(state, mc.world, pos)) { + return + } + + val pickedStack = state.block.getPickBlock(state, target, mc.world, pos, player) + if (!pickedStack.isEmpty && !hasMatchingStack(player.inventory, pickedStack)) { + NetworkHandler.INSTANCE.sendToServer(C2SRefillBlockPickPacket(pickedStack)) + } + } + + private fun hasAdvancedRefillBackpack(inventory: IInventory): Boolean { + for (slot in 0 until inventory.sizeInventory) { + val wrapper = inventory.getStackInSlot(slot).getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: continue + if (wrapper.gatherCapabilityUpgrades(Capabilities.ADVANCED_REFILL_UPGRADE_CAPABILITY) + .filterIsInstance() + .any { it.enabled } + ) { + return true + } + } + return false + } + + private fun hasMatchingStack(inventory: IInventory, stack: ItemStack): Boolean { + for (slot in 0 until inventory.sizeInventory) { + if (net.minecraftforge.items.ItemHandlerHelper.canItemStacksStack(inventory.getStackInSlot(slot), stack)) { + return true + } + } + return false + } + + private fun hasAdvancedRefillBackpack(inventory: IItemHandler): Boolean { + for (slot in 0 until inventory.slots) { + val wrapper = inventory.getStackInSlot(slot).getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: continue + if (wrapper.gatherCapabilityUpgrades(Capabilities.ADVANCED_REFILL_UPGRADE_CAPABILITY) + .filterIsInstance() + .any { it.enabled } + ) { + return true + } + } + return false } @JvmStatic @@ -108,4 +208,4 @@ object KeyInputHandler { ) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/NetworkHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/NetworkHandler.kt index fbd54b2..2db3625 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/NetworkHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/handler/NetworkHandler.kt @@ -1,6 +1,12 @@ package com.cleanroommc.retrosophisticatedbackpacks.handler import com.cleanroommc.retrosophisticatedbackpacks.network.C2SOpenBackpackPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SMobCatcherReleasePacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SRefillBlockPickPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SStashToBackpackPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SToolSwapBlockPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.C2SToolSwapEntityPacket +import com.cleanroommc.retrosophisticatedbackpacks.network.S2CMobCatcherContentsPacket import net.minecraftforge.fml.common.network.NetworkRegistry import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper import net.minecraftforge.fml.relauncher.Side @@ -23,5 +29,41 @@ object NetworkHandler { idGenerator.next(), Side.SERVER ) + INSTANCE.registerMessage( + C2SRefillBlockPickPacket.Handler::class.java, + C2SRefillBlockPickPacket::class.java, + idGenerator.next(), + Side.SERVER + ) + INSTANCE.registerMessage( + C2SStashToBackpackPacket.Handler::class.java, + C2SStashToBackpackPacket::class.java, + idGenerator.next(), + Side.SERVER + ) + INSTANCE.registerMessage( + C2SToolSwapBlockPacket.Handler::class.java, + C2SToolSwapBlockPacket::class.java, + idGenerator.next(), + Side.SERVER + ) + INSTANCE.registerMessage( + C2SToolSwapEntityPacket.Handler::class.java, + C2SToolSwapEntityPacket::class.java, + idGenerator.next(), + Side.SERVER + ) + INSTANCE.registerMessage( + C2SMobCatcherReleasePacket.Handler::class.java, + C2SMobCatcherReleasePacket::class.java, + idGenerator.next(), + Side.SERVER + ) + INSTANCE.registerMessage( + S2CMobCatcherContentsPacket.Handler::class.java, + S2CMobCatcherContentsPacket::class.java, + idGenerator.next(), + Side.CLIENT + ) } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/BackpackItemStackHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/BackpackItemStackHandler.kt index dce75d6..3bbcace 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/BackpackItemStackHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/BackpackItemStackHandler.kt @@ -14,13 +14,19 @@ class BackpackItemStackHandler(size: Int, private val wrapper: BackpackWrapper) val sortLockedSlots: MutableList = MutableList(size) { false } override fun isItemValid(slot: Int, stack: ItemStack): Boolean = - if (Config.blacklistedItems.contains(stack.item.registryName?.toString())) false + if (wrapper.isSlotBlockedByMobCatcher(slot)) false + else if (Config.isItemDisallowed(stack)) false else if (memorizedSlotStack[slot].isEmpty) stack.item !is BackpackItem || wrapper.canNestBackpack() else if (memorizedSlotRespectNbtList[slot]) ItemStack.areItemStacksEqual(stack, memorizedSlotStack[slot]) else stack.isItemEqualIgnoreDurability(memorizedSlotStack[slot]) override fun getStackLimit(slotIndex: Int, stack: ItemStack): Int = - stacks[slotIndex].maxStackSize * wrapper.getTotalStackMultiplier() + wrapper.getStackLimit(stack) + + override fun setStackInSlot(slot: Int, stack: ItemStack) { + super.setStackInSlot(slot, stack) + if (wrapper.shouldHandleSlotChangeFromGui()) wrapper.onGuiSlotChanged(slot) + } /** * Prioritize insertion by tries inserting on memorized slot first. @@ -55,6 +61,12 @@ class BackpackItemStackHandler(size: Int, private val wrapper: BackpackWrapper) validateSlotIndex(slot) + if (wrapper.isSlotBlockedByMobCatcher(slot)) + return stack + + if (!isItemValid(slot, stack)) + return stack + val existing = stacks[slot] var limit = getStackLimit(slot, stack) @@ -79,6 +91,7 @@ class BackpackItemStackHandler(size: Int, private val wrapper: BackpackWrapper) } onContentsChanged(slot) + if (wrapper.shouldHandleSlotChangeFromGui()) wrapper.onGuiSlotChanged(slot) else wrapper.onSlotChanged(slot) } return if (reachedLimit) ItemHandlerHelper.copyStackWithSize(stack, stack.count - limit) @@ -91,18 +104,22 @@ class BackpackItemStackHandler(size: Int, private val wrapper: BackpackWrapper) validateSlotIndex(slotIndex) + if (wrapper.isSlotBlockedByMobCatcher(slotIndex)) + return ItemStack.EMPTY + val stack = stacks[slotIndex] if (stack.isEmpty) return ItemStack.EMPTY - val slotMaxStackSize = stack.maxStackSize * wrapper.getTotalStackMultiplier() + val slotMaxStackSize = wrapper.getStackLimit(stack) val toExtract = min(amount, slotMaxStackSize) if (stack.count <= toExtract) { if (!simulate) { stacks[slotIndex] = ItemStack.EMPTY onContentsChanged(slotIndex) + if (wrapper.shouldHandleSlotChangeFromGui()) wrapper.onGuiSlotChanged(slotIndex) else wrapper.onSlotChanged(slotIndex) } return stack @@ -110,6 +127,7 @@ class BackpackItemStackHandler(size: Int, private val wrapper: BackpackWrapper) if (!simulate) { stacks[slotIndex] = ItemHandlerHelper.copyStackWithSize(stack, stack.count - toExtract) onContentsChanged(slotIndex) + if (wrapper.shouldHandleSlotChangeFromGui()) wrapper.onGuiSlotChanged(slotIndex) else wrapper.onSlotChanged(slotIndex) } return ItemHandlerHelper.copyStackWithSize(stack, toExtract) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/DelegatedItemHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/DelegatedItemHandler.kt index 86b6df6..5a35d69 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/DelegatedItemHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/DelegatedItemHandler.kt @@ -3,46 +3,36 @@ package com.cleanroommc.retrosophisticatedbackpacks.inventory import net.minecraft.item.ItemStack import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandlerModifiable -import net.minecraftforge.items.wrapper.EmptyHandler class DelegatedItemHandler(var delegated: () -> IItemHandler, var wrappedSlotAmount: Int) : IItemHandlerModifiable { - override fun getSlots(): Int { - val delegated = delegated() - - if (delegated != EmptyHandler.INSTANCE) - check(delegated.slots == wrappedSlotAmount) { - "Mismatched delegated item handler slot amount: assumed to have $wrappedSlotAmount but actually got ${delegated().slots}" - } - - return wrappedSlotAmount - } + override fun getSlots(): Int = wrappedSlotAmount override fun getStackInSlot(slot: Int): ItemStack = - delegated().getStackInSlot(slot) + delegated().let { if (slot in 0 until it.slots) it.getStackInSlot(slot) else ItemStack.EMPTY } override fun insertItem( slot: Int, stack: ItemStack, simulate: Boolean ): ItemStack = - delegated().insertItem(slot, stack, simulate) + delegated().let { if (slot in 0 until it.slots) it.insertItem(slot, stack, simulate) else stack } override fun extractItem( slot: Int, amount: Int, simulate: Boolean ): ItemStack = - delegated().extractItem(slot, amount, simulate) + delegated().let { if (slot in 0 until it.slots) it.extractItem(slot, amount, simulate) else ItemStack.EMPTY } override fun getSlotLimit(slot: Int): Int = - delegated().getSlotLimit(slot) + delegated().let { if (slot in 0 until it.slots) it.getSlotLimit(slot) else 0 } override fun setStackInSlot(slot: Int, stack: ItemStack) { val delegated = delegated() - if (delegated is IItemHandlerModifiable) + if (delegated is IItemHandlerModifiable && slot in 0 until delegated.slots) delegated.setStackInSlot(slot, stack) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/UpgradeItemStackHandler.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/UpgradeItemStackHandler.kt index 89ed4db..8ef5321 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/UpgradeItemStackHandler.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/inventory/UpgradeItemStackHandler.kt @@ -1,10 +1,35 @@ package com.cleanroommc.retrosophisticatedbackpacks.inventory +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import net.minecraft.item.ItemStack import net.minecraft.util.NonNullList import net.minecraftforge.items.ItemStackHandler -class UpgradeItemStackHandler(size: Int) : ItemStackHandler(size) { +class UpgradeItemStackHandler(size: Int, private val wrapper: BackpackWrapper? = null) : ItemStackHandler(size) { val inventory: NonNullList = stacks -} \ No newline at end of file + + override fun setStackInSlot(slot: Int, stack: ItemStack) { + val original = getStackInSlot(slot) + if (!original.isEmpty && !ItemStack.areItemsEqual(original, stack)) { + original.getCapability(Capabilities.UPGRADE_CAPABILITY, null)?.onBeforeRemoved() + } + super.setStackInSlot(slot, stack) + wrapper?.ensureCapturedMobLayoutCurrent() + } + + override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack { + if (!simulate && amount > 0) { + val original = getStackInSlot(slot) + if (!original.isEmpty) { + original.getCapability(Capabilities.UPGRADE_CAPABILITY, null)?.onBeforeRemoved() + } + } + return super.extractItem(slot, amount, simulate).also { + if (!simulate && !it.isEmpty) { + wrapper?.ensureCapturedMobLayoutCurrent() + } + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/AnvilUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/AnvilUpgradeItem.kt new file mode 100644 index 0000000..ea34bed --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/AnvilUpgradeItem.kt @@ -0,0 +1,15 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IAnvilUpgrade +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider + +class AnvilUpgradeItem(registryName: String, private val wrapperFactory: () -> IAnvilUpgrade) : + UpgradeItem(registryName, true) { + override fun initCapabilities(stack: ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BackpackItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BackpackItem.kt index 2fc0695..7929b2e 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BackpackItem.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BackpackItem.kt @@ -4,7 +4,6 @@ import baubles.api.BaubleType import baubles.api.IBauble import baubles.api.render.IRenderBauble import com.cleanroommc.modularui.api.IGuiHolder -import com.cleanroommc.modularui.api.widget.Interactable import com.cleanroommc.modularui.screen.ModularPanel import com.cleanroommc.modularui.screen.UISettings import com.cleanroommc.modularui.value.sync.PanelSyncManager @@ -14,20 +13,24 @@ import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackTier import com.cleanroommc.retrosophisticatedbackpacks.block.BackpackBlock import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.handler.BackpackTooltipHandler import com.cleanroommc.retrosophisticatedbackpacks.client.BackpackBipedModel import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackContainer import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackGuiHolder import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiData import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiData.InventoryType import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiFactory +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.handler.CapabilityHandler import com.cleanroommc.retrosophisticatedbackpacks.handler.RegistryHandler +import com.cleanroommc.retrosophisticatedbackpacks.mixin.EntityItemAccessor import com.cleanroommc.retrosophisticatedbackpacks.util.IModelRegister import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.client.model.ModelBiped import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.util.ITooltipFlag import net.minecraft.entity.Entity +import net.minecraft.entity.item.EntityItem import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayerMP @@ -38,8 +41,6 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.* import net.minecraft.util.math.BlockPos -import net.minecraft.util.text.Style -import net.minecraft.util.text.TextComponentString import net.minecraft.util.text.TextComponentTranslation import net.minecraft.util.text.TextFormatting import net.minecraft.world.World @@ -169,11 +170,17 @@ class BackpackItem( } override fun initCapabilities(stack: ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { - val wrapper = BackpackWrapper(numberOfSlots, numberOfUpgradeSlots) + val wrapper = BackpackWrapper(numberOfSlots, numberOfUpgradeSlots, containerStack = stack) nbt?.let(wrapper::deserializeNBT) return wrapper } + override fun shouldCauseReequipAnimation(oldStack: ItemStack, newStack: ItemStack, slotChanged: Boolean): Boolean = + slotChanged || oldStack.item !== newStack.item || oldStack.metadata != newStack.metadata + + override fun shouldCauseBlockBreakReset(oldStack: ItemStack, newStack: ItemStack): Boolean = + oldStack.item !== newStack.item || oldStack.metadata != newStack.metadata + override fun onUpdate(stack: ItemStack, worldIn: World, entityIn: Entity, itemSlot: Int, isSelected: Boolean) { if (!worldIn.isRemote && entityIn is EntityPlayerMP) { val wrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return @@ -181,12 +188,34 @@ class BackpackItem( if (entityIn.ticksExisted % 20 == 0) wrapper.feed(entityIn, wrapper) - // Only cache on server - if (!wrapper.isCached) + wrapper.tickUpgrades(entityIn, worldIn, entityIn.posX, entityIn.posY, entityIn.posZ) + + if (!Config.tickDedupeLogicDisabled && !wrapper.isCached) CapabilityHandler.cacheBackpackInventory(wrapper) } } + override fun onEntityItemUpdate(entityItem: EntityItem): Boolean { + val wrapper = entityItem.item.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return false + if (wrapper.gatherCapabilityUpgrades(Capabilities.EVERLASTING_UPGRADE_CAPABILITY).isEmpty()) { + return false + } + + entityItem.lifespan = Int.MAX_VALUE + entityItem.setEntityInvulnerable(true) + (entityItem as EntityItemAccessor).`rsb$setAge`(0) + if (entityItem.posY < 1.0) { + entityItem.setPosition(entityItem.posX, 1.0, entityItem.posZ) + entityItem.motionY = 0.2 + } + val material = entityItem.world.getBlockState(BlockPos(entityItem)).material + if (material == net.minecraft.block.material.Material.WATER || material == net.minecraft.block.material.Material.LAVA) { + entityItem.motionY = 0.08 + entityItem.fallDistance = 0f + } + return false + } + override fun isValidArmor(stack: ItemStack, armorType: EntityEquipmentSlot, entity: Entity): Boolean = armorType == EntityEquipmentSlot.CHEST @@ -243,35 +272,23 @@ class BackpackItem( tooltip: MutableList, flagIn: ITooltipFlag ) { - tooltip.add( - TextComponentTranslation( - "tooltip.backpack.inventory_size".asTranslationKey(), - numberOfSlots() - ).formattedText - ) - tooltip.add( - TextComponentTranslation( - "tooltip.backpack.upgrade_slots_size".asTranslationKey(), - numberOfUpgradeSlots() - ).formattedText - ) - - if (Interactable.hasShiftDown()) { - val wrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return - val stackHint = - if (wrapper.isStackedByMultiplication()) "(xM)" - else "(+M)" - + val wrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) + if (flagIn.isAdvanced && wrapper != null) { + tooltip.add(TextFormatting.DARK_GRAY.toString() + "UUID: ${wrapper.uuid}") + } + if (!BackpackTooltipHandler.shouldShowContentsTooltip()) { tooltip.add( TextComponentTranslation( - "tooltip.backpack.stack_multiplier".asTranslationKey(), - wrapper.getTotalStackMultiplier(), - TextComponentString(stackHint).setStyle(Style().setColor(TextFormatting.RED)).formattedText - ).formattedText + "tooltip.backpack.press_for_contents".asTranslationKey(), + TextComponentTranslation("tooltip.backpack.shift".asTranslationKey()) + .setStyle(net.minecraft.util.text.Style().setColor(TextFormatting.AQUA)) + .formattedText + ).setStyle(net.minecraft.util.text.Style().setColor(TextFormatting.GRAY)).formattedText ) - } else { - tooltip.add(TextComponentTranslation("tooltip.shift_to_reveal".asTranslationKey()).formattedText) + return } + + wrapper?.let { BackpackTooltipHandler.addTooltipLines(it, tooltip) } } override fun buildUI( @@ -282,8 +299,8 @@ class BackpackItem( val stack = data.usedItemStack val wrapper = stack.getCapability(Capabilities.BACKPACK_CAPABILITY, null)!! val slotIndex = if (data.inventoryType == InventoryType.PLAYER_INVENTORY) data.slotIndex else null - uiSettings.customContainer { BackpackContainer(wrapper, slotIndex) } - uiSettings.canInteractWith { + uiSettings.customContainer { BackpackContainer(wrapper, slotIndex, data.inventoryType, data.slotIndex, data.targetEntity) } + uiSettings.canInteractWith { if (data.targetEntity.isDead) false else it.getDistance(data.targetEntity) <= 4.0 } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BatteryUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BatteryUpgradeItem.kt new file mode 100644 index 0000000..80fc704 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/BatteryUpgradeItem.kt @@ -0,0 +1,15 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IBatteryUpgrade +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider + +class BatteryUpgradeItem(registryName: String, private val wrapperFactory: () -> IBatteryUpgrade) : + UpgradeItem(registryName, true) { + override fun initCapabilities(stack: ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CompactingUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CompactingUpgradeItem.kt new file mode 100644 index 0000000..2f17f32 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CompactingUpgradeItem.kt @@ -0,0 +1,6 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.ICompactingUpgrade + +class CompactingUpgradeItem(registryName: String, wrapperFactory: () -> ICompactingUpgrade) : + RankedUpgradeItem(registryName, wrapperFactory) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CraftingUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CraftingUpgradeItem.kt index 7c5f4d3..31a61a6 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CraftingUpgradeItem.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/CraftingUpgradeItem.kt @@ -1,12 +1,8 @@ package com.cleanroommc.retrosophisticatedbackpacks.item import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.CraftingUpgradeWrapper -import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey -import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.text.TextComponentTranslation -import net.minecraft.world.World import net.minecraftforge.common.capabilities.ICapabilityProvider class CraftingUpgradeItem(registryName: String) : UpgradeItem(registryName, true) { @@ -15,8 +11,4 @@ class CraftingUpgradeItem(registryName: String) : UpgradeItem(registryName, true nbt?.let(wrapper::deserializeNBT) return wrapper } - - override fun addInformation(stack: ItemStack, worldIn: World?, tooltip: MutableList, flagIn: ITooltipFlag) { - tooltip.add(TextComponentTranslation("tooltip.crafting_upgrade".asTranslationKey()).formattedText) - } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/EverlastingUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/EverlastingUpgradeItem.kt new file mode 100644 index 0000000..79e01b0 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/EverlastingUpgradeItem.kt @@ -0,0 +1,6 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IEverlastingUpgrade + +class EverlastingUpgradeItem(registryName: String, wrapperFactory: () -> IEverlastingUpgrade) : + HiddenUpgradeItem(registryName, wrapperFactory) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/HiddenUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/HiddenUpgradeItem.kt new file mode 100644 index 0000000..80db133 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/HiddenUpgradeItem.kt @@ -0,0 +1,17 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider +import net.minecraftforge.common.util.INBTSerializable + +abstract class HiddenUpgradeItem( + registryName: String, + private val wrapperFactory: () -> CP, +) : UpgradeItem(registryName, false) + where CP : ICapabilityProvider, CP : INBTSerializable { + override fun initCapabilities(stack: net.minecraft.item.ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory.invoke() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/Items.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/Items.kt index a84cad0..327b924 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/Items.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/Items.kt @@ -3,6 +3,7 @@ package com.cleanroommc.retrosophisticatedbackpacks.item import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackTier import com.cleanroommc.retrosophisticatedbackpacks.block.Blocks import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.* +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherUpgradeWrapper import com.cleanroommc.retrosophisticatedbackpacks.config.Config import net.minecraft.item.Item @@ -19,8 +20,8 @@ object Items { val backpackLeather = BackpackItem( "backpack_leather", Blocks.leatherBackpack, - Config.leatherBackpack::slots, - Config.leatherBackpack::upgradeSlots, + Config.leatherBackpack::inventorySlotCount, + Config.leatherBackpack::upgradeSlotCount, BackpackTier.LEATHER ) @@ -28,8 +29,8 @@ object Items { val backpackIron = BackpackItem( "backpack_iron", Blocks.ironBackpack, - Config.ironBackpack::slots, - Config.ironBackpack::upgradeSlots, + Config.ironBackpack::inventorySlotCount, + Config.ironBackpack::upgradeSlotCount, BackpackTier.IRON ) @@ -37,8 +38,8 @@ object Items { val backpackGold = BackpackItem( "backpack_gold", Blocks.goldBackpack, - Config.goldBackpack::slots, - Config.goldBackpack::upgradeSlots, + Config.goldBackpack::inventorySlotCount, + Config.goldBackpack::upgradeSlotCount, BackpackTier.GOLD ) @@ -46,8 +47,8 @@ object Items { val backpackDiamond = BackpackItem( "backpack_diamond", Blocks.diamondBackpack, - Config.diamondBackpack::slots, - Config.diamondBackpack::upgradeSlots, + Config.diamondBackpack::inventorySlotCount, + Config.diamondBackpack::upgradeSlotCount, BackpackTier.DIAMOND ) @@ -55,8 +56,8 @@ object Items { val backpackObsidian = BackpackItem( "backpack_obsidian", Blocks.obsidianBackpack, - Config.obsidianBackpack::slots, - Config.obsidianBackpack::upgradeSlots, + Config.obsidianBackpack::inventorySlotCount, + Config.obsidianBackpack::upgradeSlotCount, BackpackTier.OBSIDIAN ) @@ -117,4 +118,64 @@ object Items { @JvmField val advancedFilterUpgrade = FilterUpgradeItem("advanced_filter_upgrade", ::AdvancedFilterUpgradeWrapper) -} \ No newline at end of file + + @JvmField + val magnetUpgrade = MagnetUpgradeItem("magnet_upgrade", ::MagnetUpgradeWrapper) + + @JvmField + val advancedMagnetUpgrade = MagnetUpgradeItem("advanced_magnet_upgrade", ::AdvancedMagnetUpgradeWrapper) + + @JvmField + val voidUpgrade = VoidUpgradeItem("void_upgrade", ::VoidUpgradeWrapper) + + @JvmField + val advancedVoidUpgrade = VoidUpgradeItem("advanced_void_upgrade", ::AdvancedVoidUpgradeWrapper) + + @JvmField + val refillUpgrade = RefillUpgradeItem("refill_upgrade", ::RefillUpgradeWrapper) + + @JvmField + val advancedRefillUpgrade = RefillUpgradeItem("advanced_refill_upgrade", ::AdvancedRefillUpgradeWrapper) + + @JvmField + val compactingUpgrade = CompactingUpgradeItem("compacting_upgrade", ::CompactingUpgradeWrapper) + + @JvmField + val advancedCompactingUpgrade = CompactingUpgradeItem("advanced_compacting_upgrade", ::AdvancedCompactingUpgradeWrapper) + + @JvmField + val everlastingUpgrade = EverlastingUpgradeItem("everlasting_upgrade", ::EverlastingUpgradeWrapper) + + @JvmField + val jukeboxUpgrade = JukeboxUpgradeItem("jukebox_upgrade", ::JukeboxUpgradeWrapper) + + @JvmField + val advancedJukeboxUpgrade = JukeboxUpgradeItem("advanced_jukebox_upgrade", ::AdvancedJukeboxUpgradeWrapper) + + @JvmField + val toolSwapperUpgrade = ToolSwapperUpgradeItem("tool_swapper_upgrade", ::ToolSwapperUpgradeWrapper) + + @JvmField + val advancedToolSwapperUpgrade = ToolSwapperUpgradeItem("advanced_tool_swapper_upgrade", ::AdvancedToolSwapperUpgradeWrapper, hasTab = true) + + @JvmField + val tankUpgrade = TankUpgradeItem("tank_upgrade", ::TankUpgradeWrapper) + + @JvmField + val pumpUpgrade = PumpUpgradeItem("pump_upgrade", ::PumpUpgradeWrapper) + + @JvmField + val advancedPumpUpgrade = PumpUpgradeItem("advanced_pump_upgrade", ::AdvancedPumpUpgradeWrapper) + + @JvmField + val batteryUpgrade = BatteryUpgradeItem("battery_upgrade", ::BatteryUpgradeWrapper) + + @JvmField + val anvilUpgrade = AnvilUpgradeItem("anvil_upgrade", ::AnvilUpgradeWrapper) + + @JvmField + val mobCatcherUpgrade = MobCatcherUpgradeItem("mob_catcher_upgrade", false, ::MobCatcherUpgradeWrapper) + + @JvmField + val advancedMobCatcherUpgrade = MobCatcherUpgradeItem("advanced_mob_catcher_upgrade", true, ::MobCatcherUpgradeWrapper) +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/JukeboxUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/JukeboxUpgradeItem.kt new file mode 100644 index 0000000..acdad3f --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/JukeboxUpgradeItem.kt @@ -0,0 +1,16 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IJukeboxUpgrade +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider + +class JukeboxUpgradeItem( + registryName: String, + private val wrapperFactory: () -> IJukeboxUpgrade, +) : UpgradeItem(registryName, true, "jukebox") { + override fun initCapabilities(stack: net.minecraft.item.ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory.invoke() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/MagnetUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/MagnetUpgradeItem.kt new file mode 100644 index 0000000..09ec3e3 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/MagnetUpgradeItem.kt @@ -0,0 +1,6 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IMagnetUpgrade + +class MagnetUpgradeItem(registryName: String, wrapperFactory: () -> IMagnetUpgrade) : + RankedUpgradeItem(registryName, wrapperFactory) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/MobCatcherUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/MobCatcherUpgradeItem.kt new file mode 100644 index 0000000..d4e8442 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/MobCatcherUpgradeItem.kt @@ -0,0 +1,18 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherUpgradeWrapper +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider + +class MobCatcherUpgradeItem( + registryName: String, + val advanced: Boolean, + private val wrapperFactory: (Boolean) -> MobCatcherUpgradeWrapper +) : UpgradeItem(registryName, false) { + override fun initCapabilities(stack: ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory(advanced) + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/PumpUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/PumpUpgradeItem.kt new file mode 100644 index 0000000..67b9ccf --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/PumpUpgradeItem.kt @@ -0,0 +1,15 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IPumpUpgrade +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider + +class PumpUpgradeItem(registryName: String, private val wrapperFactory: () -> IPumpUpgrade) : + UpgradeItem(registryName, true) { + override fun initCapabilities(stack: ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/RefillUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/RefillUpgradeItem.kt new file mode 100644 index 0000000..6a3fc26 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/RefillUpgradeItem.kt @@ -0,0 +1,6 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IRefillUpgrade + +class RefillUpgradeItem(registryName: String, wrapperFactory: () -> IRefillUpgrade) : + RankedUpgradeItem(registryName, wrapperFactory) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/StackUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/StackUpgradeItem.kt index 7120b7b..8f4c53d 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/StackUpgradeItem.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/StackUpgradeItem.kt @@ -1,18 +1,16 @@ package com.cleanroommc.retrosophisticatedbackpacks.item -import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.ItemStack -import net.minecraft.util.text.TextComponentTranslation import net.minecraft.world.World -class StackUpgradeItem(registryName: String, val multiplier: () -> Int) : UpgradeItem(registryName) { +class StackUpgradeItem(registryName: String, val multiplier: () -> Int) : UpgradeItem(registryName, upgradeGroup = "stack") { override fun addInformation( stack: ItemStack, worldIn: World?, tooltip: MutableList, flagIn: ITooltipFlag ) { - tooltip.add(TextComponentTranslation("tooltip.stack_upgrade".asTranslationKey(), multiplier()).formattedText) + addUpgradeTooltip(tooltip, multiplier()) } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/TankUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/TankUpgradeItem.kt new file mode 100644 index 0000000..e987a14 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/TankUpgradeItem.kt @@ -0,0 +1,16 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.ITankUpgrade +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.capabilities.ICapabilityProvider + +class TankUpgradeItem(registryName: String, wrapperFactory: () -> ITankUpgrade) : + UpgradeItem(registryName, true) { + private val wrapperFactory = wrapperFactory + + override fun initCapabilities(stack: net.minecraft.item.ItemStack, nbt: NBTTagCompound?): ICapabilityProvider { + val capability = wrapperFactory.invoke() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/ToolSwapperUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/ToolSwapperUpgradeItem.kt new file mode 100644 index 0000000..341b675 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/ToolSwapperUpgradeItem.kt @@ -0,0 +1,16 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IToolSwapperUpgrade + +class ToolSwapperUpgradeItem( + registryName: String, + private val wrapperFactory: () -> IToolSwapperUpgrade, + hasTab: Boolean = false +) : + UpgradeItem(registryName, hasTab) { + override fun initCapabilities(stack: net.minecraft.item.ItemStack, nbt: net.minecraft.nbt.NBTTagCompound?): net.minecraftforge.common.capabilities.ICapabilityProvider { + val capability = wrapperFactory.invoke() + nbt?.let(capability::deserializeNBT) + return capability + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/UpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/UpgradeItem.kt index cfb736c..320ed79 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/UpgradeItem.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/UpgradeItem.kt @@ -4,13 +4,14 @@ import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities import com.cleanroommc.retrosophisticatedbackpacks.handler.RegistryHandler import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.resources.I18n import net.minecraft.client.util.ITooltipFlag import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound -import net.minecraft.util.text.TextComponentTranslation +import net.minecraft.util.text.TextFormatting import net.minecraft.world.World -abstract class UpgradeItem(registryName: String, val hasTab: Boolean = false) : ItemBase() { +abstract class UpgradeItem(registryName: String, val hasTab: Boolean = false, val upgradeGroup: String? = null) : ItemBase() { init { setCreativeTab(RetroSophisticatedBackpacks.CREATIVE_TAB) setRegistryName(registryName) @@ -21,7 +22,19 @@ abstract class UpgradeItem(registryName: String, val hasTab: Boolean = false) : } override fun addInformation(stack: ItemStack, worldIn: World?, tooltip: MutableList, flagIn: ITooltipFlag) { - tooltip.add(TextComponentTranslation("tooltip.${registryName!!.path}".asTranslationKey()).formattedText) + addUpgradeTooltip(tooltip) + } + + protected fun addUpgradeTooltip(tooltip: MutableList, vararg args: Any) { + val path = registryName!!.path + val key = "item.${registryName!!.namespace}.$path.tooltip" + val legacyKey = "tooltip.$path".asTranslationKey() + val tooltipText = when { + I18n.hasKey(key) -> I18n.format(key, *args) + I18n.hasKey(legacyKey) -> I18n.format(legacyKey, *args) + else -> return + } + tooltipText.replace("\\n", "\n").split('\n').forEach { tooltip.add(TextFormatting.DARK_GRAY.toString() + it) } } override fun getNBTShareTag(stack: ItemStack): NBTTagCompound? { @@ -43,4 +56,4 @@ abstract class UpgradeItem(registryName: String, val hasTab: Boolean = false) : if (nbt.hasKey("Capability")) wrapper.deserializeNBT(nbt.getCompoundTag("Capability")) else wrapper.deserializeNBT(nbt) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/VoidUpgradeItem.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/VoidUpgradeItem.kt new file mode 100644 index 0000000..36cc077 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/item/VoidUpgradeItem.kt @@ -0,0 +1,6 @@ +package com.cleanroommc.retrosophisticatedbackpacks.item + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IVoidUpgrade + +class VoidUpgradeItem(registryName: String, wrapperFactory: () -> IVoidUpgrade) : + RankedUpgradeItem(registryName, wrapperFactory) diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SMobCatcherReleasePacket.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SMobCatcherReleasePacket.kt new file mode 100644 index 0000000..5365182 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SMobCatcherReleasePacket.kt @@ -0,0 +1,33 @@ +package com.cleanroommc.retrosophisticatedbackpacks.network + +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherHandler +import io.netty.buffer.ByteBuf +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext +import java.util.UUID + +class C2SMobCatcherReleasePacket() : IRefinedMessage { + private var capturedMobId: UUID = UUID(0L, 0L) + + constructor(capturedMobId: UUID) : this() { + this.capturedMobId = capturedMobId + } + + override fun toBytes(buf: ByteBuf) { + buf.writeLong(capturedMobId.mostSignificantBits) + buf.writeLong(capturedMobId.leastSignificantBits) + } + + override fun fromBytes(buf: ByteBuf) { + capturedMobId = UUID(buf.readLong(), buf.readLong()) + } + + class Handler : INoReplyMessageHandler { + override fun onMessage(message: C2SMobCatcherReleasePacket, ctx: MessageContext): IRefinedMessage? { + val player = ctx.serverHandler.player + player.serverWorld.addScheduledTask { + MobCatcherHandler.release(player, message.capturedMobId) + } + return null + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SRefillBlockPickPacket.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SRefillBlockPickPacket.kt new file mode 100644 index 0000000..556dc2a --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SRefillBlockPickPacket.kt @@ -0,0 +1,63 @@ +package com.cleanroommc.retrosophisticatedbackpacks.network + +import baubles.api.BaublesApi +import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedRefillUpgradeWrapper +import io.netty.buffer.ByteBuf +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.network.ByteBufUtils +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext +import net.minecraftforge.items.IItemHandler +import net.minecraftforge.items.wrapper.InvWrapper + +class C2SRefillBlockPickPacket() : IRefinedMessage { + private var pickedStack = ItemStack.EMPTY + + constructor(pickedStack: ItemStack) : this() { + this.pickedStack = pickedStack.copy() + } + + override fun toBytes(buf: ByteBuf) { + ByteBufUtils.writeItemStack(buf, pickedStack) + } + + override fun fromBytes(buf: ByteBuf) { + pickedStack = ByteBufUtils.readItemStack(buf) + } + + class Handler : INoReplyMessageHandler { + override fun onMessage(message: C2SRefillBlockPickPacket, ctx: MessageContext): IRefinedMessage? { + val player = ctx.serverHandler.player + val world = player.serverWorld + + world.addScheduledTask { + if (!message.pickedStack.isEmpty) { + if (!attemptPick(player, InvWrapper(player.inventory), message.pickedStack) && RetroSophisticatedBackpacks.baublesLoaded) { + attemptPick(player, BaublesApi.getBaublesHandler(player), message.pickedStack) + } + } + } + + return null + } + + private fun attemptPick(player: EntityPlayer, targetInventory: IItemHandler, pickedStack: ItemStack): Boolean { + for (slot in 0 until targetInventory.slots) { + val wrapper = targetInventory.getStackInSlot(slot) + .getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: continue + if (tryRefill(player, wrapper, pickedStack)) { + return true + } + } + return false + } + + private fun tryRefill(player: EntityPlayer, wrapper: BackpackWrapper, pickedStack: ItemStack): Boolean = + wrapper.gatherCapabilityUpgrades(Capabilities.ADVANCED_REFILL_UPGRADE_CAPABILITY) + .filterIsInstance() + .any { it.pickBlock(player, wrapper, pickedStack) } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SStashToBackpackPacket.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SStashToBackpackPacket.kt new file mode 100644 index 0000000..b5ed868 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SStashToBackpackPacket.kt @@ -0,0 +1,112 @@ +package com.cleanroommc.retrosophisticatedbackpacks.network + +import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackStashHelper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import io.netty.buffer.ByteBuf +import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.item.ItemStack +import net.minecraft.network.play.server.SPacketSetSlot +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext +import net.minecraftforge.items.ItemHandlerHelper + +class C2SStashToBackpackPacket() : IRefinedMessage { + enum class Action { + CARRIED_TO_SLOT_BACKPACK, + SLOT_TO_CARRIED_BACKPACK + } + + private var slotNumber = -1 + private var action = Action.CARRIED_TO_SLOT_BACKPACK + + constructor(slotNumber: Int, action: Action) : this() { + this.slotNumber = slotNumber + this.action = action + } + + override fun toBytes(buf: ByteBuf) { + buf.writeInt(slotNumber) + buf.writeByte(action.ordinal) + } + + override fun fromBytes(buf: ByteBuf) { + slotNumber = buf.readInt() + action = Action.entries[buf.readUnsignedByte().toInt()] + } + + class Handler : INoReplyMessageHandler { + override fun onMessage(message: C2SStashToBackpackPacket, ctx: MessageContext): IRefinedMessage? { + val player = ctx.serverHandler.player + player.serverWorld.addScheduledTask { + val container = player.openContainer ?: return@addScheduledTask + if (message.slotNumber !in 0 until container.inventorySlots.size) { + return@addScheduledTask + } + + when (message.action) { + Action.CARRIED_TO_SLOT_BACKPACK -> stashCarriedToSlotBackpack(player, container, message.slotNumber) + Action.SLOT_TO_CARRIED_BACKPACK -> stashSlotToCarriedBackpack(player, container, message.slotNumber) + } + } + return null + } + + private fun stashCarriedToSlotBackpack(player: EntityPlayerMP, container: net.minecraft.inventory.Container, slotNumber: Int) { + val carried = player.inventory.itemStack + if (carried.isEmpty) { + return + } + + val slot = container.getSlot(slotNumber) + val backpackStack = slot.stack + if (backpackStack.isEmpty || backpackStack.count != 1 || !slot.canTakeStack(player)) { + return + } + + val wrapper = backpackStack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return + val remaining = BackpackStashHelper.stash(wrapper, carried, false) + if (remaining.count == carried.count) { + return + } + + player.inventory.itemStack = if (remaining.isEmpty) ItemStack.EMPTY else remaining + syncCarriedStack(player) + slot.putStack(backpackStack) + slot.onSlotChanged() + container.detectAndSendChanges() + } + + private fun stashSlotToCarriedBackpack(player: EntityPlayerMP, container: net.minecraft.inventory.Container, slotNumber: Int) { + val backpackStack = player.inventory.itemStack + if (backpackStack.isEmpty || backpackStack.count != 1) { + return + } + + val wrapper = backpackStack.getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: return + val slot = container.getSlot(slotNumber) + val slotStack = slot.stack + if (slotStack.isEmpty || !slot.canTakeStack(player)) { + return + } + + val remaining = BackpackStashHelper.stash(wrapper, slotStack, true) + val moved = slotStack.count - remaining.count + if (moved <= 0) { + return + } + + val taken = slot.decrStackSize(moved) + val remainder = BackpackStashHelper.stash(wrapper, taken, false) + if (!remainder.isEmpty) { + ItemHandlerHelper.giveItemToPlayer(player, remainder) + } + slot.onTake(player, taken) + slot.onSlotChanged() + syncCarriedStack(player) + container.detectAndSendChanges() + } + + private fun syncCarriedStack(player: EntityPlayerMP) { + player.connection.sendPacket(SPacketSetSlot(-1, -1, player.inventory.itemStack)) + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SToolSwapBlockPacket.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SToolSwapBlockPacket.kt new file mode 100644 index 0000000..7a170c4 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SToolSwapBlockPacket.kt @@ -0,0 +1,77 @@ +package com.cleanroommc.retrosophisticatedbackpacks.network + +import baubles.api.BaublesApi +import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IToolSwapperUpgrade +import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import io.netty.buffer.ByteBuf +import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.TextComponentTranslation +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext +import net.minecraftforge.items.IItemHandler +import net.minecraftforge.items.wrapper.InvWrapper + +class C2SToolSwapBlockPacket() : IRefinedMessage { + private var pos = BlockPos.ORIGIN + + constructor(pos: BlockPos) : this() { + this.pos = pos.toImmutable() + } + + override fun toBytes(buf: ByteBuf) { + buf.writeLong(pos.toLong()) + } + + override fun fromBytes(buf: ByteBuf) { + pos = BlockPos.fromLong(buf.readLong()) + } + + class Handler : INoReplyMessageHandler { + override fun onMessage(message: C2SToolSwapBlockPacket, ctx: MessageContext): IRefinedMessage? { + val player = ctx.serverHandler.player + player.serverWorld.addScheduledTask { + val state = player.world.getBlockState(message.pos) + var anyUpgradeCanInteract = false + var result = false + + ToolSwapPacketHelper.runOnBackpacks(player) { wrapper -> + for (upgrade in wrapper.gatherCapabilityUpgrades(Capabilities.ITOOL_SWAPPER_UPGRADE_CAPABILITY).filterIsInstance()) { + if (!upgrade.canProcessBlockInteract() || result) continue + anyUpgradeCanInteract = true + result = upgrade.onBlockInteract(player, wrapper, player.world, message.pos, state) + } + result + } + + when { + !anyUpgradeCanInteract -> ToolSwapPacketHelper.sendStatus(player, "gui.status.no_tool_swap_upgrade_present") + !result -> ToolSwapPacketHelper.sendStatus(player, "gui.status.no_tool_found_for_block") + else -> player.inventoryContainer.detectAndSendChanges() + } + } + return null + } + } +} + +internal object ToolSwapPacketHelper { + fun runOnBackpacks(player: EntityPlayerMP, action: (BackpackWrapper) -> Boolean): Boolean { + if (runOnBackpacksIn(InvWrapper(player.inventory), action)) return true + return RetroSophisticatedBackpacks.baublesLoaded && runOnBackpacksIn(BaublesApi.getBaublesHandler(player), action) + } + + fun sendStatus(player: EntityPlayerMP, langKey: String) { + player.sendStatusMessage(TextComponentTranslation(langKey.asTranslationKey()), true) + } + + private fun runOnBackpacksIn(inventory: IItemHandler, action: (BackpackWrapper) -> Boolean): Boolean { + for (slot in 0 until inventory.slots) { + val wrapper = inventory.getStackInSlot(slot).getCapability(Capabilities.BACKPACK_CAPABILITY, null) ?: continue + if (action(wrapper)) return true + } + return false + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SToolSwapEntityPacket.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SToolSwapEntityPacket.kt new file mode 100644 index 0000000..bfd3269 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/C2SToolSwapEntityPacket.kt @@ -0,0 +1,49 @@ +package com.cleanroommc.retrosophisticatedbackpacks.network + +import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IToolSwapperUpgrade +import io.netty.buffer.ByteBuf +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext + +class C2SToolSwapEntityPacket() : IRefinedMessage { + private var entityId = -1 + + constructor(entityId: Int) : this() { + this.entityId = entityId + } + + override fun toBytes(buf: ByteBuf) { + buf.writeInt(entityId) + } + + override fun fromBytes(buf: ByteBuf) { + entityId = buf.readInt() + } + + class Handler : INoReplyMessageHandler { + override fun onMessage(message: C2SToolSwapEntityPacket, ctx: MessageContext): IRefinedMessage? { + val player = ctx.serverHandler.player + player.serverWorld.addScheduledTask { + val entity = player.world.getEntityByID(message.entityId) ?: return@addScheduledTask + var anyUpgradeCanInteract = false + var result = false + + ToolSwapPacketHelper.runOnBackpacks(player) { wrapper -> + for (upgrade in wrapper.gatherCapabilityUpgrades(Capabilities.ITOOL_SWAPPER_UPGRADE_CAPABILITY).filterIsInstance()) { + if (!upgrade.canProcessEntityInteract() || result) continue + anyUpgradeCanInteract = true + result = upgrade.onEntityInteract(player, wrapper, entity) + } + result + } + + when { + !anyUpgradeCanInteract -> ToolSwapPacketHelper.sendStatus(player, "gui.status.no_tool_swap_upgrade_present") + !result -> ToolSwapPacketHelper.sendStatus(player, "gui.status.no_tool_found_for_entity") + else -> player.inventoryContainer.detectAndSendChanges() + } + } + return null + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/S2CMobCatcherContentsPacket.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/S2CMobCatcherContentsPacket.kt new file mode 100644 index 0000000..a3303f3 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/network/S2CMobCatcherContentsPacket.kt @@ -0,0 +1,32 @@ +package com.cleanroommc.retrosophisticatedbackpacks.network + +import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherStorage +import io.netty.buffer.ByteBuf +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.fml.common.network.ByteBufUtils +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext + +class S2CMobCatcherContentsPacket() : IRefinedMessage { + private var capturedMobsTag = NBTTagCompound() + + constructor(backpackWrapper: BackpackWrapper) : this() { + capturedMobsTag = MobCatcherStorage.getCapturedMobsTag(backpackWrapper) + } + + override fun toBytes(buf: ByteBuf) { + ByteBufUtils.writeTag(buf, capturedMobsTag) + } + + override fun fromBytes(buf: ByteBuf) { + capturedMobsTag = ByteBufUtils.readTag(buf) ?: NBTTagCompound() + } + + class Handler : INoReplyMessageHandler { + override fun onMessage(message: S2CMobCatcherContentsPacket, ctx: MessageContext): IRefinedMessage? { + RetroSophisticatedBackpacks.proxy.applyMobCatcherContentsSync(message.capturedMobsTag) + return null + } + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/proxy/RSBProxy.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/proxy/RSBProxy.kt index 0962f22..43f4b24 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/proxy/RSBProxy.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/proxy/RSBProxy.kt @@ -3,15 +3,22 @@ package com.cleanroommc.retrosophisticatedbackpacks.proxy import com.cleanroommc.bogosorter.BogoSortAPI import com.cleanroommc.modularui.factory.GuiManager import com.cleanroommc.retrosophisticatedbackpacks.block.Blocks +import com.cleanroommc.retrosophisticatedbackpacks.client.BackpackBlockEntityRenderer import com.cleanroommc.retrosophisticatedbackpacks.client.BackpackDynamicModel +import com.cleanroommc.retrosophisticatedbackpacks.client.BackpackItemStackRenderer +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.mobcatcher.MobCatcherStorage +import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackContainer import com.cleanroommc.retrosophisticatedbackpacks.common.gui.PlayerInventoryGuiFactory import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlot import com.cleanroommc.retrosophisticatedbackpacks.common.gui.slot.ModularBackpackSlotWrapper import com.cleanroommc.retrosophisticatedbackpacks.item.Items +import com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey +import net.minecraft.client.Minecraft import net.minecraft.client.renderer.block.model.ModelResourceLocation import net.minecraft.client.settings.KeyBinding import net.minecraft.item.Item +import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.client.model.ModelLoader import net.minecraftforge.client.model.ModelLoaderRegistry import net.minecraftforge.fml.client.registry.ClientRegistry @@ -47,12 +54,14 @@ abstract class RSBProxy { open fun registerItemRenderer(item: Item, meta: Int, id: String) {} + open fun applyMobCatcherContentsSync(nbt: NBTTagCompound) {} + class ServerProxy : RSBProxy() class ClientProxy : RSBProxy() { companion object { private val KEYBINDS: List by lazy { - listOf(OPEN_BACKPACK_KEYBIND) + listOf(OPEN_BACKPACK_KEYBIND, TOOL_SWAP_KEYBIND) } val OPEN_BACKPACK_KEYBIND = KeyBinding( @@ -60,6 +69,12 @@ abstract class RSBProxy { Keyboard.KEY_B, "key.category".asTranslationKey() ) + + val TOOL_SWAP_KEYBIND = KeyBinding( + "key.tool_swap.desc".asTranslationKey(), + Keyboard.KEY_NONE, + "key.category".asTranslationKey() + ) } @@ -79,6 +94,12 @@ abstract class RSBProxy { for (keyBinding in KEYBINDS) ClientRegistry.registerKeyBinding(keyBinding) + + ClientRegistry.bindTileEntitySpecialRenderer(BackpackTileEntity::class.java, BackpackBlockEntityRenderer()) + + for (backpackItem in Items.BACKPACK_ITEMS) { + backpackItem.tileEntityItemStackRenderer = BackpackItemStackRenderer() + } } override fun preInit(event: FMLPreInitializationEvent) { @@ -86,5 +107,13 @@ abstract class RSBProxy { ModelLoaderRegistry.registerLoader(BackpackDynamicModel.Loader()) } + + override fun applyMobCatcherContentsSync(nbt: NBTTagCompound) { + val mc = Minecraft.getMinecraft() + mc.addScheduledTask { + val container = mc.player?.openContainer as? BackpackContainer ?: return@addScheduledTask + MobCatcherStorage.applyCapturedMobsTag(container.backpackWrapper, nbt) + } + } } } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/BackpackSH.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/BackpackSH.kt index 7b897e8..848e6c4 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/BackpackSH.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/BackpackSH.kt @@ -1,18 +1,36 @@ package com.cleanroommc.retrosophisticatedbackpacks.sync import com.cleanroommc.modularui.value.sync.SyncHandler +import com.cleanroommc.retrosophisticatedbackpacks.backpack.DisplaySide import com.cleanroommc.retrosophisticatedbackpacks.backpack.BackpackInventoryHelper import com.cleanroommc.retrosophisticatedbackpacks.backpack.SortType import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.config.Config +import com.cleanroommc.retrosophisticatedbackpacks.tileentity.BackpackTileEntity +import net.minecraft.item.EnumDyeColor import net.minecraft.network.PacketBuffer import net.minecraftforge.items.wrapper.PlayerMainInvWrapper -class BackpackSH(private val playerInv: PlayerMainInvWrapper, private val wrapper: BackpackWrapper) : SyncHandler() { +class BackpackSH( + private val playerInv: PlayerMainInvWrapper, + private val wrapper: BackpackWrapper, + private val tileEntity: BackpackTileEntity? = null +) : SyncHandler() { companion object { const val UPDATE_SET_SORT_TYPE = 0 const val UPDATE_SORT_INV = 1 const val UPDATE_TRANSFER_TO_BACKPACK_INV = 2 const val UPDATE_TRANSFER_TO_PLAYER_INV = 3 + const val UPDATE_TOGGLE_SETTINGS_CONTEXT = 4 + const val UPDATE_TOGGLE_SHIFT_CLICK_INTO_OPEN_TAB = 5 + const val UPDATE_TOGGLE_KEEP_TAB_OPEN = 6 + const val UPDATE_TOGGLE_KEEP_SEARCH_PHRASE = 7 + const val UPDATE_TOGGLE_ANOTHER_PLAYER_CAN_OPEN = 8 + const val UPDATE_ITEM_DISPLAY_SLOT = 9 + const val UPDATE_ITEM_DISPLAY_ROTATION = 10 + const val UPDATE_ITEM_DISPLAY_COLOR = 11 + const val UPDATE_ITEM_DISPLAY_SIDE = 12 + const val UPDATE_SEARCH_PHRASE = 13 } override fun readOnClient(id: Int, buf: PacketBuffer) {} @@ -23,6 +41,31 @@ class BackpackSH(private val playerInv: PlayerMainInvWrapper, private val wrappe UPDATE_SORT_INV -> sortInventory(buf) UPDATE_TRANSFER_TO_BACKPACK_INV -> transferToBackpack(buf) UPDATE_TRANSFER_TO_PLAYER_INV -> transferToPlayerInventory(buf) + UPDATE_TOGGLE_SETTINGS_CONTEXT -> wrapper.toggleSettingsContext() + UPDATE_TOGGLE_SHIFT_CLICK_INTO_OPEN_TAB -> wrapper.toggleShiftClickIntoOpenTab() + UPDATE_TOGGLE_KEEP_TAB_OPEN -> wrapper.toggleKeepTabOpen() + UPDATE_TOGGLE_KEEP_SEARCH_PHRASE -> wrapper.toggleKeepSearchPhrase() + UPDATE_TOGGLE_ANOTHER_PLAYER_CAN_OPEN -> wrapper.toggleAnotherPlayerCanOpen() + UPDATE_ITEM_DISPLAY_SLOT -> updateItemDisplaySlot(buf) + UPDATE_ITEM_DISPLAY_ROTATION -> updateItemDisplayRotation(buf) + UPDATE_ITEM_DISPLAY_COLOR -> { + if (Config.itemDisplayDisabled) { + return + } + wrapper.itemDisplayColor = buf.readEnumValue(EnumDyeColor::class.java) + syncRenderState() + } + UPDATE_ITEM_DISPLAY_SIDE -> { + if (Config.itemDisplayDisabled) { + return + } + wrapper.itemDisplaySide = buf.readEnumValue(DisplaySide::class.java) + syncRenderState() + } + UPDATE_SEARCH_PHRASE -> { + val phrase = buf.readString(50) + wrapper.searchPhrase = phrase + } else -> {} } } @@ -65,4 +108,30 @@ class BackpackSH(private val playerInv: PlayerMainInvWrapper, private val wrappe BackpackInventoryHelper.transferBackpackToPlayerInventory(wrapper, playerInv, transferMatched) } -} \ No newline at end of file + + private fun updateItemDisplaySlot(buf: PacketBuffer) { + if (Config.itemDisplayDisabled) { + return + } + val slotIndex = buf.readInt() + val selected = buf.readBoolean() + if (selected) { + wrapper.selectItemDisplaySlot(slotIndex) + } else { + wrapper.unselectItemDisplaySlot(slotIndex) + } + syncRenderState() + } + + private fun updateItemDisplayRotation(buf: PacketBuffer) { + if (Config.itemDisplayDisabled) { + return + } + wrapper.rotateItemDisplaySlot(buf.readInt(), buf.readBoolean()) + syncRenderState() + } + + private fun syncRenderState() { + tileEntity?.syncRenderState() + } +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/DelegatedStackHandlerSH.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/DelegatedStackHandlerSH.kt index ab43d58..514d289 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/DelegatedStackHandlerSH.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/DelegatedStackHandlerSH.kt @@ -20,6 +20,10 @@ open class DelegatedStackHandlerSH( ) : SyncHandler() { companion object { const val UPDATE_FILTERABLE = 0 + const val UPDATE_JUKEBOX = 1 + const val UPDATE_TANK = 2 + const val UPDATE_BATTERY = 3 + const val UPDATE_ANVIL = 4 } var delegatedStackHandler: DelegatedItemHandler = DelegatedItemHandler(EmptyHandler::INSTANCE, wrappedSlotAmount) @@ -40,6 +44,28 @@ open class DelegatedStackHandlerSH( setDelegatedStackHandler(wrapper::filterItems) } + + UPDATE_JUKEBOX -> { + val wrapper = stack.getCapability(Capabilities.IJUKEBOX_UPGRADE_CAPABILITY, null) ?: return + + setDelegatedStackHandler(wrapper::discInventory) + } + + UPDATE_TANK -> { + val wrapper = stack.getCapability(Capabilities.ITANK_UPGRADE_CAPABILITY, null) ?: return + + setDelegatedStackHandler(wrapper::getInventory) + } + + UPDATE_BATTERY -> { + val wrapper = stack.getCapability(Capabilities.IBATTERY_UPGRADE_CAPABILITY, null) ?: return + setDelegatedStackHandler(wrapper::getInventory) + } + + UPDATE_ANVIL -> { + val wrapper = stack.getCapability(Capabilities.IANVIL_UPGRADE_CAPABILITY, null) ?: return + setDelegatedStackHandler(wrapper::getInventory) + } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/UpgradeSlotSH.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/UpgradeSlotSH.kt index 295692b..249d3f7 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/UpgradeSlotSH.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/sync/UpgradeSlotSH.kt @@ -6,15 +6,34 @@ import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.AdvancedFeedingUpgradeWrapper import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.CraftingUpgradeWrapper.CraftingDestination import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IAdvancedFilterable +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IAnvilUpgrade import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IBasicFilterable +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IContentsFilterable import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IFilterUpgrade +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.IPumpUpgrade +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.JukeboxUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.RefillUpgradeWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.RepeatMode +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.ToolSwapMode +import com.cleanroommc.retrosophisticatedbackpacks.capability.upgrade.VoidType +import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackContainer +import net.minecraft.entity.player.EntityPlayerMP +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound import net.minecraft.network.PacketBuffer +import net.minecraft.network.play.server.SPacketSetSlot +import net.minecraftforge.fluids.FluidUtil +import net.minecraftforge.items.IItemHandlerModifiable +import net.minecraftforge.items.ItemHandlerHelper /** * Used to synchronize upgrade item's capability, this is only fired from client to reflect client's action to server * side. */ -class UpgradeSlotSH(slot: ModularSlot) : ItemSlotSH(slot) { +class UpgradeSlotSH( + slot: ModularSlot, + private val onClientSlotUpdate: () -> Unit = {} +) : ItemSlotSH(slot) { companion object { const val UPDATE_UPGRADE_TAB_STATE = 6 const val UPDATE_UPGRADE_TOGGLE = 7 @@ -23,6 +42,53 @@ class UpgradeSlotSH(slot: ModularSlot) : ItemSlotSH(slot) { const val UPDATE_ADVANCED_FEEDING = 10 const val UPDATE_FILTER_WAY = 11 const val UPDATE_CRAFTING_DESTINATION = 12 + const val UPDATE_VOID_TYPE = 13 + const val UPDATE_COMPACT_NON_UNCRAFTABLE = 14 + const val UPDATE_REFILL_TARGET_SLOT = 15 + const val UPDATE_VOID_WORK_IN_GUI = 16 + const val UPDATE_COMPACT_WORK_IN_GUI = 17 + const val UPDATE_JUKEBOX_PLAY = 18 + const val UPDATE_JUKEBOX_STOP = 19 + const val UPDATE_JUKEBOX_NEXT = 20 + const val UPDATE_JUKEBOX_PREVIOUS = 21 + const val UPDATE_JUKEBOX_SHUFFLE = 22 + const val UPDATE_JUKEBOX_REPEAT_MODE = 23 + const val UPDATE_TOOL_SWAPPER_SWAP_WEAPON = 24 + const val UPDATE_TOOL_SWAPPER_MODE = 25 + const val UPDATE_TANK_CLICK = 26 + const val UPDATE_PUMP_INPUT = 27 + const val UPDATE_PUMP_HAND = 28 + const val UPDATE_PUMP_WORLD = 29 + const val UPDATE_PUMP_FLUID_HANDLERS = 30 + const val UPDATE_PUMP_FLUID_FILTER = 31 + const val UPDATE_ANVIL_ITEM_NAME = 32 + const val UPDATE_ANVIL_SHIFT_CLICK = 33 + const val UPDATE_ANVIL_TAKE_RESULT = 34 + const val UPDATE_REOPEN_BACKPACK = 35 + const val UPDATE_MAGNET_PICKUP_ITEMS = 36 + const val UPDATE_CONTENTS_FILTERABLE = 37 + } + + private var lastCapabilityNbt = NBTTagCompound() + + override fun onSlotUpdate(stack: ItemStack, onlyAmountChanged: Boolean, client: Boolean, init: Boolean) { + if (client && !onlyAmountChanged) { + (slot.itemHandler as? IItemHandlerModifiable)?.setStackInSlot(slot.slotIndex, stack) + } + super.onSlotUpdate(stack, onlyAmountChanged, client, init) + if (client) { + onClientSlotUpdate() + } + } + + override fun detectAndSendChanges(init: Boolean) { + super.detectAndSendChanges(init) + val wrapper = slot.stack.getCapability(Capabilities.UPGRADE_CAPABILITY, null) ?: return + val capabilityNbt = wrapper.serializeNBT() + if (init || capabilityNbt != lastCapabilityNbt) { + lastCapabilityNbt = capabilityNbt.copy() + forceSyncItem() + } } override fun readOnServer(id: Int, buf: PacketBuffer) { @@ -36,9 +102,40 @@ class UpgradeSlotSH(slot: ModularSlot) : ItemSlotSH(slot) { UPDATE_ADVANCED_FEEDING -> updateAdvanceFeedingUpgrade(buf) UPDATE_FILTER_WAY -> updateFilterUpgrade(buf) UPDATE_CRAFTING_DESTINATION -> updateCraftingDestination(buf) + UPDATE_VOID_TYPE -> updateVoidType(buf) + UPDATE_COMPACT_NON_UNCRAFTABLE -> updateCompactNonUncraftable() + UPDATE_REFILL_TARGET_SLOT -> updateRefillTargetSlot(buf) + UPDATE_VOID_WORK_IN_GUI -> updateVoidWorkInGui() + UPDATE_COMPACT_WORK_IN_GUI -> updateCompactWorkInGui() + UPDATE_JUKEBOX_PLAY -> updateJukeboxPlay() + UPDATE_JUKEBOX_STOP -> updateJukeboxStop() + UPDATE_JUKEBOX_NEXT -> updateJukeboxNext() + UPDATE_JUKEBOX_PREVIOUS -> updateJukeboxPrevious() + UPDATE_JUKEBOX_SHUFFLE -> updateJukeboxShuffle() + UPDATE_JUKEBOX_REPEAT_MODE -> updateJukeboxRepeatMode(buf) + UPDATE_TOOL_SWAPPER_SWAP_WEAPON -> updateToolSwapperSwapWeapon() + UPDATE_TOOL_SWAPPER_MODE -> updateToolSwapperMode(buf) + UPDATE_TANK_CLICK -> updateTankClick() + UPDATE_PUMP_INPUT -> updatePumpInput(buf) + UPDATE_PUMP_HAND -> updatePumpHand() + UPDATE_PUMP_WORLD -> updatePumpWorld() + UPDATE_PUMP_FLUID_HANDLERS -> updatePumpFluidHandlers() + UPDATE_PUMP_FLUID_FILTER -> updatePumpFluidFilter(buf) + UPDATE_ANVIL_ITEM_NAME -> updateAnvilItemName(buf) + UPDATE_ANVIL_SHIFT_CLICK -> updateAnvilShiftClick() + UPDATE_ANVIL_TAKE_RESULT -> updateAnvilTakeResult() + UPDATE_REOPEN_BACKPACK -> reopenBackpackGui() + UPDATE_MAGNET_PICKUP_ITEMS -> updateMagnetPickupItems() + UPDATE_CONTENTS_FILTERABLE -> updateContentsFilterable(buf) } } + private fun reopenBackpackGui() { + val player = syncManager.player as? EntityPlayerMP ?: return + val container = player.openContainer as? BackpackContainer ?: return + container.reopenBackpackGui(player) + } + private fun updateTabState(buf: PacketBuffer) { val wrapper = slot.stack.getCapability(Capabilities.UPGRADE_CAPABILITY, null) ?: return wrapper.isTabOpened = buf.readBoolean() @@ -92,4 +189,153 @@ class UpgradeSlotSH(slot: ModularSlot) : ItemSlotSH(slot) { wrapper.craftingDestination = buf.readEnumValue(CraftingDestination::class.java) } + + private fun updateVoidType(buf: PacketBuffer) { + val voidType = buf.readEnumValue(VoidType::class.java) + slot.stack.getCapability(Capabilities.VOID_UPGRADE_CAPABILITY, null)?.voidType = voidType + slot.stack.getCapability(Capabilities.ADVANCED_VOID_UPGRADE_CAPABILITY, null)?.voidType = voidType + } + + private fun updateCompactNonUncraftable() { + slot.stack.getCapability(Capabilities.COMPACTING_UPGRADE_CAPABILITY, null)?.toggleCompactNonUncraftable() + slot.stack.getCapability(Capabilities.ADVANCED_COMPACTING_UPGRADE_CAPABILITY, null)?.toggleCompactNonUncraftable() + } + + private fun updateVoidWorkInGui() { + slot.stack.getCapability(Capabilities.VOID_UPGRADE_CAPABILITY, null)?.toggleWorkInGui() + slot.stack.getCapability(Capabilities.ADVANCED_VOID_UPGRADE_CAPABILITY, null)?.toggleWorkInGui() + } + + private fun updateCompactWorkInGui() { + slot.stack.getCapability(Capabilities.COMPACTING_UPGRADE_CAPABILITY, null)?.toggleWorkInGui() + slot.stack.getCapability(Capabilities.ADVANCED_COMPACTING_UPGRADE_CAPABILITY, null)?.toggleWorkInGui() + } + + private fun updateMagnetPickupItems() { + slot.stack.getCapability(Capabilities.MAGNET_UPGRADE_CAPABILITY, null)?.togglePickupItems() + slot.stack.getCapability(Capabilities.ADVANCED_MAGNET_UPGRADE_CAPABILITY, null)?.togglePickupItems() + } + + private fun updateContentsFilterable(buf: PacketBuffer) { + val filterType = buf.readEnumValue(IContentsFilterable.ContentsFilterType::class.java) + (slot.stack.getCapability(Capabilities.UPGRADE_CAPABILITY, null) as? IContentsFilterable)?.contentsFilterType = filterType + } + + private fun updateRefillTargetSlot(buf: PacketBuffer) { + val filterSlot = buf.readInt() + val targetSlot = buf.readEnumValue(RefillUpgradeWrapper.TargetSlot::class.java) + slot.stack.getCapability(Capabilities.ADVANCED_REFILL_UPGRADE_CAPABILITY, null)?.setTargetSlot(filterSlot, targetSlot) + } + + private fun jukebox(): JukeboxUpgradeWrapper? = + slot.stack.getCapability(Capabilities.JUKEBOX_UPGRADE_CAPABILITY, null) + ?: slot.stack.getCapability(Capabilities.ADVANCED_JUKEBOX_UPGRADE_CAPABILITY, null) + + private fun updateJukeboxPlay() { + jukebox()?.requestPlay() + } + + private fun updateJukeboxStop() { + jukebox()?.requestStop() + } + + private fun updateJukeboxNext() { + jukebox()?.next() + } + + private fun updateJukeboxPrevious() { + jukebox()?.previous() + } + + private fun updateJukeboxShuffle() { + jukebox()?.toggleShuffle() + } + + private fun updateJukeboxRepeatMode(buf: PacketBuffer) { + jukebox()?.repeatMode = buf.readEnumValue(RepeatMode::class.java) + } + + private fun updateToolSwapperSwapWeapon() { + val wrapper = slot.stack.getCapability(Capabilities.ADVANCED_TOOL_SWAPPER_UPGRADE_CAPABILITY, null) ?: return + wrapper.shouldSwapWeapon = !wrapper.shouldSwapWeapon + } + + private fun updateToolSwapperMode(buf: PacketBuffer) { + val wrapper = slot.stack.getCapability(Capabilities.ADVANCED_TOOL_SWAPPER_UPGRADE_CAPABILITY, null) ?: return + wrapper.toolSwapMode = buf.readEnumValue(ToolSwapMode::class.java) + } + + private fun updateTankClick() { + val wrapper = slot.stack.getCapability(Capabilities.TANK_UPGRADE_CAPABILITY, null) ?: return + val player = slot.getSyncHandler().syncManager.player + val backpackWrapper = (player.openContainer as? BackpackContainer)?.backpackWrapper ?: return + if (player.inventory.itemStack.count <= 1) { + wrapper.interactWithCursorStack(player, backpackWrapper) + } + } + + private fun pump(): IPumpUpgrade? = + slot.stack.getCapability(Capabilities.IPUMP_UPGRADE_CAPABILITY, null) + + private fun updatePumpInput(buf: PacketBuffer) { + pump()?.isInput = buf.readBoolean() + } + + private fun updatePumpHand() { + pump()?.let { it.interactWithHand = !it.interactWithHand } + } + + private fun updatePumpWorld() { + pump()?.let { it.interactWithWorld = !it.interactWithWorld } + } + + private fun updatePumpFluidHandlers() { + pump()?.let { it.interactWithFluidHandlers = !it.interactWithFluidHandlers } + } + + private fun updatePumpFluidFilter(buf: PacketBuffer) { + val wrapper = pump() ?: return + val slotIndex = buf.readInt() + val carried = slot.getSyncHandler().syncManager.player.inventory.itemStack + val fluid = if (carried.isEmpty) null else FluidUtil.getFluidContained(carried.copy()) + wrapper.setFluidFilter(slotIndex, fluid) + } + + private fun anvil(): IAnvilUpgrade? = + slot.stack.getCapability(Capabilities.IANVIL_UPGRADE_CAPABILITY, null) + + private fun updateAnvilItemName(buf: PacketBuffer) { + anvil()?.itemName = buf.readString(64) + } + + private fun updateAnvilShiftClick() { + anvil()?.let { it.shouldShiftClickIntoStorage = !it.shouldShiftClickIntoStorage } + } + + private fun updateAnvilTakeResult() { + val wrapper = anvil() ?: return + val player = slot.getSyncHandler().syncManager.player as? EntityPlayerMP ?: return + val preview = wrapper.updateRepairOutput(player, player.world) + val carried = player.inventory.itemStack + if (preview.isEmpty || !wrapper.canTakeResult(player) || !canPlaceAnvilResultOnCursor(carried, preview)) { + return + } + + val taken = wrapper.takeResult(player, player.world) + if (taken.isEmpty) { + return + } + if (carried.isEmpty) { + player.inventory.itemStack = taken + } else { + carried.grow(taken.count) + player.inventory.itemStack = carried + } + player.connection.sendPacket(SPacketSetSlot(-1, -1, player.inventory.itemStack)) + player.openContainer.detectAndSendChanges() + } + + private fun canPlaceAnvilResultOnCursor(carried: ItemStack, result: ItemStack): Boolean = + carried.isEmpty || ItemHandlerHelper.canItemStacksStack(carried, result) && + carried.count + result.count <= carried.maxStackSize } diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/tileentity/BackpackTileEntity.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/tileentity/BackpackTileEntity.kt index 40187d6..e9a9ee9 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/tileentity/BackpackTileEntity.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/tileentity/BackpackTileEntity.kt @@ -7,10 +7,14 @@ import com.cleanroommc.modularui.screen.ModularPanel import com.cleanroommc.modularui.screen.UISettings import com.cleanroommc.modularui.value.sync.PanelSyncManager import com.cleanroommc.retrosophisticatedbackpacks.RetroSophisticatedBackpacks +import com.cleanroommc.retrosophisticatedbackpacks.block.BackpackBlock import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackWrapper +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackFluidHandler +import com.cleanroommc.retrosophisticatedbackpacks.capability.BackpackEnergyStorage import com.cleanroommc.retrosophisticatedbackpacks.capability.Capabilities import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackContainer import com.cleanroommc.retrosophisticatedbackpacks.common.gui.BackpackGuiHolder +import com.cleanroommc.retrosophisticatedbackpacks.config.Config import com.cleanroommc.retrosophisticatedbackpacks.util.Utils.asTranslationKey import net.minecraft.block.state.IBlockState import net.minecraft.entity.player.EntityPlayer @@ -20,6 +24,7 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.network.NetworkManager import net.minecraft.network.play.server.SPacketUpdateTileEntity +import net.minecraft.util.ITickable import net.minecraft.tileentity.TileEntityLockableLoot import net.minecraft.util.EnumFacing import net.minecraft.util.NonNullList @@ -30,11 +35,14 @@ import net.minecraft.util.text.TextComponentTranslation import net.minecraft.world.World import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.items.CapabilityItemHandler +import net.minecraftforge.fluids.capability.CapabilityFluidHandler +import net.minecraftforge.energy.CapabilityEnergy import net.minecraftforge.items.IItemHandler class BackpackTileEntity(val wrapper: BackpackWrapper = BackpackWrapper()) : TileEntityLockableLoot(), IItemHandler, + ITickable, IGuiHolder { companion object { private const val BACKPACK_INVENTORY_TAG = "backpackInventory" @@ -60,14 +68,28 @@ class BackpackTileEntity(val wrapper: BackpackWrapper = BackpackWrapper()) : @Suppress("UNCHECKED_CAST") override fun getCapability(capability: Capability, facing: EnumFacing?): T? = - when (capability) { - Capabilities.BACKPACK_CAPABILITY -> wrapper as T - CapabilityItemHandler.ITEM_HANDLER_CAPABILITY -> this as T + when { + capability == Capabilities.BACKPACK_CAPABILITY -> wrapper as T + isExternalConnectionBlocked(facing) -> null + capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY -> this as T + capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && wrapper.hasTankUpgrade() -> BackpackFluidHandler(wrapper) as T + capability == CapabilityEnergy.ENERGY && wrapper.hasBatteryUpgrade() -> BackpackEnergyStorage(wrapper) as T else -> null } override fun hasCapability(capability: Capability<*>, facing: EnumFacing?): Boolean = - wrapper.hasCapability(capability, facing) + capability == Capabilities.BACKPACK_CAPABILITY || + !isExternalConnectionBlocked(facing) && + (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY || + capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && wrapper.hasTankUpgrade() || + capability == CapabilityEnergy.ENERGY && wrapper.hasBatteryUpgrade()) + + private fun isExternalConnectionBlocked(facing: EnumFacing?): Boolean { + if (facing == null || !hasWorld()) { + return false + } + return Config.isConnectionBlockDisallowed(world.getBlockState(pos.offset(facing)).block) + } override fun writeToNBT(compound: NBTTagCompound): NBTTagCompound { compound.setTag(BACKPACK_INVENTORY_TAG, wrapper.serializeNBT()) @@ -89,20 +111,40 @@ class BackpackTileEntity(val wrapper: BackpackWrapper = BackpackWrapper()) : uiSettings: UISettings ): ModularPanel { val backpackInv = getCapability(Capabilities.BACKPACK_CAPABILITY, null)!! - val containerSupplier = { BackpackContainer(backpackInv, null) } + val containerSupplier = { BackpackContainer(backpackInv, null, tilePos = data.blockPos) } uiSettings.customContainer(containerSupplier) uiSettings.canInteractWith(::isUsableByPlayer) val holder: BackpackGuiHolder.TileEntityGuiHolder = BackpackGuiHolder.TileEntityGuiHolder(backpackInv) return holder.buildUI(data, syncManager, uiSettings) } + fun syncRenderState() { + if (!hasWorld()) { + return + } + markDirty() + val state = world.getBlockState(pos) + world.notifyBlockUpdate(pos, state, state, 3) + } + + override fun hasCustomName(): Boolean = + wrapper.customName != null + override fun getName(): String = - if (hasCustomName()) customName else "container.backpack".asTranslationKey() + if (hasCustomName()) wrapper.customName!! else "container.backpack".asTranslationKey() override fun getDisplayName(): ITextComponent = - if (hasCustomName()) TextComponentString(customName) + if (hasCustomName()) TextComponentString(wrapper.customName!!) else TextComponentTranslation("container.backpack".asTranslationKey()) + override fun update() { + if (!world.isRemote) { + wrapper.tickUpgrades(null, world, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, pos) + syncTankState() + syncBatteryState() + } + } + override fun getSlots(): Int = wrapper.slots @@ -125,9 +167,18 @@ class BackpackTileEntity(val wrapper: BackpackWrapper = BackpackWrapper()) : slot: Int, stack: ItemStack, simulate: Boolean - ): ItemStack = - if (wrapper.canInsert(stack)) wrapper.backpackItemStackHandler.prioritizedInsertion(slot, stack, simulate) - else stack + ): ItemStack { + if (!wrapper.canInsert(stack)) { + return stack + } + + val stack = wrapper.onBeforeInsert(stack) + if (stack.isEmpty) { + return ItemStack.EMPTY + } + val remaining = wrapper.backpackItemStackHandler.prioritizedInsertion(slot, stack, simulate) + return wrapper.onInsertRemainder(remaining) + } override fun extractItem( slot: Int, @@ -140,6 +191,32 @@ class BackpackTileEntity(val wrapper: BackpackWrapper = BackpackWrapper()) : override fun getSlotLimit(slot: Int): Int = wrapper.getSlotLimit(slot) + private fun syncTankState() { + val state = world.getBlockState(pos) + if (state.block !is BackpackBlock) { + return + } + val (leftTank, rightTank) = wrapper.tankRenderSides() + if (state.getValue(BackpackBlock.LEFT_TANK) != leftTank || state.getValue(BackpackBlock.RIGHT_TANK) != rightTank) { + world.setBlockState( + pos, + state.withProperty(BackpackBlock.LEFT_TANK, leftTank).withProperty(BackpackBlock.RIGHT_TANK, rightTank), + 3 + ) + } + } + + private fun syncBatteryState() { + val state = world.getBlockState(pos) + if (state.block !is BackpackBlock) { + return + } + val battery = wrapper.hasBatteryUpgrade() + if (state.getValue(BackpackBlock.BATTERY) != battery) { + world.setBlockState(pos, state.withProperty(BackpackBlock.BATTERY, battery), 3) + } + } + override fun createContainer( playerInventory: InventoryPlayer, playerIn: EntityPlayer diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/BackpackItemStackHelper.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/BackpackItemStackHelper.kt index 2234cec..63d1a60 100644 --- a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/BackpackItemStackHelper.kt +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/BackpackItemStackHelper.kt @@ -53,7 +53,7 @@ object BackpackItemStackHelper { /** * Returns the hunger value of the food in the slot, if it is a food item - * + * * @return the hunger value of the food in the slot, or null if the slot is not a food item */ fun getHungerFromSlot(handler: IItemHandler, slot: Int, predicate: (ItemStack) -> Boolean): Int? { diff --git a/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/DyeColorUtils.kt b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/DyeColorUtils.kt new file mode 100644 index 0000000..c2fb329 --- /dev/null +++ b/src/main/kotlin/com/cleanroommc/retrosophisticatedbackpacks/util/DyeColorUtils.kt @@ -0,0 +1,17 @@ +package com.cleanroommc.retrosophisticatedbackpacks.util + +import com.cleanroommc.retrosophisticatedbackpacks.mixin.EnumDyeColorAccessor +import net.minecraft.item.EnumDyeColor + +object DyeColorUtils { + private val fallbackColors = intArrayOf( + 16383998, 16351261, 13061821, 3847130, + 16701501, 8439583, 15961002, 4673362, + 10329495, 1481884, 8991416, 3949738, + 8606770, 6192150, 11546150, 1908001 + ) + + fun colorValue(color: EnumDyeColor): Int = + ((color as Any) as? EnumDyeColorAccessor)?.`rsb$getColorValue`() + ?: fallbackColors.getOrElse(color.ordinal) { 0xFF0000 } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/en_us.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/en_us.lang index 02c55e8..620433a 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/en_us.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/en_us.lang @@ -18,7 +18,7 @@ item.retro_sophisticated_backpacks.inception_upgrade.name=Inception Upgrade item.retro_sophisticated_backpacks.pickup_upgrade.name=Pickup Upgrade item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=Advanced Pickup Upgrade item.retro_sophisticated_backpacks.feeding_upgrade.name=Feeding Upgrade -item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=Advanced Feeding Upgrade +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=Advanced Feeding upgrade item.retro_sophisticated_backpacks.deposit_upgrade.name=Deposit Upgrade item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=Advanced Deposit Upgrade item.retro_sophisticated_backpacks.restock_upgrade.name=Restock Upgrade @@ -31,77 +31,110 @@ itemGroup.retro_sophisticated_backpacks.creative_tab=Retro Sophisticated Backpac # Keybinds retro_sophisticated_backpacks.key.open_backpack.desc=Opens Backpack in Inventory +retro_sophisticated_backpacks.key.tool_swap.desc=Swap Tool Based on Current Block/Entity retro_sophisticated_backpacks.key.category=Retro Sophisticated Backpacks # Tooltips retro_sophisticated_backpacks.tooltip.backpack.inventory_size=Inventory size: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=Upgrade slots: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> Stack multiplier: %d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=Stack Size Multiplier: %s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=Empty tank +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=Fluids +retro_sophisticated_backpacks.tooltip.backpack.energy_title=Energy +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=Upgrades +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=Inventory +retro_sophisticated_backpacks.tooltip.backpack.empty=No Upgrades or Inventory Contents +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=Press <%s> to View Contents +retro_sophisticated_backpacks.tooltip.backpack.shift=Left Shift -retro_sophisticated_backpacks.tooltip.upgrade_base=It does nothing! Feel free to put it in your upgrade slots! -retro_sophisticated_backpacks.tooltip.stack_upgrade=Multiplies stack size by %d -retro_sophisticated_backpacks.tooltip.exponential_stack_upgrade=Stack multipliers will stack by multiplying instead of adding -retro_sophisticated_backpacks.tooltip.crafting_upgrade=Adds additional crafting grid to side of backpack -retro_sophisticated_backpacks.tooltip.inception_upgrade=Allows backpacks to be stored inside backpack -retro_sophisticated_backpacks.tooltip.pickup_upgrade=Allows backpacks to pickup items -retro_sophisticated_backpacks.tooltip.advanced_pickup_upgrade=Allows backpack to pickup items with more configurations -retro_sophisticated_backpacks.tooltip.feeding_upgrade=Allows backpacks to automatically feed player -retro_sophisticated_backpacks.tooltip.advanced_feeding_upgrade=Allows backpack to automatically feed player with more configurations -retro_sophisticated_backpacks.tooltip.deposit_upgrade=Deposits items from backpack into sneak-right-clicked inventory -retro_sophisticated_backpacks.tooltip.advanced_deposit_upgrade=Deposits items from backpack into sneak-right-clicked inventory with more configurations -retro_sophisticated_backpacks.tooltip.restock_upgrade=Restocks items from sneak-right-clicked inventory to backpack -retro_sophisticated_backpacks.tooltip.advanced_restock_upgrade=Restocks items from sneak-right-clicked inventory to backpack with more configurations -retro_sophisticated_backpacks.tooltip.filter_upgrade=Filters inserted items when placing in world -retro_sophisticated_backpacks.tooltip.advanced_filter_upgrade=Filters inserted items when placing in world with more configurations -retro_sophisticated_backpacks.tooltip.shift_to_reveal= # Gui Elements retro_sophisticated_backpacks.container.backpack=Backpack -retro_sophisticated_backpacks.gui.pickup_settings=Pickup Settings -retro_sophisticated_backpacks.gui.advanced_pickup_settings=Adv. Pickup Settings - -retro_sophisticated_backpacks.gui.feeding_settings=Feeding Settings -retro_sophisticated_backpacks.gui.advanced_feeding_settings=Adv. Feeding Settings - -retro_sophisticated_backpacks.gui.deposit_settings=Deposit Settings -retro_sophisticated_backpacks.gui.advanced_deposit_settings=Adv. Deposit Settings - -retro_sophisticated_backpacks.gui.restock_settings=Restock Settings -retro_sophisticated_backpacks.gui.advanced_restock_settings=Adv. Restock Settings - -retro_sophisticated_backpacks.gui.filter_settings=Filter Settings -retro_sophisticated_backpacks.gui.advanced_filter_settings=Adv. Filter Settings - -retro_sophisticated_backpacks.gui.crafting_settings=Crafting - -retro_sophisticated_backpacks.gui.memory_settings=Memory Settings -retro_sophisticated_backpacks.gui.sorting_settings=Sorting Settings +retro_sophisticated_backpacks.gui.pickup_settings=Pickup +retro_sophisticated_backpacks.gui.advanced_pickup_settings=Adv. Pickup + +retro_sophisticated_backpacks.gui.feeding_settings=Feeding +retro_sophisticated_backpacks.gui.advanced_feeding_settings=Adv. Feeding + +retro_sophisticated_backpacks.gui.deposit_settings=Deposit +retro_sophisticated_backpacks.gui.advanced_deposit_settings=Adv. Deposit + +retro_sophisticated_backpacks.gui.restock_settings=Restock +retro_sophisticated_backpacks.gui.advanced_restock_settings=Adv. Restock + +retro_sophisticated_backpacks.gui.filter_settings=Filter +retro_sophisticated_backpacks.gui.advanced_filter_settings=Adv. Filter + +retro_sophisticated_backpacks.gui.crafting_settings=Craft + +retro_sophisticated_backpacks.gui.memory_settings=Memory +retro_sophisticated_backpacks.gui.sorting_settings=No Sort +retro_sophisticated_backpacks.gui.backpack_settings=Backpack Settings +retro_sophisticated_backpacks.gui.item_display_settings=Item Disp. +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=Backpack Settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=Item Display Settings +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=Allows selecting a slot that will be used to show its item on top of storage +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=Allows selecting a slot that will be used to show its item on top of storage\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.search=Click to search +retro_sophisticated_backpacks.gui.search_detail=@ prefix to search by mod name +retro_sophisticated_backpacks.gui.settings_button.context_player=Player +retro_sophisticated_backpacks.gui.settings_button.context_backpack=Backpack +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=Player level settings +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=Apply to all backpacks/storages unless overriden +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=This Backpack's settings +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=Inherited from player or overriden for this backpack +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Shift Click Into Open Tab First +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Shift Click Into Inventory First +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=Keep Tab Open: ON +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=Keep Tab Open: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Keep Search Phrase: ON +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Keep Search Phrase: OFF +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=Another player can open +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=Another player can NOT open +retro_sophisticated_backpacks.gui.settings_button.rotate=Rotate +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=Clockwise = Left Click\nCounter-Clockwise = Right Click +retro_sophisticated_backpacks.gui.settings_button.item_display_color=Toggle Color +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=Next = Left Click\nPrevious = Right Click +retro_sophisticated_backpacks.gui.settings_button.display_side_front=Show on Front +retro_sophisticated_backpacks.gui.settings_button.display_side_left=Show on Left side +retro_sophisticated_backpacks.gui.settings_button.display_side_right=Show on Right side +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.memory_settings.tooltip=Slot Memory Settings +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=Allows selecting slots that remember their contents and only allow matching stacks in them\nOpen tab to modify slot settings +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=Allows selecting slots that remember their contents and only allow matching stacks in them\nSelect all / Unselect all = buttons\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=No Sort Slot Settings +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=Allows selecting slots that are ignored by sorting\nOpen tab to modify slot settings +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=Allows selecting slots that are ignored by sorting\nSelect all / Unselect all = buttons\nSelect slot = left click/drag\nUnselect slot = right click/drag # Gui Tooltips retro_sophisticated_backpacks.gui.stack_size_extra=Count: %s / %s retro_sophisticated_backpacks.gui.not_in_effect=Not in effect -retro_sophisticated_backpacks.gui.whitelist=Whitelist -retro_sophisticated_backpacks.gui.blacklist=Blacklist +retro_sophisticated_backpacks.gui.whitelist=Allow +retro_sophisticated_backpacks.gui.blacklist=Block -retro_sophisticated_backpacks.gui.match_item=By Item -retro_sophisticated_backpacks.gui.match_mod_id=By Mod ID +retro_sophisticated_backpacks.gui.match_item=Match Item +retro_sophisticated_backpacks.gui.match_mod_id=Match Mod retro_sophisticated_backpacks.gui.match_ore_dict=By Ore Dictionary retro_sophisticated_backpacks.gui.match_durability=Match Durability @@ -125,32 +158,211 @@ retro_sophisticated_backpacks.gui.ignore_health=Do not consider health\nIgnores retro_sophisticated_backpacks.gui.consider_health=Feed player immediately when hurt\nIgnores hunger setting when player is not at max health retro_sophisticated_backpacks.gui.input_output=Input & Output -retro_sophisticated_backpacks.gui.input=Input Only -retro_sophisticated_backpacks.gui.output=Output Only +retro_sophisticated_backpacks.gui.input=Input +retro_sophisticated_backpacks.gui.output=Output -retro_sophisticated_backpacks.gui.sort_inventory=Sort Backpack Inventory -retro_sophisticated_backpacks.gui.sort_by_name=Sort By Name -retro_sophisticated_backpacks.gui.sort_by_mod_id=Sort By Mod ID -retro_sophisticated_backpacks.gui.sort_by_count=Sort By Count -retro_sophisticated_backpacks.gui.sort_by_ore_dict=Sort By Ore Dictionary +retro_sophisticated_backpacks.gui.sort_inventory=Sort Inventory +retro_sophisticated_backpacks.gui.sort_by_name=By Name +retro_sophisticated_backpacks.gui.sort_by_mod_id=By Mod +retro_sophisticated_backpacks.gui.sort_by_count=By Count +retro_sophisticated_backpacks.gui.sort_by_ore_dict=By Tags retro_sophisticated_backpacks.gui.craft_into_backpack=Shift Click Result Into Backpack retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift Click Result Into Player's Inventory -retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transfer All Items to Player Inventory -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transfer Matched Items to Player Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transfer to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transfer Matching to Inventory retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift To Transfer All -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transfer All Items to Backpack Inventory -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transfer Matched Items to Backpack Inventory +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transfer to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transfer Matching to Storage retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift To Transfer All retro_sophisticated_backpacks.gui.settings=Settings +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=Back To Backpack retro_sophisticated_backpacks.gui.configuration_tab=Configuration Tab retro_sophisticated_backpacks.gui.memorized_slot=Memorized Slot -retro_sophisticated_backpacks.gui.memorize_all=Memorize All Slots -retro_sophisticated_backpacks.gui.unmemorize_all=Forget All Slots +retro_sophisticated_backpacks.gui.memorize_all=Select All Slots +retro_sophisticated_backpacks.gui.unmemorize_all=Unselect All Slots retro_sophisticated_backpacks.gui.no_sorting_slot=No Sorting Slot -retro_sophisticated_backpacks.gui.lock_all_sort=Select All Slots For No Sorting -retro_sophisticated_backpacks.gui.unlock_all_sort=Select All Slots For Sorting +retro_sophisticated_backpacks.gui.lock_all_sort=Select All Slots +retro_sophisticated_backpacks.gui.unlock_all_sort=Unselect All Slots + +item.retro_sophisticated_backpacks.magnet_upgrade.name=Magnet Upgrade +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=Advanced Magnet Upgrade +item.retro_sophisticated_backpacks.void_upgrade.name=Void Upgrade +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=Advanced Void Upgrade +item.retro_sophisticated_backpacks.refill_upgrade.name=Refill Upgrade +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=Advanced Refill Upgrade +item.retro_sophisticated_backpacks.compacting_upgrade.name=Compacting Upgrade +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=Advanced Compacting Upgrade +retro_sophisticated_backpacks.gui.magnet_settings=Magnet +retro_sophisticated_backpacks.gui.advanced_magnet_settings=Adv. Magnet +retro_sophisticated_backpacks.gui.void_settings=Void +retro_sophisticated_backpacks.gui.advanced_void_settings=Adv. Void +retro_sophisticated_backpacks.gui.refill_settings=Refill +retro_sophisticated_backpacks.gui.advanced_refill_settings=Adv. Refill +retro_sophisticated_backpacks.gui.compacting_settings=Compa... +retro_sophisticated_backpacks.gui.advanced_compacting_settings=Adv. Compacting +retro_sophisticated_backpacks.gui.void_always=Void Always +retro_sophisticated_backpacks.gui.void_slot_overflow=Void Slot Overflow +retro_sophisticated_backpacks.gui.void_storage_overflow=Void Storage Overflow +retro_sophisticated_backpacks.gui.refill_target_any=Any slot +retro_sophisticated_backpacks.gui.refill_target_main_hand=Main hand +retro_sophisticated_backpacks.gui.refill_target_off_hand=Off hand +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=Hotbar slot 1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=Hotbar slot 2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=Hotbar slot 3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=Hotbar slot 4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=Hotbar slot 5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=Hotbar slot 6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=Hotbar slot 7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=Hotbar slot 8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=Hotbar slot 9 +retro_sophisticated_backpacks.gui.allow=Allow +retro_sophisticated_backpacks.gui.block=Block +retro_sophisticated_backpacks.gui.match_backpack_contents=Match Backpack Contents +retro_sophisticated_backpacks.gui.pickup_items=Pickup Items +retro_sophisticated_backpacks.gui.do_not_pickup_items=Do Not Pickup Items +retro_sophisticated_backpacks.gui.compact_only_uncraftable=Compact Only Uncraftable +retro_sophisticated_backpacks.gui.compact_anything=Compact Anything +retro_sophisticated_backpacks.gui.only_automatic=Only works with other upgrades/automation +retro_sophisticated_backpacks.gui.works_in_gui=Works in GUI as well +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=Allows single slot to be filled with the item\nand voids anything that overflows +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Allows whole storage to be filled with the item\n and voids anything that overflows +retro_sophisticated_backpacks.gui.refill_target_tooltip=Refill %s +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=Scroll to change target slot +item.retro_sophisticated_backpacks.everlasting_upgrade.name=Everlasting Upgrade +item.retro_sophisticated_backpacks.jukebox_upgrade.name=Jukebox Upgrade +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=Advanced Jukebox Upgrade +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=Tool Swapper Upgrade +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=Advanced Tool Swapper Upgrade +item.retro_sophisticated_backpacks.tank_upgrade.name=Tank Upgrade +item.retro_sophisticated_backpacks.pump_upgrade.name=Pump Upgrade +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=Advanced Pump Upgrade +item.retro_sophisticated_backpacks.battery_upgrade.name=Battery Upgrade +item.retro_sophisticated_backpacks.anvil_upgrade.name=Anvil Upgrade +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=Mob Catcher Upgrade +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.everlasting_settings=Everlasting +retro_sophisticated_backpacks.gui.jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.tool_swapper_settings=Tool Swap +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=Tool Swap +retro_sophisticated_backpacks.gui.tank_settings=Tank +retro_sophisticated_backpacks.gui.pump_settings=Pump +retro_sophisticated_backpacks.gui.advanced_pump_settings=Adv. Pump +retro_sophisticated_backpacks.gui.battery_settings=Batt. +retro_sophisticated_backpacks.gui.anvil_settings=Anvil +retro_sophisticated_backpacks.gui.pump_input=Input +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output=Output +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=Do Not Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world=Interact With World +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world=Do Not Interact With World +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand=Interact With\nFluid Container in Hand +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand=Do Not Interact With\nFluid Container in Hand +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it +retro_sophisticated_backpacks.gui.anvil_no_result=No Result +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift-click result into storage +retro_sophisticated_backpacks.gui.jukebox_play=Play +retro_sophisticated_backpacks.gui.jukebox_stop=Stop +retro_sophisticated_backpacks.gui.jukebox_previous=Previous +retro_sophisticated_backpacks.gui.jukebox_next=Next +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Shuffle Disabled +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Shuffle Enabled +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Repeat All +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Repeat One +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Repeat Disabled +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled.detail=Keeps the held item when attacking entities +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled.detail=Swaps to the best weapon when attacking entities +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_any.detail=Swaps an appropriate tool into hand when holding a weapon or another tool +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap For Other Tools +retro_sophisticated_backpacks.gui.tool_swapper_only_tools.detail=Swaps only when holding another tool, not when holding a weapon +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap.detail=Tools are never swapped into hand +retro_sophisticated_backpacks.gui.status.unable_to_swap_tool_for_backpack=Unable to swap tool with backpack in hand. Switch to a different item or empty hand. +retro_sophisticated_backpacks.gui.status.no_tool_found_for_block=No tool found that works on block +retro_sophisticated_backpacks.gui.status.no_tool_found_for_entity=No tool found that works on entity +retro_sophisticated_backpacks.gui.status.no_tool_swap_upgrade_present=No upgrade present that can swap tools on key press +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=Click to release +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=Release captured mobs before removing Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=Captured mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=Only one Mob Catcher Upgrade is allowed per backpack +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=Captured %s +retro_sophisticated_backpacks.gui.status.mob_catcher_released=Released %s +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=Backpack has no Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=That mob cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=Players cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=Bosses cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=Mobs with passengers or vehicles cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=That mob is blocked from capture +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=Only the owner can capture that mob +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=Mobs with inventories cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=Hostile mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=Mob needs %s slots, more than this upgrade allows (%s) +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=No empty %sx%s slot area available +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=Could not release mob there +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=No valid release space there + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=Compacts items into their compressed variants\nBoth 2x2 and 3x3 recipes with more filtering options +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=Deposits items from backpack into sneak right clicked inventory\nHas more filtering options +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=Feeds player with food from backpack's inventory\nMore options for when food gets fed to player +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=Filters items piped in and/or out of backpack\nHas more filtering options +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=Portable Jukebox with support for more music discs\nAlso more playback options +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=Magnets items into backpack at a greater range\nHas more filtering options +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=Captures passive and hostile mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=Makes backpack pickup items\nHas more filtering options +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=Pumps Fluids between Tank upgrade and adjacent blocks\nWorks with fluid containers in hand and fluid blocks in world\nAllows to filter which fluids are pumped +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=Keeps refilling stack of selected items in player's inventory\nAllows for more precise target slot selection\nAlso allows middle click picking blocks from backpack +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=Restocks backpack from sneak right clicked inventory\nHas more filtering options +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=Automatically swaps item in player's hand for the one effective on the block/entity when left clicked\nHas filter options and supports manual swaps with the Tool Swap key +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=Voids items selected in filter\nHas more filtering options +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=Anvil in an upgrade tab +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=Replaces part of backpack's inventory with energy storage +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=Compacts items into their compressed variants\nOnly 2x2 recipes +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=Crafting table in an upgrade tab +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=Deposits items from backpack into sneak right clicked inventory +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=Backpack becomes indestructible\nCan't despawn or fall into void +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=Stack multipliers will stack by multiplying instead of adding +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=Feeds player with food from backpack's inventory +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=Filters items piped in and/or out of backpack +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=Makes it possible to put backpacks into the backpack +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=Portable Jukebox +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=Magnets items into backpack at range +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=Captures passive mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=Makes backpack pickup items +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=Pumps fluids between Tank upgrade and adjacent blocks +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=Keeps refilling stack of selected items in player's inventory +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=Restocks backpack from sneak right clicked inventory +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=Multiplies the number of stacks that can fit in a slot by %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=Multiplies the number of stacks that can fit in a slot by %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=Multiplies the number of stacks that can fit in a slot by %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=Multiplies the number of stacks that can fit in a slot by %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=Multiplies the number of stacks that can fit in a slot by %s +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=Replaces part of backpack's inventory with fluid storage +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=Automatically swaps item in player's hand for the one effective on the block/entity when these are left clicked. +item.retro_sophisticated_backpacks.void_upgrade.tooltip=Voids items selected in filter +retro_sophisticated_backpacks.gui.inception_settings=Inception +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=Backpack can not be open by another player when right clicking on this player's back even if the backpack is worn there +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=When this backpack is worn and visible another player can open it when right clicking on this player's back +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=Backpack / Storage gui clears the search phrase when closed and shows all unfiltered item when open again +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=Backpack / Storage gui keeps the search phrase and prefills it / filters by it when open again +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=Open upgrade tab gets closed when the backpack/storage gui is closed and when the gui is next open all of the tabs are closed +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=On close of its gui the backpack/storage records which upgrade tab was last open and opens it when the gui is open next time +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=Shift click from storage/inventory will first try to put the stack in inventory/storage and only then into an open tab. +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=Shift click from storage/inventory will first try to put the stack in an open tab and only then into inventory/storage. +retro_sophisticated_backpacks.gui.tooltip.stack_count=Count: %s diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/es_es.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/es_es.lang index f4e537b..9695f2d 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/es_es.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/es_es.lang @@ -6,14 +6,15 @@ tile.retro_sophisticated_backpacks.backpack_diamond.name=Mochila de Diamante tile.retro_sophisticated_backpacks.backpack_obsidian.name=Mochila de Obsidiana #Objetos -item.retro_sophisticated_backpacks.upgrade_base.name=Base de Mejora -item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Mejora de Apilado Nivel Inicial -item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=Mejora de Apilado Nivel 1 -item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=Mejora de Apilado Nivel 2 -item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=Mejora de Apilado Nivel 3 -item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=Mejora de Apilado Nivel 4 -item.retro_sophisticated_backpacks.crafting_upgrade.name=Mejora de Artesanía -item.retro_sophisticated_backpacks.inception_upgrade.name=Mejora de Incepción +item.retro_sophisticated_backpacks.upgrade_base.name=Base de mejora +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Mejora de stack nivel inicial +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=Mejora de stack nivel 1 +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=Mejora de stack nivel 2 +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=Mejora de stack nivel 3 +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=Mejora de stack nivel 4 +item.retro_sophisticated_backpacks.exponential_stack_upgrade.name=Mejora de stack exponencial +item.retro_sophisticated_backpacks.crafting_upgrade.name=Mejora de fabricación +item.retro_sophisticated_backpacks.inception_upgrade.name=Mejora de incepción #Pestañas Creativas itemGroup.retro_sophisticated_backpacks.creative_tab=Mochila Sofisticada Retro @@ -21,17 +22,303 @@ itemGroup.retro_sophisticated_backpacks.creative_tab=Mochila Sofisticada Retro #Descripciones emergentes (Tooltips) retro_sophisticated_backpacks.tooltip.backpack.inventory_size=Tamaño de inventario: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=Tamaño de ranuras de mejora: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> Multiplicador de apilado: %d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=Multiplicador de tamaño de stack: %s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=Tanque vacío +retro_sophisticated_backpacks.tooltip.backpack.energy=%s EF +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=Fluids +retro_sophisticated_backpacks.tooltip.backpack.energy_title=Energy +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=Mejoras +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=Inventario +retro_sophisticated_backpacks.tooltip.backpack.empty=Sin mejoras o contenidos de inventario +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=Presiona <%s> para ver los contenidos +retro_sophisticated_backpacks.tooltip.backpack.shift=Shift izquierdo -retro_sophisticated_backpacks.tooltip.upgrade_base=¡No hace nada! Siéntete libre de ponerlo en tus ranuras de mejora. -retro_sophisticated_backpacks.tooltip.stack_upgrade=Multiplica el tamaño del apilado por %d -retro_sophisticated_backpacks.tooltip.crafting_upgrade=Añade una cuadrícula de fabricación adicional al lado de la mochila -retro_sophisticated_backpacks.tooltip.inception_upgrade=Permite que las mochilas se guarden dentro de otras mochilas -retro_sophisticated_backpacks.tooltip.shift_to_reveal= #Elementos de GUI -retro_sophisticated_backpacks.container.backpack=Mochila \ No newline at end of file +retro_sophisticated_backpacks.container.backpack=Mochila + +item.retro_sophisticated_backpacks.magnet_upgrade.name=Mejora de imán +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=Mejora de imán avanzada +item.retro_sophisticated_backpacks.void_upgrade.name=Mejora de vacío +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=Mejora de vacío avanzada +item.retro_sophisticated_backpacks.refill_upgrade.name=Mejora de rellenado +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=Mejora de rellenado avanzada +item.retro_sophisticated_backpacks.compacting_upgrade.name=Mejora de compactación +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=Mejora de compactación avanzada +retro_sophisticated_backpacks.gui.magnet_settings=Imán +retro_sophisticated_backpacks.gui.advanced_magnet_settings=Imán avanzado +retro_sophisticated_backpacks.gui.void_settings=Vacío +retro_sophisticated_backpacks.gui.advanced_void_settings=Vacío avanzado +retro_sophisticated_backpacks.gui.refill_settings=Rellenado +retro_sophisticated_backpacks.gui.advanced_refill_settings=Rellenado avanzado +retro_sophisticated_backpacks.gui.compacting_settings=Compactación +retro_sophisticated_backpacks.gui.advanced_compacting_settings=Compactación avanzada +retro_sophisticated_backpacks.gui.void_always=Vaciar cualquiera +retro_sophisticated_backpacks.gui.void_slot_overflow=Vaciar desbordamiento +retro_sophisticated_backpacks.gui.void_storage_overflow=Vaciar desbordamiento de almacenamiento +retro_sophisticated_backpacks.gui.refill_target_any=Cualquier slot +retro_sophisticated_backpacks.gui.refill_target_main_hand=Mano principal +retro_sophisticated_backpacks.gui.refill_target_off_hand=Mano secundaria +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=Slot de barra rápida 1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=Slot de barra rápida 2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=Slot de barra rápida 3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=Slot de barra rápida 4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=Slot de barra rápida 5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=Slot de barra rápida 6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=Slot de barra rápida 7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=Slot de barra rápida 8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=Slot de barra rápida 9 +retro_sophisticated_backpacks.gui.allow=Permitir +retro_sophisticated_backpacks.gui.block=Bloquear +retro_sophisticated_backpacks.gui.match_backpack_contents=Coincidir con el contenido de la mochila +retro_sophisticated_backpacks.gui.pickup_items=Recoger objetos +retro_sophisticated_backpacks.gui.do_not_pickup_items=No recoger objetos +retro_sophisticated_backpacks.gui.compact_only_uncraftable=Compactar solo no fabricables +retro_sophisticated_backpacks.gui.compact_anything=Compactar cualquier cosa +retro_sophisticated_backpacks.gui.only_automatic=Solo funciona con otras mejoras/automatización +retro_sophisticated_backpacks.gui.works_in_gui=Funciona también en la interfaz +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=Permite que una sola ranura se llene con el objeto\ny vacía cualquier cosa que desborde +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Permite que todo el almacenamiento se llene con el objeto\ny vacía cualquier cosa que desborde +retro_sophisticated_backpacks.gui.refill_target_tooltip=Rellenar %s +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=Desplazar para cambiar el slot objetivo +item.retro_sophisticated_backpacks.everlasting_upgrade.name=Mejora de eternidad +item.retro_sophisticated_backpacks.jukebox_upgrade.name=Mejora de tocadiscos +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=Mejora de tocadiscos avanzada +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=Mejora de cambio de herramienta +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=Mejora de cambio de herramienta avanzada +item.retro_sophisticated_backpacks.tank_upgrade.name=Mejora de tanque +item.retro_sophisticated_backpacks.pump_upgrade.name=Mejora de bombeado +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=Mejora de bombeado avanzada +item.retro_sophisticated_backpacks.battery_upgrade.name=Mejora de batería +item.retro_sophisticated_backpacks.anvil_upgrade.name=Mejora de yunque +retro_sophisticated_backpacks.gui.everlasting_settings=Everlasting +retro_sophisticated_backpacks.gui.jukebox_settings=Tocadiscos +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Tocadiscos avanzado +retro_sophisticated_backpacks.gui.tool_swapper_settings=Intercambio de herramientas +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=Intercambio de herramientas +retro_sophisticated_backpacks.gui.tank_settings=Tanque +retro_sophisticated_backpacks.gui.pump_settings=Bombeado +retro_sophisticated_backpacks.gui.advanced_pump_settings=Bombeado avanzado +retro_sophisticated_backpacks.gui.battery_settings=Batería +retro_sophisticated_backpacks.gui.anvil_settings=Yunque +retro_sophisticated_backpacks.gui.pump_input=Entrada +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Interactuar con tanques y tuberías +retro_sophisticated_backpacks.gui.pump_world=Interactuar con el mundo +retro_sophisticated_backpacks.gui.pump_hand=Interactuar con\ncontenedor de fluido en la mano +retro_sophisticated_backpacks.gui.anvil_no_result=No Result +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift-click result into storage +retro_sophisticated_backpacks.gui.jukebox_play=Reproducir +retro_sophisticated_backpacks.gui.jukebox_stop=Detener +retro_sophisticated_backpacks.gui.jukebox_previous=Anterior +retro_sophisticated_backpacks.gui.jukebox_next=Siguiente +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Reproducción aleatoria desactivada +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Reproducción aleatoria activada +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Repetir todo +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Repetir uno +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Repetición desactivada +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Any Item +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Auto Swap Tools +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=Volver a mochila +retro_sophisticated_backpacks.gui.memory_settings.tooltip=Configuración de ranuras de memoria +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=Permite seleccionar ranuras que recuerdan su contenido y solo permiten stacks coincidentes en ellas\nAbre la pestaña para modificar la configuración de las ranuras +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=Permite seleccionar ranuras que recuerdan su contenido y solo permiten stacks coincidentes en ellas\nSeleccionar todo / Deseleccionar todo = botones\nSeleccionar ranura = clic izquierdo/arrastrar\nDeseleccionar ranura = clic derecho/arrastrar +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=Configuración de ranuras sin ordenar +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=Permite seleccionar ranuras que son ignoradas por el ordenamiento\nAbre la pestaña para modificar la configuración de las ranuras +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=Permite seleccionar ranuras que son ignoradas por el ordenamiento\nSeleccionar todo / Deseleccionar todo = botones\nSeleccionar ranura = clic izquierdo/arrastrar\nDeseleccionar ranura = clic derecho/arrastrar + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=Compacta objetos en sus variantes comprimidas\nRecetas 2x2 y 3x3 con más opciones de filtrado +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=Mejora de depósito avanzada +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=Deposita objetos de la mochila en el inventario al que se le hace clic derecho agachado\nTiene más opciones de filtrado +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=Mejora de alimentación avanzada +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=Alimenta al jugador con comida del inventario de la mochila\nMás opciones para cuando la comida se le da al jugador +item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=Mejora de filtrado avanzada +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=Filtra objetos que entran y/o salen de la mochila\nTiene más opciones de filtrado +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=Tocadiscos portátil con soporte para más discos de música\nTambién más opciones de reproducción +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=Atrae objetos a la mochila a mayor distancia\nTiene más opciones de filtrado +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=Advanced Mob Catcher Upgrade +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=Captures passive and hostile mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=Mejora de recolección avanzada +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=Hace que la mochila recoja objetos\nTiene más opciones de filtrado +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=Bombea fluidos entre la mejora de tanque y bloques adyacentes\nFunciona con contenedores de fluido en la mano y bloques de fluido en el mundo\nPermite filtrar qué fluidos se bombean +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=Mantiene rellenando el stack de objetos seleccionados en el inventario del jugador\nPermite una selección de slot de objetivo más precisa\nTambién permite la selección de bloques al hacer clic con el botón central en la mochila +item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=Mejora de reabastecimiento avanzada +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=Reabastece la mochila del inventario al que se le hace clic derecho agachado\nTiene más opciones de filtrado +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=Cambia automáticamente el objeto en la mano del jugador por el adecuado para el bloque/entidad al hacer clic izquierdo\nTiene opciones de filtro e interacción adicional +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=Destruye objetos seleccionados en el filtro\nTiene más opciones de filtrado +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=Yunque en una pestaña de mejora +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=Reemplaza parte del inventario de la mochila con almacenamiento de energía +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=Compacta objetos en sus variantes comprimidas\nSólo recetas 2x2 +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=Mesa de trabajo en una pestaña de mejora +item.retro_sophisticated_backpacks.deposit_upgrade.name=Mejora de depósito +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=Deposita objetos de la mochila en el inventario al que se le hace clic derecho agachado +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=La mochila se vuelve indestructible\nNo puede desaparecer o caer al vacío +item.retro_sophisticated_backpacks.feeding_upgrade.name=Mejora de alimentación +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=Alimenta al jugador con comida del inventario de la mochila +item.retro_sophisticated_backpacks.filter_upgrade.name=Mejora de filtrado +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=Filtra objetos que entran y/o salen de la mochila +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=Hace posible poner mochilas dentro de la mochila +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=Tocadiscos portátil +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=Atrae objetos a la mochila a distancia +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=Mob Catcher Upgrade +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=Captures passive mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.pickup_upgrade.name=Mejora de recolección +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=Hace que la mochila recoja objetos +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=Bombea fluidos entre la mejora de tanque y bloques adyacentes +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=Mantiene rellenando el stack de objetos seleccionados en el inventario del jugador +item.retro_sophisticated_backpacks.restock_upgrade.name=Mejora de reabastecimiento +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=Reabastece la mochila del inventario al que se le hace clic derecho agachado +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=Multiplica el número de stacks que caben en un slot por %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=Multiplica el número de stacks que caben en un slot por %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=Multiplica el número de stacks que caben en un slot por %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=Multiplica el número de stacks que caben en un slot por %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=Multiplica el número de stacks que caben en un slot por %s +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=Los multiplicadores de stack se acumulan multiplicándose en lugar de sumándose +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=Reemplaza parte del inventario de la mochila con almacenamiento de fluido +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=Cambia automáticamente el objeto en la mano del jugador por el efectivo en el bloque/entidad cuando se hace clic izquierdo. +item.retro_sophisticated_backpacks.void_upgrade.tooltip=Destruye objetos seleccionados en el filtro +retro_sophisticated_backpacks.gui.advanced_deposit_settings=Depósito avanzado +retro_sophisticated_backpacks.gui.advanced_feeding_settings=Alimentación avanzada +retro_sophisticated_backpacks.gui.advanced_filter_settings=Filtro avanzado +retro_sophisticated_backpacks.gui.advanced_pickup_settings=Recolección avanzada +retro_sophisticated_backpacks.gui.advanced_restock_settings=Reabastecimiento avanzado +retro_sophisticated_backpacks.gui.backpack_settings=Configuración de mochila +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=Configuración de mochila +retro_sophisticated_backpacks.gui.blacklist=Bloquear +retro_sophisticated_backpacks.gui.complete_hunger=Solo alimentar cuando el jugador tenga suficiente hambre para\nno desperdiciar ningún punto de hambre de la comida en absoluto +retro_sophisticated_backpacks.gui.consider_health=Alimentar al jugador inmediatamente cuando sea herido\nIgnora la configuración de hambre cuando el jugador no está a máxima salud +retro_sophisticated_backpacks.gui.crafting_settings=Fabricación +retro_sophisticated_backpacks.gui.deposit_settings=Depósito +retro_sophisticated_backpacks.gui.feeding_settings=Alimentación +retro_sophisticated_backpacks.gui.filter_settings=Filtro +retro_sophisticated_backpacks.gui.half_hunger=Solo alimentar cuando el jugador tenga suficiente hambre para\ndesperdiciar como máximo la mitad de los puntos de hambre de la comida +retro_sophisticated_backpacks.gui.ignore_durability=Ignorar durabilidad +retro_sophisticated_backpacks.gui.ignore_health=No considerar salud\nIgnora la salud del jugador y solo alimenta según la configuración de hambre +retro_sophisticated_backpacks.gui.ignore_nbt=Ignorar NBT +retro_sophisticated_backpacks.gui.immediate_hunger=Alimentar tan pronto como el jugador tenga un poco de hambre\ndesperdicia bastantes puntos de hambre de la comida +retro_sophisticated_backpacks.gui.inception_settings=Incepción +retro_sophisticated_backpacks.gui.input=Entrada +retro_sophisticated_backpacks.gui.input_output=Entrada y salida +retro_sophisticated_backpacks.gui.item_display_settings=Disposición de objetos +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=Configuración de visualización de objetos +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=Permite seleccionar una ranura que se usará para mostrar su objeto en la parte superior del almacenamiento +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=Permite seleccionar una ranura que se usará para mostrar su objeto en la parte superior del almacenamiento\nSeleccionar ranura = clic izquierdo/arrastrar\nDeseleccionar ranura = clic derecho/arrastrar +retro_sophisticated_backpacks.gui.lock_all_sort=Seleccionar todas las ranuras +retro_sophisticated_backpacks.gui.match_durability=Coincidir durabilidad +retro_sophisticated_backpacks.gui.match_item=Coincidir objeto +retro_sophisticated_backpacks.gui.match_mod_id=Coincidir mod +retro_sophisticated_backpacks.gui.match_nbt=Coincidir NBT +retro_sophisticated_backpacks.gui.memorize_all=Seleccionar todas las ranuras +retro_sophisticated_backpacks.gui.memory_settings=Memoria +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=Click to release +retro_sophisticated_backpacks.gui.output=Salida +retro_sophisticated_backpacks.gui.pickup_settings=Recolección +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=No interactuar con tanques y tuberías +retro_sophisticated_backpacks.gui.pump_no_hand=No interactuar con\ncontenedor de fluido en la mano +retro_sophisticated_backpacks.gui.pump_no_world=No interactuar con el mundo +retro_sophisticated_backpacks.gui.pump_output=Salida +retro_sophisticated_backpacks.gui.restock_settings=Reabastecimiento +retro_sophisticated_backpacks.gui.search=Haz clic para buscar +retro_sophisticated_backpacks.gui.search_detail=@ prefijo para buscar por nombre de mod +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=Otro jugador NO puede abrir +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=La mochila no puede ser abierta por otro jugador al hacer clic derecho en la espalda de este jugador, incluso si está equipada allí +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=Otro jugador puede abrir +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=Cuando esta mochila está equipada y visible, otro jugador puede abrirla al hacer clic derecho en la espalda de este jugador +retro_sophisticated_backpacks.gui.settings_button.context_backpack=Mochila +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=Configuración de esta mochila +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=Heredado del jugador o anulado para esta mochila +retro_sophisticated_backpacks.gui.settings_button.context_player=Jugador +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=Configuración de nivel del jugador +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=Aplica a todas las mochilas/almacenamientos a menos que se anule +retro_sophisticated_backpacks.gui.settings_button.display_side_front=Mostrar en la parte frontal +retro_sophisticated_backpacks.gui.settings_button.display_side_left=Mostrar en el lado izquierdo +retro_sophisticated_backpacks.gui.settings_button.display_side_right=Mostrar en el lado derecho +retro_sophisticated_backpacks.gui.settings_button.item_display_color=Alternar color +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=Siguiente = Clic izquierdo\nAnterior = Clic derecho +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Mantener frase de búsqueda: APAGADO +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=La interfaz de mochila/almacenamiento borra la frase de búsqueda cuando se cierra y muestra todos los objetos sin filtrar cuando se abre nuevamente +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Mantener frase de búsqueda: ENCENDIDO +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=La interfaz de mochila/almacenamiento mantiene la frase de búsqueda y la prellena/filtra por ella cuando se abre nuevamente +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=Mantener pestaña abierta: APAGADO +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=La pestaña de mejora abierta se cierra cuando se cierra la interfaz de la mochila/almacenamiento y cuando la interfaz se abre la próxima vez, todas las pestañas están cerradas +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=Mantener pestaña abierta: ENCENDIDO +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=Al cerra su interfaz, la mochila/almacenamiento registra qué pestaña de mejora estaba abierta por última vez y la abre cuando la interfaz se abre la próxima vez +retro_sophisticated_backpacks.gui.settings_button.rotate=Rotar +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=Sentido horario = Clic izquierdo\nSentido antihorario = Clic derecho +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Shift clic en el inventario primero +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=Al shift clic desde el almacenamiento/inventario, primero intentará poner el stack en el inventario/almacenamiento y solo luego en una pestaña abierta. +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Shift clic en la pestaña abierta primero +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=Al shift clic desde el almacenamiento/inventario, primero intentará poner el stack en una pestaña abierta y solo luego en el inventario/almacenamiento. +retro_sophisticated_backpacks.gui.sort_by_count=Por cantidad +retro_sophisticated_backpacks.gui.sort_by_mod_id=Por mod +retro_sophisticated_backpacks.gui.sort_by_name=Por nombre +retro_sophisticated_backpacks.gui.sort_by_ore_dict=Por etiquetas +retro_sophisticated_backpacks.gui.sort_inventory=Ordenar inventario +retro_sophisticated_backpacks.gui.sorting_settings=Sin ordenar +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=That mob is blocked from capture +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=Bosses cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=Captured %s +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=Release captured mobs before removing Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=Hostile mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=That mob cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=Mobs with inventories cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=Captured mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=No valid release space there +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=No empty %sx%s slot area available +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=Backpack has no Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=Only the owner can capture that mob +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=Only one Mob Catcher Upgrade is allowed per backpack +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=Mobs with passengers or vehicles cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=Players cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=Could not release mob there +retro_sophisticated_backpacks.gui.status.mob_catcher_released=Released %s +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=Mob needs %s slots, more than this upgrade allows (%s) +retro_sophisticated_backpacks.gui.tooltip.stack_count=Cantidad: %s +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transferir al almacenamiento +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transferir coincidente al almacenamiento +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift para transferir todo +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transferir al inventario +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transferir coincidente al inventario +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift para transferir todo +retro_sophisticated_backpacks.gui.unlock_all_sort=Deseleccionar todas las ranuras +retro_sophisticated_backpacks.gui.unmemorize_all=Deseleccionar todas las ranuras +retro_sophisticated_backpacks.gui.whitelist=Permitir + +# Fallbacks for keys used by current code +retro_sophisticated_backpacks.key.open_backpack.desc=Opens Backpack in Inventory +retro_sophisticated_backpacks.key.category=Retro Sophisticated Backpacks +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.stack_size_extra=Count: %s / %s +retro_sophisticated_backpacks.gui.not_in_effect=Not in effect +retro_sophisticated_backpacks.gui.match_ore_dict=By Ore Dictionary +retro_sophisticated_backpacks.gui.add_ore_dict_entry=Add Ore Dict. +retro_sophisticated_backpacks.gui.remove_ore_dict_entry=Remove Ore Dict. +retro_sophisticated_backpacks.gui.ore_dict_input_help=You can input ore dictionary here,\nregex is supported. +retro_sophisticated_backpacks.gui.ore_dict_input_help.pro_tip=Protip: You can hover item onto entry\n to check if the entry is applicable,\nor hover on textfield to see available entries. +retro_sophisticated_backpacks.gui.ore_dict_list_entries=Available ore dict: +retro_sophisticated_backpacks.gui.none=(None) +retro_sophisticated_backpacks.gui.craft_into_backpack=Shift Click Result Into Backpack +retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift Click Result Into Player's Inventory +retro_sophisticated_backpacks.gui.settings=Settings +retro_sophisticated_backpacks.gui.configuration_tab=Configuration Tab +retro_sophisticated_backpacks.gui.memorized_slot=Memorized Slot +retro_sophisticated_backpacks.gui.no_sorting_slot=No Sorting Slot +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/ja_jp.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/ja_jp.lang index 0300ade..fd2570e 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/ja_jp.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/ja_jp.lang @@ -6,25 +6,25 @@ tile.retro_sophisticated_backpacks.backpack_diamond.name=ダイヤモンドの tile.retro_sophisticated_backpacks.backpack_obsidian.name=黒曜石のバックパック # Items -item.retro_sophisticated_backpacks.upgrade_base.name=アップグレードの原型 -item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=スタックアップグレード スターターティア -item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=スタックアップグレード ティア1 -item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=スタックアップグレード ティア2 -item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=スタックアップグレード ティア3 -item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=スタックアップグレード ティア4 +item.retro_sophisticated_backpacks.upgrade_base.name=リュックの強化素材 +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=スタック数増加x1.5 +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=スタック数増加x2 +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=スタック数増加x4 +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=スタック数増加x8 +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=スタック数増加x16 item.retro_sophisticated_backpacks.exponential_stack_upgrade.name=指数スタックアップグレード -item.retro_sophisticated_backpacks.crafting_upgrade.name=クラフトアップグレード -item.retro_sophisticated_backpacks.inception_upgrade.name=入れ子アップグレード -item.retro_sophisticated_backpacks.pickup_upgrade.name=収集アップグレード -item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=強化収集アップグレード -item.retro_sophisticated_backpacks.feeding_upgrade.name=食事アップグレード -item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=強化食事アップグレード -item.retro_sophisticated_backpacks.deposit_upgrade.name=収納アップグレード -item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=強化収納アップグレード -item.retro_sophisticated_backpacks.restock_upgrade.name=回収アップグレード -item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=強化回収アップグレード -item.retro_sophisticated_backpacks.filter_upgrade.name=フィルターアップグレード -item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=強化フィルターアップグレード +item.retro_sophisticated_backpacks.crafting_upgrade.name=作業台機能 +item.retro_sophisticated_backpacks.inception_upgrade.name=入れ子機能 +item.retro_sophisticated_backpacks.pickup_upgrade.name=アイテム直入れ機能 +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=アイテム直入れ機能・改 +item.retro_sophisticated_backpacks.feeding_upgrade.name=ごはん機能 +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=ごはん機能・改 +item.retro_sophisticated_backpacks.deposit_upgrade.name=収納機能 +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=収納機能・改 +item.retro_sophisticated_backpacks.restock_upgrade.name=回収機能 +item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=回収機能・改 +item.retro_sophisticated_backpacks.filter_upgrade.name=仕分けフィルタ機能 +item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=仕分けフィルタ機能・改 # Creative Tabs itemGroup.retro_sophisticated_backpacks.creative_tab=Retro Sophisticated Backpacks @@ -36,113 +36,323 @@ retro_sophisticated_backpacks.key.category=Retro Sophisticated Backpacks # Tooltips retro_sophisticated_backpacks.tooltip.backpack.inventory_size=インベントリスロット数: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=アップグレードスロット数: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> スタック上限倍率: %d倍 %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=スタック数: %s倍 +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=空のタンク +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=Fluids +retro_sophisticated_backpacks.tooltip.backpack.energy_title=Energy +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=アップグレード +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=インベントリ +retro_sophisticated_backpacks.tooltip.backpack.empty=からっぽ +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=<%s>を押すと中身を表示 +retro_sophisticated_backpacks.tooltip.backpack.shift=左SHIFTキー -retro_sophisticated_backpacks.tooltip.upgrade_base=何もしません! ご自由にアップグレードスロットに入れてください! -retro_sophisticated_backpacks.tooltip.stack_upgrade=スタック数の上限を%d倍にします -retro_sophisticated_backpacks.tooltip.exponential_stack_upgrade=スタックアップグレードを複数使用した時の計算方法を加算から乗算にします -retro_sophisticated_backpacks.tooltip.crafting_upgrade=バックパックの側面にクラフトグリッドを追加します -retro_sophisticated_backpacks.tooltip.inception_upgrade=バックパックの中に別のバックパックを入れられるようになります -retro_sophisticated_backpacks.tooltip.pickup_upgrade=拾ったアイテムを直接リュックに入れられるようになります -retro_sophisticated_backpacks.tooltip.advanced_pickup_upgrade=拾ったアイテムを直接リュックに入れられるようになります より詳細な設定が可能です -retro_sophisticated_backpacks.tooltip.feeding_upgrade=バックパックに入っている食べ物を自動で食べられるようになります -retro_sophisticated_backpacks.tooltip.advanced_feeding_upgrade=バックパックに入っている食べ物を自動で食べられるようになります より詳細な設定が可能です -retro_sophisticated_backpacks.tooltip.deposit_upgrade=スニーク右クリックしたインベントリにアイテムを収納できるようになります -retro_sophisticated_backpacks.tooltip.advanced_deposit_upgrade=スニーク右クリックしたインベントリにアイテムを収納できるようになります より詳細な設定が可能です -retro_sophisticated_backpacks.tooltip.restock_upgrade=スニーク右クリックしたインベントリからアイテムを回収できるようになります -retro_sophisticated_backpacks.tooltip.advanced_restock_upgrade=スニーク右クリックしたインベントリからアイテムを回収できるようになります より詳細な設定が可能です -retro_sophisticated_backpacks.tooltip.filter_upgrade=置かれているときのアイテムの入出力を制限できるようになります -retro_sophisticated_backpacks.tooltip.advanced_filter_upgrade=置かれているときに出し入れできるアイテムを制限できるようになります より詳細な設定が可能です -retro_sophisticated_backpacks.tooltip.shift_to_reveal= # Gui Elements retro_sophisticated_backpacks.container.backpack=バックパック -retro_sophisticated_backpacks.gui.pickup_settings=収集機能の設定 -retro_sophisticated_backpacks.gui.advanced_pickup_settings=強化収集機能の設定 +retro_sophisticated_backpacks.gui.pickup_settings=直入れ +retro_sophisticated_backpacks.gui.advanced_pickup_settings=直入れ・改 -retro_sophisticated_backpacks.gui.feeding_settings=食事機能の設定 -retro_sophisticated_backpacks.gui.advanced_feeding_settings=強化食事機能の設定 +retro_sophisticated_backpacks.gui.feeding_settings=ごはん +retro_sophisticated_backpacks.gui.advanced_feeding_settings=ごはん・改 -retro_sophisticated_backpacks.gui.deposit_settings=収納機能の設定 -retro_sophisticated_backpacks.gui.advanced_deposit_settings=強化収納機能の設定 +retro_sophisticated_backpacks.gui.deposit_settings=収納 +retro_sophisticated_backpacks.gui.advanced_deposit_settings=収納・改 -retro_sophisticated_backpacks.gui.restock_settings=回収機能の設定 -retro_sophisticated_backpacks.gui.advanced_restock_settings=強化回収機能の設定 +retro_sophisticated_backpacks.gui.restock_settings=回収 +retro_sophisticated_backpacks.gui.advanced_restock_settings=回収・改 -retro_sophisticated_backpacks.gui.filter_settings=フィルター機能の設定 -retro_sophisticated_backpacks.gui.advanced_filter_settings=強化フィルター機能の設定 +retro_sophisticated_backpacks.gui.filter_settings=仕分け +retro_sophisticated_backpacks.gui.advanced_filter_settings=仕分け・改 -retro_sophisticated_backpacks.gui.memory_settings=記憶機能の設定 -retro_sophisticated_backpacks.gui.sorting_settings=並べ替え機能の設定 +retro_sophisticated_backpacks.gui.memory_settings=個別設定 +retro_sophisticated_backpacks.gui.sorting_settings=並び替え除外 +retro_sophisticated_backpacks.gui.memory_settings.tooltip=スロットごとの設定 +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=スロットの内容を記憶し、一致するアイテムのみ収納します。\n設定タブを開く +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=スロットの内容を記憶し、一致するアイテムのみ収納します。\n全選択/全解除=ボタンを押す\n個別選択=左クリック/ドラッグ\n選択解除=右クリック/ドラッグ +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=並び替えの除外設定 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=並び替えしないスロットを指定します。\n除外設定のタブを開きます。 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=並び替えしないスロットを指定します。\n全選択/全解除=ボタンを押す\nスロットの選択=左クリック/ドラッグ\n選択解除=右クリック/ドラッグ # Gui Tooltips retro_sophisticated_backpacks.gui.stack_size_extra=個数: %s / %s retro_sophisticated_backpacks.gui.not_in_effect=効果なし -retro_sophisticated_backpacks.gui.whitelist=ホワイトリスト -retro_sophisticated_backpacks.gui.blacklist=ブラックリスト +retro_sophisticated_backpacks.gui.whitelist=指定したアイテムのみ +retro_sophisticated_backpacks.gui.blacklist=指定したアイテム以外 -retro_sophisticated_backpacks.gui.match_item=アイテムが一致 -retro_sophisticated_backpacks.gui.match_mod_id=Mod IDが一致 +retro_sophisticated_backpacks.gui.match_item=一致するアイテムのみ +retro_sophisticated_backpacks.gui.match_mod_id=MODが一致するアイテムのみ retro_sophisticated_backpacks.gui.match_ore_dict=鉱石辞書が一致 -retro_sophisticated_backpacks.gui.match_durability=耐久値が一致 -retro_sophisticated_backpacks.gui.ignore_durability=耐久値を無視 +retro_sophisticated_backpacks.gui.match_durability=耐久力が一致するアイテムのみ +retro_sophisticated_backpacks.gui.ignore_durability=耐久力は無視 -retro_sophisticated_backpacks.gui.match_nbt=NBTが一致 -retro_sophisticated_backpacks.gui.ignore_nbt=NBTを無視 +retro_sophisticated_backpacks.gui.match_nbt=NBTが一致するアイテムのみ +retro_sophisticated_backpacks.gui.ignore_nbt=NBTは無視 retro_sophisticated_backpacks.gui.add_ore_dict_entry=鉱石辞書の追加 retro_sophisticated_backpacks.gui.remove_ore_dict_entry=鉱石辞書の削除 retro_sophisticated_backpacks.gui.ore_dict_input_help=ここに鉱石辞書を入力 正規表現に対応しています -retro_sophisticated_backpacks.gui.complete_hunger=満腹度が十分に減少している場合のみ食べる\n満腹度を無駄にしない -retro_sophisticated_backpacks.gui.half_hunger=満腹度が十分に減少している場合のみ食べる\n最大でも半分までしか満腹度を無駄にしない -retro_sophisticated_backpacks.gui.immediate_hunger=満腹度が少しでも減少したら食べる\n満腹度がかなり無駄になる +retro_sophisticated_backpacks.gui.complete_hunger=食料が全く無駄にならないように食べます。 +retro_sophisticated_backpacks.gui.half_hunger=適当なタイミングで食べます。\n食料をあまり無駄にしません。 +retro_sophisticated_backpacks.gui.immediate_hunger=満腹度が少しでも減ると食べます。\n食料の消費が激しいです。 -retro_sophisticated_backpacks.gui.ignore_health=体力を考慮しない\nプレイヤーの体力を無視し、空腹の設定にのみ基づく -retro_sophisticated_backpacks.gui.consider_health=体力が減少したらすぐに食べる\nプレイヤーの体力が最大でない場合、空腹度設定を無視する +retro_sophisticated_backpacks.gui.ignore_health=体力を考慮しません。\n満腹度の設定のみで食べます。 +retro_sophisticated_backpacks.gui.consider_health=体力が減るとすぐに食べます。\n体力が減った場合は満腹度の設定より優先されます。 -retro_sophisticated_backpacks.gui.input_output=入力&出力 -retro_sophisticated_backpacks.gui.input=入力のみ -retro_sophisticated_backpacks.gui.output=出力のみ +retro_sophisticated_backpacks.gui.input_output=出し入れを制限 +retro_sophisticated_backpacks.gui.input=入れるアイテムを制限 +retro_sophisticated_backpacks.gui.output=取り出すアイテムを制限 -retro_sophisticated_backpacks.gui.sort_inventory=バックパックインベントリを並べ替える -retro_sophisticated_backpacks.gui.sort_by_name=アイテム名で並べ替える -retro_sophisticated_backpacks.gui.sort_by_mod_id=Mod IDで並べ替える -retro_sophisticated_backpacks.gui.sort_by_count=個数で並べ替える -retro_sophisticated_backpacks.gui.sort_by_ore_dict=鉱石辞書で並べ替える +retro_sophisticated_backpacks.gui.sort_inventory=並べ替え +retro_sophisticated_backpacks.gui.sort_by_name=五十音順 +retro_sophisticated_backpacks.gui.sort_by_mod_id=By Mod +retro_sophisticated_backpacks.gui.sort_by_count=個数順 +retro_sophisticated_backpacks.gui.sort_by_ore_dict=タグ順 -retro_sophisticated_backpacks.gui.transfer_to_player_inv=全てのアイテムをプレイヤーのインベントリに移動させる -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=一致するアイテムのみをプレイヤーのインベントリに移動させる -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shiftを押してすべて移動 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=全てのアイテムをバックパックのインベントリに移動させる -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=一致するアイテムのみをバックパックのインベントリに移動させる -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shiftを押してすべて移動 +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transfer to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transfer Matching to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift To Transfer All +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transfer to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transfer Matching to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift To Transfer All retro_sophisticated_backpacks.gui.settings=設定 +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=戻る retro_sophisticated_backpacks.gui.configuration_tab=構成タブ retro_sophisticated_backpacks.gui.memorized_slot=記憶スロット -retro_sophisticated_backpacks.gui.memorize_all=全てのスロットを記憶する -retro_sophisticated_backpacks.gui.unmemorize_all=全てのスロットの記憶を解除する +retro_sophisticated_backpacks.gui.memorize_all=全てのスロットを選択 +retro_sophisticated_backpacks.gui.unmemorize_all=スロット選択を全解除 retro_sophisticated_backpacks.gui.no_sorting_slot=並べ替え無効スロット -retro_sophisticated_backpacks.gui.lock_all_sort=全てのスロットを並べ替え無効にする -retro_sophisticated_backpacks.gui.unlock_all_sort=全てのスロットの並べ替え無効を解除する +retro_sophisticated_backpacks.gui.lock_all_sort=全てのスロットを選択 +retro_sophisticated_backpacks.gui.unlock_all_sort=スロット選択を全解除 + +item.retro_sophisticated_backpacks.magnet_upgrade.name=アイテム引き寄せ機能 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=アイテム引き寄せ機能・改 +item.retro_sophisticated_backpacks.void_upgrade.name=ごみ箱機能 +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=ごみ箱機能・改 +item.retro_sophisticated_backpacks.refill_upgrade.name=補充機能 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=補充機能・改 +item.retro_sophisticated_backpacks.compacting_upgrade.name=アイテム圧縮機能 +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=アイテム圧縮機能・改 +retro_sophisticated_backpacks.gui.magnet_settings=引き寄せ +retro_sophisticated_backpacks.gui.advanced_magnet_settings=引き寄せ・改 +retro_sophisticated_backpacks.gui.void_settings=ごみ箱 +retro_sophisticated_backpacks.gui.advanced_void_settings=ごみ箱・改 +retro_sophisticated_backpacks.gui.refill_settings=補充 +retro_sophisticated_backpacks.gui.advanced_refill_settings=補充・改 +retro_sophisticated_backpacks.gui.compacting_settings=圧縮 +retro_sophisticated_backpacks.gui.advanced_compacting_settings=圧縮・改 +retro_sophisticated_backpacks.gui.void_always=全て消去 +retro_sophisticated_backpacks.gui.void_slot_overflow=1枠キープして消去 +retro_sophisticated_backpacks.gui.void_storage_overflow=Void Storage Overflow +retro_sophisticated_backpacks.gui.refill_target_any=指定したスロット +retro_sophisticated_backpacks.gui.refill_target_main_hand=手持ちのスロット +retro_sophisticated_backpacks.gui.refill_target_off_hand=オフハンド +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=スロット 1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=スロット 2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=スロット 3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=スロット 4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=スロット 5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=スロット 6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=スロット 7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=スロット 8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=スロット 9 +retro_sophisticated_backpacks.gui.allow=指定したアイテムのみ +retro_sophisticated_backpacks.gui.block=指定したアイテム以外 +retro_sophisticated_backpacks.gui.match_backpack_contents=既に入っているアイテムのみ +retro_sophisticated_backpacks.gui.pickup_items=アイテムを回収する +retro_sophisticated_backpacks.gui.do_not_pickup_items=アイテムを回収しない +retro_sophisticated_backpacks.gui.compact_only_uncraftable=元に戻せないアイテムは圧縮しない +retro_sophisticated_backpacks.gui.compact_anything=全てのアイテムを圧縮 +retro_sophisticated_backpacks.gui.only_automatic=他の追加機能や、ホッパー・ケーブルなどで入ってきたアイテムのみ +retro_sophisticated_backpacks.gui.works_in_gui=手動で入れたアイテムも対象 +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=1枠分は残し、2枠目以降のアイテムを消去します。 +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Allows whole storage to be filled with the item\n and voids anything that overflows +retro_sophisticated_backpacks.gui.refill_target_tooltip=%sを補充 +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=スクロールで対象スロットを変更 +item.retro_sophisticated_backpacks.everlasting_upgrade.name=防護機能 +item.retro_sophisticated_backpacks.jukebox_upgrade.name=ジュークボックス機能 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=ジュークボックス機能・改 +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=ツール交換機能 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=ツール交換機能・改 +item.retro_sophisticated_backpacks.tank_upgrade.name=タンク機能 +item.retro_sophisticated_backpacks.pump_upgrade.name=ポンプ機能 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=ポンプ機能・改 +item.retro_sophisticated_backpacks.battery_upgrade.name=バッテリ機能 +item.retro_sophisticated_backpacks.anvil_upgrade.name=金床機能 +retro_sophisticated_backpacks.gui.everlasting_settings=Everlasting +retro_sophisticated_backpacks.gui.jukebox_settings=音楽 +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.tool_swapper_settings=ツール交換 +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=ツール交換 +retro_sophisticated_backpacks.gui.tank_settings=タンク +retro_sophisticated_backpacks.gui.pump_settings=ポンプ +retro_sophisticated_backpacks.gui.advanced_pump_settings=ポンプ・改 +retro_sophisticated_backpacks.gui.battery_settings=バッテリ +retro_sophisticated_backpacks.gui.anvil_settings=金床 +retro_sophisticated_backpacks.gui.pump_input=液体を回収 +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_world=周囲の液体を回収 +retro_sophisticated_backpacks.gui.pump_hand=手に持った液体容器から回収 +retro_sophisticated_backpacks.gui.anvil_no_result=No Result +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift-click result into storage +retro_sophisticated_backpacks.gui.jukebox_play=再生 +retro_sophisticated_backpacks.gui.jukebox_stop=停止 +retro_sophisticated_backpacks.gui.jukebox_previous=Previous +retro_sophisticated_backpacks.gui.jukebox_next=Next +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Shuffle Disabled +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Shuffle Enabled +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Repeat All +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Repeat One +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Repeat Disabled +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Any Item +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Auto Swap Tools + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=圧縮できるアイテムを自動で圧縮します。\n3x3で圧縮するアイテムにも対応。 +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=スニークで右クリックしたチェストにアイテムを収納します。\nアイテムフィルタの数が増えます。 +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=リュック内の食料を自動で食べます。\n食事のタイミングを指定可能。 +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=出し入れするアイテムにフィルタをかけます。\nアイテムフィルタの数が増えます。 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=リュックでより多くのディスクによるジュークボックスの機能を楽しめます。\n再生オプションも充実。 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=より拾い範囲のアイテムを引き寄せてリュックに入れます。\nアイテムフィルタの数が増えます。 +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=Advanced Mob Catcher Upgrade +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=Captures passive and hostile mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=拾ったアイテムが直接リュックに入ります。\nアイテムフィルタの数が増えます。 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=タンク機能に置いたストレージに液体を出し入れします。\n周囲の液体を回収できます。\n回収する液体を指定することができます。 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=消費したアイテムをリュックから補充します。\n対象スロットの選択が可能。\nミドルクリックで補充可能。 +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=スニークで右クリックしたチェストから、アイテムをリュックに回収します。\nアイテムフィルタの数が増えます。 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=左クリック時、手に持ったアイテムをブロック/エンティティに有効なものへ自動で入れ替えます\nフィルター設定と追加の相互作用に対応しています +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=指定したアイテムを消滅させます。\n指定できるアイテム数が増えます。 +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=リュック内で金床が使えるようになります。 +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=リュック内のインベントリを2列使用して電力を蓄積します。 +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=2x2で圧縮できるアイテムを自動で圧縮します。 +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=リュック内で手動でクラフトできるようになります。 +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=スニークで右クリックしたチェストにアイテムを収納します。 +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=死亡時や奈落に落ちてもリュックが消滅しなくなります。 +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=スタックアップグレードを複数使用した時の計算方法を加算から乗算にします +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=リュック内の食料を自動で食べます。 +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=出し入れするアイテムにフィルタをかけます。 +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=リュックの中にリュックを入れることができます。 +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=リュックでジュークボックスの機能を楽しめます。 +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=周囲のアイテムを引き寄せてリュックに入れます。 +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=Mob Catcher Upgrade +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=Captures passive mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=拾ったアイテムが直接リュックに入ります。 +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=タンク機能と併用し、液体を出し入れします。 +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=消費したアイテムをリュックから補充します。 +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=スニークで右クリックしたチェストから、アイテムをリュックに回収します。 +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=1スロットあたりのスタック数を%s倍にします +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=1スロットあたりのスタック数を%s倍にします +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=1スロットあたりのスタック数を%s倍にします +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=1スロットあたりのスタック数を%s倍にします +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=1スロットあたりのスタック数を%s倍にします +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=リュック内のインベントリを2列使用して液体を蓄積します。 +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=ブロックを破壊するとき、適切なツールに自動で切り替わります。 +item.retro_sophisticated_backpacks.void_upgrade.tooltip=フィルタで指定したアイテムを消滅させます。 +retro_sophisticated_backpacks.gui.backpack_settings=リュックの設定 +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=リュックの設定 +retro_sophisticated_backpacks.gui.crafting_settings=作業台 +retro_sophisticated_backpacks.gui.inception_settings=入れ子 +retro_sophisticated_backpacks.gui.item_display_settings=ラベル表示 +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=ラベル表示設定 +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=リュックのラベルとして表示するアイテムを指定します。 +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=リュックのラベルとして表示するアイテムを指定します。\n左クリック=スロットを選択\n右クリック=選択を解除 +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=Click to release +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=Do Not Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_no_hand=手に持った液体容器から回収しない +retro_sophisticated_backpacks.gui.pump_no_world=周囲の液体は回収しない +retro_sophisticated_backpacks.gui.pump_output=液体を取り出す +retro_sophisticated_backpacks.gui.search=Click to search +retro_sophisticated_backpacks.gui.search_detail=@ prefix to search by mod name +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=他のプレイヤは開けない +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=装備した背中のリュックを他のプレイヤが開けない +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=他のプレイヤも開ける +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=装備した背中のリュックを他のプレイヤが開ける +retro_sophisticated_backpacks.gui.settings_button.context_backpack=個別に適用 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=リュックごとに設定 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=プレイヤ単位の設定を引き継ぐか、個別に設定したものが適用されます。 +retro_sophisticated_backpacks.gui.settings_button.context_player=全てに適用 +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=プレイヤ単位で設定 +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=個別に上書き設定がない限り、プレイヤが保有する全てのリュックに適用されます。 +retro_sophisticated_backpacks.gui.settings_button.display_side_front=正面に表示 +retro_sophisticated_backpacks.gui.settings_button.display_side_left=左側に表示 +retro_sophisticated_backpacks.gui.settings_button.display_side_right=右側に表示 +retro_sophisticated_backpacks.gui.settings_button.item_display_color=選択スロットの色 +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=左クリック=次の色へ\n右クリック=前の色へ +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Keep Search Phrase: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=Backpack / Storage gui clears the search phrase when closed and shows all unfiltered item when open again +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Keep Search Phrase: ON +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=Backpack / Storage gui keeps the search phrase and prefills it / filters by it when open again +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=開いたタブを記憶: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=リュックのGUIを閉じたときに、全ての追加機能タブを閉じ、次に開いたときにはどのタブも開きません。 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=開いたタブを記憶: ON +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=リュックのGUIを閉じたときに、最後に開いていた追加機能タブを記憶し、次回開いたときにそのタブを開きます。 +retro_sophisticated_backpacks.gui.settings_button.rotate=回転 +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=左クリック=時計回り\n右クリック=反時計回り +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=インベントリ優先 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=リュックやインベントリからSHIFTキーを押すと、インベントリやリュックに優先的に入れ、その後に追加機能タブにアイテムを収納します。 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=拡張機能のタブ優先 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=リュックやインベントリからSHIFTキーを押すと、開いている追加機能タブに優先的に入れ、その後にインベントリやリュックにアイテムを収納します。 +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=That mob is blocked from capture +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=Bosses cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=Captured %s +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=Release captured mobs before removing Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=Hostile mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=That mob cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=Mobs with inventories cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=Captured mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=No valid release space there +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=No empty %sx%s slot area available +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=Backpack has no Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=Only the owner can capture that mob +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=Only one Mob Catcher Upgrade is allowed per backpack +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=Mobs with passengers or vehicles cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=Players cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=Could not release mob there +retro_sophisticated_backpacks.gui.status.mob_catcher_released=Released %s +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=Mob needs %s slots, more than this upgrade allows (%s) +retro_sophisticated_backpacks.gui.tooltip.stack_count=個数: %s + +# Fallbacks for keys used by current code +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.ore_dict_input_help.pro_tip=Protip: You can hover item onto entry\n to check if the entry is applicable,\nor hover on textfield to see available entries. +retro_sophisticated_backpacks.gui.ore_dict_list_entries=Available ore dict: +retro_sophisticated_backpacks.gui.none=(None) +retro_sophisticated_backpacks.gui.craft_into_backpack=Shift Click Result Into Backpack +retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift Click Result Into Player's Inventory +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/ko_kr.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/ko_kr.lang index e47b57d..6fce32e 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/ko_kr.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/ko_kr.lang @@ -6,25 +6,25 @@ tile.retro_sophisticated_backpacks.backpack_diamond.name=다이아몬드 가방 tile.retro_sophisticated_backpacks.backpack_obsidian.name=흑요석 가방 # Items -item.retro_sophisticated_backpacks.upgrade_base.name=업그레이드 기반 -item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=묶음 업그레이드 기본 단계 -item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=묶음 업그레이드 1단계 -item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=묶음 업그레이드 2단계 -item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=묶음 업그레이드 3단계 -item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=묶음 업그레이드 4단계 +item.retro_sophisticated_backpacks.upgrade_base.name=기능 틀 +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=0.5단계 쌓기 기능 +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=1단계 쌓기 기능 +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=2단계 쌓기 기능 +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=3단계 쌓기 기능 +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=4단계 쌓기 기능 item.retro_sophisticated_backpacks.exponential_stack_upgrade.name=지수 묶음 업그레이드 -item.retro_sophisticated_backpacks.crafting_upgrade.name=조합 업그레이드 -item.retro_sophisticated_backpacks.inception_upgrade.name=인셉션 업그레이드 -item.retro_sophisticated_backpacks.pickup_upgrade.name=아이템 줍기 업그레이드 -item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=고급 아이템 줍기 업그레이드 -item.retro_sophisticated_backpacks.feeding_upgrade.name=음식 공급 업그레이드 -item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=고급 음식 공급 업그레이드 -item.retro_sophisticated_backpacks.deposit_upgrade.name=보관 업그레이드 -item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=고급 보관 업그레이드 -item.retro_sophisticated_backpacks.restock_upgrade.name=재보급 업그레이드 -item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=고급 재보급 업그레이드 -item.retro_sophisticated_backpacks.filter_upgrade.name=필터 업그레이드 -item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=고급 필터 업그레이드 +item.retro_sophisticated_backpacks.crafting_upgrade.name=제작대 기능 +item.retro_sophisticated_backpacks.inception_upgrade.name=배낭 보관 기능 +item.retro_sophisticated_backpacks.pickup_upgrade.name=줍기 기능 +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=고급 줍기 기능 +item.retro_sophisticated_backpacks.feeding_upgrade.name=식사 기능 +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=고급 식사 기능 +item.retro_sophisticated_backpacks.deposit_upgrade.name=꺼내기 기능 +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=고급 꺼내기 기능 +item.retro_sophisticated_backpacks.restock_upgrade.name=넣기 기능 +item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=고급 넣기 기능 +item.retro_sophisticated_backpacks.filter_upgrade.name=필터 기능 +item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=고급 필터 기능 # Creative Tabs itemGroup.retro_sophisticated_backpacks.creative_tab=레트로 복잡한 배낭 @@ -36,79 +36,79 @@ retro_sophisticated_backpacks.key.category=레트로 복잡한 배낭 # Tooltips retro_sophisticated_backpacks.tooltip.backpack.inventory_size=인벤토리 크기: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=업그레이드 슬롯: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> 묶음 배율: %d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=쌓을 수 있는 아이템 최대 개수: §fx%s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s 밀리버킷만큼의 %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=탱크가 비어있음 +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=Fluids +retro_sophisticated_backpacks.tooltip.backpack.energy_title=Energy +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=강화 +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=인벤토리 +retro_sophisticated_backpacks.tooltip.backpack.empty=배낭에 아이템이나 강화가 없습니다 +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=<%s>을(를) 누르면 배낭을 확인할 수 있습니다 +retro_sophisticated_backpacks.tooltip.backpack.shift=왼쪽 Shift -retro_sophisticated_backpacks.tooltip.upgrade_base=아무 기능도 없습니다! 업그레이드 슬롯에 마음껏 넣으세요! -retro_sophisticated_backpacks.tooltip.stack_upgrade=아이템 스택 크기를 %d배로 늘립니다 -retro_sophisticated_backpacks.tooltip.exponential_stack_upgrade=묶음 배율이 더하기가 아닌 곱하기로 중첩됩니다 -retro_sophisticated_backpacks.tooltip.crafting_upgrade=가방 측면에 추가 조합 그리드를 추가합니다 -retro_sophisticated_backpacks.tooltip.inception_upgrade=가방 안에 가방을 보관할 수 있게 합니다 -retro_sophisticated_backpacks.tooltip.pickup_upgrade=가방이 아이템을 주울 수 있게 합니다 -retro_sophisticated_backpacks.tooltip.advanced_pickup_upgrade=가방이 더 많은 설정으로 아이템을 주울 수 있게 합니다 -retro_sophisticated_backpacks.tooltip.feeding_upgrade=가방이 플레이어에게 자동으로 음식을 공급합니다 -retro_sophisticated_backpacks.tooltip.advanced_feeding_upgrade=가방이 더 자세한 설정에 맞춰 플레이어에게 자동으로 음식을 공급합니다 -retro_sophisticated_backpacks.tooltip.deposit_upgrade=웅크린 채 우클릭한 인벤토리로 가방의 아이템을 보관합니다 -retro_sophisticated_backpacks.tooltip.advanced_deposit_upgrade=더 많은 설정으로, 웅크린 채 우클릭한 인벤토리로 가방의 아이템을 보관합니다 -retro_sophisticated_backpacks.tooltip.restock_upgrade=웅크린 채 우클릭한 인벤토리에서 가방으로 아이템을 재보급합니다 -retro_sophisticated_backpacks.tooltip.advanced_restock_upgrade=더 많은 설정으로, 웅크린 채 우클릭한 인벤토리에서 가방으로 아이템을 재보급합니다 -retro_sophisticated_backpacks.tooltip.filter_upgrade=월드에 설치 시 넣는 아이템을 필터링합니다 -retro_sophisticated_backpacks.tooltip.advanced_filter_upgrade=더 많은 설정으로, 월드에 설치 시 넣는 아이템을 필터링합니다 -retro_sophisticated_backpacks.tooltip.shift_to_reveal= # Gui Elements retro_sophisticated_backpacks.container.backpack=가방 -retro_sophisticated_backpacks.gui.pickup_settings=아이템 줍기 설정 -retro_sophisticated_backpacks.gui.advanced_pickup_settings=고급 아이템 줍기 설정 +retro_sophisticated_backpacks.gui.pickup_settings=획득 +retro_sophisticated_backpacks.gui.advanced_pickup_settings=고급 획득 -retro_sophisticated_backpacks.gui.feeding_settings=음식 공급 설정 -retro_sophisticated_backpacks.gui.advanced_feeding_settings=고급 음식 공급 설정 +retro_sophisticated_backpacks.gui.feeding_settings=식사 +retro_sophisticated_backpacks.gui.advanced_feeding_settings=고급 식사 -retro_sophisticated_backpacks.gui.deposit_settings=보관 설정 -retro_sophisticated_backpacks.gui.advanced_deposit_settings=고급 보관 설정 +retro_sophisticated_backpacks.gui.deposit_settings=꺼내기 +retro_sophisticated_backpacks.gui.advanced_deposit_settings=고급 꺼내기 -retro_sophisticated_backpacks.gui.restock_settings=재보급 설정 -retro_sophisticated_backpacks.gui.advanced_restock_settings=고급 재보급 설정 +retro_sophisticated_backpacks.gui.restock_settings=넣기 +retro_sophisticated_backpacks.gui.advanced_restock_settings=고급 넣기 -retro_sophisticated_backpacks.gui.filter_settings=필터 설정 -retro_sophisticated_backpacks.gui.advanced_filter_settings=고급 필터 설정 +retro_sophisticated_backpacks.gui.filter_settings=필터 +retro_sophisticated_backpacks.gui.advanced_filter_settings=고급 필터 retro_sophisticated_backpacks.gui.crafting_settings=제작 -retro_sophisticated_backpacks.gui.memory_settings=기억 설정 -retro_sophisticated_backpacks.gui.sorting_settings=정렬 설정 +retro_sophisticated_backpacks.gui.memory_settings=기억 +retro_sophisticated_backpacks.gui.sorting_settings=미정렬 +retro_sophisticated_backpacks.gui.memory_settings.tooltip=기억 슬롯 설정 +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=내용을 기억하고 일치하는 스택만 허용하는 슬롯을 선택할 수 있습니다\n탭을 열어 슬롯 설정을 수정합니다 +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=내용을 기억하고 일치하는 스택만 허용하는 슬롯을 선택할 수 있습니다\n전부 선택 / 전부 선택 해제 = 버튼\n슬롯 선택 = 마우스 왼쪽 버튼/드래그\n슬롯 선택 해제 = 마우스 오른쪽 버튼/드래 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=미정렬 슬롯 설정 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=정렬하지 않을 슬롯을 선택할 수 있습니다\n창을 열어 슬롯을 수정할 수 있습니다 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=정렬하지 않을 슬롯을 선택할 수 있습니다\n전부 선택 / 전부 선택 해제 = 버튼\n슬롯 선택 = 왼쪽 버튼/드래그\n슬롯 선택 해제 = 오른쪽 버튼/드래그 # Gui Tooltips retro_sophisticated_backpacks.gui.stack_size_extra=개수: %s / %s retro_sophisticated_backpacks.gui.not_in_effect=효과 없음 -retro_sophisticated_backpacks.gui.whitelist=허용 목록 -retro_sophisticated_backpacks.gui.blacklist=금지 목록 +retro_sophisticated_backpacks.gui.whitelist=허용 +retro_sophisticated_backpacks.gui.blacklist=차단 -retro_sophisticated_backpacks.gui.match_item=아이템 기준 -retro_sophisticated_backpacks.gui.match_mod_id=모드 ID 기준 +retro_sophisticated_backpacks.gui.match_item=아이템 일치 +retro_sophisticated_backpacks.gui.match_mod_id=모드 일치 retro_sophisticated_backpacks.gui.match_ore_dict=광물 사전 기준 -retro_sophisticated_backpacks.gui.match_durability=내구성 일치 -retro_sophisticated_backpacks.gui.ignore_durability=내구성 무시 +retro_sophisticated_backpacks.gui.match_durability=내구도 일치 +retro_sophisticated_backpacks.gui.ignore_durability=내구도 무시 -retro_sophisticated_backpacks.gui.match_nbt=NBT 데이터 일치 -retro_sophisticated_backpacks.gui.ignore_nbt=NBT 데이터 무시 +retro_sophisticated_backpacks.gui.match_nbt=NBT 일치 +retro_sophisticated_backpacks.gui.ignore_nbt=NBT 무시 retro_sophisticated_backpacks.gui.add_ore_dict_entry=광물 사전 추가 retro_sophisticated_backpacks.gui.remove_ore_dict_entry=광물 사전 제거 @@ -117,40 +117,244 @@ retro_sophisticated_backpacks.gui.ore_dict_input_help.pro_tip=꿀팁: 목록 위 retro_sophisticated_backpacks.gui.ore_dict_list_entries=사용 가능한 광물 사전: retro_sophisticated_backpacks.gui.none=(없음) -retro_sophisticated_backpacks.gui.complete_hunger=음식의 허기 포인트를 전혀 낭비하지\n않을 만큼 배고플 때만 음식을 공급합니다 -retro_sophisticated_backpacks.gui.half_hunger=음식의 허기 포인트를 최대 절반만\n낭비할 만큼 배고플 때만 음식을 공급합니다 -retro_sophisticated_backpacks.gui.immediate_hunger=플레이어가 조금이라도 배고프면 즉시 음식을\n공급합니다. 음식의 허기 포인트를 꽤 낭비합니다 +retro_sophisticated_backpacks.gui.complete_hunger=플레이어가 배고플 때만 음식을 줍니다\n음식이 채우는 배고픔을 전혀 낭비하지 않습니다 +retro_sophisticated_backpacks.gui.half_hunger=플레이어가 충분히 배고플 때만 음식을 줍니다\n많아야 음식이 채우는 배고픔의 반 칸 정도만 낭비합니다 +retro_sophisticated_backpacks.gui.immediate_hunger=플레이어가 배가 고파지면 바로 음식을 줍니다\n음식이 채우는 배고픔을 꽤 많이 낭비합니다 -retro_sophisticated_backpacks.gui.ignore_health=체력을 고려하지 않음\n플레이어의 체력을 무시하고 허기 설정에 따라 음식을 공급합니다 -retro_sophisticated_backpacks.gui.consider_health=피해를 입으면 즉시 음식 공급\n플레이어의 체력이 최대가 아닐 경우 허기 설정을 무시합니다 +retro_sophisticated_backpacks.gui.ignore_health=체력에 영향받지 않습니다\n플레이어의 체력을 무시하고 배고픔 설정에 따라 음식을 줍니다 +retro_sophisticated_backpacks.gui.consider_health=다쳤을 때 즉시 플레이어에게 음식을 줍니다\n플레이어가 최대 체력이 아닐 때 배고픔 설정을 무시하고 바로 음식을 줍니다 -retro_sophisticated_backpacks.gui.input_output=입력 및 출력 -retro_sophisticated_backpacks.gui.input=입력만 -retro_sophisticated_backpacks.gui.output=출력만 +retro_sophisticated_backpacks.gui.input_output=입력 & 출력 +retro_sophisticated_backpacks.gui.input=입력 +retro_sophisticated_backpacks.gui.output=출력 -retro_sophisticated_backpacks.gui.sort_inventory=가방 인벤토리 정렬 -retro_sophisticated_backpacks.gui.sort_by_name=이름순 정렬 -retro_sophisticated_backpacks.gui.sort_by_mod_id=모드 ID순 정렬 -retro_sophisticated_backpacks.gui.sort_by_count=개수순 정렬 -retro_sophisticated_backpacks.gui.sort_by_ore_dict=광물 사전순 정렬 +retro_sophisticated_backpacks.gui.sort_inventory=배낭 정렬 +retro_sophisticated_backpacks.gui.sort_by_name=이름순 +retro_sophisticated_backpacks.gui.sort_by_mod_id=By Mod +retro_sophisticated_backpacks.gui.sort_by_count=개수순 +retro_sophisticated_backpacks.gui.sort_by_ore_dict=태그순 retro_sophisticated_backpacks.gui.craft_into_backpack=결과물을 배낭으로 한꺼번에 옮기기 retro_sophisticated_backpacks.gui.craft_into_player_inventory=결과물을 플레이어의 인벤토리로 한꺼번에 옮기기 -retro_sophisticated_backpacks.gui.transfer_to_player_inv=모든 아이템을 플레이어 인벤토리로 옮기기 -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=일치하는 아이템을 플레이어 인벤토리로 옮기기 -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift 키를 눌러 모두 옮기기 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=모든 아이템을 가방 인벤토리로 옮기기 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=일치하는 아이템을 가방 인벤토리로 옮기기 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift 키를 눌러 모두 옮기기 +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transfer to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transfer Matching to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift To Transfer All +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transfer to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transfer Matching to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift To Transfer All retro_sophisticated_backpacks.gui.settings=설정 +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=배낭으로 돌아가기 retro_sophisticated_backpacks.gui.configuration_tab=구성 탭 retro_sophisticated_backpacks.gui.memorized_slot=기억하는 슬롯 -retro_sophisticated_backpacks.gui.memorize_all=모든 슬롯 기억하기 -retro_sophisticated_backpacks.gui.unmemorize_all=모든 슬롯 잊기 +retro_sophisticated_backpacks.gui.memorize_all=전부 선택 +retro_sophisticated_backpacks.gui.unmemorize_all=전부 선택 해제 retro_sophisticated_backpacks.gui.no_sorting_slot=정렬 안 하는 슬롯 -retro_sophisticated_backpacks.gui.lock_all_sort=모든 슬롯을 정렬 안 하도록 선택 -retro_sophisticated_backpacks.gui.unlock_all_sort=모든 슬롯을 정렬하도록 선택 +retro_sophisticated_backpacks.gui.lock_all_sort=전부 선택 +retro_sophisticated_backpacks.gui.unlock_all_sort=전부 선택 해제 + +item.retro_sophisticated_backpacks.magnet_upgrade.name=자석 기능 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=고급 자석 기능 +item.retro_sophisticated_backpacks.void_upgrade.name=제거 기능 +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=고급 제거 기능 +item.retro_sophisticated_backpacks.refill_upgrade.name=보충 기능 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=고급 보충 기능 +item.retro_sophisticated_backpacks.compacting_upgrade.name=압축 기능 +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=고급 압축 기능 +retro_sophisticated_backpacks.gui.magnet_settings=자석 +retro_sophisticated_backpacks.gui.advanced_magnet_settings=고급 자석 +retro_sophisticated_backpacks.gui.void_settings=제거 +retro_sophisticated_backpacks.gui.advanced_void_settings=고급 제거 +retro_sophisticated_backpacks.gui.refill_settings=보충 +retro_sophisticated_backpacks.gui.advanced_refill_settings=고급 보충 +retro_sophisticated_backpacks.gui.compacting_settings=압축 +retro_sophisticated_backpacks.gui.advanced_compacting_settings=고급 압축 +retro_sophisticated_backpacks.gui.void_always=Void Always +retro_sophisticated_backpacks.gui.void_slot_overflow=Void Slot Overflow +retro_sophisticated_backpacks.gui.void_storage_overflow=Void Storage Overflow +retro_sophisticated_backpacks.gui.refill_target_any=모든 슬롯 +retro_sophisticated_backpacks.gui.refill_target_main_hand=주로 사용하는 손 +retro_sophisticated_backpacks.gui.refill_target_off_hand=다른 손 +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=단축 바 슬롯 1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=단축 바 슬롯 2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=단축 바 슬롯 3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=단축 바 슬롯 4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=단축 바 슬롯 5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=단축 바 슬롯 6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=단축 바 슬롯 7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=단축 바 슬롯 8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=단축 바 슬롯 9 +retro_sophisticated_backpacks.gui.allow=허용 +retro_sophisticated_backpacks.gui.block=차단 +retro_sophisticated_backpacks.gui.match_backpack_contents=배낭 아이템과 일치 +retro_sophisticated_backpacks.gui.pickup_items=아이템 자동 줍기 +retro_sophisticated_backpacks.gui.do_not_pickup_items=아이템 자동 줍기 비활성화 +retro_sophisticated_backpacks.gui.compact_only_uncraftable=되돌릴 수 있는 아이템만 압축 +retro_sophisticated_backpacks.gui.compact_anything=전부 압축 +retro_sophisticated_backpacks.gui.only_automatic=다른 강화/장치에서만 작동합니다 +retro_sophisticated_backpacks.gui.works_in_gui=GUI에서도 작동합니다 +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=Allows single slot to be filled with the item\nand voids anything that overflows +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Allows whole storage to be filled with the item\n and voids anything that overflows +retro_sophisticated_backpacks.gui.refill_target_tooltip=%s 보충 +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=스크롤하여 대상 슬롯을 변경합니다. +item.retro_sophisticated_backpacks.everlasting_upgrade.name=영구 기능 +item.retro_sophisticated_backpacks.jukebox_upgrade.name=주크박스 기능 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=고급 주크박스 기능 +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=도구 전환 기능 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=고급 도구 전환 기능 +item.retro_sophisticated_backpacks.tank_upgrade.name=탱크 기능 +item.retro_sophisticated_backpacks.pump_upgrade.name=펌프 기능 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=고급 펌프 기능 +item.retro_sophisticated_backpacks.battery_upgrade.name=배터리 기능 +item.retro_sophisticated_backpacks.anvil_upgrade.name=모루 기능 +retro_sophisticated_backpacks.gui.everlasting_settings=Everlasting +retro_sophisticated_backpacks.gui.jukebox_settings=주크박스 +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.tool_swapper_settings=도구 전환 +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=도구 전환 +retro_sophisticated_backpacks.gui.tank_settings=탱크 +retro_sophisticated_backpacks.gui.pump_settings=펌프 +retro_sophisticated_backpacks.gui.advanced_pump_settings=고급 펌프 +retro_sophisticated_backpacks.gui.battery_settings=배터리 +retro_sophisticated_backpacks.gui.anvil_settings=모루 +retro_sophisticated_backpacks.gui.pump_input=입력 +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_world=월드와 상호작용 +retro_sophisticated_backpacks.gui.pump_hand=손의 액체 저장소와 상호작용 +retro_sophisticated_backpacks.gui.anvil_no_result=No Result +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift-click result into storage +retro_sophisticated_backpacks.gui.jukebox_play=재생 +retro_sophisticated_backpacks.gui.jukebox_stop=정지 +retro_sophisticated_backpacks.gui.jukebox_previous=Previous +retro_sophisticated_backpacks.gui.jukebox_next=Next +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Shuffle Disabled +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Shuffle Enabled +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Repeat All +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Repeat One +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Repeat Disabled +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Any Item +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Auto Swap Tools + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=아이템을 압축할 수 있게 됩니다.\n2x2와 3x3 제작법을 사용할 수 있고, 필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=저장소에다 웅크리고 우클릭하면 배낭의 아이템을 저장소로 꺼낼 수 있게 됩니다.\n필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=플레이어에게 배낭 속 음식을 먹일 수 있게 됩니다.\n설정 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=배낭에 들어가거나 나가는 아이템을 필터링할 수 있게 됩니다.\n필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=더 많은 음반과 재생 옵션을 지원하는 휴대용 주크박스 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=배낭이 넓은 범위 내에 있는 아이템을 끌어당길 수 있게 됩니다.\n필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=Advanced Mob Catcher Upgrade +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=Captures passive and hostile mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=배낭이 아이템을 주울 수 있게 됩니다.\n필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=탱크 기능이 된 배낭과 인접한 블록 사이에 액체를 넣고 뺄 수 있습니다.\n손에 든 액체 용기와 세계에 있는 액체 블록에서 작동합니다.\n어떤 액체를 받을지 지정할 수 있습니다. +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=선택한 아이템을 플레이어의 보관함으로 보충할 수 있게 됩니다.\n더욱 정밀한 대상 슬롯을 선택 가능합니다.\n또한 배낭에서 블록을 마우스 휠 클릭으로 선택할 수 있습니다. +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=저장소에다 웅크리고 우클릭하면 배낭에 저장소의 아이템을 넣을 수 있게 됩니다.\n필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=왼쪽 클릭 시 손에 든 아이템을 블록/엔티티에 효과적인 아이템으로 자동 교체합니다\n필터 옵션과 추가 상호작용을 지원합니다 +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=필터로 선택한 아이템을 제거할 수 있게 됩니다.\n필터 옵션이 추가됩니다. +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=기능 탭의 모루입니다. +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=배낭의 일부가 에너지 저장소로 변경됩니다. +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=아이템을 압축할 수 있게 됩니다.\n2x2 제작법만 사용할 수 있습니다. +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=기능 창에서 제작대를 사용할 수 있게 됩니다. +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=저장소에다 웅크리고 우클릭하면 배낭의 아이템을 저장소로 꺼낼 수 있게 됩니다. +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=배낭이 사라지지 않게 됩니다.\n아이템이 떨어져 있어도 사라지지 않고, 세계 밖으로 떨어지지 않게 됩니다. +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=묶음 배율이 더하기가 아닌 곱하기로 중첩됩니다 +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=플레이어에게 배낭 속 음식을 먹일 수 있게 됩니다. +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=배낭에 들어가거나 나가는 아이템을 필터링할 수 있게 됩니다. +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=배낭에 배낭을 넣을 수 있게 됩니다. +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=휴대용 주크박스 +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=배낭이 범위 내에 있는 아이템을 끌어당길 수 있게 됩니다. +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=Mob Catcher Upgrade +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=Captures passive mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=배낭이 아이템을 주울 수 있게 됩니다. +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=탱크 기능이 된 배낭과 인접한 블록 사이에 액체를 넣고 뺄 수 있습니다. +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=선택한 아이템을 플레이어의 보관함으로 보충할 수 있게 됩니다. +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=저장소에다 웅크리고 우클릭하면 배낭에 저장소의 아이템을 넣을 수 있게 됩니다. +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=한 슬롯에 들어갈 수 있는 스택 수가 %s배 증가합니다 +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=한 슬롯에 들어갈 수 있는 스택 수가 %s배 증가합니다 +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=한 슬롯에 들어갈 수 있는 스택 수가 %s배 증가합니다 +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=한 슬롯에 들어갈 수 있는 스택 수가 %s배 증가합니다 +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=한 슬롯에 들어갈 수 있는 스택 수가 %s배 증가합니다 +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=배낭의 일부가 액체 저장소로 변경됩니다. +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=플레이어의 손에 있는 도구를 해당 블록/개체에 맞는 도구로 전환합니다. +item.retro_sophisticated_backpacks.void_upgrade.tooltip=필터로 선택한 아이템을 제거할 수 있게 됩니다. +retro_sophisticated_backpacks.gui.backpack_settings=배낭 설정 +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=배낭 설정 +retro_sophisticated_backpacks.gui.inception_settings=넣기 +retro_sophisticated_backpacks.gui.item_display_settings=Item Disp. +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=Item Display Settings +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=Allows selecting a slot that will be used to show its item on top of storage +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=Allows selecting a slot that will be used to show its item on top of storage\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=Click to release +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=Do Not Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_no_hand=손의 액체 저장소와 상호작용 비활성화 +retro_sophisticated_backpacks.gui.pump_no_world=월드와 상호작용 비활성화 +retro_sophisticated_backpacks.gui.pump_output=출력 +retro_sophisticated_backpacks.gui.search=Click to search +retro_sophisticated_backpacks.gui.search_detail=@ prefix to search by mod name +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=다른 플레이어가 열기: 차단 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=배낭을 착용한 경우에도 다른 플레이어가 우클릭으로 배낭을 열 수 없습니다. +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=다른 플레이어가 열기: 허용 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=배낭을 보이게 착용했을 때, 다른 플레이어가 이 플레이어의 등을 우클릭해서 배낭을 열 수 있습니다. +retro_sophisticated_backpacks.gui.settings_button.context_backpack=배낭 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=이 배낭의 설정 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=이 배낭을 재정의합니다. +retro_sophisticated_backpacks.gui.settings_button.context_player=플레이어 +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=플레이어 설정 +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=재정의되지 않는 한 모든 배낭에 적용합니다 +retro_sophisticated_backpacks.gui.settings_button.display_side_front=Show on Front +retro_sophisticated_backpacks.gui.settings_button.display_side_left=Show on Left side +retro_sophisticated_backpacks.gui.settings_button.display_side_right=Show on Right side +retro_sophisticated_backpacks.gui.settings_button.item_display_color=선택 색 +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=다음 = 왼쪽 버튼\n이전 = 오른쪽 버튼 +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Keep Search Phrase: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=Backpack / Storage gui clears the search phrase when closed and shows all unfiltered item when open again +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Keep Search Phrase: ON +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=Backpack / Storage gui keeps the search phrase and prefills it / filters by it when open again +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=창 유지: 꺼짐 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=GUI가 닫히고 열리면 모든 창이 닫힙니다 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=창 유지: 켜짐 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=마지막으로 연 강화 창이 GUI가 닫히고 열려도 유지됩니다 +retro_sophisticated_backpacks.gui.settings_button.rotate=Rotate +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=Clockwise = Left Click\nCounter-Clockwise = Right Click +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Shift 클릭이 인벤토리 우선 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=인벤토리/배낭에서 Shift 클릭을 하면 먼저 아이템을 인벤토리/배낭에 넣은 다음 열린 창에 넣습니다 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Shift 클릭이 열린 창 우선 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=인벤토리/배낭에서 Shift 클릭을 하면 먼저 아이템을 열린 창에 넣은 다음 인벤토리/배낭에 넣습니다 +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=That mob is blocked from capture +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=Bosses cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=Captured %s +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=Release captured mobs before removing Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=Hostile mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=That mob cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=Mobs with inventories cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=Captured mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=No valid release space there +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=No empty %sx%s slot area available +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=Backpack has no Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=Only the owner can capture that mob +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=Only one Mob Catcher Upgrade is allowed per backpack +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=Mobs with passengers or vehicles cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=Players cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=Could not release mob there +retro_sophisticated_backpacks.gui.status.mob_catcher_released=Released %s +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=Mob needs %s slots, more than this upgrade allows (%s) +retro_sophisticated_backpacks.gui.tooltip.stack_count=개수: %s + +# Fallbacks for keys used by current code +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/pl_pl.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/pl_pl.lang index 2922018..2c7024c 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/pl_pl.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/pl_pl.lang @@ -6,18 +6,19 @@ tile.retro_sophisticated_backpacks.backpack_diamond.name=Diamentowy plecak tile.retro_sophisticated_backpacks.backpack_obsidian.name=Obsydianowy plecak # Przedmioty -item.retro_sophisticated_backpacks.upgrade_base.name=Podstawa ulepszeń -item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Podstawowe ulepszenie stosu -item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=Ulepszenie stosu poziomu 1 -item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=Ulepszenie stosu poziomu 2 -item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=Ulepszenie stosu poziomu 3 -item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=Ulepszenie stosu poziomu 4 -item.retro_sophisticated_backpacks.crafting_upgrade.name=Ulepszenie rzemieślnicze -item.retro_sophisticated_backpacks.inception_upgrade.name=Ulepszenie incepcyjne -item.retro_sophisticated_backpacks.pickup_upgrade.name=Ulepszenie zbierające -item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=Zaawansowane ulepszenie zbierające -item.retro_sophisticated_backpacks.feeding_upgrade.name=Ulepszenie karmiące -item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=Zaawansoawane ulepszenie karmiące +item.retro_sophisticated_backpacks.upgrade_base.name=Moduł ulepszenia +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Ulepszenie stosów bazowe +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=Ulepszenie stosów poziom 1 +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=Ulepszenie stosów poziom 2 +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=Ulepszenie stosów poziom 3 +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=Ulepszenie stosów poziom 4 +item.retro_sophisticated_backpacks.exponential_stack_upgrade.name=Ulepszenie stosów wykładnicze +item.retro_sophisticated_backpacks.crafting_upgrade.name=Ulepszenie wytwarzania +item.retro_sophisticated_backpacks.inception_upgrade.name=Ulepszenie incepcji +item.retro_sophisticated_backpacks.pickup_upgrade.name=Ulepszenie podnoszenia +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=Zaawansowane ulepszenie podnoszenia +item.retro_sophisticated_backpacks.feeding_upgrade.name=Ulepszenie karmienia +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=Zaawansowane ulepszenie Karmienia # Zakładki trybu kreatywnego itemGroup.retro_sophisticated_backpacks.creative_tab=Retro Sophisticated Backpack @@ -29,60 +30,314 @@ retro_sophisticated_backpacks.key.category=Retro Sophisticated Backpack # Podpowiedzi retro_sophisticated_backpacks.tooltip.backpack.inventory_size=Rozmiar ekwipunku: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=Sloty ulepszeń: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> Mnożnik stosu: %d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=Stack Size Multiplier: %s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=Empty tank +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=Fluids +retro_sophisticated_backpacks.tooltip.backpack.energy_title=Energy +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=Upgrades +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=Inventory +retro_sophisticated_backpacks.tooltip.backpack.empty=No Upgrades or Inventory Contents +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=Press <%s> to View Contents +retro_sophisticated_backpacks.tooltip.backpack.shift=Left Shift -retro_sophisticated_backpacks.tooltip.upgrade_base=Nic nie robi! Możesz umieścić ją w miejscu na ulepszenia! -retro_sophisticated_backpacks.tooltip.stack_upgrade=Mnoży rozmiar stosu o %d -retro_sophisticated_backpacks.tooltip.crafting_upgrade=Dodaje siatkę rzemieślniczą z boku plecaka -retro_sophisticated_backpacks.tooltip.inception_upgrade=Pozwala umieścić plecaki w plecaku -retro_sophisticated_backpacks.tooltip.pickup_upgrade=Pozwala plecakom podnosić przedmioty -retro_sophisticated_backpacks.tooltip.advanced_pickup_upgrade=Pozwala plecakom podnosić przedmioty z większą ilością konfiguracji -retro_sophisticated_backpacks.tooltip.feeding_upgrade=Pozwala plecakom automatycznie karmić gracza -retro_sophisticated_backpacks.tooltip.advanced_feeding_upgrade=Pozwala plecakom automatycznie karmić z większą ilością konfiguracji -retro_sophisticated_backpacks.tooltip.shift_to_reveal= # Elementy interfejsu retro_sophisticated_backpacks.container.backpack=Plecak -retro_sophisticated_backpacks.gui.pickup_settings=Ustawienia zbierania +retro_sophisticated_backpacks.gui.pickup_settings=Pickup -retro_sophisticated_backpacks.gui.advanced_pickup_settings=Zaaw. ustawienia zbierania +retro_sophisticated_backpacks.gui.advanced_pickup_settings=Adv. Pickup -retro_sophisticated_backpacks.gui.feeding_settings=Ustawienia karmienia +retro_sophisticated_backpacks.gui.feeding_settings=Feeding -retro_sophisticated_backpacks.gui.advanced_feeding_settings=Zaaw. ustawienia karmienia +retro_sophisticated_backpacks.gui.advanced_feeding_settings=Adv. Feeding # Podpowiedzi interfejsu retro_sophisticated_backpacks.gui.not_in_effect=Wyłączone -retro_sophisticated_backpacks.gui.whitelist=Biała lista -retro_sophisticated_backpacks.gui.blacklist=Czarna lista +retro_sophisticated_backpacks.gui.whitelist=Allow +retro_sophisticated_backpacks.gui.blacklist=Block -retro_sophisticated_backpacks.gui.match_item=Po przedmiocie -retro_sophisticated_backpacks.gui.match_mod_id=Po Mod ID +retro_sophisticated_backpacks.gui.match_item=Match Item +retro_sophisticated_backpacks.gui.match_mod_id=Match Mod retro_sophisticated_backpacks.gui.match_ore_dict=Po Ore Dictionary -retro_sophisticated_backpacks.gui.consider_durability=Dopasuj zużycie -retro_sophisticated_backpacks.gui.ignore_durability=Ignoruj zużycie +retro_sophisticated_backpacks.gui.ignore_durability=Ignore Durability -retro_sophisticated_backpacks.gui.consider_nbt=Dopasuj NBT -retro_sophisticated_backpacks.gui.ignore_nbt=Ignoruj NBT +retro_sophisticated_backpacks.gui.ignore_nbt=Ignore NBT retro_sophisticated_backpacks.gui.add_ore_dict_entry=Dodaj Ore Dict. retro_sophisticated_backpacks.gui.remove_ore_dict_entry=Usuń Ore Dict. retro_sophisticated_backpacks.gui.ore_dict_input_help=Tu możesz wprowadzić ore dictionary, regex jest wspierany -retro_sophisticated_backpacks.gui.complete_hunger=Karm gracza tylko jeśli jest wystarczająco głodny\naby nie marnować żadnych punktów głodu jedzenia -retro_sophisticated_backpacks.gui.half_hunger=Karm gracza tylko jeśli jest wystarczająco głodny\naby marnować co najwyżej połowę punktów głodu jedzenia -retro_sophisticated_backpacks.gui.immediate_hunger=Karm gracza kiedy tylko stanie się głodny\nmarnuje sporo punktów głodu jedzenia +retro_sophisticated_backpacks.gui.complete_hunger=Only feed when player is hungry enough\nto not waste any hunger points of the food at all +retro_sophisticated_backpacks.gui.half_hunger=Only feed when player is hungry enough\nto only waste half hunger points of the food at most +retro_sophisticated_backpacks.gui.immediate_hunger=Feed as soon as player is tiny bit hungry\nwastes quite a few hunger points of the food -retro_sophisticated_backpacks.gui.ignore_health=Nie bierz pod uwagę zdrowia\nIgnoruje punkty zdrowia gracza i karmi go tylko na podstawie ustawienia głodu -retro_sophisticated_backpacks.gui.consider_health=Karm gracza natychmiast po otrzymaniu obrażeń\nIgnoruje ustawienia głodu kiedy gracz nie ma wszystkich punktów zdrowia +retro_sophisticated_backpacks.gui.ignore_health=Do not consider health\nIgnores player's health and only feeds based on hunger setting +retro_sophisticated_backpacks.gui.consider_health=Feed player immediately when hurt\nIgnores hunger setting when player is not at max health + +item.retro_sophisticated_backpacks.magnet_upgrade.name=Ulepszenie magnesu +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=Zaawansowane ulepszenie magnesu +item.retro_sophisticated_backpacks.void_upgrade.name=Ulepszenie pustki +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=Zaawansowane ulepszenie pustki +item.retro_sophisticated_backpacks.refill_upgrade.name=Ulepszenie zapasu +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=Zaawansowane ulepszenie zapasu +item.retro_sophisticated_backpacks.compacting_upgrade.name=Ulepszenie kompaktujące +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=Zaawansowane ulepszenie kompaktujące +retro_sophisticated_backpacks.gui.magnet_settings=Magnet +retro_sophisticated_backpacks.gui.advanced_magnet_settings=Adv. Magnet +retro_sophisticated_backpacks.gui.void_settings=Void +retro_sophisticated_backpacks.gui.advanced_void_settings=Adv. Void +retro_sophisticated_backpacks.gui.refill_settings=Zapas +retro_sophisticated_backpacks.gui.advanced_refill_settings=Zaaw. zapas +retro_sophisticated_backpacks.gui.compacting_settings=Compa... +retro_sophisticated_backpacks.gui.advanced_compacting_settings=Adv. Compacting +retro_sophisticated_backpacks.gui.void_always=Void Always +retro_sophisticated_backpacks.gui.void_slot_overflow=Void Slot Overflow +retro_sophisticated_backpacks.gui.void_storage_overflow=Void Storage Overflow +retro_sophisticated_backpacks.gui.refill_target_any=Dowolny slot +retro_sophisticated_backpacks.gui.refill_target_main_hand=Główna ręka +retro_sophisticated_backpacks.gui.refill_target_off_hand=Druga ręka +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=Slot paska narzędzi 1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=Slot paska narzędzi 2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=Slot paska narzędzi 3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=Slot paska narzędzi 4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=Slot paska narzędzi 5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=Slot paska narzędzi 6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=Slot paska narzędzi 7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=Slot paska narzędzi 8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=Slot paska narzędzi 9 +retro_sophisticated_backpacks.gui.allow=Allow +retro_sophisticated_backpacks.gui.block=Block +retro_sophisticated_backpacks.gui.match_backpack_contents=Dopasuj zawartość plecaka +retro_sophisticated_backpacks.gui.pickup_items=Pickup Items +retro_sophisticated_backpacks.gui.do_not_pickup_items=Do Not Pickup Items +retro_sophisticated_backpacks.gui.compact_only_uncraftable=Compact Only Uncraftable +retro_sophisticated_backpacks.gui.compact_anything=Compact Anything +retro_sophisticated_backpacks.gui.only_automatic=Only works with other upgrades/automation +retro_sophisticated_backpacks.gui.works_in_gui=Works in GUI as well +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=Allows single slot to be filled with the item\nand voids anything that overflows +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Allows whole storage to be filled with the item\n and voids anything that overflows +retro_sophisticated_backpacks.gui.refill_target_tooltip=Uzupełnij %s +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=Przewiń, aby zmienić docelowy slot. +item.retro_sophisticated_backpacks.everlasting_upgrade.name=Ulepszenie wieczne +item.retro_sophisticated_backpacks.jukebox_upgrade.name=Ulepszenie szafy grającej +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=Advanced Jukebox Upgrade +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=Ulepszenie zamiany narzędzi +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=Zaawansowane ulepszenie zamiany narzędzi +item.retro_sophisticated_backpacks.tank_upgrade.name=Ulepszenie zbiornika +item.retro_sophisticated_backpacks.pump_upgrade.name=Ulepszenie pompy +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=Zaawansowane ulepszenie Pompy +item.retro_sophisticated_backpacks.battery_upgrade.name=Ulepszenie baterii +item.retro_sophisticated_backpacks.anvil_upgrade.name=Ulepszenie kowadła +retro_sophisticated_backpacks.gui.everlasting_settings=Everlasting +retro_sophisticated_backpacks.gui.jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.tool_swapper_settings=Zamiana narzędzi +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=Zamiana narzędzi +retro_sophisticated_backpacks.gui.tank_settings=Tank +retro_sophisticated_backpacks.gui.pump_settings=Pump +retro_sophisticated_backpacks.gui.advanced_pump_settings=Adv. Pump +retro_sophisticated_backpacks.gui.battery_settings=Batt. +retro_sophisticated_backpacks.gui.anvil_settings=Kowadło +retro_sophisticated_backpacks.gui.pump_input=Input +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_world=Interact With World +retro_sophisticated_backpacks.gui.pump_hand=Interact With\nFluid Container in Hand +retro_sophisticated_backpacks.gui.anvil_no_result=No Result +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift-click result into storage +retro_sophisticated_backpacks.gui.jukebox_play=Play +retro_sophisticated_backpacks.gui.jukebox_stop=Stop +retro_sophisticated_backpacks.gui.jukebox_previous=Previous +retro_sophisticated_backpacks.gui.jukebox_next=Next +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Shuffle Disabled +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Shuffle Enabled +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Repeat All +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Repeat One +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Repeat Disabled +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Any Item +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Auto Swap Tools +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=Wróć Do plecaka +retro_sophisticated_backpacks.gui.memory_settings.tooltip=Slot Memory Settings +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=Allows selecting slots that remember their contents and only allow matching stacks in them\nOpen tab to modify slot settings +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=Allows selecting slots that remember their contents and only allow matching stacks in them\nSelect all / Unselect all = buttons\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=No Sort Slot Settings +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=Allows selecting slots that are ignored by sorting\nOpen tab to modify slot settings +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=Allows selecting slots that are ignored by sorting\nSelect all / Unselect all = buttons\nSelect slot = left click/drag\nUnselect slot = right click/drag + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=Kompaktuje przedmioty\nzarówno crafting 2x2 jak i 3x3 z większą ilością opcji filtrujących +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=Zaawansowane ulepszenie depozytu +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=Umieszcza przedmioty z plecaka do wybranego inwentarza skradając się i klikając prawy przycisk myszy, trzymając plecak w ręce\nma więcej opcji filtrowania +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=Karmi gracza jedzeniem z wyposażenia plecaka\nmożna wybrać kiedy karmi +item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=Zaawansowane ulepszenie filtra +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=Filtruje przedmioty przesłane do lub z plecaka\nma więcej opcjii filtrowania +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=Portable Jukebox with support for more music discs\nAlso more playback options +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=Przyciąga przedmioty do plecaka z daleka\nma wiecej opcjii filtrowania +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=Advanced Mob Catcher Upgrade +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=Captures passive and hostile mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=Sprawia że plecak podnosi przedmioty\nma więcej filtrów +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=Pompuje płyny między ulepszeniem zbiornika a pobliskimi blokami\ndziała z pojemnikami na płyn trzymanymi w ręce i blokami płynów w świecie\nPozwala na wybranie które płyny są pompowane +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=Utrzymuje uzupełnianie stosu wybranych przedmiotów w ekwipunku gracza\nUmożliwia precyzyjniejszy wybór docelowego slotu\nUmożliwia również wybieranie bloków z plecaka za pomocą środkowego przycisku myszki +item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=Zaawansowane ulepszenie uzupełnienia +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=Przerzuca rzeczy z wyposażenia gracza do wyposażenia plecaka jeżeli trzymany w ręce przy prawym kliknięciu i skradając się\nma więcej opcji filtrowania +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=Automatycznie zamienia przedmiot w ręce gracza na skuteczny dla bloku/bytu przy kliknięciu lewym przyciskiem\nMa opcje filtrowania i dodatkową obsługę interakcji +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=Wyrzuca, wybrane w filtrze przedmioty do pustki\nma więcej opcjii filtrowania +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=Kowadło w rubryce ulepszenia +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=Zamienia część inwentarza plecaka na magazyn energii +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=Kompaktuje przedmioty\ntylko crafting 2x2 +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=Stół Rzemieślniczy w rubryce ulepszenia +item.retro_sophisticated_backpacks.deposit_upgrade.name=Ulepszenie depozytu +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=Umieszcza przedmioty z plecaka do wybranego inwentarza skradając się i klikając prawy przycisk myszy, trzymając plecak w ręce. +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=Plecak staje się nieznisczalny\nnie może zniknąć ani wpaść do pustki +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=Karmi gracza jedzeniem z wyposażenia plecaka +item.retro_sophisticated_backpacks.filter_upgrade.name=Ulepszenia filtra +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=Filtruje przedmioty przesłane do lub z plecaka +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=Umożliwia umieszczanie plecaków w plecakach +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=Przenośna szafa grająca +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=Przyciąga przedmioty do plecaka +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=Mob Catcher Upgrade +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=Captures passive mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=Sprawia że plecak podnosi przedmioty +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=Pompuje płyny między ulepszeniem zbiornika a pobliskimi blokami +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=Uzupełnia stack przedmiotów w ekwipunku gracza +item.retro_sophisticated_backpacks.restock_upgrade.name=Ulepszenie uzupełnienia +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=Przerzuca rzeczy z wyposażenia gracza do wyposażenia plecaka jeżeli trzymany w ręce przy prawym kliknięciu i skradając się +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=Mnoży liczbę stosów mieszczących się w slocie przez %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=Mnoży liczbę stosów mieszczących się w slocie przez %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=Mnoży liczbę stosów mieszczących się w slocie przez %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=Mnoży liczbę stosów mieszczących się w slocie przez %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=Mnoży liczbę stosów mieszczących się w slocie przez %s +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=Mnożniki stosów będą się łączyć przez mnożenie zamiast dodawania +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=Zamienia część inwentarza plecaka na zbiornik na płyny +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=Automatycznie zamienia przedmiot w ręku gracza na ten, który jest skuteczny na bloku/przedmiocie, gdy te zostaną kliknięte lewym przyciskiem myszy. +item.retro_sophisticated_backpacks.void_upgrade.tooltip=Wyrzuca, wybrane w filtrze przedmioty do pustki +retro_sophisticated_backpacks.gui.advanced_deposit_settings=Zaaw. depozyt +retro_sophisticated_backpacks.gui.advanced_filter_settings=Adv. Filter +retro_sophisticated_backpacks.gui.advanced_restock_settings=Zaaw. uzupełnienie +retro_sophisticated_backpacks.gui.backpack_settings=Ustawienia plecaka +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=Ustawienia plecaka +retro_sophisticated_backpacks.gui.crafting_settings=Craft +retro_sophisticated_backpacks.gui.deposit_settings=Depozyt +retro_sophisticated_backpacks.gui.filter_settings=Filter +retro_sophisticated_backpacks.gui.inception_settings=Incepcja +retro_sophisticated_backpacks.gui.input=Input +retro_sophisticated_backpacks.gui.input_output=Input & Output +retro_sophisticated_backpacks.gui.item_display_settings=Item Disp. +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=Item Display Settings +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=Allows selecting a slot that will be used to show its item on top of storage +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=Allows selecting a slot that will be used to show its item on top of storage\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.lock_all_sort=Select All Slots +retro_sophisticated_backpacks.gui.match_durability=Match Durability +retro_sophisticated_backpacks.gui.match_nbt=Match NBT +retro_sophisticated_backpacks.gui.memorize_all=Select All Slots +retro_sophisticated_backpacks.gui.memory_settings=Memory +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=Click to release +retro_sophisticated_backpacks.gui.output=Output +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=Do Not Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_no_hand=Do Not Interact With\nFluid Container in Hand +retro_sophisticated_backpacks.gui.pump_no_world=Do Not Interact With World +retro_sophisticated_backpacks.gui.pump_output=Output +retro_sophisticated_backpacks.gui.restock_settings=Uzupełnienie +retro_sophisticated_backpacks.gui.search=Click to search +retro_sophisticated_backpacks.gui.search_detail=@ prefix to search by mod name +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=Inny gracz NIE może otworzyć +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=Plecak nie może być otwarty przez innego gracza po kliknięciu prawym przyciskiem myszy na plecy tego gracza. +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=Inny gracz może otworzyć +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=Kiedy ten plecak jest noszony i widoczny, inny gracz może go otworzyć, klikając prawym przyciskiem myszy na plecach tego gracza +retro_sophisticated_backpacks.gui.settings_button.context_backpack=Plecak +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=Ustawienia Tego plecaka +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=Odziedziczone od gracza lub nadpisane dla tego plecaka +retro_sophisticated_backpacks.gui.settings_button.context_player=Player +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=Player level settings +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=Apply to all backpacks/storages unless overriden +retro_sophisticated_backpacks.gui.settings_button.display_side_front=Show on Front +retro_sophisticated_backpacks.gui.settings_button.display_side_left=Show on Left side +retro_sophisticated_backpacks.gui.settings_button.display_side_right=Show on Right side +retro_sophisticated_backpacks.gui.settings_button.item_display_color=Toggle Color +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=Next = Left Click\nPrevious = Right Click +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Keep Search Phrase: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=Backpack / Storage gui clears the search phrase when closed and shows all unfiltered item when open again +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Keep Search Phrase: ON +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=Backpack / Storage gui keeps the search phrase and prefills it / filters by it when open again +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=Keep Tab Open: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=Open upgrade tab gets closed when the backpack/storage gui is closed and when the gui is next open all of the tabs are closed +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=Keep Tab Open: ON +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=On close of its gui the backpack/storage records which upgrade tab was last open and opens it when the gui is open next time +retro_sophisticated_backpacks.gui.settings_button.rotate=Rotate +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=Clockwise = Left Click\nCounter-Clockwise = Right Click +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Shift Click Into Inventory First +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=Shift click from storage/inventory will first try to put the stack in inventory/storage and only then into an open tab. +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Shift Click Into Open Tab First +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=Shift click from storage/inventory will first try to put the stack in an open tab and only then into inventory/storage. +retro_sophisticated_backpacks.gui.sort_by_count=By Count +retro_sophisticated_backpacks.gui.sort_by_mod_id=By Mod +retro_sophisticated_backpacks.gui.sort_by_name=By Name +retro_sophisticated_backpacks.gui.sort_by_ore_dict=By Tags +retro_sophisticated_backpacks.gui.sort_inventory=Sort Inventory +retro_sophisticated_backpacks.gui.sorting_settings=No Sort +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=That mob is blocked from capture +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=Bosses cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=Captured %s +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=Release captured mobs before removing Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=Hostile mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=That mob cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=Mobs with inventories cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=Captured mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=No valid release space there +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=No empty %sx%s slot area available +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=Backpack has no Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=Only the owner can capture that mob +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=Only one Mob Catcher Upgrade is allowed per backpack +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=Mobs with passengers or vehicles cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=Players cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=Could not release mob there +retro_sophisticated_backpacks.gui.status.mob_catcher_released=Released %s +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=Mob needs %s slots, more than this upgrade allows (%s) +retro_sophisticated_backpacks.gui.tooltip.stack_count=Count: %s +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transfer to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transfer Matching to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift To Transfer All +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transfer to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transfer Matching to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift To Transfer All +retro_sophisticated_backpacks.gui.unlock_all_sort=Unselect All Slots +retro_sophisticated_backpacks.gui.unmemorize_all=Unselect All Slots + +# Fallbacks for keys used by current code +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.stack_size_extra=Count: %s / %s +retro_sophisticated_backpacks.gui.ore_dict_input_help.pro_tip=Protip: You can hover item onto entry\n to check if the entry is applicable,\nor hover on textfield to see available entries. +retro_sophisticated_backpacks.gui.ore_dict_list_entries=Available ore dict: +retro_sophisticated_backpacks.gui.none=(None) +retro_sophisticated_backpacks.gui.craft_into_backpack=Shift Click Result Into Backpack +retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift Click Result Into Player's Inventory +retro_sophisticated_backpacks.gui.settings=Settings +retro_sophisticated_backpacks.gui.configuration_tab=Configuration Tab +retro_sophisticated_backpacks.gui.memorized_slot=Memorized Slot +retro_sophisticated_backpacks.gui.no_sorting_slot=No Sorting Slot +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/ru_ru.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/ru_ru.lang index 1ba6a51..df64631 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/ru_ru.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/ru_ru.lang @@ -6,14 +6,15 @@ tile.retro_sophisticated_backpacks.backpack_diamond.name=Алмазный Рюк tile.retro_sophisticated_backpacks.backpack_obsidian.name=Обсидиановый Рюкзак #Предметы -item.retro_sophisticated_backpacks.upgrade_base.name=База для улучшений -item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Начальный уровень улучшения стаков -item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=Улучшение стаков Уровень 1 -item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=Улучшение стаков Уровень 2 -item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=Улучшение стаков Уровень 3 -item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=Улучшение стаков Уровень 4 -item.retro_sophisticated_backpacks.crafting_upgrade.name=Улучшение Крафта -item.retro_sophisticated_backpacks.inception_upgrade.name=Улучшение Инцепции +item.retro_sophisticated_backpacks.upgrade_base.name=Основа улучшения +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Улучшение «Переполнение 0» +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=Улучшение «Переполнение I» +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=Улучшение «Переполнение II» +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=Улучшение «Переполнение III» +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=Улучшение «Переполнение IV» +item.retro_sophisticated_backpacks.exponential_stack_upgrade.name=Экспоненциальное улучшение стака +item.retro_sophisticated_backpacks.crafting_upgrade.name=Улучшение: «Верстак» +item.retro_sophisticated_backpacks.inception_upgrade.name=Улучшение «Внедрение» #Творческие вкладки itemGroup.retro_sophisticated_backpacks.creative_tab=Ретро Современный Рюкзак @@ -21,17 +22,303 @@ itemGroup.retro_sophisticated_backpacks.creative_tab=Ретро Современ #Всплывающие подсказки (Tooltips) retro_sophisticated_backpacks.tooltip.backpack.inventory_size=Размер инвентаря: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=Размер слотов для улучшений: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> Множитель стаков: %d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=Множитель стака: %s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=Резервуар пуст +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=Fluids +retro_sophisticated_backpacks.tooltip.backpack.energy_title=Energy +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=Улучшения +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=Инвентарь +retro_sophisticated_backpacks.tooltip.backpack.empty=Не содержит предметы или улучшения +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=Зажмите <%s> для просмотра содержимого +retro_sophisticated_backpacks.tooltip.backpack.shift=Shift слева -retro_sophisticated_backpacks.tooltip.upgrade_base=Ничего не делает! Можете положить в слоты для улучшений! -retro_sophisticated_backpacks.tooltip.stack_upgrade=Умножает размер стака на %d -retro_sophisticated_backpacks.tooltip.crafting_upgrade=Добавляет дополнительную сетку крафта на боковую часть рюкзака -retro_sophisticated_backpacks.tooltip.inception_upgrade=Позволяет рюкзакам храниться внутри других рюкзаков -retro_sophisticated_backpacks.tooltip.shift_to_reveal= #Элементы интерфейса (GUI) -retro_sophisticated_backpacks.container.backpack=Рюкзак \ No newline at end of file +retro_sophisticated_backpacks.container.backpack=Рюкзак + +item.retro_sophisticated_backpacks.magnet_upgrade.name=Улучшение «Магнит» +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=Продвинутое улучшение «Магнит» +item.retro_sophisticated_backpacks.void_upgrade.name=Улучшение «Пустота» +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=Продвинутое улучшение «Пустота» +item.retro_sophisticated_backpacks.refill_upgrade.name=Улучшение «Пополнение» +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=Продвинутое улучшение «Пополнение» +item.retro_sophisticated_backpacks.compacting_upgrade.name=Улучшение «Сжатие» +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=Продвинутое улучшение «Сжатие» +retro_sophisticated_backpacks.gui.magnet_settings=Магнит +retro_sophisticated_backpacks.gui.advanced_magnet_settings=Продв. магнит +retro_sophisticated_backpacks.gui.void_settings=Уничт. +retro_sophisticated_backpacks.gui.advanced_void_settings=Продв. уничт. +retro_sophisticated_backpacks.gui.refill_settings=Пополн. +retro_sophisticated_backpacks.gui.advanced_refill_settings=Продв. пополн. +retro_sophisticated_backpacks.gui.compacting_settings=Сжатие +retro_sophisticated_backpacks.gui.advanced_compacting_settings=Продв. сжатие +retro_sophisticated_backpacks.gui.void_always=Уничтожать всегда +retro_sophisticated_backpacks.gui.void_slot_overflow=Уничтожать сверх слота +retro_sophisticated_backpacks.gui.void_storage_overflow=Уничтожать сверх хранилища +retro_sophisticated_backpacks.gui.refill_target_any=Любой слот +retro_sophisticated_backpacks.gui.refill_target_main_hand=Основная рука +retro_sophisticated_backpacks.gui.refill_target_off_hand=Вторая рука +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=Слот 1 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=Слот 2 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=Слот 3 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=Слот 4 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=Слот 5 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=Слот 6 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=Слот 7 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=Слот 8 на хотбаре +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=Слот 9 на хотбаре +retro_sophisticated_backpacks.gui.allow=Допускать +retro_sophisticated_backpacks.gui.block=Исключать +retro_sophisticated_backpacks.gui.match_backpack_contents=Соответствовать содержимому рюкзака +retro_sophisticated_backpacks.gui.pickup_items=Подбирать предметы +retro_sophisticated_backpacks.gui.do_not_pickup_items=Не подбирать предметы +retro_sophisticated_backpacks.gui.compact_only_uncraftable=Сжимать только то, что раскрафчивается +retro_sophisticated_backpacks.gui.compact_anything=Сжимать всё +retro_sophisticated_backpacks.gui.only_automatic=Работать лишь с другими улучшениями и при автоматизации +retro_sophisticated_backpacks.gui.works_in_gui=Работать также при открытом интерфейсе +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=Позволяет заполнять предметами лишь один слот\nи уничтожать лишнее при переполнении +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Позволяет заполнять предметами всё хранилище\nи уничтожать лишнее при переполнении +retro_sophisticated_backpacks.gui.refill_target_tooltip=Пополнять: %s +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=Прокрутите для смены целевого слота +item.retro_sophisticated_backpacks.everlasting_upgrade.name=Улучшение «Вечность» +item.retro_sophisticated_backpacks.jukebox_upgrade.name=Улучшение «Проигрыватель» +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=Продвинутое улучшение «Проигрыватель» +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=Улучшение «Мультиинструмент» +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=Продвинутое улучшение «Мультиинструмент» +item.retro_sophisticated_backpacks.tank_upgrade.name=Улучшение «Резервуар» +item.retro_sophisticated_backpacks.pump_upgrade.name=Улучшение «Помпа» +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=Продвинутое улучшение «Помпа» +item.retro_sophisticated_backpacks.battery_upgrade.name=Улучшение «Аккумулятор» +item.retro_sophisticated_backpacks.anvil_upgrade.name=Улучшение «Наковальня» +retro_sophisticated_backpacks.gui.everlasting_settings=Everlasting +retro_sophisticated_backpacks.gui.jukebox_settings=Проигр. +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Продв. проигр. +retro_sophisticated_backpacks.gui.tool_swapper_settings=Мультиинстр. +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=Мультиинстр. +retro_sophisticated_backpacks.gui.tank_settings=Рез. +retro_sophisticated_backpacks.gui.pump_settings=Помпа +retro_sophisticated_backpacks.gui.advanced_pump_settings=Продв. помпа +retro_sophisticated_backpacks.gui.battery_settings=Аккум. +retro_sophisticated_backpacks.gui.anvil_settings=Наковальня +retro_sophisticated_backpacks.gui.pump_input=Принимать +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Взаимодействовать с жидкостными хранилищами и трубами +retro_sophisticated_backpacks.gui.pump_world=Взаимодействовать с миром +retro_sophisticated_backpacks.gui.pump_hand=Взаимодействовать с резервуаром в руке +retro_sophisticated_backpacks.gui.anvil_no_result=No Result +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift-click result into storage +retro_sophisticated_backpacks.gui.jukebox_play=Проиграть +retro_sophisticated_backpacks.gui.jukebox_stop=Остановить +retro_sophisticated_backpacks.gui.jukebox_previous=Пред. +retro_sophisticated_backpacks.gui.jukebox_next=След. +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Случайный порядок выключен +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Случайный порядок включён +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Повтор всех пластинок +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Повтор одной пластинки +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Повтор выключен +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Any Item +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Auto Swap Tools +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=Назад к рюкзаку +retro_sophisticated_backpacks.gui.memory_settings.tooltip=Настройки запоминания слота +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=Позволяют задавать слоты для запоминания содержимого и учёта точного совпадения предметов в них\nОткройте вкладку для дальнейшей настройки +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=Позволяют задавать слоты для запоминания содержимого и учёта точного совпадения предметов в них\nВыделить все слоты или снять всё выделение = отдельные клавиши\nВыделить слот = нажмите или зажмите ЛКМ\nСнять выделение со слота = нажмите или зажмите ПКМ +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=Настройки игнорирования сортировки +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=Позволяют задавать слоты для их исключения из сортировки\nОткройте вкладку для дальнейшей настройки +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=Позволяют задавать слоты для их исключения из сортировки\nВыделить все слоты или снять всё выделение = отдельные клавиши\nВыделить слот = нажмите или зажмите ЛКМ\nСнять выделение со слота = нажмите или зажмите ПКМ + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=Упаковывает предметы в их сжатые варианты\nПоддерживает рецепты 2×2 и 3×3, а также имеет больше настроек фильтрации +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=Продвинутое улучшение «Разгрузка» +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=Выгружает предметы из рюкзака во внешний инвентарь при нажатии Shift + ПКМ\nИмеет больше настроек фильтрации +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=Продвинутое улучшение «Кормление» +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=Кормит игрока едой из инвентаря рюкзака\nИмеет больше настроек кормления +item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=Продвинутое улучшение «Фильтр» +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=Фильтрует предметы, проходящие через рюкзак при автоматизации\nИмеет больше настроек фильтрации +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=Портативный проигрыватель для большего количества пластинок\nИмеет больше настроек воспроизведения +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=Притягивает предметы в рюкзак на большем расстоянии\nИмеет больше настроек фильтрации +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=Advanced Mob Catcher Upgrade +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=Captures passive and hostile mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=Продвинутое улучшение «Подбор» +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=Позволяет рюкзаку подбирать предметы\nИмеет больше настроек фильтрации +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=Перекачивает жидкости между резервуаром от улучшения и прилегающими блоками\nРаботает с жидкостными контейнерами в руке и блоками в мире\nПозволяет фильтровать перекачиваемые жидкости +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=Поддерживает стак заданных предметов из рюкзака в инвентаре игрока\nПозволяет тонко настраивать целевой слот\nТакже выдаёт блоки из рюкзака при нажатии СКМ +item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=Продвинутое улучшение «Загрузка» +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=Нагружает рюкзак предметами из внешнего инвентаря при нажатии Shift + ПКМ\nИмеет больше настроек фильтрации +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=Автоматически меняет предмет в руке игрока на подходящий для блока/сущности при щелчке ЛКМ\nИмеет параметры фильтра и дополнительное взаимодействие +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=Уничтожает попадающие в рюкзак предметы согласно фильтру\nИмеет больше настроек фильтрации +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=Наковальня во вкладке улучшения +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=Замещает часть инвентаря рюкзака на хранилище энергии +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=Упаковывает предметы в их сжатые варианты\nПоддерживает только рецепты 2×2 +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=Верстак во вкладке улучшения +item.retro_sophisticated_backpacks.deposit_upgrade.name=Улучшение «Разгрузка» +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=Выгружает предметы из рюкзака во внешний инвентарь при нажатии Shift + ПКМ +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=Делает рюкзак неразрушимым\nНе позволяет ему исчезнуть или упасть в пустоту +item.retro_sophisticated_backpacks.feeding_upgrade.name=Улучшение «Кормление» +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=Кормит игрока едой из инвентаря рюкзака +item.retro_sophisticated_backpacks.filter_upgrade.name=Улучшение «Фильтр» +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=Фильтрует предметы, проходящие через рюкзак при автоматизации +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=Позволяет рюкзаку хранить в себе другие рюкзаки +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=Портативный проигрыватель +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=Притягивает предметы в рюкзак на расстоянии +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=Mob Catcher Upgrade +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=Captures passive mobs into backpack storage slots with sneak right-click +item.retro_sophisticated_backpacks.pickup_upgrade.name=Улучшение «Подбор» +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=Позволяет рюкзаку подбирать предметы +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=Перекачивает жидкости между резервуаром от улучшения и прилегающими блоками +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=Поддерживает стак заданных предметов из рюкзака в инвентаре игрока +item.retro_sophisticated_backpacks.restock_upgrade.name=Улучшение «Загрузка» +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=Нагружает рюкзак предметами из внешнего инвентаря при нажатии Shift + ПКМ +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=Умножает количество стаков, помещаемых в слот, на %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=Умножает количество стаков, помещаемых в слот, на %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=Умножает количество стаков, помещаемых в слот, на %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=Умножает количество стаков, помещаемых в слот, на %s +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=Умножает количество стаков, помещаемых в слот, на %s +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=Множители стаков складываются умножением, а не сложением +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=Замещает часть инвентаря рюкзака на хранилище жидкости +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=Автоматически заменяет инструмент в руке игрока на подходящий блоку или сущности при взаимодействии +item.retro_sophisticated_backpacks.void_upgrade.tooltip=Уничтожает попадающие в рюкзак предметы согласно фильтру +retro_sophisticated_backpacks.gui.advanced_deposit_settings=Продв. разгр. +retro_sophisticated_backpacks.gui.advanced_feeding_settings=Продв. корм. +retro_sophisticated_backpacks.gui.advanced_filter_settings=Продв. фильтр +retro_sophisticated_backpacks.gui.advanced_pickup_settings=Продв. подбор +retro_sophisticated_backpacks.gui.advanced_restock_settings=Продв. загр. +retro_sophisticated_backpacks.gui.backpack_settings=Общие настройки +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=Общие настройки +retro_sophisticated_backpacks.gui.blacklist=Исключать +retro_sophisticated_backpacks.gui.complete_hunger=Кормить лишь в том случае, когда игрок достаточно голоден\n(с максимально эффективным расходом потребляемой пищи) +retro_sophisticated_backpacks.gui.consider_health=Кормить игрока при малейшем получении урона\n(с игнорированием настройки кормления при неполном здоровье игрока) +retro_sophisticated_backpacks.gui.crafting_settings=Созд. +retro_sophisticated_backpacks.gui.deposit_settings=Разгр. +retro_sophisticated_backpacks.gui.feeding_settings=Корм. +retro_sophisticated_backpacks.gui.filter_settings=Фильтр +retro_sophisticated_backpacks.gui.half_hunger=Кормить лишь в том случае, когда игрок достаточно голоден\n(с расходом потребляемой пищи до половины единиц насыщения) +retro_sophisticated_backpacks.gui.ignore_durability=Игнорировать прочность +retro_sophisticated_backpacks.gui.ignore_health=Кормить игрока без учёта уровня здоровья\n(c игнорированием здоровья игрока и исключительным учётом настройки кормления) +retro_sophisticated_backpacks.gui.ignore_nbt=Игнорировать NBT +retro_sophisticated_backpacks.gui.immediate_hunger=Кормить игрока при малейшем падении уровня голода\n(с максимально неэффективным расходом потребляемой пищи) +retro_sophisticated_backpacks.gui.inception_settings=Внедрение +retro_sophisticated_backpacks.gui.input=Принимать +retro_sophisticated_backpacks.gui.input_output=Принимать и отдавать +retro_sophisticated_backpacks.gui.item_display_settings=Отображ. +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=Настройки отображения предмета +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=Позволяют задавать слот под показ находящегося в нём предмета на модели хранилища\nОткройте вкладку для дальнейшей настройки +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=Позволяют задавать слот под показ находящегося в нём предмета на модели хранилища\nВыбрать слот = нажмите или зажмите ЛКМ\nСнять выделение со слота = нажмите или зажмите ПКМ +retro_sophisticated_backpacks.gui.lock_all_sort=Выделить все слоты +retro_sophisticated_backpacks.gui.match_durability=Соответствовать прочности +retro_sophisticated_backpacks.gui.match_item=Соответствовать предметам +retro_sophisticated_backpacks.gui.match_mod_id=Соответствовать списку модов +retro_sophisticated_backpacks.gui.match_nbt=Соответствовать NBT +retro_sophisticated_backpacks.gui.memorize_all=Выделить все слоты +retro_sophisticated_backpacks.gui.memory_settings=Запом. +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=Click to release +retro_sophisticated_backpacks.gui.output=Отдавать +retro_sophisticated_backpacks.gui.pickup_settings=Подбор +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=Не взаимодействовать с жидкостными хранилищами и трубами +retro_sophisticated_backpacks.gui.pump_no_hand=Не взаимодействовать с резервуаром в руке +retro_sophisticated_backpacks.gui.pump_no_world=Не взаимодействовать с миром +retro_sophisticated_backpacks.gui.pump_output=Отдавать +retro_sophisticated_backpacks.gui.restock_settings=Загр. +retro_sophisticated_backpacks.gui.search=Нажмите ЛКМ для поиска +retro_sophisticated_backpacks.gui.search_detail=Добавьте @ для поиска по имени мода +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=Запретить открывать другим игрокам +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=Другой игрок никак не сможет открыть рюкзак владельца (даже надетый и видимый), нажав ПКМ по чужой спине +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=Разрешить открывать другим игрокам +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=При надетом на владельце видимом рюкзаке другой игрок сможет открыть его, нажав ПКМ по чужой спине +retro_sophisticated_backpacks.gui.settings_button.context_backpack=Рюкзак +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=Настройки на уровне данного рюкзака +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=Наследуются от игрока или переназначаются для данного рюкзака +retro_sophisticated_backpacks.gui.settings_button.context_player=Игрок +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=Настройки на уровне игрока +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=Применяются ко всем рюкзакам или хранилищам до переназначения +retro_sophisticated_backpacks.gui.settings_button.display_side_front=Отобразить спереди +retro_sophisticated_backpacks.gui.settings_button.display_side_left=Отобразить слева +retro_sophisticated_backpacks.gui.settings_button.display_side_right=Отобразить справа +retro_sophisticated_backpacks.gui.settings_button.item_display_color=Задать цвет +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=Следующий цвет = ЛКМ\nПредыдущий цвет = ПКМ +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Сохранять поисковый запрос: ВЫКЛ. +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=Интерфейс рюкзака или хранилища очистит поисковый запрос при закрытии и отобразит все неотфильтрованные предметы при повторном открытии +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Сохранять поисковый запрос: ВКЛ. +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=Интерфейс рюкзака или хранилища сохранит и удержит поисковый запрос, а также отфильтрует результат при повторном открытии +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=Держать вкладку открытой: ВЫКЛ. +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=Интерфейс рюкзака или хранилища закроет последнюю открытую вкладку улучшения при выходе из настроек и будет держать все вкладки свёрнутыми при повторном открытии +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=Держать вкладку открытой: ВКЛ. +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=Интерфейс рюкзака или хранилища запомнит последнюю открытую вкладку улучшения при выходе из настроек и автоматически развернёт её при повторном открытии +retro_sophisticated_backpacks.gui.settings_button.rotate=Повернуть +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=По часовой стрелке = ЛКМ\nПротив часовой стрелки = ПКМ +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Нажатие с Shift в приоритете для инвентарей +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=Стак предметов при нажатии с Shift сперва перейдёт в инвентарь игрока или хранилища, а затем — в открытую вкладку +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Нажатие с Shift в приоритете для вкладок +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=Стак предметов при нажатии c Shift сперва перейдёт в открытую вкладку, а затем — в инвентарь игрока или хранилища +retro_sophisticated_backpacks.gui.sort_by_count=По количеству +retro_sophisticated_backpacks.gui.sort_by_mod_id=По моду +retro_sophisticated_backpacks.gui.sort_by_name=По имени +retro_sophisticated_backpacks.gui.sort_by_ore_dict=По тегам +retro_sophisticated_backpacks.gui.sort_inventory=Сортировать инвентарь +retro_sophisticated_backpacks.gui.sorting_settings=Игнор. сорт. +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=That mob is blocked from capture +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=Bosses cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=Captured %s +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=Release captured mobs before removing Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=Hostile mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=That mob cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=Mobs with inventories cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=Captured mobs require Advanced Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=No valid release space there +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=No empty %sx%s slot area available +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=Backpack has no Mob Catcher Upgrade +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=Only the owner can capture that mob +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=Only one Mob Catcher Upgrade is allowed per backpack +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=Mobs with passengers or vehicles cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=Players cannot be captured +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=Could not release mob there +retro_sophisticated_backpacks.gui.status.mob_catcher_released=Released %s +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=Mob needs %s slots, more than this upgrade allows (%s) +retro_sophisticated_backpacks.gui.tooltip.stack_count=Количество предметов: %s +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Перенести всё в хранилище +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Перенести похожее в хранилище +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Зажмите Shift, чтобы перенести всё +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Перенести всё в инвентарь +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Перенести похожее в инвентарь +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Зажмите Shift, чтобы перенести всё +retro_sophisticated_backpacks.gui.unlock_all_sort=Снять выделение со всех слотов +retro_sophisticated_backpacks.gui.unmemorize_all=Снять выделение со всех слотов +retro_sophisticated_backpacks.gui.whitelist=Допускать + +# Fallbacks for keys used by current code +retro_sophisticated_backpacks.key.open_backpack.desc=Opens Backpack in Inventory +retro_sophisticated_backpacks.key.category=Retro Sophisticated Backpacks +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.stack_size_extra=Count: %s / %s +retro_sophisticated_backpacks.gui.not_in_effect=Not in effect +retro_sophisticated_backpacks.gui.match_ore_dict=By Ore Dictionary +retro_sophisticated_backpacks.gui.add_ore_dict_entry=Add Ore Dict. +retro_sophisticated_backpacks.gui.remove_ore_dict_entry=Remove Ore Dict. +retro_sophisticated_backpacks.gui.ore_dict_input_help=You can input ore dictionary here,\nregex is supported. +retro_sophisticated_backpacks.gui.ore_dict_input_help.pro_tip=Protip: You can hover item onto entry\n to check if the entry is applicable,\nor hover on textfield to see available entries. +retro_sophisticated_backpacks.gui.ore_dict_list_entries=Available ore dict: +retro_sophisticated_backpacks.gui.none=(None) +retro_sophisticated_backpacks.gui.craft_into_backpack=Shift Click Result Into Backpack +retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift Click Result Into Player's Inventory +retro_sophisticated_backpacks.gui.settings=Settings +retro_sophisticated_backpacks.gui.configuration_tab=Configuration Tab +retro_sophisticated_backpacks.gui.memorized_slot=Memorized Slot +retro_sophisticated_backpacks.gui.no_sorting_slot=No Sorting Slot +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_cn.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_cn.lang index 5a273d7..077ba4f 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_cn.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_cn.lang @@ -31,81 +31,113 @@ itemGroup.retro_sophisticated_backpacks.creative_tab=复古精妙背包 # Keybinds retro_sophisticated_backpacks.key.open_backpack.desc=在物品栏中打开背包 +retro_sophisticated_backpacks.key.tool_swap.desc=基于所指方块/实体替换工具 retro_sophisticated_backpacks.key.category=复古精妙背包 # Tooltips retro_sophisticated_backpacks.tooltip.backpack.inventory_size=背包容量:%d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=升级插槽:%d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> 堆叠倍数:%d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=堆叠倍数:%s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=空罐 +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=流体 +retro_sophisticated_backpacks.tooltip.backpack.energy_title=能量 +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=升级 +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=物品 +retro_sophisticated_backpacks.tooltip.backpack.empty=未加装升级或物品存储 +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=按<%s>查看内容 +retro_sophisticated_backpacks.tooltip.backpack.shift=左Shift -retro_sophisticated_backpacks.tooltip.upgrade_base=没有任何效果!大可放进升级插槽里! -retro_sophisticated_backpacks.tooltip.stack_upgrade=堆叠上限×%d -retro_sophisticated_backpacks.tooltip.exponential_stack_upgrade=堆叠倍数将按乘算而非加算计算 -retro_sophisticated_backpacks.tooltip.crafting_upgrade=在背包旁添加额外的合成方格 -retro_sophisticated_backpacks.tooltip.inception_upgrade=可套娃存放背包 -retro_sophisticated_backpacks.tooltip.pickup_upgrade=能够让背包捡起物品 -retro_sophisticated_backpacks.tooltip.advanced_pickup_upgrade=能够让背包捡起物品,拥有更多过滤选项 -retro_sophisticated_backpacks.tooltip.feeding_upgrade=能够让背包自动喂食玩家 -retro_sophisticated_backpacks.tooltip.advanced_feeding_upgrade=能够让背包自动喂食玩家,拥有更多过滤选项 -retro_sophisticated_backpacks.tooltip.deposit_upgrade=手持背包,潜行右击容器以卸载物品 -retro_sophisticated_backpacks.tooltip.advanced_deposit_upgrade=手持背包,潜行右击容器以卸载物品,拥有更多过滤选项 -retro_sophisticated_backpacks.tooltip.restock_upgrade=手持背包,潜行右击容器以提取物品 -retro_sophisticated_backpacks.tooltip.advanced_restock_upgrade=手持背包,潜行右击容器以提取物品,拥有更多过滤选项 -retro_sophisticated_backpacks.tooltip.filter_upgrade=放置在世界中时,过滤输入/输出背包的物品 -retro_sophisticated_backpacks.tooltip.advanced_filter_upgrade=放置在世界中时,过滤输入/输出背包的物品,拥有更多配置项 -retro_sophisticated_backpacks.tooltip.shift_to_reveal=<按Shift显示> # Gui Elements retro_sophisticated_backpacks.container.backpack=背包 -retro_sophisticated_backpacks.gui.pickup_settings=拾取设置 -retro_sophisticated_backpacks.gui.advanced_pickup_settings=高级拾取设置 +retro_sophisticated_backpacks.gui.pickup_settings=拾取 +retro_sophisticated_backpacks.gui.advanced_pickup_settings=高级拾取 -retro_sophisticated_backpacks.gui.feeding_settings=喂食设置 -retro_sophisticated_backpacks.gui.advanced_feeding_settings=高级喂食设置 +retro_sophisticated_backpacks.gui.feeding_settings=喂食 +retro_sophisticated_backpacks.gui.advanced_feeding_settings=高级喂食 -retro_sophisticated_backpacks.gui.deposit_settings=卸货设置 -retro_sophisticated_backpacks.gui.advanced_deposit_settings=高级卸货设置 +retro_sophisticated_backpacks.gui.deposit_settings=卸货 +retro_sophisticated_backpacks.gui.advanced_deposit_settings=高级卸货 -retro_sophisticated_backpacks.gui.restock_settings=取货设置 -retro_sophisticated_backpacks.gui.advanced_restock_settings=高级取货设置 +retro_sophisticated_backpacks.gui.restock_settings=取货 +retro_sophisticated_backpacks.gui.advanced_restock_settings=高级取货 -retro_sophisticated_backpacks.gui.filter_settings=过滤设置 -retro_sophisticated_backpacks.gui.advanced_filter_settings=高级过滤设置 +retro_sophisticated_backpacks.gui.filter_settings=过滤 +retro_sophisticated_backpacks.gui.advanced_filter_settings=高级过滤 retro_sophisticated_backpacks.gui.crafting_settings=合成 - -retro_sophisticated_backpacks.gui.memory_settings=记忆设置 -retro_sophisticated_backpacks.gui.sorting_settings=整理设置 +retro_sophisticated_backpacks.gui.memory_settings=记忆 +retro_sophisticated_backpacks.gui.sorting_settings=忽略整理 +retro_sophisticated_backpacks.gui.backpack_settings=背包设置 +retro_sophisticated_backpacks.gui.item_display_settings=物品显示 +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=背包设置 +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=配置背包行为\n打开标签页以修改背包设置 +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=配置背包行为\n上下文=选择修改玩家设置或背包设置\n按钮用于切换 Shift 点击、标签页、搜索和访问行为 +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=物品显示设置 +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=选择一个槽位,让物品得以在容器顶端显示 +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=选择一个槽位,让物品得以在容器顶端显示\n选择槽位=左键单击/拖动\n取消选择槽位=右键单击/拖动 +retro_sophisticated_backpacks.gui.search=点击以搜索 +retro_sophisticated_backpacks.gui.search_detail=使用前缀@以按模组名称搜索 +retro_sophisticated_backpacks.gui.settings_button.context_player=玩家设置 +retro_sophisticated_backpacks.gui.settings_button.context_backpack=背包设置 +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=玩家全局设置 +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=应用于玩家的所有背包/容器,被单独覆写的除外 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=该背包的设置 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=不受到玩家全局设置影响 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Shift左击优先放入升级标签页 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Shift左击优先放入物品栏 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=保持标签页打开: 开 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=保持标签页打开: 关 +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=保留搜索内容:开 +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=保留搜索内容:关 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=其他玩家可以打开 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=其他玩家不可打开 +retro_sophisticated_backpacks.gui.settings_button.rotate=旋转 +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=顺时针=左击\n逆时针=右击 +retro_sophisticated_backpacks.gui.settings_button.item_display_color=更换颜色 +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=上一种=左击\n下一种=右击 +retro_sophisticated_backpacks.gui.settings_button.display_side_front=显示在正面 +retro_sophisticated_backpacks.gui.settings_button.display_side_left=显示在左侧 +retro_sophisticated_backpacks.gui.settings_button.display_side_right=显示在右侧 +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=左键选择下一个侧面\n右键选择上一个侧面 +retro_sophisticated_backpacks.gui.memory_settings.tooltip=槽位记忆设置 +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=使所选槽位记忆当前物品,该槽位只会接收对应物品\n打开选项卡以修改槽位设置 +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=使所选槽位记忆当前物品,该槽位只会接收对应物品\n全选槽位/取消全选槽位=按钮\n选择槽位=左键单击/拖动\n取消选择槽位=右键单击/拖动 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=忽略整理设置 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=选择忽略整理的槽位\n打开标签页以修改此设置 +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=选择忽略整理的槽位\n全选/取消全选槽位=按钮\n选择槽位=左键单击/拖动\n取消选择槽位=右键单击/拖动 # Gui Tooltips retro_sophisticated_backpacks.gui.stack_size_extra=数量:%s / %s retro_sophisticated_backpacks.gui.not_in_effect=未生效 -retro_sophisticated_backpacks.gui.whitelist=白名单 -retro_sophisticated_backpacks.gui.blacklist=黑名单 +retro_sophisticated_backpacks.gui.whitelist=允许 +retro_sophisticated_backpacks.gui.blacklist=阻止 -retro_sophisticated_backpacks.gui.match_item=按物品 -retro_sophisticated_backpacks.gui.match_mod_id=按模组ID +retro_sophisticated_backpacks.gui.match_item=匹配物品 +retro_sophisticated_backpacks.gui.match_mod_id=匹配模组 retro_sophisticated_backpacks.gui.match_ore_dict=按矿物词典 -retro_sophisticated_backpacks.gui.match_durability=匹配耐久度 -retro_sophisticated_backpacks.gui.ignore_durability=忽略耐久度 +retro_sophisticated_backpacks.gui.match_durability=匹配耐久 +retro_sophisticated_backpacks.gui.ignore_durability=忽略耐久 retro_sophisticated_backpacks.gui.match_nbt=匹配NBT retro_sophisticated_backpacks.gui.ignore_nbt=忽略NBT @@ -125,32 +157,211 @@ retro_sophisticated_backpacks.gui.ignore_health=忽略生命值\n忽略玩家的 retro_sophisticated_backpacks.gui.consider_health=当玩家受伤时立即喂食\n在玩家未达到最大生命值前忽略饥饿值 retro_sophisticated_backpacks.gui.input_output=输入&输出 -retro_sophisticated_backpacks.gui.input=仅输入 -retro_sophisticated_backpacks.gui.output=仅输出 +retro_sophisticated_backpacks.gui.input=输入 +retro_sophisticated_backpacks.gui.output=输出 -retro_sophisticated_backpacks.gui.sort_inventory=整理背包 -retro_sophisticated_backpacks.gui.sort_by_name=按名称整理 -retro_sophisticated_backpacks.gui.sort_by_mod_id=按模组ID整理 -retro_sophisticated_backpacks.gui.sort_by_count=按数量整理 -retro_sophisticated_backpacks.gui.sort_by_ore_dict=按矿物词典整理 +retro_sophisticated_backpacks.gui.sort_inventory=整理物品栏 +retro_sophisticated_backpacks.gui.sort_by_name=按名字 +retro_sophisticated_backpacks.gui.sort_by_mod_id=按模组 +retro_sophisticated_backpacks.gui.sort_by_count=按数量 +retro_sophisticated_backpacks.gui.sort_by_ore_dict=按标签 retro_sophisticated_backpacks.gui.craft_into_backpack=Shift点击合成产物转移至背包 retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift点击合成产物转移至玩家物品栏 -retro_sophisticated_backpacks.gui.transfer_to_player_inv=转移所有物品至玩家物品栏 -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=转移匹配物品至玩家物品栏 +retro_sophisticated_backpacks.gui.transfer_to_player_inv=转移至物品栏 +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=转移匹配物品至物品栏 retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=按住Shift以转移所有物品 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=转移所有物品至背包物品栏 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=转移匹配物品至背包物品栏 +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=转移至容器 +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=转移匹配物品至容器 retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=按住Shift以转移所有物品 retro_sophisticated_backpacks.gui.settings=设置 +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=返回背包 retro_sophisticated_backpacks.gui.configuration_tab=设置标签页 retro_sophisticated_backpacks.gui.memorized_slot=被记忆的槽位 -retro_sophisticated_backpacks.gui.memorize_all=记忆所有槽位 -retro_sophisticated_backpacks.gui.unmemorize_all=遗忘所有槽位 +retro_sophisticated_backpacks.gui.memorize_all=全选槽位 +retro_sophisticated_backpacks.gui.unmemorize_all=取消全选槽位 retro_sophisticated_backpacks.gui.no_sorting_slot=禁止整理的槽位 -retro_sophisticated_backpacks.gui.lock_all_sort=禁止整理所有槽位 -retro_sophisticated_backpacks.gui.unlock_all_sort=允许整理所有槽位 +retro_sophisticated_backpacks.gui.lock_all_sort=全选槽位 +retro_sophisticated_backpacks.gui.unlock_all_sort=取消全选槽位 + +item.retro_sophisticated_backpacks.magnet_upgrade.name=磁铁升级 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=高级磁铁升级 +item.retro_sophisticated_backpacks.void_upgrade.name=虚空升级 +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=高级虚空升级 +item.retro_sophisticated_backpacks.refill_upgrade.name=补货升级 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=高级补货升级 +item.retro_sophisticated_backpacks.compacting_upgrade.name=压制升级 +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=高级压制升级 +retro_sophisticated_backpacks.gui.magnet_settings=磁铁 +retro_sophisticated_backpacks.gui.advanced_magnet_settings=高级磁铁 +retro_sophisticated_backpacks.gui.void_settings=虚空 +retro_sophisticated_backpacks.gui.advanced_void_settings=高级虚空 +retro_sophisticated_backpacks.gui.refill_settings=补货 +retro_sophisticated_backpacks.gui.advanced_refill_settings=高级补货 +retro_sophisticated_backpacks.gui.compacting_settings=压缩 +retro_sophisticated_backpacks.gui.advanced_compacting_settings=高级压缩 +retro_sophisticated_backpacks.gui.void_always=总是销毁 +retro_sophisticated_backpacks.gui.void_slot_overflow=销毁槽位过量 +retro_sophisticated_backpacks.gui.void_storage_overflow=销毁存储过量 +retro_sophisticated_backpacks.gui.refill_target_any=任意槽位 +retro_sophisticated_backpacks.gui.refill_target_main_hand=主手 +retro_sophisticated_backpacks.gui.refill_target_off_hand=副手 +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=快捷栏1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=快捷栏2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=快捷栏3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=快捷栏4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=快捷栏5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=快捷栏6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=快捷栏7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=快捷栏8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=快捷栏9 +retro_sophisticated_backpacks.gui.allow=允许 +retro_sophisticated_backpacks.gui.block=阻止 +retro_sophisticated_backpacks.gui.match_backpack_contents=匹配背包已有物品 +retro_sophisticated_backpacks.gui.pickup_items=吸取物品 +retro_sophisticated_backpacks.gui.do_not_pickup_items=不吸取物品 +retro_sophisticated_backpacks.gui.compact_only_uncraftable=仅压缩可逆配方 +retro_sophisticated_backpacks.gui.compact_anything=压缩任意配方 +retro_sophisticated_backpacks.gui.only_automatic=仅与其他升级/自动化方式(如漏斗)配合工作 +retro_sophisticated_backpacks.gui.works_in_gui=在GUI中工作 +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=允许一个槽位填满物品\n销毁所有过量物品 +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=允许整体存储空间填满物品\n销毁所有过量物品 +retro_sophisticated_backpacks.gui.refill_target_tooltip=补充%s +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=用滚轮改变槽位 +item.retro_sophisticated_backpacks.everlasting_upgrade.name=永恒升级 +item.retro_sophisticated_backpacks.jukebox_upgrade.name=唱片机升级 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=高级唱片机升级 +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=工具替换升级 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=高级工具替换升级 +item.retro_sophisticated_backpacks.tank_upgrade.name=储罐升级 +item.retro_sophisticated_backpacks.pump_upgrade.name=液泵升级 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=高级液泵升级 +item.retro_sophisticated_backpacks.battery_upgrade.name=电池升级 +item.retro_sophisticated_backpacks.anvil_upgrade.name=铁砧升级 +retro_sophisticated_backpacks.gui.everlasting_settings=永恒 +retro_sophisticated_backpacks.gui.jukebox_settings=唱片机 +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=唱片机 +retro_sophisticated_backpacks.gui.tool_swapper_settings=工具替换 +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=工具替换 +retro_sophisticated_backpacks.gui.tank_settings=储罐 +retro_sophisticated_backpacks.gui.pump_settings=液泵 +retro_sophisticated_backpacks.gui.advanced_pump_settings=高级液泵 +retro_sophisticated_backpacks.gui.battery_settings=电池 +retro_sophisticated_backpacks.gui.anvil_settings=铁砧 +retro_sophisticated_backpacks.gui.pump_input=输入 +retro_sophisticated_backpacks.gui.pump_input_detail=将流体抽入背包储罐 +retro_sophisticated_backpacks.gui.pump_output=输出 +retro_sophisticated_backpacks.gui.pump_output_detail=将流体从背包储罐泵出 +retro_sophisticated_backpacks.gui.pump_fluid_handlers=与储罐&管道交互 +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=允许通过相邻流体处理器移动流体 +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=不与与储罐&管道交互 +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=禁止通过相邻流体处理器移动流体 +retro_sophisticated_backpacks.gui.pump_world=与世界中方块交互 +retro_sophisticated_backpacks.gui.pump_world_detail=允许在世界中放置或抽取流体 +retro_sophisticated_backpacks.gui.pump_no_world=不与世界中方块交互 +retro_sophisticated_backpacks.gui.pump_no_world_detail=禁止在世界中放置或抽取流体 +retro_sophisticated_backpacks.gui.pump_hand=与手持流体容器交互 +retro_sophisticated_backpacks.gui.pump_hand_detail=允许填充或抽取玩家手持容器 +retro_sophisticated_backpacks.gui.pump_no_hand=不与手持流体容器交互 +retro_sophisticated_backpacks.gui.pump_no_hand_detail=禁止填充或抽取玩家手持容器 +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=手持流体容器点击可设置该过滤\n空手点击可清除 +retro_sophisticated_backpacks.gui.anvil_no_result=无结果 +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift 点击背包存储 +retro_sophisticated_backpacks.gui.jukebox_play=播放 +retro_sophisticated_backpacks.gui.jukebox_stop=停止 +retro_sophisticated_backpacks.gui.jukebox_previous=上一首 +retro_sophisticated_backpacks.gui.jukebox_next=下一首 +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=关闭随机播放 +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=启用随机播放 +retro_sophisticated_backpacks.gui.jukebox_repeat_all=全部循环 +retro_sophisticated_backpacks.gui.jukebox_repeat_one=单曲循环 +retro_sophisticated_backpacks.gui.jukebox_repeat_no=关闭循环 +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=不切换武器 +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled.detail=攻击实体时保留手持物品 +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=切换武器 +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled.detail=攻击实体时切换为最合适的武器 +retro_sophisticated_backpacks.gui.tool_swapper_any=替换工具 +retro_sophisticated_backpacks.gui.tool_swapper_any.detail=手持武器或工具时,将合适的工具替换到手上 +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=只由工具替换工具 +retro_sophisticated_backpacks.gui.tool_swapper_only_tools.detail=仅在手持工具时替换,手持武器不替换 +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=不替换工具 +retro_sophisticated_backpacks.gui.tool_swapper_no_swap.detail=永远不会将工具替换到手上 +retro_sophisticated_backpacks.gui.status.unable_to_swap_tool_for_backpack=手持背包时无法替换工具。切换为空手或切换至其他物品 +retro_sophisticated_backpacks.gui.status.no_tool_found_for_block=没有适用于该方块的工具 +retro_sophisticated_backpacks.gui.status.no_tool_found_for_entity=没有适用于该实体的工具 +retro_sophisticated_backpacks.gui.status.no_tool_swap_upgrade_present=当前没有可进行手动工具替换的升级卡 +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=生物捕捉升级 +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=高级生物捕捉升级 +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=点击释放 +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=释放已捕捉的生物后才能移除生物捕捉升级 +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=已捕捉的生物需要高级生物捕捉升级 +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=每个背包只能安装一个生物捕捉升级 +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=已捕捉%s +retro_sophisticated_backpacks.gui.status.mob_catcher_released=已释放%s +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=背包没有生物捕捉升级 +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=该生物不能被捕捉 +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=不能捕捉玩家 +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=不能捕捉Boss +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=不能捕捉带有乘客或载具关系的生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=该生物被禁止捕捉 +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=只有所有者可以捕捉该生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=不能捕捉带有物品栏的生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=敌对生物需要高级生物捕捉升级 +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=该生物需要%s个槽位,超过此升级允许的上限(%s) +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=没有空余的%sx%s槽位区域 +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=无法在那里释放生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=那里没有有效的释放空间 + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=将物品压制为对应的压缩变体\n包括2x2,3x3配方以及更多的过滤选项 +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=手持背包 Shift 右击容器卸载物品\n更多的过滤选项 +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=将背包内的食物喂给玩家\n更多喂食条件选项 +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=过滤输入/输出背包的物品\n更多的过滤选项 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=支持更多唱片的便携唱片机\n当然还有更多播放选项 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=将更大范围内的物品吸入背包\n更多的过滤选项 +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=潜行右击可将被动和敌对生物捕捉到背包存储槽位中 +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=使背包捡起物品\n更多的过滤选项 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=在储罐升级和相邻方块之间泵送流体\n适用于手持流体容器和世界中的流体方块\n允许过滤泵送的流体 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=使所选物品在玩家物品栏中自动补充至一组\n更精准的目标槽位选择\n可使用鼠标中键从背包中选取方块 +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=手持背包潜行右击容器提取物品\n更多的过滤选项 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=左击时自动将玩家手持物品替换为对应方块/实体的挖掘或攻击工具\n具有过滤选项,并可使用工具替换按键手动替换 +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=销毁过滤器中选定的物品\n更多的过滤选项 +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=在升级标签页中添加一个铁砧 +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=将部分背包空间替换为能量存储空间 +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=将物品压制为对应的压缩变体\n仅2x2配方 +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=在升级标签页中添加一个工作台 +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=手持背包潜行右击容器卸载物品 +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=使背包变得坚不可摧\n不会消失及掉入虚空 +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=堆叠倍数将按乘算而非加算计算 +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=将背包内的食物喂给玩家 +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=过滤输入/输出背包的物品 +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=使背包套背包成为可能 +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=便携唱片机 +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=将一定范围内的物品吸入背包 +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=潜行右击可将被动生物捕捉到背包存储槽位中 +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=能够让背包捡起物品 +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=在储罐升级和相邻方块之间泵送流体 +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=使所选物品在玩家物品栏中自动补充至一组 +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=手持背包潜行右击容器提取物品 +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=单个槽位可容纳的堆叠数乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=单个槽位可容纳的堆叠数乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=单个槽位可容纳的堆叠数乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=单个槽位可容纳的堆叠数乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=单个槽位可容纳的堆叠数乘以%s +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=将部分背包空间替换为流体存储空间 +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=左击时自动将玩家手持物品替换为对应方块/实体的挖掘或攻击工具 +item.retro_sophisticated_backpacks.void_upgrade.tooltip=销毁过滤器中选定的物品 +retro_sophisticated_backpacks.gui.inception_settings=嵌套 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=即使该背包已被穿戴,其他玩家也无法通过对该玩家后背右击打开它 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=当该背包被穿戴且设置为可见时,其他玩家可以对该玩家后背右击打开它 +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=关闭背包/容器GUI后将清空搜索内容,重新打开时显示所有未过滤的物品 +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=背包/容器GUI会保留搜索内容,重新打开时会自动填充并根据搜索词过滤物品 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=关闭背包/容器时关闭所有标签页 +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=关闭背包/容器时保存升级标签页的状态 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=Shift左击物品优先放入背包/容器物品栏 +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=Shift左击物品优先放入打开的升级标签页 +retro_sophisticated_backpacks.gui.tooltip.stack_count=数量:%s diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_tw.lang b/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_tw.lang index 9f90e90..d1c66c8 100644 --- a/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_tw.lang +++ b/src/main/resources/assets/retro_sophisticated_backpacks/lang/zh_tw.lang @@ -7,24 +7,24 @@ tile.retro_sophisticated_backpacks.backpack_obsidian.name=黑曜石背包 # Items item.retro_sophisticated_backpacks.upgrade_base.name=基礎升級 -item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=初級堆疊升級 -item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=堆疊升級 T1 -item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=堆疊升級 T2 -item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=堆疊升級 T3 -item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=堆疊升級 T4 +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.name=Stack Upgrade Starter Tier +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.name=堆疊升級T1 +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.name=堆疊升級T2 +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.name=堆疊升級T3 +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.name=堆疊升級T4 item.retro_sophisticated_backpacks.exponential_stack_upgrade.name=指數堆疊升級 item.retro_sophisticated_backpacks.crafting_upgrade.name=合成升級 item.retro_sophisticated_backpacks.inception_upgrade.name=嵌套升級 item.retro_sophisticated_backpacks.pickup_upgrade.name=拾取升級 -item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=進階拾取升級 +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.name=高級拾取升級 item.retro_sophisticated_backpacks.feeding_upgrade.name=餵食升級 -item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=進階餵食升級 +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.name=高級餵食升級 item.retro_sophisticated_backpacks.deposit_upgrade.name=卸貨升級 -item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=進階卸貨升級 +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.name=高級卸貨升級 item.retro_sophisticated_backpacks.restock_upgrade.name=取貨升級 -item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=進階取貨升級 +item.retro_sophisticated_backpacks.advanced_restock_upgrade.name=高級取貨升級 item.retro_sophisticated_backpacks.filter_upgrade.name=過濾升級 -item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=進階過濾升級 +item.retro_sophisticated_backpacks.advanced_filter_upgrade.name=高級過濾升級 # Creative Tabs itemGroup.retro_sophisticated_backpacks.creative_tab=復古精緻背包 @@ -36,82 +36,82 @@ retro_sophisticated_backpacks.key.category=復古精緻背包 # Tooltips retro_sophisticated_backpacks.tooltip.backpack.inventory_size=背包容量: %d retro_sophisticated_backpacks.tooltip.backpack.upgrade_slots_size=升級插槽: %d -retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=-> 當前堆疊倍率: %d %s +retro_sophisticated_backpacks.tooltip.backpack.stack_multiplier=Stack Size Multiplier: %s +retro_sophisticated_backpacks.tooltip.backpack.fluid=%s mB %s +retro_sophisticated_backpacks.tooltip.backpack.fluid_empty=Empty tank +retro_sophisticated_backpacks.tooltip.backpack.energy=%s FE +retro_sophisticated_backpacks.tooltip.backpack.fluid_title=流體 +retro_sophisticated_backpacks.tooltip.backpack.energy_title=能量 +retro_sophisticated_backpacks.tooltip.backpack.upgrades_title=Upgrades +retro_sophisticated_backpacks.tooltip.backpack.inventory_title=Inventory +retro_sophisticated_backpacks.tooltip.backpack.empty=No Upgrades or Inventory Contents +retro_sophisticated_backpacks.tooltip.backpack.press_for_contents=Press <%s> to View Contents +retro_sophisticated_backpacks.tooltip.backpack.shift=Left Shift -retro_sophisticated_backpacks.tooltip.upgrade_base=沒有效果!你大可放在升級插槽! -retro_sophisticated_backpacks.tooltip.stack_upgrade=提供堆疊倍率 x%d -retro_sophisticated_backpacks.tooltip.exponential_stack_upgrade=堆疊倍率會以相乘堆疊而非相加 -retro_sophisticated_backpacks.tooltip.crafting_upgrade=在背包旁增加額外的合成欄 -retro_sophisticated_backpacks.tooltip.inception_upgrade=允許將背包放入背包 -retro_sophisticated_backpacks.tooltip.pickup_upgrade=允許背包拾取並存入背包 -retro_sophisticated_backpacks.tooltip.advanced_pickup_upgrade=允許背包根據更多配置拾取並存入背包 -retro_sophisticated_backpacks.tooltip.feeding_upgrade=允許背包自動餵食玩家 -retro_sophisticated_backpacks.tooltip.advanced_feeding_upgrade=允許背包根據更多配置自動餵食玩家 -retro_sophisticated_backpacks.tooltip.deposit_upgrade=將背包物品卸貨至潛行右鍵的容器 -retro_sophisticated_backpacks.tooltip.advanced_deposit_upgrade=根據更多配置將背包物品卸貨至潛行右鍵的容器 -retro_sophisticated_backpacks.tooltip.restock_upgrade=將潛行右鍵的容器物品取貨至背包 -retro_sophisticated_backpacks.tooltip.advanced_restock_upgrade=根據更多配置將潛行右鍵的容器物品取貨至背包 -retro_sophisticated_backpacks.tooltip.filter_upgrade=當放置於世界時過濾輸入的物品 -retro_sophisticated_backpacks.tooltip.advanced_filter_upgrade=當放置於世界時根據更多配置過濾輸入的物品 -retro_sophisticated_backpacks.tooltip.shift_to_reveal=<按 Shift 顯示> # Gui Elements retro_sophisticated_backpacks.container.backpack=背包 -retro_sophisticated_backpacks.gui.pickup_settings=拾取設定 -retro_sophisticated_backpacks.gui.advanced_pickup_settings=進階拾取設定 +retro_sophisticated_backpacks.gui.pickup_settings=Pickup +retro_sophisticated_backpacks.gui.advanced_pickup_settings=Adv. Pickup -retro_sophisticated_backpacks.gui.feeding_settings=餵食設定 -retro_sophisticated_backpacks.gui.advanced_feeding_settings=進階餵食設定 +retro_sophisticated_backpacks.gui.feeding_settings=Feeding +retro_sophisticated_backpacks.gui.advanced_feeding_settings=Adv. Feeding -retro_sophisticated_backpacks.gui.deposit_settings=卸貨設定 -retro_sophisticated_backpacks.gui.advanced_deposit_settings=進階卸貨設定 +retro_sophisticated_backpacks.gui.deposit_settings=卸貨 +retro_sophisticated_backpacks.gui.advanced_deposit_settings=高級卸貨 -retro_sophisticated_backpacks.gui.restock_settings=取貨設定 -retro_sophisticated_backpacks.gui.advanced_restock_settings=進階取貨設定 +retro_sophisticated_backpacks.gui.restock_settings=取貨 +retro_sophisticated_backpacks.gui.advanced_restock_settings=高級取貨 -retro_sophisticated_backpacks.gui.filter_settings=過濾設定 -retro_sophisticated_backpacks.gui.advanced_filter_settings=進階過濾設定 +retro_sophisticated_backpacks.gui.filter_settings=Filter +retro_sophisticated_backpacks.gui.advanced_filter_settings=Adv. Filter -retro_sophisticated_backpacks.gui.crafting_settings=合成 +retro_sophisticated_backpacks.gui.crafting_settings=Craft -retro_sophisticated_backpacks.gui.memory_settings=記憶設定 -retro_sophisticated_backpacks.gui.sorting_settings=整理設定 +retro_sophisticated_backpacks.gui.memory_settings=Memory +retro_sophisticated_backpacks.gui.sorting_settings=No Sort +retro_sophisticated_backpacks.gui.memory_settings.tooltip=Slot Memory Settings +retro_sophisticated_backpacks.gui.memory_settings.tooltip_detail=Allows selecting slots that remember their contents and only allow matching stacks in them\nOpen tab to modify slot settings +retro_sophisticated_backpacks.gui.memory_settings.tooltip_open_detail=Allows selecting slots that remember their contents and only allow matching stacks in them\nSelect all / Unselect all = buttons\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.sorting_settings.tooltip=No Sort Slot Settings +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_detail=Allows selecting slots that are ignored by sorting\nOpen tab to modify slot settings +retro_sophisticated_backpacks.gui.sorting_settings.tooltip_open_detail=Allows selecting slots that are ignored by sorting\nSelect all / Unselect all = buttons\nSelect slot = left click/drag\nUnselect slot = right click/drag # Gui Tooltips retro_sophisticated_backpacks.gui.stack_size_extra=數量: %s / %s retro_sophisticated_backpacks.gui.not_in_effect=未生效 -retro_sophisticated_backpacks.gui.whitelist=白名單 -retro_sophisticated_backpacks.gui.blacklist=黑名單 +retro_sophisticated_backpacks.gui.whitelist=Allow +retro_sophisticated_backpacks.gui.blacklist=Block -retro_sophisticated_backpacks.gui.match_item=根據物品屬性 -retro_sophisticated_backpacks.gui.match_mod_id=根據模組 ID +retro_sophisticated_backpacks.gui.match_item=Match Item +retro_sophisticated_backpacks.gui.match_mod_id=Match Mod retro_sophisticated_backpacks.gui.match_ore_dict=根據礦物詞典 retro_sophisticated_backpacks.gui.craft_into_backpack=Shift 點擊合成結果轉移至背包 retro_sophisticated_backpacks.gui.craft_into_player_inventory=Shift 點擊合成結果轉移至玩家物品欄 -retro_sophisticated_backpacks.gui.match_durability=考慮耐久 -retro_sophisticated_backpacks.gui.ignore_durability=無視耐久 +retro_sophisticated_backpacks.gui.match_durability=Match Durability +retro_sophisticated_backpacks.gui.ignore_durability=Ignore Durability -retro_sophisticated_backpacks.gui.match_nbt=考慮 NBT -retro_sophisticated_backpacks.gui.ignore_nbt=無視 NBT +retro_sophisticated_backpacks.gui.match_nbt=Match NBT +retro_sophisticated_backpacks.gui.ignore_nbt=Ignore NBT retro_sophisticated_backpacks.gui.add_ore_dict_entry=新增礦物詞典 retro_sophisticated_backpacks.gui.remove_ore_dict_entry=移除礦物詞典 @@ -120,37 +120,241 @@ retro_sophisticated_backpacks.gui.ore_dict_input_help.pro_tip=提示:你可以 retro_sophisticated_backpacks.gui.ore_dict_list_entries=可用的礦物詞典: retro_sophisticated_backpacks.gui.none=(無) -retro_sophisticated_backpacks.gui.complete_hunger=只在玩家夠餓時餵食\n不會浪費食物提供的任何飢餓值 -retro_sophisticated_backpacks.gui.half_hunger=只在玩家夠餓時餵食\n最多浪費食物提供的一半飢餓值 -retro_sophisticated_backpacks.gui.immediate_hunger=當玩家飢餓時立即餵食\n浪費食物提供的大部分飢餓值 +retro_sophisticated_backpacks.gui.complete_hunger=Only feed when player is hungry enough\nto not waste any hunger points of the food at all +retro_sophisticated_backpacks.gui.half_hunger=Only feed when player is hungry enough\nto only waste half hunger points of the food at most +retro_sophisticated_backpacks.gui.immediate_hunger=Feed as soon as player is tiny bit hungry\nwastes quite a few hunger points of the food -retro_sophisticated_backpacks.gui.ignore_health=忽略生命值\n忽略玩家的生命值,僅基於飢餓值設定餵食玩家 -retro_sophisticated_backpacks.gui.consider_health=當玩家受傷時立即餵食\n在玩家生命值未滿時忽略飢餓值設定 +retro_sophisticated_backpacks.gui.ignore_health=Do not consider health\nIgnores player's health and only feeds based on hunger setting +retro_sophisticated_backpacks.gui.consider_health=Feed player immediately when hurt\nIgnores hunger setting when player is not at max health -retro_sophisticated_backpacks.gui.input_output=輸入 & 輸出 -retro_sophisticated_backpacks.gui.input=僅限輸入 -retro_sophisticated_backpacks.gui.output=僅限輸出 +retro_sophisticated_backpacks.gui.input_output=Input & Output +retro_sophisticated_backpacks.gui.input=Input +retro_sophisticated_backpacks.gui.output=Output -retro_sophisticated_backpacks.gui.sort_inventory=整理背包 -retro_sophisticated_backpacks.gui.sort_by_name=根據名稱整理 -retro_sophisticated_backpacks.gui.sort_by_mod_id=根據模組 ID 整理 -retro_sophisticated_backpacks.gui.sort_by_count=根據數量整理 -retro_sophisticated_backpacks.gui.sort_by_ore_dict=根據礦物詞典整理 +retro_sophisticated_backpacks.gui.sort_inventory=Sort Inventory +retro_sophisticated_backpacks.gui.sort_by_name=By Name +retro_sophisticated_backpacks.gui.sort_by_mod_id=By Mod +retro_sophisticated_backpacks.gui.sort_by_count=By Count +retro_sophisticated_backpacks.gui.sort_by_ore_dict=By Tags -retro_sophisticated_backpacks.gui.transfer_to_player_inv=轉移所有物品至玩家物品欄 -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=轉移對應物品至玩家物品欄 -retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift 以轉移全部 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=轉移所有物品至背包物品欄 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=轉移對應物品至背包物品欄 -retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift 以轉移全部 +retro_sophisticated_backpacks.gui.transfer_to_player_inv=Transfer to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_1=Transfer Matching to Inventory +retro_sophisticated_backpacks.gui.transfer_to_player_inv_matched_2=Shift To Transfer All +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv=Transfer to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_1=Transfer Matching to Storage +retro_sophisticated_backpacks.gui.transfer_to_backpack_inv_matched_2=Shift To Transfer All retro_sophisticated_backpacks.gui.settings=設定 +retro_sophisticated_backpacks.gui.back_to_backpack.tooltip=返回背包 retro_sophisticated_backpacks.gui.configuration_tab=設定介面 retro_sophisticated_backpacks.gui.memorized_slot=記憶欄位 -retro_sophisticated_backpacks.gui.memorize_all=記憶所有欄位 -retro_sophisticated_backpacks.gui.unmemorize_all=遺忘所有欄位 +retro_sophisticated_backpacks.gui.memorize_all=Select All Slots +retro_sophisticated_backpacks.gui.unmemorize_all=Unselect All Slots retro_sophisticated_backpacks.gui.no_sorting_slot=禁止整理欄位 -retro_sophisticated_backpacks.gui.lock_all_sort=禁止整理所有欄位 -retro_sophisticated_backpacks.gui.unlock_all_sort=允許整理所有欄位 +retro_sophisticated_backpacks.gui.lock_all_sort=Select All Slots +retro_sophisticated_backpacks.gui.unlock_all_sort=Unselect All Slots + +item.retro_sophisticated_backpacks.magnet_upgrade.name=磁鐵升級 +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.name=高級磁鐵升級 +item.retro_sophisticated_backpacks.void_upgrade.name=虛空升級 +item.retro_sophisticated_backpacks.advanced_void_upgrade.name=高級虛空升級 +item.retro_sophisticated_backpacks.refill_upgrade.name=補貨升級 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.name=高級補貨升級 +item.retro_sophisticated_backpacks.compacting_upgrade.name=壓縮升級 +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.name=高級壓縮升級 +retro_sophisticated_backpacks.gui.magnet_settings=Magnet +retro_sophisticated_backpacks.gui.advanced_magnet_settings=Adv. Magnet +retro_sophisticated_backpacks.gui.void_settings=Void +retro_sophisticated_backpacks.gui.advanced_void_settings=Adv. Void +retro_sophisticated_backpacks.gui.refill_settings=補貨 +retro_sophisticated_backpacks.gui.advanced_refill_settings=高級補貨 +retro_sophisticated_backpacks.gui.compacting_settings=Compa... +retro_sophisticated_backpacks.gui.advanced_compacting_settings=Adv. Compacting +retro_sophisticated_backpacks.gui.void_always=Void Always +retro_sophisticated_backpacks.gui.void_slot_overflow=Void Slot Overflow +retro_sophisticated_backpacks.gui.void_storage_overflow=Void Storage Overflow +retro_sophisticated_backpacks.gui.refill_target_any=任何插槽 +retro_sophisticated_backpacks.gui.refill_target_main_hand=主手 +retro_sophisticated_backpacks.gui.refill_target_off_hand=副手 +retro_sophisticated_backpacks.gui.refill_target_hotbar_1=快捷欄插槽 1 +retro_sophisticated_backpacks.gui.refill_target_hotbar_2=快捷欄插槽 2 +retro_sophisticated_backpacks.gui.refill_target_hotbar_3=快捷欄插槽 3 +retro_sophisticated_backpacks.gui.refill_target_hotbar_4=快捷欄插槽 4 +retro_sophisticated_backpacks.gui.refill_target_hotbar_5=快捷欄插槽 5 +retro_sophisticated_backpacks.gui.refill_target_hotbar_6=快捷欄插槽 6 +retro_sophisticated_backpacks.gui.refill_target_hotbar_7=快捷欄插槽 7 +retro_sophisticated_backpacks.gui.refill_target_hotbar_8=快捷欄插槽 8 +retro_sophisticated_backpacks.gui.refill_target_hotbar_9=快捷欄插槽 9 +retro_sophisticated_backpacks.gui.allow=Allow +retro_sophisticated_backpacks.gui.block=Block +retro_sophisticated_backpacks.gui.match_backpack_contents=依照背包已有物品取出 +retro_sophisticated_backpacks.gui.pickup_items=Pickup Items +retro_sophisticated_backpacks.gui.do_not_pickup_items=Do Not Pickup Items +retro_sophisticated_backpacks.gui.compact_only_uncraftable=Compact Only Uncraftable +retro_sophisticated_backpacks.gui.compact_anything=Compact Anything +retro_sophisticated_backpacks.gui.only_automatic=Only works with other upgrades/automation +retro_sophisticated_backpacks.gui.works_in_gui=Works in GUI as well +retro_sophisticated_backpacks.gui.void_slot_overflow_detail=Allows single slot to be filled with the item\nand voids anything that overflows +retro_sophisticated_backpacks.gui.void_storage_overflow_detail=Allows whole storage to be filled with the item\n and voids anything that overflows +retro_sophisticated_backpacks.gui.refill_target_tooltip=補貨 %s +retro_sophisticated_backpacks.gui.refill_scroll_tooltip=滾動以更改目標插槽 +item.retro_sophisticated_backpacks.everlasting_upgrade.name=永恆升級 +item.retro_sophisticated_backpacks.jukebox_upgrade.name=唱片機升級 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.name=Advanced Jukebox Upgrade +item.retro_sophisticated_backpacks.tool_swapper_upgrade.name=工具替換升級 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.name=高級工具替換升級 +item.retro_sophisticated_backpacks.tank_upgrade.name=儲罐升級 +item.retro_sophisticated_backpacks.pump_upgrade.name=液泵升級 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.name=高級液泵升級 +item.retro_sophisticated_backpacks.battery_upgrade.name=電池升級 +item.retro_sophisticated_backpacks.anvil_upgrade.name=Anvil Upgrade +retro_sophisticated_backpacks.gui.everlasting_settings=永恆 +retro_sophisticated_backpacks.gui.jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.advanced_jukebox_settings=Jukebox +retro_sophisticated_backpacks.gui.tool_swapper_settings=工具替換 +retro_sophisticated_backpacks.gui.advanced_tool_swapper_settings=工具替換 +retro_sophisticated_backpacks.gui.tank_settings=Tank +retro_sophisticated_backpacks.gui.pump_settings=Pump +retro_sophisticated_backpacks.gui.advanced_pump_settings=Adv. Pump +retro_sophisticated_backpacks.gui.battery_settings=Batt. +retro_sophisticated_backpacks.gui.anvil_settings=Anvil +retro_sophisticated_backpacks.gui.pump_input=Input +retro_sophisticated_backpacks.gui.pump_fluid_handlers=Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_world=Interact With World +retro_sophisticated_backpacks.gui.pump_hand=Interact With\nFluid Container in Hand +retro_sophisticated_backpacks.gui.anvil_no_result=無結果 +retro_sophisticated_backpacks.gui.anvil_shift_click_storage=Shift 點擊背包儲存 +retro_sophisticated_backpacks.gui.jukebox_play=Play +retro_sophisticated_backpacks.gui.jukebox_stop=Stop +retro_sophisticated_backpacks.gui.jukebox_previous=Previous +retro_sophisticated_backpacks.gui.jukebox_next=Next +retro_sophisticated_backpacks.gui.jukebox_shuffle_disabled=Shuffle Disabled +retro_sophisticated_backpacks.gui.jukebox_shuffle_enabled=Shuffle Enabled +retro_sophisticated_backpacks.gui.jukebox_repeat_all=Repeat All +retro_sophisticated_backpacks.gui.jukebox_repeat_one=Repeat One +retro_sophisticated_backpacks.gui.jukebox_repeat_no=Repeat Disabled +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_disabled=Do Not Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_swap_weapon_enabled=Swap Weapons +retro_sophisticated_backpacks.gui.tool_swapper_any=Swap Any Item +retro_sophisticated_backpacks.gui.tool_swapper_only_tools=Only Swap Tools +retro_sophisticated_backpacks.gui.tool_swapper_no_swap=Do Not Auto Swap Tools + +# Tooltips and GUI text +item.retro_sophisticated_backpacks.advanced_compacting_upgrade.tooltip=將物品壓縮為對應的壓縮變體\n包括2x2,3x3配方以及更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_deposit_upgrade.tooltip=手持背包 Shift 右擊容器卸載物品\n更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_feeding_upgrade.tooltip=將背包內的食物餵給玩家\n更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_filter_upgrade.tooltip=過濾輸入/輸出背包的物品\n更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_jukebox_upgrade.tooltip=Portable Jukebox with support for more music discs\nAlso more playback options +item.retro_sophisticated_backpacks.advanced_magnet_upgrade.tooltip=將更大範圍內的物品吸入背包\n更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.name=高級生物捕捉升級 +item.retro_sophisticated_backpacks.advanced_mob_catcher_upgrade.tooltip=潛行右擊可將被動和敵對生物捕捉到背包儲存槽位中 +item.retro_sophisticated_backpacks.advanced_pickup_upgrade.tooltip=使背包撿起物品\n更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_pump_upgrade.tooltip=在儲罐升級和相鄰方塊之間泵送流體\n適用於手持流體容器和世界中的流體方塊\n允許過濾泵送的流體 +item.retro_sophisticated_backpacks.advanced_refill_upgrade.tooltip=不斷補充玩家物品欄中選定物品的堆疊\n允許更精確的目標槽選擇\n還允許中鍵點擊從背包中拾取方塊 +item.retro_sophisticated_backpacks.advanced_restock_upgrade.tooltip=手持背包潛行右擊容器提取物品\n更多的過濾選項 +item.retro_sophisticated_backpacks.advanced_tool_swapper_upgrade.tooltip=左擊時自動將玩家手持物品替換為對應方塊/實體的挖掘或攻擊工具\n具有過濾選項和額外交互支援 +item.retro_sophisticated_backpacks.advanced_void_upgrade.tooltip=銷毀篩檢程式中選定的物品\n更多的過濾選項 +item.retro_sophisticated_backpacks.anvil_upgrade.tooltip=Anvil in an upgrade tab +item.retro_sophisticated_backpacks.battery_upgrade.tooltip=將部分背包空間替換為能量存儲空間 +item.retro_sophisticated_backpacks.compacting_upgrade.tooltip=將物品壓縮為對應的壓縮變體\n僅2x2配方 +item.retro_sophisticated_backpacks.crafting_upgrade.tooltip=在升級標籤頁中添加一個合成台 +item.retro_sophisticated_backpacks.deposit_upgrade.tooltip=手持背包潛行右擊容器卸載物品 +item.retro_sophisticated_backpacks.everlasting_upgrade.tooltip=使背包變得堅不可摧\n不會消失及掉入虛空 +item.retro_sophisticated_backpacks.exponential_stack_upgrade.tooltip=堆疊倍率會以相乘堆疊而非相加 +item.retro_sophisticated_backpacks.feeding_upgrade.tooltip=將背包內的食物餵給玩家 +item.retro_sophisticated_backpacks.filter_upgrade.tooltip=過濾輸入/輸出背包的物品 +item.retro_sophisticated_backpacks.inception_upgrade.tooltip=使背包套背包成為可能 +item.retro_sophisticated_backpacks.jukebox_upgrade.tooltip=便攜式唱片機 +item.retro_sophisticated_backpacks.magnet_upgrade.tooltip=將一定範圍內的物品吸入背包 +item.retro_sophisticated_backpacks.mob_catcher_upgrade.name=生物捕捉升級 +item.retro_sophisticated_backpacks.mob_catcher_upgrade.tooltip=潛行右擊可將被動生物捕捉到背包儲存槽位中 +item.retro_sophisticated_backpacks.pickup_upgrade.tooltip=使背包撿起物品 +item.retro_sophisticated_backpacks.pump_upgrade.tooltip=在儲罐升級和相鄰方塊之間泵送流體 +item.retro_sophisticated_backpacks.refill_upgrade.tooltip=使所選物品在玩家物品欄中自動補充至一組 +item.retro_sophisticated_backpacks.restock_upgrade.tooltip=手持背包潛行右擊容器提取物品 +item.retro_sophisticated_backpacks.stack_upgrade_starter_tier.tooltip=單個槽位可容納的堆疊數乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_1.tooltip=單個槽位可容納的堆疊數乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_2.tooltip=單個槽位可容納的堆疊數乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_3.tooltip=單個槽位可容納的堆疊數乘以%s +item.retro_sophisticated_backpacks.stack_upgrade_tier_4.tooltip=單個槽位可容納的堆疊數乘以%s +item.retro_sophisticated_backpacks.tank_upgrade.tooltip=將部分背包空間替換為流體存儲空間 +item.retro_sophisticated_backpacks.tool_swapper_upgrade.tooltip=左擊時自動將玩家手持物品替換為對應方塊/實體的挖掘或攻擊工具 +item.retro_sophisticated_backpacks.void_upgrade.tooltip=銷毀篩檢程式中選定的物品 +retro_sophisticated_backpacks.gui.backpack_settings=背包設置 +retro_sophisticated_backpacks.gui.backpack_settings.tooltip=背包設置 +retro_sophisticated_backpacks.gui.inception_settings=嵌套 +retro_sophisticated_backpacks.gui.item_display_settings=Item Disp. +retro_sophisticated_backpacks.gui.item_display_settings.tooltip=Item Display Settings +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_detail=Allows selecting a slot that will be used to show its item on top of storage +retro_sophisticated_backpacks.gui.item_display_settings.tooltip_open_detail=Allows selecting a slot that will be used to show its item on top of storage\nSelect slot = left click/drag\nUnselect slot = right click/drag +retro_sophisticated_backpacks.gui.mob_catcher.click_to_release=點擊釋放 +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers=Do Not Interact With Tanks & Pipes +retro_sophisticated_backpacks.gui.pump_no_hand=Do Not Interact With\nFluid Container in Hand +retro_sophisticated_backpacks.gui.pump_no_world=Do Not Interact With World +retro_sophisticated_backpacks.gui.pump_output=Output +retro_sophisticated_backpacks.gui.search=Click to search +retro_sophisticated_backpacks.gui.search_detail=@ prefix to search by mod name +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off=其他玩家不可打開 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.off.tooltip=即使該背包已被穿戴,其他玩家也無法通過對該玩家後背右擊打開它 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on=其他玩家可以打開 +retro_sophisticated_backpacks.gui.settings_button.another_player_can_open.on.tooltip=當該背包被穿戴且設置為可見時,其他玩家可以對該玩家後背右擊打開它 +retro_sophisticated_backpacks.gui.settings_button.context_backpack=背包設置 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip=該背包的設置 +retro_sophisticated_backpacks.gui.settings_button.context_backpack.tooltip_detail=不受到玩家全局設置影響 +retro_sophisticated_backpacks.gui.settings_button.context_player=Player +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip=Player level settings +retro_sophisticated_backpacks.gui.settings_button.context_player.tooltip_detail=Apply to all backpacks/storages unless overriden +retro_sophisticated_backpacks.gui.settings_button.display_side_front=Show on Front +retro_sophisticated_backpacks.gui.settings_button.display_side_left=Show on Left side +retro_sophisticated_backpacks.gui.settings_button.display_side_right=Show on Right side +retro_sophisticated_backpacks.gui.settings_button.item_display_color=Toggle Color +retro_sophisticated_backpacks.gui.settings_button.item_display_color_detail=Next = Left Click\nPrevious = Right Click +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off=Keep Search Phrase: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.off.tooltip=Backpack / Storage gui clears the search phrase when closed and shows all unfiltered item when open again +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on=Keep Search Phrase: ON +retro_sophisticated_backpacks.gui.settings_button.keep_search_phrase.on.tooltip=Backpack / Storage gui keeps the search phrase and prefills it / filters by it when open again +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off=Keep Tab Open: OFF +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.off.tooltip=Open upgrade tab gets closed when the backpack/storage gui is closed and when the gui is next open all of the tabs are closed +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on=Keep Tab Open: ON +retro_sophisticated_backpacks.gui.settings_button.keep_tab_open.on.tooltip=On close of its gui the backpack/storage records which upgrade tab was last open and opens it when the gui is open next time +retro_sophisticated_backpacks.gui.settings_button.rotate=Rotate +retro_sophisticated_backpacks.gui.settings_button.rotate_detail=Clockwise = Left Click\nCounter-Clockwise = Right Click +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off=Shift Click Into Inventory First +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.off.tooltip=Shift click from storage/inventory will first try to put the stack in inventory/storage and only then into an open tab. +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on=Shift Click Into Open Tab First +retro_sophisticated_backpacks.gui.settings_button.shift_click_open_tab.on.tooltip=Shift click from storage/inventory will first try to put the stack in an open tab and only then into inventory/storage. +retro_sophisticated_backpacks.gui.status.mob_catcher_blocklisted=該生物被禁止捕捉 +retro_sophisticated_backpacks.gui.status.mob_catcher_boss_blocked=不能捕捉Boss +retro_sophisticated_backpacks.gui.status.mob_catcher_captured=已捕捉%s +retro_sophisticated_backpacks.gui.status.mob_catcher_contains_mobs=釋放已捕捉的生物後才能移除生物捕捉升級 +retro_sophisticated_backpacks.gui.status.mob_catcher_hostile_needs_advanced=敵對生物需要高級生物捕捉升級 +retro_sophisticated_backpacks.gui.status.mob_catcher_invalid_entity=該生物不能被捕捉 +retro_sophisticated_backpacks.gui.status.mob_catcher_inventory_blocked=不能捕捉帶有物品欄的生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_mobs_need_advanced=已捕捉的生物需要高級生物捕捉升級 +retro_sophisticated_backpacks.gui.status.mob_catcher_no_release_space=那裡沒有有效的釋放空間 +retro_sophisticated_backpacks.gui.status.mob_catcher_no_space=沒有空餘的%sx%s槽位區域 +retro_sophisticated_backpacks.gui.status.mob_catcher_no_upgrade=背包沒有生物捕捉升級 +retro_sophisticated_backpacks.gui.status.mob_catcher_not_owner=只有所有者可以捕捉該生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_only_one_allowed=每個背包只能安裝一個生物捕捉升級 +retro_sophisticated_backpacks.gui.status.mob_catcher_passengers_blocked=不能捕捉帶有乘客或載具關係的生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_players_blocked=不能捕捉玩家 +retro_sophisticated_backpacks.gui.status.mob_catcher_release_failed=無法在那裡釋放生物 +retro_sophisticated_backpacks.gui.status.mob_catcher_released=已釋放%s +retro_sophisticated_backpacks.gui.status.mob_catcher_too_large=該生物需要%s個槽位,超過此升級允許的上限(%s) +retro_sophisticated_backpacks.gui.tooltip.stack_count=Count: %s + +# Fallbacks for keys used by current code +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_detail=Allows configuring backpack behavior\nOpen tab to modify backpack settings +retro_sophisticated_backpacks.gui.backpack_settings.tooltip_open_detail=Allows configuring backpack behavior\nContext = choose whether changes apply to player or backpack\nToggle buttons change shift-click, tab, search, and access behavior +retro_sophisticated_backpacks.gui.settings_button.display_side_detail=Left click selects next side\nRight click selects previous side +retro_sophisticated_backpacks.gui.pump_input_detail=Pull fluids into the backpack tank +retro_sophisticated_backpacks.gui.pump_output_detail=Push fluids out of the backpack tank +retro_sophisticated_backpacks.gui.pump_fluid_handlers_detail=Allows moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_no_fluid_handlers_detail=Prevents moving fluid through adjacent fluid handlers +retro_sophisticated_backpacks.gui.pump_world_detail=Allows placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_no_world_detail=Prevents placing or draining fluids in the world +retro_sophisticated_backpacks.gui.pump_hand_detail=Allows filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_no_hand_detail=Prevents filling or draining containers held by the player +retro_sophisticated_backpacks.gui.pump_fluid_filter_detail=Click with a fluid container to set this filter\nClick with an empty cursor to clear it diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/block/backpack_left_tank.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/block/backpack_left_tank.json new file mode 100644 index 0000000..3078738 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/block/backpack_left_tank.json @@ -0,0 +1,144 @@ +{ + "credit": "Made with Blockbench", + "texture_size": [32, 32], + "textures": { + "particle": "retro_sophisticated_backpacks:block/backpack_cloth", + "border": "retro_sophisticated_backpacks:block/backpack_border", + "modules": "retro_sophisticated_backpacks:block/backpack_modules" + }, + "elements": [ + { + "name": "back", + "from": [12.5, 1.5, 6], + "to": [13.5, 7.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [2.5, 5.5, 2, 8.5], "texture": "#modules"}, + "east": {"uv": [2, 5.5, 0, 8.5], "texture": "#modules"}, + "south": {"uv": [3, 5.5, 2.5, 8.5], "texture": "#modules"}, + "west": {"uv": [5, 5, 3, 9], "texture": "#modules"}, + "up": {"uv": [5.5, 5, 5, 7], "texture": "#modules"}, + "down": {"uv": [5.5, 7, 5, 9], "texture": "#modules"} + } + }, + { + "name": "cap", + "from": [12.5, 10.5, 6.5], + "to": [15.5, 11.5, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [8, 6.5, 6.5, 7], "texture": "#modules"}, + "east": {"uv": [8, 7, 6.5, 7.5], "texture": "#modules"}, + "south": {"uv": [8.5, 5, 8, 6.5], "rotation": 90, "texture": "#modules"}, + "west": {"uv": [8, 9, 6.5, 9.5], "texture": "#modules"}, + "up": {"uv": [8, 5, 6.5, 6.5], "texture": "#modules"}, + "down": {"uv": [8, 7.5, 6.5, 9], "texture": "#modules"} + } + }, + { + "name": "cap-bottom", + "from": [13, 9.5, 6], + "to": [16, 10.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [3.5, 13.5, 2, 14], "texture": "#modules"}, + "east": {"uv": [2, 12, 0, 12.5], "texture": "#modules"}, + "south": {"uv": [3.5, 12, 2, 12.5], "texture": "#modules"}, + "west": {"uv": [2, 13.5, 0, 15], "texture": "#modules"}, + "up": {"uv": [5, 12, 3.5, 14], "texture": "#modules"}, + "down": {"uv": [6.5, 12, 5, 14], "texture": "#modules"} + } + }, + { + "name": "trim", + "from": [13, 8.5, 6], + "to": [16, 9.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [7.5, 2, 6, 2.5], "texture": "#border", "tintindex": 1}, + "east": {"uv": [8, 2.5, 6, 3], "texture": "#border", "tintindex": 1}, + "south": {"uv": [7.5, 3, 6, 3.5], "texture": "#border", "tintindex": 1}, + "west": {"uv": [2, 13.5, 0, 15], "texture": "#border", "tintindex": 1}, + "up": {"uv": [5, 12, 3.5, 14], "texture": "#border", "tintindex": 1}, + "down": {"uv": [6.5, 12, 5, 14], "texture": "#border", "tintindex": 1} + } + }, + { + "name": "top", + "from": [13, 7.5, 6], + "to": [16, 8.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [3.5, 14.5, 2, 15], "texture": "#modules"}, + "east": {"uv": [2, 13, 0, 13.5], "texture": "#modules"}, + "south": {"uv": [3.5, 13, 2, 13.5], "texture": "#modules"}, + "west": {"uv": [2, 13.5, 0, 15], "texture": "#modules"}, + "up": {"uv": [5, 12, 3.5, 14], "texture": "#modules"}, + "down": {"uv": [6.5, 12, 5, 14], "texture": "#modules"} + } + }, + { + "name": "bottom", + "from": [13, 0.5, 6], + "to": [16, 1.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [1.5, 11, 0, 11.5], "texture": "#modules"}, + "east": {"uv": [3.5, 9, 1.5, 9.5], "texture": "#modules"}, + "south": {"uv": [1.5, 11.5, 0, 12], "texture": "#modules"}, + "west": {"uv": [3.5, 9.5, 1.5, 10], "texture": "#modules"}, + "up": {"uv": [1.5, 9, 0, 11], "texture": "#modules"}, + "down": {"uv": [3, 10, 1.5, 12], "texture": "#modules"} + } + }, + { + "name": "front-glass", + "from": [15.5, 1.5, 6.5], + "to": [15.5, 7.5, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [4.5, 0, 2.5, 5], "texture": "#modules"}, + "east": {"uv": [2.5, 0, 0, 5], "texture": "#modules"}, + "south": {"uv": [6.5, 0, 4.5, 5], "texture": "#modules"}, + "west": {"uv": [0, 0, 2.5, 5], "texture": "#modules"}, + "up": {"uv": [8.5, 0, 6.5, 2.5], "texture": "#modules"}, + "down": {"uv": [8.5, 2.5, 6.5, 5], "texture": "#modules"} + } + }, + { + "name": "left-glass", + "from": [13.5, 1.5, 9.5], + "to": [15.5, 7.5, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [4.5, 0, 6.5, 5], "texture": "#modules"}, + "east": {"uv": [2.5, 0, 0, 5], "texture": "#modules"}, + "south": {"uv": [6.5, 0, 4.5, 5], "texture": "#modules"}, + "west": {"uv": [2.5, 0, 0, 5], "texture": "#modules"}, + "up": {"uv": [8.5, 0, 6.5, 2.5], "texture": "#modules"}, + "down": {"uv": [8.5, 2.5, 6.5, 5], "texture": "#modules"} + } + }, + { + "name": "right-glass", + "from": [13.5, 1.5, 6.5], + "to": [15.5, 7.5, 6.5], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [4.5, 0, 2.5, 5], "texture": "#modules"}, + "east": {"uv": [2.5, 0, 0, 5], "texture": "#modules"}, + "south": {"uv": [2.5, 0, 4.5, 5], "texture": "#modules"}, + "west": {"uv": [2.5, 0, 0, 5], "texture": "#modules"}, + "up": {"uv": [8.5, 0, 6.5, 2.5], "texture": "#modules"}, + "down": {"uv": [8.5, 2.5, 6.5, 5], "texture": "#modules"} + } + } + ], + "groups": [ + { + "name": "left-tank", + "origin": [16, 0, 0], + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8] + } + ] +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/block/backpack_right_tank.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/block/backpack_right_tank.json new file mode 100644 index 0000000..44e661b --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/block/backpack_right_tank.json @@ -0,0 +1,135 @@ +{ + "credit": "Made with Blockbench", + "texture_size": [32, 32], + "textures": { + "particle": "retro_sophisticated_backpacks:block/backpack_cloth", + "border": "retro_sophisticated_backpacks:block/backpack_border", + "modules": "retro_sophisticated_backpacks:block/backpack_modules" + }, + "elements": [ + { + "name": "back", + "from": [2.5, 1.5, 6], + "to": [3.5, 7.5, 10], + "faces": { + "north": {"uv": [2, 5.5, 2.5, 8.5], "texture": "#modules"}, + "east": {"uv": [3, 5, 5, 9], "texture": "#modules"}, + "south": {"uv": [2.5, 5.5, 3, 8.5], "texture": "#modules"}, + "west": {"uv": [0, 5.5, 2, 8.5], "texture": "#modules"}, + "up": {"uv": [5, 5, 5.5, 7], "texture": "#modules"}, + "down": {"uv": [5, 7, 5.5, 9], "texture": "#modules"} + } + }, + { + "name": "cap", + "from": [0.5, 10.5, 6.5], + "to": [3.5, 11.5, 9.5], + "faces": { + "north": {"uv": [6.5, 6.5, 8, 7], "texture": "#modules"}, + "east": {"uv": [6.5, 9, 8, 9.5], "texture": "#modules"}, + "south": {"uv": [8.5, 6.5, 8, 5], "rotation": 90, "texture": "#modules"}, + "west": {"uv": [6.5, 7, 8, 7.5], "texture": "#modules"}, + "up": {"uv": [6.5, 5, 8, 6.5], "texture": "#modules"}, + "down": {"uv": [6.5, 7.5, 8, 9], "texture": "#modules"} + } + }, + { + "name": "cap-bottom", + "from": [0, 9.5, 6], + "to": [3, 10.5, 10], + "faces": { + "north": {"uv": [2, 13.5, 3.5, 14], "texture": "#modules"}, + "east": {"uv": [0, 13.5, 2, 15], "texture": "#modules"}, + "south": {"uv": [2, 12, 3.5, 12.5], "texture": "#modules"}, + "west": {"uv": [0, 12, 2, 12.5], "texture": "#modules"}, + "up": {"uv": [3.5, 12, 5, 14], "texture": "#modules"}, + "down": {"uv": [5, 12, 6.5, 14], "texture": "#modules"} + } + }, + { + "name": "trim", + "from": [0, 8.5, 6], + "to": [3, 9.5, 10], + "faces": { + "north": {"uv": [6, 2, 7.5, 2.5], "texture": "#border", "tintindex": 1}, + "east": {"uv": [0, 13.5, 2, 15], "texture": "#border", "tintindex": 1}, + "south": {"uv": [6, 3, 7.5, 3.5], "texture": "#border", "tintindex": 1}, + "west": {"uv": [6, 2.5, 8, 3], "texture": "#border", "tintindex": 1}, + "up": {"uv": [3.5, 12, 5, 14], "texture": "#border", "tintindex": 1}, + "down": {"uv": [5, 12, 6.5, 14], "texture": "#border", "tintindex": 1} + } + }, + { + "name": "top", + "from": [0, 7.5, 6], + "to": [3, 8.5, 10], + "faces": { + "north": {"uv": [2, 14.5, 3.5, 15], "texture": "#modules"}, + "east": {"uv": [0, 13.5, 2, 15], "texture": "#modules"}, + "south": {"uv": [2, 13, 3.5, 13.5], "texture": "#modules"}, + "west": {"uv": [0, 13, 2, 13.5], "texture": "#modules"}, + "up": {"uv": [3.5, 12, 5, 14], "texture": "#modules"}, + "down": {"uv": [5, 12, 6.5, 14], "texture": "#modules"} + } + }, + { + "name": "bottom", + "from": [0, 0.5, 6], + "to": [3, 1.5, 10], + "faces": { + "north": {"uv": [0, 11, 1.5, 11.5], "texture": "#modules"}, + "east": {"uv": [1.5, 9.5, 3.5, 10], "texture": "#modules"}, + "south": {"uv": [0, 11.5, 1.5, 12], "texture": "#modules"}, + "west": {"uv": [1.5, 9, 3.5, 9.5], "texture": "#modules"}, + "up": {"uv": [0, 9, 1.5, 11], "texture": "#modules"}, + "down": {"uv": [1.5, 10, 3, 12], "texture": "#modules"} + } + }, + { + "name": "front-glass", + "from": [0.5, 1.5, 6.5], + "to": [0.5, 7.5, 9.5], + "faces": { + "north": {"uv": [2.5, 0, 4.5, 5], "texture": "#modules"}, + "east": {"uv": [2.5, 0, 0, 5], "texture": "#modules"}, + "south": {"uv": [4.5, 0, 6.5, 5], "texture": "#modules"}, + "west": {"uv": [0, 0, 2.5, 5], "texture": "#modules"}, + "up": {"uv": [6.5, 0, 8.5, 2.5], "texture": "#modules"}, + "down": {"uv": [6.5, 2.5, 8.5, 5], "texture": "#modules"} + } + }, + { + "name": "right-glass", + "from": [0.5, 1.5, 9.5], + "to": [2.5, 7.5, 9.5], + "faces": { + "north": {"uv": [6.5, 0, 4.5, 5], "texture": "#modules"}, + "east": {"uv": [0, 0, 2.5, 5], "texture": "#modules"}, + "south": {"uv": [4.5, 0, 6.5, 5], "texture": "#modules"}, + "west": {"uv": [0, 0, 2.5, 5], "texture": "#modules"}, + "up": {"uv": [6.5, 0, 8.5, 2.5], "texture": "#modules"}, + "down": {"uv": [6.5, 2.5, 8.5, 5], "texture": "#modules"} + } + }, + { + "name": "left-glass", + "from": [0.5, 1.5, 6.5], + "to": [2.5, 7.5, 6.5], + "faces": { + "north": {"uv": [2.5, 0, 4.5, 5], "texture": "#modules"}, + "east": {"uv": [0, 0, 2.5, 5], "texture": "#modules"}, + "south": {"uv": [4.5, 0, 2.5, 5], "texture": "#modules"}, + "west": {"uv": [0, 0, 2.5, 5], "texture": "#modules"}, + "up": {"uv": [6.5, 0, 8.5, 2.5], "texture": "#modules"}, + "down": {"uv": [6.5, 2.5, 8.5, 5], "texture": "#modules"} + } + } + ], + "groups": [ + { + "name": "right-tank", + "origin": [16, 0, 0], + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8] + } + ] +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_compacting_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_compacting_upgrade.json new file mode 100644 index 0000000..70a0681 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_compacting_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_compacting_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_jukebox_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_jukebox_upgrade.json new file mode 100644 index 0000000..bb0429b --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_jukebox_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_jukebox_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_magnet_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_magnet_upgrade.json new file mode 100644 index 0000000..0ba3697 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_magnet_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_magnet_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_mob_catcher_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_mob_catcher_upgrade.json new file mode 100644 index 0000000..c94212e --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_mob_catcher_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_mob_catcher_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_pump_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_pump_upgrade.json new file mode 100644 index 0000000..9df2bad --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_pump_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_pump_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_refill_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_refill_upgrade.json new file mode 100644 index 0000000..e0b5537 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_refill_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_refill_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_tool_swapper_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_tool_swapper_upgrade.json new file mode 100644 index 0000000..7b54a68 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_tool_swapper_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_tool_swapper_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_void_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_void_upgrade.json new file mode 100644 index 0000000..be702d2 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/advanced_void_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/advanced_void_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/anvil_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/anvil_upgrade.json new file mode 100644 index 0000000..18a9f3c --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/anvil_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/anvil_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/battery_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/battery_upgrade.json new file mode 100644 index 0000000..05a160b --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/battery_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/battery_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/compacting_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/compacting_upgrade.json new file mode 100644 index 0000000..81a594f --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/compacting_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/compacting_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/everlasting_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/everlasting_upgrade.json new file mode 100644 index 0000000..b022c53 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/everlasting_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/everlasting_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/jukebox_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/jukebox_upgrade.json new file mode 100644 index 0000000..28244a8 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/jukebox_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/jukebox_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/magnet_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/magnet_upgrade.json new file mode 100644 index 0000000..8fa2e4c --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/magnet_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/magnet_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/mob_catcher_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/mob_catcher_upgrade.json new file mode 100644 index 0000000..80c407e --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/mob_catcher_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/mob_catcher_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/pump_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/pump_upgrade.json new file mode 100644 index 0000000..b57b348 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/pump_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/pump_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/refill_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/refill_upgrade.json new file mode 100644 index 0000000..f605406 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/refill_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/refill_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/tank_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/tank_upgrade.json new file mode 100644 index 0000000..13b3959 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/tank_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/tank_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/tool_swapper_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/tool_swapper_upgrade.json new file mode 100644 index 0000000..aa5e342 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/tool_swapper_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/tool_swapper_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/models/item/void_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/void_upgrade.json new file mode 100644 index 0000000..4ba262a --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/models/item/void_upgrade.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "retro_sophisticated_backpacks:item/void_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_compacting_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_compacting_upgrade.json new file mode 100644 index 0000000..90a863f --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_compacting_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "D": { + "item": "minecraft:diamond" + }, + "G": { + "item": "minecraft:gold_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + "C": { + "item": "retro_sophisticated_backpacks:compacting_upgrade" + } + }, + "pattern": [ + " D ", + "GCG", + "RRR" + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_compacting_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_jukebox_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_jukebox_upgrade.json new file mode 100644 index 0000000..da5fa43 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_jukebox_upgrade.json @@ -0,0 +1,22 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "D": { + "item": "minecraft:diamond" + }, + "R": { + "item": "minecraft:redstone" + }, + "J": { + "item": "retro_sophisticated_backpacks:jukebox_upgrade" + } + }, + "pattern": [ + " D ", + "RJR", + " R " + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_jukebox_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_magnet_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_magnet_upgrade.json new file mode 100644 index 0000000..597899e --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_magnet_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "D": { + "item": "minecraft:diamond" + }, + "G": { + "item": "minecraft:gold_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + "M": { + "item": "retro_sophisticated_backpacks:magnet_upgrade" + } + }, + "pattern": [ + " D ", + "GMG", + "RRR" + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_magnet_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_mob_catcher_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_mob_catcher_upgrade.json new file mode 100644 index 0000000..b8428e8 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_mob_catcher_upgrade.json @@ -0,0 +1,34 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "H": { + "item": "minecraft:skull", + "data": 1 + }, + "D": { + "type": "forge:ore_dict", + "ore": "gemDiamond" + }, + "E": { + "item": "minecraft:ender_eye" + }, + "G": { + "type": "forge:ore_dict", + "ore": "ingotGold" + }, + "B": { + "item": "retro_sophisticated_backpacks:mob_catcher_upgrade" + }, + "S": { + "item": "minecraft:soul_sand" + } + }, + "pattern": [ + "HDE", + "GBG", + "SSS" + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_mob_catcher_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_refill_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_refill_upgrade.json new file mode 100644 index 0000000..c7fed95 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_refill_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "D": { + "item": "minecraft:diamond" + }, + "G": { + "item": "minecraft:gold_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + "F": { + "item": "retro_sophisticated_backpacks:refill_upgrade" + } + }, + "pattern": [ + " D ", + "GFG", + "RRR" + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_refill_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_tool_swapper_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_tool_swapper_upgrade.json new file mode 100644 index 0000000..46c9cf9 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_tool_swapper_upgrade.json @@ -0,0 +1,22 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "D": { + "item": "minecraft:diamond" + }, + "S": { + "item": "minecraft:iron_sword" + }, + "T": { + "item": "retro_sophisticated_backpacks:tool_swapper_upgrade" + } + }, + "pattern": [ + " D ", + "STS", + " D " + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_tool_swapper_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_void_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_void_upgrade.json new file mode 100644 index 0000000..9abfa8a --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/advanced_void_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "D": { + "item": "minecraft:diamond" + }, + "G": { + "item": "minecraft:gold_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + "V": { + "item": "retro_sophisticated_backpacks:void_upgrade" + } + }, + "pattern": [ + " D ", + "GVG", + "RRR" + ], + "result": { + "item": "retro_sophisticated_backpacks:advanced_void_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/compacting_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/compacting_upgrade.json new file mode 100644 index 0000000..ba31349 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/compacting_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "I": { + "item": "minecraft:iron_ingot" + }, + "P": { + "item": "minecraft:piston" + }, + "R": { + "item": "minecraft:redstone" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + "IPI", + "PBP", + "RPR" + ], + "result": { + "item": "retro_sophisticated_backpacks:compacting_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/everlasting_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/everlasting_upgrade.json new file mode 100644 index 0000000..f6cddbf --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/everlasting_upgrade.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "N": { + "item": "minecraft:nether_star" + }, + "O": { + "item": "minecraft:obsidian" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + "O O", + "OBO", + " N " + ], + "result": { + "item": "retro_sophisticated_backpacks:everlasting_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/jukebox_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/jukebox_upgrade.json new file mode 100644 index 0000000..e1e6c99 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/jukebox_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "J": { + "item": "minecraft:jukebox" + }, + "N": { + "item": "minecraft:noteblock" + }, + "R": { + "item": "minecraft:redstone" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + " N ", + "RBR", + " J " + ], + "result": { + "item": "retro_sophisticated_backpacks:jukebox_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/magnet_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/magnet_upgrade.json new file mode 100644 index 0000000..fc547e1 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/magnet_upgrade.json @@ -0,0 +1,29 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "E": { + "item": "minecraft:ender_pearl" + }, + "I": { + "item": "minecraft:iron_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + "L": { + "item": "minecraft:dye", + "data": 4 + }, + "P": { + "item": "retro_sophisticated_backpacks:pickup_upgrade" + } + }, + "pattern": [ + "EIE", + "IPI", + "R L" + ], + "result": { + "item": "retro_sophisticated_backpacks:magnet_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/mob_catcher_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/mob_catcher_upgrade.json new file mode 100644 index 0000000..ee85522 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/mob_catcher_upgrade.json @@ -0,0 +1,26 @@ +{ + "type": "retro_sophisticated_backpacks:upgrade_shaped", + "key": { + "E": { + "item": "minecraft:ender_pearl" + }, + "I": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + }, + "L": { + "item": "minecraft:lead" + } + }, + "pattern": [ + " E ", + "IBI", + "L L" + ], + "result": { + "item": "retro_sophisticated_backpacks:mob_catcher_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/refill_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/refill_upgrade.json new file mode 100644 index 0000000..ae5b726 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/refill_upgrade.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "E": { + "item": "minecraft:ender_pearl" + }, + "I": { + "item": "minecraft:iron_ingot" + }, + "R": { + "item": "minecraft:redstone" + }, + "C": { + "item": "minecraft:chest" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + " E ", + "IBI", + "RCR" + ], + "result": { + "item": "retro_sophisticated_backpacks:refill_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/tank_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/tank_upgrade.json new file mode 100644 index 0000000..2396ce1 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/tank_upgrade.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "G": { + "type": "forge:ore_dict", + "ore": "blockGlass" + }, + "I": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + "GIG", + "GBG", + "GIG" + ], + "result": { + "item": "retro_sophisticated_backpacks:tank_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/tool_swapper_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/tool_swapper_upgrade.json new file mode 100644 index 0000000..a27c01c --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/tool_swapper_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "P": { + "item": "minecraft:iron_pickaxe" + }, + "A": { + "item": "minecraft:iron_axe" + }, + "S": { + "item": "minecraft:iron_shovel" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + " P ", + "ABA", + " S " + ], + "result": { + "item": "retro_sophisticated_backpacks:tool_swapper_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/recipes/void_upgrade.json b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/void_upgrade.json new file mode 100644 index 0000000..9ca38e6 --- /dev/null +++ b/src/main/resources/assets/retro_sophisticated_backpacks/recipes/void_upgrade.json @@ -0,0 +1,25 @@ +{ + "type": "minecraft:crafting_shaped", + "key": { + "E": { + "item": "minecraft:ender_pearl" + }, + "O": { + "item": "minecraft:obsidian" + }, + "R": { + "item": "minecraft:redstone" + }, + "B": { + "item": "retro_sophisticated_backpacks:upgrade_base" + } + }, + "pattern": [ + " E ", + "OBO", + "ROR" + ], + "result": { + "item": "retro_sophisticated_backpacks:void_upgrade" + } +} diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/slots_background.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/slots_background.png index a631fe7..f157775 100644 Binary files a/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/slots_background.png and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/slots_background.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_12.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_12.png new file mode 100644 index 0000000..4f804b7 Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_12.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_12_wider.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_12_wider.png new file mode 100644 index 0000000..141569f Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_12_wider.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_9.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_9.png new file mode 100644 index 0000000..371f86d Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_9.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_9_wider.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_9_wider.png new file mode 100644 index 0000000..def1643 Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/gui/storage_background_9_wider.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/advanced_mob_catcher_upgrade.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/advanced_mob_catcher_upgrade.png new file mode 100644 index 0000000..b0abe62 Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/advanced_mob_catcher_upgrade.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_battery_input_slot.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_battery_input_slot.png new file mode 100644 index 0000000..d0099d2 Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_battery_input_slot.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_battery_output_slot.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_battery_output_slot.png new file mode 100644 index 0000000..ff4cfdc Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_battery_output_slot.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_tank_input_slot.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_tank_input_slot.png new file mode 100644 index 0000000..9804b22 Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_tank_input_slot.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_tank_output_slot.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_tank_output_slot.png new file mode 100644 index 0000000..4d1311d Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_tank_output_slot.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_upgrade_slot.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_upgrade_slot.png new file mode 100644 index 0000000..e3301df Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/empty_upgrade_slot.png differ diff --git a/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/mob_catcher_upgrade.png b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/mob_catcher_upgrade.png new file mode 100644 index 0000000..e123b1b Binary files /dev/null and b/src/main/resources/assets/retro_sophisticated_backpacks/textures/item/mob_catcher_upgrade.png differ diff --git a/src/main/resources/mixin.retro_sophisticated_backpacks.json b/src/main/resources/mixin.retro_sophisticated_backpacks.json index 2d79ed8..cfd5db8 100644 --- a/src/main/resources/mixin.retro_sophisticated_backpacks.json +++ b/src/main/resources/mixin.retro_sophisticated_backpacks.json @@ -5,9 +5,11 @@ "minVersion": "0.8", "compatibilityLevel": "JAVA_8", "mixins": [ + "EntityItemAccessor", "EnumDyeColorAccessor" ], "client": [ + "GuiContainerAccessor", "GuiContainerMixin", "LayerArmorBaseMixin" ]