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

Compare commits

..

36 Commits

Author SHA1 Message Date
Auxilor
5504a89f1b libreforge-updater 2023-03-02 12:54:27 +00:00
Auxilor
ae3f3ac2d3 Updated to 2.1.2 2023-02-25 17:53:51 +00:00
Auxilor
704dfc65e5 Fixed dependencies 2023-02-25 17:38:56 +00:00
Auxilor
2169581466 libreforge-updater 2023-02-22 15:01:32 +00:00
Auxilor
034265a0e9 Updated to 2.1.0 2023-02-18 12:11:11 +00:00
Will FP
4eded008fe Merge pull request #20 from PQguanfang/master
Add %level_numeral% placeholder in Job Main and Job Level Gui
2023-02-18 12:09:01 +00:00
Will FP
7492f3e548 Merge pull request #21
Fixed inappropriate xp gain handling
2023-02-18 12:07:38 +00:00
_OfTeN_
6bf9d371b9 Fixed leaderboard placeholders updating 2023-02-18 02:20:16 +03:00
_OfTeN_
4034f5098f Added %ecojobs_top_<job>_<place>_<name/amount>% placeholder 2023-02-17 04:45:45 +03:00
_OfTeN_
2014a6dd5f Updated eco 2023-02-17 04:44:33 +03:00
_OfTeN_
bcf5ef3b0f Fixed inappropriate xp gain handling 2023-02-17 03:54:57 +03:00
PQguanfang
7e10899c72 Merge pull request #1 from PQguanfang/dev
Update Job.kt
2023-02-16 15:10:08 +08:00
PQguanfang
9bd24ed4e9 Update Job.kt 2023-02-16 15:07:56 +08:00
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
Auxilor
ed33219f97 libreforge-updater 2022-12-20 15:16:12 +00:00
Auxilor
78cef09786 libreforge-updater 2022-12-12 14:00:27 +00:00
Auxilor
2a72f7d6ce libreforge-updater 2022-12-09 17:00:49 +00:00
Auxilor
762a871354 libreforge-updater 2022-12-05 10:49:59 +00:00
Auxilor
b101e23a86 libreforge-updater 2022-11-30 17:37:48 +00:00
Auxilor
07e1cb7797 libreforge-updater 2022-11-27 21:43:56 +00:00
26 changed files with 607 additions and 564 deletions

View File

