15 Commits
1.1.0 ... 1.2.0

Author SHA1 Message Date
Auxilor
0c24d6be21 libreforge-updater 2024-07-19 20:28:49 +01:00
Auxilor
281e605ce1 libreforge-updater 2024-07-18 13:24:24 +01:00
Will FP
95438d151f Added resource ID 2024-07-18 11:38:47 +01:00
Auxilor
d2a00f3c2d Added customItem 2024-07-17 19:01:57 +01:00
Auxilor
18c7635232 Added per-target scroll limits 2024-07-17 18:16:33 +01:00
Auxilor
a17c412a5c Changed placeholder format, improved hot potato books 2024-07-17 18:04:52 +01:00
Auxilor
5c62b3b467 Added hot potato book example 2024-07-17 17:46:49 +01:00
Auxilor
bef67355a6 Fixed inscribe_item 2024-07-17 17:08:14 +01:00
Auxilor
8f8036a518 Minor codestyle improvements 2024-07-17 16:58:58 +01:00
Auxilor
3fe0a8071d Fixed several effect bugs 2024-07-17 16:09:39 +01:00
Auxilor
de7339ac84 Improved _example.yml 2024-07-17 15:02:05 +01:00
Auxilor
e902c65dd0 Added /ecoscrolls inscribe 2024-07-08 18:59:52 +01:00
Auxilor
9207babde5 Minor codestyle 2024-07-08 18:46:48 +01:00
Auxilor
8919ab0d2a Rewrote how scroll uses are tracked 2024-07-08 18:45:58 +01:00
Will FP
776abef5e2 Update gradle.properties 2024-07-08 15:59:33 +01:00
18 changed files with 294 additions and 74 deletions

View File

