9
0
mirror of https://github.com/Auxilor/EcoJobs.git synced 2025-12-20 15:39:26 +00:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Auxilor
935e9a7937 libreforge-updater 2023-02-15 20:57:47 +00:00
Auxilor
04c2571d0f Added %ecojobs_<id>_active% placeholder 2023-02-15 18:48:47 +00:00
Auxilor
bd1b1aaa75 Cleaned up default config 2023-02-15 18:47:26 +00:00
Auxilor
f942ebd9ed Updated to 2.0.0 2023-02-15 18:43:32 +00:00
Auxilor
dfbc77cd0a Updated GUI 2023-02-15 18:43:15 +00:00
Auxilor
760c3ce707 Updated to 2.0 with multiple jobs support (todo: GUI) 2023-02-15 18:01:43 +00:00
Auxilor
b4db83feb1 libreforge-updater 2023-02-12 16:54:39 +00:00
Auxilor
31980faf05 libreforge-updater 2023-02-09 14:11:37 +00:00
Auxilor
91645c13ac Added JobEvent 2023-02-08 17:24:41 +00:00
Auxilor
420279fefa libreforge-updater 2023-02-07 14:45:38 +00:00
Auxilor
e5d4cfb4ac libreforge-updater 2023-02-04 15:35:40 +00:00
Auxilor
ee3a4110ee libreforge-updater 2023-01-24 10:08:34 +00:00
Auxilor
c8ae5bd4be libreforge-updater 2023-01-17 16:47:31 +00:00
Auxilor
83ee1c2c1e libreforge-updater 2023-01-13 18:07:37 +00:00
Auxilor
f79cb8192a libreforge-updater 2023-01-07 12:18:26 +00:00
Auxilor
7d8f79a81b libreforge-updater 2023-01-02 15:59:08 +00:00
Auxilor
3571d7ddf3 libreforge-updater 2022-12-26 14:00:32 +01:00
25 changed files with 558 additions and 561 deletions

View File

