Item.kt

Branches > main > latest > files / src / main / kotlin / net / bowmap / stormbow / game / Item.kt

package net.bowmap.stormbow.game

import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.inventory.ItemStack
import org.bukkit.Material
import kotlinx.collections.immutable.*

// Bowmap's Item type

enum class Rarity {
  COMMON, UNCOMMON, RARE, SPECIAL
}

fun rarityLoreComponent(rarity: Rarity): Component = when (rarity) {
  Rarity.COMMON -> Component.text("COMMON", NamedTextColor.WHITE).decoration(TextDecoration.ITALIC, false).decoration(TextDecoration.BOLD, true)
  Rarity.UNCOMMON -> Component.text("UNCOMMON", NamedTextColor.GREEN).decoration(TextDecoration.ITALIC, false).decoration(TextDecoration.BOLD, true)
  Rarity.RARE -> Component.text("RARE", NamedTextColor.BLUE).decoration(TextDecoration.ITALIC, false).decoration(TextDecoration.BOLD, true)
  Rarity.SPECIAL -> Component.text("SPECIAL", NamedTextColor.RED).decoration(TextDecoration.ITALIC, false).decoration(TextDecoration.BOLD, true)
}

data class Item(
  val type: ItemType,
  val amount: Int = 1,
) {
  val name: Component = name(type)
  val rarity: Rarity = rarity(type)
  val description: String? = description(type)
}

fun computeLore(item: Item): PersistentList<Component> {
  var loreList = persistentListOf<Component>()
  // TODO need spacing, so make a list of these lists and put spaces between them then merge
  if (item.description != null) {
    // TODO split the lore by text size
    loreList = loreList.adding(Component.text(item.description, NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false))
  }
  return loreList
}

/// The type of an item
/// Corrosponds to its Minecraft/Bukkit material and custommodeldata/texture
/// Properties like max stack size, name, description, etc., can be derived from this type as well
sealed interface ItemType

fun maxStackSize(type: ItemType) : Int = when (type) {
  is CustomItemType -> type.maxStackSize
  is MaterialItemType -> type.minecraftMaterial.maxStackSize
}

fun name(type: ItemType): Component = when (type) {
  is CustomItemType -> type.defaultName
  is MaterialItemType -> Component.translatable(type.material)
}

fun material(type: ItemType): Material = when (type) {
  is CustomItemType -> type.minecraftMaterial
  is MaterialItemType -> type.minecraftMaterial
}

fun description(type: ItemType): String? = when (type) {
  is CustomItemType -> type.description
  is MaterialItemType -> null // Can have descriptions later
}

fun rarity(type: ItemType): Rarity = when (type) {
  is CustomItemType -> type.rarity
  is MaterialItemType -> Rarity.COMMON
}

data class MaterialItemType(val material: Material) : ItemType {
  val minecraftMaterial = material
  val customModelData = null
}

enum class CustomItemType(
    val minecraftMaterial: Material,
    val defaultName: Component,
    val description: String?,
    val customModelData: Int? = null,
    val maxStackSize: Int = 64,
    val rarity: Rarity = Rarity.COMMON
) : ItemType {
  BUG(Material.STONE_BUTTON, Component.text("This is a bug yeag"), "You found a bug!", rarity = Rarity.SPECIAL)
}

/// itemStack interop functions

fun toItemStack(item: Item): ItemStack {
  // TODO custommodeldata
  val itemStack = ItemStack(material(item.type), item.amount)
  val meta = itemStack.getItemMeta()

  // itemName is not editable via an anvil, which is the desired behavior
  // TODO make sure its not italics!
  meta.itemName(item.name)
  meta.lore(computeLore(item))

  itemStack.setItemMeta(meta)
  return itemStack
}

fun toItemStacks(items: List<Item>): List<ItemStack> {
  return items.map(::toItemStack)
}

/// Turns a vanilla ItemStack into a Bowmap item.
/// This function is the inverse of toItemStack, so that toItem(toItemStack(x)) == x
fun toItem(itemStack: ItemStack): Item {
  // TODO custommodeldata
  // See the above itemName comment
  // val meta = itemStack.getItemMeta()
  return Item(
    // TODO right now this won't work with any custom materials
    // It'll have to be stored somwhere on the item :)
    type = MaterialItemType(itemStack.type),
    amount = itemStack.amount,
  )
}