@@ -63,7 +63,7 @@ class EcoScrollsPlugin : LibreforgePlugin() {
}
PlaceholderManager.registerPlaceholder(
object : DynamicPlaceholder(plugin, Pattern.compile("scroll_([a-z]+)_([a-zA-Z0-9_]+)")) {
object : DynamicPlaceholder(plugin, Pattern.compile("scroll_([a-z0-9_]+):([a-zA-Z0-9_]+)")) {
override fun getValue(args: String, context: PlaceholderContext): String? {
val matcher = pattern.matcher(args)

View File

@@ -13,6 +13,7 @@ class CommandEcoScrolls(plugin: EcoPlugin) : PluginCommand(
init {
this.addSubcommand(CommandReload(plugin))
.addSubcommand(CommandGive(plugin))
.addSubcommand(CommandInscribeDirect(plugin))
}
override fun onExecute(sender: CommandSender, args: List<String>) {

View File

@@ -5,6 +5,7 @@ import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.eco.core.drops.DropQueue
import com.willfp.eco.core.items.Items.toSNBT
import com.willfp.eco.core.items.toSNBT
import com.willfp.eco.util.StringUtils
import com.willfp.ecoscrolls.scrolls.Scrolls
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
@@ -65,9 +66,9 @@ class CommandGive(
.forceTelekinesis()
.push()
val message = plugin.langYml.getMessage("give-success")
val message = plugin.langYml.getMessage("give-success", StringUtils.FormatOption.WITHOUT_PLACEHOLDERS)
.replace("%scroll%", scroll.name)
.replace("%recipient%", reciever.name)
.replace("%player%", reciever.name)
sender.sendMessage(message)
}

View File

@@ -0,0 +1,92 @@
package com.willfp.ecoscrolls.commands
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.savedDisplayName
import com.willfp.ecoscrolls.scrolls.Scrolls
import com.willfp.ecoscrolls.scrolls.getScrollLevel
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
class CommandInscribeDirect(
plugin: EcoPlugin
) : Subcommand(
plugin,
"inscribe",
"ecoscrolls.command.incscribedirect",
false
) {
override fun onExecute(sender: CommandSender, rawArgs: List<String>) {
var args = rawArgs
var player = sender as? Player
if (sender !is Player) {
player = notifyPlayerRequired(args.getOrNull(0), "invalid-player")
args = rawArgs.subList(1, rawArgs.size)
}
if (player == null) {
return
}
val scroll = notifyNull(
args.getOrNull(0)?.lowercase()?.let { Scrolls[it] },
"invalid-scroll"
)
val item = player.inventory.itemInMainHand
val level = args.getOrNull(1)?.toIntOrNull() ?: 1
val currentLevel = item.getScrollLevel(scroll)?.level ?: 0
val levelsToAdd = level - currentLevel
repeat(levelsToAdd) {
scroll.inscribe(item)
}
sender.sendMessage(
plugin.langYml.getMessage("inscribed-item", StringUtils.FormatOption.WITHOUT_PLACEHOLDERS)
.replace("%scroll%", scroll.name)
.replace("%player%", player.savedDisplayName)
)
}
override fun tabComplete(sender: CommandSender, rawArgs: List<String>): List<String> {
val completions = mutableListOf<String>()
var args = rawArgs
if (sender !is Player) {
args = rawArgs.subList(1, rawArgs.size)
}
if (args.size == 1) {
StringUtil.copyPartialMatches(
args[0],
Scrolls.values().map { it.id },
completions
)
}
if (args.size == 2) {
val scroll = Scrolls[args[0].lowercase()]
val levels = if (scroll != null) {
val maxLevel = scroll.maxLevel
(0..maxLevel).toList()
} else {
(0..5).toList()
}
StringUtil.copyPartialMatches(
args[1],
levels.map { it.toString() },
completions
)
}
return completions
}
}

View File

@@ -58,12 +58,12 @@ internal fun updateInscribeMenu(plugin: EcoScrollsPlugin) {
val violationContext = ViolationContext(plugin, "Inscription Table")
val openEffects = Effects.compile(
val openEffects = Effects.compileChain(
plugin.configYml.getSubsections("gui.open-effects"),
violationContext.with("Open Effects")
)
val closeEffects = Effects.compile(
val closeEffects = Effects.compileChain(
plugin.configYml.getSubsections("gui.close-effects"),
violationContext.with("Close Effects")
)
@@ -139,13 +139,13 @@ internal fun updateInscribeMenu(plugin: EcoScrollsPlugin) {
}
onOpen { player, _ ->
openEffects.trigger(TriggerData(player = player).dispatch(player.toDispatcher()))
openEffects?.trigger(player.toDispatcher(), TriggerData(player = player))
}
onClose { event, menu ->
val player = event.player as Player
closeEffects.trigger(TriggerData(player = player).dispatch(player.toDispatcher()))
closeEffects?.trigger(player.toDispatcher(), TriggerData(player = player))
DropQueue(player)
.addItems(menu.getCaptiveItems(player))
@@ -299,10 +299,5 @@ private class AllowSlot(plugin: EcoScrollsPlugin) : MenuSlot(plugin, Inscription
}
}
private class DenySlot(plugin: EcoScrollsPlugin) : MenuSlot(plugin, InscriptionStatus.DENY) {
}
private class EmptySlot(plugin: EcoScrollsPlugin) : MenuSlot(plugin, InscriptionStatus.EMPTY) {
}
private class DenySlot(plugin: EcoScrollsPlugin) : MenuSlot(plugin, InscriptionStatus.DENY)
private class EmptySlot(plugin: EcoScrollsPlugin) : MenuSlot(plugin, InscriptionStatus.EMPTY)

View File

@@ -15,16 +15,14 @@ import com.willfp.libreforge.triggers.TriggerData
import com.willfp.libreforge.triggers.TriggerParameter
object EffectInscribeItem : Effect<NoCompileData>("inscribe_item") {
override val parameters = setOf(
TriggerParameter.ITEM
)
override val isPermanent = false
override val arguments = arguments {
require("scroll", "You must specify the scroll!")
}
override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean {
val item = data.item ?: return false
val item = data.foundItem ?: return false
val scroll = Scrolls[config.getString("scroll")] ?: return false

View File

@@ -3,34 +3,38 @@ package com.willfp.ecoscrolls.scrolls
import com.willfp.ecoscrolls.EcoScrollsPlugin
import com.willfp.ecoscrolls.scrolls.event.ScrollInscribeEvent
import com.willfp.ecoscrolls.scrolls.event.ScrollTryInscribeEvent
import com.willfp.ecoscrolls.target.Targets.targets
import com.willfp.libreforge.NamedValue
import com.willfp.libreforge.ViolationContext
import com.willfp.libreforge.effects.EffectList
import com.willfp.libreforge.effects.Chain
import com.willfp.libreforge.effects.Effects
import com.willfp.libreforge.toDispatcher
import com.willfp.libreforge.triggers.TriggerData
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.Optional
import kotlin.math.max
import kotlin.math.min
class InscriptionHandler(private val plugin: EcoScrollsPlugin) {
private lateinit var applyEffects: EffectList
private lateinit var denyEffects: EffectList
private lateinit var applyEffects: Optional<Chain>
private lateinit var denyEffects: Optional<Chain>
val scrollLimit = plugin.configYml.getInt("inscription.scroll-limit")
private val globalScrollLimit = plugin.configYml.getInt("inscription.scroll-limit")
.let { if (it <= 0) Int.MAX_VALUE else it }
internal fun reload() {
val context = ViolationContext(plugin, "Inscriptions")
applyEffects = Effects.compile(
applyEffects = Optional.ofNullable(Effects.compileChain(
plugin.configYml.getSubsections("inscription.apply-effects"),
context.with("Apply Effects")
)
))
denyEffects = Effects.compile(
denyEffects = Optional.ofNullable(Effects.compileChain(
plugin.configYml.getSubsections("inscription.deny-effects"),
context.with("Deny Effects")
)
))
}
private fun inscriptionTrigger(item: ItemStack, scroll: Scroll, player: Player) =
@@ -72,14 +76,33 @@ class InscriptionHandler(private val plugin: EcoScrollsPlugin) {
val didInscribe = scroll.inscribe(item, player)
if (didInscribe) {
applyEffects.trigger(inscriptionTrigger(item, scroll, player))
applyEffects.ifPresent {
it.trigger(inscriptionTrigger(item, scroll, player))
}
val event = ScrollInscribeEvent(player, scroll, item)
plugin.server.pluginManager.callEvent(event)
} else {
denyEffects.trigger(inscriptionTrigger(item, scroll, player))
denyEffects.ifPresent {
it.trigger(inscriptionTrigger(item, scroll, player))
}
}
return didInscribe
}
fun getScrollLimit(item: ItemStack): Int {
val targets = item.targets
var highest = Int.MAX_VALUE
for (target in targets) {
val limit = target.scrollLimit ?: continue
if (limit < highest) {
highest = limit
}
}
return min(highest, globalScrollLimit)
}
}

View File

@@ -9,7 +9,7 @@ import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
private val SCROLL_KEY = plugin.createNamespacedKey("scroll")
private val SCROLL_USES_KEY = plugin.createNamespacedKey("scroll_uses")
private val SCROLL_USES_LEFT_KEY = plugin.createNamespacedKey("scroll_uses_left")
private val SCROLLS_KEY = plugin.createNamespacedKey("scrolls")
private val SCROLL_ID_KEY = plugin.createNamespacedKey("scroll")
private val SCROLL_LEVEL_KEY = plugin.createNamespacedKey("level")
@@ -40,37 +40,39 @@ var PersistentDataContainer.scroll: Scroll?
this.set(SCROLL_KEY, PersistentDataType.STRING, value.id)
}
var ItemStack.scrollUses: Int
get() = this.fast().scrollUses
var ItemStack.scrollUsesLeft: Int
get() = this.fast().scrollUsesLeft
set(value) {
this.fast().scrollUses = value
this.fast().scrollUsesLeft = value
}
var FastItemStack.scrollUses: Int
get() = this.persistentDataContainer.scrollUses
var FastItemStack.scrollUsesLeft: Int
get() = this.persistentDataContainer.scrollUsesLeft
set(value) {
this.persistentDataContainer.scrollUses = value
this.persistentDataContainer.scrollUsesLeft = value
}
var PersistentDataContainer.scrollUses: Int
var PersistentDataContainer.scrollUsesLeft: Int
get() {
return this.get(SCROLL_USES_KEY, PersistentDataType.INTEGER) ?: 0
return this.get(SCROLL_USES_LEFT_KEY, PersistentDataType.INTEGER) ?: 0
}
set(value) {
if (value == 0) {
this.remove(SCROLL_USES_KEY)
this.remove(SCROLL_USES_LEFT_KEY)
return
}
this.set(SCROLL_USES_KEY, PersistentDataType.INTEGER, value)
this.set(SCROLL_USES_LEFT_KEY, PersistentDataType.INTEGER, value)
}
fun ItemStack.useScroll() {
val scroll = this.scroll ?: return
if (this.scroll == null) {
return
}
this.scrollUses++
this.scrollUsesLeft--
if (this.scrollUses >= scroll.maxUses) {
if (this.scrollUsesLeft <= 0) {
this.amount--
}
}

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.core.fast.fast
import com.willfp.eco.core.items.CustomItem
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.PlaceholderInjectable
@@ -13,7 +14,6 @@ import com.willfp.eco.core.placeholder.templates.DynamicInjectablePlaceholder
import com.willfp.eco.core.price.ConfiguredPrice
import com.willfp.eco.core.recipe.Recipes
import com.willfp.eco.core.registry.KRegistrable
import com.willfp.eco.util.evaluateExpression
import com.willfp.eco.util.evaluateExpressionOrNull
import com.willfp.eco.util.formatEco
import com.willfp.eco.util.toNumeral
@@ -29,7 +29,6 @@ import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.Objects
import java.util.regex.Pattern
import kotlin.math.max
class Scroll(
plugin: EcoScrollsPlugin,
@@ -65,12 +64,19 @@ class Scroll(
fis.scroll = this
fis.displayName = itemName.formatEco()
fis.lore = itemLore.formatEco().map { Display.PREFIX + it } + fis.lore
fis.scrollUsesLeft = maxUses
fis.unwrap()
}
val item: ItemStack
get() = _item.clone()
val customItem = CustomItem(
plugin.createNamespacedKey("scroll_$id"),
{ it.scroll == this },
_item
).apply { register() }
val recipe = if (config.getBool("item.craftable")) Recipes.createAndRegisterRecipe(
plugin,
id,
@@ -98,7 +104,7 @@ class Scroll(
context.with("inscription conditions")
)
private val inscriptionEffects = Effects.compile(
private val inscriptionEffects = Effects.compileChain(
config.getSubsections("inscription.effects"),
context.with("inscription effects")
)
@@ -131,15 +137,15 @@ class Scroll(
private val usesLeftPlaceholder = object : DynamicInjectablePlaceholder(Pattern.compile("uses_left")) {
override fun getValue(p0: String, p1: PlaceholderContext): String? {
val item = p1.itemStack ?: return null
val scroll = item.scroll ?: return null
return (scroll.maxUses - item.scrollUses).toString()
return item.scrollUsesLeft.toString()
}
}
private val usesPlaceholder = object : DynamicInjectablePlaceholder(Pattern.compile("uses")) {
override fun getValue(p0: String, p1: PlaceholderContext): String? {
val item = p1.itemStack ?: return null
return item.scrollUses.toString()
val scroll = item.scroll ?: return null
return (scroll.maxUses - item.scrollUsesLeft).toString()
}
}
@@ -187,7 +193,7 @@ class Scroll(
val currentScrolls = itemStack.scrolls
if (currentScrolls.size >= plugin.inscriptionHandler.scrollLimit) {
if (currentScrolls.size >= plugin.inscriptionHandler.getScrollLimit(itemStack)) {
return false
}
@@ -231,12 +237,13 @@ class Scroll(
inscribe(itemStack)
inscriptionEffects.trigger(
inscriptionEffects?.trigger(
player.toDispatcher(),
TriggerData(
player = player,
item = itemStack,
value = itemStack.getScrollLevel(this)?.level?.toDouble() ?: 1.0
).dispatch(player.toDispatcher())
)
)
return true

View File

@@ -17,6 +17,7 @@ interface Target : Registrable {
val displayName: String
val slot: SlotType
val items: List<TestableItem>
val scrollLimit: Int?
fun matches(itemStack: ItemStack): Boolean {
for (item in items) {
@@ -45,6 +46,8 @@ class ConfiguredTarget(
.map { Items.lookup(it) }
.filterNot { it is EmptyTestableItem }
override val scrollLimit = config.getIntOrNull("scroll-limit")
override fun equals(other: Any?): Boolean {
if (other !is Target) {
return false
@@ -64,6 +67,7 @@ internal object AllTarget : Target {
override val slot = SlotTypeAny
override var items = emptyList<TestableItem>()
private set
override val scrollLimit = null
fun updateItems() {
items = Targets.values()

View File

@@ -5,9 +5,6 @@ import com.willfp.eco.core.items.HashedItem
import com.willfp.eco.core.registry.Registry
import com.willfp.ecoscrolls.EcoScrollsPlugin
import com.willfp.ecoscrolls.plugin
import com.willfp.ecoscrolls.scrolls.Scroll
import com.willfp.ecoscrolls.scrolls.Scrolls
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
import java.util.concurrent.TimeUnit
@@ -23,14 +20,9 @@ object Targets : Registry<Target>() {
.filter { it.matches(item) }
}
val ItemStack.isModifiable: Boolean
get() = modifiableCache.get(HashedItem.of(this)) {
getForItem(this).isNotEmpty() || this.type == Material.BOOK || this.type == Material.ENCHANTED_BOOK
}
val ItemStack.applicableScrolls: List<Scroll>
get() = canModifyCache.get(HashedItem.of(this)) {
Scrolls.values().filter { it.canInscribe(this) }
val ItemStack.targets: List<Target>
get() = targetsCache.get(HashedItem.of(this)) {
getForItem(this)
}
internal fun update(plugin: EcoScrollsPlugin) {
@@ -49,10 +41,6 @@ object Targets : Registry<Target>() {
}
}
private val modifiableCache = Caffeine.newBuilder()
private val targetsCache = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.SECONDS)
.build<HashedItem, Boolean>()
private val canModifyCache = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.SECONDS)
.build<HashedItem, List<Scroll>>()
.build<HashedItem, List<Target>>()

View File

@@ -3,6 +3,6 @@ environment:
value: ${libreforgeVersion}
options:
resource-id: 0
resource-id: 6131
bstats-id: 22538
color: "&#1e3c72"

View File

@@ -10,4 +10,4 @@ messages:
needs-scroll: "&cYou must specify a scroll!"
invalid-player: "&cYou must specify a player!"
invalid-scroll: "&cYou must specify a scroll!"
give-success: "Gave %scroll%&r to %recipient%&r!"
give-success: "Gave %scroll%&r to %player%&r!"

View File

@@ -32,6 +32,7 @@ permissions:
ecoscrolls.command.reload: true
ecoscrolls.command.inscribe: true
ecoscrolls.command.give: true
ecoscrolls.command.inscribedirect: true
ecoscrolls.command.reload:
description: Allows reloading the config
@@ -44,3 +45,7 @@ permissions:
ecoscrolls.command.inscribe:
description: Allows opening the inscription table with /inscribe
default: true
ecoscrolls.command.inscribedirect:
description: Allows directly inscribing an item with /ecoscrolls inscribe
default: op

View File

@@ -22,6 +22,8 @@ item:
lore:
- "&7This is an example scroll."
- "&7It does nothing."
# Options for crafting, read here: https://plugins.auxilor.io/all-plugins/the-item-lookup-system#crafting-recipes
craftable: false
recipe: [ ]
@@ -32,6 +34,8 @@ inscription:
conditions: [ ]
# The effects that will be run when the item is inscribed
# If your scroll works by modifying the item (e.g. adding enchantments, changing durability),
# then put those effects here.
effects: [ ]
# Read https://plugins.auxilor.io/all-plugins/prices
@@ -73,7 +77,8 @@ lore:
- "&6Example Scroll"
# Item placeholders for dynamic lore in plugins like EcoItems
# The placeholder is %ecoscrolls_scroll_<scroll>_<placeholder>%
# The placeholder is %ecoscrolls_scroll_<scroll>:<placeholder>%, e.g.
# %ecoscrolls_scroll_example:bonus%
placeholders:
bonus: "%level% * 2"

View File

@@ -0,0 +1,94 @@
# The ID of the scroll is the name of the .yml file,
# for example coins_on_kill.yml has the ID of coins_on_kill
# You can place scrolls anywhere in this folder,
# including in subfolders if you want to organize your scroll configs
# _example.yml is not loaded.
# The name of the scroll
name: "&cHot Potato Book"
max-level: 10
max-uses: 1
item:
item: book unbreaking:1 hide_enchants
name: "&cHot Potato Book"
lore:
- "&7When inscribed on armor, grants"
- "&f+2 %ecoskills_defense_name% &7and &f+1 %ecoskills_health_name%&7."
- ""
- "&7When inscribed to weapons, grants"
- "&f+2 %ecoskills_strength_name% &7and &f+4 %ecoskills_crit_damage_name%&7."
- ""
- "&7This can be inscribed up to &a10 &7times"
- "&7on an item!"
craftable: false
recipe: [ ]
inscription:
conditions: [ ]
effects: [ ]
price:
value: 100
type: coins
display: "&e%value% coins"
price-level-multiplier: "1 + %level% * 0.5"
drag-and-drop: false
inscription-table: true
targets:
- sword
- axe
- armor
placeholders:
# For EcoItems items, use %ecoscrolls_scroll_hot_potato_book:defense% in lore
defense: "&8(&e+{^{%level% * 2}}&8)"
# Same as above, but for health / strength / crit_damage
health: "&8(&e+{^{%level%}}&8)"
strength: "&8(&e+{^{%level% * 2}}&8)"
crit_damage: "&8(&e+{^{%level% * 4}}&8)"
effects:
# Armor effects
- id: add_stat
args:
stat: defense
amount: "%level% * 2"
conditions:
- id: in_slot
args:
slot: armor
- id: add_stat
args:
stat: health
amount: "%level%"
conditions:
- id: in_slot
args:
slot: armor
# Weapon effects
- id: add_stat
args:
stat: strength
amount: "%level% * 2"
conditions:
- id: in_slot
args:
slot: mainhand
- id: add_stat
args:
stat: crit_damage
amount: "%level% * 4"
conditions:
- id: in_slot
args:
slot: mainhand
conditions: [ ]

View File

@@ -8,10 +8,15 @@
# a target with slot "mainhand", the scroll will activate whenever the item is
# in your mainhand.
# You can also specify a scroll limit for a target, for example you can limit
# pickaxes to only have 3 scrolls inscribed on them.
# If an item has multiple targets, the lowest limit will be used.
targets:
- id: pickaxe
display-name: "Pickaxes"
slot: mainhand
# scroll-limit: 10 # Example of a scroll limit
items:
- "*wooden_pickaxe"
- "*stone_pickaxe"

View File

@@ -1,5 +1,5 @@
#libreforge-updater
#Mon Jul 08 15:58:38 BST 2024
#Fri Jul 19 20:28:49 BST 2024
kotlin.code.style=official
libreforge-version=4.65.0
version=1.1.0
libreforge-version=4.68.0
version=1.2.0