mirror of
https://github.com/alexcrea/CustomAnvil.git
synced 2026-08-28 08:25:56 +02:00
Use material key instead of material enum This is made with the goal of making eco item work as independant item. so item type key now depend on eco item's id Known issue: EcoEnchant target group still do not has the eco item id inside it and cannot be handled properly by custom anvil
312 lines
No EOL
11 KiB
Kotlin
312 lines
No EOL
11 KiB
Kotlin
package xyz.alexcrea.cuanvil.group
|
|
|
|
import io.delilaheve.CustomAnvil
|
|
import org.bukkit.NamespacedKey
|
|
import org.bukkit.configuration.ConfigurationSection
|
|
import org.bukkit.enchantments.Enchantment
|
|
import org.bukkit.inventory.ItemStack
|
|
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment
|
|
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
|
|
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
|
|
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
|
|
import java.util.*
|
|
import kotlin.collections.set
|
|
|
|
class EnchantConflictManager {
|
|
|
|
companion object {
|
|
// Path for the enchantments list
|
|
const val ENCH_LIST_PATH = "enchantments"
|
|
|
|
// Path for list of enchantments conflicting before level
|
|
//TODO add test and gui
|
|
const val CONFLICT_AFTER_LEVEL_LIST_PATH = "conflict_after_level"
|
|
|
|
// Path for list of enchantments conflicting before level
|
|
//TODO add test and gui
|
|
const val CONFLICT_BEFORE_LEVEL_LIST_PATH = "conflict_before_level"
|
|
|
|
// Path for group list related to the conflict
|
|
const val CONFLICT_GROUP_PATH = "notAffectedGroups"
|
|
|
|
// Path for the maximum number of enchantment before validating the conflict
|
|
const val ENCH_MAX_PATH = "maxEnchantmentBeforeConflict"
|
|
|
|
// Path for a flag: if the enchantment will be used in the last supported version
|
|
// TODO maybe replace this system by a list of "future" enchantment.
|
|
private const val FUTURE_USE_PATH = "useInFuture"
|
|
|
|
// Default name for a joining group
|
|
const val DEFAULT_GROUP_NAME = "joinedGroup"
|
|
|
|
// 1.20.5 compatibility TODO better update system
|
|
private val SWEEPING_EDGE_ENCHANT = Collections.singletonList<CAEnchantment>(
|
|
CAEnchantment.getByKey(NamespacedKey.minecraft("sweeping_edge"))
|
|
?: CAEnchantment.getByKey(Enchantment.SWEEPING_EDGE.key)
|
|
)
|
|
|
|
}
|
|
|
|
lateinit var conflictList: ArrayList<EnchantConflictGroup>
|
|
|
|
|
|
private fun warnBadKey(key: String) {
|
|
CustomAnvil.instance.logger.warning("Invalid key $key for conflict: is not a conflict")
|
|
}
|
|
|
|
// Read and prepare all conflict
|
|
fun prepareConflicts(config: ConfigurationSection, itemManager: ItemGroupManager) {
|
|
conflictList = ArrayList()
|
|
|
|
// Clear conflict if exist
|
|
for (enchant in CAEnchantmentRegistry.getInstance().values()) {
|
|
enchant.clearConflict()
|
|
}
|
|
|
|
val keys = config.getKeys(false)
|
|
for (key in keys) {
|
|
val section = config.getConfigurationSection(key)
|
|
if(section == null) {
|
|
warnBadKey(key)
|
|
continue
|
|
}
|
|
val conflict = createConflict(section, itemManager, key)
|
|
|
|
addConflict(conflict)
|
|
}
|
|
|
|
}
|
|
|
|
fun addConflict(conflict: EnchantConflictGroup) {
|
|
addConflictToEnchantments(conflict)
|
|
conflictList.add(conflict)
|
|
}
|
|
|
|
fun removeConflict(conflict: EnchantConflictGroup) {
|
|
removeConflictFromEnchantments(conflict)
|
|
conflictList.remove(conflict)
|
|
}
|
|
|
|
// Add the conflict to enchantments
|
|
private fun addConflictToEnchantments(conflict: EnchantConflictGroup) {
|
|
conflict.getEnchants().forEach { enchant ->
|
|
enchant.addConflict(conflict)
|
|
}
|
|
}
|
|
|
|
// Remove the conflict from enchantments
|
|
private fun removeConflictFromEnchantments(conflict: EnchantConflictGroup) {
|
|
conflict.getEnchants().forEach { enchant ->
|
|
enchant.removeConflict(conflict)
|
|
}
|
|
}
|
|
|
|
// create and read a conflict from a yaml section
|
|
private fun createConflict(
|
|
section: ConfigurationSection,
|
|
itemManager: ItemGroupManager,
|
|
conflictName: String
|
|
): EnchantConflictGroup {
|
|
// Is it planed for the future
|
|
val futureUse = section.getBoolean(FUTURE_USE_PATH, false)
|
|
// Create conflict
|
|
val conflict = createConflictObject(section, itemManager, conflictName)
|
|
// Read and add enchantment to conflict
|
|
val enchantList = section.getStringList(ENCH_LIST_PATH)
|
|
for (enchantName in enchantList) {
|
|
val enchants = getEnchantByIdentifier(enchantName)
|
|
if (enchants.isEmpty()) {
|
|
if (!futureUse) { //TODO future use will be deprecated once the new update system is finished
|
|
CustomAnvil.instance.logger.warning("Enchantment $enchantName do not exist but was asked for conflict $conflictName")
|
|
}
|
|
continue
|
|
}
|
|
conflict.addEnchantments(enchants)
|
|
}
|
|
if (conflict.getEnchants().isEmpty()) {
|
|
if (!futureUse) { //TODO future use will be deprecated once the new update system is finished
|
|
CustomAnvil.instance.logger.warning("Conflict $conflictName do not have valid enchantment, it will not do anything")
|
|
}
|
|
}
|
|
|
|
val conflictsAfterLevel = section.getConfigurationSection(CONFLICT_AFTER_LEVEL_LIST_PATH)
|
|
val conflictsAfterMap = conflict.getConflictAfters()
|
|
fetchConditionalRestriction(conflictsAfterMap, conflictsAfterLevel, conflictName)
|
|
|
|
val conflictsBeforeLevel = section.getConfigurationSection(CONFLICT_BEFORE_LEVEL_LIST_PATH)
|
|
val conflictsBeforeMap = conflict.getConflictsBefore()
|
|
fetchConditionalRestriction(conflictsBeforeMap, conflictsBeforeLevel, conflictName)
|
|
|
|
return conflict
|
|
}
|
|
|
|
private fun fetchConditionalRestriction(restrictions: MutableMap<CAEnchantment, Int>, section: ConfigurationSection?, conflictName: String) {
|
|
if(section == null) return
|
|
for (enchantName in section.getKeys(false)) {
|
|
val enchants = getEnchantByIdentifier(enchantName)
|
|
if (enchants.isEmpty()) {
|
|
CustomAnvil.instance.logger.warning("Enchantment $enchantName do not exist but was asked for conditional restriction for conflict $conflictName")
|
|
continue
|
|
}
|
|
|
|
val value = section.getInt(enchantName, -1)
|
|
if(value < 0) continue
|
|
|
|
for (enchant in enchants) {
|
|
val previous = restrictions.getOrDefault(enchant, value)
|
|
restrictions[enchant] = value.coerceAtMost(previous)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getEnchantByIdentifier(enchantName: String): List<CAEnchantment> {
|
|
val key = NamespacedKey.fromString(enchantName)
|
|
if (key != null) {
|
|
val enchantment = CAEnchantment.getByKey(key)
|
|
if (enchantment != null) return Collections.singletonList(enchantment)
|
|
|
|
}
|
|
|
|
// Temporary solution for 1.20.5
|
|
when (enchantName) {
|
|
"minecraft:sweeping", "sweeping",
|
|
"minecraft:sweeping_edge", "sweeping_edge" -> {
|
|
return SWEEPING_EDGE_ENCHANT
|
|
}
|
|
}
|
|
|
|
return CAEnchantment.getListByName(enchantName)
|
|
}
|
|
|
|
|
|
private fun createConflictObject(
|
|
section: ConfigurationSection,
|
|
itemManager: ItemGroupManager,
|
|
conflictName: String
|
|
): EnchantConflictGroup {
|
|
// Get the maximum number of enchantment before validating the conflict
|
|
var minBeforeBlock = section.getInt(ENCH_MAX_PATH, 0)
|
|
if (minBeforeBlock < 0) {
|
|
minBeforeBlock = 0
|
|
CustomAnvil.instance.logger.warning("Conflict $conflictName have an invalid value of $ENCH_MAX_PATH")
|
|
CustomAnvil.instance.logger.warning("It should be more or equal to 0. default to 0")
|
|
}
|
|
// Find or create the selected group for the conflict
|
|
val groupList = section.getStringList(CONFLICT_GROUP_PATH)
|
|
val finalGroup = IncludeGroup(DEFAULT_GROUP_NAME)
|
|
for (groupName in groupList) {
|
|
finalGroup.addToPolicy(findGroup(groupName, itemManager, conflictName))
|
|
}
|
|
|
|
// Return conflict
|
|
return EnchantConflictGroup(conflictName, finalGroup, minBeforeBlock)
|
|
}
|
|
|
|
private fun findGroup(
|
|
groupName: String,
|
|
itemManager: ItemGroupManager,
|
|
conflictName: String
|
|
): AbstractMaterialGroup {
|
|
val group = itemManager.get(groupName)
|
|
if (group == null) {
|
|
CustomAnvil.instance.logger.warning("Material group $groupName do not exist but is ask by conflict $conflictName")
|
|
return IncludeGroup("error_placeholder")
|
|
}
|
|
|
|
return group
|
|
}
|
|
|
|
fun isConflicting(
|
|
appliedEnchants: Map<CAEnchantment, Int>,
|
|
item: ItemStack,
|
|
newEnchant: CAEnchantment
|
|
): ConflictType {
|
|
val type = item.customType
|
|
CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${type}")
|
|
val conflictList = newEnchant.conflicts
|
|
|
|
var result = ConflictType.NO_CONFLICT
|
|
for (conflict in conflictList) {
|
|
val isBigConflict = conflict.getEnchants().size > 1
|
|
if (result == ConflictType.ITEM_CONFLICT && !isBigConflict) {
|
|
CustomAnvil.verboseLog("skipping small conflict ${conflict.name}")
|
|
continue
|
|
}
|
|
|
|
val allowed = conflict.allowed(appliedEnchants, type)
|
|
CustomAnvil.verboseLog("Was against $conflict and conflicting: ${!allowed} ")
|
|
if (!allowed) {
|
|
if (conflict.getEnchants().size <= 1) {
|
|
result = ConflictType.ITEM_CONFLICT
|
|
CustomAnvil.verboseLog("Small conflict (${conflict.name}), continuing")
|
|
} else {
|
|
CustomAnvil.verboseLog("Big conflict (${conflict.name}), stopping")
|
|
return ConflictType.ENCHANTMENT_CONFLICT
|
|
}
|
|
}
|
|
}
|
|
|
|
val immutableEnchants = Collections.unmodifiableMap(appliedEnchants)
|
|
for (appliedEnchant in appliedEnchants.keys) {
|
|
if (appliedEnchant is AdditionalTestEnchantment) {
|
|
val doConflict = appliedEnchant.isEnchantConflict(immutableEnchants, type)
|
|
if (doConflict) {
|
|
CustomAnvil.verboseLog("Big conflict by additional test, stopping")
|
|
return ConflictType.ENCHANTMENT_CONFLICT
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)) {
|
|
val partialItem = createPartialResult(item, immutableEnchants)
|
|
|
|
if (newEnchant.isItemConflict(immutableEnchants, type, partialItem)) {
|
|
return ConflictType.ITEM_CONFLICT
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
private fun createPartialResult(item: ItemStack, enchantments: Map<CAEnchantment, Int>): ItemStack {
|
|
val newItem = item.clone()
|
|
|
|
CAEnchantment.clearEnchants(newItem)
|
|
enchantments.forEach { enchantment ->
|
|
enchantment.key.addEnchantmentUnsafe(newItem, enchantment.value)
|
|
}
|
|
|
|
return newItem
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Provide information about the current conflict.
|
|
*/
|
|
enum class ConflictType(private val importance: Int) {
|
|
/**
|
|
* Allowed to proceed the anvil process.
|
|
*/
|
|
NO_CONFLICT(0),
|
|
|
|
/**
|
|
* Inform that the anvil process should not change the current applied enchantment.
|
|
*/
|
|
ITEM_CONFLICT(1),
|
|
|
|
/**
|
|
* Inform that the anvil process should not change the current applied enchantment.
|
|
* Also add sacrificeIllegalCost for every enchantment marked as big conflict.
|
|
*/
|
|
ENCHANTMENT_CONFLICT(2);
|
|
|
|
fun getWorstConflict(otherConflict: ConflictType): ConflictType {
|
|
return if (this.importance > otherConflict.importance) this
|
|
else otherConflict
|
|
|
|
}
|
|
|
|
} |