LootTables.kt

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

package net.bowmap.server.loot

import org.bukkit.Material
import java.util.concurrent.ThreadLocalRandom
import net.bowmap.server.item.*

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

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<Item> {
    val i = ThreadLocalRandom.current().nextInt(list.size)
    return list[i].getLoot()
  }
}

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

  override fun getLoot(): List<Item> {
    val amount = ThreadLocalRandom.current().nextInt(min, max + 1)
    return listOf(Item(type = type, amount = 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<Item> = 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<Item> {
    return pickRandomCategory().getLoot()
  }
}