- Support for
[ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants--75-vanilla-like-enchantments.61693/)
- Add enchantment merge level limit (& config gui for to configure it)
- Use namespace for enchantment instead of name (still use name as
backup)
- Deprecated API method using enchantment name and replaced them with
namespace methods
- Improved main logic code readability (but may break thing)

Multiplies changes (improved logic, namespace...) make this version
likely to have something broken. Next version(s) may be fix
This commit is contained in:
alexcrea 2024-10-15 08:42:22 +02:00 committed by GitHub
commit 7266d4b0c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 2048 additions and 1463 deletions

View file

@ -14,7 +14,7 @@ plugins {
}
group = "xyz.alexcrea"
version = "1.6.2"
version = "1.6.3"
repositories {
// EcoEnchants
@ -36,6 +36,10 @@ dependencies {
compileOnly("com.willfp:EcoEnchants:12.5.1")
compileOnly("com.willfp:eco:6.70.1")
// ExcellentEnchants
compileOnly(files("libs/nightcore-2.6.4.jar"))
compileOnly(files("libs/ExcellentEnchants-4.2.2.jar"))
// Disenchantment
compileOnly("cz.kominekjan:Disenchantment:v5.4.0")

View file

@ -97,49 +97,46 @@ default_limit: 5
#
# Valid range of 1 - 255 for each enchantment
enchant_limits:
aqua_affinity: 1
binding_curse: 1
channeling: 1
flame: 1
infinity: 1
mending: 1
multishot: 1
silk_touch: 1
vanishing_curse: 1
depth_strider: 3 # anything more than 3 is treated as 3 by the game
protection: 4
fire_protection: 4
blast_protection: 4
projectile_protection: 4
feather_falling: 4
thorns: 3
respiration: 3
sharpness: 5
smite: 5
bane_of_arthropods: 5
knockback: 2
fire_aspect: 2
looting: 3
sweeping: 3
sweeping_edge: 3
efficiency: 5
unbreaking: 3
fortune: 3
power: 5
punch: 2
luck_of_the_sea: 3
lure: 3
frost_walker: 2
impaling: 5
riptide: 3
loyalty: 3
piercing: 4
quick_charge: 3
soul_speed: 3
swift_sneak: 3
density: 5
breach: 4
wind_burst: 3
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
minecraft:channeling: 1
minecraft:flame: 1
minecraft:infinity: 1
minecraft:mending: 1
minecraft:multishot: 1
minecraft:silk_touch: 1
minecraft:vanishing_curse: 1
minecraft:depth_strider: 3 # anything more than 3 is treated as 3 by the game
minecraft:protection: 4
minecraft:fire_protection: 4
minecraft:blast_protection: 4
minecraft:projectile_protection: 4
minecraft:feather_falling: 4
minecraft:thorns: 3
minecraft:respiration: 3
minecraft:sharpness: 5
minecraft:smite: 5
minecraft:bane_of_arthropods: 5
minecraft:knockback: 2
minecraft:fire_aspect: 2
minecraft:looting: 3
minecraft:sweeping: 3
minecraft:sweeping_edge: 3
minecraft:efficiency: 5
minecraft:unbreaking: 3
minecraft:fortune: 3
minecraft:power: 5
minecraft:punch: 2
minecraft:luck_of_the_sea: 3
minecraft:lure: 3
minecraft:frost_walker: 2
minecraft:impaling: 5
minecraft:riptide: 3
minecraft:loyalty: 3
minecraft:piercing: 4
minecraft:quick_charge: 3
minecraft:soul_speed: 3
minecraft:swift_sneak: 3
# Multipliers used to calculate the enchantment's value in repair/combining
#
@ -153,135 +150,137 @@ enchant_limits:
# With default values protection 4 would have a value of 4 when
# coming from either a book (4 * 1) or an item (4 * 1)
enchant_values:
aqua_affinity:
minecraft:aqua_affinity:
item: 4
book: 2
bane_of_arthropods:
minecraft:bane_of_arthropods:
item: 2
book: 1
binding_curse:
minecraft:binding_curse:
item: 8
book: 4
blast_protection:
minecraft:blast_protection:
item: 4
book: 2
channeling:
minecraft:channeling:
item: 8
book: 4
depth_strider:
minecraft:depth_strider:
item: 4
book: 2
efficiency:
minecraft:efficiency:
item: 1
book: 1
flame:
minecraft:flame:
item: 4
book: 2
feather_falling:
minecraft:feather_falling:
item: 2
book: 1
fire_aspect:
minecraft:fire_aspect:
item: 4
book: 2
fire_protection:
minecraft:fire_protection:
item: 2
book: 1
fortune:
minecraft:fortune:
item: 4
book: 2
frost_walker:
minecraft:frost_walker:
item: 4
book: 2
impaling:
minecraft:impaling:
item: 4
book: 2
infinity:
minecraft:infinity:
item: 8
book: 4
knockback:
minecraft:knockback:
item: 2
book: 1
looting:
minecraft:looting:
item: 4
book: 2
loyalty:
minecraft:loyalty:
item: 1
book: 1
luck_of_the_sea:
minecraft:luck_of_the_sea:
item: 4
book: 2
lure:
minecraft:lure:
item: 4
book: 2
mending:
minecraft:mending:
item: 4
book: 2
multishot:
minecraft:multishot:
item: 4
book: 2
piercing:
minecraft:piercing:
item: 1
book: 1
power:
minecraft:power:
item: 1
book: 1
projectile_protection:
minecraft:projectile_protection:
item: 2
book: 1
protection:
minecraft:protection:
item: 1
book: 1
punch:
minecraft:punch:
item: 4
book: 2
quick_charge:
minecraft:quick_charge:
item: 2
book: 1
respiration:
minecraft:respiration:
item: 4
book: 2
riptide:
minecraft:riptide:
item: 4
book: 2
silk_touch:
minecraft:silk_touch:
item: 8
book: 4
sharpness:
minecraft:sharpness:
item: 1
book: 1
smite:
minecraft:smite:
item: 2
book: 1
soul_speed:
minecraft:soul_speed:
item: 8
book: 4
swift_sneak:
minecraft:swift_sneak:
item: 8
book: 4
sweeping:
minecraft:sweeping:
item: 4
book: 2
sweeping_edge:
minecraft:sweeping_edge:
item: 4
book: 2
thorns:
minecraft:thorns:
item: 8
book: 4
unbreaking:
minecraft:unbreaking:
item: 2
book: 1
vanishing_curse:
minecraft:vanishing_curse:
item: 8
book: 4
density:
item: 1
book: 1
breach:
item: 4
book: 2
wind_burst:
item: 4
book: 2
# Disable enchantment merging for level above the set value
# Enchantment merging is when, for example, 2 unbreaking II book combine to give sharpness III
# But Enchantment above this value can still be applied. following the previous example, we could still apply a unbreaking III book to a sword
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
#minecraft:unbreaking: 2
# Whether to show debug logging
debug_log: false
@ -296,4 +295,3 @@ debug_log_verbose: false
force_protocolib: false
configVersion: 1.6.2
lowMinecraftVersion: '1.21'

View file

@ -17,158 +17,158 @@
# ----------------------------------------------------
restriction_aqua_affinity:
enchantments: [ aqua_affinity ]
enchantments: [ minecraft:aqua_affinity ]
notAffectedGroups: [ enchanted_book, helmets ]
restriction_bane_of_arthropods:
enchantments: [ bane_of_arthropods ]
enchantments: [ minecraft:bane_of_arthropods ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_blast_protection:
enchantments: [ blast_protection ]
enchantments: [ minecraft:blast_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_channeling:
enchantments: [ channeling ]
enchantments: [ minecraft:channeling ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_binding_curse:
enchantments: [ binding_curse ]
enchantments: [ minecraft:binding_curse ]
notAffectedGroups: [ enchanted_book, wearable ]
restriction_vanishing_curse:
enchantments: [ vanishing_curse ]
enchantments: [ minecraft:vanishing_curse ]
notAffectedGroups: [ enchanted_book, can_vanish ]
restriction_depth_strider:
enchantments: [ depth_strider ]
enchantments: [ minecraft:depth_strider ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_efficiency:
enchantments: [ efficiency ]
enchantments: [ minecraft:efficiency ]
notAffectedGroups: [ enchanted_book, tools, shears ]
restriction_feather_falling:
enchantments: [ feather_falling ]
enchantments: [ minecraft:feather_falling ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_fire_aspect:
enchantments: [ fire_aspect ]
enchantments: [ minecraft:fire_aspect ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_fire_protection:
enchantments: [ fire_protection ]
enchantments: [ minecraft:fire_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_flame:
enchantments: [ flame ]
enchantments: [ minecraft:flame ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_fortune:
enchantments: [ fortune ]
enchantments: [ minecraft:fortune ]
notAffectedGroups: [ enchanted_book, tools ]
restriction_frost_walker:
enchantments: [ frost_walker ]
enchantments: [ minecraft:frost_walker ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_impaling:
enchantments: [ impaling ]
enchantments: [ minecraft:impaling ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_infinity:
enchantments: [ infinity ]
enchantments: [ minecraft:infinity ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_knockback:
enchantments: [ knockback ]
enchantments: [ minecraft:knockback ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_looting:
enchantments: [ looting ]
enchantments: [ minecraft:looting ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_loyalty:
enchantments: [ loyalty ]
enchantments: [ minecraft:loyalty ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_lure:
enchantments: [ lure ]
enchantments: [ minecraft:lure ]
notAffectedGroups: [ enchanted_book, fishing_rod ]
restriction_mending:
enchantments: [ mending ]
enchantments: [ minecraft:mending ]
notAffectedGroups: [ enchanted_book, can_unbreak ]
restriction_multishot:
enchantments: [ multishot ]
restriction_minecraft_multishot:
enchantments: [ minecraft:multishot ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_piercing:
enchantments: [ piercing ]
enchantments: [ minecraft:piercing ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_power:
enchantments: [ power ]
enchantments: [ minecraft:power ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_projectile_protection:
enchantments: [ projectile_protection ]
enchantments: [ minecraft:projectile_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_protection:
enchantments: [ protection ]
enchantments: [ minecraft:protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_punch:
enchantments: [ punch ]
enchantments: [ minecraft:punch ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_quick_charge:
enchantments: [ quick_charge ]
enchantments: [ minecraft:quick_charge ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_respiration:
enchantments: [ respiration ]
enchantments: [ minecraft:respiration ]
notAffectedGroups: [ enchanted_book, helmets ]
restriction_riptide:
enchantments: [ riptide ]
enchantments: [ minecraft:riptide ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_sharpness:
enchantments: [ sharpness ]
enchantments: [ minecraft:sharpness ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_silk_touch:
enchantments: [ silk_touch ]
restriction__silk_touch:
enchantments: [ minecraft:silk_touch ]
notAffectedGroups: [ enchanted_book, tools ]
restriction_smite:
enchantments: [ smite ]
enchantments: [ minecraft:smite ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_soul_speed:
enchantments: [ soul_speed ]
enchantments: [ minecraft:soul_speed ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_sweeping_edge:
enchantments: [ sweeping, sweeping_edge ]
enchantments: [ minecraft:sweeping, minecraft:sweeping_edge ]
notAffectedGroups: [ enchanted_book, swords ]
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments: [ swift_sneak ]
enchantments: [ minecraft:swift_sneak ]
notAffectedGroups: [ enchanted_book, leggings ]
restriction_thorns:
enchantments: [ thorns ]
enchantments: [ minecraft:thorns ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_unbreaking:
enchantments: [ unbreaking ]
restriction__unbreaking:
enchantments: [ minecraft:unbreaking ]
notAffectedGroups: [ enchanted_book, can_unbreak ]
# ----------------------------------------------------
@ -180,60 +180,60 @@ restriction_unbreaking:
sword_enchant_conflict:
enchantments:
- bane_of_arthropods
- smite
- sharpness
- minecraft:bane_of_arthropods
- minecraft:smite
- minecraft:sharpness
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
protection_enchant_conflict:
enchantments:
- blast_protection
- fire_protection
- projectile_protection
- protection
- minecraft:blast_protection
- minecraft:fire_protection
- minecraft:projectile_protection
- minecraft:protection
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
trident_conflict1:
enchantments:
- channeling
- riptide
- minecraft:channeling
- minecraft:riptide
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
trident_conflict2:
enchantments:
- loyalty
- riptide
- minecraft:loyalty
- minecraft:riptide
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
boot_conflict:
enchantments:
- depth_strider
- frost_walker
- minecraft:depth_strider
- minecraft:frost_walker
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
tool_conflict:
enchantments:
- fortune
- silk_touch
- minecraft:fortune
- minecraft:silk_touch
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
bow_conflict:
enchantments:
- mending
- infinity
- minecraft:mending
- minecraft:infinity
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
crossbow_conflict:
enchantments:
- multishot
- piercing
- minecraft:multishot
- minecraft:piercing
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1

View file

@ -97,49 +97,46 @@ default_limit: 5
#
# Valid range of 1 - 255 for each enchantment
enchant_limits:
aqua_affinity: 1
binding_curse: 1
channeling: 1
flame: 1
infinity: 1
mending: 1
multishot: 1
silk_touch: 1
vanishing_curse: 1
depth_strider: 3 # anything more than 3 is treated as 3 by the game
protection: 4
fire_protection: 4
blast_protection: 4
projectile_protection: 4
feather_falling: 4
thorns: 3
respiration: 3
sharpness: 5
smite: 5
bane_of_arthropods: 5
knockback: 2
fire_aspect: 2
looting: 3
sweeping: 3
sweeping_edge: 3
efficiency: 5
unbreaking: 3
fortune: 3
power: 5
punch: 2
luck_of_the_sea: 3
lure: 3
frost_walker: 2
impaling: 5
riptide: 3
loyalty: 3
piercing: 4
quick_charge: 3
soul_speed: 3
swift_sneak: 3
density: 5
breach: 4
wind_burst: 3
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
minecraft:channeling: 1
minecraft:flame: 1
minecraft:infinity: 1
minecraft:mending: 1
minecraft:multishot: 1
minecraft:silk_touch: 1
minecraft:vanishing_curse: 1
minecraft:depth_strider: 3 # anything more than 3 is treated as 3 by the game
minecraft:protection: 4
minecraft:fire_protection: 4
minecraft:blast_protection: 4
minecraft:projectile_protection: 4
minecraft:feather_falling: 4
minecraft:thorns: 3
minecraft:respiration: 3
minecraft:sharpness: 5
minecraft:smite: 5
minecraft:bane_of_arthropods: 5
minecraft:knockback: 2
minecraft:fire_aspect: 2
minecraft:looting: 3
minecraft:sweeping: 3
minecraft:sweeping_edge: 3
minecraft:efficiency: 5
minecraft:unbreaking: 3
minecraft:fortune: 3
minecraft:power: 5
minecraft:punch: 2
minecraft:luck_of_the_sea: 3
minecraft:lure: 3
minecraft:frost_walker: 2
minecraft:impaling: 5
minecraft:riptide: 3
minecraft:loyalty: 3
minecraft:piercing: 4
minecraft:quick_charge: 3
minecraft:soul_speed: 3
minecraft:swift_sneak: 3
# Multipliers used to calculate the enchantment's value in repair/combining
#
@ -153,135 +150,137 @@ enchant_limits:
# With default values protection 4 would have a value of 4 when
# coming from either a book (4 * 1) or an item (4 * 1)
enchant_values:
aqua_affinity:
minecraft:aqua_affinity:
item: 4
book: 2
bane_of_arthropods:
minecraft:bane_of_arthropods:
item: 2
book: 1
binding_curse:
minecraft:binding_curse:
item: 8
book: 4
blast_protection:
minecraft:blast_protection:
item: 4
book: 2
channeling:
minecraft:channeling:
item: 8
book: 4
depth_strider:
minecraft:depth_strider:
item: 4
book: 2
efficiency:
minecraft:efficiency:
item: 1
book: 1
flame:
minecraft:flame:
item: 4
book: 2
feather_falling:
minecraft:feather_falling:
item: 2
book: 1
fire_aspect:
minecraft:fire_aspect:
item: 4
book: 2
fire_protection:
minecraft:fire_protection:
item: 2
book: 1
fortune:
minecraft:fortune:
item: 4
book: 2
frost_walker:
minecraft:frost_walker:
item: 4
book: 2
impaling:
minecraft:impaling:
item: 4
book: 2
infinity:
minecraft:infinity:
item: 8
book: 4
knockback:
minecraft:knockback:
item: 2
book: 1
looting:
minecraft:looting:
item: 4
book: 2
loyalty:
minecraft:loyalty:
item: 1
book: 1
luck_of_the_sea:
minecraft:luck_of_the_sea:
item: 4
book: 2
lure:
minecraft:lure:
item: 4
book: 2
mending:
minecraft:mending:
item: 4
book: 2
multishot:
minecraft:multishot:
item: 4
book: 2
piercing:
minecraft:piercing:
item: 1
book: 1
power:
minecraft:power:
item: 1
book: 1
projectile_protection:
minecraft:projectile_protection:
item: 2
book: 1
protection:
minecraft:protection:
item: 1
book: 1
punch:
minecraft:punch:
item: 4
book: 2
quick_charge:
minecraft:quick_charge:
item: 2
book: 1
respiration:
minecraft:respiration:
item: 4
book: 2
riptide:
minecraft:riptide:
item: 4
book: 2
silk_touch:
minecraft:silk_touch:
item: 8
book: 4
sharpness:
minecraft:sharpness:
item: 1
book: 1
smite:
minecraft:smite:
item: 2
book: 1
soul_speed:
minecraft:soul_speed:
item: 8
book: 4
swift_sneak:
minecraft:swift_sneak:
item: 8
book: 4
sweeping:
minecraft:sweeping:
item: 4
book: 2
sweeping_edge:
minecraft:sweeping_edge:
item: 4
book: 2
thorns:
minecraft:thorns:
item: 8
book: 4
unbreaking:
minecraft:unbreaking:
item: 2
book: 1
vanishing_curse:
minecraft:vanishing_curse:
item: 8
book: 4
density:
item: 1
book: 1
breach:
item: 4
book: 2
wind_burst:
item: 4
book: 2
# Disable enchantment merging for level above the set value
# Enchantment merging is when, for example, 2 unbreaking II book combine to give sharpness III
# But Enchantment above this value can still be applied. following the previous example, we could still apply a unbreaking III book to a sword
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
#minecraft:unbreaking: 2
# Whether to show debug logging
debug_log: false
@ -296,4 +295,3 @@ debug_log_verbose: false
force_protocolib: false
configVersion: 1.6.2
lowMinecraftVersion: '1.21'

View file

@ -17,278 +17,159 @@
# ----------------------------------------------------
restriction_aqua_affinity:
enchantments:
- aqua_affinity
notAffectedGroups:
- enchanted_book
- helmets
enchantments: [ minecraft:aqua_affinity ]
notAffectedGroups: [ enchanted_book, helmets ]
restriction_bane_of_arthropods:
enchantments:
- bane_of_arthropods
notAffectedGroups:
- enchanted_book
- melee_weapons
- mace
enchantments: [ minecraft:bane_of_arthropods ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_blast_protection:
enchantments:
- blast_protection
notAffectedGroups:
- enchanted_book
- armors
enchantments: [ minecraft:blast_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_channeling:
enchantments:
- channeling
notAffectedGroups:
- enchanted_book
- trident
enchantments: [ minecraft:channeling ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_binding_curse:
enchantments:
- binding_curse
notAffectedGroups:
- enchanted_book
- wearable
enchantments: [ minecraft:binding_curse ]
notAffectedGroups: [ enchanted_book, wearable ]
restriction_vanishing_curse:
enchantments:
- vanishing_curse
notAffectedGroups:
- enchanted_book
- can_vanish
enchantments: [ minecraft:vanishing_curse ]
notAffectedGroups: [ enchanted_book, can_vanish ]
restriction_depth_strider:
enchantments:
- depth_strider
notAffectedGroups:
- enchanted_book
- boots
enchantments: [ minecraft:depth_strider ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_efficiency:
enchantments:
- efficiency
notAffectedGroups:
- enchanted_book
- tools
- shears
enchantments: [ minecraft:efficiency ]
notAffectedGroups: [ enchanted_book, tools, shears ]
restriction_feather_falling:
enchantments:
- feather_falling
notAffectedGroups:
- enchanted_book
- boots
enchantments: [ minecraft:feather_falling ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_fire_aspect:
enchantments:
- fire_aspect
notAffectedGroups:
- enchanted_book
- swords
- mace
enchantments: [ minecraft:fire_aspect ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_fire_protection:
enchantments:
- fire_protection
notAffectedGroups:
- enchanted_book
- armors
enchantments: [ minecraft:fire_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_flame:
enchantments:
- flame
notAffectedGroups:
- enchanted_book
- bow
enchantments: [ minecraft:flame ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_fortune:
enchantments:
- fortune
notAffectedGroups:
- enchanted_book
- tools
enchantments: [ minecraft:fortune ]
notAffectedGroups: [ enchanted_book, tools ]
restriction_frost_walker:
enchantments:
- frost_walker
notAffectedGroups:
- enchanted_book
- boots
enchantments: [ minecraft:frost_walker ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_impaling:
enchantments:
- impaling
notAffectedGroups:
- enchanted_book
- trident
enchantments: [ minecraft:impaling ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_infinity:
enchantments:
- infinity
notAffectedGroups:
- enchanted_book
- bow
enchantments: [ minecraft:infinity ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_knockback:
enchantments:
- knockback
notAffectedGroups:
- enchanted_book
- swords
enchantments: [ minecraft:knockback ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_looting:
enchantments:
- looting
notAffectedGroups:
- enchanted_book
- swords
enchantments: [ minecraft:looting ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_loyalty:
enchantments:
- loyalty
notAffectedGroups:
- enchanted_book
- trident
enchantments: [ minecraft:loyalty ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_lure:
enchantments:
- lure
notAffectedGroups:
- enchanted_book
- fishing_rod
enchantments: [ minecraft:lure ]
notAffectedGroups: [ enchanted_book, fishing_rod ]
restriction_mending:
enchantments:
- mending
notAffectedGroups:
- enchanted_book
- can_unbreak
enchantments: [ minecraft:mending ]
notAffectedGroups: [ enchanted_book, can_unbreak ]
restriction_multishot:
enchantments:
- multishot
notAffectedGroups:
- enchanted_book
- crossbow
restriction_minecraft_multishot:
enchantments: [ minecraft:multishot ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_piercing:
enchantments:
- piercing
notAffectedGroups:
- enchanted_book
- crossbow
enchantments: [ minecraft:piercing ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_power:
enchantments:
- power
notAffectedGroups:
- enchanted_book
- bow
enchantments: [ minecraft:power ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_projectile_protection:
enchantments:
- projectile_protection
notAffectedGroups:
- enchanted_book
- armors
enchantments: [ minecraft:projectile_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_protection:
enchantments:
- protection
notAffectedGroups:
- enchanted_book
- armors
enchantments: [ minecraft:protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_punch:
enchantments:
- punch
notAffectedGroups:
- enchanted_book
- bow
enchantments: [ minecraft:punch ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_quick_charge:
enchantments:
- quick_charge
notAffectedGroups:
- enchanted_book
- crossbow
enchantments: [ minecraft:quick_charge ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_respiration:
enchantments:
- respiration
notAffectedGroups:
- enchanted_book
- helmets
enchantments: [ minecraft:respiration ]
notAffectedGroups: [ enchanted_book, helmets ]
restriction_riptide:
enchantments:
- riptide
notAffectedGroups:
- enchanted_book
- trident
enchantments: [ minecraft:riptide ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_sharpness:
enchantments:
- sharpness
notAffectedGroups:
- enchanted_book
- melee_weapons
enchantments: [ minecraft:sharpness ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_silk_touch:
enchantments:
- silk_touch
notAffectedGroups:
- enchanted_book
- tools
restriction__silk_touch:
enchantments: [ minecraft:silk_touch ]
notAffectedGroups: [ enchanted_book, tools ]
restriction_smite:
enchantments:
- smite
notAffectedGroups:
- enchanted_book
- melee_weapons
- mace
enchantments: [ minecraft:smite ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_soul_speed:
enchantments:
- soul_speed
notAffectedGroups:
- enchanted_book
- boots
enchantments: [ minecraft:soul_speed ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_sweeping_edge:
enchantments:
- sweeping
- sweeping_edge
notAffectedGroups:
- enchanted_book
- swords
enchantments: [ minecraft:sweeping, minecraft:sweeping_edge ]
notAffectedGroups: [ enchanted_book, swords ]
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments:
- swift_sneak
notAffectedGroups:
- enchanted_book
- leggings
enchantments: [ minecraft:swift_sneak ]
notAffectedGroups: [ enchanted_book, leggings ]
restriction_thorns:
enchantments:
- thorns
notAffectedGroups:
- enchanted_book
- armors
enchantments: [ minecraft:thorns ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_unbreaking:
enchantments:
- unbreaking
notAffectedGroups:
- enchanted_book
- can_unbreak
restriction__unbreaking:
enchantments: [ minecraft:unbreaking ]
notAffectedGroups: [ enchanted_book, can_unbreak ]
# ----------------------------------------------------
# Now we have conflicts about enchantment Incompatibility
@ -299,86 +180,61 @@ restriction_unbreaking:
sword_enchant_conflict:
enchantments:
- bane_of_arthropods
- smite
- sharpness
notAffectedGroups: []
- minecraft:bane_of_arthropods
- minecraft:smite
- minecraft:sharpness
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
protection_enchant_conflict:
enchantments:
- blast_protection
- fire_protection
- projectile_protection
- protection
notAffectedGroups: []
- minecraft:blast_protection
- minecraft:fire_protection
- minecraft:projectile_protection
- minecraft:protection
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
trident_conflict1:
enchantments:
- channeling
- riptide
notAffectedGroups: []
- minecraft:channeling
- minecraft:riptide
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
trident_conflict2:
enchantments:
- loyalty
- riptide
notAffectedGroups: []
- minecraft:loyalty
- minecraft:riptide
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
boot_conflict:
enchantments:
- depth_strider
- frost_walker
notAffectedGroups: []
- minecraft:depth_strider
- minecraft:frost_walker
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
tool_conflict:
enchantments:
- fortune
- silk_touch
notAffectedGroups: []
- minecraft:fortune
- minecraft:silk_touch
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
bow_conflict:
enchantments:
- mending
- infinity
notAffectedGroups: []
- minecraft:mending
- minecraft:infinity
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
crossbow_conflict:
enchantments:
- multishot
- piercing
notAffectedGroups: []
maxEnchantmentBeforeConflict: 1
restriction_density:
enchantments:
- density
notAffectedGroups:
- mace
- enchanted_book
restriction_breach:
enchantments:
- breach
notAffectedGroups:
- mace
- enchanted_book
restriction_wind_burst:
enchantments:
- wind_burst
notAffectedGroups:
- mace
- enchanted_book
mace_enchant_conflict:
enchantments:
- density
- breach
- smite
- bane_of_arthropods
- minecraft:multishot
- minecraft:piercing
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
# ----------------------------------------------------

View file

@ -121,6 +121,7 @@ wearable:
- player_head
- creeper_head
- dragon_head
# do not exist in 1.18 but exist in future update
- piglin_head
groups:
- armors
@ -187,6 +188,7 @@ can_unbreak:
- shield
- carrot_on_a_stick
- warped_fungus_on_a_stick
# do not exist in 1.18 but exist in future update
- brush
groups:
- melee_weapons
@ -197,7 +199,6 @@ can_unbreak:
- crossbow
- fishing_rod
- shears
- mace
can_vanish:
type: include
@ -206,8 +207,4 @@ can_vanish:
groups:
- wearable
- can_unbreak
mace:
type: include
items:
- mace

View file

@ -188,5 +188,3 @@ warped_planks:
wooden_shovel: 0.25
wooden_hoe: 0.25
shield: 0.25
breeze_rod:
mace: 0.25

Binary file not shown.

BIN
libs/nightcore-2.6.4.jar Normal file

Binary file not shown.

View file

@ -0,0 +1,24 @@
package xyz.alexcrea.cuanvil.dependency.gui
import org.bukkit.inventory.InventoryView
interface ExternGuiTester {
val wesjdAnvilGuiName: String?
fun getContainerClass(inventory: InventoryView): Class<Any>?
fun testIfGui(inventory: InventoryView): Boolean {
val clazz = getContainerClass(inventory)
if(clazz == null) return false
val expectedWesjdGuiPath = "anvilgui.version.$wesjdAnvilGuiName"
val clazzName = clazz.name
val isWejdsGui = clazzName.contains(expectedWesjdGuiPath)
return isWejdsGui
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_17R1_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_17_R1"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_18R1_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_18_R1"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_18R2_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_18_R2"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_19R1_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_19_R1"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_19R2_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_19_R2"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_19R3_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_19_R3"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_20R1_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_20_R1"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,17 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
import kotlin.jvm.javaClass
class v1_20R2_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_20_R2"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,17 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
import kotlin.jvm.javaClass
class v1_20R3_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_20_R3"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,17 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
import kotlin.jvm.javaClass
class v1_20R4_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_20_R4"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if (view !is CraftInventoryView) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui.version
import org.bukkit.craftbukkit.inventory.CraftInventoryView
import org.bukkit.inventory.InventoryView
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
class v1_21R1_ExternGuiTester: ExternGuiTester {
override val wesjdAnvilGuiName = "Wrapper1_21_R1"
override fun getContainerClass(view: InventoryView): Class<Any>? {
if(view !is CraftInventoryView<*>) return null
val container = view.handle
return container.javaClass
}
}

View file

@ -119,7 +119,7 @@ public class ConflictAPI {
private static List<String> extractEnchantments(@NotNull ConflictBuilder builder){
List<String> result = new ArrayList<>(builder.getEnchantmentNames());
for (NamespacedKey enchantmentKey : builder.getEnchantmentKeys()) {
result.add(enchantmentKey.getKey());
result.add(enchantmentKey.toString());
}
return result;

View file

@ -10,6 +10,7 @@ import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.group.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@ -372,7 +373,7 @@ public class ConflictBuilder {
*/
protected void appendEnchantments(@NotNull EnchantConflictGroup conflict){
for (String enchantmentName : getEnchantmentNames()){
if(appendEnchantment(conflict, EnchantmentApi.getByName(enchantmentName))){
if(appendEnchantments(conflict, EnchantmentApi.getListByName(enchantmentName)) == 0){
CustomAnvil.instance.getLogger().warning("Could not find enchantment " + enchantmentName + " for conflict " + getName());
ConflictAPI.logConflictOrigin(this);
}
@ -399,6 +400,24 @@ public class ConflictBuilder {
return true;
}
/**
* Append a list of enchantments.
*
* @param conflict The conflict target
* @param enchantments List of enchantment to add
* @return Number of enchantment added
*/
protected static int appendEnchantments(@NotNull EnchantConflictGroup conflict, @NotNull List<CAEnchantment> enchantments){
int numberValid = 0;
for (CAEnchantment enchantment : enchantments) {
if(appendEnchantment(conflict, enchantment)){
numberValid++;
}
}
return numberValid;
}
/**
* Extract group abstract material group.
*

View file

@ -18,6 +18,7 @@ import xyz.alexcrea.cuanvil.gui.config.global.EnchantCostConfigGui;
import xyz.alexcrea.cuanvil.gui.config.global.EnchantLimitConfigGui;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
@ -104,7 +105,7 @@ public class EnchantmentApi {
* @return True if successful.
*/
public static boolean unregisterEnchantment(@NotNull NamespacedKey key){
CAEnchantment enchantment = CAEnchantmentRegistry.getInstance().getByKey(key);
CAEnchantment enchantment = CAEnchantment.getByKey(key);
return unregisterEnchantment(enchantment);
}
@ -126,7 +127,7 @@ public class EnchantmentApi {
*/
@Nullable
public static CAEnchantment getByKey(@NotNull NamespacedKey key){
return CAEnchantmentRegistry.getInstance().getByKey(key);
return CAEnchantment.getByKey(key);
}
/**
@ -134,10 +135,22 @@ public class EnchantmentApi {
*
* @param name The name used to fetch
* @return The custom anvil enchantment of this name. null if not found.
* @deprecated use {@link #getListByName(String)}
*/
@Deprecated(since = "1.6.3")
@Nullable
public static CAEnchantment getByName(@NotNull String name){
return CAEnchantmentRegistry.getInstance().getByName(name);
return CAEnchantment.getByName(name);
}
/**
* Get list of enchantment using the provided name.
*
* @param name The name used to fetch
* @return List of custom anvil enchantments of this name. May be empty if not found.
*/
public static List<CAEnchantment> getListByName(@NotNull String name){
return CAEnchantment.getListByName(name);
}
/**
@ -167,9 +180,9 @@ public class EnchantmentApi {
private static void writeDefaultConfig(FileConfiguration defaultConfig, CAEnchantment enchantment) {
defaultConfig.set("enchant_limits." + enchantment.getKey().getKey(), enchantment.defaultMaxLevel());
defaultConfig.set("enchant_limits." + enchantment.getKey(), enchantment.defaultMaxLevel());
String basePath = "enchant_values." + enchantment.getKey().getKey();
String basePath = "enchant_values." + enchantment.getKey();
EnchantmentRarity rarity = enchantment.defaultRarity();
defaultConfig.set(basePath + ".item", rarity.getItemValue());

View file

@ -12,6 +12,7 @@ import xyz.alexcrea.cuanvil.group.EnchantConflictGroup;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -226,12 +227,24 @@ public interface CAEnchantment {
}
/**
* Gets a list of all the unoptimised enchantments.
* @param name The enchantment name
* @return List of enchantment.
* Gets the enchantment by the provided name.
* @param name Name to fetch.
* @return Registered enchantment. null if absent.
*
* @deprecated use {@link #getListByName(String)}
*/
@Deprecated(since = "1.6.3")
static @Nullable CAEnchantment getByName(@NotNull String name){
return CAEnchantmentRegistry.getInstance().getByName(name);
}
/**
* Gets list of enchantment using the provided name.
* @param name Name to fetch.
* @return List of registered enchantment.
*/
static List<CAEnchantment> getListByName(@NotNull String name){
return CAEnchantmentRegistry.getInstance().getListByName(name);
}
}

View file

@ -22,7 +22,7 @@ public class CAEnchantmentRegistry {
// Register enchantment functions
private final HashMap<NamespacedKey, CAEnchantment> byKeyMap;
private final HashMap<String, CAEnchantment> byNameMap;
private final HashMap<String, List<CAEnchantment>> byNameMap;
private final SortedSet<CAEnchantment> nameSortedEnchantments;
@ -62,6 +62,8 @@ public class CAEnchantmentRegistry {
}
private static boolean hasWarnedRegistering = false;
/**
* Can be used to register new enchantment.
* <p>
@ -73,19 +75,25 @@ public class CAEnchantmentRegistry {
public boolean register(@NotNull CAEnchantment enchantment){
if(byKeyMap.containsKey(enchantment.getKey())){
CustomAnvil.instance.getLogger().log(Level.WARNING,
"Duplicate registered enchantment. This should NOT happen.",
"Duplicate registered enchantment. This should NOT happen any time.\n" +
"If you are a custom anvil developer. You maybe custom anvil detected your enchantment as a bukkit enchantment. " +
"maybe remove enchantment with the same key before registering yours",
new IllegalStateException(enchantment.getKey()+" enchantment was already registered"));
return false;
}
if(byNameMap.containsKey(enchantment.getName())){
if((!hasWarnedRegistering) && byNameMap.containsKey(enchantment.getName())){
hasWarnedRegistering = true;
CustomAnvil.instance.getLogger().log(Level.WARNING,
"Duplicate registered enchantment name. There will have issue. " +
"\nI hope this do not happen to you on a production server. If it do, there is probably a plugin trying to register an enchantment with the same name than another one",
new IllegalStateException(enchantment.getKey()+" enchantment name was already registered"));
"Duplicate registered enchantment name. Please check that configuration is using namespace.");
}
byKeyMap.put(enchantment.getKey(), enchantment);
byNameMap.put(enchantment.getName(), enchantment);
byNameMap.putIfAbsent(enchantment.getName(), new ArrayList<>());
byNameMap.get(enchantment.getName()).add(enchantment);
nameSortedEnchantments.add(enchantment);
if(!enchantment.isGetOptimised()){
@ -112,7 +120,7 @@ public class CAEnchantmentRegistry {
public boolean unregister(@Nullable CAEnchantment enchantment){
if(enchantment == null) return false;
byKeyMap.remove(enchantment.getKey());
byNameMap.remove(enchantment.getName());
byNameMap.get(enchantment.getName()).remove(enchantment);
nameSortedEnchantments.remove(enchantment);
@ -135,10 +143,26 @@ public class CAEnchantmentRegistry {
* Gets the enchantment by the provided name.
* @param name Name to fetch.
* @return Registered enchantment. null if absent.
*
* @deprecated use {@link #getListByName(String)}
*/
@Deprecated(since = "1.6.3")
@Nullable
public CAEnchantment getByName(@NotNull String name){
return byNameMap.get(name);
List<CAEnchantment> enchantments = getListByName(name);
if(enchantments.isEmpty()) return null;
return enchantments.get(0);
}
/**
* Gets list of enchantment using the provided name.
* @param name Name to fetch.
* @return List of registered enchantment.
*/
@NotNull
public List<CAEnchantment> getListByName(@NotNull String name){
return byNameMap.getOrDefault(name, Collections.emptyList());
}
/**

View file

@ -0,0 +1,46 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
import su.nightexpress.excellentenchants.api.enchantment.Definition;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import java.util.Map;
import java.util.Set;
public class CAEEEnchantment extends CABukkitEnchantment implements AdditionalTestEnchantment {
@NotNull CustomEnchantment eeenchantment;
@NotNull Definition definition;
public CAEEEnchantment(@NotNull CustomEnchantment enchantment) {
super(enchantment.getBukkitEnchantment(), EnchantmentRarity.getRarity(enchantment.getDefinition().getAnvilCost()));
this.eeenchantment = enchantment;
this.definition = enchantment.getDefinition();
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
if(!definition.hasConflicts()) return false;
Set<String> conflicts = definition.getConflicts();
for (CAEnchantment caEnchantment : enchantments.keySet()) {
if(conflicts.contains(caEnchantment.getName())) return true;
}
return false;
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
if(Material.ENCHANTED_BOOK.equals(itemMat)) return false;
return !definition.getSupportedItems().is(item);
}
}

View file

@ -30,7 +30,7 @@ public class MainConfigGui extends ChestGui {
public void init(PacketManager packetManager) {
Pattern pattern = new Pattern(
GuiSharedConstant.EMPTY_GUI_FULL_LINE,
"012304567",
"012345678",
"Q00000000"
);
PatternPane pane = new PatternPane(0, 0, 9, 3, pattern);
@ -62,6 +62,18 @@ public class MainConfigGui extends ChestGui {
GuiItem enchantLimitItem = GuiGlobalItems.goToGuiItem(enchantLimitItemstack, new EnchantLimitConfigGui());
pane.bindItem('2', enchantLimitItem);
// enchant level limit item
ItemStack enchantMergeLimitItemstack = new ItemStack(Material.ENCHANTED_BOOK);
ItemMeta enchantMergeLimitMeta = enchantMergeLimitItemstack.getItemMeta();
assert enchantMergeLimitMeta != null;
enchantMergeLimitMeta.setDisplayName("§aEnchantment Merge Limit");
enchantMergeLimitMeta.setLore(Collections.singletonList("§7Click here to open enchantment merge limit menu"));
enchantMergeLimitItemstack.setItemMeta(enchantMergeLimitMeta);
GuiItem enchantMergeLimitItem = GuiGlobalItems.goToGuiItem(enchantMergeLimitItemstack, new EnchantMergeLimitConfigGui());
pane.bindItem('3', enchantMergeLimitItem);
// enchant cost item
ItemStack enchantCostItemstack = new ItemStack(Material.EXPERIENCE_BOTTLE);
ItemMeta enchantCostMeta = enchantCostItemstack.getItemMeta();
@ -72,7 +84,7 @@ public class MainConfigGui extends ChestGui {
enchantCostItemstack.setItemMeta(enchantCostMeta);
GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, new EnchantCostConfigGui());
pane.bindItem('3', enchantCostItem);
pane.bindItem('4', enchantCostItem);
// Enchantment Conflicts item
ItemStack enchantConflictItemstack = new ItemStack(Material.OAK_FENCE);
@ -84,7 +96,7 @@ public class MainConfigGui extends ChestGui {
enchantConflictItemstack.setItemMeta(enchantConflictMeta);
GuiItem enchantConflictItem = GuiGlobalItems.goToGuiItem(enchantConflictItemstack, EnchantConflictGui.getInstance());
pane.bindItem('4', enchantConflictItem);
pane.bindItem('5', enchantConflictItem);
// Group config items
ItemStack groupItemstack = new ItemStack(Material.CHEST);
@ -97,7 +109,7 @@ public class MainConfigGui extends ChestGui {
GuiItem groupConfigItem = GuiGlobalItems.goToGuiItem(groupItemstack, GroupConfigGui.getInstance());
pane.bindItem('5', groupConfigItem);
pane.bindItem('6', groupConfigItem);
// Unit repair item
ItemStack unirRepairItemstack = new ItemStack(Material.DIAMOND);
@ -109,7 +121,7 @@ public class MainConfigGui extends ChestGui {
unirRepairItemstack.setItemMeta(unitRepairMeta);
GuiItem unitRepairItem = GuiGlobalItems.goToGuiItem(unirRepairItemstack, UnitRepairConfigGui.getInstance());
pane.bindItem('6', unitRepairItem);
pane.bindItem('7', unitRepairItem);
// Custom recipe item
ItemStack customRecipeItemstack = new ItemStack(Material.CRAFTING_TABLE);
@ -121,7 +133,7 @@ public class MainConfigGui extends ChestGui {
customRecipeItemstack.setItemMeta(customRecipeMeta);
GuiItem customRecipeItem = GuiGlobalItems.goToGuiItem(customRecipeItemstack, CustomRecipeConfigGui.getInstance());
pane.bindItem('7', customRecipeItem);
pane.bindItem('8', customRecipeItem);
// quit item
ItemStack quitItemstack = new ItemStack(Material.BARRIER);

View file

@ -44,24 +44,16 @@ public class EnchantCostConfigGui extends AbstractEnchantConfigGui<EnchantCostSe
@Override
public EnchantCostSettingsGui.EnchantCostSettingFactory createFactory(CAEnchantment enchant) {
String key = enchant.getKey().getKey().toLowerCase(Locale.ENGLISH);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key);
String key = enchant.getKey().toString().toLowerCase(Locale.ENGLISH);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key.replace(":", "_"));
// try to find rarity. default to 0 if not found
EnchantmentRarity rarity = enchant.defaultRarity();
try {
rarity = EnchantmentProperties.valueOf(key.toUpperCase(Locale.ENGLISH)).getRarity();
} catch (IllegalArgumentException ignored) {
}
return EnchantCostSettingsGui.enchantCostFactory(prettyKey + " Level Cost", this,
ConfigHolder.DEFAULT_CONFIG, SECTION_NAME + '.' + key,
return new EnchantCostSettingsGui.EnchantCostSettingFactory(prettyKey + " Cost", this,
SECTION_NAME + '.' + key, ConfigHolder.DEFAULT_CONFIG,
Arrays.asList(
"§7How many level should " + prettyKey,
"§7cost when applied by book or by another item."
),
0, 255,
rarity.getItemValue(), rarity.getBookValue(),
enchant, 0, 255,
1, 10, 50);
}

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.Material;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
@ -37,17 +38,23 @@ public class EnchantLimitConfigGui extends AbstractEnchantConfigGui<IntSettingsG
@Override
public IntSettingsGui.IntSettingFactory createFactory(CAEnchantment enchant) {
String key = enchant.getKey().getKey().toLowerCase(Locale.ROOT);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key);
String key = enchant.getKey().toString().toLowerCase(Locale.ROOT);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key.replace(":", "_"));
return new IntSettingsGui.IntSettingFactory(prettyKey + " Level Limit", this,
return new IntSettingsGui.IntSettingFactory(prettyKey + " Limit", this,
SECTION_NAME + '.' + key, ConfigHolder.DEFAULT_CONFIG,
Collections.singletonList(
"§7Maximum applied level of " + prettyKey
),
0, 255,
enchant.defaultMaxLevel(),
1, 5, 10, 50, 100);
1, 5, 10, 50, 100){
@Override
public int getConfiguredValue() {
return ConfigOptions.INSTANCE.enchantLimit(enchant);
}
};
}
@Override

View file

@ -0,0 +1,67 @@
package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.Material;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.Arrays;
import java.util.Locale;
public class EnchantMergeLimitConfigGui extends AbstractEnchantConfigGui<IntSettingsGui.IntSettingFactory> {
private static final String SECTION_NAME = "disable-merge-over";
private static EnchantMergeLimitConfigGui INSTANCE = null;
@Nullable
public static EnchantMergeLimitConfigGui getInstance() {
return INSTANCE;
}
/**
* Constructor of this Global gui for enchantment level limit settings.
*/
public EnchantMergeLimitConfigGui() {
super("§8Enchantment Maximum Merge Level");
if(INSTANCE == null) INSTANCE = this;
init();
}
@Override
public IntSettingsGui.IntSettingFactory createFactory(CAEnchantment enchant) {
String key = enchant.getKey().toString().toLowerCase(Locale.ROOT);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key.replace(":", "_"));
return new IntSettingsGui.IntSettingFactory(prettyKey + " Merge Limit", this,
SECTION_NAME + '.' + key, ConfigHolder.DEFAULT_CONFIG,
Arrays.asList(
"§7Maximum merge level for for " + prettyKey,
"",
"§7For example, if set to §e2§7, §alvl1 §7+ §alvl1 §7of will give a §alvl2",
"§7But §alvl2 §7+ §alvl2 §7will not give a §clv3§7.",
"§7Will still not merge above max enchantment level",
"§e-1 §7(default) will set the merge limit to enchantment's maximum level"
),
-1, 255, -1,
1, 5, 10, 50, 100){
@Override
public int getConfiguredValue() {
return ConfigOptions.INSTANCE.maxBeforeMergeDisabled(enchant);
}
};
}
@Override
public GuiItem itemFromFactory(CAEnchantment enchantment, IntSettingsGui.IntSettingFactory inventoryFactory) {
return inventoryFactory.getItem(
Material.ENCHANTED_BOOK,
inventoryFactory.getTitle());
}
}

View file

@ -258,7 +258,7 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
String[] enchantKeys = new String[enchantments.size()];
int index = 0;
for (CAEnchantment enchantment : enchantments) {
enchantKeys[index++] = enchantment.getKey().getKey();
enchantKeys[index++] = enchantment.getKey().toString();
}
ConfigHolder.CONFLICT_HOLDER.getConfig().set(enchantConflict + ".enchantments", enchantKeys);

View file

@ -5,6 +5,7 @@ import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.pane.PatternPane;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import io.delilaheve.CustomAnvil;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.Material;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
@ -13,6 +14,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
@ -237,43 +239,13 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
return super.hadChange() || nowBook != beforeBook;
}
/**
* Create an int setting factory from setting's parameters.
*
* @param title The title of the gui.
* @param parent Parent gui to go back when completed.
* @param config Configuration holder of this setting.
* @param configPath Configuration path of this setting.
* @param displayLore Gui display item lore.
* @param min Minimum value of this setting.
* @param max Maximum value of this setting.
* @param defaultItemVal Default item value if not found on the config.
* @param defaultBookVal Default book value if not found on the config.
* @param steps List of step the value can increment/decrement.
* List's size should be between 1 (included) and 3 (included).
* it is visually preferable to have an odd number of step.
* If step only contain 1 value, no step item should be displayed.
* @return A factory for an enchant cost setting gui.
*/
public static EnchantCostSettingFactory enchantCostFactory(
@NotNull String title, @NotNull ValueUpdatableGui parent,
@NotNull ConfigHolder config, @NotNull String configPath,
@Nullable List<String> displayLore,
int min, int max, int defaultItemVal, int defaultBookVal,
int... steps) {
return new EnchantCostSettingFactory(
title, parent,
configPath, config,
displayLore,
min, max, defaultItemVal, defaultBookVal, steps);
}
/**
* A factory for an enchantment cost setting gui that hold setting's information.
*/
public static class EnchantCostSettingFactory extends IntSettingsGui.IntSettingFactory {
int defaultBookVal;
@NotNull CAEnchantment enchantment;
/**
* Constructor for an enchantment cost setting gui factory.
@ -285,25 +257,27 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
* @param displayLore Gui display item lore.
* @param min Minimum value of this setting.
* @param max Maximum value of this setting.
* @param defaultItemVal Default item value if not found on the config.
* @param defaultBookVal Default book value if not found on the config.
* @param enchantment Enchantment to change the cost to
* @param steps List of step the value can increment/decrement.
* List's size should be between 1 (included) and 3 (included).
* it is visually preferable to have an odd number of step.
* If step only contain 1 value, no step item should be displayed.
*/
protected EnchantCostSettingFactory(
public EnchantCostSettingFactory(
@NotNull String title, ValueUpdatableGui parent,
@NotNull String configPath, @NotNull ConfigHolder config,
@Nullable List<String> displayLore,
int min, int max, int defaultItemVal, int defaultBookVal,
int... steps) {
@NotNull CAEnchantment enchantment,
int min, int max, int... steps) {
super(title, parent,
configPath, config,
displayLore,
min, max, defaultItemVal, steps);
this.defaultBookVal = defaultBookVal;
min, max, enchantment.defaultRarity().getItemValue(),
steps);
this.defaultBookVal = enchantment.defaultRarity().getBookValue();
this.enchantment = enchantment;
}
/**
@ -311,14 +285,14 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
*/
@Override
public int getConfiguredValue() {
return this.config.getConfig().getInt(this.configPath + ITEM_PATH, this.defaultVal);
return ConfigOptions.INSTANCE.enchantmentValue(enchantment, false);
}
/**
* @return The configured value for the enchant setting book value.
*/
public int getConfiguredBookValue() {
return this.config.getConfig().getInt(this.configPath + BOOK_PATH, this.defaultBookVal);
return ConfigOptions.INSTANCE.enchantmentValue(enchantment, true);
}
@Override

View file

@ -45,13 +45,13 @@ public class Update_1_21 {
addToStringList(groupConfig, "can_unbreak.groups", "mace");
// Add new enchant conflicts
addToStringList(conflictConfig, "restriction_density.enchantments", "density");
addToStringList(conflictConfig, "restriction_density.enchantments", "minecraft:density");
addToStringList(conflictConfig, "restriction_density.notAffectedGroups", "mace", "enchanted_book");
addToStringList(conflictConfig, "restriction_breach.enchantments", "breach");
addToStringList(conflictConfig, "restriction_breach.enchantments", "minecraft:breach");
addToStringList(conflictConfig, "restriction_breach.notAffectedGroups", "mace", "enchanted_book");
addToStringList(conflictConfig, "restriction_wind_burst.enchantments", "wind_burst");
addToStringList(conflictConfig, "restriction_wind_burst.enchantments", "minecraft:wind_burst");
addToStringList(conflictConfig, "restriction_wind_burst.notAffectedGroups", "mace", "enchanted_book");
// Add mace to conflicts
@ -59,13 +59,14 @@ public class Update_1_21 {
addToStringList(conflictConfig, "restriction_smite.notAffectedGroups", "mace");
addToStringList(conflictConfig, "restriction_bane_of_arthropods.notAffectedGroups", "mace");
addToStringList(conflictConfig, "mace_enchant_conflict.enchantments", "density", "breach", "smite", "bane_of_arthropods");
addToStringList(conflictConfig, "mace_enchant_conflict.enchantments",
"minecraft:density", "minecraft:breach", "minecraft:smite", "minecraft:bane_of_arthropods");
conflictConfig.set("mace_enchant_conflict.maxEnchantmentBeforeConflict", 1);
// Add level limit
baseConfig.set("enchant_limits.density", 5);
baseConfig.set("enchant_limits.breach", 4);
baseConfig.set("enchant_limits.wind_burst", 3);
baseConfig.set("enchant_limits.minecraft:density", 5);
baseConfig.set("enchant_limits.minecraft:breach", 4);
baseConfig.set("enchant_limits.minecraft:wind_burst", 3);
// Add enchant values
baseConfig.set("enchant_values.density.item", 1);

View file

@ -1,580 +0,0 @@
package io.delilaheve
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.ItemUtil.canMergeWith
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook
import io.delilaheve.util.ItemUtil.repairFrom
import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.ChatColor
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.Event
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority.HIGHEST
import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView.Property.REPAIR_COST
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.calculatePenalty
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.getRightValues
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.setAnvilInvXp
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import java.util.regex.Matcher
import java.util.regex.Pattern
import kotlin.math.min
/**
* Listener for anvil events
*/
class AnvilEventListener(private val packetManager: PacketManager) : Listener {
companion object {
// Anvil's output slot
const val ANVIL_INPUT_LEFT = 0
const val ANVIL_INPUT_RIGHT = 1
const val ANVIL_OUTPUT_SLOT = 2
// static slot container
private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0)
private val CURSOR_SLOT = SlotContainer(SlotType.CURSOR, 0)
}
/**
* Event handler logic for when an anvil contains items to be combined
*/
@EventHandler(priority = HIGHEST)
fun anvilCombineCheck(event: PrepareAnvilEvent) {
// Test if the event should bypass custom anvil.
if(DependencyManager.tryEventPreAnvilBypass(event)) return
val inventory = event.inventory
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val second = inventory.getItem(ANVIL_INPUT_RIGHT)
// Should find player
val player = event.view.player
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
// Test custom recipe
val recipe = getCustomRecipe(first, second)
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
if(recipe != null){
val amount = getCustomRecipeAmount(recipe, first, second)
val resultItem: ItemStack = recipe.resultItem!!.clone()
resultItem.amount *= amount
event.result = resultItem
setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true)
return
}
// Test rename lonely item
if (second == null) {
val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("no right item, But input is same as output")
event.result = null
return
}
event.result = resultItem
anvilCost += calculatePenalty(first, null, resultItem)
setAnvilInvXp(inventory, event.view, anvilCost)
return
}
// Test for merge
if (first.canMergeWith(second)) {
val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first, player)
val resultItem = first.clone()
resultItem.setEnchantmentsUnsafe(newEnchants)
// Calculate enchantment cost
var anvilCost = getRightValues(second, resultItem)
// Calculate repair cost
if (!first.isEnchantedBook() && !second.isEnchantedBook()) {
// we only need to be concerned with repair when neither item is a book
val repaired = resultItem.repairFrom(first, second)
anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0
}
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("Mergable with second, But input is same as output")
event.result = null
return
}
// As calculatePenalty edit result, we need to calculate penalty after checking equality
anvilCost += calculatePenalty(first, second, resultItem)
// Calculate rename cost
anvilCost += handleRename(resultItem, inventory, player)
// Finally, we set result
event.result = resultItem
setAnvilInvXp(inventory, event.view, anvilCost)
return
}
// Test for unit repair
val unitRepairAmount = first.getRepair(second)
if (unitRepairAmount != null) {
val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player)
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
if (repairAmount > 0) {
anvilCost += repairAmount * ConfigOptions.unitRepairCost
}
// We do not care about right item penalty for unit repair
anvilCost += calculatePenalty(first, null, resultItem, true)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("unit repair, But input is same as output")
event.result = null
return
}
event.result = resultItem
setAnvilInvXp(inventory, event.view, anvilCost)
} else {
CustomAnvil.log("no anvil fuse type found")
event.result = null
}
}
private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int {
// Rename item and add renaming cost
resultItem.itemMeta?.let {
val displayName = ChatColor.stripColor(it.displayName)
var inventoryName = ChatColor.stripColor(inventory.renameText)
var sumCost = 0
var useColor = false
if(ConfigOptions.renameColorPossible){
val resultString = StringBuilder(inventoryName)
useColor = handleRenamingColor(resultString, player)
if(useColor) {
inventoryName = resultString.toString()
sumCost+= ConfigOptions.useOfColorCost
}
}
if ((!useColor && (!displayName.contentEquals(inventoryName))) || (useColor && !(it.displayName).contentEquals(inventoryName))) {
it.setDisplayName(inventoryName)
resultItem.itemMeta = it
sumCost+= ConfigOptions.itemRenameCost
}
return sumCost
}
return 0
}
private fun handleRenamingColor(textToColor: StringBuilder, player: HumanEntity): Boolean {
val usePermission = ConfigOptions.permissionNeededForColor
val canUseColorCode = ConfigOptions.allowColorCode && (!usePermission || player.hasPermission("ca.color.code"))
val canUseHexColor = ConfigOptions.allowHexadecimalColor && (!usePermission || player.hasPermission("ca.color.hex"))
if((!canUseColorCode) && (!canUseHexColor)) return false
var useColor = false
// Handle color code
if(canUseColorCode){
var nbReplacement = replaceAll(textToColor, "&", "§", 2)
nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2)
if(nbReplacement > 0) useColor = true
}
if(canUseHexColor){
val nbReplacement = replaceHexToColor(textToColor, 7)
if(nbReplacement > 0) useColor = true
}
return useColor
}
/**
* Replace every instance of "from" to "to".
* @param builder The builder to replace the string from.
* @param from The source that should be replaced.
* @param to The string that should replace.
* @param endOffset Amount of character that should be ignored at the end.
* @return The number of replacement was that was done.
*/
private fun replaceAll(builder: java.lang.StringBuilder, from: String, to: String, endOffset: Int): Int {
var index = builder.indexOf(from)
var numberOfChanges = 0
while (index != -1 && index < builder.length - endOffset) {
builder.replace(index, index + from.length, to)
index += to.length
index = builder.indexOf(from, index)
numberOfChanges+=1
}
return numberOfChanges
}
val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string
/**
* Replace every hex color formatted like #000000 to the minecraft format
* @param builder The builder to replace the hex color from.
* @param endOffset Amount of character that should be ignored at the end.
* @return The number of replacement was that was done.
*/
private fun replaceHexToColor(builder: StringBuilder, endOffset: Int): Int {
val matcher: Matcher = HEX_PATTERN.matcher(builder)
var numberOfChanges = 0
var startIndex = 0
while(matcher.find(startIndex)){
startIndex = matcher.start()
if(startIndex >= builder.length - endOffset) break
builder.replace(startIndex, startIndex + 1, "§x")
startIndex+=2
for (i in 0..5) {
builder.insert(startIndex, '§')
startIndex+=2
}
numberOfChanges+=1
}
return numberOfChanges
}
/**
* Event handler logic for when a player is trying to pull an item out of the anvil
*/
@EventHandler(ignoreCancelled = true)
fun anvilExtractionCheck(event: InventoryClickEvent) {
val player = event.whoClicked as? Player ?: return
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
val inventory = event.inventory as? AnvilInventory ?: return
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
return
}
// Test if the event should bypass custom anvil.
if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
// Test custom recipe
val recipe = getCustomRecipe(leftItem, rightItem)
if(recipe != null){
event.result = Event.Result.ALLOW
onCustomCraft(
event, recipe, player,
leftItem, rightItem, output, inventory)
return
}
val canMerge = leftItem.canMergeWith(rightItem)
val unitRepairResult = leftItem.getRepair(rightItem)
val allowed = (rightItem == null)
|| (canMerge)
|| (unitRepairResult != null)
// True if there was no change or not allowed
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))
|| !allowed
) {
event.result = Event.Result.DENY
return
}
if (rightItem == null) {
event.result = Event.Result.ALLOW
return
}
if (canMerge) {
event.result = Event.Result.ALLOW
} else if (unitRepairResult != null) {
onUnitRepairExtract(
leftItem, rightItem, output,
unitRepairResult, event, player, inventory
)
return
}
}
private fun onCustomCraft(event: InventoryClickEvent,
recipe: AnvilCustomRecipe,
player: Player,
leftItem: ItemStack,
rightItem: ItemStack?,
output: ItemStack,
inventory: AnvilInventory) {
event.result = Event.Result.DENY
if(recipe.leftItem == null) return // in case it changed
val amount = getCustomRecipeAmount(recipe, leftItem, rightItem)
val xpCost = amount * recipe.xpCostPerCraft
if ((player.gameMode != GameMode.CREATIVE) && (player.level < xpCost)) return
// We give the item manually
// But first we check if we should give the item
val slotDestination = getActionSlot(event, player)
if (slotDestination.type == SlotType.NO_SLOT) return
// If not creative middle click...
if (event.click != ClickType.MIDDLE) {
// We remove what should be removed
leftItem.amount -= amount * recipe.leftItem!!.amount
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
if(rightItem != null){
if(recipe.rightItem == null) return // in case it changed
rightItem.amount -= amount * recipe.rightItem!!.amount
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
}
if(player.gameMode != GameMode.CREATIVE){
player.level -= xpCost
}
// Then we try to find the new values for the anvil
val newAmount = getCustomRecipeAmount(recipe, leftItem, rightItem)
CustomAnvil.verboseLog("new amount is $newAmount")
if(newAmount <= 0 || recipe.exactCount){
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
}else{
val resultItem: ItemStack = recipe.resultItem!!.clone()
resultItem.amount *= newAmount
val newXp = newAmount * newAmount
inventory.repairCost = newXp
event.view.setProperty(REPAIR_COST, newXp)
inventory.setItem(ANVIL_OUTPUT_SLOT, resultItem)
player.updateInventory()
}
}
// Finally, we add the item to the player
if (slotDestination.type == SlotType.CURSOR) {
player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, output)
}
}
private fun onUnitRepairExtract(
leftItem: ItemStack,
rightItem: ItemStack,
output: ItemStack,
unitRepairResult: Double,
event: InventoryClickEvent,
player: Player,
inventory: AnvilInventory
) {
val resultCopy = leftItem.clone()
val resultAmount = resultCopy.unitRepair(
rightItem.amount, unitRepairResult
)
// To avoid vanilla, we cancel the event for unit repair
event.result = Event.Result.DENY
event.isCancelled = true
// And we give the item manually
// But first we check if we should give the item
val slotDestination = getActionSlot(event, player)
if (slotDestination.type == SlotType.NO_SLOT) return
// Test repair cost
var repairCost = 0
if (player.gameMode != GameMode.CREATIVE) {
// Get repairCost
leftItem.itemMeta?.let { leftMeta ->
val leftName = leftMeta.displayName
output.itemMeta?.let {
// Rename cost
if (!leftName.contentEquals(it.displayName)) {
repairCost += ConfigOptions.itemRenameCost
// Color cost
if(it.displayName.contains('§')){
repairCost += ConfigOptions.useOfColorCost
}
}
}
}
repairCost += calculatePenalty(leftItem, null, resultCopy)
repairCost += resultAmount * ConfigOptions.unitRepairCost
if (
!ConfigOptions.doRemoveCostLimit &&
ConfigOptions.doCapCost) {
repairCost = min(repairCost, ConfigOptions.maxAnvilCost)
}
if ((inventory.maximumRepairCost <= repairCost)
|| (player.level < repairCost)
) return
}
// If not creative middle click...
if (event.click != ClickType.MIDDLE) {
// We remove what should be removed
inventory.setItem(ANVIL_INPUT_LEFT, null)
rightItem.amount -= resultAmount
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
player.level -= repairCost
}
// Finally, we add the item to the player
if (slotDestination.type == SlotType.CURSOR) {
player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, output)
}
}
/**
* Get the destination slot or "NO_SLOT" slot container if there is no slot available
*/
private fun getActionSlot(event: InventoryClickEvent, player: Player): SlotContainer {
if (event.isShiftClick) {
val inventory = player.inventory
val firstEmpty = inventory.firstEmpty()
if (firstEmpty == -1) {
return NO_SLOT
}
//check hotbare full
var slotIndex = 8
while (slotIndex >= 0 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
slotIndex--
}
if (slotIndex >= 0) {
return SlotContainer(SlotType.INVENTORY, slotIndex)
}
slotIndex = 35 //4*9 - 1 (max of player inventory)
while (slotIndex >= 9 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
slotIndex--
}
if (slotIndex < 9) {
return NO_SLOT
}
return SlotContainer(SlotType.INVENTORY, slotIndex)
} else {
if (player.itemOnCursor.type != Material.AIR) {
return NO_SLOT
}
return CURSOR_SLOT
}
}
private fun getCustomRecipe (
leftItem: ItemStack,
rightItem: ItemStack?) : AnvilCustomRecipe? {
val recipeList = ConfigHolder.CUSTOM_RECIPE_HOLDER.recipeManager.recipeByMat[leftItem.type] ?: return null
CustomAnvil.verboseLog("Testing " + recipeList.size+" recipe...")
for (recipe in recipeList) {
if(recipe.testItem(leftItem, rightItem)){
return recipe
}
}
return null
}
private fun getCustomRecipeAmount(
recipe: AnvilCustomRecipe,
leftItem: ItemStack,
rightItem: ItemStack?
): Int{
return if(recipe.exactCount) {
if(leftItem.amount != recipe.leftItem!!.amount){
0
}else if(rightItem != null && rightItem.amount != recipe.rightItem!!.amount){
0
}else{
1
}
}
else {
// test amount
val resultItem = recipe.resultItem!! // we know exist as the recipe was returned to us
val maxResultAmount = resultItem.type.maxStackSize/resultItem.amount
val maxLeftAmount = leftItem.amount/recipe.leftItem!!.amount
val maxRightAmount = if(rightItem == null){ maxLeftAmount } else{ rightItem.amount/recipe.rightItem!!.amount }
CustomAnvil.verboseLog("resultItem: $resultItem, maxResultAmount: $maxResultAmount, maxLeftAmount: $maxLeftAmount, maxRightAmount: $maxRightAmount")
min(min(maxResultAmount, maxLeftAmount), maxRightAmount)
}
}
@EventHandler
fun onAnvilClose(event: InventoryCloseEvent){
val player = event.player
if(event.inventory !is AnvilInventory) return
if(player is Player && GameMode.CREATIVE != player.gameMode){
packetManager.setInstantBuild(player, false)
}
}
}
private class SlotContainer(val type: SlotType, val slot: Int)
private enum class SlotType {
CURSOR,
INVENTORY,
NO_SLOT
}

View file

@ -13,7 +13,10 @@ import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant
import xyz.alexcrea.cuanvil.listener.AnvilCloseListener
import xyz.alexcrea.cuanvil.listener.AnvilResultListener
import xyz.alexcrea.cuanvil.listener.ChatEventListener
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.update.PluginSetDefault
import xyz.alexcrea.cuanvil.update.Update_1_21
import xyz.alexcrea.cuanvil.update.plugin.PluginUpdates
@ -110,7 +113,9 @@ class CustomAnvil : JavaPlugin() {
DependencyManager.loadDependency()
// Register anvil events
server.pluginManager.registerEvents(AnvilEventListener(DependencyManager.packetManager), this)
server.pluginManager.registerEvents(PrepareAnvilListener(), this)
server.pluginManager.registerEvents(AnvilResultListener(), this)
server.pluginManager.registerEvents(AnvilCloseListener(DependencyManager.packetManager), this)
// Load metrics
Metrics(this, bstatsPluginId)

View file

@ -41,6 +41,8 @@ object ConfigOptions {
const val ENCHANT_LIMIT_ROOT = "enchant_limits"
const val ENCHANT_VALUES_ROOT = "enchant_values"
const val DISABLE_MERGE_OVER_ROOT = "disable-merge-over"
// Keys for specific enchantment values
private const val KEY_BOOK = "book"
private const val KEY_ITEM = "item"
@ -110,6 +112,9 @@ object ConfigOptions {
// Default value for an enchantment multiplier
private const val DEFAULT_ENCHANT_VALUE = 0
// Default max before merge disabled (negative mean enabled)
const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1;
// -------------
// Get methods
// -------------
@ -296,21 +301,28 @@ object ConfigOptions {
* Get the given [enchantment]'s limit
*/
fun enchantLimit(enchantment: CAEnchantment): Int {
return enchantLimit(enchantment.enchantmentName)
// Test namespace
var limit = enchantLimit(enchantment.key.toString())
if(limit != null) return limit;
// Test legacy (name only)
limit = enchantLimit(enchantment.enchantmentName)
if(limit != null) return limit;
// get default (and test old legacy if present)
return getDefaultLevel(enchantment.enchantmentName)
}
/**
* Get the given [enchantmentName]'s limit
*/
private fun enchantLimit(enchantmentName: String): Int {
val default = getDefaultLevel(enchantmentName)
private fun enchantLimit(enchantmentName: String): Int? {
val path = "${ENCHANT_LIMIT_ROOT}.$enchantmentName"
return CustomAnvil.instance
.config
.getInt(path, default)
.getInt(path, ENCHANT_LIMIT_RANGE.first-1)
.takeIf { it in ENCHANT_LIMIT_RANGE }
?: default
}
/**
@ -319,7 +331,9 @@ object ConfigOptions {
private fun getDefaultLevel(enchantmentName: String, // compatibility with 1.20.5. TODO better update system
) : Int {
if(enchantmentName == "sweeping_edge"){
return enchantLimit("sweeping")
val limit = enchantLimit("sweeping")
if(limit != null) return limit
}
return defaultEnchantLimit
}
@ -332,7 +346,17 @@ object ConfigOptions {
enchantment: CAEnchantment,
isFromBook: Boolean
): Int {
return enchantmentValue(enchantment.enchantmentName, isFromBook)
// Test namespace
var limit = enchantmentValue(enchantment.key.toString(), isFromBook)
if(limit != null) return limit;
// Test legacy (name only)
limit = enchantmentValue(enchantment.enchantmentName, isFromBook)
if(limit != null) return limit;
// get default (and test old legacy if present)
return getDefaultValue(enchantment, isFromBook)
}
/**
@ -342,36 +366,76 @@ object ConfigOptions {
private fun enchantmentValue(
enchantmentName: String,
isFromBook: Boolean
): Int {
val default = getDefaultValue(enchantmentName, isFromBook)
): Int? {
val typeKey = if (isFromBook) KEY_BOOK else KEY_ITEM
val path = "${ENCHANT_VALUES_ROOT}.${enchantmentName}.$typeKey"
return CustomAnvil.instance
.config
.getInt(path, default)
.getInt(path, DEFAULT_ENCHANT_VALUE - 1)
.takeIf { it >= DEFAULT_ENCHANT_VALUE }
?: DEFAULT_ENCHANT_VALUE
}
/**
* Get default value if enchantment do not exist on config
*/
private fun getDefaultValue(enchantmentName: String, // compatibility with 1.20.5. TODO better update system
private fun getDefaultValue(enchantment: CAEnchantment, // compatibility with 1.20.5. TODO better update system
isFromBook: Boolean) : Int {
if(enchantmentName == "sweeping_edge"){
return enchantmentValue("sweeping", isFromBook)
val enchantmentName = enchantment.key.toString()
if(enchantmentName == "minecraft:sweeping_edge"){
var limit = enchantmentValue("minecraft:sweeping", isFromBook)
if(limit != null) return limit
// legacy name
limit = enchantmentValue("sweeping", isFromBook)
if(limit != null) return limit
}
val enchantment = CAEnchantment.getByName(enchantmentName)
if(enchantment != null){
val rarity = enchantment.defaultRarity()
return if(isFromBook) rarity.bookValue
else rarity.itemValue
return if(isFromBook)
rarity.bookValue
else
rarity.itemValue
}
return DEFAULT_ENCHANT_VALUE
/**
* Get the given [enchantmentName]'s level before merge is disabled
* a negative value would mean never disabled
*/
fun maxBeforeMergeDisabled(enchantment: CAEnchantment): Int {
val key = enchantment.key.toString()
var value = maxBeforeMergeDisabled(key)
if(value != null) return value
// Legacy name
val legacy = enchantment.enchantmentName
value = maxBeforeMergeDisabled(legacy)
if(value != null) return value
if(key == "minecraft:sweeping_edge"){
value = maxBeforeMergeDisabled("minecraft:sweeping")
if(value != null) return value
// legacy name of legacy enchantment name
value = maxBeforeMergeDisabled("sweeping")
if(value != null) return value
}
return DEFAULT_MAX_BEFORE_MERGE_DISABLED
}
/**
* Get the given [enchantmentName]'s level before merge is disabled
* a negative value would mean never disabled
*/
private fun maxBeforeMergeDisabled(enchantmentName: String) : Int? {
// find if set
val path = "${DISABLE_MERGE_OVER_ROOT}.$enchantmentName"
return CustomAnvil.instance
.config
.getInt(path, ENCHANT_LIMIT_RANGE.min() - 1)
.takeIf { it in ENCHANT_LIMIT_RANGE }
}
}

View file

@ -76,7 +76,11 @@ object EnchantmentUtil {
}
// ... and they're the same level
else {
// try to increase the enchantment level by 1
// We test if it is allowed to merge at this level
val maxBeforeDisabled = ConfigOptions.maxBeforeMergeDisabled(enchantment)
if((maxBeforeDisabled > 0) && (oldLevel >= maxBeforeDisabled)) return@forEach
// Now we increase the enchantment level by 1
var newLevel = oldLevel + 1
newLevel = max(min(newLevel, maxLevel), oldLevel)
this[enchantment] = newLevel

View file

@ -6,6 +6,8 @@ import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
import xyz.alexcrea.cuanvil.dependency.gui.GuiTesterSelector
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
import xyz.alexcrea.cuanvil.dependency.packet.PacketManagerSelector
import xyz.alexcrea.cuanvil.dependency.scheduler.BukkitScheduler
@ -17,8 +19,12 @@ object DependencyManager {
var isFolia: Boolean = false
lateinit var scheduler: TaskScheduler
lateinit var packetManager: PacketManager
var externGuiTester: ExternGuiTester? = null
var enchantmentSquaredCompatibility: EnchantmentSquaredDependency? = null
var ecoEnchantCompatibility: EcoEnchantDependency? = null
var excellentEnchantsCompatibility: ExcellentEnchantsDependency? = null
var disenchantmentCompatibility: DisenchantmentDependency? = null
fun loadDependency(){
@ -35,6 +41,7 @@ object DependencyManager {
// Packet Manager
val forceProtocolib = ConfigHolder.DEFAULT_CONFIG.config.getBoolean("force_protocolib", false)
packetManager = PacketManagerSelector.selectPacketManager(forceProtocolib)
externGuiTester = GuiTesterSelector.selectGuiTester
// Enchantment Squared dependency
if(pluginManager.isPluginEnabled("EnchantsSquared")){
@ -48,6 +55,12 @@ object DependencyManager {
ecoEnchantCompatibility!!.disableAnvilListener()
}
// Excellent Enchants dependency
if(pluginManager.isPluginEnabled("ExcellentEnchants")){
excellentEnchantsCompatibility = ExcellentEnchantsDependency()
excellentEnchantsCompatibility!!.redirectListeners()
}
// Disenchantment dependency
if(pluginManager.isPluginEnabled("Disenchantment")){
disenchantmentCompatibility = DisenchantmentDependency()
@ -64,6 +77,7 @@ object DependencyManager {
fun registerEnchantments() {
enchantmentSquaredCompatibility?.registerEnchantments()
ecoEnchantCompatibility?.registerEnchantments()
excellentEnchantsCompatibility?.registerEnchantments()
}
@ -79,16 +93,30 @@ object DependencyManager {
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent): Boolean {
var bypass = false
// Test if disenchantment used special prepare anvil
if(disenchantmentCompatibility?.testPrepareAnvil(event) == true) bypass = true
// Test excellent enchantments used special prepare anvil
if(!bypass && (excellentEnchantsCompatibility?.testPrepareAnvil(event) == true)) bypass = true
// Test if the inventory is a gui(version specific)
if(!bypass && (externGuiTester?.testIfGui(event.view) == true)) bypass = true
return bypass
}
fun tryClickAnvilResultBypass(event: InventoryClickEvent, inventory: AnvilInventory): Boolean {
var bypass = false
// Test if disenchantment used special event click
if(disenchantmentCompatibility?.testAnvilResult(event, inventory) == true) bypass = true
// Test if disenchantment used special event click
if(!bypass && (excellentEnchantsCompatibility?.testAnvilResult(event) == true)) bypass = true
// Test if the inventory is a gui(version specific)
if(!bypass && (externGuiTester?.testIfGui(event.view) == true)) bypass = true
return bypass
}

View file

@ -4,13 +4,13 @@ import cz.kominekjan.disenchantment.events.DisenchantClickEvent
import cz.kominekjan.disenchantment.events.DisenchantEvent
import cz.kominekjan.disenchantment.events.ShatterClickEvent
import cz.kominekjan.disenchantment.events.ShatterEvent
import io.delilaheve.AnvilEventListener
import io.delilaheve.CustomAnvil
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
class DisenchantmentDependency {
@ -26,7 +26,6 @@ class DisenchantmentDependency {
private lateinit var itemClickEvent: DisenchantClickEvent
fun redirectListeners() {
val toUnregister = ArrayList<RegisteredListener>()
// get required PrepareAnvilEvent listener
for (registeredListener in PrepareAnvilEvent.getHandlerList().registeredListeners) {
@ -96,7 +95,7 @@ class DisenchantmentDependency {
}
fun testAnvilResult(event: InventoryClickEvent, inventory: AnvilInventory): Boolean {
val previousResultSlot = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)?.clone()
val previousResultSlot = inventory.getItem(PrepareAnvilListener.ANVIL_OUTPUT_SLOT)?.clone()
// Test event if change the result
itemClickEvent.onDisenchantmentClickEvent(event)
@ -115,7 +114,7 @@ class DisenchantmentDependency {
}
private fun testAnvilInventoryChange(inventory: AnvilInventory, previous: ItemStack?): Boolean {
val currentResult = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)
val currentResult = inventory.getItem(PrepareAnvilListener.ANVIL_OUTPUT_SLOT)
return currentResult == previous
}

View file

@ -0,0 +1,107 @@
package xyz.alexcrea.cuanvil.dependency
import io.delilaheve.CustomAnvil
import org.bukkit.Material
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.RegisteredListener
import su.nightexpress.excellentenchants.enchantment.impl.universal.CurseOfFragilityEnchant
import su.nightexpress.excellentenchants.enchantment.listener.AnvilListener
import su.nightexpress.excellentenchants.registry.EnchantRegistry
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEEnchantment
import java.lang.reflect.Method
class ExcellentEnchantsDependency {
init {
CustomAnvil.instance.logger.info("Excellent Enchants Detected !")
}
fun registerEnchantments() {
CustomAnvil.instance.logger.info("Preparing Excellent Enchants compatibility...")
for (enchantment in EnchantRegistry.getRegistered()) {
EnchantmentApi.unregisterEnchantment(enchantment.bukkitEnchantment.key) // As excellent enchants is loaded before custom anvil and register enchantment to registry, we need to unregister old "vanilla" enchant.
EnchantmentApi.registerEnchantment(CAEEEnchantment(enchantment))
}
CustomAnvil.instance.logger.info("Excellent Enchants should now work as expected !")
}
private lateinit var fragilityCurse: CurseOfFragilityEnchant
private lateinit var anvilListener: AnvilListener
private lateinit var handleRechargeMethod: Method
private lateinit var handleCombineMethod: Method
fun redirectListeners() {
val toUnregister = ArrayList<RegisteredListener>()
// get required PrepareAnvilEvent listener
for (registeredListener in PrepareAnvilEvent.getHandlerList().registeredListeners) {
val listener = registeredListener.listener
if(listener is CurseOfFragilityEnchant){
this.fragilityCurse = listener
toUnregister.add(registeredListener)
}
if(listener is AnvilListener){
this.anvilListener = listener;
toUnregister.add(registeredListener)
}
}
for (listener in toUnregister) {
PrepareAnvilEvent.getHandlerList().unregister(listener)
}
// Unregister inventory click event
InventoryClickEvent.getHandlerList().unregister(this.anvilListener)
findAnvilFunctions()
}
private fun findAnvilFunctions() {
this.handleRechargeMethod = AnvilListener::class.java.getDeclaredMethod("handleRecharge",
PrepareAnvilEvent::class.java, ItemStack::class.java, ItemStack::class.java)
this.handleRechargeMethod.setAccessible(true)
this.handleCombineMethod = AnvilListener::class.java.getDeclaredMethod("handleCombine",
PrepareAnvilEvent::class.java, ItemStack::class.java, ItemStack::class.java, ItemStack::class.java)
this.handleCombineMethod.setAccessible(true)
}
fun testPrepareAnvil(event: PrepareAnvilEvent): Boolean {
if(event.result != null){
this.fragilityCurse.onItemAnvil(event)
if(event.result == null) return true
}
var first: ItemStack? = event.inventory.getItem(0)
var second: ItemStack? = event.inventory.getItem(1)
var result = event.result
if (first == null) first = ItemStack(Material.AIR)
if (second == null) second = ItemStack(Material.AIR)
if (result == null) result = ItemStack(Material.AIR)
if(handleRechargeMethod.invoke(this.anvilListener, event, first, second) as Boolean) return true
return handleCombineMethod.invoke(this.anvilListener, event, first, second, result) as Boolean
}
fun testAnvilResult(event: InventoryClickEvent): Any {
if(event.inventory.getItem(2) != null){
this.anvilListener.onClickAnvil(event)
return event.inventory.getItem(2) == null
}
return false;
}
}

View file

@ -0,0 +1,51 @@
package xyz.alexcrea.cuanvil.dependency.gui
import xyz.alexcrea.cuanvil.dependency.gui.version.*;
import xyz.alexcrea.cuanvil.update.UpdateUtils
object GuiTesterSelector {
val selectGuiTester: ExternGuiTester?
get() {
val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (versionParts[0] != 1) return null
return when (versionParts[1]) {
// Can't support 1.16.5 bc 1.16.5 paper userdev do not exist
17 -> when (versionParts[2]) {
0, 1 -> v1_17R1_ExternGuiTester()
else -> null
}
18 -> when (versionParts[2]) {
0, 1 -> v1_18R1_ExternGuiTester()
2 -> v1_18R2_ExternGuiTester()
else -> null
}
19 -> when (versionParts[2]) {
0, 1, 2 -> v1_19R1_ExternGuiTester()
3 -> v1_19R2_ExternGuiTester()
4 -> v1_19R3_ExternGuiTester()
else -> null
}
20 -> when (versionParts[2]) {
0, 1 -> v1_20R1_ExternGuiTester()
2 -> v1_20R2_ExternGuiTester()
3, 4 -> v1_20R3_ExternGuiTester()
5, 6 -> v1_20R4_ExternGuiTester()
else -> null
}
21 -> when (versionParts[2]) {
0, 1 -> v1_21R1_ExternGuiTester()
else -> null
}
else -> null
}
}
}

View file

@ -15,6 +15,9 @@ class EnchantConflictGroup(
fun addEnchantment(enchant: CAEnchantment) {
enchantments.add(enchant)
}
fun addEnchantments(enchants: List<CAEnchantment>) {
enchantments.addAll(enchants)
}
fun allowed(enchants: Set<CAEnchantment>, mat: Material): Boolean {
if (enchantments.size < minBeforeBlock) {

View file

@ -30,9 +30,9 @@ class EnchantConflictManager {
const val DEFAULT_GROUP_NAME = "joinedGroup"
// 1.20.5 compatibility TODO better update system
private val SWEEPING_EDGE_ENCHANT =
private val SWEEPING_EDGE_ENCHANT = Collections.singletonList<CAEnchantment>(
CAEnchantment.getByKey(NamespacedKey.minecraft("sweeping_edge")) ?:
CAEnchantment.getByKey(Enchantment.SWEEPING_EDGE.key)
CAEnchantment.getByKey(Enchantment.SWEEPING_EDGE.key))
}
@ -94,14 +94,14 @@ class EnchantConflictManager {
// Read and add enchantment to conflict
val enchantList = section.getStringList(ENCH_LIST_PATH)
for (enchantName in enchantList) {
val enchant = getEnchantByName(enchantName)
if (enchant == null) {
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.addEnchantment(enchant)
conflict.addEnchantments(enchants)
}
if (conflict.getEnchants().isEmpty()) {
if (!futureUse) { //TODO future use will be deprecated once the new update system is finished
@ -112,16 +112,23 @@ class EnchantConflictManager {
return conflict
}
private fun getEnchantByName(enchantName: String): CAEnchantment? {
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){
"sweeping", "sweeping_edge" -> {
"minecraft:sweeping", "sweeping",
"minecraft:sweeping_edge", "sweeping_edge" -> {
return SWEEPING_EDGE_ENCHANT
}
}
return CAEnchantment.getByName(enchantName)
return CAEnchantment.getListByName(enchantName)
}

View file

@ -0,0 +1,23 @@
package xyz.alexcrea.cuanvil.listener
import org.bukkit.GameMode
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.AnvilInventory
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
class AnvilCloseListener(private val packetManager: PacketManager) : Listener {
@EventHandler
fun onAnvilClose(event: InventoryCloseEvent){
val player = event.player
if(event.inventory !is AnvilInventory) return
if(player is Player && GameMode.CREATIVE != player.gameMode){
packetManager.setInstantBuild(player, false)
}
}
}

View file

@ -0,0 +1,292 @@
package xyz.alexcrea.cuanvil.listener
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.ItemUtil.canMergeWith
import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.Event
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import kotlin.math.min
class AnvilResultListener: Listener {
companion object {
// static slot container
private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0)
private val CURSOR_SLOT = SlotContainer(SlotType.CURSOR, 0)
}
/**
* Event handler logic for when a player is trying to pull an item out of the anvil
*/
@EventHandler(ignoreCancelled = true)
fun anvilExtractionCheck(event: InventoryClickEvent) {
val player = event.whoClicked as? Player ?: return
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
val inventory = event.inventory as? AnvilInventory ?: return
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
return
}
// Test if the event should bypass custom anvil.
if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
// Test custom recipe
val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem)
if(recipe != null){
event.result = Event.Result.ALLOW
onCustomCraft(
event, recipe, player,
leftItem, rightItem, output, inventory)
return
}
val canMerge = leftItem.canMergeWith(rightItem)
val unitRepairResult = leftItem.getRepair(rightItem)
val allowed = (rightItem == null)
|| (canMerge)
|| (unitRepairResult != null)
// True if there was no change or not allowed
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))
|| !allowed
) {
event.result = Event.Result.DENY
return
}
if (rightItem == null) {
event.result = Event.Result.ALLOW
return
}
if (canMerge) {
event.result = Event.Result.ALLOW
} else if (unitRepairResult != null) {
onUnitRepairExtract(
leftItem, rightItem, output,
unitRepairResult, event, player, inventory
)
return
}
}
private fun onCustomCraft(event: InventoryClickEvent,
recipe: AnvilCustomRecipe,
player: Player,
leftItem: ItemStack,
rightItem: ItemStack?,
output: ItemStack,
inventory: AnvilInventory
) {
event.result = Event.Result.DENY
if(recipe.leftItem == null) return // in case it changed
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
val xpCost = amount * recipe.xpCostPerCraft
if ((player.gameMode != GameMode.CREATIVE) && (player.level < xpCost)) return
// We give the item manually
// But first we check if we should give the item
val slotDestination = getActionSlot(event, player)
if (slotDestination.type == SlotType.NO_SLOT) return
// Handle not creative middle click...
if (event.click != ClickType.MIDDLE &&
!handleCustomCraftClick(event, recipe, inventory, player, leftItem, rightItem, amount, xpCost)) return;
// Finally, we add the item to the player
if (slotDestination.type == SlotType.CURSOR) {
player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, output)
}
}
private fun handleCustomCraftClick(event: InventoryClickEvent, recipe: AnvilCustomRecipe,
inventory: AnvilInventory, player: Player,
leftItem: ItemStack, rightItem: ItemStack?,
amount: Int, xpCost: Int): Boolean {
// We remove what should be removed
if(rightItem != null){
if(recipe.rightItem == null) return false// in case it changed
rightItem.amount -= amount * recipe.rightItem!!.amount
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
}
leftItem.amount -= amount * recipe.leftItem!!.amount
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
if(player.gameMode != GameMode.CREATIVE){
player.level -= xpCost
}
// Then we try to find the new values for the anvil
val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
CustomAnvil.verboseLog("new amount is $newAmount")
if(newAmount <= 0 || recipe.exactCount){
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
}else{
val resultItem: ItemStack = recipe.resultItem!!.clone()
resultItem.amount *= newAmount
val newXp = newAmount * newAmount
inventory.repairCost = newXp
event.view.setProperty(InventoryView.Property.REPAIR_COST, newXp)
inventory.setItem(ANVIL_OUTPUT_SLOT, resultItem)
player.updateInventory()
}
return true
}
private fun onUnitRepairExtract(
leftItem: ItemStack,
rightItem: ItemStack,
output: ItemStack,
unitRepairResult: Double,
event: InventoryClickEvent,
player: Player,
inventory: AnvilInventory
) {
val resultCopy = leftItem.clone()
val resultAmount = resultCopy.unitRepair(
rightItem.amount, unitRepairResult
)
// To avoid vanilla, we cancel the event for unit repair
event.result = Event.Result.DENY
event.isCancelled = true
// And we give the item manually
// But first we check if we should give the item
val slotDestination = getActionSlot(event, player)
if (slotDestination.type == SlotType.NO_SLOT) return
// Test repair cost
val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount)
if(repairCost == Int.MIN_VALUE) return
// If not creative middle click...
if (event.click != ClickType.MIDDLE) {
// We remove what should be removed
inventory.setItem(ANVIL_INPUT_LEFT, null)
rightItem.amount -= resultAmount
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
player.level -= repairCost
}
// Finally, we add the item to the player
if (slotDestination.type == SlotType.CURSOR) {
player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, output)
}
}
private fun getUnitRepairCost(inventory: AnvilInventory, player: Player,
leftItem: ItemStack, output: ItemStack,
resultCopy: ItemStack, resultAmount: Int): Int {
if (player.gameMode == GameMode.CREATIVE) return 0
var repairCost = 0;
// Get repairCost
leftItem.itemMeta?.let { leftMeta ->
val leftName = leftMeta.displayName
output.itemMeta?.let {
// Rename cost
if (!leftName.contentEquals(it.displayName)) {
repairCost += ConfigOptions.itemRenameCost
// Color cost
if(it.displayName.contains('§')){
repairCost += ConfigOptions.useOfColorCost
}
}
}
}
repairCost += AnvilXpUtil.calculatePenalty(leftItem, null, resultCopy)
repairCost += resultAmount * ConfigOptions.unitRepairCost
if (
!ConfigOptions.doRemoveCostLimit &&
ConfigOptions.doCapCost
) {
repairCost = min(repairCost, ConfigOptions.maxAnvilCost)
}
if ((inventory.maximumRepairCost <= repairCost)
|| (player.level < repairCost)
) return Int.MIN_VALUE
return repairCost
}
/**
* Get the destination slot or "NO_SLOT" slot container if there is no slot available
*/
private fun getActionSlot(event: InventoryClickEvent, player: Player): SlotContainer {
if (event.isShiftClick) {
val inventory = player.inventory
val firstEmpty = inventory.firstEmpty()
if (firstEmpty == -1) {
return NO_SLOT
}
//check hotbare full
var slotIndex = 8
while (slotIndex >= 0 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
slotIndex--
}
if (slotIndex >= 0) {
return SlotContainer(SlotType.INVENTORY, slotIndex)
}
slotIndex = 35 //4*9 - 1 (max of player inventory)
while (slotIndex >= 9 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
slotIndex--
}
if (slotIndex < 9) {
return NO_SLOT
}
return SlotContainer(SlotType.INVENTORY, slotIndex)
}
else if (player.itemOnCursor.type != Material.AIR) return NO_SLOT
return CURSOR_SLOT
}
private class SlotContainer(val type: SlotType, val slot: Int)
private enum class SlotType {
CURSOR,
INVENTORY,
NO_SLOT
}
}

View file

@ -0,0 +1,206 @@
package xyz.alexcrea.cuanvil.listener
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.ItemUtil.canMergeWith
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook
import io.delilaheve.util.ItemUtil.repairFrom
import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.ChatColor
import org.bukkit.entity.HumanEntity
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.util.AnvilColorUtil
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
/**
* Listener for anvil events
*/
class PrepareAnvilListener : Listener {
companion object {
// Anvil's output slot
const val ANVIL_INPUT_LEFT = 0
const val ANVIL_INPUT_RIGHT = 1
const val ANVIL_OUTPUT_SLOT = 2
}
/**
* Event handler logic for when an anvil contains items to be combined
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun anvilCombineCheck(event: PrepareAnvilEvent) {
// Test if the event should bypass custom anvil.
if(DependencyManager.tryEventPreAnvilBypass(event)) return
val inventory = event.inventory
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val second = inventory.getItem(ANVIL_INPUT_RIGHT)
// Should find player
val player = event.view.player
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
// Test custom recipe
if(testCustomRecipe(event, inventory, first, second)) return
// Test rename lonely item
if(second == null) {
doRenaming(event, inventory, player, first)
return
}
// Test for merge
if (first.canMergeWith(second)) {
doMerge(event, inventory, player, first, second)
return
}
// Test for unit repair
if(testUnitRepair(event, inventory, player, first, second)) return
CustomAnvil.log("no anvil fuse type found")
event.result = null
}
private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, first: ItemStack, second: ItemStack?): Boolean {
val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
if(recipe == null) return false
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
val resultItem: ItemStack = recipe.resultItem!!.clone()
resultItem.amount *= amount
event.result = resultItem
AnvilXpUtil.setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true)
return true
}
private fun doRenaming(event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity, first: ItemStack) {
val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("no right item, But input is same as output")
event.result = null
return
}
event.result = resultItem
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem)
AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost)
}
private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int {
// Rename item and add renaming cost
resultItem.itemMeta?.let {
val displayName = ChatColor.stripColor(it.displayName)
var inventoryName = ChatColor.stripColor(inventory.renameText)
var sumCost = 0
var useColor = false
if(ConfigOptions.renameColorPossible){
val resultString = StringBuilder(inventoryName)
useColor = AnvilColorUtil.handleRenamingColor(resultString, player)
if(useColor) {
inventoryName = resultString.toString()
sumCost+= ConfigOptions.useOfColorCost
}
}
if ((!useColor && (!displayName.contentEquals(inventoryName))) || (useColor && !(it.displayName).contentEquals(inventoryName))) {
it.setDisplayName(inventoryName)
resultItem.itemMeta = it
sumCost+= ConfigOptions.itemRenameCost
}
return sumCost
}
return 0
}
private fun doMerge(event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity,
first: ItemStack, second: ItemStack) {
val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first, player)
val resultItem = first.clone()
resultItem.setEnchantmentsUnsafe(newEnchants)
// Calculate enchantment cost
var anvilCost = AnvilXpUtil.getRightValues(second, resultItem)
// Calculate repair cost
if (!first.isEnchantedBook() && !second.isEnchantedBook()) {
// we only need to be concerned with repair when neither item is a book
val repaired = resultItem.repairFrom(first, second)
anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0
}
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("Mergable with second, But input is same as output")
event.result = null
return
}
// As calculatePenalty edit result, we need to calculate penalty after checking equality
anvilCost += AnvilXpUtil.calculatePenalty(first, second, resultItem)
// Calculate rename cost
anvilCost += handleRename(resultItem, inventory, player)
// Finally, we set result
event.result = resultItem
AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost)
}
private fun testUnitRepair(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
first: ItemStack, second: ItemStack): Boolean {
val unitRepairAmount = first.getRepair(second) ?: return false
val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player)
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
if (repairAmount > 0) {
anvilCost += repairAmount * ConfigOptions.unitRepairCost
}
// We do not care about right item penalty for unit repair
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, true)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("unit repair, But input is same as output")
event.result = null
return true
}
event.result = resultItem
AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost)
return true
}
}

View file

@ -0,0 +1,88 @@
package xyz.alexcrea.cuanvil.util
import io.delilaheve.util.ConfigOptions
import org.bukkit.entity.HumanEntity
import java.util.regex.Matcher
import java.util.regex.Pattern
object AnvilColorUtil {
private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string
fun handleRenamingColor(textToColor: StringBuilder, player: HumanEntity): Boolean {
val usePermission = ConfigOptions.permissionNeededForColor
val canUseColorCode = ConfigOptions.allowColorCode && (!usePermission || player.hasPermission("ca.color.code"))
val canUseHexColor = ConfigOptions.allowHexadecimalColor && (!usePermission || player.hasPermission("ca.color.hex"))
if((!canUseColorCode) && (!canUseHexColor)) return false
var useColor = false
// Handle color code
if(canUseColorCode){
var nbReplacement = replaceAll(textToColor, "&", "§", 2)
nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2)
if(nbReplacement > 0) useColor = true
}
if(canUseHexColor){
val nbReplacement = replaceHexToColor(textToColor, 7)
if(nbReplacement > 0) useColor = true
}
return useColor
}
/**
* Replace every instance of "from" to "to".
* @param builder The builder to replace the string from.
* @param from The source that should be replaced.
* @param to The string that should replace.
* @param endOffset Amount of character that should be ignored at the end.
* @return The number of replacement was that was done.
*/
private fun replaceAll(builder: java.lang.StringBuilder, from: String, to: String, endOffset: Int): Int {
var index = builder.indexOf(from)
var numberOfChanges = 0
while (index != -1 && index < builder.length - endOffset) {
builder.replace(index, index + from.length, to)
index += to.length
index = builder.indexOf(from, index)
numberOfChanges+=1
}
return numberOfChanges
}
/**
* Replace every hex color formatted like #000000 to the minecraft format
* @param builder The builder to replace the hex color from.
* @param endOffset Amount of character that should be ignored at the end.
* @return The number of replacement was that was done.
*/
private fun replaceHexToColor(builder: StringBuilder, endOffset: Int): Int {
val matcher: Matcher = HEX_PATTERN.matcher(builder)
var numberOfChanges = 0
var startIndex = 0
while(matcher.find(startIndex)){
startIndex = matcher.start()
if(startIndex >= builder.length - endOffset) break
builder.replace(startIndex, startIndex + 1, "§x")
startIndex+=2
for (i in 0..5) {
builder.insert(startIndex, '§')
startIndex+=2
}
numberOfChanges+=1
}
return numberOfChanges
}
}

View file

@ -0,0 +1,54 @@
package xyz.alexcrea.cuanvil.util
import io.delilaheve.CustomAnvil
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import kotlin.math.min
object CustomRecipeUtil {
fun getCustomRecipe (
leftItem: ItemStack,
rightItem: ItemStack?) : AnvilCustomRecipe? {
val recipeList = ConfigHolder.CUSTOM_RECIPE_HOLDER.recipeManager.recipeByMat[leftItem.type] ?: return null
CustomAnvil.verboseLog("Testing " + recipeList.size + " recipe...")
for (recipe in recipeList) {
if(recipe.testItem(leftItem, rightItem)){
return recipe
}
}
return null
}
fun getCustomRecipeAmount(
recipe: AnvilCustomRecipe,
leftItem: ItemStack,
rightItem: ItemStack?
): Int{
return if(recipe.exactCount) {
if(leftItem.amount != recipe.leftItem!!.amount){
0
}else if(rightItem != null && rightItem.amount != recipe.rightItem!!.amount){
0
}else{
1
}
}
else {
// test amount
val resultItem = recipe.resultItem!! // we know exist as the recipe was returned to us
val maxResultAmount = resultItem.type.maxStackSize/resultItem.amount
val maxLeftAmount = leftItem.amount/recipe.leftItem!!.amount
val maxRightAmount = if(rightItem == null){ maxLeftAmount } else{ rightItem.amount/recipe.rightItem!!.amount }
CustomAnvil.verboseLog("resultItem: $resultItem, maxResultAmount: $maxResultAmount, maxLeftAmount: $maxLeftAmount, maxRightAmount: $maxRightAmount")
min(min(maxResultAmount, maxLeftAmount), maxRightAmount)
}
}
}

View file

@ -97,46 +97,46 @@ default_limit: 5
#
# Valid range of 1 - 255 for each enchantment
enchant_limits:
aqua_affinity: 1
binding_curse: 1
channeling: 1
flame: 1
infinity: 1
mending: 1
multishot: 1
silk_touch: 1
vanishing_curse: 1
depth_strider: 3 # anything more than 3 is treated as 3 by the game
protection: 4
fire_protection: 4
blast_protection: 4
projectile_protection: 4
feather_falling: 4
thorns: 3
respiration: 3
sharpness: 5
smite: 5
bane_of_arthropods: 5
knockback: 2
fire_aspect: 2
looting: 3
sweeping: 3
sweeping_edge: 3
efficiency: 5
unbreaking: 3
fortune: 3
power: 5
punch: 2
luck_of_the_sea: 3
lure: 3
frost_walker: 2
impaling: 5
riptide: 3
loyalty: 3
piercing: 4
quick_charge: 3
soul_speed: 3
swift_sneak: 3
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
minecraft:channeling: 1
minecraft:flame: 1
minecraft:infinity: 1
minecraft:mending: 1
minecraft:multishot: 1
minecraft:silk_touch: 1
minecraft:vanishing_curse: 1
minecraft:depth_strider: 3 # anything more than 3 is treated as 3 by the game
minecraft:protection: 4
minecraft:fire_protection: 4
minecraft:blast_protection: 4
minecraft:projectile_protection: 4
minecraft:feather_falling: 4
minecraft:thorns: 3
minecraft:respiration: 3
minecraft:sharpness: 5
minecraft:smite: 5
minecraft:bane_of_arthropods: 5
minecraft:knockback: 2
minecraft:fire_aspect: 2
minecraft:looting: 3
minecraft:sweeping: 3
minecraft:sweeping_edge: 3
minecraft:efficiency: 5
minecraft:unbreaking: 3
minecraft:fortune: 3
minecraft:power: 5
minecraft:punch: 2
minecraft:luck_of_the_sea: 3
minecraft:lure: 3
minecraft:frost_walker: 2
minecraft:impaling: 5
minecraft:riptide: 3
minecraft:loyalty: 3
minecraft:piercing: 4
minecraft:quick_charge: 3
minecraft:soul_speed: 3
minecraft:swift_sneak: 3
# Multipliers used to calculate the enchantment's value in repair/combining
#
@ -150,127 +150,138 @@ enchant_limits:
# With default values protection 4 would have a value of 4 when
# coming from either a book (4 * 1) or an item (4 * 1)
enchant_values:
aqua_affinity:
minecraft:aqua_affinity:
item: 4
book: 2
bane_of_arthropods:
minecraft:bane_of_arthropods:
item: 2
book: 1
binding_curse:
minecraft:binding_curse:
item: 8
book: 4
blast_protection:
minecraft:blast_protection:
item: 4
book: 2
channeling:
minecraft:channeling:
item: 8
book: 4
depth_strider:
minecraft:depth_strider:
item: 4
book: 2
efficiency:
minecraft:efficiency:
item: 1
book: 1
flame:
minecraft:flame:
item: 4
book: 2
feather_falling:
minecraft:feather_falling:
item: 2
book: 1
fire_aspect:
minecraft:fire_aspect:
item: 4
book: 2
fire_protection:
minecraft:fire_protection:
item: 2
book: 1
fortune:
minecraft:fortune:
item: 4
book: 2
frost_walker:
minecraft:frost_walker:
item: 4
book: 2
impaling:
minecraft:impaling:
item: 4
book: 2
infinity:
minecraft:infinity:
item: 8
book: 4
knockback:
minecraft:knockback:
item: 2
book: 1
looting:
minecraft:looting:
item: 4
book: 2
loyalty:
minecraft:loyalty:
item: 1
book: 1
luck_of_the_sea:
minecraft:luck_of_the_sea:
item: 4
book: 2
lure:
minecraft:lure:
item: 4
book: 2
mending:
minecraft:mending:
item: 4
book: 2
multishot:
minecraft:multishot:
item: 4
book: 2
piercing:
minecraft:piercing:
item: 1
book: 1
power:
minecraft:power:
item: 1
book: 1
projectile_protection:
minecraft:projectile_protection:
item: 2
book: 1
protection:
minecraft:protection:
item: 1
book: 1
punch:
minecraft:punch:
item: 4
book: 2
quick_charge:
minecraft:quick_charge:
item: 2
book: 1
respiration:
minecraft:respiration:
item: 4
book: 2
riptide:
minecraft:riptide:
item: 4
book: 2
silk_touch:
minecraft:silk_touch:
item: 8
book: 4
sharpness:
minecraft:sharpness:
item: 1
book: 1
smite:
minecraft:smite:
item: 2
book: 1
soul_speed:
minecraft:soul_speed:
item: 8
book: 4
swift_sneak:
minecraft:swift_sneak:
item: 8
book: 4
sweeping:
minecraft:sweeping:
item: 4
book: 2
sweeping_edge:
minecraft:sweeping_edge:
item: 4
book: 2
thorns:
minecraft:thorns:
item: 8
book: 4
unbreaking:
minecraft:unbreaking:
item: 2
book: 1
vanishing_curse:
minecraft:vanishing_curse:
item: 8
book: 4
# Disable enchantment merging for level above the set value
# Enchantment merging is when, for example, 2 unbreaking II book combine to give sharpness III
# But Enchantment above this value can still be applied. following the previous example, we could still apply a unbreaking III book to a sword
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
#minecraft:unbreaking: 2
# Whether to show debug logging
debug_log: false

View file

@ -17,158 +17,158 @@
# ----------------------------------------------------
restriction_aqua_affinity:
enchantments: [ aqua_affinity ]
enchantments: [ minecraft:aqua_affinity ]
notAffectedGroups: [ enchanted_book, helmets ]
restriction_bane_of_arthropods:
enchantments: [ bane_of_arthropods ]
enchantments: [ minecraft:bane_of_arthropods ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_blast_protection:
enchantments: [ blast_protection ]
enchantments: [ minecraft:blast_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_channeling:
enchantments: [ channeling ]
enchantments: [ minecraft:channeling ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_binding_curse:
enchantments: [ binding_curse ]
enchantments: [ minecraft:binding_curse ]
notAffectedGroups: [ enchanted_book, wearable ]
restriction_vanishing_curse:
enchantments: [ vanishing_curse ]
enchantments: [ minecraft:vanishing_curse ]
notAffectedGroups: [ enchanted_book, can_vanish ]
restriction_depth_strider:
enchantments: [ depth_strider ]
enchantments: [ minecraft:depth_strider ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_efficiency:
enchantments: [ efficiency ]
enchantments: [ minecraft:efficiency ]
notAffectedGroups: [ enchanted_book, tools, shears ]
restriction_feather_falling:
enchantments: [ feather_falling ]
enchantments: [ minecraft:feather_falling ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_fire_aspect:
enchantments: [ fire_aspect ]
enchantments: [ minecraft:fire_aspect ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_fire_protection:
enchantments: [ fire_protection ]
enchantments: [ minecraft:fire_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_flame:
enchantments: [ flame ]
enchantments: [ minecraft:flame ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_fortune:
enchantments: [ fortune ]
enchantments: [ minecraft:fortune ]
notAffectedGroups: [ enchanted_book, tools ]
restriction_frost_walker:
enchantments: [ frost_walker ]
enchantments: [ minecraft:frost_walker ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_impaling:
enchantments: [ impaling ]
enchantments: [ minecraft:impaling ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_infinity:
enchantments: [ infinity ]
enchantments: [ minecraft:infinity ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_knockback:
enchantments: [ knockback ]
enchantments: [ minecraft:knockback ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_looting:
enchantments: [ looting ]
enchantments: [ minecraft:looting ]
notAffectedGroups: [ enchanted_book, swords ]
restriction_loyalty:
enchantments: [ loyalty ]
enchantments: [ minecraft:loyalty ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_lure:
enchantments: [ lure ]
enchantments: [ minecraft:lure ]
notAffectedGroups: [ enchanted_book, fishing_rod ]
restriction_mending:
enchantments: [ mending ]
enchantments: [ minecraft:mending ]
notAffectedGroups: [ enchanted_book, can_unbreak ]
restriction_multishot:
enchantments: [ multishot ]
restriction_minecraft_multishot:
enchantments: [ minecraft:multishot ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_piercing:
enchantments: [ piercing ]
enchantments: [ minecraft:piercing ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_power:
enchantments: [ power ]
enchantments: [ minecraft:power ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_projectile_protection:
enchantments: [ projectile_protection ]
enchantments: [ minecraft:projectile_protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_protection:
enchantments: [ protection ]
enchantments: [ minecraft:protection ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_punch:
enchantments: [ punch ]
enchantments: [ minecraft:punch ]
notAffectedGroups: [ enchanted_book, bow ]
restriction_quick_charge:
enchantments: [ quick_charge ]
enchantments: [ minecraft:quick_charge ]
notAffectedGroups: [ enchanted_book, crossbow ]
restriction_respiration:
enchantments: [ respiration ]
enchantments: [ minecraft:respiration ]
notAffectedGroups: [ enchanted_book, helmets ]
restriction_riptide:
enchantments: [ riptide ]
enchantments: [ minecraft:riptide ]
notAffectedGroups: [ enchanted_book, trident ]
restriction_sharpness:
enchantments: [ sharpness ]
enchantments: [ minecraft:sharpness ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_silk_touch:
enchantments: [ silk_touch ]
restriction__silk_touch:
enchantments: [ minecraft:silk_touch ]
notAffectedGroups: [ enchanted_book, tools ]
restriction_smite:
enchantments: [ smite ]
enchantments: [ minecraft:smite ]
notAffectedGroups: [ enchanted_book, melee_weapons ]
restriction_soul_speed:
enchantments: [ soul_speed ]
enchantments: [ minecraft:soul_speed ]
notAffectedGroups: [ enchanted_book, boots ]
restriction_sweeping_edge:
enchantments: [ sweeping, sweeping_edge ]
enchantments: [ minecraft:sweeping, minecraft:sweeping_edge ]
notAffectedGroups: [ enchanted_book, swords ]
# Do not exist in 1.18, that mean useInFuture will be set to true
# useInFuture set to true also mean it will not warn if there is an issue
restriction_swift_sneak:
useInFuture: true
enchantments: [ swift_sneak ]
enchantments: [ minecraft:swift_sneak ]
notAffectedGroups: [ enchanted_book, leggings ]
restriction_thorns:
enchantments: [ thorns ]
enchantments: [ minecraft:thorns ]
notAffectedGroups: [ enchanted_book, armors ]
restriction_unbreaking:
enchantments: [ unbreaking ]
restriction__unbreaking:
enchantments: [ minecraft:unbreaking ]
notAffectedGroups: [ enchanted_book, can_unbreak ]
# ----------------------------------------------------
@ -180,60 +180,60 @@ restriction_unbreaking:
sword_enchant_conflict:
enchantments:
- bane_of_arthropods
- smite
- sharpness
- minecraft:bane_of_arthropods
- minecraft:smite
- minecraft:sharpness
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
protection_enchant_conflict:
enchantments:
- blast_protection
- fire_protection
- projectile_protection
- protection
- minecraft:blast_protection
- minecraft:fire_protection
- minecraft:projectile_protection
- minecraft:protection
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
trident_conflict1:
enchantments:
- channeling
- riptide
- minecraft:channeling
- minecraft:riptide
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
trident_conflict2:
enchantments:
- loyalty
- riptide
- minecraft:loyalty
- minecraft:riptide
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
boot_conflict:
enchantments:
- depth_strider
- frost_walker
- minecraft:depth_strider
- minecraft:frost_walker
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
tool_conflict:
enchantments:
- fortune
- silk_touch
- minecraft:fortune
- minecraft:silk_touch
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
bow_conflict:
enchantments:
- mending
- infinity
- minecraft:mending
- minecraft:infinity
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
crossbow_conflict:
enchantments:
- multishot
- piercing
- minecraft:multishot
- minecraft:piercing
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1

View file

@ -1,7 +1,7 @@
main: io.delilaheve.CustomAnvil
name: CustomAnvil
prefix: "Custom Anvil"
version: 1.6.2
version: 1.6.3
folia-supported: true
description: Allow to customise anvil mechanics
api-version: 1.16
@ -49,8 +49,8 @@ permissions:
description: Allow player to use hexadecimal color if permission is required (toggleable)
# soft depend on old name, so I can disable it if it is on the same server
# as it is the old name for this plugin
# soft depend on old name (UnsafeEnchantsPlus), so I can disable it if it is on the same server (old name for this plugin)
# Also depend to other plugin for compatibility
softdepend:
- UnsafeEnchantsPlus
- ProtocolLib
@ -58,3 +58,4 @@ softdepend:
- EnchantsSquared
- EcoEnchants
- eco
- ExcellentEnchants