Skip to content

Commit 1d20038

Browse files
authored
Merge pull request #229 from balugaq/feat/ingredients-calculation
Feat/ingredients calculation
2 parents 751e629 + de45a06 commit 1d20038

15 files changed

Lines changed: 712 additions & 29 deletions

File tree

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.github.pylonmc.pylon.core.guide.pages.SearchItemsAndFluidsPage
66
import io.github.pylonmc.pylon.core.guide.pages.SettingsAndInfoPage
77
import io.github.pylonmc.pylon.core.guide.pages.base.GuidePage
88
import io.github.pylonmc.pylon.core.guide.pages.fluid.FluidsPage
9+
import io.github.pylonmc.pylon.core.guide.pages.item.ItemIngredientsPage
910
import io.github.pylonmc.pylon.core.guide.pages.research.ResearchesPage
1011
import io.github.pylonmc.pylon.core.item.PylonItem
1112
import io.github.pylonmc.pylon.core.item.base.PylonInteractor
@@ -61,22 +62,25 @@ class PylonGuide(stack: ItemStack) : PylonItem(stack), PylonInteractor {
6162
val hiddenResearches: MutableSet<NamespacedKey> = mutableSetOf()
6263

6364
@JvmStatic
64-
var fluidsPage = FluidsPage()
65+
val fluidsPage = FluidsPage()
6566

6667
@JvmStatic
67-
var infoPage = InfoPage()
68+
val infoPage = InfoPage()
6869

6970
@JvmStatic
70-
var researchesPage = ResearchesPage()
71+
val researchesPage = ResearchesPage()
7172

7273
@JvmStatic
73-
var rootPage = RootPage()
74+
val rootPage = RootPage()
7475

7576
@JvmStatic
76-
var searchItemsAndFluidsPage = SearchItemsAndFluidsPage()
77+
val searchItemsAndFluidsPage = SearchItemsAndFluidsPage()
7778

7879
@JvmStatic
79-
var settingsAndInfoPage = SettingsAndInfoPage()
80+
val settingsAndInfoPage = SettingsAndInfoPage()
81+
82+
@JvmStatic
83+
fun ingredientsPage(stack: ItemStack) = ItemIngredientsPage(stack)
8084

8185
/**
8286
* Hide an item from showing up in searches

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/FluidButton.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,20 @@ import org.bukkit.NamespacedKey
1313
import org.bukkit.entity.Player
1414
import org.bukkit.event.inventory.ClickType
1515
import org.bukkit.event.inventory.InventoryClickEvent
16+
import org.bukkit.inventory.ItemStack
1617
import xyz.xenondevs.invui.item.impl.AbstractItem
1718

18-
open class FluidButton(val key: NamespacedKey, val amount: Double?) : AbstractItem() {
19-
19+
open class FluidButton(val key: NamespacedKey, val amount: Double?, val preDisplayDecorator: (ItemStackBuilder) -> ItemStackBuilder) : AbstractItem() {
20+
constructor(key: NamespacedKey, amount: Double?) : this(key, amount, { it })
2021
constructor(key: NamespacedKey) : this(key, null)
2122

2223
val fluid = PylonRegistry.FLUIDS[key] ?: throw IllegalArgumentException("There is no fluid with key $key")
2324

2425
override fun getItemProvider() = try {
2526
if (amount == null) {
26-
fluid.getItem()
27+
preDisplayDecorator.invoke(fluid.getItem())
2728
} else {
28-
fluid.getItem()
29+
preDisplayDecorator.invoke(fluid.getItem())
2930
.name(Component.translatable(
3031
"pylon.pyloncore.guide.button.fluid.name",
3132
PylonArgument.of("fluid", fluid.getItem().stack.getData(DataComponentTypes.ITEM_NAME)!!),

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ItemButton.kt

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import xyz.xenondevs.invui.item.impl.AbstractItem
2828
import xyz.xenondevs.invui.item.impl.AutoCycleItem
2929
import xyz.xenondevs.invui.item.impl.SimpleItem
3030

31-
class ItemButton(val stack: ItemStack) : AbstractItem() {
31+
class ItemButton(val stack: ItemStack, val preDisplayDecorator: (ItemStack, Player) -> ItemStack) : AbstractItem() {
32+
constructor(stack: ItemStack) : this(stack, { stack: ItemStack, player: Player -> stack})
3233

3334
constructor(key: NamespacedKey) : this(
3435
PylonRegistry.ITEMS[key]?.itemStack ?: throw IllegalArgumentException("There is no item with key $key")
@@ -37,22 +38,23 @@ class ItemButton(val stack: ItemStack) : AbstractItem() {
3738
@Suppress("UnstableApiUsage")
3839
override fun getItemProvider(player: Player): ItemProvider {
3940
try {
40-
val item = PylonItem.fromStack(stack)
41+
val displayStack = preDisplayDecorator.invoke(stack.clone(), player)
42+
val item = PylonItem.fromStack(displayStack)
4143
if (item == null) {
42-
return ItemStackBuilder.of(stack)
44+
return ItemStackBuilder.of(displayStack)
4345
}
4446

45-
val placeholders = item.getPlaceholders()
46-
val builder = ItemStackBuilder.of(stack.clone())
47-
.editData(DataComponentTypes.LORE) { lore ->
48-
ItemLore.lore(lore.lines().map { it.withArguments(placeholders) })
49-
}
47+
val placeholders = item.getPlaceholders()
48+
val builder = ItemStackBuilder.of(displayStack.clone())
49+
.editData(DataComponentTypes.LORE) { lore ->
50+
ItemLore.lore(lore.lines().map { it.withArguments(placeholders) })
51+
}
5052

51-
// buffoonery to bypass InvUI's translation mess
52-
// Search message 'any idea why items displayed in InvUI are not having placeholders' on Pylon's Discord for more info
53-
builder.editData(DataComponentTypes.ITEM_NAME) {
54-
it.withArguments(placeholders)
55-
}
53+
// buffoonery to bypass InvUI's translation mess
54+
// Search message 'any idea why items displayed in InvUI are not having placeholders' on Pylon's Discord for more info
55+
builder.editData(DataComponentTypes.ITEM_NAME) {
56+
it.withArguments(placeholders)
57+
}
5658

5759
if (item.isDisabled) {
5860
builder.set(DataComponentTypes.ITEM_MODEL, Material.STRUCTURE_VOID.key)
@@ -138,6 +140,15 @@ class ItemButton(val stack: ItemStack) : AbstractItem() {
138140
return ItemButton(stack)
139141
}
140142

143+
@JvmStatic
144+
fun fromStack(stack: ItemStack?, preDisplayDecorator: (ItemStack, Player) -> ItemStack): Item {
145+
if (stack == null) {
146+
return SimpleItem(ItemStack(Material.AIR))
147+
}
148+
149+
return ItemButton(stack, preDisplayDecorator)
150+
}
151+
141152
@JvmStatic
142153
fun fromChoice(choice: RecipeChoice?): Item = when (choice) {
143154
is RecipeChoice.MaterialChoice -> if (choice.choices.size == 1) {

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/PageButton.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
package io.github.pylonmc.pylon.core.guide.button
22

33
import io.github.pylonmc.pylon.core.guide.pages.base.GuidePage
4-
import io.github.pylonmc.pylon.core.guide.pages.base.SimpleStaticGuidePage
4+
import io.github.pylonmc.pylon.core.guide.pages.RootPage;
55
import org.bukkit.entity.Player
66
import org.bukkit.event.inventory.ClickType
77
import org.bukkit.event.inventory.InventoryClickEvent
88
import xyz.xenondevs.invui.item.impl.AbstractItem
99

10+
/**
11+
* When player click on the button, we'll show the `page` for the player
12+
*
13+
* Example:
14+
* ```
15+
* PagedGui.items()
16+
* .setStructure(
17+
* "# e # # # # # s #",
18+
* "x x x x x x x x x",
19+
* "x x x x x x x x x",
20+
* "x x x x x x x x x",
21+
* "x x x x x x x x x",
22+
* "x x x x x x x x x",
23+
* )
24+
* .addIngredient('#', GuiItems.background())
25+
* .addIngredient('e', PageButton(PylonGuide.settingsAndInfoPage)) // When the player clicks on the `e`, we'll show the `settingsAndInfoPage`
26+
* .addIngredient('s', PageButton(PylonGuide.searchItemsAndFluidsPage)) // When the player clicks on the `s`, we'll show the `searchItemsAndFluidsPage`
27+
* .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL)
28+
* ```
29+
*
30+
* @author LordIdra
31+
* @see RootPage
32+
*/
1033
open class PageButton(val page: GuidePage) : AbstractItem() {
1134

1235
override fun getItemProvider() = page.item

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/fluid/FluidRecipesPage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ open class FluidRecipesPage(fluidKey: NamespacedKey) : GuidePage {
3636

3737
open fun getHeader(player: Player, pages: List<Gui>) = PagedGui.guis()
3838
.setStructure(
39-
"< b # # # # # s >",
39+
"< b # # g # # s >",
4040
"x x x x x x x x x",
4141
"x x x x x x x x x",
4242
"x x x x x x x x x",
@@ -46,6 +46,7 @@ open class FluidRecipesPage(fluidKey: NamespacedKey) : GuidePage {
4646
.addIngredient('#', GuiItems.background())
4747
.addIngredient('<', if (pages.size > 1) GuiItems.pagePrevious() else GuiItems.background())
4848
.addIngredient('b', BackButton(player))
49+
.addIngredient('g', PageButton(PylonGuide.ingredientsPage(fluid.getItem().build())))
4950
.addIngredient('s', PageButton(PylonGuide.searchItemsAndFluidsPage))
5051
.addIngredient('>', if (pages.size > 1) GuiItems.pageNext() else GuiItems.background())
5152
.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package io.github.pylonmc.pylon.core.guide.pages.item
2+
3+
import io.github.pylonmc.pylon.core.guide.button.BackButton
4+
import io.github.pylonmc.pylon.core.guide.button.FluidButton
5+
import io.github.pylonmc.pylon.core.guide.button.ItemButton
6+
import io.github.pylonmc.pylon.core.guide.pages.base.SimpleStaticGuidePage
7+
import io.github.pylonmc.pylon.core.i18n.PylonArgument
8+
import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder
9+
import io.github.pylonmc.pylon.core.recipe.Container
10+
import io.github.pylonmc.pylon.core.recipe.IngredientCalculation
11+
import io.github.pylonmc.pylon.core.recipe.IngredientCalculator
12+
import io.github.pylonmc.pylon.core.util.gui.GuiItems
13+
import io.github.pylonmc.pylon.core.util.pylonKey
14+
import io.papermc.paper.datacomponent.DataComponentTypes
15+
import net.kyori.adventure.text.Component
16+
import net.kyori.adventure.translation.GlobalTranslator
17+
import org.bukkit.Material
18+
import org.bukkit.entity.Player
19+
import org.bukkit.inventory.ItemStack
20+
import xyz.xenondevs.invui.gui.Gui
21+
import xyz.xenondevs.invui.gui.PagedGui
22+
import xyz.xenondevs.invui.gui.structure.Markers
23+
import xyz.xenondevs.invui.item.Item
24+
import xyz.xenondevs.invui.item.impl.SimpleItem
25+
import kotlin.math.max
26+
27+
/**
28+
* Magic numbers:
29+
* 27 -> 27 "i" in sub-page, which means input items/fluid
30+
* 9 -> 9 "o" in sub-page, which means intermediates
31+
*
32+
* @author balugaq
33+
*/
34+
open class ItemIngredientsPage(val stack: ItemStack) : SimpleStaticGuidePage(
35+
pylonKey("item_ingredients"),
36+
Material.SCULK_SENSOR
37+
) {
38+
override fun getKey() = KEY
39+
40+
// page is 0 based
41+
open fun getSubPage(player: Player, stack: ItemStack, calculation: IngredientCalculation, page: Int, maxPage: Int) =
42+
PagedGui.guis()
43+
.setStructure(
44+
"i i i i i i i i i",
45+
"i i i i i i i i i",
46+
"i i i i i i i i i",
47+
"m a x x x x x x x",
48+
"f o o o o o o o o",
49+
)
50+
.addIngredient('x', GuiItems.background())
51+
.addIngredient('f', flatWithAmount(Container.of(stack, calculation.outputAmount.toInt())))
52+
.addIngredient('m', mainProductButton)
53+
.addIngredient(
54+
'a', if (!calculation.intermediates.isEmpty()) {
55+
intermediatesButton
56+
} else {
57+
GuiItems.background()
58+
}
59+
)
60+
.addModifier {
61+
for (i in 0..26) {
62+
it.setItem(i, flatWithAmount(calculation.inputs.getOrNull(27 * page + i)))
63+
}
64+
}
65+
.addModifier {
66+
for (i in 1..8) {
67+
it.setItem(36 + i, flatWithAmount(calculation.intermediates.getOrNull(9 * page + i)))
68+
}
69+
}
70+
.build()
71+
72+
open fun getGuiHeader(player: Player, pages: List<Gui>) = PagedGui.guis()
73+
.setStructure(
74+
"< b # # # # # # >",
75+
"x x x x x x x x x",
76+
"x x x x x x x x x",
77+
"x x x x x x x x x",
78+
"x x x x x x x x x",
79+
"x x x x x x x x x",
80+
)
81+
.addIngredient('#', GuiItems.background())
82+
.addIngredient('<', if (pages.size > 1) GuiItems.pagePrevious() else GuiItems.background())
83+
.addIngredient('b', BackButton(player))
84+
.addIngredient('>', if (pages.size > 1) GuiItems.pageNext() else GuiItems.background())
85+
.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL)
86+
87+
override fun getGui(player: Player): Gui {
88+
val pages = mutableListOf<Gui>()
89+
val calculation = IngredientCalculator.calculateFinal(stack).flat()
90+
val maxPage = max(calculation.inputs.size / 27, calculation.intermediates.size / 9)
91+
for (i in 0..maxPage) {
92+
pages += getSubPage(player, stack, calculation, i, maxPage)
93+
}
94+
95+
val gui = getGuiHeader(player, pages)
96+
for (page in pages) {
97+
gui.addContent(page)
98+
}
99+
return gui.build()
100+
}
101+
102+
companion object {
103+
val KEY = pylonKey("item_ingredients")
104+
}
105+
106+
/**
107+
* Display amount in the input/intermediate stacks
108+
*/
109+
fun flatWithAmount(container: Container?): Item {
110+
if (container == null) return GuiItems.background()
111+
112+
return when (container) {
113+
is Container.Item -> ItemButton.fromStack(container.item) { item: ItemStack, player: Player ->
114+
ItemStackBuilder.of(item).name(
115+
GlobalTranslator.render(Component.translatable(
116+
"pylon.pyloncore.message.guide.ingredients-page.item",
117+
PylonArgument.of("item_ingredients_page_amount", container.item.amount),
118+
PylonArgument.of("item_ingredients_page_item", container.item.getData(DataComponentTypes.ITEM_NAME)!!)),
119+
player.locale())
120+
).build()
121+
}
122+
123+
is Container.Fluid -> FluidButton(container.fluid.key, container.amountMillibuckets)
124+
}
125+
}
126+
127+
val intermediatesButton: Item = SimpleItem(
128+
ItemStackBuilder.of(Material.ORANGE_STAINED_GLASS_PANE)
129+
.amount(1)
130+
.name(Component.translatable("pylon.pyloncore.guide.button.intermediates.name"))
131+
.lore(Component.translatable("pylon.pyloncore.guide.button.intermediates.lore"))
132+
)
133+
134+
val mainProductButton: Item = SimpleItem(
135+
ItemStackBuilder.of(Material.GREEN_STAINED_GLASS_PANE)
136+
.amount(1)
137+
.name(Component.translatable("pylon.pyloncore.guide.button.main_product.name"))
138+
.lore(Component.translatable("pylon.pyloncore.guide.button.main_product.lore"))
139+
)
140+
}

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/item/ItemRecipesPage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ open class ItemRecipesPage(val stack: ItemStack) : GuidePage {
3636

3737
open fun getHeader(player: Player, pages: List<Gui>) = PagedGui.guis()
3838
.setStructure(
39-
"< b # # # # # s >",
39+
"< b # # g # # s >",
4040
"x x x x x x x x x",
4141
"x x x x x x x x x",
4242
"x x x x x x x x x",
@@ -46,6 +46,7 @@ open class ItemRecipesPage(val stack: ItemStack) : GuidePage {
4646
.addIngredient('#', GuiItems.background())
4747
.addIngredient('<', if (pages.size > 1) GuiItems.pagePrevious() else GuiItems.background())
4848
.addIngredient('b', BackButton(player))
49+
.addIngredient('g', PageButton(PylonGuide.ingredientsPage(stack)))
4950
.addIngredient('s', PageButton(PylonGuide.searchItemsAndFluidsPage))
5051
.addIngredient('>', if (pages.size > 1) GuiItems.pageNext() else GuiItems.background())
5152
.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package io.github.pylonmc.pylon.core.item.base
2+
3+
interface NotPlaceable {
4+
}

pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/recipe/FluidOrItem.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ sealed interface FluidOrItem {
2424
fun of(choice: RecipeChoice): List<FluidOrItem>
2525
= when (choice) {
2626
is RecipeChoice.ExactChoice -> listOf(of(choice.itemStack))
27-
is RecipeChoice.MaterialChoice -> choice.choices.map { of(ItemStack(it)) }
27+
is RecipeChoice.MaterialChoice -> listOf(of(choice.itemStack))
2828
else -> emptyList()
2929
}
3030
}

0 commit comments

Comments
 (0)