@@ -48,8 +48,8 @@ allprojects {
dependencies { dependencies {
compileOnly 'com.willfp:eco:6.46.0' compileOnly 'com.willfp:eco:6.46.0'
implementation 'com.willfp:libreforge:3.126.2' implementation 'com.willfp:libreforge:3.129.2'
implementation 'com.willfp:ecomponent:1.0.0' implementation 'com.willfp:ecomponent:1.3.0'
implementation 'org.joml:joml:1.10.4' implementation 'org.joml:joml:1.10.4'
compileOnly 'org.jetbrains:annotations:23.0.0' compileOnly 'org.jetbrains:annotations:23.0.0'

View File

@@ -1,36 +0,0 @@
package com.willfp.ecojobs
import com.willfp.ecojobs.api.EcoJobsAPI
import com.willfp.ecojobs.jobs.Job
import com.willfp.ecojobs.jobs.activeJob
import com.willfp.ecojobs.jobs.getJobLevel
import com.willfp.ecojobs.jobs.getJobProgress
import com.willfp.ecojobs.jobs.getJobXP
import com.willfp.ecojobs.jobs.getJobXPRequired
import com.willfp.ecojobs.jobs.giveJobExperience
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
internal object EcoJobsAPIImpl : EcoJobsAPI {
override fun hasJob(player: OfflinePlayer, job: Job) = player.hasJob(job)
override fun getActiveJob(player: OfflinePlayer): Job? = player.activeJob
override fun getJobLevel(player: OfflinePlayer, job: Job) = player.getJobLevel(job)
override fun giveJobExperience(player: Player, job: Job, amount: Double) =
player.giveJobExperience(job, amount)
override fun giveJobExperience(player: Player, job: Job, amount: Double, applyMultipliers: Boolean) =
player.giveJobExperience(job, amount, noMultiply = !applyMultipliers)
override fun getJobProgress(player: OfflinePlayer, job: Job) =
player.getJobProgress(job)
override fun getJobXPRequired(player: OfflinePlayer, job: Job) =
player.getJobXPRequired(job)
override fun getJobXP(player: OfflinePlayer, job: Job) =
player.getJobXP(job)
}

View File

@@ -2,7 +2,9 @@ package com.willfp.ecojobs
import com.willfp.eco.core.command.impl.PluginCommand import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.placeholder.PlayerPlaceholder import com.willfp.eco.core.placeholder.PlayerPlaceholder
import com.willfp.eco.util.toSingletonList import com.willfp.ecojobs.api.activeJobs
import com.willfp.ecojobs.api.getJobLevel
import com.willfp.ecojobs.api.jobLimit
import com.willfp.ecojobs.commands.CommandEcojobs import com.willfp.ecojobs.commands.CommandEcojobs
import com.willfp.ecojobs.commands.CommandJobs import com.willfp.ecojobs.commands.CommandJobs
import com.willfp.ecojobs.jobs.JobLevelListener import com.willfp.ecojobs.jobs.JobLevelListener
@@ -10,16 +12,15 @@ import com.willfp.ecojobs.jobs.JobTriggerXPGainListener
import com.willfp.ecojobs.jobs.Jobs import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.PriceHandler import com.willfp.ecojobs.jobs.PriceHandler
import com.willfp.ecojobs.jobs.ResetOnQuitListener import com.willfp.ecojobs.jobs.ResetOnQuitListener
import com.willfp.ecojobs.jobs.activeJob
import com.willfp.ecojobs.jobs.activeJobLevel
import com.willfp.ecojobs.jobs.getJobLevel
import com.willfp.libreforge.LibReforgePlugin import com.willfp.libreforge.LibReforgePlugin
import org.bukkit.event.Listener import org.bukkit.event.Listener
class EcoJobsPlugin : LibReforgePlugin() { class EcoJobsPlugin : LibReforgePlugin() {
init { init {
instance = this instance = this
registerHolderProvider { it.activeJobLevel?.toSingletonList() ?: emptyList() } registerHolderProvider { player ->
player.activeJobs.map { it.getLevel(player.getJobLevel(it)) }
}
} }
override fun handleEnableAdditional() { override fun handleEnableAdditional() {
@@ -27,18 +28,13 @@ class EcoJobsPlugin : LibReforgePlugin() {
PlayerPlaceholder( PlayerPlaceholder(
this, this,
"job" "limit"
) { it.activeJob?.name ?: "" }.register() ) { it.jobLimit.toString() }.register()
PlayerPlaceholder( PlayerPlaceholder(
this, this,
"job_level" "in_jobs"
) { it.activeJobLevel?.level?.toString() ?: "" }.register() ) { it.activeJobs.size.toString() }.register()
PlayerPlaceholder(
this,
"job_id"
) { it.activeJob?.id ?: "" }.register()
PlayerPlaceholder( PlayerPlaceholder(
this, this,

View File

@@ -1,117 +1,192 @@
@file:JvmName("EcoJobsAPI")
package com.willfp.ecojobs.api package com.willfp.ecojobs.api
import com.willfp.ecojobs.EcoJobsAPIImpl import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.core.data.profile
import com.willfp.ecojobs.EcoJobsPlugin
import com.willfp.ecojobs.api.event.PlayerJobExpGainEvent
import com.willfp.ecojobs.api.event.PlayerJobJoinEvent
import com.willfp.ecojobs.api.event.PlayerJobLeaveEvent
import com.willfp.ecojobs.api.event.PlayerJobLevelUpEvent
import com.willfp.ecojobs.jobs.Job import com.willfp.ecojobs.jobs.Job
import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.getNumericalPermission
import com.willfp.ecojobs.jobs.jobExperienceMultiplier
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player import org.bukkit.entity.Player
import kotlin.math.abs
interface EcoJobsAPI { private val plugin = EcoJobsPlugin.instance
/**
* Get if a player has a job.
*
* @param player The player.
* @param job The job.
* @return If the player has the job unlocked.
*/
fun hasJob(
player: OfflinePlayer,
job: Job
): Boolean
/** /*
* Get a player's active job.
*
* @param player The player.
* @return The active job.
*/
fun getActiveJob(
player: OfflinePlayer
): Job?
/** The old key is around for backwards compatibility with 1.x.x.
* Get a player's level of a certain job.
*
* @param player The player.
* @param job The job.
* @return The level.
*/
fun getJobLevel(
player: OfflinePlayer,
job: Job
): Int
/** */
* Give job experience to a player.
*
* @param player The player.
* @param job The job.
* @param amount The amount of experience to give.
*/
fun giveJobExperience(
player: Player,
job: Job,
amount: Double
)
/** private val legacyActiveJobKey: PersistentDataKey<String> = PersistentDataKey(
* Give job experience to a player. plugin.namespacedKeyFactory.create("active_job"), PersistentDataKeyType.STRING, ""
* )
* @param player The player.
* @param job The job.
* @param amount The amount of experience to give.
* @param applyMultipliers If multipliers should be applied.
*/
fun giveJobExperience(
player: Player,
job: Job,
amount: Double,
applyMultipliers: Boolean
)
/** private val activeJobsKey: PersistentDataKey<List<String>> = PersistentDataKey(
* Get progress to next level between 0 and 1, where 0 is none and 1 is complete. plugin.namespacedKeyFactory.create("active_jobs"), PersistentDataKeyType.STRING_LIST, listOf()
* )
* @param player The player.
* @param job The job.
* @return The progress.
*/
fun getJobProgress(
player: OfflinePlayer,
job: Job
): Double
/** /**
* Get the experience required to advance to the next level. * The job limit.
* */
* @param player The player. val Player.jobLimit: Int
* @param job The job. get() {
* @return The experience required. return this.getNumericalPermission("ecojobs.limit", plugin.configYml.getDouble("jobs.limit")).toInt()
*/ }
fun getJobXPRequired(
player: OfflinePlayer,
job: Job
): Int
/** /**
* Get experience to the next level. * If a player can join a job.
* */
* @param player The player. fun Player.canJoinJob(job: Job): Boolean {
* @param job The job. return this.activeJobs.size < this.jobLimit && job !in this.activeJobs && this.hasJob(job)
* @return The experience. }
*/
fun getJobXP(
player: OfflinePlayer,
job: Job
): Double
companion object { /**
/** * Get if a job is unlocked.
* Get the instance of the API. */
* fun OfflinePlayer.hasJob(job: Job) = this.getJobLevel(job) > 0
* @return The API.
*/ /**
@JvmStatic * Get if a player has a job active.
val instance: EcoJobsAPI */
get() = EcoJobsAPIImpl fun OfflinePlayer.hasJobActive(job: Job) = job in this.activeJobs
/**
* Get a player's active jobs.
*/
val OfflinePlayer.activeJobs: Collection<Job>
get() {
if (this.profile.read(legacyActiveJobKey).isNotBlank()) {
this.profile.write(activeJobsKey, listOf(this.profile.read(legacyActiveJobKey)))
this.profile.write(legacyActiveJobKey, "")
}
return this.profile.read(activeJobsKey).mapNotNull { Jobs.getByID(it) }
}
/**
* Join a job.
*/
fun OfflinePlayer.joinJob(job: Job) {
val event = PlayerJobJoinEvent(this, job)
Bukkit.getPluginManager().callEvent(event)
if (!event.isCancelled) {
this.profile.write(activeJobsKey, this.activeJobs.plus(job).map { it.id })
}
}
/**
* Leave a job.
*/
fun OfflinePlayer.leaveJob(job: Job) {
if (job !in this.activeJobs) {
return
}
val event = PlayerJobLeaveEvent(this, job)
Bukkit.getPluginManager().callEvent(event)
if (!event.isCancelled) {
this.forceLeaveJob(job)
}
}
/**
* Leave a job without checking.
*/
fun OfflinePlayer.forceLeaveJob(job: Job) {
this.profile.write(activeJobsKey, this.activeJobs.minus(job).map { it.id })
}
/**
* Get the level of a certain job.
*/
fun OfflinePlayer.getJobLevel(job: Job) = this.profile.read(job.levelKey)
/**
* Set the level of a certain job.
*/
fun OfflinePlayer.setJobLevel(job: Job, level: Int) = this.profile.write(job.levelKey, level)
/**
* Get current job experience.
*/
fun OfflinePlayer.getJobXP(job: Job) = this.profile.read(job.xpKey)
/**
* Set current job experience.
*/
fun OfflinePlayer.setJobXP(job: Job, xp: Double) = this.profile.write(job.xpKey, xp)
/**
* Reset a job.
*/
fun OfflinePlayer.resetJob(job: Job) {
this.setJobLevel(job, 1)
this.setJobXP(job, 0.0)
}
/**
* Get the experience required to advance to the next level.
*/
fun OfflinePlayer.getJobXPRequired(job: Job) = job.getExpForLevel(this.getJobLevel(job) + 1)
/**
* Get progress to next level between 0 and 1, where 0 is none and 1 is complete.
*/
fun OfflinePlayer.getJobProgress(job: Job): Double {
val currentXP = this.getJobXP(job)
val requiredXP = job.getExpForLevel(this.getJobLevel(job) + 1)
return currentXP / requiredXP
}
/**
* Give job experience.
*/
@JvmOverloads
fun Player.giveJobExperience(job: Job, experience: Double, withMultipliers: Boolean = true) {
val exp = abs(
if (withMultipliers) experience * this.jobExperienceMultiplier
else experience
)
val gainEvent = PlayerJobExpGainEvent(this, job, exp, !withMultipliers)
Bukkit.getPluginManager().callEvent(gainEvent)
if (gainEvent.isCancelled) {
return
}
this.giveExactJobExperience(job, gainEvent.amount)
}
/**
* Give exact job experience, without calling PlayerJobExpGainEvent.
*/
fun Player.giveExactJobExperience(job: Job, experience: Double) {
val level = this.getJobLevel(job)
val progress = this.getJobXP(job) + experience
if (progress >= job.getExpForLevel(level + 1) && level + 1 <= job.maxLevel) {
val overshoot = progress - job.getExpForLevel(level + 1)
this.setJobXP(job, 0.0)
this.setJobLevel(job, level + 1)
val levelUpEvent = PlayerJobLevelUpEvent(this, job, level + 1)
Bukkit.getPluginManager().callEvent(levelUpEvent)
this.giveExactJobExperience(job, overshoot)
} else {
this.setJobXP(job, progress)
} }
} }

View File

@@ -0,0 +1,7 @@
package com.willfp.ecojobs.api.event
import com.willfp.ecojobs.jobs.Job
interface JobEvent {
val job: Job
}

View File

@@ -8,10 +8,10 @@ import org.bukkit.event.Cancellable
class PlayerJobExpGainEvent( class PlayerJobExpGainEvent(
who: Player, who: Player,
val job: Job, override val job: Job,
var amount: Double, var amount: Double,
val isMultiply: Boolean val isMultiply: Boolean
) : PlayerEvent(who), Cancellable { ) : PlayerEvent(who), Cancellable, JobEvent {
private var cancelled = false private var cancelled = false
override fun setCancelled(cancel: Boolean) { override fun setCancelled(cancel: Boolean) {

View File

@@ -8,9 +8,8 @@ import org.bukkit.event.HandlerList
class PlayerJobJoinEvent( class PlayerJobJoinEvent(
val player: OfflinePlayer, val player: OfflinePlayer,
val job: Job, override val job: Job
val oldJob: Job? ) : Event(), Cancellable, JobEvent {
) : Event(), Cancellable {
private var cancelled = false private var cancelled = false
override fun isCancelled() = this.cancelled override fun isCancelled() = this.cancelled

View File

@@ -8,8 +8,8 @@ import org.bukkit.event.HandlerList
class PlayerJobLeaveEvent( class PlayerJobLeaveEvent(
val player: OfflinePlayer, val player: OfflinePlayer,
val job: Job override val job: Job
) : Event(), Cancellable { ) : Event(), Cancellable, JobEvent {
private var cancelled = false private var cancelled = false
override fun isCancelled() = this.cancelled override fun isCancelled() = this.cancelled

View File

@@ -7,9 +7,9 @@ import org.bukkit.event.HandlerList
class PlayerJobLevelUpEvent( class PlayerJobLevelUpEvent(
who: Player, who: Player,
val job: Job, override val job: Job,
val level: Int val level: Int
) : PlayerEvent(who) { ) : PlayerEvent(who), JobEvent {
override fun getHandlers(): HandlerList { override fun getHandlers(): HandlerList {
return handlerList return handlerList
} }

View File

@@ -5,9 +5,9 @@ import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.savedDisplayName import com.willfp.eco.util.savedDisplayName
import com.willfp.eco.util.toNiceString import com.willfp.eco.util.toNiceString
import com.willfp.ecojobs.api.giveExactJobExperience
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.jobs.Jobs import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.giveExactJobExperience
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender

View File

@@ -2,9 +2,9 @@ package com.willfp.ecojobs.commands
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.PluginCommand import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.jobs.Jobs import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.JobsGUI import com.willfp.ecojobs.jobs.JobsGUI
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.util.StringUtil import org.bukkit.util.StringUtil

View File

@@ -3,9 +3,11 @@ package com.willfp.ecojobs.commands
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.Subcommand import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import com.willfp.ecojobs.api.canJoinJob
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.joinJob
import com.willfp.ecojobs.jobs.Jobs import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.activeJob
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.util.StringUtil import org.bukkit.util.StringUtil
@@ -28,13 +30,13 @@ class CommandJoin(plugin: EcoPlugin) : Subcommand(plugin, "join", "ecojobs.comma
return return
} }
if (player.activeJob == job) { if (player.hasJobActive(job)) {
player.sendMessage(plugin.langYml.getMessage("job-already-joined")) player.sendMessage(plugin.langYml.getMessage("job-already-joined"))
return return
} }
if (player.activeJob != null) { if (!player.canJoinJob(job)) {
player.sendMessage(plugin.langYml.getMessage("leave-current-job")) player.sendMessage(plugin.langYml.getMessage("cannot-join-job"))
return return
} }
@@ -42,7 +44,8 @@ class CommandJoin(plugin: EcoPlugin) : Subcommand(plugin, "join", "ecojobs.comma
plugin.langYml.getMessage("joined-job", StringUtils.FormatOption.WITHOUT_PLACEHOLDERS) plugin.langYml.getMessage("joined-job", StringUtils.FormatOption.WITHOUT_PLACEHOLDERS)
.replace("%job%", job.name) .replace("%job%", job.name)
) )
player.activeJob = job
player.joinJob(job)
} }
override fun tabComplete(sender: CommandSender, args: List<String>): List<String> { override fun tabComplete(sender: CommandSender, args: List<String>): List<String> {
@@ -51,6 +54,7 @@ class CommandJoin(plugin: EcoPlugin) : Subcommand(plugin, "join", "ecojobs.comma
} }
val completions = mutableListOf<String>() val completions = mutableListOf<String>()
if (args.isEmpty()) { if (args.isEmpty()) {
return Jobs.values().filter { sender.hasJob(it) }.map { it.id } return Jobs.values().filter { sender.hasJob(it) }.map { it.id }
} }

View File

@@ -2,24 +2,46 @@ package com.willfp.ecojobs.commands
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.Subcommand import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.ecojobs.jobs.activeJob import com.willfp.ecojobs.api.activeJobs
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.leaveJob
import com.willfp.ecojobs.jobs.Jobs
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
class CommandLeave(plugin: EcoPlugin) : Subcommand(plugin, "leave", "ecojobs.command.leave", true) { class CommandLeave(plugin: EcoPlugin) : Subcommand(plugin, "leave", "ecojobs.command.leave", true) {
override fun onExecute(player: CommandSender, args: List<String>) { override fun onExecute(player: CommandSender, args: List<String>) {
player as Player player as Player
if (player.activeJob == null) { if (args.isEmpty()) {
player.sendMessage(plugin.langYml.getMessage("needs-job"))
return
}
if (player.activeJobs.isEmpty()) {
player.sendMessage(plugin.langYml.getMessage("no-job")) player.sendMessage(plugin.langYml.getMessage("no-job"))
return return
} }
val job = player.activeJob ?: return val id = args[0]
player.activeJob = null val job = Jobs.getByID(id)
if (player.activeJob == null) { if (job == null || !player.hasJob(job)) {
player.sendMessage(plugin.langYml.getMessage("invalid-job"))
return
}
if (!player.hasJobActive(job)) {
player.sendMessage(plugin.langYml.getMessage("not-in-job"))
return
}
player.leaveJob(job)
if (!player.hasJobActive(job)) {
player.sendMessage( player.sendMessage(
plugin.langYml.getMessage("left-job") plugin.langYml.getMessage("left-job")
.replace("%job%", job.name) .replace("%job%", job.name)
@@ -31,4 +53,27 @@ class CommandLeave(plugin: EcoPlugin) : Subcommand(plugin, "leave", "ecojobs.com
) )
} }
} }
override fun tabComplete(sender: CommandSender, args: List<String>): List<String> {
if (sender !is Player) {
return emptyList()
}
val completions = mutableListOf<String>()
if (args.isEmpty()) {
return Jobs.values().filter { sender.hasJobActive(it) }.map { it.id }
}
if (args.size == 1) {
StringUtil.copyPartialMatches(
args[0],
Jobs.values().filter { sender.hasJobActive(it) }.map { it.id },
completions
)
return completions
}
return emptyList()
}
} }

View File

@@ -4,10 +4,10 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.Subcommand import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.savedDisplayName import com.willfp.eco.util.savedDisplayName
import com.willfp.ecojobs.api.forceLeaveJob
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.api.resetJob
import com.willfp.ecojobs.jobs.Jobs import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.activeJob
import com.willfp.ecojobs.jobs.hasJob
import com.willfp.ecojobs.jobs.resetJob
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
@@ -44,9 +44,7 @@ class CommandReset(plugin: EcoPlugin) : Subcommand(plugin, "reset", "ecojobs.com
return return
} }
if (player.activeJob == job) { player.forceLeaveJob(job)
player.activeJob = null
}
player.resetJob(job) player.resetJob(job)
sender.sendMessage( sender.sendMessage(

View File

@@ -4,9 +4,9 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.Subcommand import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.savedDisplayName import com.willfp.eco.util.savedDisplayName
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.api.setJobLevel
import com.willfp.ecojobs.jobs.Jobs import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.hasJob
import com.willfp.ecojobs.jobs.setJobLevel
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
import org.bukkit.util.StringUtil import org.bukkit.util.StringUtil

View File

@@ -16,10 +16,18 @@ import com.willfp.eco.util.NumberUtils
import com.willfp.eco.util.formatEco import com.willfp.eco.util.formatEco
import com.willfp.eco.util.toNiceString import com.willfp.eco.util.toNiceString
import com.willfp.ecojobs.EcoJobsPlugin import com.willfp.ecojobs.EcoJobsPlugin
import com.willfp.ecojobs.api.activeJobs
import com.willfp.ecojobs.api.canJoinJob
import com.willfp.ecojobs.api.event.PlayerJobExpGainEvent import com.willfp.ecojobs.api.event.PlayerJobExpGainEvent
import com.willfp.ecojobs.api.event.PlayerJobJoinEvent import com.willfp.ecojobs.api.event.PlayerJobJoinEvent
import com.willfp.ecojobs.api.event.PlayerJobLeaveEvent import com.willfp.ecojobs.api.event.PlayerJobLeaveEvent
import com.willfp.ecojobs.api.event.PlayerJobLevelUpEvent import com.willfp.ecojobs.api.event.PlayerJobLevelUpEvent
import com.willfp.ecojobs.api.getJobLevel
import com.willfp.ecojobs.api.getJobProgress
import com.willfp.ecojobs.api.getJobXP
import com.willfp.ecojobs.api.getJobXPRequired
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.jobLimit
import com.willfp.libreforge.conditions.Conditions import com.willfp.libreforge.conditions.Conditions
import com.willfp.libreforge.conditions.ConfiguredCondition import com.willfp.libreforge.conditions.ConfiguredCondition
import com.willfp.libreforge.effects.ConfiguredEffect import com.willfp.libreforge.effects.ConfiguredEffect
@@ -30,14 +38,14 @@ import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import java.util.DoubleSummaryStatistics
import java.util.Objects import java.util.Objects
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max
class Job( class Job(
val id: String, val id: String, val config: Config, private val plugin: EcoJobsPlugin
val config: Config,
private val plugin: EcoJobsPlugin
) { ) {
val name = config.getFormattedString("name") val name = config.getFormattedString("name")
val description = config.getFormattedString("description") val description = config.getFormattedString("description")
@@ -45,13 +53,11 @@ class Job(
val resetsOnQuit = config.getBool("reset-on-quit") val resetsOnQuit = config.getBool("reset-on-quit")
val joinPrice = ConfiguredPrice.create(config.getSubsection("join-price")) ?: ConfiguredPrice( val joinPrice = ConfiguredPrice.create(config.getSubsection("join-price")) ?: ConfiguredPrice(
PriceEconomy(config.getDouble("join-price")), PriceEconomy(config.getDouble("join-price")), ""
""
) )
val leavePrice = ConfiguredPrice.create(config.getSubsection("leave-price")) ?: ConfiguredPrice( val leavePrice = ConfiguredPrice.create(config.getSubsection("leave-price")) ?: ConfiguredPrice(
PriceEconomy(config.getDouble("leave-price")), PriceEconomy(config.getDouble("leave-price")), ""
""
) )
val levelKey: PersistentDataKey<Int> = PersistentDataKey( val levelKey: PersistentDataKey<Int> = PersistentDataKey(
@@ -61,9 +67,7 @@ class Job(
) )
val xpKey: PersistentDataKey<Double> = PersistentDataKey( val xpKey: PersistentDataKey<Double> = PersistentDataKey(
EcoJobsPlugin.instance.namespacedKeyFactory.create("${id}_xp"), EcoJobsPlugin.instance.namespacedKeyFactory.create("${id}_xp"), PersistentDataKeyType.DOUBLE, 0.0
PersistentDataKeyType.DOUBLE,
0.0
) )
private val levelXpRequirements = listOf(0) + config.getInts("level-xp-requirements") private val levelXpRequirements = listOf(0) + config.getInts("level-xp-requirements")
@@ -79,41 +83,33 @@ class Job(
private val effects: Set<ConfiguredEffect> private val effects: Set<ConfiguredEffect>
private val conditions: Set<ConfiguredCondition> private val conditions: Set<ConfiguredCondition>
private val levels = Caffeine.newBuilder() private val levels = Caffeine.newBuilder().build<Int, JobLevel>()
.build<Int, JobLevel>() private val effectsDescription = Caffeine.newBuilder().build<Int, List<String>>()
private val effectsDescription = Caffeine.newBuilder() private val rewardsDescription = Caffeine.newBuilder().build<Int, List<String>>()
.build<Int, List<String>>() private val levelUpMessages = Caffeine.newBuilder().build<Int, List<String>>()
private val rewardsDescription = Caffeine.newBuilder()
.build<Int, List<String>>()
private val levelUpMessages = Caffeine.newBuilder()
.build<Int, List<String>>()
private val levelCommands = mutableMapOf<Int, MutableList<String>>() private val levelCommands = mutableMapOf<Int, MutableList<String>>()
private val levelPlaceholders = config.getSubsections("level-placeholders") private val levelPlaceholders = config.getSubsections("level-placeholders").map { sub ->
.map { sub -> LevelPlaceholder(
LevelPlaceholder( sub.getString("id")
sub.getString("id") ) {
) { NumberUtils.evaluateExpression(
NumberUtils.evaluateExpression( sub.getString("value").replace("%level%", it.toString())
sub.getString("value") ).toNiceString()
.replace("%level%", it.toString())
).toNiceString()
}
} }
}
private val jobXpGains = config.getSubsections("xp-gain-methods").mapNotNull { private val jobXpGains = config.getSubsections("xp-gain-methods").mapNotNull {
Counters.compile(it, "Job $id") Counters.compile(it, "Job $id")
} }
init { init {
config.injectPlaceholders( config.injectPlaceholders(PlayerStaticPlaceholder(
PlayerStaticPlaceholder( "level"
"level" ) { p ->
) { p -> p.getJobLevel(this).toString()
p.getJobLevel(this).toString() })
}
)
effects = config.getSubsections("effects").mapNotNull { effects = config.getSubsections("effects").mapNotNull {
Effects.compile(it, "Job $id") Effects.compile(it, "Job $id")
@@ -143,52 +139,51 @@ class Job(
} }
PlayerPlaceholder( PlayerPlaceholder(
plugin, plugin, "${id}_percentage_progress"
"${id}_percentage_progress"
) { ) {
(it.getJobProgress(this) * 100).toNiceString() (it.getJobProgress(this) * 100).toNiceString()
}.register() }.register()
PlayerPlaceholder( PlayerPlaceholder(
plugin, plugin, id
id
) { ) {
it.getJobLevel(this).toString() it.getJobLevel(this).toString()
}.register() }.register()
PlayerPlaceholder( PlayerPlaceholder(
plugin, plugin, "${id}_current_xp"
"${id}_current_xp"
) { ) {
NumberUtils.format(it.getJobXP(this)) NumberUtils.format(it.getJobXP(this))
}.register() }.register()
PlayerPlaceholder( PlayerPlaceholder(
plugin, plugin, "${id}_required_xp"
"${id}_required_xp"
) { ) {
it.getJobXPRequired(this).toString() it.getJobXPRequired(this).toString()
}.register() }.register()
PlayerlessPlaceholder( PlayerlessPlaceholder(
plugin, plugin, "${id}_name"
"${id}_name"
) { ) {
this.name this.name
}.register() }.register()
PlayerPlaceholder( PlayerPlaceholder(
plugin, plugin, "${id}_level"
"${id}_level"
) { ) {
it.getJobLevel(this).toString() it.getJobLevel(this).toString()
}.register() }.register()
PlayerPlaceholder( PlayerPlaceholder(
plugin, plugin, "${id}_active"
"${id}_total_players"
) { ) {
Bukkit.getOfflinePlayers().count { it.activeJob == this }.toString() it.hasJobActive(this).toString()
}.register()
PlayerPlaceholder(
plugin, "${id}_total_players"
) {
Bukkit.getOfflinePlayers().count { this in it.activeJobs }.toString()
}.register() }.register()
} }
@@ -208,13 +203,11 @@ class Job(
} }
} }
this.config.getStrings("level-up-messages.$highestConfiguredLevel") this.config.getStrings("level-up-messages.$highestConfiguredLevel").map {
.map { levelPlaceholders.format(it, level)
levelPlaceholders.format(it, level) }.map {
} " ".repeat(whitespace) + it
.map { }
" ".repeat(whitespace) + it
}
} }
private fun getEffectsDescription(level: Int, whitespace: Int = 0): List<String> = effectsDescription.get(level) { private fun getEffectsDescription(level: Int, whitespace: Int = 0): List<String> = effectsDescription.get(level) {
@@ -229,13 +222,11 @@ class Job(
} }
} }
this.config.getStrings("effects-description.$highestConfiguredLevel") this.config.getStrings("effects-description.$highestConfiguredLevel").map {
.map { levelPlaceholders.format(it, level)
levelPlaceholders.format(it, level) }.map {
} " ".repeat(whitespace) + it
.map { }
" ".repeat(whitespace) + it
}
} }
private fun getRewardsDescription(level: Int, whitespace: Int = 0): List<String> = rewardsDescription.get(level) { private fun getRewardsDescription(level: Int, whitespace: Int = 0): List<String> = rewardsDescription.get(level) {
@@ -250,53 +241,40 @@ class Job(
} }
} }
this.config.getStrings("rewards-description.$highestConfiguredLevel") this.config.getStrings("rewards-description.$highestConfiguredLevel").map {
.map { levelPlaceholders.format(it, level)
levelPlaceholders.format(it, level) }.map {
} " ".repeat(whitespace) + it
.map { }
" ".repeat(whitespace) + it
}
} }
private fun getLeaveLore(level: Int, whitespace: Int = 0): List<String> = private fun getLeaveLore(level: Int, whitespace: Int = 0): List<String> = this.config.getStrings("leave-lore").map {
this.config.getStrings("leave-lore") levelPlaceholders.format(it, level)
.map { }.map {
levelPlaceholders.format(it, level) " ".repeat(whitespace) + it
} }
.map {
" ".repeat(whitespace) + it
}
private fun getJoinLore(level: Int, whitespace: Int = 0): List<String> = private fun getJoinLore(level: Int, whitespace: Int = 0): List<String> = this.config.getStrings("join-lore").map {
this.config.getStrings("join-lore") levelPlaceholders.format(it, level)
.map { }.map {
levelPlaceholders.format(it, level) " ".repeat(whitespace) + it
} }
.map {
" ".repeat(whitespace) + it
}
fun injectPlaceholdersInto(lore: List<String>, player: Player, forceLevel: Int? = null): List<String> { fun injectPlaceholdersInto(lore: List<String>, player: Player, forceLevel: Int? = null): List<String> {
val withPlaceholders = lore val withPlaceholders = lore.map {
.map { it.replace("%percentage_progress%", (player.getJobProgress(this) * 100).toNiceString())
it.replace("%percentage_progress%", (player.getJobProgress(this) * 100).toNiceString()) .replace("%current_xp%", player.getJobXP(this).toNiceString())
.replace("%current_xp%", player.getJobXP(this).toNiceString()) .replace("%required_xp%", this.getExpForLevel(player.getJobLevel(this) + 1).let { req ->
.replace("%required_xp%", this.getExpForLevel(player.getJobLevel(this) + 1).let { req -> if (req == Int.MAX_VALUE) {
if (req == Int.MAX_VALUE) { plugin.langYml.getFormattedString("infinity")
plugin.langYml.getFormattedString("infinity") } else {
} else { req.toNiceString()
req.toNiceString()
}
} }
) }).replace("%description%", this.description).replace("%job%", this.name)
.replace("%description%", this.description) .replace("%level%", (forceLevel ?: player.getJobLevel(this)).toString())
.replace("%job%", this.name) .replace("%join_price%", this.joinPrice.getDisplay(player))
.replace("%level%", (forceLevel ?: player.getJobLevel(this)).toString()) .replace("%leave_price%", this.leavePrice.getDisplay(player))
.replace("%join_price%", this.joinPrice.getDisplay(player)) }.toMutableList()
.replace("%leave_price%", this.leavePrice.getDisplay(player))
}
.toMutableList()
val processed = mutableListOf<List<String>>() val processed = mutableListOf<List<String>>()
@@ -328,35 +306,32 @@ class Job(
val level = player.getJobLevel(this) val level = player.getJobLevel(this)
return ItemStackBuilder(base) return ItemStackBuilder(base).setDisplayName(
.setDisplayName( plugin.configYml.getFormattedString("gui.job-icon.name").replace("%level%", level.toString())
plugin.configYml.getFormattedString("gui.job-icon.name") .replace("%job%", this.name)
.replace("%level%", level.toString()) ).addLoreLines {
.replace("%job%", this.name) injectPlaceholdersInto(
) plugin.configYml.getStrings("gui.job-icon.lore"), player
.addLoreLines { ) + if (player.hasJobActive(this)) {
injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-icon.lore"), player) + plugin.configYml.getStrings("gui.job-icon.active-lore")
when (player.activeJob) { } else if (player.canJoinJob(this)) {
this -> plugin.configYml.getStrings("gui.job-icon.active-lore") plugin.configYml.getStrings("gui.job-icon.join-lore")
null -> plugin.configYml.getStrings("gui.job-icon.join-lore") } else if (player.activeJobs.size == player.jobLimit) {
else -> emptyList() plugin.configYml.getStrings("gui.job-icon.too-many-jobs-lore")
} } else {
emptyList()
} }
.build() }.build()
} }
fun getJobInfoIcon(player: Player): ItemStack { fun getJobInfoIcon(player: Player): ItemStack {
val base = baseItem.clone() val base = baseItem.clone()
return ItemStackBuilder(base) return ItemStackBuilder(base).setDisplayName(
.setDisplayName( plugin.configYml.getFormattedString("gui.job-info.active.name")
plugin.configYml.getFormattedString("gui.job-info.active.name") .replace("%level%", player.getJobLevel(this).toString()).replace("%job%", this.name)
.replace("%level%", player.getJobLevel(this).toString()) ).addLoreLines {
.replace("%job%", this.name) injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-info.active.lore"), player)
) }.build()
.addLoreLines {
injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-info.active.lore"), player)
}
.build()
} }
fun getExpForLevel(level: Int): Int { fun getExpForLevel(level: Int): Int {
@@ -393,8 +368,7 @@ class Job(
} }
private class LevelPlaceholder( private class LevelPlaceholder(
val id: String, val id: String, private val function: (Int) -> String
private val function: (Int) -> String
) { ) {
operator fun invoke(level: Int) = function(level) operator fun invoke(level: Int) = function(level)
} }
@@ -407,86 +381,12 @@ private fun Collection<LevelPlaceholder>.format(string: String, level: Int): Str
return process return process
} }
private val activeJobKey: PersistentDataKey<String> = PersistentDataKey( fun OfflinePlayer.getJobLevelObject(job: Job): JobLevel = job.getLevel(this.getJobLevel(job))
EcoJobsPlugin.instance.namespacedKeyFactory.create("active_job"),
PersistentDataKeyType.STRING,
""
)
var OfflinePlayer.activeJob: Job? private val expMultiplierCache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build<Player, Double> {
get() = Jobs.getByID(this.profile.read(activeJobKey)) it.cacheJobExperienceMultiplier()
set(job) {
val oldJob = this.activeJob
if (oldJob != job) {
// Have to check for oldJob too to have null safety
if (job == null && oldJob != null) {
val event = PlayerJobLeaveEvent(this, oldJob)
Bukkit.getPluginManager().callEvent(event)
if (event.isCancelled) {
return
}
}
// Not using else because null safety as well
if (job != null) {
val event = PlayerJobJoinEvent(this, job, oldJob)
Bukkit.getPluginManager().callEvent(event)
if (event.isCancelled) {
return
}
}
}
this.profile.write(activeJobKey, job?.id ?: "")
}
val OfflinePlayer.activeJobLevel: JobLevel?
get() {
val active = this.activeJob ?: return null
return this.getJobLevelObject(active)
}
fun OfflinePlayer.getJobLevel(job: Job): Int =
this.profile.read(job.levelKey)
fun OfflinePlayer.setJobLevel(job: Job, level: Int) =
this.profile.write(job.levelKey, level)
fun OfflinePlayer.resetJob(job: Job) {
this.setJobLevel(job, 1)
this.setJobXP(job, 0.0)
} }
fun OfflinePlayer.getJobProgress(job: Job): Double {
val currentXP = this.getJobXP(job)
val requiredXP = job.getExpForLevel(this.getJobLevel(job) + 1)
return currentXP / requiredXP
}
fun OfflinePlayer.getJobLevelObject(job: Job): JobLevel =
job.getLevel(this.getJobLevel(job))
fun OfflinePlayer.hasJob(job: Job): Boolean =
this.getJobLevel(job) > 0
fun OfflinePlayer.getJobXP(job: Job): Double =
this.profile.read(job.xpKey)
fun OfflinePlayer.setJobXP(job: Job, xp: Double) =
this.profile.write(job.xpKey, xp)
fun OfflinePlayer.getJobXPRequired(job: Job): Int =
job.getExpForLevel(this.getJobLevel(job) + 1)
private val expMultiplierCache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.build<Player, Double> {
it.cacheJobExperienceMultiplier()
}
val Player.jobExperienceMultiplier: Double val Player.jobExperienceMultiplier: Double
get() = expMultiplierCache.get(this) get() = expMultiplierCache.get(this)
@@ -507,43 +407,19 @@ private fun Player.cacheJobExperienceMultiplier(): Double {
return 1.5 return 1.5
} }
val prefix = "ecojobs.xpmultiplier." return 1 + getNumericalPermission("ecojobs.xpmultiplier", 0.0) / 100
}
fun Player.getNumericalPermission(permission: String, default: Double): Double {
var highest: Double? = null
for (permissionAttachmentInfo in this.effectivePermissions) { for (permissionAttachmentInfo in this.effectivePermissions) {
val permission = permissionAttachmentInfo.permission val perm = permissionAttachmentInfo.permission
if (permission.startsWith(prefix)) { if (perm.startsWith(permission)) {
return ((permission.substring(permission.lastIndexOf(".") + 1).toDoubleOrNull() ?: 100.0) / 100) + 1 val found = perm.substring(perm.lastIndexOf(".") + 1).toDoubleOrNull() ?: continue
highest = max(highest ?: Double.MIN_VALUE, found)
} }
} }
return 1.0 return highest ?: default
}
fun Player.giveJobExperience(job: Job, experience: Double, noMultiply: Boolean = false) {
val exp = abs(if (noMultiply) experience else experience * this.jobExperienceMultiplier)
val gainEvent = PlayerJobExpGainEvent(this, job, exp, !noMultiply)
Bukkit.getPluginManager().callEvent(gainEvent)
if (gainEvent.isCancelled) {
return
}
this.giveExactJobExperience(job, gainEvent.amount)
}
fun Player.giveExactJobExperience(job: Job, experience: Double) {
val level = this.getJobLevel(job)
val progress = this.getJobXP(job) + experience
if (progress >= job.getExpForLevel(level + 1) && level + 1 <= job.maxLevel) {
val overshoot = progress - job.getExpForLevel(level + 1)
this.setJobXP(job, 0.0)
this.setJobLevel(job, level + 1)
val levelUpEvent = PlayerJobLevelUpEvent(this, job, level + 1)
Bukkit.getPluginManager().callEvent(levelUpEvent)
this.giveExactJobExperience(job, overshoot)
} else {
this.setJobXP(job, progress)
}
} }

View File

@@ -9,6 +9,8 @@ import com.willfp.eco.core.gui.slot.MaskItems
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.core.items.builder.ItemStackBuilder import com.willfp.eco.core.items.builder.ItemStackBuilder
import com.willfp.eco.util.formatEco import com.willfp.eco.util.formatEco
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.leaveJob
import org.bukkit.entity.Player import org.bukkit.entity.Player
class JobLeaveGUI( class JobLeaveGUI(
@@ -59,9 +61,9 @@ class JobLeaveGUI(
.build() .build()
}) { }) {
onLeftClick { player, _, _, _ -> onLeftClick { player, _, _, _ ->
player.activeJob = null player.leaveJob(job)
if (player.activeJob == null) { if (!player.hasJobActive(job)) {
player.sendMessage( player.sendMessage(
plugin.langYml.getMessage("left-job") plugin.langYml.getMessage("left-job")
.replace("%job%", job.name) .replace("%job%", job.name)

View File

@@ -13,6 +13,7 @@ import com.willfp.eco.core.gui.slot.MaskItems
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.core.items.builder.ItemStackBuilder import com.willfp.eco.core.items.builder.ItemStackBuilder
import com.willfp.eco.util.NumberUtils import com.willfp.eco.util.NumberUtils
import com.willfp.ecojobs.api.getJobLevel
import com.willfp.ecomponent.components.LevelComponent import com.willfp.ecomponent.components.LevelComponent
import com.willfp.ecomponent.components.LevelState import com.willfp.ecomponent.components.LevelState
import org.bukkit.entity.Player import org.bukkit.entity.Player

View File

@@ -1,5 +1,7 @@
package com.willfp.ecojobs.jobs package com.willfp.ecojobs.jobs
import com.willfp.ecojobs.api.activeJobs
import com.willfp.ecojobs.api.giveJobExperience
import com.willfp.libreforge.events.TriggerPreProcessEvent import com.willfp.libreforge.events.TriggerPreProcessEvent
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
@@ -9,14 +11,16 @@ object JobTriggerXPGainListener : Listener {
fun handle(event: TriggerPreProcessEvent) { fun handle(event: TriggerPreProcessEvent) {
val player = event.player val player = event.player
val job = event.player.activeJob ?: return val jobs = event.player.activeJobs
val amount = job.getXP(event) for (job in jobs) {
val amount = job.getXP(event)
if (amount <= 0.0) { if (amount <= 0.0) {
return return
}
player.giveJobExperience(job, amount)
} }
player.giveJobExperience(job, amount)
} }
} }

View File

@@ -5,6 +5,7 @@ import com.google.common.collect.HashBiMap
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.willfp.eco.core.config.updating.ConfigUpdater import com.willfp.eco.core.config.updating.ConfigUpdater
import com.willfp.ecojobs.EcoJobsPlugin import com.willfp.ecojobs.EcoJobsPlugin
import com.willfp.ecojobs.api.getJobLevel
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
object Jobs { object Jobs {

View File

@@ -4,18 +4,28 @@ import com.willfp.eco.core.config.updating.ConfigUpdater
import com.willfp.eco.core.gui.menu import com.willfp.eco.core.gui.menu
import com.willfp.eco.core.gui.menu.Menu import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.onLeftClick import com.willfp.eco.core.gui.onLeftClick
import com.willfp.eco.core.gui.onRightClick
import com.willfp.eco.core.gui.page.PageChanger
import com.willfp.eco.core.gui.slot import com.willfp.eco.core.gui.slot
import com.willfp.eco.core.gui.slot.ConfigSlot import com.willfp.eco.core.gui.slot.ConfigSlot
import com.willfp.eco.core.gui.slot.FillerMask import com.willfp.eco.core.gui.slot.FillerMask
import com.willfp.eco.core.gui.slot.MaskItems import com.willfp.eco.core.gui.slot.MaskItems
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.core.items.builder.ItemStackBuilder import com.willfp.eco.core.items.builder.ItemStackBuilder
import com.willfp.eco.core.items.builder.SkullBuilder
import com.willfp.eco.util.formatEco
import com.willfp.ecojobs.EcoJobsPlugin import com.willfp.ecojobs.EcoJobsPlugin
import com.willfp.ecojobs.api.activeJobs
import com.willfp.ecojobs.api.canJoinJob
import com.willfp.ecojobs.api.getJobLevel
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.joinJob
import com.willfp.ecojobs.jobs.Jobs.unlockedJobs import com.willfp.ecojobs.jobs.Jobs.unlockedJobs
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -23,17 +33,6 @@ import kotlin.math.min
object JobsGUI { object JobsGUI {
private lateinit var menu: Menu private lateinit var menu: Menu
private val jobAreaSlots = mutableListOf<Pair<Int, Int>>() private val jobAreaSlots = mutableListOf<Pair<Int, Int>>()
private const val pageKey = "page"
private fun getPage(menu: Menu, player: Player): Int {
val pages = ceil(Jobs.values()
.filter { player.getJobLevel(it) > 0 }
.size.toDouble() / jobAreaSlots.size).toInt()
val page = menu.getState(player, pageKey) ?: 1
return max(min(pages, page + 1), 1)
}
@JvmStatic @JvmStatic
@ConfigUpdater @ConfigUpdater
@@ -54,18 +53,8 @@ object JobsGUI {
} }
private fun buildMenu(plugin: EcoJobsPlugin): Menu { private fun buildMenu(plugin: EcoJobsPlugin): Menu {
val jobInfoItemBuilder = { player: Player, _: Menu ->
val job = player.activeJob
job?.getJobInfoIcon(player)
?: ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.job-info.no-active.item")))
.setDisplayName(plugin.configYml.getFormattedString("gui.job-info.no-active.name"))
.addLoreLines(plugin.configYml.getFormattedStrings("gui.job-info.no-active.lore"))
.build()
}
val jobIconBuilder = { player: Player, menu: Menu, index: Int -> val jobIconBuilder = { player: Player, menu: Menu, index: Int ->
val page = getPage(menu, player) val page = menu.getPage(player)
val unlockedJobs = player.unlockedJobs val unlockedJobs = player.unlockedJobs
@@ -86,13 +75,46 @@ object JobsGUI {
) )
setSlot( setSlot(
plugin.configYml.getInt("gui.job-info.row"), plugin.configYml.getInt("gui.player-info.row"),
plugin.configYml.getInt("gui.job-info.column"), plugin.configYml.getInt("gui.player-info.column"),
slot(jobInfoItemBuilder) { slot { player, _ ->
onLeftClick { event, _, _ -> val skullBuilder = SkullBuilder()
val player = event.whoClicked as Player .setDisplayName(
player.activeJob?.levelGUI?.open(player) plugin.configYml.getString("gui.player-info.name")
.replace("%player%", player.displayName)
.formatEco(player, true)
)
if (player.activeJobs.isEmpty()) {
skullBuilder.addLoreLines(
plugin.configYml.getStrings("gui.player-info.no-jobs")
.formatEco(player, true)
)
} else {
skullBuilder.addLoreLines(
plugin.configYml.getStrings("gui.player-info.has-jobs")
.flatMap {
if (it == "%jobs%") {
player.activeJobs.flatMap { job ->
job.injectPlaceholdersInto(
plugin.configYml.getStrings("gui.player-info.job-line"),
player
)
}
} else {
listOf(it)
}
}
.formatEco(player, true)
)
} }
val skull = skullBuilder.build()
val meta = skull.itemMeta as SkullMeta
meta.owningPlayer = player
skull.itemMeta = meta
skull
} }
) )
@@ -100,10 +122,8 @@ object JobsGUI {
val (row, column) = pair val (row, column) = pair
setSlot(row, column, slot({ p, m -> jobIconBuilder(p, m, index) }) { setSlot(row, column, slot({ p, m -> jobIconBuilder(p, m, index) }) {
onLeftClick { event, _, _ -> onLeftClick { player, _, _, menu ->
val player = event.whoClicked as Player val page = menu.getPage(player)
val page = getPage(menu, player)
val unlockedJobs = player.unlockedJobs val unlockedJobs = player.unlockedJobs
@@ -111,10 +131,22 @@ object JobsGUI {
val job = unlockedJobs.getOrNull(pagedIndex) ?: return@onLeftClick val job = unlockedJobs.getOrNull(pagedIndex) ?: return@onLeftClick
if (player.activeJob == null) { if (player.hasJobActive(job)) {
player.activeJob = job job.levelGUI.open(player)
} else { } else {
player.sendMessage(plugin.langYml.getMessage("leave-current-job")) if (player.canJoinJob(job)) {
player.joinJob(job)
if (player.hasJobActive(job)) {
player.sendMessage(
plugin.langYml.getMessage("joined-job")
.replace("%job%", job.name)
)
}
} else {
player.sendMessage(plugin.langYml.getMessage("cannot-join-job"))
return@onLeftClick
}
} }
player.playSound( player.playSound(
@@ -124,52 +156,58 @@ object JobsGUI {
plugin.configYml.getDouble("gui.job-icon.click.pitch").toFloat() plugin.configYml.getDouble("gui.job-icon.click.pitch").toFloat()
) )
} }
onRightClick { player, _, _, menu ->
val page = menu.getPage(player)
val unlockedJobs = player.unlockedJobs
val pagedIndex = ((page - 1) * jobAreaSlots.size) + index
val job = unlockedJobs.getOrNull(pagedIndex) ?: return@onRightClick
if (player.hasJobActive(job)) {
job.leaveGUI.open(player)
player.playSound(
player.location,
Sound.valueOf(plugin.configYml.getString("gui.job-icon.click.sound").uppercase()),
1f,
plugin.configYml.getDouble("gui.job-icon.click.pitch").toFloat()
)
}
}
}) })
} }
setSlot( addComponent(
plugin.configYml.getInt("gui.prev-page.location.row"), plugin.configYml.getInt("gui.prev-page.location.row"),
plugin.configYml.getInt("gui.prev-page.location.column"), plugin.configYml.getInt("gui.prev-page.location.column"),
slot( PageChanger(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.prev-page.item"))) ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.prev-page.item")))
.setDisplayName(plugin.configYml.getString("gui.prev-page.name")) .setDisplayName(plugin.configYml.getString("gui.prev-page.name"))
.build() .build(),
) { PageChanger.Direction.BACKWARDS
onLeftClick { event, _, menu -> )
val player = event.whoClicked as Player
val page = getPage(menu, player)
val newPage = max(1, page - 1)
menu.setState(player, pageKey, newPage)
}
}
) )
setSlot( addComponent(
plugin.configYml.getInt("gui.next-page.location.row"), plugin.configYml.getInt("gui.next-page.location.row"),
plugin.configYml.getInt("gui.next-page.location.column"), plugin.configYml.getInt("gui.next-page.location.column"),
slot( PageChanger(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.next-page.item"))) ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.next-page.item")))
.setDisplayName(plugin.configYml.getString("gui.next-page.name")) .setDisplayName(plugin.configYml.getString("gui.next-page.name"))
.build() .build(),
) { PageChanger.Direction.FORWARDS
onLeftClick { event, _, menu -> )
val player = event.whoClicked as Player
val pages = ceil(Jobs.values()
.filter { player.getJobLevel(it) > 0 }
.size.toDouble() / jobAreaSlots.size).toInt()
val page = getPage(menu, player)
val newPage = min(pages, page + 1)
menu.setState(player, pageKey, newPage)
}
}
) )
maxPages { player ->
ceil(Jobs.values()
.filter { player.getJobLevel(it) > 0 }
.size.toDouble() / jobAreaSlots.size).toInt()
}
setSlot(plugin.configYml.getInt("gui.close.location.row"), setSlot(plugin.configYml.getInt("gui.close.location.row"),
plugin.configYml.getInt("gui.close.location.column"), plugin.configYml.getInt("gui.close.location.column"),
slot( slot(
@@ -181,19 +219,6 @@ object JobsGUI {
} }
) )
setSlot(plugin.configYml.getInt("gui.deactivate-job.location.row"),
plugin.configYml.getInt("gui.deactivate-job.location.column"),
slot(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.deactivate-job.item")))
.setDisplayName(plugin.configYml.getString("gui.deactivate-job.name"))
.build()
) {
onLeftClick { player, _, _, _ ->
player.activeJob?.leaveGUI?.open(player)
}
}
)
for (config in plugin.configYml.getSubsections("gui.custom-slots")) { for (config in plugin.configYml.getSubsections("gui.custom-slots")) {
setSlot( setSlot(
config.getInt("row"), config.getInt("row"),

View File

@@ -1,6 +1,7 @@
package com.willfp.ecojobs.jobs package com.willfp.ecojobs.jobs
import com.willfp.ecojobs.api.event.PlayerJobLeaveEvent import com.willfp.ecojobs.api.event.PlayerJobLeaveEvent
import com.willfp.ecojobs.api.resetJob
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener

View File

@@ -3,6 +3,10 @@
# by Auxilor # by Auxilor
# #
jobs:
limit: 3 # The most jobs a player can have at once.
# You can set custom limits with the ecojobs.limit.<number> permission
gui: gui:
rows: 6 rows: 6
@@ -20,49 +24,43 @@ gui:
materials: materials:
- black_stained_glass_pane - black_stained_glass_pane
pattern: pattern:
- "111101111"
- "111111111" - "111111111"
- "101000001" - "100000001"
- "111000001" - "100000001"
- "101000001" - "100000001"
- "111111111" - "111101111"
- "111100011"
job-area: job-area:
top-left: top-left:
row: 2 row: 3
column: 4 column: 2
bottom-right: bottom-right:
row: 4 row: 5
column: 8 column: 8
job-info: player-info:
row: 2 row: 1
column: 2 column: 5
no-active: name: "&r%player%&f's Jobs:"
name: "&cNo Active Job"
lore:
- ""
- "&cYou do not currently have a job active"
- "&fPick one from the options below!"
- ""
item: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmFkYzA0OGE3Y2U3OGY3ZGFkNzJhMDdkYTI3ZDg1YzA5MTY4ODFlNTUyMmVlZWQxZTNkYWYyMTdhMzhjMWEifX19
# %join_price% and %leave_price% are also available as placeholders no-jobs:
- ""
- "&cYou do not currently have a job active"
- "&fPick one from the options below!"
- ""
- "&fYou can have up to &a%ecojobs_limit% &fjobs at once!"
- ""
active: has-jobs:
name: "%job% &fLvl. &a%level%" - ""
lore: - "%jobs%"
- "%description%" - ""
- "&f" - "&fJob slots in use: &a%ecojobs_in_jobs%&8/&a%ecojobs_limit%"
- "&fJob Rewards:"
- "%effects%" job-line:
- "" - " %job% &fLvl. &a%level%"
- "&fProgress:"
- "&8» &e%percentage_progress%%"
- "&8» &e%current_xp%&8/&7%required_xp% &fXP"
- ""
- "&eClick to view Level Progression!"
job-icon: job-icon:
name: "%job% &fLvl. &a%level%" name: "%job% &fLvl. &a%level%"
@@ -76,9 +74,16 @@ gui:
- "&8» &e%percentage_progress%%" - "&8» &e%percentage_progress%%"
- "&8» &e%current_xp%&8/&7%required_xp% &fXP" - "&8» &e%current_xp%&8/&7%required_xp% &fXP"
# %join_price% and %leave_price% are also available as placeholders
active-lore: active-lore:
- "" - ""
- "&cYou've already joined this job!" - "&eClick to view Level Progression!"
- "&eRight-click to leave this job!"
too-many-jobs-lore:
- ""
- "&cYou have too many jobs already!"
join-lore: join-lore:
- "" - ""
@@ -93,28 +98,21 @@ gui:
name: "&fPrevious Page" name: "&fPrevious Page"
location: location:
row: 6 row: 6
column: 5 column: 4
next-page: next-page:
item: arrow item: arrow
name: "&fNext Page" name: "&fNext Page"
location: location:
row: 6 row: 6
column: 7 column: 6
close: close:
item: barrier item: barrier
name: "&cClose" name: "&cClose"
location: location:
row: 6 row: 6
column: 6 column: 5
deactivate-job:
item: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTRiZDlhNDViOTY4MWNlYTViMjhjNzBmNzVhNjk1NmIxZjU5NGZlYzg0MGI5NjA3Nzk4ZmIxZTcwNzc2NDQzMCJ9fX0=
name: "&cDeactivate Job"
location:
row: 4
column: 2
# Custom GUI slots; see here for a how-to: https://plugins.auxilor.io/all-plugins/custom-gui-slots # Custom GUI slots; see here for a how-to: https://plugins.auxilor.io/all-plugins/custom-gui-slots
custom-slots: [ ] custom-slots: [ ]

View File

@@ -33,9 +33,10 @@ messages:
joined-job: "&fYou have joined the %job%&f job!" joined-job: "&fYou have joined the %job%&f job!"
left-job: "&fYou have left the %job%&f job!" left-job: "&fYou have left the %job%&f job!"
job-already-joined: "&cYou already have this job!" job-already-joined: "&cYou already have this job!"
leave-current-job: "&cYou must leave your current job before joining a new one!" cannot-join-job: "&cYou are already in too many jobs, please leave one!"
cant-leave-job: "&cYou can't leave the %job%&f job!" cant-leave-job: "&cYou can't leave the %job%&f job!"
dont-have-job: "&cYou don't have this job unlocked!" dont-have-job: "&cYou don't have this job unlocked!"
not-in-job: "&cYou are not in this job!"
menu: menu:
title: "Jobs" title: "Jobs"

View File

@@ -1,4 +1,4 @@
#libreforge-updater #libreforge-updater
#Tue Dec 20 15:16:12 GMT 2022 #Wed Feb 15 20:57:47 GMT 2023
version=1.32.2 version=2.0.0
plugin-name=EcoJobs plugin-name=EcoJobs