LootTables.kt

Branches > main > ad6f0ad > files / src / main / kotlin / net / bowmap / server / loot / LootTables.kt

package net.bowmap.server.loot

import org.bukkit.Material
import org.bukkit.inventory.ItemStack
import java.util.concurrent.ThreadLocalRandom

interface Loot {
  fun getLoot(): List<ItemStack>
}

data class LootList(val list: List<Loot>) : Loot {
  init {
    require(list.isNotEmpty()) { "Loot list must not be empty!" }
  }

  constructor(vararg loots: Loot) : this(loots.toList())

  override fun getLoot(): List<ItemStack> {
    val i = ThreadLocalRandom.current().nextInt(list.size)
    return list[i].getLoot()
  }
}

data class MaterialLoot(val material: Material, val min: Int = 1, val max: Int = min) : Loot {
  init {
    require(min >= 1 && min <= max && max <= material.maxStackSize) {
      "Invalid min/max stack sizes!"
    }
  }

  override fun getLoot(): List<ItemStack> {
    val amount = ThreadLocalRandom.current().nextInt(min, max + 1)
    return listOf(ItemStack(material, amount))
  }
}

data class Category(val weight: Int, val loot: Loot) : Loot {
  constructor(name: String, weight: Int, vararg loots: Loot) : this(
    weight,
    LootList(loots.toList())
  )

  init {
    require(weight > 0) { "weight must be > 0" }
  }

  override fun getLoot(): List<ItemStack> = loot.getLoot()
}

private fun buildLookup(categories: Array<out Category>): Array<Category> {
  val totalWeight = totalWeight(categories)
  val lookup = ArrayList<Category>(totalWeight)

  for (c in categories) {
    repeat(c.weight) {
      lookup.add(c)
    }
  }

  return lookup.toTypedArray()
}

private fun totalWeight(categories: Array<out Category>): Int {
  require(categories.isNotEmpty()) { "Category list must not be empty!" }

  val total = categories.sumOf { it.weight }

  require(total != 0) { "Weight cannot be 0" }

  return total
}

data class WeightedLoot(private val lookup: Array<Category>, private val totalWeight: Int) : Loot {
  constructor(vararg categories: Category) : this(buildLookup(categories), totalWeight(categories))

  private fun pickRandomCategory(): Category {
    val randomWeight = ThreadLocalRandom.current().nextInt(totalWeight)
    return lookup[randomWeight]
  }

  override fun getLoot(): List<ItemStack> {
    return pickRandomCategory().getLoot()
  }
}