mirror of
https://github.com/Auxilor/EcoEnchants.git
synced 2024-11-22 15:05:18 +01:00
Added repairing, reworked enchantment lookups, improved enchant lookup API, added custom placeholders
This commit is contained in:
parent
41b2502275
commit
33b66dc0fd
@ -12,6 +12,8 @@ import com.willfp.ecoenchants.config.VanillaEnchantsYml
|
||||
import com.willfp.ecoenchants.display.EnchantDisplay
|
||||
import com.willfp.ecoenchants.enchants.EcoEnchants
|
||||
import com.willfp.ecoenchants.enchants.LoreConversion
|
||||
import com.willfp.ecoenchants.enchants.impl.EnchantmentPermanenceCurse
|
||||
import com.willfp.ecoenchants.enchants.impl.EnchantmentRepairing
|
||||
import com.willfp.ecoenchants.enchants.impl.EnchantmentTelekinesis
|
||||
import com.willfp.ecoenchants.enchants.registerVanillaEnchants
|
||||
import com.willfp.ecoenchants.integrations.EnchantRegistrations
|
||||
@ -45,6 +47,9 @@ class EcoEnchantsPlugin : LibReforgePlugin() {
|
||||
override fun handleReloadAdditional() {
|
||||
// Load hardcoded enchantments
|
||||
EnchantmentTelekinesis(this)
|
||||
EnchantmentPermanenceCurse(this)
|
||||
EnchantmentRepairing(this)
|
||||
|
||||
registerVanillaEnchants(this)
|
||||
|
||||
logger.info(EcoEnchants.values().size.toString() + " Enchants Loaded")
|
||||
|
@ -0,0 +1,6 @@
|
||||
package com.willfp.ecoenchants.enchants
|
||||
|
||||
class DescriptionPlaceholder(
|
||||
val id: String,
|
||||
val value: Double
|
||||
)
|
@ -140,9 +140,18 @@ abstract class EcoEnchant(
|
||||
// and that way the enchantment isn't registered.
|
||||
if (!config.getBool("dont-register")) {
|
||||
register()
|
||||
doOnInit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doOnInit() {
|
||||
onInit()
|
||||
}
|
||||
|
||||
protected open fun onInit() {
|
||||
// Override when needed
|
||||
}
|
||||
|
||||
private fun register() {
|
||||
EcoEnchants.addNewEnchant(this)
|
||||
}
|
||||
@ -159,26 +168,46 @@ abstract class EcoEnchant(
|
||||
}
|
||||
|
||||
override fun getUnformattedDescription(level: Int): String {
|
||||
val placeholderValue = NumberUtils.evaluateExpression(
|
||||
config.getString("placeholder"),
|
||||
null,
|
||||
object : PlaceholderInjectable {
|
||||
override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
|
||||
return listOf(
|
||||
StaticPlaceholder(
|
||||
"level",
|
||||
) { level.toString() }
|
||||
)
|
||||
}
|
||||
// Fetch custom placeholders other than %placeholder%
|
||||
val uncompiledPlaceholders = config.getSubsection("placeholders").getKeys(false).associateWith {
|
||||
config.getString("placeholders.$it")
|
||||
}.toMutableMap()
|
||||
|
||||
override fun clearInjectedPlaceholders() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
// Add %placeholder% placeholder in
|
||||
uncompiledPlaceholders["placeholder"] = config.getString("placeholder")
|
||||
|
||||
return config.getString("description")
|
||||
.replace("%placeholder%", NumberUtils.format(placeholderValue))
|
||||
// Evaluate each placeholder
|
||||
val placeholders = uncompiledPlaceholders.map { (id, expr) ->
|
||||
DescriptionPlaceholder(
|
||||
id,
|
||||
NumberUtils.evaluateExpression(
|
||||
expr,
|
||||
null,
|
||||
object : PlaceholderInjectable {
|
||||
override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
|
||||
return listOf(
|
||||
StaticPlaceholder(
|
||||
"level",
|
||||
) { level.toString() }
|
||||
)
|
||||
}
|
||||
|
||||
override fun clearInjectedPlaceholders() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Apply placeholders to description
|
||||
val rawDescription = config.getString("description")
|
||||
var description = rawDescription
|
||||
for (placeholder in placeholders) {
|
||||
description = description.replace("%${placeholder.id}%", NumberUtils.format(placeholder.value))
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
|
@ -0,0 +1,53 @@
|
||||
package com.willfp.ecoenchants.enchants.impl
|
||||
|
||||
import com.willfp.eco.util.DurabilityUtils
|
||||
import com.willfp.ecoenchants.EcoEnchantsPlugin
|
||||
import com.willfp.ecoenchants.enchants.EcoEnchant
|
||||
import com.willfp.ecoenchants.target.EnchantLookup.getActiveEnchantLevelInSlot
|
||||
import com.willfp.ecoenchants.target.EnchantLookup.hasEnchantActive
|
||||
import com.willfp.ecoenchants.target.TargetSlot
|
||||
import org.bukkit.Bukkit
|
||||
|
||||
class EnchantmentRepairing(
|
||||
plugin: EcoEnchantsPlugin
|
||||
) : EcoEnchant(
|
||||
"repairing",
|
||||
plugin,
|
||||
force = false
|
||||
) {
|
||||
override fun onInit() {
|
||||
val frequency = config.getInt("frequency").toLong()
|
||||
|
||||
plugin.scheduler.runTimer(frequency, frequency) {
|
||||
handleRepairing()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRepairing() {
|
||||
val notWhileHolding = config.getBool("not-while-holding")
|
||||
|
||||
for (player in Bukkit.getOnlinePlayers()) {
|
||||
if (player.hasEnchantActive(this)) {
|
||||
val repairPerLevel = config.getIntFromExpression("repair-per-level", player)
|
||||
|
||||
for ((slot, item) in player.inventory.withIndex()) {
|
||||
if (item == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (notWhileHolding && slot in TargetSlot.HANDS.getItemSlots(player)) {
|
||||
continue
|
||||
}
|
||||
|
||||
val level = player.getActiveEnchantLevelInSlot(this, slot)
|
||||
|
||||
if (level == 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
DurabilityUtils.repairItem(item, level * repairPerLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,44 +2,55 @@ package com.willfp.ecoenchants.target
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.willfp.eco.core.fast.fast
|
||||
import com.willfp.ecoenchants.EcoEnchantsPlugin
|
||||
import com.willfp.ecoenchants.enchants.EcoEnchant
|
||||
import com.willfp.ecoenchants.enchants.EcoEnchantLevel
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
typealias SlotProvider = (Player) -> Map<ItemStack?, TargetSlot?>
|
||||
// The Int is the inventory slot ID
|
||||
typealias SlotProvider = (Player) -> Map<Int, ItemInSlot>
|
||||
|
||||
data class ItemInSlot(
|
||||
val item: ItemStack,
|
||||
val slot: TargetSlot
|
||||
)
|
||||
|
||||
private data class HeldEnchant(
|
||||
val enchant: EcoEnchant,
|
||||
val level: Int
|
||||
)
|
||||
|
||||
object EnchantLookup {
|
||||
private val plugin = EcoEnchantsPlugin.instance
|
||||
|
||||
private val slotProviders = mutableSetOf<(Player) -> Map<ItemStack, TargetSlot>>()
|
||||
private val slotProviders = mutableSetOf<SlotProvider>()
|
||||
|
||||
private val itemCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(2, TimeUnit.SECONDS)
|
||||
.build<Player, Map<ItemStack, TargetSlot>>()
|
||||
.build<Player, Map<Int, ItemInSlot>>()
|
||||
|
||||
private val enchantCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(2, TimeUnit.SECONDS)
|
||||
.build<Player, Map<Int, Collection<HeldEnchant>>>()
|
||||
|
||||
// Higher frequency cache as less intensive
|
||||
private val enchantLevelCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(200, TimeUnit.MILLISECONDS)
|
||||
.build<Player, Map<EcoEnchant, Int>>()
|
||||
|
||||
@JvmStatic
|
||||
fun registerProvider(provider: SlotProvider) {
|
||||
slotProviders.add {
|
||||
val found = mutableMapOf<ItemStack, TargetSlot>()
|
||||
for ((item, slot) in provider(it)) {
|
||||
if (item != null && slot != null) {
|
||||
found[item] = slot
|
||||
}
|
||||
val found = mutableMapOf<Int, ItemInSlot>()
|
||||
for ((slot, inSlot) in provider(it)) {
|
||||
found[slot] = inSlot
|
||||
}
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
private fun provide(player: Player): Map<ItemStack, TargetSlot> {
|
||||
private fun provide(player: Player): Map<Int, ItemInSlot> {
|
||||
return itemCache.get(player) {
|
||||
val found = mutableMapOf<ItemStack, TargetSlot>()
|
||||
val found = mutableMapOf<Int, ItemInSlot>()
|
||||
for (provider in slotProviders) {
|
||||
found.putAll(provider(player))
|
||||
}
|
||||
@ -48,22 +59,36 @@ object EnchantLookup {
|
||||
}
|
||||
}
|
||||
|
||||
val Player.heldEnchants: Map<EcoEnchant, Int>
|
||||
/**
|
||||
* The inventory slot IDs mapped to HeldEnchants found in that slot.
|
||||
*/
|
||||
private val Player.slotHeldEnchants: Map<Int, Collection<HeldEnchant>>
|
||||
get() {
|
||||
return enchantCache.get(player) {
|
||||
val found = mutableMapOf<EcoEnchant, Int>()
|
||||
return enchantCache.get(this) {
|
||||
val found = mutableMapOf<Int, MutableCollection<HeldEnchant>>()
|
||||
|
||||
for ((itemStack, slot) in provide(this)) {
|
||||
val enchants = itemStack.fast().enchants
|
||||
for ((slotID, inSlot) in provide(this)) {
|
||||
val (item, slot) = inSlot
|
||||
|
||||
// Prevent repeating slot IDs found in multiple TargetSlots (e.g. HANDS and ANY)
|
||||
if (found.containsKey(slotID)) {
|
||||
continue
|
||||
}
|
||||
|
||||
val enchants = item.fast().enchants
|
||||
|
||||
for ((enchant, level) in enchants) {
|
||||
if (enchant !is EcoEnchant) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (enchant.slots.contains(slot) || slot == TargetSlot.ANY) {
|
||||
found[enchant] = found.getOrDefault(enchant, 0) + level
|
||||
if (slot !in enchant.slots) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Basically a multimap
|
||||
found[slotID] = (found.getOrDefault(slotID, mutableListOf())
|
||||
+ HeldEnchant(enchant, level)).toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +96,25 @@ object EnchantLookup {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All EcoEnchants mapped to their IDs, regardless of conditions.
|
||||
*/
|
||||
val Player.heldEnchants: Map<EcoEnchant, Int>
|
||||
get() {
|
||||
return enchantLevelCache.get(this) {
|
||||
val found = mutableMapOf<EcoEnchant, Int>()
|
||||
|
||||
for ((enchant, level) in it.slotHeldEnchants.values.flatten()) {
|
||||
found[enchant] = found.getOrDefault(enchant, 0) + level
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All EcoEnchants mapped to their IDs, respecting conditions.
|
||||
*/
|
||||
val Player.activeEnchants: Map<EcoEnchant, Int>
|
||||
get() {
|
||||
return this.heldEnchants.filter { (enchant, level) ->
|
||||
@ -78,22 +122,90 @@ object EnchantLookup {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enchantment level on a player, ignoring conditions.
|
||||
*
|
||||
* @return The level, or 0 if not found.
|
||||
*/
|
||||
fun Player.getEnchantLevel(enchant: EcoEnchant): Int {
|
||||
return this.heldEnchants[enchant] ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enchantment level on a player, respecting.
|
||||
*
|
||||
* @return The level, or 0 if not found.
|
||||
*/
|
||||
fun Player.getActiveEnchantLevel(enchant: EcoEnchant): Int {
|
||||
return this.activeEnchants[enchant] ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enchantment level on a player in a specific slot, ignoring conditions.
|
||||
*
|
||||
* @return The level, or 0 if not found.
|
||||
*/
|
||||
fun Player.getEnchantLevelInSlot(enchant: EcoEnchant, slot: Int): Int {
|
||||
val inSlot = this.slotHeldEnchants[slot] ?: return 0
|
||||
val heldEnchant = inSlot.firstOrNull { it.enchant == enchant } ?: return 0
|
||||
return heldEnchant.level
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enchantment level on a player in a specific slot, respecting conditions.
|
||||
*
|
||||
* @return The level, or 0 if not found.
|
||||
*/
|
||||
fun Player.getActiveEnchantLevelInSlot(enchant: EcoEnchant, slot: Int): Int {
|
||||
val level = getEnchantLevelInSlot(enchant, slot)
|
||||
|
||||
if (level == 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (enchant.getLevel(level).conditions.any { !it.isMet(this) }) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if a player has an enchantment, ignoring conditions.
|
||||
*
|
||||
* @return If the player has the enchantment.
|
||||
*/
|
||||
fun Player.hasEnchant(enchant: EcoEnchant): Boolean {
|
||||
return this.getEnchantLevel(enchant) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if a player has an enchantment, respecting conditions.
|
||||
*
|
||||
* @return If the player has the enchantment.
|
||||
*/
|
||||
fun Player.hasEnchantActive(enchant: EcoEnchant): Boolean {
|
||||
return this.getActiveEnchantLevel(enchant) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if a player has an enchantment in a slot, ignoring conditions.
|
||||
*
|
||||
* @return If the player has the enchantment.
|
||||
*/
|
||||
fun Player.hasEnchantInSlot(enchant: EcoEnchant, slot: Int): Boolean {
|
||||
return this.getEnchantLevelInSlot(enchant, slot) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if a player has an enchantment in a slot, respecting conditions.
|
||||
*
|
||||
* @return If the player has the enchantment.
|
||||
*/
|
||||
fun Player.hasEnchantActiveInSlot(enchant: EcoEnchant, slot: Int): Boolean {
|
||||
return this.getActiveEnchantLevelInSlot(enchant, slot) > 0
|
||||
}
|
||||
|
||||
val Player.heldEnchantLevels: List<EcoEnchantLevel>
|
||||
get() = this.heldEnchants
|
||||
.map { (enchant, level) -> enchant.getLevel(level) }
|
||||
@ -107,32 +219,21 @@ object EnchantLookup {
|
||||
}
|
||||
|
||||
init {
|
||||
registerProvider {
|
||||
mapOf(
|
||||
Pair(
|
||||
it.inventory.itemInMainHand,
|
||||
TargetSlot.HANDS
|
||||
)
|
||||
)
|
||||
}
|
||||
fun createProvider(slot: TargetSlot): SlotProvider {
|
||||
return { player: Player ->
|
||||
val found = mutableMapOf<Int, ItemInSlot>()
|
||||
|
||||
if (!plugin.configYml.getBool("no-offhand")) {
|
||||
registerProvider {
|
||||
mapOf(
|
||||
Pair(
|
||||
it.inventory.itemInOffHand,
|
||||
TargetSlot.HANDS
|
||||
)
|
||||
)
|
||||
for (slotID in slot.getItemSlots(player)) {
|
||||
val item = player.inventory.getItem(slotID) ?: continue
|
||||
found[slotID] = ItemInSlot(item, slot)
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
registerProvider {
|
||||
val items = mutableMapOf<ItemStack?, TargetSlot?>()
|
||||
for (stack in it.inventory.armorContents) {
|
||||
items[stack] = TargetSlot.ARMOR
|
||||
}
|
||||
items
|
||||
for (slot in TargetSlot.values()) {
|
||||
registerProvider(createProvider(slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.willfp.eco.core.items.Items
|
||||
import com.willfp.eco.core.items.TestableItem
|
||||
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
|
||||
import com.willfp.ecoenchants.EcoEnchantsPlugin
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.*
|
||||
|
||||
@ -66,8 +67,23 @@ internal object AllEnchantmentTarget : EnchantmentTarget {
|
||||
}
|
||||
}
|
||||
|
||||
enum class TargetSlot {
|
||||
HANDS,
|
||||
ARMOR,
|
||||
ANY
|
||||
enum class TargetSlot(
|
||||
private val itemSlotGetter: (Player) -> Collection<Int>
|
||||
) {
|
||||
HANDS({
|
||||
listOf(
|
||||
it.inventory.heldItemSlot,
|
||||
45
|
||||
)
|
||||
}),
|
||||
|
||||
ARMOR({
|
||||
listOf(5, 6, 7, 8)
|
||||
}),
|
||||
|
||||
ANY({
|
||||
(0..45).toList()
|
||||
});
|
||||
|
||||
fun getItemSlots(player: Player): Collection<Int> = itemSlotGetter(player)
|
||||
}
|
||||
|
@ -3,9 +3,6 @@
|
||||
# by Auxilor
|
||||
#
|
||||
|
||||
# If the hand slot shouldn't work in the players offhand
|
||||
no-offhand: false # Restart your server after this option, doesn't work with /ecoenchants reload
|
||||
|
||||
# Options for enchanting items in the enchanting table
|
||||
enchanting-table:
|
||||
enabled: true # If custom enchantments should be available from enchanting tables
|
||||
|
@ -5,8 +5,10 @@
|
||||
# _example.yml is not loaded.
|
||||
|
||||
display-name: "Example" # The name of the enchantment in-game
|
||||
description: "Gives a &a%placeholder%%&8 bonus to damage" # The description of the enchantment
|
||||
placeholder: "%level% * 20" # The placeholder to show in the enchantment description
|
||||
description: "Gives a &a%placeholder%%&8 and a &a+%damage%&8 bonus to damage" # The description of the enchantment
|
||||
placeholder: "%level% * 20" # The placeholder to show in the enchantment description (optional, can only use custom placeholders)
|
||||
placeholders: # Extra placeholders to show in the enchantment description (optional)
|
||||
damage: "%level% * 2" # Here, %damage% would be the extra placeholder to use
|
||||
type: normal # The enchantment type, from types.yml
|
||||
|
||||
targets: # The items that the enchantment can be applied to, see targets.yml
|
||||
@ -26,7 +28,12 @@ enchantable: true # If the enchantment can be obtained from enchanting tables
|
||||
effects:
|
||||
- id: damage_multiplier
|
||||
args:
|
||||
multiplier: 1 + 0.2 * %level%
|
||||
multiplier: "1 + 0.2 * %level%"
|
||||
triggers:
|
||||
- melee_attack
|
||||
- id: damage_victim
|
||||
args:
|
||||
damage: "2 * %level%"
|
||||
triggers:
|
||||
- melee_attack
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
display-name: "Repairing"
|
||||
description: "Automatically gain &a%amount%&8 durability every &a%frequency% &8seconds"
|
||||
type: normal
|
||||
placeholders:
|
||||
frequency: "1.5"
|
||||
amount: "%level%"
|
||||
|
||||
targets:
|
||||
- pickaxe
|
||||
- sword
|
||||
- axe
|
||||
conflicts: [ ]
|
||||
rarity: common
|
||||
max-level: 1
|
||||
|
||||
tradeable: true
|
||||
discoverable: true
|
||||
enchantable: true
|
||||
|
||||
conditions: [ ]
|
||||
|
||||
repair-per-level: 1 # The amount of durability to add on each item
|
||||
frequency: 30 # How often to repair the item (in ticks, doesn't support %level%)
|
||||
not-while-holding: true # If items shouldn't be repaired while they are being held
|
Loading…
Reference in New Issue
Block a user