fancywb.sk

Created by pesekjan

Other available versions. Ordered by newest to oldest versions:

Just so you know, we don't know the file format for every file. If it's just a bunch of random characters, it's probably a .zip or .jar.

options:
	enable: true
	enable-recipebook: true
	recipebook-register-delay: 1 tick
	enable-suggestions: true
	suggestions-permission: fancywb.suggestion

import:
	java.util.HashMap
	org.bukkit.Bukkit
	org.bukkit.event.inventory.InventoryDragEvent
	org.bukkit.inventory.Recipe
	org.bukkit.inventory.ShapedRecipe
	org.bukkit.inventory.ShapelessRecipe
	org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack
	net.minecraft.world.inventory.InventoryCrafting
	net.minecraft.world.inventory.ContainerWorkbench
	net.minecraft.world.entity.player.PlayerInventory
	net.minecraft.world.item.crafting.Recipes

function initWorkbench(p: player):
	set metadata tag "fancyTable" of {_p} to chest inventory with 8 rows named "Craft Item"
	set slots (integers between 0 and 44) of metadata tag "fancyTable" of {_p} to black stained glass pane named "&0"
	set slots (integers between 45 and 53) of metadata tag "fancyTable" of {_p} to red stained glass pane named "&0"
	set slots (49 and (23 if {@enable-suggestions} is true, else 24)) of metadata tag "fancyTable" of {_p} to barrier named "&cClose" and getInvalidRecipeItem()
	set slots (10, 11, 12, 19, 20, 21, 28, 29 and 30) of metadata tag "fancyTable" of {_p} to air
	set slots (16, 25, 34) of metadata tag "fancyTable" of {_p} to getEmptySuggestionItem({_p}) if {@enable-suggestions} is true
	set metadata tag "fancyContainer" of {_p} to new InventoryCrafting((new ContainerWorkbench(-1, new PlayerInventory(null))), 3, 3, null)

function resetWorkbench(p: player):
	set slot (integers between 45 and 53) of metadata tag "fancyTable" of {_p} to red stained glass pane named "&0"
	set slots (49 and (23 if {@enable-suggestions} is true, else 24)) of metadata tag "fancyTable" of {_p} to barrier named "&cClose" and getInvalidRecipeItem()
	set slots (10, 11, 12, 19, 20, 21, 28, 29 and 30) of metadata tag "fancyTable" of {_p} to air
	set slots (16, 25, 34) of metadata tag "fancyTable" of {_p} to getEmptySuggestionItem({_p}) if {@enable-suggestions} is true

function openWorkbench(p: player):
	resetWorkbench({_p})
	open (metadata tag "fancyTable" of {_p}) to {_p}
	if {@enable-suggestions} is true:
		delete (metadata tag "suggestions" of {_p}) 
		displaySuggestions({_p}) if {_p} has permission "{@suggestions-permission}"

function getInvalidRecipeItem() :: item:
	return (knowledge book if {@enable-recipebook} is true, else barrier) named "&cRecipe Required" with lore "&7Add the items for a valid recipe", "&7in the crafting grid to the", "&7left!" and (("", "&eClick to view Recipe Book!") if {@enable-recipebook} is true, else {_})

