@file:Suppress("MemberVisibilityCanBePrivate", "unused") package net.axay.kspigot.gui import net.axay.kspigot.gui.elements.* import org.bukkit.inventory.ItemStack import kotlin.math.absoluteValue fun kSpigotGUI( type: GUIType, guiCreator: GUICreator, builder: GUIBuilder.() -> Unit, ) = GUIBuilder(type, guiCreator).apply(builder).build() class GUIBuilder( val type: GUIType, private val guiCreator: GUICreator ) { /** * The title of this GUI. * This title will be visible for every page of * this GUI. */ var title: String = "" /** * The transition applied, if another GUI redirects to * this GUI. */ var transitionTo: InventoryChangeEffect? = null /** * The transition applied, if this GUI redirects to * another GUI and the other GUI has no transitionTo * value defined. */ var transitionFrom: InventoryChangeEffect? = null /** * The default page will be loaded first for every * GUI instance. */ var defaultPage = 1 private val guiSlots = HashMap>() private var onClickElement: ((GUIClickEvent) -> Unit)? = null /** * Opens the builder for a new page and adds * the new page to the GUI. * @param page The index of the page. */ fun page(page: Int, builder: GUIPageBuilder.() -> Unit) { guiSlots[page] = GUIPageBuilder(type, page).apply(builder).build() } fun onClickElement(onClick: (GUIClickEvent) -> Unit) { onClickElement = onClick } internal fun build() = guiCreator.createInstance( GUIData(type, title, guiSlots, transitionTo, transitionFrom, onClickElement) ) } class GUIPageBuilder( private val type: GUIType, val page: Int ) { private val guiSlots = HashMap>() var transitionTo: PageChangeEffect? = null var transitionFrom: PageChangeEffect? = null internal fun build() = GUIPage(page, guiSlots, transitionTo, transitionFrom) private fun defineSlots(slots: InventorySlotCompound, element: GUISlot) = slots.withInvType(type).forEach { curSlot -> curSlot.realSlotIn(type.dimensions)?.let { guiSlots[it] = element } } /** * A button is an item protected from any player * actions. If clicked, the specified [onClick] * function is invoked. */ fun button(slots: InventorySlotCompound, itemStack: ItemStack, onClick: (GUIClickEvent) -> Unit) = defineSlots(slots, GUIButton(itemStack, onClick)) /** * An item protected from any player actions. * This is not a button. */ fun placeholder(slots: InventorySlotCompound, itemStack: ItemStack) = defineSlots(slots, GUIPlaceholder(itemStack)) /** * A free slot does not block any player actions. * The player can put items in this slot or take * items out of it. */ fun freeSlot(slots: InventorySlotCompound) = defineSlots(slots, GUIFreeSlot()) /** * This is a button which loads the specified * [toPage] if clicked. */ fun pageChanger( slots: InventorySlotCompound, icon: ItemStack, toPage: Int, onChange: ((GUIClickEvent) -> Unit)? = null ) = defineSlots( slots, GUIButtonPageChange( icon, GUIPageChangeCalculator.GUIConsistentPageCalculator(toPage), onChange ) ) /** * This button always tries to find the previous * page if clicked, and if a previous page * exists it is loaded. */ fun previousPage( slots: InventorySlotCompound, icon: ItemStack, onChange: ((GUIClickEvent) -> Unit)? = null ) = defineSlots( slots, GUIButtonPageChange( icon, GUIPageChangeCalculator.GUIPreviousPageCalculator, onChange ) ) /** * This button always tries to find the next * page if clicked, and if a next page * exists it is loaded. */ fun nextPage( slots: InventorySlotCompound, icon: ItemStack, onChange: ((GUIClickEvent) -> Unit)? = null ) = defineSlots( slots, GUIButtonPageChange( icon, GUIPageChangeCalculator.GUINextPageCalculator, onChange ) ) /** * By pressing this button, the player switches to another * GUI. The transition effect is applied. */ fun changeGUI( slots: InventorySlotCompound, icon: ItemStack, newGUI: () -> GUI<*>, newPage: Int? = null, onChange: ((GUIClickEvent) -> Unit)? = null ) = defineSlots( slots, GUIButtonInventoryChange( icon, newGUI, newPage, onChange ) ) /** * Creates a new compound, holding data which can be displayed * in any compound space. */ fun createCompound( iconGenerator: (E) -> ItemStack, onClick: (clickEvent: GUIClickEvent, element: E) -> Unit ) = GUISpaceCompound(type, iconGenerator, onClick) /** * Creates a new compound, holding data which can be displayed * in any compound space. * This compound is strictly a rectangle. * The space is automatically defined. */ fun createCompound( fromSlot: SingleInventorySlot, toSlot: SingleInventorySlot, iconGenerator: (E) -> ItemStack, onClick: (clickEvent: GUIClickEvent, element: E) -> Unit ): GUIRectSpaceCompound { val rectSlotCompound = fromSlot rectTo toSlot return GUIRectSpaceCompound( type, iconGenerator, onClick, (rectSlotCompound.endInclusive.slotInRow - rectSlotCompound.start.slotInRow) + 1 ).apply { addSlots(rectSlotCompound) defineSlots( rectSlotCompound, GUISpaceCompoundElement(this) ) } } /** * Defines an area where the content of the given compound * is displayed. */ fun compoundSpace( slots: InventorySlotCompound, compound: AbstractGUISpaceCompound ) { compound.addSlots(slots) defineSlots( slots, GUISpaceCompoundElement(compound) ) } /** * By pressing this button, * the user scrolls forwards or backwards in the compound. */ fun compoundScroll( slots: InventorySlotCompound, icon: ItemStack, compound: GUISpaceCompound, scrollDistance: Int = 1, scrollTimes: Int = 1, reverse: Boolean = false ) = defineSlots( slots, GUISpaceCompoundScrollButton( icon, compound, scrollDistance.absoluteValue, scrollTimes, reverse ) ) /** * By pressing this button, * the user scrolls forwards or backwards in the compound. */ fun compoundScroll( slots: InventorySlotCompound, icon: ItemStack, compound: GUIRectSpaceCompound, scrollTimes: Int = 1, reverse: Boolean = false ) = defineSlots( slots, GUISpaceCompoundScrollButton( icon, compound, compound.compoundWidth, scrollTimes, reverse ) ) }