9
0
mirror of https://github.com/Auxilor/EcoSkills.git synced 2026-01-02 22:02:19 +00:00

Reimplemented xp gain display, libreforge effects, and skill crits

This commit is contained in:
Auxilor
2023-05-11 14:41:39 +01:00
parent bb9007e0de
commit 63da63c7d7
26 changed files with 882 additions and 61 deletions

View File

@@ -8,15 +8,31 @@ import com.willfp.ecoskills.actionbar.ActionBarHandler
import com.willfp.ecoskills.commands.CommandEcoSkills
import com.willfp.ecoskills.commands.CommandSkills
import com.willfp.ecoskills.effects.Effects
import com.willfp.ecoskills.libreforge.ConditionHasSkillLevel
import com.willfp.ecoskills.libreforge.EffectAddStat
import com.willfp.ecoskills.libreforge.EffectAddStatTemporarily
import com.willfp.ecoskills.libreforge.EffectGiveSkillXp
import com.willfp.ecoskills.libreforge.EffectMultiplyAllStats
import com.willfp.ecoskills.libreforge.EffectMultiplyStat
import com.willfp.ecoskills.libreforge.EffectMultiplyStatTemporarily
import com.willfp.ecoskills.libreforge.EffectSkillXpMultiplier
import com.willfp.ecoskills.libreforge.FilterSkill
import com.willfp.ecoskills.libreforge.TriggerGainSkillXp
import com.willfp.ecoskills.libreforge.TriggerLevelUpSkill
import com.willfp.ecoskills.skills.EcoSkillsTopPlaceholder
import com.willfp.ecoskills.skills.Skill
import com.willfp.ecoskills.skills.SkillLevelUpListener
import com.willfp.ecoskills.skills.Skills
import com.willfp.ecoskills.skills.display.DamageIndicatorListener
import com.willfp.ecoskills.skills.display.GainXPDisplay
import com.willfp.ecoskills.skills.display.LevelUpDisplay
import com.willfp.ecoskills.skills.display.TemporaryBossBarHandler
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.SimpleProvidedHolder
import com.willfp.libreforge.conditions.Conditions
import com.willfp.libreforge.filters.Filters
import com.willfp.libreforge.loader.LibreforgePlugin
import com.willfp.libreforge.loader.configs.ConfigCategory
import com.willfp.libreforge.registerHolderProvider
import com.willfp.libreforge.triggers.Triggers
import org.bukkit.event.Listener
internal lateinit var plugin: EcoSkillsPlugin
@@ -42,11 +58,24 @@ class EcoSkillsPlugin : LibreforgePlugin() {
}
override fun handleEnable() {
com.willfp.libreforge.effects.Effects.register(EffectAddStat)
com.willfp.libreforge.effects.Effects.register(EffectMultiplyStat)
com.willfp.libreforge.effects.Effects.register(EffectSkillXpMultiplier)
com.willfp.libreforge.effects.Effects.register(EffectGiveSkillXp)
com.willfp.libreforge.effects.Effects.register(EffectMultiplyAllStats)
com.willfp.libreforge.effects.Effects.register(EffectAddStatTemporarily(this))
com.willfp.libreforge.effects.Effects.register(EffectMultiplyStatTemporarily(this))
Conditions.register(ConditionHasSkillLevel)
Triggers.register(TriggerGainSkillXp)
Triggers.register(TriggerLevelUpSkill)
Filters.register(FilterSkill)
EcoSkillsTopPlaceholder(this).register()
}
override fun handleReload() {
ActionBarHandler(this).beginTickingActionBar()
ActionBarHandler(this).startTicking()
TemporaryBossBarHandler(this).startTicking()
}
override fun loadPluginCommands(): List<PluginCommand> {
@@ -58,7 +87,9 @@ class EcoSkillsPlugin : LibreforgePlugin() {
override fun loadListeners(): List<Listener> {
return listOf(
SkillLevelUpListener(this),
LevelUpDisplay(this),
GainXPDisplay(this),
DamageIndicatorListener(this),
ActionBarGamemodeListener
)
}

View File

@@ -18,6 +18,15 @@ private val blacklist = mutableMapOf<UUID, Long>()
private val whitelist = mutableMapOf<UUID, Long>()
fun Player.sendCompatibleActionBarMessage(message: String) {
// Have to use the shit method for compatibility.
@Suppress("DEPRECATION")
this.spigot().sendMessage(
net.md_5.bungee.api.ChatMessageType.ACTION_BAR,
*net.md_5.bungee.api.chat.TextComponent.fromLegacyText(message)
)
}
fun Player.pausePersistentActionBar() {
if (isSendingPersistentActionBar) {
return
@@ -32,15 +41,9 @@ private val Player.isPersistentActionBarPaused: Boolean
return time > System.currentTimeMillis()
}
private fun Player.sendPersistentActionBar(string: String) {
private fun Player.sendPersistentActionBar(message: String) {
whitelist[this.uniqueId] = System.currentTimeMillis() + TICK_DURATION
// Have to use the shit method for compatibility.
@Suppress("DEPRECATION")
this.spigot().sendMessage(
net.md_5.bungee.api.ChatMessageType.ACTION_BAR,
*net.md_5.bungee.api.chat.TextComponent.fromLegacyText(string)
)
sendCompatibleActionBarMessage(message)
}
private val Player.isSendingPersistentActionBar: Boolean
@@ -53,11 +56,7 @@ object ActionBarGamemodeListener : Listener {
@EventHandler
fun handle(event: PlayerGameModeChangeEvent) {
if (event.newGameMode in setOf(GameMode.CREATIVE, GameMode.SPECTATOR)) {
@Suppress("DEPRECATION")
event.player.spigot().sendMessage(
net.md_5.bungee.api.ChatMessageType.ACTION_BAR,
*net.md_5.bungee.api.chat.TextComponent.fromLegacyText("")
)
event.player.sendCompatibleActionBarMessage("")
}
}
}
@@ -96,7 +95,7 @@ class ActionBarHandler(
)
}
internal fun beginTickingActionBar() {
internal fun startTicking() {
plugin.scheduler.runTimer(5, 5) {
for (player in Bukkit.getOnlinePlayers()) {
trySendMessage(player)

View File

@@ -33,6 +33,9 @@ fun OfflinePlayer.giveSkillXP(skill: Skill, xp: Double): Unit =
fun OfflinePlayer.getRequiredXP(skill: Skill) =
skill.getXPRequired(this.getSkillLevel(skill))
fun OfflinePlayer.getFormattedRequiredXP(skill: Skill) =
skill.getFormattedXPRequired(this.getSkillLevel(skill))
fun OfflinePlayer.getSkillProgress(skill: Skill): Double {
val currentXP = getSkillXP(skill)
val requiredXP = getRequiredXP(skill)

View File

@@ -8,7 +8,7 @@ import org.bukkit.event.HandlerList
class PlayerSkillXPGainEvent(
who: Player,
skill: Skill,
var xp: Double
var gainedXP: Double
) : PlayerSkillEvent(who, skill), Cancellable {
private var _cancelled = false

View File

@@ -0,0 +1,31 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.event.PlayerSkillLevelUpEvent
import com.willfp.ecoskills.api.getSkillLevel
import com.willfp.ecoskills.skills.Skills
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.arguments
import com.willfp.libreforge.conditions.Condition
import com.willfp.libreforge.updateEffects
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
object ConditionHasSkillLevel : Condition<NoCompileData>("has_skill_level") {
override val arguments = arguments {
require("skill", "You must specify the skill!")
require("level", "You must specify the skill level!")
}
override fun isMet(player: Player, config: Config, compileData: NoCompileData): Boolean {
val skill = Skills.getByID(config.getString("skill").lowercase()) ?: return false
return player.getSkillLevel(skill) >= config.getIntFromExpression("level", player)
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun handle(event: PlayerSkillLevelUpEvent) {
event.player.updateEffects()
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.addStatModifier
import com.willfp.ecoskills.api.modifiers.ModifierOperation
import com.willfp.ecoskills.api.modifiers.StatModifier
import com.willfp.ecoskills.api.removeStatModifier
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.ProvidedHolder
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.effects.Identifiers
import org.bukkit.entity.Player
object EffectAddStat : Effect<NoCompileData>("add_stat") {
override val arguments = arguments {
require("stat", "You must specify the stat!")
require("amount", "You must specify the amount to add/remove!")
}
override fun onEnable(
player: Player,
config: Config,
identifiers: Identifiers,
holder: ProvidedHolder,
compileData: NoCompileData
) {
val stat = Stats.getByID(config.getString("stat")) ?: return
player.addStatModifier(
StatModifier(
identifiers.uuid,
stat,
config.getDoubleFromExpression("amount", player),
ModifierOperation.ADD
)
)
}
override fun onDisable(player: Player, identifiers: Identifiers, holder: ProvidedHolder) {
player.removeStatModifier(identifiers.uuid)
}
}

View File

@@ -0,0 +1,54 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.util.NumberUtils
import com.willfp.ecoskills.api.addStatModifier
import com.willfp.ecoskills.api.modifiers.ModifierOperation
import com.willfp.ecoskills.api.modifiers.StatModifier
import com.willfp.ecoskills.api.removeStatModifier
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.getDoubleFromExpression
import com.willfp.libreforge.getIntFromExpression
import com.willfp.libreforge.triggers.TriggerData
import com.willfp.libreforge.triggers.TriggerParameter
import java.util.UUID
class EffectAddStatTemporarily(
private val plugin: EcoPlugin
) : Effect<NoCompileData>("add_stat_temporarily") {
override val parameters = setOf(
TriggerParameter.PLAYER
)
override val arguments = arguments {
require("stat", "You must specify the stat!")
require("amount", "You must specify the amount to add/remove!")
require("duration", "You must specify the duration for the boost!")
}
override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean {
val player = data.player ?: return false
val stat = Stats.getByID(config.getString("stat")) ?: return false
val amount = config.getDoubleFromExpression("amount", data)
val uuid = UUID.nameUUIDFromBytes("ast_${NumberUtils.randInt(0, 1000000)}".toByteArray())
player.addStatModifier(
StatModifier(
uuid,
stat,
amount,
ModifierOperation.ADD
)
)
plugin.scheduler.runLater(config.getIntFromExpression("duration", data).toLong()) {
player.removeStatModifier(uuid)
}
return true
}
}

View File

@@ -0,0 +1,32 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.giveSkillXP
import com.willfp.ecoskills.skills.Skills
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.getDoubleFromExpression
import com.willfp.libreforge.triggers.TriggerData
import com.willfp.libreforge.triggers.TriggerParameter
object EffectGiveSkillXp : Effect<NoCompileData>("give_skill_xp") {
override val parameters = setOf(
TriggerParameter.PLAYER
)
override val arguments = arguments {
require("amount", "You must specify the amount of xp to give!")
require("skill", "You must specify the skill to give xp for!")
}
override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean {
val player = data.player ?: return false
val skill = Skills.getByID(config.getString("skill")) ?: return false
player.giveSkillXP(skill, config.getDoubleFromExpression("amount", data))
return true
}
}

View File

@@ -0,0 +1,40 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.addStatModifier
import com.willfp.ecoskills.api.modifiers.ModifierOperation
import com.willfp.ecoskills.api.modifiers.StatModifier
import com.willfp.ecoskills.api.removeStatModifier
import com.willfp.ecoskills.skills.skillCrit
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.ProvidedHolder
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.effects.Identifiers
import com.willfp.libreforge.effects.RunOrder
import com.willfp.libreforge.getDoubleFromExpression
import com.willfp.libreforge.triggers.TriggerData
import com.willfp.libreforge.triggers.TriggerParameter
import org.bukkit.entity.Player
import org.bukkit.event.entity.EntityDamageByEntityEvent
object EffectMakeSkillCrit : Effect<NoCompileData>("make_skill_crit") {
override val runOrder = RunOrder.EARLY
override val arguments = arguments {
require("multiplier", "You must specify the damage multiplier!")
}
override val parameters = setOf(
TriggerParameter.EVENT
)
override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean {
val event = data.event as? EntityDamageByEntityEvent ?: return false
event.skillCrit *= config.getDoubleFromExpression("multiplier", data)
return true
}
}

View File

@@ -0,0 +1,49 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.addStatModifier
import com.willfp.ecoskills.api.modifiers.ModifierOperation
import com.willfp.ecoskills.api.modifiers.StatModifier
import com.willfp.ecoskills.api.removeStatModifier
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.ProvidedHolder
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.effects.Identifiers
import org.bukkit.entity.Player
object EffectMultiplyAllStats : Effect<NoCompileData>("multiply_all_stats") {
override val arguments = arguments {
require("multiplier", "You must specify the multiplier!")
}
override fun onEnable(
player: Player,
config: Config,
identifiers: Identifiers,
holder: ProvidedHolder,
compileData: NoCompileData
) {
val factory = identifiers.makeFactory()
for ((offset, stat) in Stats.values().withIndex()) {
player.addStatModifier(
StatModifier(
factory.makeIdentifiers(offset).uuid,
stat,
config.getDoubleFromExpression("multiplier", player),
ModifierOperation.MULTIPLY
)
)
}
}
override fun onDisable(player: Player, identifiers: Identifiers, holder: ProvidedHolder) {
val factory = identifiers.makeFactory()
for (offset in Stats.values().indices) {
player.removeStatModifier(factory.makeIdentifiers(offset).uuid)
}
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.addStatModifier
import com.willfp.ecoskills.api.modifiers.ModifierOperation
import com.willfp.ecoskills.api.modifiers.StatModifier
import com.willfp.ecoskills.api.removeStatModifier
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.ProvidedHolder
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.effects.Identifiers
import org.bukkit.entity.Player
object EffectMultiplyStat : Effect<NoCompileData>("multiply_stat") {
override val arguments = arguments {
require("stat", "You must specify the stat!")
require("multiplier", "You must specify the multiplier!")
}
override fun onEnable(
player: Player,
config: Config,
identifiers: Identifiers,
holder: ProvidedHolder,
compileData: NoCompileData
) {
val stat = Stats.getByID(config.getString("stat")) ?: return
player.addStatModifier(
StatModifier(
identifiers.uuid,
stat,
config.getDoubleFromExpression("multiplier", player),
ModifierOperation.MULTIPLY
)
)
}
override fun onDisable(player: Player, identifiers: Identifiers, holder: ProvidedHolder) {
player.removeStatModifier(identifiers.uuid)
}
}

View File

@@ -0,0 +1,54 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.util.NumberUtils
import com.willfp.ecoskills.api.addStatModifier
import com.willfp.ecoskills.api.modifiers.ModifierOperation
import com.willfp.ecoskills.api.modifiers.StatModifier
import com.willfp.ecoskills.api.removeStatModifier
import com.willfp.ecoskills.stats.Stats
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.arguments
import com.willfp.libreforge.effects.Effect
import com.willfp.libreforge.getDoubleFromExpression
import com.willfp.libreforge.getIntFromExpression
import com.willfp.libreforge.triggers.TriggerData
import com.willfp.libreforge.triggers.TriggerParameter
import java.util.UUID
class EffectMultiplyStatTemporarily(
private val plugin: EcoPlugin
) : Effect<NoCompileData>("add_stat_temporarily") {
override val parameters = setOf(
TriggerParameter.PLAYER
)
override val arguments = arguments {
require("stat", "You must specify the stat!")
require("amount", "You must specify the amount to add/remove!")
require("duration", "You must specify the duration for the boost!")
}
override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean {
val player = data.player ?: return false
val stat = Stats.getByID(config.getString("stat")) ?: return false
val multiplier = config.getDoubleFromExpression("multiplier", data)
val uuid = UUID.nameUUIDFromBytes("mst_${NumberUtils.randInt(0, 1000000)}".toByteArray())
player.addStatModifier(
StatModifier(
uuid,
stat,
multiplier,
ModifierOperation.MULTIPLY
)
)
plugin.scheduler.runLater(config.getIntFromExpression("duration", data).toLong()) {
player.removeStatModifier(uuid)
}
return true
}
}

View File

@@ -0,0 +1,26 @@
package com.willfp.ecoskills.libreforge
import com.willfp.ecoskills.api.event.PlayerSkillXPGainEvent
import com.willfp.ecoskills.skills.Skill
import com.willfp.ecoskills.skills.Skills
import com.willfp.libreforge.effects.templates.MultiMultiplierEffect
import org.bukkit.event.EventHandler
object EffectSkillXpMultiplier : MultiMultiplierEffect<Skill>("skill_xp_multiplier") {
override val key = "skills"
override fun getElement(key: String): Skill? {
return Skills.getByID(key)
}
override fun getAllElements(): Collection<Skill> {
return Skills.values()
}
@EventHandler(ignoreCancelled = true)
fun handle(event: PlayerSkillXPGainEvent) {
val player = event.player
event.gainedXP *= getMultiplier(player, event.skill)
}
}

View File

@@ -0,0 +1,21 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.api.event.SkillEvent
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.filters.Filter
import com.willfp.libreforge.triggers.TriggerData
object FilterSkill : Filter<NoCompileData, Collection<String>>("skill") {
override fun getValue(config: Config, data: TriggerData?, key: String): Collection<String> {
return config.getStrings(key)
}
override fun isMet(data: TriggerData, value: Collection<String>, compileData: NoCompileData): Boolean {
val event = data.event as? SkillEvent ?: return true
return value.any { skillName ->
skillName.equals(event.skill.id, ignoreCase = true)
}
}
}

View File

@@ -0,0 +1,20 @@
package com.willfp.ecoskills.libreforge
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoskills.skills.isSkillCrit
import com.willfp.libreforge.NoCompileData
import com.willfp.libreforge.filters.Filter
import com.willfp.libreforge.triggers.TriggerData
import org.bukkit.event.entity.EntityDamageByEntityEvent
object FilterSkillCrit : Filter<NoCompileData, Boolean>("skill_crit") {
override fun getValue(config: Config, data: TriggerData?, key: String): Boolean {
return config.getBool(key)
}
override fun isMet(data: TriggerData, value: Boolean, compileData: NoCompileData): Boolean {
val event = data.event as? EntityDamageByEntityEvent ?: return true
return event.isSkillCrit == value
}
}

View File

@@ -0,0 +1,30 @@
package com.willfp.ecoskills.libreforge
import com.willfp.ecoskills.api.event.PlayerSkillXPGainEvent
import com.willfp.libreforge.triggers.Trigger
import com.willfp.libreforge.triggers.TriggerData
import com.willfp.libreforge.triggers.TriggerParameter
import org.bukkit.event.EventHandler
object TriggerGainSkillXp : Trigger("gain_skill_xp") {
override val parameters = setOf(
TriggerParameter.PLAYER,
TriggerParameter.LOCATION,
TriggerParameter.EVENT
)
@EventHandler(ignoreCancelled = true)
fun handle(event: PlayerSkillXPGainEvent) {
val player = event.player
this.dispatch(
player,
TriggerData(
player = player,
location = player.location,
event = event,
value = event.gainedXP
)
)
}
}

View File

@@ -11,6 +11,7 @@ import com.willfp.eco.util.formatEco
import com.willfp.eco.util.toNiceString
import com.willfp.eco.util.toNumeral
import com.willfp.ecoskills.EcoSkillsPlugin
import com.willfp.ecoskills.api.getFormattedRequiredXP
import com.willfp.ecoskills.api.getRequiredXP
import com.willfp.ecoskills.api.getSkillLevel
import com.willfp.ecoskills.api.getSkillProgress
@@ -91,7 +92,7 @@ class Skill(
}.register()
PlayerPlaceholder(plugin, "${id}_required_xp") {
getXPRequired(it.getSkillLevel(this)).toNiceString()
getFormattedXPRequired(it.getSkillLevel(this))
}.register()
PlayerPlaceholder(plugin, "${id}_percentage_progress") {
@@ -100,7 +101,7 @@ class Skill(
}
override fun onRegister() {
val accumulator = SkillXPAccumulator(this)
val accumulator = SkillXPAccumulator(plugin, this)
for (counter in xpGainMethods) {
counter.bind(accumulator)
@@ -133,6 +134,15 @@ class Skill(
return Double.POSITIVE_INFINITY
}
fun getFormattedXPRequired(level: Int): String {
val required = getXPRequired(level)
return if (required.isInfinite()) {
plugin.langYml.getFormattedString("infinity")
} else {
required.toNiceString()
}
}
/**
* Add skill placeholders into [strings], to be shown to a [player].
*/
@@ -145,38 +155,38 @@ class Skill(
val withPlaceholders = strings.map { s ->
s.replace("%percentage_progress%", (player.getSkillProgress(this) * 100).toNiceString())
.replace("%current_xp%", player.getSkillXP(this).toNiceString())
.replace("%required_xp%", player.getRequiredXP(this).let { req ->
if (req.isInfinite()) {
plugin.langYml.getFormattedString("infinity")
} else {
req.toNiceString()
}
})
.replace("%required_xp%", player.getFormattedRequiredXP(this))
.replace("%description%", this.getDescription(player))
.replace("%skill%", this.name)
.replace("%level%", level.toString())
.replace("%level_numeral%", level.toNumeral())
}
// Replace the placeholder "%rewards%" with level up messages.
val processed = withPlaceholders.map { s ->
// Replace multi-line placeholders.
val processed = withPlaceholders.flatMap { s ->
val margin = s.count { it == ' ' }
if (s.contains("%rewards%")) {
getRewardMessages(level, margin)
getRewardMessages(level).addMargin(margin)
} else if (s.contains("%gui_lore%")) {
config.getStrings("gui.lore").addMargin(margin)
} else {
listOf(s)
}
}
return processed.flatten().formatEco(player)
return processed.formatEco(player)
}
private fun List<String>.addMargin(margin: Int): List<String> {
return this.map { s -> " ".repeat(margin) + s }
}
/**
* Get the reward messages for a certain [level], with a [margin].
* Get the reward messages for a certain [level].
*/
private fun getRewardMessages(
level: Int,
margin: Int = 0
level: Int
): List<String> = rewardMessages.getOrPut(level) {
// Determine the highest level of messages from the config that is not greater than the provided level.
val highestConfiguredLevel = config.getSubsection("reward-messages")
@@ -195,7 +205,7 @@ class Skill(
placeholderContext(
injectable = LevelInjectable(level)
)
).map { " ".repeat(margin) + it }
)
}
internal fun handleLevelUp(player: OfflinePlayer, level: Int) {

View File

@@ -0,0 +1,15 @@
package com.willfp.ecoskills.skills
import org.bukkit.event.entity.EntityDamageByEntityEvent
import java.util.WeakHashMap
private val map = WeakHashMap<EntityDamageByEntityEvent, Double>()
var EntityDamageByEntityEvent.skillCrit: Double
get() = map[this] ?: 1.0
set(value) {
map[this] = value
}
val EntityDamageByEntityEvent.isSkillCrit: Boolean
get() = map.containsKey(this)

View File

@@ -72,7 +72,7 @@ class SkillLevelMap(
Bukkit.getPluginManager().callEvent(event)
giveXP(skill, event.xp)
giveXP(skill, event.gainedXP)
} else {
giveXP(skill, xp)
}

View File

@@ -1,13 +1,20 @@
package com.willfp.ecoskills.skills
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.afk.AFKManager
import com.willfp.ecoskills.api.gainSkillXP
import com.willfp.libreforge.counters.Accumulator
import org.bukkit.entity.Player
class SkillXPAccumulator(
private val plugin: EcoPlugin,
private val skill: Skill
) : Accumulator {
override fun accept(player: Player, count: Double) {
if (plugin.configYml.getBool("skills.prevent-levelling-while-afk") && AFKManager.isAfk(player)) {
return
}
player.gainSkillXP(skill, count)
}
}

View File

@@ -0,0 +1,110 @@
package com.willfp.ecoskills.skills.display
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.Prerequisite
import com.willfp.eco.core.integrations.hologram.HologramManager
import com.willfp.eco.util.NumberUtils
import com.willfp.eco.util.formatEco
import com.willfp.eco.util.toNiceString
import com.willfp.ecoskills.skills.isSkillCrit
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.entity.Allay
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntityRegainHealthEvent
class DamageIndicatorListener(
private val plugin: EcoPlugin
) : Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) {
if (!plugin.configYml.getBool("damage-indicators.enabled")) {
return
}
val victim = event.entity
if (event.entity == event.damager) {
return
}
if (event.damage == 0.0) {
return
}
if (victim is Player && victim.isBlocking) {
return
}
val location = victim.location.clone()
.add(0.0, victim.height, 0.0)
.withHoloOffset()
var text: String = if (event.isSkillCrit) {
plugin.configYml.getString("damage-indicators.format.crit")
} else {
plugin.configYml.getString("damage-indicators.format.normal")
}
text = if (plugin.configYml.getBool("damage-indicators.final-damage")) {
text.replace("%damage%", NumberUtils.format(event.finalDamage))
} else {
text.replace("%damage%", NumberUtils.format(event.damage))
}.formatEco()
val holo = HologramManager.createHologram(location, listOf(text))
plugin.scheduler.runLater(30) {
holo.remove()
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onHeal(event: EntityRegainHealthEvent) {
if (!plugin.configYml.getBool("damage-indicators.healing.enabled")) {
return
}
val entity = event.entity
if (entity is Player && entity.gameMode == GameMode.SPECTATOR) {
return
}
if (Prerequisite.HAS_1_19.isMet) {
if (entity is Allay) {
return
}
}
val location = entity.location.clone()
.add(0.0, entity.height, 0.0)
.withHoloOffset()
val text = plugin.configYml.getString("damage-indicators.healing.format")
.replace("%damage%", event.amount.toNiceString())
.formatEco()
val holo = HologramManager.createHologram(location, listOf(text))
plugin.scheduler.runLater(30) {
holo.remove()
}
}
private fun Location.withHoloOffset(): Location {
val x = plugin.configYml.getDouble("damage-indicators.max-x-offset")
val y = plugin.configYml.getDouble("damage-indicators.max-y-offset")
val z = plugin.configYml.getDouble("damage-indicators.max-z-offset")
return this.add(
NumberUtils.randFloat(-x, x),
NumberUtils.randFloat(-y, y),
NumberUtils.randFloat(-z, z)
)
}
}

View File

@@ -0,0 +1,80 @@
package com.willfp.ecoskills.skills.display
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.sound.PlayableSound
import com.willfp.eco.util.formatEco
import com.willfp.eco.util.toNiceString
import com.willfp.ecoskills.actionbar.sendCompatibleActionBarMessage
import com.willfp.ecoskills.api.event.PlayerSkillXPGainEvent
import com.willfp.ecoskills.api.getFormattedRequiredXP
import com.willfp.ecoskills.api.getRequiredXP
import com.willfp.ecoskills.api.getSkillProgress
import com.willfp.ecoskills.api.getSkillXP
import org.bukkit.boss.BarColor
import org.bukkit.boss.BarStyle
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
class GainXPDisplay(
private val plugin: EcoPlugin
) : Listener {
private val sound = if (plugin.configYml.getBool("skills.gain-xp.sound.enabled")) {
PlayableSound.create(
plugin.configYml.getSubsection("skills.gain-xp.sound")
)
} else null
@EventHandler
fun handle(event: PlayerSkillXPGainEvent) {
val player = event.player
// Run next tick because level up calls before xp is added
plugin.scheduler.run {
handleActionBar(event)
handleBossBar(event)
sound?.playTo(player)
}
}
private fun handleBossBar(event: PlayerSkillXPGainEvent) {
if (!plugin.configYml.getBool("skills.gain-xp.boss-bar.enabled")) {
return
}
val player = event.player
val skill = event.skill
val message = plugin.configYml.getString("skills.gain-xp.boss-bar.message")
.formatMessage(event)
player.sendTemporaryBossBar(
message,
event.skill.id,
this.plugin.configYml.getInt("skills.progress.boss-bar.duration"),
BarColor.valueOf(this.plugin.configYml.getString("skills.progress.boss-bar.color").uppercase()),
BarStyle.valueOf(this.plugin.configYml.getString("skills.progress.boss-bar.style").uppercase()),
player.getSkillProgress(skill).coerceIn(0.0..1.0)
)
}
private fun handleActionBar(event: PlayerSkillXPGainEvent) {
if (!plugin.configYml.getBool("skills.gain-xp.action-bar.enabled")) {
return
}
val player = event.player
val message = plugin.configYml.getString("skills.gain-xp.action-bar.message")
.formatMessage(event)
player.sendCompatibleActionBarMessage(message)
}
private fun String.formatMessage(event: PlayerSkillXPGainEvent): String =
this.replace("%skill%", event.skill.name)
.replace("%current_xp%", event.player.getSkillXP(event.skill).toNiceString())
.replace("%required_xp%", event.player.getFormattedRequiredXP(event.skill))
.replace("%gained_xp%", event.gainedXP.toNiceString())
.formatEco(event.player)
}

View File

@@ -1,4 +1,4 @@
package com.willfp.ecoskills.skills
package com.willfp.ecoskills.skills.display
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.sound.PlayableSound
@@ -6,12 +6,12 @@ import com.willfp.ecoskills.api.event.PlayerSkillLevelUpEvent
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
class SkillLevelUpListener(
class LevelUpDisplay(
private val plugin: EcoPlugin
) : Listener {
private val sound = if (plugin.configYml.getBool("level-up.sound.enabled")) {
private val sound = if (plugin.configYml.getBool("skills.level-up.sound.enabled")) {
PlayableSound.create(
plugin.configYml.getSubsection("level-up.sound")
plugin.configYml.getSubsection("skills.level-up.sound")
)
} else null
@@ -21,8 +21,8 @@ class SkillLevelUpListener(
val skill = event.skill
val level = event.level
if (plugin.configYml.getBool("level-up.message.enabled")) {
val rawMessage = plugin.configYml.getStrings("level-up.message.message")
if (plugin.configYml.getBool("skills.level-up.message.enabled")) {
val rawMessage = plugin.configYml.getStrings("skills.level-up.message.message")
val formatted = skill.addPlaceholdersInto(
rawMessage,

View File

@@ -0,0 +1,60 @@
package com.willfp.ecoskills.skills.display
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.util.namespacedKeyOf
import org.bukkit.Bukkit
import org.bukkit.NamespacedKey
import org.bukkit.boss.BarColor
import org.bukkit.boss.BarStyle
import org.bukkit.entity.Player
import java.util.UUID
// Maps BossBar's to their expiry time
private val bossBars = mutableMapOf<NamespacedKey, Long>()
fun Player.sendTemporaryBossBar(
message: String,
identifier: String, // The identifier for the boss bar (e.g. The skill name)
duration: Int, // In milliseconds
barColor: BarColor,
barStyle: BarStyle,
progress: Double // Progress between 0 and 1
) {
val idBytes = (this.uniqueId.hashCode() shl 5) xor identifier.hashCode()
val key = namespacedKeyOf("ecoskills", idBytes.toString())
val bossBar = Bukkit.getBossBar(key) ?: Bukkit.createBossBar(
key,
message,
barColor,
barStyle
)
bossBar.setTitle(message)
bossBar.progress = progress
bossBar.addPlayer(this)
bossBars[key] = System.currentTimeMillis() + duration
}
class TemporaryBossBarHandler(
private val plugin: EcoPlugin
) {
internal fun startTicking() {
plugin.scheduler.runTimer(5, 5) {
val iterator = bossBars.iterator()
while (iterator.hasNext()) {
val (key, expiry) = iterator.next()
if (System.currentTimeMillis() > expiry) {
val bossBar = Bukkit.getBossBar(key) ?: continue
bossBar.removeAll()
Bukkit.removeBossBar(key)
iterator.remove()
}
}
}
}
}

View File

@@ -57,7 +57,7 @@ gui:
lore:
- "&8&o%description%"
- "&f"
- "%rewards%"
- "%gui_lore%"
- "&f"
- "&fProgress to next level:"
- "&8» &e%percentage_progress%"
@@ -230,22 +230,70 @@ persistent-action-bar:
# The worlds that the action bar should be disabled in.
disabled-in-worlds: [ ]
level-up:
message:
damage-indicators:
# Requires a compatible Hologram plugin to be installed
enabled: true
final-damage: false # If final damage (with reductions applied) should be used.
format:
normal: "&7%damage%"
crit: "&f✧ <gradient:#f953c6>%damage%</gradient:#b91d73> &f✧"
healing:
enabled: true
format: "&a+%damage%"
max-x-offset: 0.6
max-y-offset: 0.6
max-z-offset: 0.6
skills:
prevent-levelling-while-afk: true # If the player is AFK then don't give xp
# Ways to tell the player about skill xp gain
gain-xp:
action-bar:
# If the action bar should be used
enabled: true
# The actionbar message that should be sent
message: "&f%skill% &8| &9(%current_xp%/%required_xp%) &e+%gained_xp%"
boss-bar:
# If the boss bar should be used
enabled: false
# The boss bar message that should be sent
format: "&f%skill% &8| &9(%current_xp%/%required_xp%)"
# The color of the boss bar (from https://hub.spigotmc.org/javadocs/spigot/org/bukkit/boss/BarColor.html)
color: blue
# The style of the boss bar (from https://hub.spigotmc.org/javadocs/spigot/org/bukkit/boss/BarStyle.html)
style: solid
# How long the boss bar should last (in milliseconds)
duration: 2500
sound:
# If a sound should be played
enabled: true
# The sound that should be played
sound: entity_experience_orb_pickup
# Pitch between 0.5 and 2
pitch: 2
# The volume
volume: 0.1
level-up:
message:
- "&f"
- " &#ff00aeYou levelled up &d%skill%&#ff00ae to &eLevel %level_numeral%&#ff00ae!"
- "&f"
- " &#ff00ae&lREWARDS:"
- "%rewards%"
- "&f"
sound:
# If a sound should be played
enabled: true
# The sound that should be played
sound: entity_player_levelup
# Pitch between 0.5 and 2
pitch: 0.8
# The volume
volume: 1.0
enabled: true
message:
- "&f"
- " &#ff00aeYou levelled up &d%skill%&#ff00ae to &eLevel %level_numeral%&#ff00ae!"
- "&f"
- " &#ff00ae&lREWARDS:"
- "%rewards%"
- "&f"
sound:
# If a sound should be played
enabled: true
# The sound that should be played
sound: entity_player_levelup
# Pitch between 0.5 and 2
pitch: 0.8
# The volume
volume: 1.0

View File

@@ -14,6 +14,19 @@ gui:
icon: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmIxYzI2OGVmZWM4ZDdkODhhMWNiODhjMmJmYTA5N2ZhNTcwMzc5NDIyOTlmN2QyMDIxNTlmYzkzY2QzMDM2ZCJ9fX0=
lore:
- "&fImproves Stats:"
- "&8» &r%ecoskills_defense_name%"
- "&8» &r%ecoskills_ferocity_name%"
- "&f"
- "&fEffects:"
- "&8» &r&6Versatile Tools %ecoskills_versatile_tools_numeral%"
- " %ecoskills_versatile_tools_description%"
- "&8» &r&6Spelunking %ecoskills_spelunking_numeral%"
- " %ecoskills_spelunking_description%"
- "&8» &r&6Dynamic Mining %ecoskills_dynamic_mining_numeral%"
- " %ecoskills_dynamic_mining_description%"
position:
row: 3
column: 3