mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +09:00
fix: techtree button not working if craftingUI is loaded independently
This commit is contained in:
@@ -1,12 +1,9 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameactors
|
package net.torvald.terrarum.modulebasegame.gameactors
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx
|
|
||||||
import net.torvald.gdx.graphics.Cvec
|
import net.torvald.gdx.graphics.Cvec
|
||||||
import net.torvald.spriteanimation.SheetSpriteAnimation
|
import net.torvald.spriteanimation.SheetSpriteAnimation
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.App.printdbg
|
|
||||||
import net.torvald.terrarum.audio.MusicContainer
|
import net.torvald.terrarum.audio.MusicContainer
|
||||||
import net.torvald.terrarum.audio.decibelsToFullscale
|
|
||||||
import net.torvald.terrarum.audio.dsp.Gain
|
import net.torvald.terrarum.audio.dsp.Gain
|
||||||
import net.torvald.terrarum.audio.dsp.NullFilter
|
import net.torvald.terrarum.audio.dsp.NullFilter
|
||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
@@ -18,9 +15,7 @@ import net.torvald.terrarum.langpack.Lang
|
|||||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UICrafting
|
import net.torvald.terrarum.modulebasegame.ui.UICrafting
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull
|
|
||||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||||
import net.torvald.unsafe.UnsafeHelper
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2023-12-05.
|
* Created by minjaesong on 2023-12-05.
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameactors
|
package net.torvald.terrarum.modulebasegame.gameactors
|
||||||
|
|
||||||
import net.torvald.terrarum.BlockCodex
|
import net.torvald.terrarum.BlockCodex
|
||||||
import net.torvald.terrarum.INGAME
|
|
||||||
import net.torvald.terrarum.Terrarum
|
|
||||||
import net.torvald.terrarum.blockproperties.Block
|
import net.torvald.terrarum.blockproperties.Block
|
||||||
import net.torvald.terrarum.gameactors.AVKey
|
import net.torvald.terrarum.gameactors.AVKey
|
||||||
import net.torvald.terrarum.langpack.Lang
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UICrafting
|
import net.torvald.terrarum.modulebasegame.ui.UICrafting
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull
|
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UIWallCalendar
|
|
||||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,594 +1,84 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.ui
|
package net.torvald.terrarum.modulebasegame.ui
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
|
||||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.App
|
||||||
import net.torvald.terrarum.App.*
|
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
||||||
import net.torvald.terrarum.gameactors.AVKey
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
import net.torvald.terrarum.gameitems.GameItem
|
import net.torvald.terrarum.ui.UICanvas
|
||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.ui.UIItemHorizontalFadeSlide
|
||||||
import net.torvald.terrarum.gameitems.isWall
|
|
||||||
import net.torvald.terrarum.itemproperties.CraftingCodex
|
|
||||||
import net.torvald.terrarum.langpack.Lang
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.CraftingStation
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.InventoryPair
|
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes.tooltipShowing
|
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.listGap
|
|
||||||
import net.torvald.terrarum.modulebasegame.ui.UITemplateHalfInventory.Companion.INVENTORY_NAME_TEXT_GAP
|
|
||||||
import net.torvald.terrarum.ui.*
|
|
||||||
import net.torvald.terrarum.ui.UIItemCatBar.Companion.FILTER_CAT_ALL
|
|
||||||
import net.torvald.unicode.getKeycapPC
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This UI has inventory, but it's just there to display all craftable items and should not be serialised.
|
* Created by minjaesong on 2024-03-02.
|
||||||
*
|
|
||||||
* Created by minjaesong on 2022-03-10.
|
|
||||||
*/
|
*/
|
||||||
class UICrafting(val full: UIInventoryFull?) : UICanvas(
|
class UICrafting(val full: UIInventoryFull?) : UICanvas(
|
||||||
toggleKeyLiteral = if (full == null) "control_key_inventory" else null,
|
toggleKeyLiteral = if (full == null) "control_key_inventory" else null,
|
||||||
toggleButtonLiteral = if (full == null) "control_gamepad_start" else null
|
toggleButtonLiteral = if (full == null) "control_gamepad_start" else null
|
||||||
), HasInventory {
|
) {
|
||||||
|
|
||||||
override var width = Toolkit.drawWidth
|
override var width = Toolkit.drawWidth
|
||||||
override var height = App.scr.height
|
override var height = App.scr.height
|
||||||
|
|
||||||
private val playerThings = UITemplateHalfInventory(this, false).also { pt ->
|
internal val transitionalCraftingUI = UICraftingWorkbench(full, this)
|
||||||
pt.itemListTouchDownFun = { gameItem, _, _, _, theButton -> if (gameItem != null) {
|
internal val transitionalTechtreePanel = UITechView(full, this)
|
||||||
val recipe = recipeClicked
|
val transitionPanel = UIItemHorizontalFadeSlide(
|
||||||
val itemID = gameItem.dynamicID
|
this,
|
||||||
|
(width - UIInventoryFull.internalWidth) / 2,
|
||||||
|
UIInventoryFull.INVENTORY_CELLS_OFFSET_Y(),
|
||||||
|
width,
|
||||||
|
App.scr.height,
|
||||||
|
0f,
|
||||||
|
listOf(transitionalCraftingUI, transitionalTechtreePanel),
|
||||||
|
listOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
private val uis = listOf(transitionalCraftingUI, transitionalTechtreePanel)
|
||||||
|
|
||||||
// change ingredient used
|
|
||||||
if (recipe != null) {
|
|
||||||
// don't rely on highlightedness of the button to determine the item on the button is the selected
|
|
||||||
// ingredient (because I don't fully trust my code lol)
|
|
||||||
val targetItemToAlter =
|
|
||||||
recipe.ingredients.filter { (key, mode) -> // altering recipe doesn't make sense if player selected a recipe that requires no tag-ingredients
|
|
||||||
val tags = key.split(',')
|
|
||||||
val wantsWall = tags.contains("WALL")
|
|
||||||
(mode == CraftingCodex.CraftingItemKeyMode.TAG && gameItem.hasAllTags(tags) && (wantsWall == gameItem.originalID.isWall())) // true if (wants wall and is wall) or (wants no wall and is not wall)
|
|
||||||
}.let {
|
|
||||||
if (it.size > 1)
|
|
||||||
println(
|
|
||||||
"[UICrafting] Your recipe seems to have two similar ingredients defined\n" +
|
|
||||||
"affected ingredients: ${it.joinToString()}\n" +
|
|
||||||
"the recipe: ${recipe}"
|
|
||||||
)
|
|
||||||
it.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
targetItemToAlter?.let { (key, mode) ->
|
|
||||||
val oldItem = _getItemListIngredients().getInventory().first { (itm, qty) ->
|
|
||||||
val tags = key.split(',')
|
|
||||||
val wantsWall = tags.contains("WALL")
|
|
||||||
(mode == CraftingCodex.CraftingItemKeyMode.TAG && ItemCodex[itm]!!.hasAllTags(tags) && (wantsWall == itm.isWall())) // true if (wants wall and is wall) or (wants no wall and is not wall)
|
|
||||||
}
|
|
||||||
changeIngredient(recipe, oldItem, itemID)
|
|
||||||
refreshCraftButtonStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// show all the items that can be made using this ingredient
|
|
||||||
else {
|
|
||||||
itemListCraftable.rebuild(arrayOf(itemID))
|
|
||||||
pt.itemList.clearForceHighlightList()
|
|
||||||
pt.itemList.addToForceHighlightList(listOf(itemID))
|
|
||||||
|
|
||||||
if (itemListCraftable.craftingRecipes.isEmpty()) {
|
|
||||||
pt.itemList.clearForceHighlightList()
|
|
||||||
itemListCraftable.rebuild(FILTER_CAT_ALL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pt.itemList.clearForceHighlightList()
|
|
||||||
recipeClicked = null
|
|
||||||
filterPlayerListUsing(recipeClicked)
|
|
||||||
highlightCraftingCandidateButton(null)
|
|
||||||
ingredients.clear()
|
|
||||||
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val catIcons = CommonResourcePool.getAsTextureRegionPack("inventory_category")
|
|
||||||
|
|
||||||
|
|
||||||
internal val itemListCraftable: UIItemCraftingCandidateGrid // might be changed to something else
|
|
||||||
internal val itemListIngredients: UIItemInventoryItemGrid // this one is definitely not to be changed
|
|
||||||
private val buttonCraft: UIItemTextButton
|
|
||||||
private val spinnerCraftCount: UIItemSpinner
|
|
||||||
|
|
||||||
private val ingredients = FixtureInventory() // this one is definitely not to be changed
|
|
||||||
|
|
||||||
private val negotiator = object : InventoryTransactionNegotiator() {
|
|
||||||
override fun accept(player: FixtureInventory, fixture: FixtureInventory, item: GameItem, amount: Long) {
|
|
||||||
// TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun refund(fixture: FixtureInventory, player: FixtureInventory, item: GameItem, amount: Long) {
|
|
||||||
// TODO()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getNegotiator() = negotiator
|
|
||||||
override fun getFixtureInventory(): FixtureInventory = TODO()
|
|
||||||
override fun getPlayerInventory(): ActorInventory = INGAME.actorNowPlaying!!.inventory
|
|
||||||
|
|
||||||
private val halfSlotOffset = (UIItemInventoryElemSimple.height + listGap) / 2
|
|
||||||
|
|
||||||
private val thisOffsetX = UIInventoryFull.INVENTORY_CELLS_OFFSET_X() + UIItemInventoryElemSimple.height + listGap - halfSlotOffset
|
|
||||||
private val thisOffsetX2 = thisOffsetX + (listGap + UIItemInventoryElemWide.height) * 7
|
|
||||||
private val thisXend = thisOffsetX + (listGap + UIItemInventoryElemWide.height) * 13 - listGap
|
|
||||||
private val thisOffsetY = UIInventoryFull.INVENTORY_CELLS_OFFSET_Y()
|
|
||||||
private val cellsWidth = (listGap + UIItemInventoryElemWide.height) * 6 - listGap
|
|
||||||
|
|
||||||
private val LAST_LINE_IN_GRID = ((UIItemInventoryElemWide.height + listGap) * (UIInventoryFull.CELLS_VRT - 2)) + 22//359 // TEMPORARY VALUE!
|
|
||||||
|
|
||||||
private var recipeClicked: CraftingCodex.CraftingRecipe? = null
|
|
||||||
|
|
||||||
private val controlHelp: String
|
|
||||||
get() = if (App.environment == RunningEnvironment.PC)
|
|
||||||
"${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
|
|
||||||
else
|
|
||||||
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
|
|
||||||
"$gamepadLabelLEFTRIGHT ${Lang["GAME_OBJECTIVE_MULTIPLIER"]}\u3000 " +
|
|
||||||
"${App.gamepadLabelWest} ${Lang["GAME_ACTION_CRAFT"]}"
|
|
||||||
|
|
||||||
private val oldSelectedItems = ArrayList<ItemID>()
|
|
||||||
|
|
||||||
private val craftMult
|
|
||||||
get() = spinnerCraftCount.value.toLong()
|
|
||||||
|
|
||||||
private fun _getItemListPlayer() = playerThings.itemList
|
|
||||||
private fun _getItemListIngredients() = itemListIngredients
|
|
||||||
private fun _getItemListCraftables() = itemListCraftable
|
|
||||||
|
|
||||||
init {
|
|
||||||
val craftButtonsY = thisOffsetY + 23 + (UIItemInventoryElemWide.height + listGap) * (UIInventoryFull.CELLS_VRT - 1)
|
|
||||||
val buttonWidth = (UIItemInventoryElemWide.height + listGap) * 3 - listGap - 2
|
|
||||||
|
|
||||||
// ingredient list
|
|
||||||
itemListIngredients = UIItemInventoryItemGrid(
|
|
||||||
this,
|
|
||||||
{ ingredients },
|
|
||||||
thisOffsetX,
|
|
||||||
thisOffsetY + LAST_LINE_IN_GRID,
|
|
||||||
6, 1,
|
|
||||||
drawScrollOnRightside = false,
|
|
||||||
drawWallet = false,
|
|
||||||
hideSidebar = true,
|
|
||||||
colourTheme = UIItemInventoryCellCommonRes.defaultInventoryCellTheme.copy(
|
|
||||||
cellHighlightSubCol = Toolkit.Theme.COL_INACTIVE
|
|
||||||
),
|
|
||||||
keyDownFun = { _, _, _, _, _ -> },
|
|
||||||
wheelFun = { _, _, _, _, _, _ -> },
|
|
||||||
touchDownFun = { gameItem, amount, _, _, _ -> gameItem?.let { gameItem ->
|
|
||||||
// if the clicked item is craftable one, present its recipe to the player //
|
|
||||||
|
|
||||||
CraftingRecipeCodex.getRecipesFor(gameItem.originalID)?.let { recipes ->
|
|
||||||
// select most viable recipe (completely greedy search)
|
|
||||||
val player = getPlayerInventory()
|
|
||||||
// list of [Score, Ingredients, Recipe]
|
|
||||||
recipes.map { recipe ->
|
|
||||||
// list of (Item, How many player has, How many the recipe requires)
|
|
||||||
val items = recipeToIngredientRecord(player, recipe, nearbyCraftingStations)
|
|
||||||
|
|
||||||
val score = items.fold(1L) { acc, item ->
|
|
||||||
(item.howManyPlayerHas).times(16L) + 1L
|
|
||||||
}
|
|
||||||
|
|
||||||
listOf(score, items, recipe)
|
|
||||||
}.maxByOrNull { it[0] as Long }?.let { (_, items, recipe) ->
|
|
||||||
val items = items as List<RecipeIngredientRecord>
|
|
||||||
val recipe = recipe as CraftingCodex.CraftingRecipe
|
|
||||||
|
|
||||||
// change selected recipe to mostViableRecipe then update the UIs accordingly
|
|
||||||
val selectedItems = ArrayList<ItemID>()
|
|
||||||
|
|
||||||
resetSpinner()
|
|
||||||
|
|
||||||
ingredients.clear()
|
|
||||||
recipeClicked = recipe
|
|
||||||
|
|
||||||
items.forEach {
|
|
||||||
val itm = it.selectedItem
|
|
||||||
val qty = it.howManyRecipeWants
|
|
||||||
|
|
||||||
selectedItems.add(itm)
|
|
||||||
ingredients.add(itm, qty)
|
|
||||||
}
|
|
||||||
|
|
||||||
_getItemListPlayer().let {
|
|
||||||
it.removeFromForceHighlightList(oldSelectedItems)
|
|
||||||
//filterPlayerListUsing(recipeClicked) // ???
|
|
||||||
it.addToForceHighlightList(selectedItems)
|
|
||||||
filterPlayerListUsing(recipeClicked)
|
|
||||||
}
|
|
||||||
|
|
||||||
_getItemListIngredients().rebuild(FILTER_CAT_ALL)
|
|
||||||
|
|
||||||
_getItemListCraftables().highlightRecipe(recipeClicked, true)
|
|
||||||
|
|
||||||
oldSelectedItems.clear()
|
|
||||||
oldSelectedItems.addAll(selectedItems)
|
|
||||||
|
|
||||||
refreshCraftButtonStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
)
|
|
||||||
|
|
||||||
// make sure grid buttons for ingredients do nothing (even if they are hidden!)
|
|
||||||
itemListIngredients.navRemoCon.listButtonListener = { _,_, -> }
|
|
||||||
itemListIngredients.navRemoCon.gridButtonListener = { _,_, -> }
|
|
||||||
itemListIngredients.isCompactMode = true
|
|
||||||
itemListIngredients.setCustomHighlightRuleSub {
|
|
||||||
it.item?.let { ingredient ->
|
|
||||||
return@setCustomHighlightRuleSub getPlayerInventory().searchByID(ingredient.dynamicID)?.let { itemOnPlayer ->
|
|
||||||
itemOnPlayer.qty * craftMult >= it.amount * craftMult
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// crafting list to the left
|
|
||||||
itemListCraftable = UIItemCraftingCandidateGrid(
|
|
||||||
this,
|
|
||||||
thisOffsetX,
|
|
||||||
thisOffsetY,
|
|
||||||
6, UIInventoryFull.CELLS_VRT - 2, // decrease the internal height so that craft/cancel button would fit in
|
|
||||||
keyDownFun = { _, _, _, _, _ -> },
|
|
||||||
touchDownFun = { gameItem, amount, _, recipe0, button ->
|
|
||||||
(recipe0 as? CraftingCodex.CraftingRecipe).let { recipe ->
|
|
||||||
val selectedItems = ArrayList<ItemID>()
|
|
||||||
|
|
||||||
val playerInventory = getPlayerInventory()
|
|
||||||
ingredients.clear()
|
|
||||||
recipeClicked = recipe
|
|
||||||
// printdbg(this, "Recipe selected: $recipe")
|
|
||||||
recipe?.ingredients?.forEach { ingredient ->
|
|
||||||
val selectedItem = resolveIngredientKey(playerInventory, ingredient, recipe.product)
|
|
||||||
selectedItems.add(selectedItem)
|
|
||||||
ingredients.add(selectedItem, ingredient.qty)
|
|
||||||
}
|
|
||||||
|
|
||||||
_getItemListPlayer().removeFromForceHighlightList(oldSelectedItems)
|
|
||||||
_getItemListPlayer().addToForceHighlightList(selectedItems)
|
|
||||||
if (recipe != null) _getItemListPlayer().itemPage = 0
|
|
||||||
filterPlayerListUsing(recipeClicked)
|
|
||||||
_getItemListIngredients().rebuild(FILTER_CAT_ALL)
|
|
||||||
|
|
||||||
highlightCraftingCandidateButton(recipe)
|
|
||||||
|
|
||||||
oldSelectedItems.clear()
|
|
||||||
oldSelectedItems.addAll(selectedItems)
|
|
||||||
|
|
||||||
if (recipe == null) {
|
|
||||||
playerThings.itemList.clearForceHighlightList()
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshCraftButtonStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
buttonCraft = UIItemTextButton(this,
|
|
||||||
{ Lang["GAME_ACTION_CRAFT"] }, thisOffsetX + 3 + buttonWidth + listGap, craftButtonsY, buttonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
|
||||||
spinnerCraftCount = UIItemSpinner(this, thisOffsetX + 1, craftButtonsY, 1, 1, App.getConfigInt("basegame:gameplay_max_crafting"), 1, buttonWidth, numberToTextFunction = {"×\u200A${it.toInt()}"})
|
|
||||||
spinnerCraftCount.selectionChangeListener = {
|
|
||||||
itemListIngredients.numberMultiplier = it.toLong()
|
|
||||||
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
|
||||||
itemListCraftable.numberMultiplier = it.toLong()
|
|
||||||
itemListCraftable.rebuild()
|
|
||||||
refreshCraftButtonStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
buttonCraft.clickOnceListener = { _,_ ->
|
|
||||||
getPlayerInventory().let { player -> recipeClicked?.let { recipe ->
|
|
||||||
// check if player has enough amount of ingredients
|
|
||||||
val itemCraftable = itemListIngredients.getInventory().all { (itm, qty) ->
|
|
||||||
(player.searchByID(itm)?.qty ?: -1) >= qty * craftMult
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (itemCraftable) {
|
|
||||||
itemListIngredients.getInventory().forEach { (itm, qty) ->
|
|
||||||
player.remove(itm, qty * craftMult)
|
|
||||||
}
|
|
||||||
player.add(recipe.product, recipe.moq * craftMult)
|
|
||||||
|
|
||||||
// reset selection status after a crafting to hide the possible artefact where no-longer-craftable items are still displayed due to ingredient depletion
|
|
||||||
resetUI() // also clears forcehighlightlist
|
|
||||||
playerThings.rebuild(FILTER_CAT_ALL)
|
|
||||||
itemListCraftable.rebuild(FILTER_CAT_ALL)
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
refreshCraftButtonStatus()
|
|
||||||
}
|
|
||||||
// make grid mode buttons work together
|
|
||||||
// itemListCraftable.gridModeButtons[0].clickOnceListener = { _,_ -> setCompact(false) }
|
|
||||||
// itemListCraftable.gridModeButtons[1].clickOnceListener = { _,_ -> setCompact(true) }
|
|
||||||
|
|
||||||
handler.allowESCtoClose = true
|
|
||||||
|
|
||||||
|
|
||||||
val menuButtonTechView = UIItemImageButton(
|
|
||||||
this, catIcons.get(20, 1),
|
|
||||||
initialX = itemListCraftable.navRemoCon.posX + 12,
|
|
||||||
initialY = itemListCraftable.navRemoCon.getIconPosY(-2) - 8,
|
|
||||||
highlightable = true
|
|
||||||
).also {
|
|
||||||
it.clickOnceListener = { _, _ ->
|
|
||||||
full?.transitionPanel?.setLeftUIto(1)
|
|
||||||
full?.transitionPanel?.uis?.get(0)?.show()
|
|
||||||
it.highlighted = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val menuButtonCraft = UIItemImageButton(
|
|
||||||
this, catIcons.get(19, 1),
|
|
||||||
initialX = itemListCraftable.navRemoCon.posX + 12,
|
|
||||||
initialY = itemListCraftable.navRemoCon.getIconPosY(-1) - 8,
|
|
||||||
activeCol = Toolkit.Theme.COL_SELECTED,
|
|
||||||
inactiveCol = Toolkit.Theme.COL_SELECTED,
|
|
||||||
highlightable = true
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
addUIitem(itemListCraftable)
|
|
||||||
addUIitem(itemListIngredients)
|
|
||||||
addUIitem(playerThings)
|
|
||||||
addUIitem(spinnerCraftCount)
|
|
||||||
addUIitem(buttonCraft)
|
|
||||||
// temporarily disabled for 0.4 release
|
|
||||||
if (TerrarumAppConfiguration.VERSION_RAW >= 0x0000_000004_000001) {
|
|
||||||
addUIitem(menuButtonCraft)
|
|
||||||
addUIitem(menuButtonTechView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun filterPlayerListUsing(recipe: CraftingCodex.CraftingRecipe?) {
|
|
||||||
if (recipe == null)
|
|
||||||
playerThings.rebuild(FILTER_CAT_ALL)
|
|
||||||
else {
|
|
||||||
val items = recipe.ingredients.flatMap {
|
|
||||||
getItemCandidatesForIngredient(getPlayerInventory(), it).map { it.itm }
|
|
||||||
}.filter { it != recipe.product }.sorted() // filter out the product itself from the ingredient
|
|
||||||
|
|
||||||
val filterFun = { pair: InventoryPair ->
|
|
||||||
items.binarySearch(pair.itm) >= 0
|
|
||||||
}
|
|
||||||
playerThings.rebuild(filterFun, recipe.product)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nearbyCraftingStations = emptyList<String>(); protected set
|
|
||||||
|
|
||||||
fun getCraftingStationsWithinReach(): List<String> {
|
|
||||||
val reach = 2 * INGAME.actorNowPlaying!!.actorValue.getAsDouble(AVKey.REACH)!! * (INGAME.actorNowPlaying!!.actorValue.getAsDouble(AVKey.REACHBUFF) ?: 1.0) * INGAME.actorNowPlaying!!.scale
|
|
||||||
val nearbyCraftingStations = INGAME.findKNearestActors(INGAME.actorNowPlaying!!, 256) {
|
|
||||||
it is CraftingStation && (distBetweenActors(it, INGAME.actorNowPlaying!!) < reach)
|
|
||||||
}
|
|
||||||
return nearbyCraftingStations.flatMap { (it.get() as CraftingStation).tags }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun changeIngredient(recipe: CraftingCodex.CraftingRecipe?, old: InventoryPair, new: ItemID) {
|
|
||||||
playerThings.itemList.removeFromForceHighlightList(oldSelectedItems)
|
|
||||||
|
|
||||||
oldSelectedItems.remove(old.itm)
|
|
||||||
oldSelectedItems.add(new)
|
|
||||||
|
|
||||||
playerThings.itemList.addToForceHighlightList(oldSelectedItems)
|
|
||||||
playerThings.itemList.itemPage = 0
|
|
||||||
filterPlayerListUsing(recipe)
|
|
||||||
|
|
||||||
// change highlight status of itemListIngredients
|
|
||||||
itemListIngredients.getInventory().let {
|
|
||||||
val amount = old.qty
|
|
||||||
it.remove(old.itm, amount)
|
|
||||||
it.add(new, amount)
|
|
||||||
}
|
|
||||||
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun highlightCraftingCandidateButton(recipe: CraftingCodex.CraftingRecipe?) { // a proxy function
|
|
||||||
itemListCraftable.highlightRecipe(recipe)
|
|
||||||
itemListCraftable.rebuild(FILTER_CAT_ALL)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates Craft! button so that the button is correctly highlighted
|
|
||||||
*/
|
|
||||||
fun refreshCraftButtonStatus() {
|
|
||||||
val itemCraftable = if (itemListIngredients.getInventory().totalUniqueCount < 1) false
|
|
||||||
else getPlayerInventory().let { player ->
|
|
||||||
// check if player has enough amount of ingredients
|
|
||||||
itemListIngredients.getInventory().all { (itm, qty) ->
|
|
||||||
(player.searchByID(itm)?.qty ?: -1) >= qty * craftMult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonCraft.isEnabled = itemCraftable
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset whatever player has selected to null and bring UI to its initial state
|
|
||||||
fun resetUI() {
|
fun resetUI() {
|
||||||
// reset spinner
|
transitionalCraftingUI.resetUI()
|
||||||
resetSpinner()
|
transitionalTechtreePanel.resetUI()
|
||||||
|
|
||||||
// reset selected recipe status
|
|
||||||
recipeClicked = null
|
|
||||||
filterPlayerListUsing(recipeClicked)
|
|
||||||
highlightCraftingCandidateButton(null)
|
|
||||||
ingredients.clear()
|
|
||||||
playerThings.itemList.clearForceHighlightList()
|
|
||||||
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
|
||||||
|
|
||||||
// reset scroll
|
|
||||||
itemListCraftable.itemPage = 0
|
|
||||||
playerThings.itemList.itemPage = 0
|
|
||||||
|
|
||||||
refreshCraftButtonStatus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetSpinner() {
|
fun showCraftingUI() {
|
||||||
spinnerCraftCount.resetToSmallest()
|
transitionPanel.setLeftUIto(0)
|
||||||
itemListIngredients.numberMultiplier = 1L
|
transitionalCraftingUI.show()
|
||||||
itemListCraftable.numberMultiplier = 1L
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var openingClickLatched = false
|
fun showTechViewUI() {
|
||||||
|
transitionPanel.setLeftUIto(1)
|
||||||
override fun show() {
|
transitionalTechtreePanel.show()
|
||||||
nearbyCraftingStations = getCraftingStationsWithinReach()
|
|
||||||
// printdbg(this, "Nearby crafting stations: $nearbyCraftingStations")
|
|
||||||
|
|
||||||
playerThings.setGetInventoryFun { INGAME.actorNowPlaying!!.inventory }
|
|
||||||
itemListUpdate()
|
|
||||||
|
|
||||||
openingClickLatched = Terrarum.mouseDown
|
|
||||||
|
|
||||||
tooltipShowing.clear()
|
|
||||||
INGAME.setTooltipMessage(null)
|
|
||||||
|
|
||||||
resetUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var encumbrancePerc = 0f
|
|
||||||
|
|
||||||
private fun itemListUpdate() {
|
|
||||||
// let itemlists be sorted
|
|
||||||
itemListCraftable.rebuild()
|
|
||||||
playerThings.rebuild(FILTER_CAT_ALL)
|
|
||||||
encumbrancePerc = getPlayerInventory().encumberment.toFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
|
||||||
if (!openingClickLatched) {
|
|
||||||
return super.touchDown(screenX, screenY, pointer, button)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateImpl(delta: Float) {
|
override fun updateImpl(delta: Float) {
|
||||||
// NO super.update due to an infinite recursion
|
uiItems.forEach { it.update(delta) }
|
||||||
this.uiItems.forEach { it.update(delta) }
|
|
||||||
|
|
||||||
if (openingClickLatched && !Terrarum.mouseDown) openingClickLatched = false
|
// copy transition of parent (UIItemHorizontalFadeSlide) to this (also UIItemHorizontalFadeSlide)
|
||||||
|
full?.let { full ->
|
||||||
|
uis.forEach {
|
||||||
|
it.posX = full.transitionPanel.getOffX(0)
|
||||||
|
it.opacity = full.transitionPanel.getOpacity(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {
|
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {
|
||||||
// NO super.render due to an infinite recursion
|
if (full == null) {
|
||||||
this.uiItems.forEach { it.render(frameDelta, batch, camera) }
|
UIInventoryFull.drawBackground(batch, opacity)
|
||||||
|
|
||||||
batch.color = Color.WHITE
|
|
||||||
|
|
||||||
// text label for two inventory grids
|
|
||||||
val craftingLabel = Lang["GAME_CRAFTING"]
|
|
||||||
val ingredientsLabel = Lang["GAME_INVENTORY_INGREDIENTS"]
|
|
||||||
|
|
||||||
App.fontGame.draw(batch, craftingLabel, thisOffsetX + (cellsWidth - App.fontGame.getWidth(craftingLabel)) / 2, thisOffsetY - INVENTORY_NAME_TEXT_GAP)
|
|
||||||
App.fontGame.draw(batch, ingredientsLabel, thisOffsetX + (cellsWidth - App.fontGame.getWidth(ingredientsLabel)) / 2, thisOffsetY + LAST_LINE_IN_GRID - INVENTORY_NAME_TEXT_GAP)
|
|
||||||
|
|
||||||
|
|
||||||
// control hints
|
|
||||||
val controlHintXPos = thisOffsetX + 2f
|
|
||||||
blendNormalStraightAlpha(batch)
|
|
||||||
App.fontGame.draw(batch, controlHelp, controlHintXPos, UIInventoryFull.yEnd - 20)
|
|
||||||
|
|
||||||
|
|
||||||
if (INGAME.actorNowPlaying != null) {
|
|
||||||
//draw player encumb
|
|
||||||
val encumbBarXPos = thisXend - UIInventoryCells.weightBarWidth + 36
|
|
||||||
val encumbBarYPos = UIInventoryFull.yEnd - 20 + 3f
|
|
||||||
UIInventoryCells.drawEncumbranceBar(batch, encumbBarXPos, encumbBarYPos, encumbrancePerc, INGAME.actorNowPlaying!!.inventory)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uiItems.forEach { it.render(frameDelta, batch, camera) }
|
||||||
blendNormalStraightAlpha(batch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doOpening(delta: Float) {
|
|
||||||
super.doOpening(delta)
|
|
||||||
INGAME.setTooltipMessage(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doClosing(delta: Float) {
|
|
||||||
super.doClosing(delta)
|
|
||||||
INGAME.setTooltipMessage(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun endOpening(delta: Float) {
|
|
||||||
super.endOpening(delta)
|
|
||||||
tooltipShowing.clear()
|
|
||||||
INGAME.setTooltipMessage(null) // required!
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun endClosing(delta: Float) {
|
|
||||||
super.endClosing(delta)
|
|
||||||
resetUI()
|
|
||||||
tooltipShowing.clear()
|
|
||||||
INGAME.setTooltipMessage(null) // required!
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
|
transitionPanel.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
init {
|
||||||
data class RecipeIngredientRecord(
|
addUIitem(transitionPanel)
|
||||||
val selectedItem: ItemID,
|
}
|
||||||
val howManyPlayerHas: Long,
|
|
||||||
val howManyRecipeWants: Long,
|
|
||||||
val craftingStationAvailable: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getItemCandidatesForIngredient(inventory: FixtureInventory, ingredient: CraftingCodex.CraftingIngredients): List<InventoryPair> {
|
override fun setPosition(x: Int, y: Int) {
|
||||||
return if (ingredient.keyMode == CraftingCodex.CraftingItemKeyMode.TAG) {
|
transitionalCraftingUI.setPosition(x, y)
|
||||||
val tags = ingredient.key.split(',')
|
transitionalTechtreePanel.setPosition(x, y)
|
||||||
val wantsWall = tags.contains("WALL")
|
|
||||||
// If the player has the required item, use it; otherwise, will take an item from the ItemCodex
|
|
||||||
inventory.filter { (itm, qty) ->
|
|
||||||
ItemCodex[itm]?.hasAllTags(tags) == true && qty >= ingredient.qty && (wantsWall == itm.isWall()) // true if (wants wall and is wall) or (wants no wall and is not wall)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listOf(InventoryPair(ingredient.key, -1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resolveIngredientKey(inventory: FixtureInventory, ingredient: CraftingCodex.CraftingIngredients, product: ItemID): ItemID {
|
|
||||||
val candidate = getItemCandidatesForIngredient(inventory, ingredient).filter { it.itm != product }
|
|
||||||
|
|
||||||
// printdbg(this, "resolveIngredientKey product=$product, candidate=$candidate")
|
|
||||||
|
|
||||||
return if (ingredient.keyMode == CraftingCodex.CraftingItemKeyMode.TAG) {
|
|
||||||
// filter out the product itself from the ingredient
|
|
||||||
candidate.maxByOrNull { it.qty }?.itm ?: (
|
|
||||||
(ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.hasTag(ingredient.key)) it.key else null }) ?:
|
|
||||||
throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed (ingredient: $ingredient)")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ingredient.key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For each ingredient of the recipe, returns list of (ingredient, how many the player has the ingredient, how many the recipe wants)
|
|
||||||
*/
|
|
||||||
fun recipeToIngredientRecord(inventory: FixtureInventory, recipe: CraftingCodex.CraftingRecipe, nearbyCraftingStations: List<String>): List<RecipeIngredientRecord> {
|
|
||||||
val hasStation = if (recipe.workbench.isBlank()) true else nearbyCraftingStations.containsAll(recipe.workbench.split(','))
|
|
||||||
return recipe.ingredients.map { ingredient ->
|
|
||||||
val selectedItem = resolveIngredientKey(inventory, ingredient, recipe.product)
|
|
||||||
val howManyPlayerHas = inventory.searchByID(selectedItem)?.qty ?: 0L
|
|
||||||
val howManyTheRecipeWants = ingredient.qty
|
|
||||||
|
|
||||||
RecipeIngredientRecord(selectedItem, howManyPlayerHas, howManyTheRecipeWants, hasStation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,593 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.ui
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
|
import net.torvald.terrarum.*
|
||||||
|
import net.torvald.terrarum.App.*
|
||||||
|
import net.torvald.terrarum.gameactors.AVKey
|
||||||
|
import net.torvald.terrarum.gameitems.GameItem
|
||||||
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
|
import net.torvald.terrarum.gameitems.isWall
|
||||||
|
import net.torvald.terrarum.itemproperties.CraftingCodex
|
||||||
|
import net.torvald.terrarum.langpack.Lang
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.CraftingStation
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.InventoryPair
|
||||||
|
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes.tooltipShowing
|
||||||
|
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.listGap
|
||||||
|
import net.torvald.terrarum.modulebasegame.ui.UITemplateHalfInventory.Companion.INVENTORY_NAME_TEXT_GAP
|
||||||
|
import net.torvald.terrarum.ui.*
|
||||||
|
import net.torvald.terrarum.ui.UIItemCatBar.Companion.FILTER_CAT_ALL
|
||||||
|
import net.torvald.unicode.getKeycapPC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This UI has inventory, but it's just there to display all craftable items and should not be serialised.
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2022-03-10.
|
||||||
|
*/
|
||||||
|
class UICraftingWorkbench(val inventoryUI: UIInventoryFull?, val parentContainer: UICrafting) : UICanvas(
|
||||||
|
toggleKeyLiteral = if (inventoryUI == null) "control_key_inventory" else null,
|
||||||
|
toggleButtonLiteral = if (inventoryUI == null) "control_gamepad_start" else null
|
||||||
|
), HasInventory {
|
||||||
|
|
||||||
|
override var width = Toolkit.drawWidth
|
||||||
|
override var height = App.scr.height
|
||||||
|
|
||||||
|
private val playerThings = UITemplateHalfInventory(this, false).also { pt ->
|
||||||
|
pt.itemListTouchDownFun = { gameItem, _, _, _, theButton -> if (gameItem != null) {
|
||||||
|
val recipe = recipeClicked
|
||||||
|
val itemID = gameItem.dynamicID
|
||||||
|
|
||||||
|
// change ingredient used
|
||||||
|
if (recipe != null) {
|
||||||
|
// don't rely on highlightedness of the button to determine the item on the button is the selected
|
||||||
|
// ingredient (because I don't fully trust my code lol)
|
||||||
|
val targetItemToAlter =
|
||||||
|
recipe.ingredients.filter { (key, mode) -> // altering recipe doesn't make sense if player selected a recipe that requires no tag-ingredients
|
||||||
|
val tags = key.split(',')
|
||||||
|
val wantsWall = tags.contains("WALL")
|
||||||
|
(mode == CraftingCodex.CraftingItemKeyMode.TAG && gameItem.hasAllTags(tags) && (wantsWall == gameItem.originalID.isWall())) // true if (wants wall and is wall) or (wants no wall and is not wall)
|
||||||
|
}.let {
|
||||||
|
if (it.size > 1)
|
||||||
|
println(
|
||||||
|
"[UICrafting] Your recipe seems to have two similar ingredients defined\n" +
|
||||||
|
"affected ingredients: ${it.joinToString()}\n" +
|
||||||
|
"the recipe: ${recipe}"
|
||||||
|
)
|
||||||
|
it.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
targetItemToAlter?.let { (key, mode) ->
|
||||||
|
val oldItem = _getItemListIngredients().getInventory().first { (itm, qty) ->
|
||||||
|
val tags = key.split(',')
|
||||||
|
val wantsWall = tags.contains("WALL")
|
||||||
|
(mode == CraftingCodex.CraftingItemKeyMode.TAG && ItemCodex[itm]!!.hasAllTags(tags) && (wantsWall == itm.isWall())) // true if (wants wall and is wall) or (wants no wall and is not wall)
|
||||||
|
}
|
||||||
|
changeIngredient(recipe, oldItem, itemID)
|
||||||
|
refreshCraftButtonStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// show all the items that can be made using this ingredient
|
||||||
|
else {
|
||||||
|
itemListCraftable.rebuild(arrayOf(itemID))
|
||||||
|
pt.itemList.clearForceHighlightList()
|
||||||
|
pt.itemList.addToForceHighlightList(listOf(itemID))
|
||||||
|
|
||||||
|
if (itemListCraftable.craftingRecipes.isEmpty()) {
|
||||||
|
pt.itemList.clearForceHighlightList()
|
||||||
|
itemListCraftable.rebuild(FILTER_CAT_ALL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pt.itemList.clearForceHighlightList()
|
||||||
|
recipeClicked = null
|
||||||
|
filterPlayerListUsing(recipeClicked)
|
||||||
|
highlightCraftingCandidateButton(null)
|
||||||
|
ingredients.clear()
|
||||||
|
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val catIcons = CommonResourcePool.getAsTextureRegionPack("inventory_category")
|
||||||
|
|
||||||
|
|
||||||
|
internal val itemListCraftable: UIItemCraftingCandidateGrid // might be changed to something else
|
||||||
|
internal val itemListIngredients: UIItemInventoryItemGrid // this one is definitely not to be changed
|
||||||
|
private val buttonCraft: UIItemTextButton
|
||||||
|
private val spinnerCraftCount: UIItemSpinner
|
||||||
|
|
||||||
|
private val ingredients = FixtureInventory() // this one is definitely not to be changed
|
||||||
|
|
||||||
|
private val negotiator = object : InventoryTransactionNegotiator() {
|
||||||
|
override fun accept(player: FixtureInventory, fixture: FixtureInventory, item: GameItem, amount: Long) {
|
||||||
|
// TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun refund(fixture: FixtureInventory, player: FixtureInventory, item: GameItem, amount: Long) {
|
||||||
|
// TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNegotiator() = negotiator
|
||||||
|
override fun getFixtureInventory(): FixtureInventory = TODO()
|
||||||
|
override fun getPlayerInventory(): ActorInventory = INGAME.actorNowPlaying!!.inventory
|
||||||
|
|
||||||
|
private val halfSlotOffset = (UIItemInventoryElemSimple.height + listGap) / 2
|
||||||
|
|
||||||
|
private val thisOffsetX = UIInventoryFull.INVENTORY_CELLS_OFFSET_X() + UIItemInventoryElemSimple.height + listGap - halfSlotOffset
|
||||||
|
private val thisOffsetX2 = thisOffsetX + (listGap + UIItemInventoryElemWide.height) * 7
|
||||||
|
private val thisXend = thisOffsetX + (listGap + UIItemInventoryElemWide.height) * 13 - listGap
|
||||||
|
private val thisOffsetY = UIInventoryFull.INVENTORY_CELLS_OFFSET_Y()
|
||||||
|
private val cellsWidth = (listGap + UIItemInventoryElemWide.height) * 6 - listGap
|
||||||
|
|
||||||
|
private val LAST_LINE_IN_GRID = ((UIItemInventoryElemWide.height + listGap) * (UIInventoryFull.CELLS_VRT - 2)) + 22//359 // TEMPORARY VALUE!
|
||||||
|
|
||||||
|
private var recipeClicked: CraftingCodex.CraftingRecipe? = null
|
||||||
|
|
||||||
|
private val controlHelp: String
|
||||||
|
get() = if (App.environment == RunningEnvironment.PC)
|
||||||
|
"${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
|
||||||
|
else
|
||||||
|
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
|
||||||
|
"$gamepadLabelLEFTRIGHT ${Lang["GAME_OBJECTIVE_MULTIPLIER"]}\u3000 " +
|
||||||
|
"${App.gamepadLabelWest} ${Lang["GAME_ACTION_CRAFT"]}"
|
||||||
|
|
||||||
|
private val oldSelectedItems = ArrayList<ItemID>()
|
||||||
|
|
||||||
|
private val craftMult
|
||||||
|
get() = spinnerCraftCount.value.toLong()
|
||||||
|
|
||||||
|
private fun _getItemListPlayer() = playerThings.itemList
|
||||||
|
private fun _getItemListIngredients() = itemListIngredients
|
||||||
|
private fun _getItemListCraftables() = itemListCraftable
|
||||||
|
|
||||||
|
init {
|
||||||
|
val craftButtonsY = thisOffsetY + 23 + (UIItemInventoryElemWide.height + listGap) * (UIInventoryFull.CELLS_VRT - 1)
|
||||||
|
val buttonWidth = (UIItemInventoryElemWide.height + listGap) * 3 - listGap - 2
|
||||||
|
|
||||||
|
// ingredient list
|
||||||
|
itemListIngredients = UIItemInventoryItemGrid(
|
||||||
|
this,
|
||||||
|
{ ingredients },
|
||||||
|
thisOffsetX,
|
||||||
|
thisOffsetY + LAST_LINE_IN_GRID,
|
||||||
|
6, 1,
|
||||||
|
drawScrollOnRightside = false,
|
||||||
|
drawWallet = false,
|
||||||
|
hideSidebar = true,
|
||||||
|
colourTheme = UIItemInventoryCellCommonRes.defaultInventoryCellTheme.copy(
|
||||||
|
cellHighlightSubCol = Toolkit.Theme.COL_INACTIVE
|
||||||
|
),
|
||||||
|
keyDownFun = { _, _, _, _, _ -> },
|
||||||
|
wheelFun = { _, _, _, _, _, _ -> },
|
||||||
|
touchDownFun = { gameItem, amount, _, _, _ -> gameItem?.let { gameItem ->
|
||||||
|
// if the clicked item is craftable one, present its recipe to the player //
|
||||||
|
|
||||||
|
CraftingRecipeCodex.getRecipesFor(gameItem.originalID)?.let { recipes ->
|
||||||
|
// select most viable recipe (completely greedy search)
|
||||||
|
val player = getPlayerInventory()
|
||||||
|
// list of [Score, Ingredients, Recipe]
|
||||||
|
recipes.map { recipe ->
|
||||||
|
// list of (Item, How many player has, How many the recipe requires)
|
||||||
|
val items = recipeToIngredientRecord(player, recipe, nearbyCraftingStations)
|
||||||
|
|
||||||
|
val score = items.fold(1L) { acc, item ->
|
||||||
|
(item.howManyPlayerHas).times(16L) + 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
listOf(score, items, recipe)
|
||||||
|
}.maxByOrNull { it[0] as Long }?.let { (_, items, recipe) ->
|
||||||
|
val items = items as List<RecipeIngredientRecord>
|
||||||
|
val recipe = recipe as CraftingCodex.CraftingRecipe
|
||||||
|
|
||||||
|
// change selected recipe to mostViableRecipe then update the UIs accordingly
|
||||||
|
val selectedItems = ArrayList<ItemID>()
|
||||||
|
|
||||||
|
resetSpinner()
|
||||||
|
|
||||||
|
ingredients.clear()
|
||||||
|
recipeClicked = recipe
|
||||||
|
|
||||||
|
items.forEach {
|
||||||
|
val itm = it.selectedItem
|
||||||
|
val qty = it.howManyRecipeWants
|
||||||
|
|
||||||
|
selectedItems.add(itm)
|
||||||
|
ingredients.add(itm, qty)
|
||||||
|
}
|
||||||
|
|
||||||
|
_getItemListPlayer().let {
|
||||||
|
it.removeFromForceHighlightList(oldSelectedItems)
|
||||||
|
//filterPlayerListUsing(recipeClicked) // ???
|
||||||
|
it.addToForceHighlightList(selectedItems)
|
||||||
|
filterPlayerListUsing(recipeClicked)
|
||||||
|
}
|
||||||
|
|
||||||
|
_getItemListIngredients().rebuild(FILTER_CAT_ALL)
|
||||||
|
|
||||||
|
_getItemListCraftables().highlightRecipe(recipeClicked, true)
|
||||||
|
|
||||||
|
oldSelectedItems.clear()
|
||||||
|
oldSelectedItems.addAll(selectedItems)
|
||||||
|
|
||||||
|
refreshCraftButtonStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
)
|
||||||
|
|
||||||
|
// make sure grid buttons for ingredients do nothing (even if they are hidden!)
|
||||||
|
itemListIngredients.navRemoCon.listButtonListener = { _,_, -> }
|
||||||
|
itemListIngredients.navRemoCon.gridButtonListener = { _,_, -> }
|
||||||
|
itemListIngredients.isCompactMode = true
|
||||||
|
itemListIngredients.setCustomHighlightRuleSub {
|
||||||
|
it.item?.let { ingredient ->
|
||||||
|
return@setCustomHighlightRuleSub getPlayerInventory().searchByID(ingredient.dynamicID)?.let { itemOnPlayer ->
|
||||||
|
itemOnPlayer.qty * craftMult >= it.amount * craftMult
|
||||||
|
} == true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// crafting list to the left
|
||||||
|
itemListCraftable = UIItemCraftingCandidateGrid(
|
||||||
|
this,
|
||||||
|
thisOffsetX,
|
||||||
|
thisOffsetY,
|
||||||
|
6, UIInventoryFull.CELLS_VRT - 2, // decrease the internal height so that craft/cancel button would fit in
|
||||||
|
keyDownFun = { _, _, _, _, _ -> },
|
||||||
|
touchDownFun = { gameItem, amount, _, recipe0, button ->
|
||||||
|
(recipe0 as? CraftingCodex.CraftingRecipe).let { recipe ->
|
||||||
|
val selectedItems = ArrayList<ItemID>()
|
||||||
|
|
||||||
|
val playerInventory = getPlayerInventory()
|
||||||
|
ingredients.clear()
|
||||||
|
recipeClicked = recipe
|
||||||
|
// printdbg(this, "Recipe selected: $recipe")
|
||||||
|
recipe?.ingredients?.forEach { ingredient ->
|
||||||
|
val selectedItem = resolveIngredientKey(playerInventory, ingredient, recipe.product)
|
||||||
|
selectedItems.add(selectedItem)
|
||||||
|
ingredients.add(selectedItem, ingredient.qty)
|
||||||
|
}
|
||||||
|
|
||||||
|
_getItemListPlayer().removeFromForceHighlightList(oldSelectedItems)
|
||||||
|
_getItemListPlayer().addToForceHighlightList(selectedItems)
|
||||||
|
if (recipe != null) _getItemListPlayer().itemPage = 0
|
||||||
|
filterPlayerListUsing(recipeClicked)
|
||||||
|
_getItemListIngredients().rebuild(FILTER_CAT_ALL)
|
||||||
|
|
||||||
|
highlightCraftingCandidateButton(recipe)
|
||||||
|
|
||||||
|
oldSelectedItems.clear()
|
||||||
|
oldSelectedItems.addAll(selectedItems)
|
||||||
|
|
||||||
|
if (recipe == null) {
|
||||||
|
playerThings.itemList.clearForceHighlightList()
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshCraftButtonStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
buttonCraft = UIItemTextButton(this,
|
||||||
|
{ Lang["GAME_ACTION_CRAFT"] }, thisOffsetX + 3 + buttonWidth + listGap, craftButtonsY, buttonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||||
|
spinnerCraftCount = UIItemSpinner(this, thisOffsetX + 1, craftButtonsY, 1, 1, App.getConfigInt("basegame:gameplay_max_crafting"), 1, buttonWidth, numberToTextFunction = {"×\u200A${it.toInt()}"})
|
||||||
|
spinnerCraftCount.selectionChangeListener = {
|
||||||
|
itemListIngredients.numberMultiplier = it.toLong()
|
||||||
|
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
||||||
|
itemListCraftable.numberMultiplier = it.toLong()
|
||||||
|
itemListCraftable.rebuild()
|
||||||
|
refreshCraftButtonStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buttonCraft.clickOnceListener = { _,_ ->
|
||||||
|
getPlayerInventory().let { player -> recipeClicked?.let { recipe ->
|
||||||
|
// check if player has enough amount of ingredients
|
||||||
|
val itemCraftable = itemListIngredients.getInventory().all { (itm, qty) ->
|
||||||
|
(player.searchByID(itm)?.qty ?: -1) >= qty * craftMult
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (itemCraftable) {
|
||||||
|
itemListIngredients.getInventory().forEach { (itm, qty) ->
|
||||||
|
player.remove(itm, qty * craftMult)
|
||||||
|
}
|
||||||
|
player.add(recipe.product, recipe.moq * craftMult)
|
||||||
|
|
||||||
|
// reset selection status after a crafting to hide the possible artefact where no-longer-craftable items are still displayed due to ingredient depletion
|
||||||
|
resetUI() // also clears forcehighlightlist
|
||||||
|
playerThings.rebuild(FILTER_CAT_ALL)
|
||||||
|
itemListCraftable.rebuild(FILTER_CAT_ALL)
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
refreshCraftButtonStatus()
|
||||||
|
}
|
||||||
|
// make grid mode buttons work together
|
||||||
|
// itemListCraftable.gridModeButtons[0].clickOnceListener = { _,_ -> setCompact(false) }
|
||||||
|
// itemListCraftable.gridModeButtons[1].clickOnceListener = { _,_ -> setCompact(true) }
|
||||||
|
|
||||||
|
handler.allowESCtoClose = true
|
||||||
|
|
||||||
|
|
||||||
|
val menuButtonTechView = UIItemImageButton(
|
||||||
|
this, catIcons.get(20, 1),
|
||||||
|
initialX = itemListCraftable.navRemoCon.posX + 12,
|
||||||
|
initialY = itemListCraftable.navRemoCon.getIconPosY(-2) - 8,
|
||||||
|
highlightable = true
|
||||||
|
).also {
|
||||||
|
it.clickOnceListener = { _, _ ->
|
||||||
|
parentContainer.showTechViewUI()
|
||||||
|
it.highlighted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val menuButtonCraft = UIItemImageButton(
|
||||||
|
this, catIcons.get(19, 1),
|
||||||
|
initialX = itemListCraftable.navRemoCon.posX + 12,
|
||||||
|
initialY = itemListCraftable.navRemoCon.getIconPosY(-1) - 8,
|
||||||
|
activeCol = Toolkit.Theme.COL_SELECTED,
|
||||||
|
inactiveCol = Toolkit.Theme.COL_SELECTED,
|
||||||
|
highlightable = true
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
addUIitem(itemListCraftable)
|
||||||
|
addUIitem(itemListIngredients)
|
||||||
|
addUIitem(playerThings)
|
||||||
|
addUIitem(spinnerCraftCount)
|
||||||
|
addUIitem(buttonCraft)
|
||||||
|
// temporarily disabled for 0.4 release
|
||||||
|
if (TerrarumAppConfiguration.VERSION_RAW >= 0x0000_000004_000001) {
|
||||||
|
addUIitem(menuButtonCraft)
|
||||||
|
addUIitem(menuButtonTechView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterPlayerListUsing(recipe: CraftingCodex.CraftingRecipe?) {
|
||||||
|
if (recipe == null)
|
||||||
|
playerThings.rebuild(FILTER_CAT_ALL)
|
||||||
|
else {
|
||||||
|
val items = recipe.ingredients.flatMap {
|
||||||
|
getItemCandidatesForIngredient(getPlayerInventory(), it).map { it.itm }
|
||||||
|
}.filter { it != recipe.product }.sorted() // filter out the product itself from the ingredient
|
||||||
|
|
||||||
|
val filterFun = { pair: InventoryPair ->
|
||||||
|
items.binarySearch(pair.itm) >= 0
|
||||||
|
}
|
||||||
|
playerThings.rebuild(filterFun, recipe.product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nearbyCraftingStations = emptyList<String>(); protected set
|
||||||
|
|
||||||
|
fun getCraftingStationsWithinReach(): List<String> {
|
||||||
|
val reach = 2 * INGAME.actorNowPlaying!!.actorValue.getAsDouble(AVKey.REACH)!! * (INGAME.actorNowPlaying!!.actorValue.getAsDouble(AVKey.REACHBUFF) ?: 1.0) * INGAME.actorNowPlaying!!.scale
|
||||||
|
val nearbyCraftingStations = INGAME.findKNearestActors(INGAME.actorNowPlaying!!, 256) {
|
||||||
|
it is CraftingStation && (distBetweenActors(it, INGAME.actorNowPlaying!!) < reach)
|
||||||
|
}
|
||||||
|
return nearbyCraftingStations.flatMap { (it.get() as CraftingStation).tags }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeIngredient(recipe: CraftingCodex.CraftingRecipe?, old: InventoryPair, new: ItemID) {
|
||||||
|
playerThings.itemList.removeFromForceHighlightList(oldSelectedItems)
|
||||||
|
|
||||||
|
oldSelectedItems.remove(old.itm)
|
||||||
|
oldSelectedItems.add(new)
|
||||||
|
|
||||||
|
playerThings.itemList.addToForceHighlightList(oldSelectedItems)
|
||||||
|
playerThings.itemList.itemPage = 0
|
||||||
|
filterPlayerListUsing(recipe)
|
||||||
|
|
||||||
|
// change highlight status of itemListIngredients
|
||||||
|
itemListIngredients.getInventory().let {
|
||||||
|
val amount = old.qty
|
||||||
|
it.remove(old.itm, amount)
|
||||||
|
it.add(new, amount)
|
||||||
|
}
|
||||||
|
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun highlightCraftingCandidateButton(recipe: CraftingCodex.CraftingRecipe?) { // a proxy function
|
||||||
|
itemListCraftable.highlightRecipe(recipe)
|
||||||
|
itemListCraftable.rebuild(FILTER_CAT_ALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates Craft! button so that the button is correctly highlighted
|
||||||
|
*/
|
||||||
|
fun refreshCraftButtonStatus() {
|
||||||
|
val itemCraftable = if (itemListIngredients.getInventory().totalUniqueCount < 1) false
|
||||||
|
else getPlayerInventory().let { player ->
|
||||||
|
// check if player has enough amount of ingredients
|
||||||
|
itemListIngredients.getInventory().all { (itm, qty) ->
|
||||||
|
(player.searchByID(itm)?.qty ?: -1) >= qty * craftMult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonCraft.isEnabled = itemCraftable
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset whatever player has selected to null and bring UI to its initial state
|
||||||
|
fun resetUI() {
|
||||||
|
// reset spinner
|
||||||
|
resetSpinner()
|
||||||
|
|
||||||
|
// reset selected recipe status
|
||||||
|
recipeClicked = null
|
||||||
|
filterPlayerListUsing(recipeClicked)
|
||||||
|
highlightCraftingCandidateButton(null)
|
||||||
|
ingredients.clear()
|
||||||
|
playerThings.itemList.clearForceHighlightList()
|
||||||
|
itemListIngredients.rebuild(FILTER_CAT_ALL)
|
||||||
|
|
||||||
|
// reset scroll
|
||||||
|
itemListCraftable.itemPage = 0
|
||||||
|
playerThings.itemList.itemPage = 0
|
||||||
|
|
||||||
|
refreshCraftButtonStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetSpinner() {
|
||||||
|
spinnerCraftCount.resetToSmallest()
|
||||||
|
itemListIngredients.numberMultiplier = 1L
|
||||||
|
itemListCraftable.numberMultiplier = 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
private var openingClickLatched = false
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
nearbyCraftingStations = getCraftingStationsWithinReach()
|
||||||
|
// printdbg(this, "Nearby crafting stations: $nearbyCraftingStations")
|
||||||
|
|
||||||
|
playerThings.setGetInventoryFun { INGAME.actorNowPlaying!!.inventory }
|
||||||
|
itemListUpdate()
|
||||||
|
|
||||||
|
openingClickLatched = Terrarum.mouseDown
|
||||||
|
|
||||||
|
tooltipShowing.clear()
|
||||||
|
INGAME.setTooltipMessage(null)
|
||||||
|
|
||||||
|
resetUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var encumbrancePerc = 0f
|
||||||
|
|
||||||
|
private fun itemListUpdate() {
|
||||||
|
// let itemlists be sorted
|
||||||
|
itemListCraftable.rebuild()
|
||||||
|
playerThings.rebuild(FILTER_CAT_ALL)
|
||||||
|
encumbrancePerc = getPlayerInventory().encumberment.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||||
|
if (!openingClickLatched) {
|
||||||
|
return super.touchDown(screenX, screenY, pointer, button)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateImpl(delta: Float) {
|
||||||
|
// NO super.update due to an infinite recursion
|
||||||
|
this.uiItems.forEach { it.update(delta) }
|
||||||
|
|
||||||
|
if (openingClickLatched && !Terrarum.mouseDown) openingClickLatched = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {
|
||||||
|
// NO super.render due to an infinite recursion
|
||||||
|
this.uiItems.forEach { it.render(frameDelta, batch, camera) }
|
||||||
|
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
|
||||||
|
// text label for two inventory grids
|
||||||
|
val craftingLabel = Lang["GAME_CRAFTING"]
|
||||||
|
val ingredientsLabel = Lang["GAME_INVENTORY_INGREDIENTS"]
|
||||||
|
|
||||||
|
App.fontGame.draw(batch, craftingLabel, thisOffsetX + (cellsWidth - App.fontGame.getWidth(craftingLabel)) / 2, thisOffsetY - INVENTORY_NAME_TEXT_GAP)
|
||||||
|
App.fontGame.draw(batch, ingredientsLabel, thisOffsetX + (cellsWidth - App.fontGame.getWidth(ingredientsLabel)) / 2, thisOffsetY + LAST_LINE_IN_GRID - INVENTORY_NAME_TEXT_GAP)
|
||||||
|
|
||||||
|
|
||||||
|
// control hints
|
||||||
|
val controlHintXPos = thisOffsetX + 2f
|
||||||
|
blendNormalStraightAlpha(batch)
|
||||||
|
App.fontGame.draw(batch, controlHelp, controlHintXPos, UIInventoryFull.yEnd - 20)
|
||||||
|
|
||||||
|
|
||||||
|
if (INGAME.actorNowPlaying != null) {
|
||||||
|
//draw player encumb
|
||||||
|
val encumbBarXPos = thisXend - UIInventoryCells.weightBarWidth + 36
|
||||||
|
val encumbBarYPos = UIInventoryFull.yEnd - 20 + 3f
|
||||||
|
UIInventoryCells.drawEncumbranceBar(batch, encumbBarXPos, encumbBarYPos, encumbrancePerc, INGAME.actorNowPlaying!!.inventory)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
blendNormalStraightAlpha(batch)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doOpening(delta: Float) {
|
||||||
|
super.doOpening(delta)
|
||||||
|
INGAME.setTooltipMessage(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doClosing(delta: Float) {
|
||||||
|
super.doClosing(delta)
|
||||||
|
INGAME.setTooltipMessage(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun endOpening(delta: Float) {
|
||||||
|
super.endOpening(delta)
|
||||||
|
tooltipShowing.clear()
|
||||||
|
INGAME.setTooltipMessage(null) // required!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun endClosing(delta: Float) {
|
||||||
|
super.endClosing(delta)
|
||||||
|
resetUI()
|
||||||
|
tooltipShowing.clear()
|
||||||
|
INGAME.setTooltipMessage(null) // required!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
data class RecipeIngredientRecord(
|
||||||
|
val selectedItem: ItemID,
|
||||||
|
val howManyPlayerHas: Long,
|
||||||
|
val howManyRecipeWants: Long,
|
||||||
|
val craftingStationAvailable: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getItemCandidatesForIngredient(inventory: FixtureInventory, ingredient: CraftingCodex.CraftingIngredients): List<InventoryPair> {
|
||||||
|
return if (ingredient.keyMode == CraftingCodex.CraftingItemKeyMode.TAG) {
|
||||||
|
val tags = ingredient.key.split(',')
|
||||||
|
val wantsWall = tags.contains("WALL")
|
||||||
|
// If the player has the required item, use it; otherwise, will take an item from the ItemCodex
|
||||||
|
inventory.filter { (itm, qty) ->
|
||||||
|
ItemCodex[itm]?.hasAllTags(tags) == true && qty >= ingredient.qty && (wantsWall == itm.isWall()) // true if (wants wall and is wall) or (wants no wall and is not wall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listOf(InventoryPair(ingredient.key, -1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveIngredientKey(inventory: FixtureInventory, ingredient: CraftingCodex.CraftingIngredients, product: ItemID): ItemID {
|
||||||
|
val candidate = getItemCandidatesForIngredient(inventory, ingredient).filter { it.itm != product }
|
||||||
|
|
||||||
|
// printdbg(this, "resolveIngredientKey product=$product, candidate=$candidate")
|
||||||
|
|
||||||
|
return if (ingredient.keyMode == CraftingCodex.CraftingItemKeyMode.TAG) {
|
||||||
|
// filter out the product itself from the ingredient
|
||||||
|
candidate.maxByOrNull { it.qty }?.itm ?: (
|
||||||
|
(ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.hasTag(ingredient.key)) it.key else null }) ?:
|
||||||
|
throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed (ingredient: $ingredient)")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ingredient.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each ingredient of the recipe, returns list of (ingredient, how many the player has the ingredient, how many the recipe wants)
|
||||||
|
*/
|
||||||
|
fun recipeToIngredientRecord(inventory: FixtureInventory, recipe: CraftingCodex.CraftingRecipe, nearbyCraftingStations: List<String>): List<RecipeIngredientRecord> {
|
||||||
|
val hasStation = if (recipe.workbench.isBlank()) true else nearbyCraftingStations.containsAll(recipe.workbench.split(','))
|
||||||
|
return recipe.ingredients.map { ingredient ->
|
||||||
|
val selectedItem = resolveIngredientKey(inventory, ingredient, recipe.product)
|
||||||
|
val howManyPlayerHas = inventory.searchByID(selectedItem)?.qty ?: 0L
|
||||||
|
val howManyTheRecipeWants = ingredient.qty
|
||||||
|
|
||||||
|
RecipeIngredientRecord(selectedItem, howManyPlayerHas, howManyTheRecipeWants, hasStation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -247,7 +247,6 @@ class UIInventoryFull(
|
|||||||
|
|
||||||
// private val transitionalMinimap = UIInventoryMinimap(this)
|
// private val transitionalMinimap = UIInventoryMinimap(this)
|
||||||
internal val transitionalCraftingUI = UICrafting(this)
|
internal val transitionalCraftingUI = UICrafting(this)
|
||||||
internal val transitionalTechTreeViewUI = UITechView(this)
|
|
||||||
internal val transitionalItemCells = UIInventoryCells(this)
|
internal val transitionalItemCells = UIInventoryCells(this)
|
||||||
internal val transitionalEscMenu = UIInventoryEscMenu(this)
|
internal val transitionalEscMenu = UIInventoryEscMenu(this)
|
||||||
val transitionPanel = UIItemHorizontalFadeSlide(
|
val transitionPanel = UIItemHorizontalFadeSlide(
|
||||||
@@ -257,7 +256,7 @@ class UIInventoryFull(
|
|||||||
width,
|
width,
|
||||||
App.scr.height,
|
App.scr.height,
|
||||||
1f,
|
1f,
|
||||||
listOf(transitionalCraftingUI, transitionalTechTreeViewUI),
|
listOf(transitionalCraftingUI),
|
||||||
listOf(transitionalItemCells),
|
listOf(transitionalItemCells),
|
||||||
listOf(transitionalEscMenu)
|
listOf(transitionalEscMenu)
|
||||||
)
|
)
|
||||||
@@ -320,7 +319,6 @@ class UIInventoryFull(
|
|||||||
transitionPanel.forcePosition(0)
|
transitionPanel.forcePosition(0)
|
||||||
catBar.setSelectedPanel(0)
|
catBar.setSelectedPanel(0)
|
||||||
transitionalCraftingUI.resetUI()
|
transitionalCraftingUI.resetUI()
|
||||||
transitionalTechTreeViewUI.resetUI()
|
|
||||||
it.setAsOpen()
|
it.setAsOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package net.torvald.terrarum.modulebasegame.ui
|
|||||||
|
|
||||||
import net.torvald.terrarum.CraftingRecipeCodex
|
import net.torvald.terrarum.CraftingRecipeCodex
|
||||||
import net.torvald.terrarum.ItemCodex
|
import net.torvald.terrarum.ItemCodex
|
||||||
import net.torvald.terrarum.ui.UIItemCatBar
|
|
||||||
import net.torvald.terrarum.ceilToInt
|
import net.torvald.terrarum.ceilToInt
|
||||||
import net.torvald.terrarum.gameitems.GameItem
|
import net.torvald.terrarum.gameitems.GameItem
|
||||||
import net.torvald.terrarum.gameitems.ItemID
|
import net.torvald.terrarum.gameitems.ItemID
|
||||||
@@ -15,7 +14,7 @@ import net.torvald.terrarum.ui.UIItemCatBar.Companion.CAT_ALL
|
|||||||
* Created by minjaesong on 2022-06-28.
|
* Created by minjaesong on 2022-06-28.
|
||||||
*/
|
*/
|
||||||
class UIItemCraftingCandidateGrid(
|
class UIItemCraftingCandidateGrid(
|
||||||
parentUI: UICrafting,
|
parentUI: UICraftingWorkbench,
|
||||||
initialX: Int, initialY: Int,
|
initialX: Int, initialY: Int,
|
||||||
horizontalCells: Int, verticalCells: Int,
|
horizontalCells: Int, verticalCells: Int,
|
||||||
drawScrollOnRightside: Boolean = false,
|
drawScrollOnRightside: Boolean = false,
|
||||||
@@ -83,7 +82,7 @@ class UIItemCraftingCandidateGrid(
|
|||||||
|
|
||||||
private fun isCraftable(player: FixtureInventory, recipe: CraftingCodex.CraftingRecipe, nearbyCraftingStations: List<String>): Boolean {
|
private fun isCraftable(player: FixtureInventory, recipe: CraftingCodex.CraftingRecipe, nearbyCraftingStations: List<String>): Boolean {
|
||||||
// printdbg(this, "Is this recipe craftable? $recipe")
|
// printdbg(this, "Is this recipe craftable? $recipe")
|
||||||
return UICrafting.recipeToIngredientRecord(player, recipe, nearbyCraftingStations).none {
|
return UICraftingWorkbench.recipeToIngredientRecord(player, recipe, nearbyCraftingStations).none {
|
||||||
// printdbg(this, " considering ingredient ${it.selectedItem}, ${it.howManyRecipeWants} is required and got ${it.howManyPlayerHas}; crafting station available? ${it.craftingStationAvailable}")
|
// printdbg(this, " considering ingredient ${it.selectedItem}, ${it.howManyRecipeWants} is required and got ${it.howManyPlayerHas}; crafting station available? ${it.craftingStationAvailable}")
|
||||||
it.howManyPlayerHas <= 0L || !it.craftingStationAvailable
|
it.howManyPlayerHas <= 0L || !it.craftingStationAvailable
|
||||||
}
|
}
|
||||||
@@ -114,14 +113,14 @@ class UIItemCraftingCandidateGrid(
|
|||||||
if (currentFilter1 == CAT_ALL)
|
if (currentFilter1 == CAT_ALL)
|
||||||
CraftingRecipeCodex.props.forEach { (_, recipes) ->
|
CraftingRecipeCodex.props.forEach { (_, recipes) ->
|
||||||
recipes.forEach {
|
recipes.forEach {
|
||||||
if (isCraftable((parentUI as UICrafting).getPlayerInventory(), it, (parentUI as UICrafting).nearbyCraftingStations)) {
|
if (isCraftable((parentUI as UICraftingWorkbench).getPlayerInventory(), it, (parentUI as UICraftingWorkbench).nearbyCraftingStations)) {
|
||||||
craftingRecipes.add(it)
|
craftingRecipes.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CraftingRecipeCodex.getCraftableRecipesUsingTheseItems(currentFilter1).forEach {
|
CraftingRecipeCodex.getCraftableRecipesUsingTheseItems(currentFilter1).forEach {
|
||||||
if (isCraftable((parentUI as UICrafting).getPlayerInventory(), it, (parentUI as UICrafting).nearbyCraftingStations)) {
|
if (isCraftable((parentUI as UICraftingWorkbench).getPlayerInventory(), it, (parentUI as UICraftingWorkbench).nearbyCraftingStations)) {
|
||||||
craftingRecipes.add(it)
|
craftingRecipes.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import net.torvald.unicode.getKeycapPC
|
|||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2024-02-18.
|
* Created by minjaesong on 2024-02-18.
|
||||||
*/
|
*/
|
||||||
class UITechView(val full: UIInventoryFull?, private val colourTheme: InventoryCellColourTheme = UIItemInventoryCellCommonRes.defaultInventoryCellTheme,
|
class UITechView(val inventoryUI: UIInventoryFull?, val parentContainer: UICrafting, private val colourTheme: InventoryCellColourTheme = UIItemInventoryCellCommonRes.defaultInventoryCellTheme,
|
||||||
) : UICanvas(
|
) : UICanvas(
|
||||||
toggleKeyLiteral = if (full == null) "control_key_inventory" else null,
|
toggleKeyLiteral = if (inventoryUI == null) "control_key_inventory" else null,
|
||||||
toggleButtonLiteral = if (full == null) "control_gamepad_start" else null
|
toggleButtonLiteral = if (inventoryUI == null) "control_gamepad_start" else null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override var width = Toolkit.drawWidth
|
override var width = Toolkit.drawWidth
|
||||||
@@ -34,10 +34,10 @@ class UITechView(val full: UIInventoryFull?, private val colourTheme: InventoryC
|
|||||||
|
|
||||||
// private val navbarX = posX1 + UIItemListNavBarVertical.LIST_TO_CONTROL_GAP
|
// private val navbarX = posX1 + UIItemListNavBarVertical.LIST_TO_CONTROL_GAP
|
||||||
// private val navbarY = posY1
|
// private val navbarY = posY1
|
||||||
private val navbarX = full!!.transitionalCraftingUI.itemListCraftable.navRemoCon.posX + 12
|
private val navbarX = parentContainer.transitionalCraftingUI.itemListCraftable.navRemoCon.posX + 12
|
||||||
private val navbarY = full!!.transitionalCraftingUI.itemListCraftable.navRemoCon.posY - 8
|
private val navbarY = parentContainer.transitionalCraftingUI.itemListCraftable.navRemoCon.posY - 8
|
||||||
private val navbarWidth = UIItemListNavBarVertical.WIDTH
|
private val navbarWidth = UIItemListNavBarVertical.WIDTH
|
||||||
private val navbarHeight = full!!.transitionalCraftingUI.itemListCraftable.height
|
private val navbarHeight = parentContainer.transitionalCraftingUI.itemListCraftable.height
|
||||||
|
|
||||||
private val panelX = 32 + navbarX
|
private val panelX = 32 + navbarX
|
||||||
private val panelY = navbarY
|
private val panelY = navbarY
|
||||||
@@ -61,8 +61,7 @@ class UITechView(val full: UIInventoryFull?, private val colourTheme: InventoryC
|
|||||||
highlightable = true
|
highlightable = true
|
||||||
).also {
|
).also {
|
||||||
it.clickOnceListener = { _, _ ->
|
it.clickOnceListener = { _, _ ->
|
||||||
full?.transitionPanel?.setLeftUIto(0)
|
parentContainer.showCraftingUI()
|
||||||
full?.transitionPanel?.uis?.get(0)?.show()
|
|
||||||
it.highlighted = false
|
it.highlighted = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user