function getEmptySuggestionItem(p: player) :: item:
	return (gray stained glass pane if {_p} has permission "{@suggestions-permission}", else red stained glass pane) named "&cQuick Crafting Slot" with lore "&7Quick crafting allows you to", "&7craft items without assembling", "&7the recipe." and ("" and "&cRequires &aVIP &cor higher!" if {_p} doesn't have permission "{@suggestions-permission}", else {_})

function updateWorkbench(p: player):
	wait a tick
	set {_ic} to metadata tag "fancyContainer" of {_p}
	loop (10, 11, 12, 19, 20, 21, 28, 29 and 30):
		add 1 to {_c}
		{_ic}.a({_c} - 1, CraftItemStack.asNMSCopy(random item of (slot loop-value ? {_} of (metadata tag "fancyTable" of {_p}))))
	set {_optional} to Bukkit.getServer().getHandle().b().aB.b().e().a(Recipes.a, {_ic}, null)
	if {_optional}.isPresent():
		set {_itemStack} to CraftItemStack.asBukkitCopy({_optional}.get().a({_ic}))
		set lore of {_itemStack} to lore of {_itemStack}, "&8&l&m-----------------", "&7This is the item you are" and "&7crafting."
		set slot (23 if {@enable-suggestions} is true, else 24) of (metadata tag "fancyTable" of {_p}) to {_itemStack}
		set slot (integers between 45 and 53) of metadata tag "fancyTable" of {_p} to (light green stained glass pane named "&0")
		set slot 49 of metadata tag "fancyTable" of {_p} to barrier named "&cClose"
	else:
		set slot (integers between 45 and 53) of metadata tag "fancyTable" of {_p} to red stained glass pane named "&0"
		set slots (49 and (23 if {@enable-suggestions} is true, else 24)) of metadata tag "fancyTable" of {_p} to barrier named "&cClose" and getInvalidRecipeItem()

function craftWorkbench(p: player, click: click type):
	set {_result} to slot (23 if {@enable-suggestions} is true, else 24) of metadata tag "fancyTable" of {_p}
	loop integers between 0 and 2:
		clear line ((size of lore of {_result}) - loop-value) of lore of {_result}
	if "%{_click}%" is "left mouse button" or "right mouse button" or "double click using mouse":
		((item amount of cursor slot of {_p}) + (item amount of {_result})) <= {_result}.getMaxStackSize()
		if 1 of {_p}'s cursor slot is 1 of {_result}:
			add (item amount of {_result}) to item amount of {_p}'s cursor slot
			removeItems({_p}, 1)
		else if type of {_p}'s cursor slot is air:
			set cursor slot of {_p} to {_result}
			removeItems({_p}, 1)
	else if "%{_click}%" is "left mouse button with shift" or "right mouse button with shift":
		loop (10, 11, 12, 19, 20, 21, 28, 29, 30):
			slot loop-value ? {_} of metadata tag "fancyTable" of {_p} is not air
			set {_amounts::%loop-value%} to item amount of slot loop-value ? {_} of metadata tag "fancyTable" of {_p}
		set {_amount} to (item amount of {_result}) * min({_amounts::*})
		while {_p} doesn't have enough space for {_amount} of {_result}:
			subtract item amount of {_result} from {_amount}
		give {_amount} of {_result} to {_p}
		removeItems({_p}, {_amount} / (item amount of {_result}))
	if {@enable-suggestions} is true:
		displaySuggestions({_p}) if {_p} has permission "{@suggestions-permission}"

function removeItems(p: player, count: numbers) :: object:
	loop (10, 11, 12, 19, 20, 21, 28, 29, 30):
		add 1 to {_c}
		set item amount of slot loop-value ? {_} of metadata tag "fancyTable" of {_p} to (item amount of slot loop-value ? {_} of metadata tag "fancyTable" of {_p}) - ({_count::%{_c}%} ? {_count::1})

function refreshRecipes():
	set {recipeMap} to new HashMap()
	set {recipeBook} to new HashMap()
	set {suggestionMap} to new HashMap()
	set {_recipeIterator} to Bukkit.recipeIterator()
	create section stored in {_recipeRefresh}:
		while {_recipeIterator}.hasNext():
			set {_recipe} to {_recipeIterator}.next()
			sum(1 if {_recipe} is instance of ShapedRecipe, else 0, 1 if {_recipe} is instance of ShapelessRecipe, else 0) > 0
			add 1 to {_c}
			{recipeMap}.put({_c}, {_recipe})
			{@enable-suggestions} is true
			set {_ingredients::*} to getRecipeItems({_recipe}) where [type of input is not air]
			set {_sumStorage} to chest inventory with 1 row
			add {_ingredients::*} to {_sumStorage}
			set {_sumIngredients::*} to slots (integers between 0 and 8) of {_sumStorage} where [type of input is not air]
			clear {_ingredientsKeys::*}
			loop {_sumIngredients::*}:
				set {_ingredientsKeys::%loop-value%} to name of loop-value ? raw name of loop-value
			set {_suggestionMapWriter} to {suggestionMap}
			loop (alphabetically sorted {_ingredientsKeys::*}):
				{_suggestionMapWriter}.put(loop-value, new HashMap()) if {_suggestionMapWriter}.get(loop-value) is not set
				set {_suggestionMapWriter} to {_suggestionMapWriter}.get(loop-value)
			{_suggestionMapWriter}.put({_recipe}.hashCode(), {_recipe})
	run section {_recipeRefresh} async and wait

function displaySuggestions(p: player):
	create section with {_p} stored in {_displaySuggestions}:
		set {_suggestions::*} to getSuggestedRecipes({_p})
		set {_oldSuggestions::*} to ...(metadata tag "suggestions" of {_p})
		loop {_oldSuggestions::*}:
			add (1 if loop-value is {_suggestions::%loop-index%}, else -1) to {_c}
		stop trigger if {_c} is size of {_suggestions::*}
		set metadata tag "suggestions" of {_p} to [{_suggestions::*}]
		set {_suggestionsSlots::*} to (16, 25, 34)
		set slots {_suggestionsSlots::*} of metadata tag "fancyTable" of {_p} to getEmptySuggestionItem({_p})
		loop {_suggestions::*}:
			set {_itemStack} to loop-value.getResult()
			set {_ingredients::*} to getRecipeItems(loop-value) where [type of input is not air]
			set {_sumStorage} to chest inventory with 1 row
			add {_ingredients::*} to {_sumStorage}
			set {_sumIngredients::*} to slots (integers between 0 and 8) of {_sumStorage} where [type of input is not air]
			add "&0", "&9Ingredients:" to lore of {_itemStack}
			loop {_sumIngredients::*}:
				add "&7%item amount of loop-value-2%x %name of loop-value-2 ? proper case (raw name of loop-value-2).replace("minecraft:", "").replace("_", " ")%" to lore of {_itemStack}
			add "&0" and "&eClick to craft!" to lore of {_itemStack}
			set slot {_suggestionsSlots::%loop-index%} of metadata tag "fancyTable" of {_p} to {_itemStack}
	run section {_displaySuggestions} async with {_p}

function getSuggestedRecipes(p: player, count: number = 3) :: objects:
	set {_tableContents::*} to slots (10, 11, 12, 19, 20, 21, 28, 29 and 30) of metadata tag "fancyTable" of {_p}
	set {_playerItems::*} to ({_tableContents::*}, all items in {_p}'s inventory, cursor slot of {_p}) where [type of input is not air]
	return {_} if size of {_playerItems::*} is 0
	loop {_playerItems::*}:
		set {_playerItemsKeys::%name of loop-value ? raw name of loop-value%} to name of loop-value ? raw name of loop-value
	set {_sortedPlayerItemsKeys::*} to alphabetically sorted {_playerItemsKeys::*}
	set {_c} to 0
	set {_sumStorage} to chest inventory with 1 row
	set {_playerInventoryCopy} to chest inventory with 1 row
	add {_playerItems::*} to {_playerInventoryCopy}
	set {_suggestionMapReader::%{_c}%} to {suggestionMap}
	while true is true:
		loop {_suggestionMapReader::*}:
			set {_recipes::*} to ...(loop-value).values() where [input is instance of Recipe]
			loop {_recipes::*}:
				set slots (integers between 0 and 8) of {_sumStorage} to air
				add getRecipeItems(loop-value-2) to {_sumStorage}
				set {_sumIngredients::*} to slots (integers between 0 and 8) of {_sumStorage} where [type of input is not air]
				set {_suggestions::%loop-value-2.hashCode()%} to loop-value-2 if {_playerInventoryCopy} has {_sumIngredients::*}
				return {_suggestions::*} if size of {_suggestions::*} is {_count}
			loop {_sortedPlayerItemsKeys::*}:
				add 1 to {_c}
				set {_suggestionMapReader::%{_c}%} to (loop-value-1).get(loop-value-2)
			delete {_suggestionMapReader::%loop-index%}
		exit this section if size of {_suggestionMapReader::*} is 0
	return {_suggestions::*}
			
function openRecipeBook(p: player, page: number = 1):
	set {_page} to round({_page})
	if {recipeBook}.get({_page}) is not set:
		set {_gui} to chest inventory with 8 rows named "Recipe Book (Page %{_page}%)"
		set slots (integers between 0 and 44) of {_gui} to light gray stained glass pane named "&0"
		set slots ((integers between 0 and 9), 18, 27, 36, 45, 17, 26, 35, (integers between 44 and 53)) of {_gui} to black stained glass pane named "&0"
		set slots (45, 49 and 53) of {_gui} to (arrow named "&aPage %{_page} - 1%" if {_page} > 1, else black stained glass pane named "&0"), barrier named "&cClose" and (arrow named "&aPage %{_page} + 1%" if {recipeMap}.size() > {_page} * 28, else black stained glass pane named "&0")
		{recipeBook}.put({_page}, {_gui})
		create section with {_gui}, {_page} stored in {_loadRecipes}:
			set {_offset} to ({_page} - 1) * 28
			loop (integers between 10 and 16), (integers between 19 and 25), (integers between 28 and 34) and (integers between 37 and 43):
				add 1 to {_c}
				set {_itemStack} to {recipeMap}.get(({_offset} + {_c}).longValue()).getResult()
				exit loop if {_itemStack} is not set
				set lore of {_itemStack} to lore of {_itemStack}, "&8&l&m-----------------", "&7Click to veiw recipe" and "&7for &e%name of {_itemStack} ? proper case (raw name of {_itemStack}).replace("minecraft:", "").replace("_", " ")%"
				set slot loop-number ? {_} of {_gui} to {_itemStack}
		run section {_loadRecipes} async with {_gui} and {_page}
	open {recipeBook}.get({_page}) to {_p}

function getRecipeItems(recipe: object) :: objects:
	loop 9 times:
		set {_items::%loop-value%} to air
	if {_recipe} is instance of ShapedRecipe:
		loop ...{_recipe}.getShape():
			set {_recipeLine} to loop-value
			while length of {_recipeLine} is not 3:
				set {_recipeLine} to join {_recipeLine} and "-"
			set {_recipeLines} to join {_recipeLines} and {_recipeLine}
		set {_shape::*} to (split {_recipeLines} at "") where [length of input > 0]
		loop 9 times:
			set {_items::%loop-value%} to {_recipe}.getIngredientMap().get({_shape::%loop-value%}.charAt(0)) ? air
	else if {_recipe} is instance of ShapelessRecipe:
		set {_c} to 0
		loop reversed ...{_recipe}.getIngredientList():
			add 1 to {_c}
			set {_items::%{_c}%} to loop-value
	return {_items::*}

on InventoryDragEvent:
	updateWorkbench(event.getWhoClicked()) if event.getWhoClicked()'s current inventory is (metadata tag "fancyTable" of event.getWhoClicked())

on inventory click:
	updateWorkbench(player) if player's current inventory is (metadata tag "fancyTable" of player)
	event-inventory is not inventory of player
	if event-inventory is (metadata tag "fancyTable" of player):
		cancel event if (10, 11, 12, 19, 20, 21, 28, 29, 30) doesn't contain index of event-slot
		close player's inventory if index of event-slot is 49
		if (index of event-slot) is (23 if {@enable-suggestions} is true, else 24):
			if event-slot is not getInvalidRecipeItem():
				craftWorkbench(player, click type)
			else:
				openRecipeBook(player) if {@enable-recipebook} is true
		if (16, 25, 34) contains index of event-slot:
			{@enable-suggestions} is true
			set {_recipe} to (metadata tag "suggestions" of player)[mod(index of event-slot, 8)]
			set {_sumStorage} to chest inventory with 1 row
			add getRecipeItems({_recipe}) to {_sumStorage}
			set {_sumIngredients::*} to slots (integers between 0 and 8) of {_sumStorage} where [type of input is not air]
			player has {_sumIngredients::*}
			set {_result} to {_recipe}.getResult()
			if "%click type%" is "left mouse button" or "right mouse button" or "double click using mouse":
				((item amount of cursor slot of player) + (item amount of {_result})) <= {_result}.getMaxStackSize()
				if 1 of player's cursor slot is 1 of {_result}:
					add (item amount of {_result}) to item amount of player's cursor slot
					remove {_sumIngredients::*} from player
				else if type of player's cursor slot is air:
					set cursor slot of player to {_result}
					remove {_sumIngredients::*} from player
				displaySuggestions(player)
			else if "%click type%" is "left mouse button with shift" or "right mouse button with shift":
				while player has {_sumIngredients::*}:
					exit this section if player doesn't have enough space for {_result}
					remove {_sumIngredients::*} from player
					give {_result} to player
				displaySuggestions(player)
	if name of event-inventory partially matches "Recipe Book \(Page [0-9]+\)":
		cancel event
		set {_recipeSlots::*} to (integers between 10 and 16), (integers between 19 and 25), (integers between 28 and 34) and (integers between 37 and 43)
		openWorkbench(player) if index of event-slot is 49
		if type of event-slot is arrow:
			set {_page} to first element of ((name of event-slot parsed as "&aPage %number%") and 1)
			openRecipeBook(player, {_page}) if index of event-slot is 45 or 53
		if {_recipeSlots::*} contains index of event-slot:
			set {_page} to first element of ((name of event-inventory parsed as "Recipe Book \(Page %number%\)") and 1)
			event-slot is not light gray stained glass pane named "&0"
			set metadata tag "lastPage" of player to {_page}
			set {_gui} to chest inventory with 8 rows named "Preview recipe for: %name of event-slot ? proper case (raw name of event-slot).replace("minecraft:", "").replace("_", " ")%"
			set slots (integers between 0 and 53) of {_gui} to black stained glass pane named "&0"
			set slot 49 of {_gui} to barrier named "&cClose"
			set {_offset} to ({_page} - 1) * 28
			loop {_recipeSlots::*}:
				add 1 to {_c}
				exit loop if loop-value is index of event-slot
			set {_recipe} to {recipeMap}.get(({_offset} + {_c}).longValue())
			set {_recipeDisplaySlots::*} to (10, 11, 12, 19, 20, 21, 28, 29 and 30)
			set slots {_recipeDisplaySlots::*} of {_gui} to light gray stained glass pane named "&0"
			create section with {_recipe}, {_recipeDisplaySlots::*} stored in {_loadIngredients}:
				set {_items::*} to getRecipeItems({_recipe})
				loop {_recipeDisplaySlots::*}:
					set slot loop-value of {_gui} to {_items::%loop-index%} if type of {_items::%loop-index%} is not air, else light gray stained glass pane named "&0"
			run section {_loadIngredients} async with {_recipe} and {_recipeDisplaySlots::*}
			set slot 24 of {_gui} to {_recipe}.getResult()
			open {_gui} to player
	if name of event-inventory partially matches "Preview recipe for: ":
		cancel event
		openRecipeBook(player, metadata tag "lastPage" of player) if index of event-slot is 49

on inventory close:
	player's current inventory is (metadata tag "fancyTable" of player)
	set {_v} to (vector from yaw player's yaw and pitch player's pitch) ** (vector(0.3, 0.3, 0.3))
	loop (10, 11, 12, 19, 20, 21, 28, 29 and 30):
		if player has enough space for (slot loop-value ? {_} of (metadata tag "fancyTable" of player)):
			give (slot loop-value ? {_} of (metadata tag "fancyTable" of player)) to player
		else:
			drop (slot loop-value ? {_} of (metadata tag "fancyTable" of player)) 1 above player's location
			set velocity of last dropped item to {_v}

on rightclick on crafting table:
	stop trigger if {@enable} is false
	stop trigger if player is sneaking
	cancel event
	openWorkbench(player)

on join:
	initWorkbench(player)

on load:
	stop trigger if {@enable} is false
	loop all players:
		initWorkbench(loop-player)
	wait {@recipebook-register-delay}
	refreshRecipes() if sum(1 if {@enable-recipebook} is true, else 0, 1 if {@enable-suggestions} is true, else 0) > 0

on unload:
	loop all players:
		close loop-player's inventory if loop-player's current inventory is (metadata tag "fancyTable" of loop-player)
		close loop-player's inventory if name of (loop-player's current inventory) partially matches "Recipe Book \(Page [0-9]+\)"
		close loop-player's inventory if name of (loop-player's current inventory) partially matches "Preview recipe for: "
	delete {recipeMap}, {recipeBook} and {suggestionMap}