@@ -47,9 +47,9 @@ allprojects {
}
dependencies {
compileOnly 'com.willfp:eco:6.46.0'
implementation 'com.willfp:libreforge:3.123.0'
implementation 'com.willfp:ecomponent:1.0.0'
compileOnly 'com.willfp:eco:6.51.1'
implementation 'com.willfp:libreforge:3.129.4'
implementation 'com.willfp:ecomponent:1.3.0'
implementation 'org.joml:joml:1.10.4'
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

@@ -1,8 +1,13 @@
package com.willfp.ecojobs
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.placeholder.DynamicPlaceholder
import com.willfp.eco.core.placeholder.PlayerPlaceholder
import com.willfp.eco.util.toSingletonList
import com.willfp.eco.util.savedDisplayName
import com.willfp.eco.util.toNiceString
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.CommandJobs
import com.willfp.ecojobs.jobs.JobLevelListener
@@ -10,16 +15,16 @@ import com.willfp.ecojobs.jobs.JobTriggerXPGainListener
import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.PriceHandler
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 org.bukkit.event.Listener
import java.util.regex.Pattern
class EcoJobsPlugin : LibReforgePlugin() {
init {
instance = this
registerHolderProvider { it.activeJobLevel?.toSingletonList() ?: emptyList() }
registerHolderProvider { player ->
player.activeJobs.map { it.getLevel(player.getJobLevel(it)) }
}
}
override fun handleEnableAdditional() {
@@ -27,18 +32,13 @@ class EcoJobsPlugin : LibReforgePlugin() {
PlayerPlaceholder(
this,
"job"
) { it.activeJob?.name ?: "" }.register()
"limit"
) { it.jobLimit.toString() }.register()
PlayerPlaceholder(
this,
"job_level"
) { it.activeJobLevel?.level?.toString() ?: "" }.register()
PlayerPlaceholder(
this,
"job_id"
) { it.activeJob?.id ?: "" }.register()
"in_jobs"
) { it.activeJobs.size.toString() }.register()
PlayerPlaceholder(
this,
@@ -50,6 +50,26 @@ class EcoJobsPlugin : LibReforgePlugin() {
}
level.toString()
}.register()
DynamicPlaceholder(
this,
Pattern.compile("top_[a-z]+_[0-9]+_[a-z]+")
) {
val split = it.split("_")
val jobId = split.getOrNull(1) ?: return@DynamicPlaceholder "You must specify the job id!"
val job = Jobs.getByID(jobId) ?: return@DynamicPlaceholder "Invalid job id!"
val placeString = split.getOrNull(2) ?: return@DynamicPlaceholder "You must specify the place!"
val place = placeString.toIntOrNull() ?: return@DynamicPlaceholder "Invalid place!"
val type = split.getOrNull(3) ?: return@DynamicPlaceholder "You must specify the top type!"
val topEntry = job.getTop(place)
return@DynamicPlaceholder when(type) {
"name" -> topEntry?.player?.savedDisplayName
?: this.langYml.getFormattedString("top.name-empty")
"amount" -> topEntry?.amount?.toNiceString()
?: this.langYml.getFormattedString("top.amount-empty")
else -> "Invalid type: $type! Available types: name/amount"
}
}.register()
}
override fun loadPluginCommands(): List<PluginCommand> {

View File

@@ -1,117 +1,192 @@
@file:JvmName("EcoJobsAPI")
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.Jobs
import com.willfp.ecojobs.jobs.getNumericalPermission
import com.willfp.ecojobs.jobs.jobExperienceMultiplier
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import kotlin.math.abs
interface EcoJobsAPI {
/**
* 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
private val plugin = EcoJobsPlugin.instance
/**
* Get a player's active job.
*
* @param player The player.
* @return The active job.
*/
fun getActiveJob(
player: OfflinePlayer
): Job?
/*
/**
* 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
The old key is around for backwards compatibility with 1.x.x.
/**
* 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
)
*/
/**
* Give job experience to a player.
*
* @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 legacyActiveJobKey: PersistentDataKey<String> = PersistentDataKey(
plugin.namespacedKeyFactory.create("active_job"), PersistentDataKeyType.STRING, ""
)
/**
* Get progress to next level between 0 and 1, where 0 is none and 1 is complete.
*
* @param player The player.
* @param job The job.
* @return The progress.
*/
fun getJobProgress(
player: OfflinePlayer,
job: Job
): Double
private val activeJobsKey: PersistentDataKey<List<String>> = PersistentDataKey(
plugin.namespacedKeyFactory.create("active_jobs"), PersistentDataKeyType.STRING_LIST, listOf()
)
/**
* Get the experience required to advance to the next level.
*
* @param player The player.
* @param job The job.
* @return The experience required.
*/
fun getJobXPRequired(
player: OfflinePlayer,
job: Job
): Int
/**
* The job limit.
*/
val Player.jobLimit: Int
get() {
return this.getNumericalPermission("ecojobs.limit", plugin.configYml.getDouble("jobs.limit")).toInt()
}
/**
* Get experience to the next level.
*
* @param player The player.
* @param job The job.
* @return The experience.
*/
fun getJobXP(
player: OfflinePlayer,
job: Job
): Double
/**
* If a player can join a job.
*/
fun Player.canJoinJob(job: Job): Boolean {
return this.activeJobs.size < this.jobLimit && job !in this.activeJobs && this.hasJob(job)
}
companion object {
/**
* Get the instance of the API.
*
* @return The API.
*/
@JvmStatic
val instance: EcoJobsAPI
get() = EcoJobsAPIImpl
/**
* Get if a job is unlocked.
*/
fun OfflinePlayer.hasJob(job: Job) = this.getJobLevel(job) > 0
/**
* Get if a player has a job active.
*/
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(
who: Player,
val job: Job,
override val job: Job,
var amount: Double,
val isMultiply: Boolean
) : PlayerEvent(who), Cancellable {
) : PlayerEvent(who), Cancellable, JobEvent {
private var cancelled = false
override fun setCancelled(cancel: Boolean) {

View File

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

View File

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

View File

@@ -7,9 +7,9 @@ import org.bukkit.event.HandlerList
class PlayerJobLevelUpEvent(
who: Player,
val job: Job,
override val job: Job,
val level: Int
) : PlayerEvent(who) {
) : PlayerEvent(who), JobEvent {
override fun getHandlers(): 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.savedDisplayName
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.giveExactJobExperience
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.Bukkit
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.command.impl.PluginCommand
import com.willfp.ecojobs.api.hasJob
import com.willfp.ecojobs.jobs.Jobs
import com.willfp.ecojobs.jobs.JobsGUI
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.entity.Player
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.command.impl.Subcommand
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.activeJob
import com.willfp.ecojobs.jobs.hasJob
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
@@ -28,13 +30,13 @@ class CommandJoin(plugin: EcoPlugin) : Subcommand(plugin, "join", "ecojobs.comma
return
}
if (player.activeJob == job) {
if (player.hasJobActive(job)) {
player.sendMessage(plugin.langYml.getMessage("job-already-joined"))
return
}
if (player.activeJob != null) {
player.sendMessage(plugin.langYml.getMessage("leave-current-job"))
if (!player.canJoinJob(job)) {
player.sendMessage(plugin.langYml.getMessage("cannot-join-job"))
return
}
@@ -42,7 +44,8 @@ class CommandJoin(plugin: EcoPlugin) : Subcommand(plugin, "join", "ecojobs.comma
plugin.langYml.getMessage("joined-job", StringUtils.FormatOption.WITHOUT_PLACEHOLDERS)
.replace("%job%", job.name)
)
player.activeJob = job
player.joinJob(job)
}
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>()
if (args.isEmpty()) {
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.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.entity.Player
import org.bukkit.util.StringUtil
class CommandLeave(plugin: EcoPlugin) : Subcommand(plugin, "leave", "ecojobs.command.leave", true) {
override fun onExecute(player: CommandSender, args: List<String>) {
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"))
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(
plugin.langYml.getMessage("left-job")
.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.util.StringUtils
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.activeJob
import com.willfp.ecojobs.jobs.hasJob
import com.willfp.ecojobs.jobs.resetJob
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
@@ -44,9 +44,7 @@ class CommandReset(plugin: EcoPlugin) : Subcommand(plugin, "reset", "ecojobs.com
return
}
if (player.activeJob == job) {
player.activeJob = null
}
player.forceLeaveJob(job)
player.resetJob(job)
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.util.StringUtils
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.hasJob
import com.willfp.ecojobs.jobs.setJobLevel
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
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.toNiceString
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.PlayerJobJoinEvent
import com.willfp.ecojobs.api.event.PlayerJobLeaveEvent
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.ConfiguredCondition
import com.willfp.libreforge.effects.ConfiguredEffect
@@ -30,28 +38,31 @@ import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.time.Duration
import java.util.DoubleSummaryStatistics
import java.util.Objects
import java.util.concurrent.TimeUnit
import kotlin.math.abs
import kotlin.math.max
class Job(
val id: String,
val config: Config,
private val plugin: EcoJobsPlugin
val id: String, val config: Config, private val plugin: EcoJobsPlugin
) {
private val topCache = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(plugin.configYml.getInt("leaderboard-cache-lifetime").toLong()))
.build<Int, LeaderboardCacheEntry?>()
val name = config.getFormattedString("name")
val description = config.getFormattedString("description")
val isUnlockedByDefault = config.getBool("unlocked-by-default")
val resetsOnQuit = config.getBool("reset-on-quit")
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(
PriceEconomy(config.getDouble("leave-price")),
""
PriceEconomy(config.getDouble("leave-price")), ""
)
val levelKey: PersistentDataKey<Int> = PersistentDataKey(
@@ -61,9 +72,7 @@ class Job(
)
val xpKey: PersistentDataKey<Double> = PersistentDataKey(
EcoJobsPlugin.instance.namespacedKeyFactory.create("${id}_xp"),
PersistentDataKeyType.DOUBLE,
0.0
EcoJobsPlugin.instance.namespacedKeyFactory.create("${id}_xp"), PersistentDataKeyType.DOUBLE, 0.0
)
private val levelXpRequirements = listOf(0) + config.getInts("level-xp-requirements")
@@ -79,41 +88,33 @@ class Job(
private val effects: Set<ConfiguredEffect>
private val conditions: Set<ConfiguredCondition>
private val levels = Caffeine.newBuilder()
.build<Int, JobLevel>()
private val effectsDescription = 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 levels = Caffeine.newBuilder().build<Int, JobLevel>()
private val effectsDescription = 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 levelPlaceholders = config.getSubsections("level-placeholders")
.map { sub ->
LevelPlaceholder(
sub.getString("id")
) {
NumberUtils.evaluateExpression(
sub.getString("value")
.replace("%level%", it.toString())
).toNiceString()
}
private val levelPlaceholders = config.getSubsections("level-placeholders").map { sub ->
LevelPlaceholder(
sub.getString("id")
) {
NumberUtils.evaluateExpression(
sub.getString("value").replace("%level%", it.toString())
).toNiceString()
}
}
private val jobXpGains = config.getSubsections("xp-gain-methods").mapNotNull {
Counters.compile(it, "Job $id")
}
init {
config.injectPlaceholders(
PlayerStaticPlaceholder(
"level"
) { p ->
p.getJobLevel(this).toString()
}
)
config.injectPlaceholders(PlayerStaticPlaceholder(
"level"
) { p ->
p.getJobLevel(this).toString()
})
effects = config.getSubsections("effects").mapNotNull {
Effects.compile(it, "Job $id")
@@ -143,52 +144,51 @@ class Job(
}
PlayerPlaceholder(
plugin,
"${id}_percentage_progress"
plugin, "${id}_percentage_progress"
) {
(it.getJobProgress(this) * 100).toNiceString()
}.register()
PlayerPlaceholder(
plugin,
id
plugin, id
) {
it.getJobLevel(this).toString()
}.register()
PlayerPlaceholder(
plugin,
"${id}_current_xp"
plugin, "${id}_current_xp"
) {
NumberUtils.format(it.getJobXP(this))
}.register()
PlayerPlaceholder(
plugin,
"${id}_required_xp"
plugin, "${id}_required_xp"
) {
it.getJobXPRequired(this).toString()
}.register()
PlayerlessPlaceholder(
plugin,
"${id}_name"
plugin, "${id}_name"
) {
this.name
}.register()
PlayerPlaceholder(
plugin,
"${id}_level"
plugin, "${id}_level"
) {
it.getJobLevel(this).toString()
}.register()
PlayerPlaceholder(
plugin,
"${id}_total_players"
plugin, "${id}_active"
) {
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()
}
@@ -208,13 +208,11 @@ class Job(
}
}
this.config.getStrings("level-up-messages.$highestConfiguredLevel")
.map {
levelPlaceholders.format(it, level)
}
.map {
" ".repeat(whitespace) + it
}
this.config.getStrings("level-up-messages.$highestConfiguredLevel").map {
levelPlaceholders.format(it, level)
}.map {
" ".repeat(whitespace) + it
}
}
private fun getEffectsDescription(level: Int, whitespace: Int = 0): List<String> = effectsDescription.get(level) {
@@ -229,13 +227,11 @@ class Job(
}
}
this.config.getStrings("effects-description.$highestConfiguredLevel")
.map {
levelPlaceholders.format(it, level)
}
.map {
" ".repeat(whitespace) + it
}
this.config.getStrings("effects-description.$highestConfiguredLevel").map {
levelPlaceholders.format(it, level)
}.map {
" ".repeat(whitespace) + it
}
}
private fun getRewardsDescription(level: Int, whitespace: Int = 0): List<String> = rewardsDescription.get(level) {
@@ -250,53 +246,41 @@ class Job(
}
}
this.config.getStrings("rewards-description.$highestConfiguredLevel")
.map {
levelPlaceholders.format(it, level)
}
.map {
" ".repeat(whitespace) + it
}
this.config.getStrings("rewards-description.$highestConfiguredLevel").map {
levelPlaceholders.format(it, level)
}.map {
" ".repeat(whitespace) + it
}
}
private fun getLeaveLore(level: Int, whitespace: Int = 0): List<String> =
this.config.getStrings("leave-lore")
.map {
levelPlaceholders.format(it, level)
}
.map {
" ".repeat(whitespace) + it
}
private fun getLeaveLore(level: Int, whitespace: Int = 0): List<String> = this.config.getStrings("leave-lore").map {
levelPlaceholders.format(it, level)
}.map {
" ".repeat(whitespace) + it
}
private fun getJoinLore(level: Int, whitespace: Int = 0): List<String> =
this.config.getStrings("join-lore")
.map {
levelPlaceholders.format(it, level)
}
.map {
" ".repeat(whitespace) + it
}
private fun getJoinLore(level: Int, whitespace: Int = 0): List<String> = this.config.getStrings("join-lore").map {
levelPlaceholders.format(it, level)
}.map {
" ".repeat(whitespace) + it
}
fun injectPlaceholdersInto(lore: List<String>, player: Player, forceLevel: Int? = null): List<String> {
val withPlaceholders = lore
.map {
it.replace("%percentage_progress%", (player.getJobProgress(this) * 100).toNiceString())
.replace("%current_xp%", player.getJobXP(this).toNiceString())
.replace("%required_xp%", this.getExpForLevel(player.getJobLevel(this) + 1).let { req ->
if (req == Int.MAX_VALUE) {
plugin.langYml.getFormattedString("infinity")
} else {
req.toNiceString()
}
val withPlaceholders = lore.map {
it.replace("%percentage_progress%", (player.getJobProgress(this) * 100).toNiceString())
.replace("%current_xp%", player.getJobXP(this).toNiceString())
.replace("%required_xp%", this.getExpForLevel(player.getJobLevel(this) + 1).let { req ->
if (req == Int.MAX_VALUE) {
plugin.langYml.getFormattedString("infinity")
} else {
req.toNiceString()
}
)
.replace("%description%", this.description)
.replace("%job%", this.name)
.replace("%level%", (forceLevel ?: player.getJobLevel(this)).toString())
.replace("%join_price%", this.joinPrice.getDisplay(player))
.replace("%leave_price%", this.leavePrice.getDisplay(player))
}
.toMutableList()
}).replace("%description%", this.description).replace("%job%", this.name)
.replace("%level%", (forceLevel ?: player.getJobLevel(this)).toString())
.replace("%level_numeral%", NumberUtils.toNumeral(forceLevel ?: player.getJobLevel(this)))
.replace("%join_price%", this.joinPrice.getDisplay(player))
.replace("%leave_price%", this.leavePrice.getDisplay(player))
}.toMutableList()
val processed = mutableListOf<List<String>>()
@@ -328,35 +312,32 @@ class Job(
val level = player.getJobLevel(this)
return ItemStackBuilder(base)
.setDisplayName(
plugin.configYml.getFormattedString("gui.job-icon.name")
.replace("%level%", level.toString())
.replace("%job%", this.name)
)
.addLoreLines {
injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-icon.lore"), player) +
when (player.activeJob) {
this -> plugin.configYml.getStrings("gui.job-icon.active-lore")
null -> plugin.configYml.getStrings("gui.job-icon.join-lore")
else -> emptyList()
}
return ItemStackBuilder(base).setDisplayName(
plugin.configYml.getFormattedString("gui.job-icon.name").replace("%level%", level.toString())
.replace("%level_numeral%", NumberUtils.toNumeral(level)).replace("%job%", this.name)
).addLoreLines {
injectPlaceholdersInto(
plugin.configYml.getStrings("gui.job-icon.lore"), player
) + if (player.hasJobActive(this)) {
plugin.configYml.getStrings("gui.job-icon.active-lore")
} else if (player.canJoinJob(this)) {
plugin.configYml.getStrings("gui.job-icon.join-lore")
} else if (player.activeJobs.size == player.jobLimit) {
plugin.configYml.getStrings("gui.job-icon.too-many-jobs-lore")
} else {
emptyList()
}
.build()
}.build()
}
fun getJobInfoIcon(player: Player): ItemStack {
val base = baseItem.clone()
return ItemStackBuilder(base)
.setDisplayName(
plugin.configYml.getFormattedString("gui.job-info.active.name")
.replace("%level%", player.getJobLevel(this).toString())
.replace("%job%", this.name)
)
.addLoreLines {
injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-info.active.lore"), player)
}
.build()
return ItemStackBuilder(base).setDisplayName(
plugin.configYml.getFormattedString("gui.job-info.active.name")
.replace("%level%", player.getJobLevel(this).toString()).replace("%level_numeral%", NumberUtils.toNumeral(player.getJobLevel(this))).replace("%job%", this.name)
).addLoreLines {
injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-info.active.lore"), player)
}.build()
}
fun getExpForLevel(level: Int): Int {
@@ -379,6 +360,14 @@ class Job(
return jobXpGains.sumOf { it.getCount(event) }
}
fun getTop(place: Int): LeaderboardCacheEntry? {
return topCache.get(place) {
val players = Bukkit.getOfflinePlayers().sortedByDescending { it.getJobLevel(this) }
val target = players.getOrNull(place-1) ?: return@get null
return@get LeaderboardCacheEntry(target, target.getJobLevel(this))
}
}
override fun equals(other: Any?): Boolean {
if (other !is Job) {
return false
@@ -393,12 +382,16 @@ class Job(
}
private class LevelPlaceholder(
val id: String,
private val function: (Int) -> String
val id: String, private val function: (Int) -> String
) {
operator fun invoke(level: Int) = function(level)
}
data class LeaderboardCacheEntry(
val player: OfflinePlayer,
val amount: Int
)
private fun Collection<LevelPlaceholder>.format(string: String, level: Int): String {
var process = string
for (placeholder in this) {
@@ -407,86 +400,12 @@ private fun Collection<LevelPlaceholder>.format(string: String, level: Int): Str
return process
}
private val activeJobKey: PersistentDataKey<String> = PersistentDataKey(
EcoJobsPlugin.instance.namespacedKeyFactory.create("active_job"),
PersistentDataKeyType.STRING,
""
)
fun OfflinePlayer.getJobLevelObject(job: Job): JobLevel = job.getLevel(this.getJobLevel(job))
var OfflinePlayer.activeJob: Job?
get() = Jobs.getByID(this.profile.read(activeJobKey))
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)
private val expMultiplierCache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build<Player, Double> {
it.cacheJobExperienceMultiplier()
}
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
get() = expMultiplierCache.get(this)
@@ -507,43 +426,19 @@ private fun Player.cacheJobExperienceMultiplier(): Double {
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) {
val permission = permissionAttachmentInfo.permission
if (permission.startsWith(prefix)) {
return ((permission.substring(permission.lastIndexOf(".") + 1).toDoubleOrNull() ?: 100.0) / 100) + 1
val perm = permissionAttachmentInfo.permission
if (perm.startsWith(permission)) {
val found = perm.substring(perm.lastIndexOf(".") + 1).toDoubleOrNull() ?: continue
highest = max(highest ?: Double.MIN_VALUE, found)
}
}
return 1.0
}
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)
}
return highest ?: default
}

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.builder.ItemStackBuilder
import com.willfp.eco.util.formatEco
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.leaveJob
import org.bukkit.entity.Player
class JobLeaveGUI(
@@ -59,9 +61,9 @@ class JobLeaveGUI(
.build()
}) {
onLeftClick { player, _, _, _ ->
player.activeJob = null
player.leaveJob(job)
if (player.activeJob == null) {
if (!player.hasJobActive(job)) {
player.sendMessage(
plugin.langYml.getMessage("left-job")
.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.builder.ItemStackBuilder
import com.willfp.eco.util.NumberUtils
import com.willfp.ecojobs.api.getJobLevel
import com.willfp.ecomponent.components.LevelComponent
import com.willfp.ecomponent.components.LevelState
import org.bukkit.entity.Player

View File

@@ -1,5 +1,7 @@
package com.willfp.ecojobs.jobs
import com.willfp.ecojobs.api.activeJobs
import com.willfp.ecojobs.api.giveJobExperience
import com.willfp.libreforge.events.TriggerPreProcessEvent
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
@@ -9,14 +11,16 @@ object JobTriggerXPGainListener : Listener {
fun handle(event: TriggerPreProcessEvent) {
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) {
return
if (amount <= 0.0) {
continue
}
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.willfp.eco.core.config.updating.ConfigUpdater
import com.willfp.ecojobs.EcoJobsPlugin
import com.willfp.ecojobs.api.getJobLevel
import org.bukkit.OfflinePlayer
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.Menu
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.ConfigSlot
import com.willfp.eco.core.gui.slot.FillerMask
import com.willfp.eco.core.gui.slot.MaskItems
import com.willfp.eco.core.items.Items
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.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 org.bukkit.Material
import org.bukkit.Sound
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@@ -23,17 +33,6 @@ import kotlin.math.min
object JobsGUI {
private lateinit var menu: Menu
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
@ConfigUpdater
@@ -54,18 +53,8 @@ object JobsGUI {
}
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 page = getPage(menu, player)
val page = menu.getPage(player)
val unlockedJobs = player.unlockedJobs
@@ -86,13 +75,46 @@ object JobsGUI {
)
setSlot(
plugin.configYml.getInt("gui.job-info.row"),
plugin.configYml.getInt("gui.job-info.column"),
slot(jobInfoItemBuilder) {
onLeftClick { event, _, _ ->
val player = event.whoClicked as Player
player.activeJob?.levelGUI?.open(player)
plugin.configYml.getInt("gui.player-info.row"),
plugin.configYml.getInt("gui.player-info.column"),
slot { player, _ ->
val skullBuilder = SkullBuilder()
.setDisplayName(
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
setSlot(row, column, slot({ p, m -> jobIconBuilder(p, m, index) }) {
onLeftClick { event, _, _ ->
val player = event.whoClicked as Player
val page = getPage(menu, player)
onLeftClick { player, _, _, menu ->
val page = menu.getPage(player)
val unlockedJobs = player.unlockedJobs
@@ -111,10 +131,22 @@ object JobsGUI {
val job = unlockedJobs.getOrNull(pagedIndex) ?: return@onLeftClick
if (player.activeJob == null) {
player.activeJob = job
if (player.hasJobActive(job)) {
job.levelGUI.open(player)
} 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(
@@ -124,52 +156,58 @@ object JobsGUI {
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.column"),
slot(
PageChanger(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.prev-page.item")))
.setDisplayName(plugin.configYml.getString("gui.prev-page.name"))
.build()
) {
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)
}
}
.build(),
PageChanger.Direction.BACKWARDS
)
)
setSlot(
addComponent(
plugin.configYml.getInt("gui.next-page.location.row"),
plugin.configYml.getInt("gui.next-page.location.column"),
slot(
PageChanger(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("gui.next-page.item")))
.setDisplayName(plugin.configYml.getString("gui.next-page.name"))
.build()
) {
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)
}
}
.build(),
PageChanger.Direction.FORWARDS
)
)
maxPages { player ->
ceil(Jobs.values()
.filter { player.getJobLevel(it) > 0 }
.size.toDouble() / jobAreaSlots.size).toInt()
}
setSlot(plugin.configYml.getInt("gui.close.location.row"),
plugin.configYml.getInt("gui.close.location.column"),
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")) {
setSlot(
config.getInt("row"),

View File

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

View File

@@ -3,6 +3,10 @@
# 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:
rows: 6
@@ -20,49 +24,43 @@ gui:
materials:
- black_stained_glass_pane
pattern:
- "111101111"
- "111111111"
- "101000001"
- "111000001"
- "101000001"
- "111111111"
- "111100011"
- "100000001"
- "100000001"
- "100000001"
- "111101111"
job-area:
top-left:
row: 2
column: 4
row: 3
column: 2
bottom-right:
row: 4
row: 5
column: 8
job-info:
row: 2
column: 2
player-info:
row: 1
column: 5
no-active:
name: "&cNo Active Job"
lore:
- ""
- "&cYou do not currently have a job active"
- "&fPick one from the options below!"
- ""
item: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmFkYzA0OGE3Y2U3OGY3ZGFkNzJhMDdkYTI3ZDg1YzA5MTY4ODFlNTUyMmVlZWQxZTNkYWYyMTdhMzhjMWEifX19
name: "&r%player%&f's Jobs:"
# %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:
name: "%job% &fLvl. &a%level%"
lore:
- "%description%"
- "&f"
- "&fJob Rewards:"
- "%effects%"
- ""
- "&fProgress:"
- "&8» &e%percentage_progress%%"
- "&8» &e%current_xp%&8/&7%required_xp% &fXP"
- ""
- "&eClick to view Level Progression!"
has-jobs:
- ""
- "%jobs%"
- ""
- "&fJob slots in use: &a%ecojobs_in_jobs%&8/&a%ecojobs_limit%"
job-line:
- " %job% &fLvl. &a%level%"
job-icon:
name: "%job% &fLvl. &a%level%"
@@ -76,9 +74,16 @@ gui:
- "&8» &e%percentage_progress%%"
- "&8» &e%current_xp%&8/&7%required_xp% &fXP"
# %join_price% and %leave_price% are also available as placeholders
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:
- ""
@@ -93,28 +98,21 @@ gui:
name: "&fPrevious Page"
location:
row: 6
column: 5
column: 4
next-page:
item: arrow
name: "&fNext Page"
location:
row: 6
column: 7
column: 6
close:
item: barrier
name: "&cClose"
location:
row: 6
column: 6
deactivate-job:
item: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTRiZDlhNDViOTY4MWNlYTViMjhjNzBmNzVhNjk1NmIxZjU5NGZlYzg0MGI5NjA3Nzk4ZmIxZTcwNzc2NDQzMCJ9fX0=
name: "&cDeactivate Job"
location:
row: 4
column: 2
column: 5
# Custom GUI slots; see here for a how-to: https://plugins.auxilor.io/all-plugins/custom-gui-slots
custom-slots: [ ]
@@ -314,6 +312,7 @@ point-names: # If you have point names that look ugly (eg g_souls) then you can
use-faster-move-trigger: true # Disable if you want move trigger to detect sub-1-block movements
raytrace-distance: 80 # The distance that alt_click should check for a location
leaderboard-cache-lifetime: 180 # How often will top placeholders update their cache (in seconds)
potions:
icon:

View File

@@ -33,11 +33,16 @@ messages:
joined-job: "&fYou have joined the %job%&f job!"
left-job: "&fYou have left the %job%&f 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!"
dont-have-job: "&cYou don't have this job unlocked!"
not-in-job: "&cYou are not in this job!"
menu:
title: "Jobs"
infinity: "∞"
top:
name-empty: "&cEmpty"
amount-empty: "0"

View File

@@ -7,8 +7,6 @@ website: willfp.com
load: STARTUP
depend:
- eco
- ProtocolLib
- PlaceholderAPI
softdepend:
- AureliumSkills
- Vault

View File

@@ -1,4 +1,4 @@
#libreforge-updater
#Sat Nov 26 19:19:52 GMT 2022
version=1.29.0
#Thu Mar 02 12:54:27 GMT 2023
version=2.1.3
plugin-name=EcoJobs