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

Compare commits

...

83 Commits

Author SHA1 Message Date
Auxilor
edf978fa28 libreforge-updater 2023-03-13 13:38:53 +00:00
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
Auxilor
409a84aea8 libreforge-updater 2022-11-26 19:19:52 +00:00
Auxilor
886b2e0c06 libreforge-updater 2022-11-24 14:29:42 +00:00
Auxilor
0ff5dcf670 libreforge-updater 2022-11-23 17:24:43 +00:00
Auxilor
1b93969d3b libreforge-updater 2022-11-21 16:02:22 +00:00
Auxilor
af5f5cfba6 Updated to 1.26.2 2022-11-21 12:59:11 +00:00
Auxilor
2c726fd9ac Fixed jumberjack 2022-11-21 12:59:03 +00:00
Auxilor
8dbe453bc5 Fix 2022-11-21 12:57:23 +00:00
Auxilor
768d2218d0 Fix 2022-11-21 11:45:20 +00:00
Auxilor
e212762951 Fix 2022-11-21 11:45:05 +00:00
Auxilor
c5d7e07d49 Updated to 1.26.1 2022-11-21 11:41:54 +00:00
Auxilor
88d118cdd7 Added /jobs <job> 2022-11-21 11:41:46 +00:00
Auxilor
b08616b01e libreforge-updater 2022-11-17 08:37:53 +00:00
Auxilor
3811e46fac libreforge-updater 2022-11-12 17:33:55 +00:00
Auxilor
b16905e435 Improved default config 2022-11-07 12:49:10 +00:00
Auxilor
e0084d61f7 Added %join_lore% and %leave_lore% 2022-11-07 12:47:15 +00:00
Auxilor
3606d5f12b Updated to 1.24.0 2022-11-07 12:36:37 +00:00
Auxilor
599b2800d8 Added confirmation GUI, improved prices 2022-11-07 12:36:28 +00:00
Auxilor
e4af959945 libreforge-updater 2022-11-06 19:51:41 +00:00
Auxilor
d1af2e25e7 Reworked Level GUI to use ecomponent 2022-11-06 15:29:38 +00:00
Auxilor
1eb0bdb639 libreforge-updater 2022-11-01 16:22:52 +00:00
Will FP
85e117ba78 Merge pull request #12 from 0ft3n/master
/jobs join tabcompletion fix (hardest fix in my life)
2022-11-01 16:18:08 +00:00
_OfTeN_
e27077dd71 Hardest fix in my life 2022-10-29 15:49:50 +03:00
Auxilor
17f471228d libreforge-updater 2022-10-28 11:34:14 +01:00
Auxilor
14d0997690 libreforge-updater 2022-10-26 22:10:07 +01:00
Auxilor
0f449aa7cb libreforge-updater 2022-10-26 22:09:18 +01:00
Auxilor
5ad4401762 Fix 2022-10-24 17:24:16 +01:00
Auxilor
e54712c960 Fix 2022-10-24 17:07:54 +01:00
Auxilor
72aa54a9e3 libreforge-updater 2022-10-24 17:04:52 +01:00
Auxilor
d9487300d7 Merge branch 'Kristopherrr_master'
# Conflicts:
#	build.gradle
#	eco-core/core-plugin/src/main/kotlin/com/willfp/ecojobs/EcoJobsPlugin.kt
2022-10-24 17:02:44 +01:00
Auxilor
9911244f34 PR Cleanup 2022-10-24 17:02:25 +01:00
Auxilor
2447dc0279 Lots of cleanup 2022-10-24 17:01:43 +01:00
Will FP
3b949c0119 Merge pull request #8
Added placeholder to get total number of players with specific job
2022-10-24 16:50:12 +01:00
Auxilor
a8f11ffb44 libreforge-updater 2022-10-21 19:25:32 +01:00
Auxilor
3829e326ba libreforge-updater 2022-10-19 20:43:19 +01:00
Auxilor
26d44fb2d9 libreforge-updater 2022-10-18 14:12:39 +01:00
Auxilor
5cba64d19f libreforge-updater 2022-10-16 23:26:32 +01:00
Auxilor
52f3230307 libreforge-updater 2022-10-13 20:11:59 +01:00
ItsMeKrisK
ad16872efd Job Changes n Additions
Added Enchanter/ Lumberjack/ Smelter job and fixed some things in beekeeper
2022-10-11 19:25:54 +03:00
ItsMeKrisK
21ab58b425 Placeholder for the total levels of player's jobs
**The placeholder:** 😉
• `%ecojobs_total_jobs_level%`
2022-10-11 03:11:24 +03:00
Bruno de Araujo Alves
c80dd2371c Added placeholder to get total number of players with specific job
- %ecojobs_<id>_total_players%
2022-10-10 18:50:05 -03:00
Auxilor
e0b2ae4f40 libreforge-updater 2022-10-10 21:03:58 +01:00
Auxilor
b9f7d6db42 libreforge-updater 2022-10-09 15:02:04 +01:00
Will FP
f62476c1d4 Merge pull request #7 from badomensurvival/master
Job level placeholder
2022-10-09 14:50:32 +01:00
Will FP
ca25f78c60 Merge pull request #6 from 0ft3n/master
Fixed default builder.yml
2022-10-09 14:50:17 +01:00
devbaraus
765d7ee7e2 Job level placeholder 2022-10-09 10:26:34 -03:00
0ft3n
850e1ec888 Fixed default builder.yml 2022-10-09 01:26:47 +03:00
42 changed files with 1435 additions and 938 deletions

View File

@@ -42,12 +42,14 @@ allprojects {
shadowJar { shadowJar {
relocate('com.willfp.libreforge', 'com.willfp.ecojobs.libreforge') relocate('com.willfp.libreforge', 'com.willfp.ecojobs.libreforge')
relocate('com.willfp.ecomponent', 'com.willfp.ecojobs.ecomponent')
relocate('org.joml', 'com.willfp.ecojobs.libreforge.joml') relocate('org.joml', 'com.willfp.ecojobs.libreforge.joml')
} }
dependencies { dependencies {
compileOnly 'com.willfp:eco:6.42.0' compileOnly 'com.willfp:eco:6.51.1'
implementation 'com.willfp:libreforge:3.110.0' implementation 'com.willfp:libreforge:3.129.5'
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,184 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
- the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
- the Javadoc guidelines at
https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
- the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
https://checkstyle.org (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<module name="SuppressionFilter">
<property name="file" value="config/checkstyle/suppression.xml"/>
</module>
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
https://checkstyle.org/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- Checks whether files end with a new line. -->
<!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See https://checkstyle.org/config_misc.html#Translation -->
<module name="Translation"/>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/config_sizes.html -->
<module name="FileLength"/>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="200"/>
</module>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/config_misc.html -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- Checks for Headers -->
<!-- See https://checkstyle.org/config_header.html -->
<!-- <module name="Header"> -->
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
<!-- <property name="fileExtensions" value="java"/> -->
<!-- </module> -->
<module name="TreeWalker">
<!-- Checks for Javadoc comments. -->
<!-- See https://checkstyle.org/config_javadoc.html -->
<module name="InvalidJavadocPosition"/>
<module name="JavadocMethod"/>
<module name="JavadocType"/>
<module name="JavadocVariable"/>
<module name="JavadocStyle"/>
<module name="MissingJavadocMethod"/>
<!-- Checks for Naming Conventions. -->
<!-- See https://checkstyle.org/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See https://checkstyle.org/config_imports.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports">
<property name="processJavadoc" value="true"/>
</module>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/config_sizes.html -->
<!-- <module name="MethodLength"/> -->
<module name="ParameterNumber"/>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See https://checkstyle.org/config_modifiers.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See https://checkstyle.org/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See https://checkstyle.org/config_coding.html -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<!-- <module name="MagicNumber"/> -->
<module name="MissingSwitchDefault"/>
<module name="MultipleVariableDeclarations"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See https://checkstyle.org/config_design.html -->
<!-- <module name="DesignForExtension"/> -->
<module name="FinalClass"/>
<!-- <module name="HideUtilityClassConstructor"/> -->
<module name="InterfaceIsType"/>
<module name="VisibilityModifier"/>
<module name="RequireThis"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/config_misc.html -->
<module name="ArrayTypeStyle"/>
<module name="FinalParameters"/>
<module name="TodoComment"/>
<module name="UpperEll"/>
</module>
</module>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<!-- Fields don't need javadoc -->
<suppress files="Effects.java" checks="JavadocVariable"/>
<suppress files="Skills.java" checks="JavadocVariable"/>
<suppress files="Stats.java" checks="JavadocVariable"/>
</suppressions>

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,23 +1,30 @@
package com.willfp.ecojobs 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.DynamicPlaceholder
import com.willfp.eco.core.placeholder.PlayerPlaceholder 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.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
import com.willfp.ecojobs.jobs.JobTriggerXPGainListener import com.willfp.ecojobs.jobs.JobTriggerXPGainListener
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.libreforge.LibReforgePlugin import com.willfp.libreforge.LibReforgePlugin
import org.bukkit.event.Listener import org.bukkit.event.Listener
import java.util.regex.Pattern
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() {
@@ -25,13 +32,44 @@ class EcoJobsPlugin : LibReforgePlugin() {
PlayerPlaceholder( PlayerPlaceholder(
this, this,
"job" "limit"
) { it.activeJob?.name ?: "" }.register() ) { it.jobLimit.toString() }.register()
PlayerPlaceholder( PlayerPlaceholder(
this, this,
"job_id" "in_jobs"
) { it.activeJob?.id ?: "" }.register() ) { it.activeJobs.size.toString() }.register()
PlayerPlaceholder(
this,
"total_job_level"
) {
var level = 0
for (job in Jobs.values()) {
level += it.getJobLevel(job)
}
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> { override fun loadPluginCommands(): List<PluginCommand> {

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.
* @param job The job.
* @return The experience required.
*/ */
fun getJobXPRequired( val Player.jobLimit: Int
player: OfflinePlayer, get() {
job: Job return this.getNumericalPermission("ecojobs.limit", plugin.configYml.getDouble("jobs.limit")).toInt()
): Int }
/** /**
* Get experience to the next level. * If a player can join a job.
*
* @param player The player.
* @param job The job.
* @return The experience.
*/ */
fun getJobXP( fun Player.canJoinJob(job: Job): Boolean {
player: OfflinePlayer, return this.activeJobs.size < this.jobLimit && job !in this.activeJobs && this.hasJob(job)
job: Job }
): Double
companion object { /**
/** * Get if a job is unlocked.
* Get the instance of the API.
*
* @return The API.
*/ */
@JvmStatic fun OfflinePlayer.hasJob(job: Job) = this.getJobLevel(job) > 0
val instance: EcoJobsAPI
get() = EcoJobsAPIImpl /**
* 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( 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

@@ -2,17 +2,14 @@ package com.willfp.ecojobs.api.event
import com.willfp.ecojobs.jobs.Job import com.willfp.ecojobs.jobs.Job
import org.bukkit.OfflinePlayer import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.bukkit.event.Cancellable import org.bukkit.event.Cancellable
import org.bukkit.event.Event import org.bukkit.event.Event
import org.bukkit.event.HandlerList import org.bukkit.event.HandlerList
import org.bukkit.event.player.PlayerEvent
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

@@ -2,14 +2,28 @@ 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.jobs.Jobs
import com.willfp.libreforge.LibReforgePlugin
import com.willfp.libreforge.lrcdb.CommandExport
import com.willfp.libreforge.lrcdb.CommandImport
import com.willfp.libreforge.lrcdb.ExportableConfig
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
class CommandEcojobs(plugin: EcoPlugin) : PluginCommand(plugin, "ecojobs", "ecojobs.command.ecojobs", false) { class CommandEcojobs(plugin: LibReforgePlugin) : PluginCommand(plugin, "ecojobs", "ecojobs.command.ecojobs", false) {
init { init {
this.addSubcommand(CommandReload(plugin)) this.addSubcommand(CommandReload(plugin))
.addSubcommand(CommandUnlock(plugin)) .addSubcommand(CommandUnlock(plugin))
.addSubcommand(CommandGiveXP(plugin)) .addSubcommand(CommandGiveXP(plugin))
.addSubcommand(CommandReset(plugin)) .addSubcommand(CommandReset(plugin))
.addSubcommand(CommandImport("jobs", plugin))
.addSubcommand(CommandExport(plugin) {
Jobs.values().map {
ExportableConfig(
it.id,
it.config
)
}
})
} }
override fun onExecute(sender: CommandSender, args: List<String>) { override fun onExecute(sender: CommandSender, args: List<String>) {

View File

@@ -5,10 +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.giveJobExperience
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,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.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.JobsGUI import com.willfp.ecojobs.jobs.JobsGUI
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
class CommandJobs(plugin: EcoPlugin) : PluginCommand(plugin, "jobs", "ecojobs.command.jobs", true) { class CommandJobs(plugin: EcoPlugin) : PluginCommand(plugin, "jobs", "ecojobs.command.jobs", true) {
init { init {
@@ -12,8 +14,44 @@ class CommandJobs(plugin: EcoPlugin) : PluginCommand(plugin, "jobs", "ecojobs.co
.addSubcommand(CommandLeave(plugin)) .addSubcommand(CommandLeave(plugin))
} }
override fun onExecute(player: CommandSender, args: List<String>) { override fun onExecute(player: Player, args: List<String>) {
player as Player if (args.isEmpty()) {
JobsGUI.open(player) JobsGUI.open(player)
return
}
val id = args[0].lowercase()
val job = Jobs.getByID(id)
if (job == null) {
player.sendMessage(plugin.langYml.getMessage("invalid-job"))
return
}
if (!player.hasJob(job)) {
player.sendMessage(plugin.langYml.getMessage("dont-have-job"))
return
}
job.levelGUI.open(player)
}
override fun tabComplete(player: Player, args: List<String>): List<String> {
val completions = mutableListOf<String>()
if (args.isEmpty()) {
return Jobs.values().filter { player.hasJob(it) }.map { it.id }
}
if (args.size == 1) {
StringUtil.copyPartialMatches(
args[0],
Jobs.values().filter { player.hasJob(it) }.map { it.id },
completions
)
return completions
}
return emptyList()
} }
} }

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,16 +30,22 @@ 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.canJoinJob(job)) {
player.sendMessage(plugin.langYml.getMessage("cannot-join-job"))
return
}
player.sendMessage( player.sendMessage(
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> {
@@ -46,14 +54,14 @@ class CommandJoin(plugin: EcoPlugin) : Subcommand(plugin, "join", "ecojobs.comma
} }
val completions = mutableListOf<String>() val completions = mutableListOf<String>()
if (args.isEmpty()) { if (args.isEmpty()) {
// Currently, this case is not ever reached
return Jobs.values().filter { sender.hasJob(it) }.map { it.id } return Jobs.values().filter { sender.hasJob(it) }.map { it.id }
} }
if (args.size == 1) { if (args.size == 1) {
StringUtil.copyPartialMatches( StringUtil.copyPartialMatches(
args[1], args[0],
Jobs.values().filter { sender.hasJob(it) }.map { it.id }, Jobs.values().filter { sender.hasJob(it) }.map { it.id },
completions completions
) )

View File

@@ -2,25 +2,78 @@ 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.ecojobs.api.activeJobs
import com.willfp.ecojobs.jobs.activeJob 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
} }
player.sendMessage( val id = args[0]
plugin.langYml.getMessage("left-job", StringUtils.FormatOption.WITHOUT_PLACEHOLDERS)
.replace("%job%", player.activeJob?.name ?: "")
)
player.activeJob = null val job = Jobs.getByID(id)
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)
)
} else {
player.sendMessage(
plugin.langYml.getMessage("cant-leave-job")
.replace("%job%", job.name)
)
}
}
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,12 +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 com.willfp.ecojobs.jobs.setJobLevel
import com.willfp.ecojobs.jobs.setJobXP
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.command.CommandSender import org.bukkit.command.CommandSender
@@ -46,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

@@ -10,14 +10,24 @@ import com.willfp.eco.core.items.builder.ItemStackBuilder
import com.willfp.eco.core.placeholder.PlayerPlaceholder import com.willfp.eco.core.placeholder.PlayerPlaceholder
import com.willfp.eco.core.placeholder.PlayerStaticPlaceholder import com.willfp.eco.core.placeholder.PlayerStaticPlaceholder
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder import com.willfp.eco.core.placeholder.PlayerlessPlaceholder
import com.willfp.eco.core.price.ConfiguredPrice
import com.willfp.eco.core.price.impl.PriceEconomy
import com.willfp.eco.util.NumberUtils 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
@@ -28,21 +38,32 @@ 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.* import java.time.Duration
import java.util.DoubleSummaryStatistics
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
) { ) {
private val topCache = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(plugin.configYml.getInt("leaderboard-cache-lifetime").toLong()))
.build<Int, LeaderboardCacheEntry?>()
val name = config.getFormattedString("name") val name = config.getFormattedString("name")
val description = config.getFormattedString("description") val description = config.getFormattedString("description")
val isUnlockedByDefault = config.getBool("unlocked-by-default") val isUnlockedByDefault = config.getBool("unlocked-by-default")
val resetsOnQuit = config.getBool("reset-on-quit") val resetsOnQuit = config.getBool("reset-on-quit")
val joinPrice = config.getDouble("join-price")
val leavePrice = config.getDouble("leave-price") val joinPrice = ConfiguredPrice.create(config.getSubsection("join-price")) ?: ConfiguredPrice(
PriceEconomy(config.getDouble("join-price")), ""
)
val leavePrice = ConfiguredPrice.create(config.getSubsection("leave-price")) ?: ConfiguredPrice(
PriceEconomy(config.getDouble("leave-price")), ""
)
val levelKey: PersistentDataKey<Int> = PersistentDataKey( val levelKey: PersistentDataKey<Int> = PersistentDataKey(
EcoJobsPlugin.instance.namespacedKeyFactory.create("${id}_level"), EcoJobsPlugin.instance.namespacedKeyFactory.create("${id}_level"),
@@ -51,9 +72,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")
@@ -62,30 +81,26 @@ class Job(
val levelGUI = JobLevelGUI(plugin, this) val levelGUI = JobLevelGUI(plugin, this)
val leaveGUI = JobLeaveGUI(plugin, this)
private val baseItem: ItemStack = Items.lookup(config.getString("icon")).item private val baseItem: ItemStack = Items.lookup(config.getString("icon")).item
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") sub.getString("value").replace("%level%", it.toString())
.replace("%level%", it.toString())
).toNiceString() ).toNiceString()
} }
} }
@@ -95,13 +110,11 @@ class Job(
} }
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")
@@ -131,46 +144,52 @@ 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(
plugin, "${id}_active"
) {
it.hasJobActive(this).toString()
}.register()
PlayerPlaceholder(
plugin, "${id}_total_players"
) {
Bukkit.getOfflinePlayers().count { this in it.activeJobs }.toString()
}.register()
} }
fun getLevel(level: Int): JobLevel = levels.get(level) { fun getLevel(level: Int): JobLevel = levels.get(level) {
@@ -189,11 +208,9 @@ 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 {
.map {
" ".repeat(whitespace) + it " ".repeat(whitespace) + it
} }
} }
@@ -210,11 +227,9 @@ 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 {
.map {
" ".repeat(whitespace) + it " ".repeat(whitespace) + it
} }
} }
@@ -231,18 +246,27 @@ 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 {
.map {
" ".repeat(whitespace) + it " ".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
}
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 ->
@@ -251,15 +275,12 @@ class Job(
} else { } else {
req.toNiceString() req.toNiceString()
} }
} }).replace("%description%", this.description).replace("%job%", this.name)
)
.replace("%description%", this.description)
.replace("%job%", this.name)
.replace("%level%", (forceLevel ?: player.getJobLevel(this)).toString()) .replace("%level%", (forceLevel ?: player.getJobLevel(this)).toString())
.replace("%join_price%", NumberUtils.format(this.joinPrice)) .replace("%level_numeral%", NumberUtils.toNumeral(forceLevel ?: player.getJobLevel(this)))
.replace("%leave_price%", NumberUtils.format(this.leavePrice)) .replace("%join_price%", this.joinPrice.getDisplay(player))
} .replace("%leave_price%", this.leavePrice.getDisplay(player))
.toMutableList() }.toMutableList()
val processed = mutableListOf<List<String>>() val processed = mutableListOf<List<String>>()
@@ -273,6 +294,10 @@ class Job(
getRewardsDescription(forceLevel ?: player.getJobLevel(this), whitespace) getRewardsDescription(forceLevel ?: player.getJobLevel(this), whitespace)
} else if (s.contains("%level_up_messages%")) { } else if (s.contains("%level_up_messages%")) {
getLevelUpMessages(forceLevel ?: player.getJobLevel(this), whitespace) getLevelUpMessages(forceLevel ?: player.getJobLevel(this), whitespace)
} else if (s.contains("%leave_lore%")) {
getLeaveLore(forceLevel ?: player.getJobLevel(this), whitespace)
} else if (s.contains("%join_lore%")) {
getJoinLore(forceLevel ?: player.getJobLevel(this), whitespace)
} else { } else {
listOf(s) listOf(s)
} }
@@ -286,34 +311,33 @@ class Job(
val base = baseItem.clone() val base = baseItem.clone()
val level = player.getJobLevel(this) val level = player.getJobLevel(this)
val isActive = player.activeJob == 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("%level_numeral%", NumberUtils.toNumeral(level)).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")
if (isActive) plugin.configYml.getStrings("gui.job-icon.active-lore") else } else if (player.canJoinJob(this)) {
plugin.configYml.getStrings("gui.job-icon.not-active-lore") 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 { 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("%level%", player.getJobLevel(this).toString()).replace("%level_numeral%", NumberUtils.toNumeral(player.getJobLevel(this))).replace("%job%", this.name)
.replace("%job%", this.name) ).addLoreLines {
)
.addLoreLines {
injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-info.active.lore"), player) injectPlaceholdersInto(plugin.configYml.getStrings("gui.job-info.active.lore"), player)
} }.build()
.build()
} }
fun getExpForLevel(level: Int): Int { fun getExpForLevel(level: Int): Int {
@@ -336,6 +360,14 @@ class Job(
return jobXpGains.sumOf { it.getCount(event) } 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 { override fun equals(other: Any?): Boolean {
if (other !is Job) { if (other !is Job) {
return false return false
@@ -350,12 +382,16 @@ 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)
} }
data class LeaderboardCacheEntry(
val player: OfflinePlayer,
val amount: Int
)
private fun Collection<LevelPlaceholder>.format(string: String, level: Int): String { private fun Collection<LevelPlaceholder>.format(string: String, level: Int): String {
var process = string var process = string
for (placeholder in this) { for (placeholder in this) {
@@ -364,85 +400,11 @@ 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))
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() it.cacheJobExperienceMultiplier()
} }
val Player.jobExperienceMultiplier: Double val Player.jobExperienceMultiplier: Double
get() = expMultiplierCache.get(this) get() = expMultiplierCache.get(this)
@@ -464,43 +426,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

@@ -0,0 +1,87 @@
package com.willfp.ecojobs.jobs
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.gui.menu
import com.willfp.eco.core.gui.onLeftClick
import com.willfp.eco.core.gui.slot
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.util.formatEco
import com.willfp.ecojobs.api.hasJobActive
import com.willfp.ecojobs.api.leaveJob
import org.bukkit.entity.Player
class JobLeaveGUI(
plugin: EcoPlugin,
job: Job
) {
private val menu = menu(plugin.configYml.getInt("leave-gui.rows")) {
val maskPattern = plugin.configYml.getStrings("leave-gui.mask.pattern").toTypedArray()
val maskItems = MaskItems.fromItemNames(plugin.configYml.getStrings("leave-gui.mask.materials"))
title = plugin.configYml.getString("leave-gui.title")
.replace("%job%", job.name)
.formatEco()
setMask(
FillerMask(
maskItems,
*maskPattern
)
)
setSlot(
plugin.configYml.getInt("leave-gui.cancel.location.row"),
plugin.configYml.getInt("leave-gui.cancel.location.column"),
slot({ player, _ ->
ItemStackBuilder(Items.lookup(plugin.configYml.getString("leave-gui.cancel.item")))
.setDisplayName(plugin.configYml.getString("leave-gui.cancel.name").replace("%job%", job.name))
.addLoreLines(
job.injectPlaceholdersInto(plugin.configYml.getStrings("leave-gui.cancel.lore"), player)
)
.build()
}) {
onLeftClick { player, _, _, _ ->
JobsGUI.open(player)
}
}
)
setSlot(
plugin.configYml.getInt("leave-gui.confirm.location.row"),
plugin.configYml.getInt("leave-gui.confirm.location.column"),
slot({ player, _ ->
ItemStackBuilder(Items.lookup(plugin.configYml.getString("leave-gui.confirm.item")))
.setDisplayName(plugin.configYml.getString("leave-gui.confirm.name").replace("%job%", job.name))
.addLoreLines(
job.injectPlaceholdersInto(plugin.configYml.getStrings("leave-gui.confirm.lore"), player)
)
.build()
}) {
onLeftClick { player, _, _, _ ->
player.leaveJob(job)
if (!player.hasJobActive(job)) {
player.sendMessage(
plugin.langYml.getMessage("left-job")
.replace("%job%", job.name)
)
} else {
player.sendMessage(
plugin.langYml.getMessage("cant-leave-job")
.replace("%job%", job.name)
)
}
JobsGUI.open(player)
}
}
)
}
fun open(player: Player) {
menu.open(player)
}
}

View File

@@ -3,6 +3,9 @@ package com.willfp.ecojobs.jobs
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
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.menu.MenuLayer
import com.willfp.eco.core.gui.onLeftClick
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
@@ -10,63 +13,62 @@ 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 org.bukkit.Material import com.willfp.ecojobs.api.getJobLevel
import com.willfp.ecomponent.components.LevelComponent
import com.willfp.ecomponent.components.LevelState
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
import kotlin.properties.Delegates
class JobLevelGUI( class JobLevelGUI(
plugin: EcoPlugin, plugin: EcoPlugin,
private val job: Job private val job: Job
) { ) {
private val menu: Menu private val menu: Menu
private val pageKey = "page"
private var levelsPerPage by Delegates.notNull<Int>()
private var pages by Delegates.notNull<Int>()
private fun getPage(menu: Menu, player: Player): Int {
val page = menu.getState(player, pageKey) ?: 1
return min(pages, max(page, 1))
}
init { init {
val maskPattern = plugin.configYml.getStrings("level-gui.mask.pattern").toTypedArray() val maskPattern = plugin.configYml.getStrings("level-gui.mask.pattern").toTypedArray()
val maskItems = MaskItems.fromItemNames(plugin.configYml.getStrings("level-gui.mask.materials")) val maskItems = MaskItems.fromItemNames(plugin.configYml.getStrings("level-gui.mask.materials"))
val progressionOrder = "123456789abcdefghijklmnopqrstuvwxyz"
val progressionPattern = plugin.configYml.getStrings("level-gui.progression-slots.pattern") val progressionPattern = plugin.configYml.getStrings("level-gui.progression-slots.pattern")
val progressionSlots = mutableMapOf<Int, Pair<Int, Int>>() val component = object : LevelComponent(progressionPattern, job.maxLevel) {
override fun getLevelItem(player: Player, menu: Menu, level: Int, levelState: LevelState): ItemStack {
val key = levelState.name.lowercase().replace("_", "-")
var x = 0 return ItemStackBuilder(Items.lookup(plugin.configYml.getString("level-gui.progression-slots.$key.item")))
for (row in progressionPattern) { .setDisplayName(
x++ plugin.configYml.getFormattedString("level-gui.progression-slots.$key.name")
var y = 0 .replace("%job%", job.name)
for (char in row) { .replace("%level%", level.toString())
y++ .replace("%level_numeral%", NumberUtils.toNumeral(level))
if (char == '0') { )
continue .addLoreLines(
job.injectPlaceholdersInto(
plugin.configYml.getFormattedStrings("level-gui.progression-slots.$key.lore"),
player,
forceLevel = level
)
)
.setAmount(
if (plugin.configYml.getBool("level-gui.progression-slots.level-as-amount")) level else 1
)
.build()
} }
val pos = progressionOrder.indexOf(char) override fun getLevelState(player: Player, level: Int): LevelState {
return when {
if (pos == -1) { level <= player.getJobLevel(job) -> LevelState.UNLOCKED
continue level == player.getJobLevel(job) + 1 -> LevelState.IN_PROGRESS
} else -> LevelState.LOCKED
}
progressionSlots[pos + 1] = Pair(x, y)
} }
} }
levelsPerPage = progressionSlots.size
pages = ceil(job.maxLevel.toDouble() / levelsPerPage).toInt()
menu = menu(plugin.configYml.getInt("level-gui.rows")) { menu = menu(plugin.configYml.getInt("level-gui.rows")) {
setTitle(job.name) title = job.name
maxPages(component.pages)
setMask( setMask(
FillerMask( FillerMask(
maskItems, maskItems,
@@ -74,58 +76,11 @@ class JobLevelGUI(
) )
) )
for ((level, value) in progressionSlots) { addComponent(1, 1, component)
setSlot(
value.first,
value.second,
slot(ItemStack(Material.BLACK_STAINED_GLASS_PANE)) {
setUpdater { player, menu, _ ->
val page = getPage(menu, player)
val slotLevel = ((page - 1) * levelsPerPage) + level // Instead of the page changer, this will show up when on the first page
addComponent(
fun getItem(section: String) = MenuLayer.LOWER,
ItemStackBuilder(Items.lookup(plugin.configYml.getString("level-gui.progression-slots.$section.item")))
.setDisplayName(
plugin.configYml.getFormattedString("level-gui.progression-slots.$section.name")
.replace("%job%", job.name)
.replace("%level%", slotLevel.toString())
.replace("%level_numeral%", NumberUtils.toNumeral(slotLevel))
)
.addLoreLines(
job.injectPlaceholdersInto(
plugin.configYml.getFormattedStrings("level-gui.progression-slots.$section.lore"),
player,
forceLevel = slotLevel
)
)
.build()
if (slotLevel > job.maxLevel) {
maskItems.items[0].item
} else {
val item = when {
slotLevel <= player.getJobLevel(job) -> {
getItem("unlocked")
}
slotLevel == player.getJobLevel(job) + 1 -> {
getItem("in-progress")
}
else -> {
getItem("locked")
}
}
if (plugin.configYml.getBool("level-gui.progression-slots.level-as-amount")) {
item.amount = slotLevel
}
item
}
}
}
)
}
setSlot(
plugin.configYml.getInt("level-gui.progression-slots.prev-page.location.row"), plugin.configYml.getInt("level-gui.progression-slots.prev-page.location.row"),
plugin.configYml.getInt("level-gui.progression-slots.prev-page.location.column"), plugin.configYml.getInt("level-gui.progression-slots.prev-page.location.column"),
slot( slot(
@@ -133,38 +88,30 @@ class JobLevelGUI(
.setDisplayName(plugin.configYml.getString("level-gui.progression-slots.prev-page.name")) .setDisplayName(plugin.configYml.getString("level-gui.progression-slots.prev-page.name"))
.build() .build()
) { ) {
onLeftClick { event, _, menu -> onLeftClick { player, _, _, _ -> JobsGUI.open(player) }
val player = event.whoClicked as Player
val page = getPage(menu, player)
val newPage = max(0, page - 1)
if (newPage == 0) {
JobsGUI.open(player)
} else {
menu.addState(player, pageKey, newPage)
}
}
} }
) )
setSlot(
addComponent(
plugin.configYml.getInt("level-gui.progression-slots.prev-page.location.row"),
plugin.configYml.getInt("level-gui.progression-slots.prev-page.location.column"),
PageChanger(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("level-gui.progression-slots.prev-page.material")))
.setDisplayName(plugin.configYml.getString("level-gui.progression-slots.prev-page.name"))
.build(),
PageChanger.Direction.BACKWARDS
)
)
addComponent(
plugin.configYml.getInt("level-gui.progression-slots.next-page.location.row"), plugin.configYml.getInt("level-gui.progression-slots.next-page.location.row"),
plugin.configYml.getInt("level-gui.progression-slots.next-page.location.column"), plugin.configYml.getInt("level-gui.progression-slots.next-page.location.column"),
slot( PageChanger(
ItemStackBuilder(Items.lookup(plugin.configYml.getString("level-gui.progression-slots.next-page.material"))) ItemStackBuilder(Items.lookup(plugin.configYml.getString("level-gui.progression-slots.next-page.material")))
.setDisplayName(plugin.configYml.getString("level-gui.progression-slots.next-page.name")) .setDisplayName(plugin.configYml.getString("level-gui.progression-slots.next-page.name"))
.build() .build(),
) { PageChanger.Direction.FORWARDS
onLeftClick { event, _, menu -> )
val player = event.whoClicked as Player
val page = getPage(menu, player)
val newPage = min(pages, page + 1)
menu.addState(player, pageKey, newPage)
}
}
) )
setSlot( setSlot(
@@ -191,5 +138,7 @@ class JobLevelGUI(
} }
} }
fun open(player: Player) = menu.open(player) fun open(player: Player) {
menu.open(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
for (job in jobs) {
val amount = job.getXP(event) val amount = job.getXP(event)
if (amount <= 0.0) { if (amount <= 0.0) {
return 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.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

@@ -3,18 +3,29 @@ package com.willfp.ecojobs.jobs
import com.willfp.eco.core.config.updating.ConfigUpdater 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.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
@@ -22,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
@@ -53,21 +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
if (job == null) {
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()
} else {
job.getJobInfoIcon(player)
}
}
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
@@ -78,7 +65,7 @@ object JobsGUI {
} }
return menu(plugin.configYml.getInt("gui.rows")) { return menu(plugin.configYml.getInt("gui.rows")) {
setTitle(plugin.langYml.getString("menu.title")) title = plugin.langYml.getString("menu.title")
setMask( setMask(
FillerMask( FillerMask(
@@ -88,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
} }
) )
@@ -102,14 +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) }) {
setUpdater { p, m, _ -> onLeftClick { player, _, _, menu ->
jobIconBuilder(p, m, index) val page = menu.getPage(player)
}
onLeftClick { event, _, _ ->
val player = event.whoClicked as Player
val page = getPage(menu, player)
val unlockedJobs = player.unlockedJobs val unlockedJobs = player.unlockedJobs
@@ -117,8 +131,22 @@ object JobsGUI {
val job = unlockedJobs.getOrNull(pagedIndex) ?: return@onLeftClick val job = unlockedJobs.getOrNull(pagedIndex) ?: return@onLeftClick
if (player.activeJob != job) { if (player.hasJobActive(job)) {
player.activeJob = job job.levelGUI.open(player)
} else {
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(
@@ -128,51 +156,57 @@ 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.addState(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() maxPages { player ->
ceil(Jobs.values()
.filter { player.getJobLevel(it) > 0 } .filter { player.getJobLevel(it) > 0 }
.size.toDouble() / jobAreaSlots.size).toInt() .size.toDouble() / jobAreaSlots.size).toInt()
val page = getPage(menu, player)
val newPage = min(pages, page + 1)
menu.addState(player, pageKey, newPage)
} }
}
)
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"),
@@ -185,20 +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 { event, _ ->
val player = event.whoClicked as Player
player.activeJob = null
}
}
)
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,8 +1,8 @@
package com.willfp.ecojobs.jobs package com.willfp.ecojobs.jobs
import com.willfp.eco.core.integrations.economy.balance
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 org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
@@ -13,19 +13,16 @@ object PriceHandler : Listener {
ignoreCancelled = true ignoreCancelled = true
) )
fun onJoin(event: PlayerJobJoinEvent) { fun onJoin(event: PlayerJobJoinEvent) {
val player = event.player val player = event.player as? Player ?: return
val job = event.job val job = event.job
val price = job.joinPrice val price = job.joinPrice
if (price > 0) { if (!price.canAfford(player)) {
val hasMoney = player.balance >= price
if (!hasMoney) {
event.isCancelled = true event.isCancelled = true
return
} }
player.balance -= price price.pay(player)
}
} }
@EventHandler( @EventHandler(
@@ -33,18 +30,15 @@ object PriceHandler : Listener {
ignoreCancelled = true ignoreCancelled = true
) )
fun onLeave(event: PlayerJobLeaveEvent) { fun onLeave(event: PlayerJobLeaveEvent) {
val player = event.player val player = event.player as? Player ?: return
val job = event.job val job = event.job
val price = job.leavePrice val price = job.leavePrice
if (price > 0) { if (!price.canAfford(player)) {
val hasMoney = player.balance >= price
if (!hasMoney) {
event.isCancelled = true event.isCancelled = true
return
} }
player.balance -= price price.pay(player)
}
} }
} }

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: no-jobs:
- "" - ""
- "&cYou do not currently have a job active" - "&cYou do not currently have a job active"
- "&fPick one from the options below!" - "&fPick one from the options below!"
- "" - ""
item: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmFkYzA0OGE3Y2U3OGY3ZGFkNzJhMDdkYTI3ZDg1YzA5MTY4ODFlNTUyMmVlZWQxZTNkYWYyMTdhMzhjMWEifX19 - "&fYou can have up to &a%ecojobs_limit% &fjobs at once!"
# %join_price% and %leave_price% are also available as placeholders
active:
name: "%job% &fLvl. &a%level%"
lore:
- "%description%"
- "&f"
- "&fJob Rewards:"
- "%effects%"
- "" - ""
- "&fProgress:"
- "&8» &e%percentage_progress%%" has-jobs:
- "&8» &e%current_xp%&8/&7%required_xp% &fXP"
- "" - ""
- "&eClick to view Level Progression!" - "%jobs%"
- ""
- "&fJob slots in use: &a%ecojobs_in_jobs%&8/&a%ecojobs_limit%"
job-line:
- " %job% &fLvl. &a%level%"
job-icon: job-icon:
name: "%job% &fLvl. &a%level%" name: "%job% &fLvl. &a%level%"
@@ -76,13 +74,20 @@ 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:
- "" - ""
- "&cThis job is already active!" - "&eClick to view Level Progression!"
- "&eRight-click to leave this job!"
not-active-lore: too-many-jobs-lore:
- "" - ""
- "&eClick to activate this job!" - "&cYou have too many jobs already!"
join-lore:
- ""
- "&eClick to join this job!"
click: click:
sound: ui_button_click sound: ui_button_click
@@ -93,31 +98,24 @@ 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: [ ]
level-gui: level-gui:
rows: 6 rows: 6
@@ -214,7 +212,54 @@ level-gui:
column: 5 column: 5
# 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: [ ]
leave-gui:
rows: 3
title: "Confirm Leaving %job%"
mask:
# The way the mask works is by having a list of materials
# And then a pattern to use those materials.
# The pattern is the rows in the GUI
# Each line must be 9 long, and the amount of rows should be the amount of rows in the GUI
# A zero represents nothing
# A 1 represents the first material
# A 2 represents the second material
# And so on, you can add up to 9.
materials:
- black_stained_glass_pane
pattern:
- "111111111"
- "110111011"
- "111111111"
cancel:
item: arrow
name: "&eCancel"
lore:
- ""
- "&fGo back to the Jobs menu"
location:
row: 2
column: 3
confirm:
item: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTRiZDlhNDViOTY4MWNlYTViMjhjNzBmNzVhNjk1NmIxZjU5NGZlYzg0MGI5NjA3Nzk4ZmIxZTcwNzc2NDQzMCJ9fX0=
name: "&cLeave %job%"
lore:
- ""
- "&cAre you sure?"
- "%leave_lore%"
location:
row: 2
column: 7
# Custom GUI slots; see here for a how-to: https://plugins.auxilor.io/all-plugins/custom-gui-slots
custom-slots: [ ]
level-up: level-up:
message: message:
@@ -255,11 +300,19 @@ cannot-afford-type:
sound: "BLOCK_NOTE_BLOCK_PLING" sound: "BLOCK_NOTE_BLOCK_PLING"
pitch: 0.5 pitch: 0.5
cannot-afford-price:
in-actionbar: true
sound:
enabled: true
sound: "BLOCK_NOTE_BLOCK_PLING"
pitch: 0.5
point-names: # If you have point names that look ugly (eg g_souls) then you can map them to nice names to be shown to players. point-names: # If you have point names that look ugly (eg g_souls) then you can map them to nice names to be shown to players.
example_point: "Nicely Formatted Point" example_point: "Nicely Formatted Point"
use-faster-move-trigger: true # Disable if you want move trigger to detect sub-1-block movements 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 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: potions:
icon: icon:
@@ -271,5 +324,3 @@ potions:
particles: particles:
permanent: false permanent: false
triggered: true triggered: true
share-configs: true # If your configs are allowed to be used to gather data and improve the plugin. Nothing identifying (IP, Name, etc) is shared.

View File

@@ -16,9 +16,28 @@ unlocked-by-default: true
# If job progress should be reset when quitting # If job progress should be reset when quitting
reset-on-quit: false reset-on-quit: false
# The price to join or leave this job (set to 0 to disable) # The price to join this job (set to 0 to disable)
join-price: 0 # Read here for more: https://plugins.auxilor.io/all-plugins/prices
leave-price: 0 join-price:
value: 0
type: coins
display: "&a$%value%"
# Lore shown when clicking on the job icon to join it
# Reference with %join_lore%
join-lore: []
# The price to leave this job (set to 0 to disable)
# Read here for more: https://plugins.auxilor.io/all-plugins/prices
leave-price:
value: 20000
type: coins
display: "&a$%value%"
# Lore shown on the confirm leave button
# Reference with %leave_lore%
leave-lore:
- " &8» This will cost %leave_price%"
# The xp requirements for each job level - add new levels by adding more to this list # The xp requirements for each job level - add new levels by adding more to this list
level-xp-requirements: level-xp-requirements:

View File

@@ -5,8 +5,20 @@ unlocked-by-default: true
reset-on-quit: false reset-on-quit: false
join-price: 0 join-price:
leave-price: 0 value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements: level-xp-requirements:
- 100 - 100
@@ -92,7 +104,7 @@ effects:
triggers: triggers:
- breed - breed
filters: filters:
entites: entities:
- bee - bee
- id: take_money - id: take_money
@@ -101,7 +113,7 @@ effects:
triggers: triggers:
- consume - consume
filters: filters:
imems: items:
- honey_bottle - honey_bottle

View File

@@ -5,8 +5,20 @@ unlocked-by-default: true
reset-on-quit: false reset-on-quit: false
join-price: 0 join-price:
leave-price: 0 value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements: level-xp-requirements:
- 100 - 100
@@ -89,14 +101,6 @@ effects:
args: args:
every: "ceil(12 - %level% / 8)" every: "ceil(12 - %level% / 8)"
amount: "1.2 * %level%" amount: "1.2 * %level%"
filters:
items:
- "*wooden_pickaxe"
- "*stone_pickaxe"
- "*iron_pickaxe"
- "*golden_pickaxe"
- "*diamond_pickaxe"
- "*netherite_pickaxe"
triggers: triggers:
- place_block - place_block

View File

@@ -0,0 +1,106 @@
name: "&#7A54FBEnchanter"
description: "&8&oLevel up by enchanting items"
unlocked-by-default: true
reset-on-quit: false
join-price:
value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements:
- 100
- 120
- 150
- 180
- 210
- 250
- 300
- 360
- 430
- 520
- 620
- 740
- 890
- 1000
- 1300
- 1500
- 1900
- 2200
- 2700
- 3200
- 3800
- 4600
- 5500
- 6600
- 7900
- 9500
- 11500
- 14000
- 17000
- 19000
- 24000
- 29000
- 34000
- 41000
- 50000
- 60000
- 70000
- 85000
- 100000
- 120000
- 150000
- 180000
- 210000
- 250000
- 300000
- 360000
- 440000
- 580000
- 750000
xp-gain-methods:
- trigger: enchant_item
multiplier: 0.5
conditions: [ ]
level-placeholders:
- id: "money"
value: "%level% * 0.25"
effects-description:
1:
- "&8» &8Earn &a$%money%&8 for each enchanted item"
rewards-description:
1:
- "&8» &8Earn &a$%money%&8 for each enchanted item"
level-up-messages:
1:
- "&8» &8Earn &a$%money%&8 for each enchanted item"
level-commands: [ ]
effects:
- id: give_money
args:
amount: "%level% * 0.25"
triggers:
- enchant_item
conditions: [ ]
icon: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDJjOGU3NmZjZGRlOWExNWFlMzkyOWI4NGJmNmZmYTRjMTc3N2IxZTI2YjdkNmQ4OTM4ZjBlZjA0ZTg0ODc0YyJ9fX0=

View File

@@ -5,8 +5,20 @@ unlocked-by-default: true
reset-on-quit: false reset-on-quit: false
join-price: 0 join-price:
leave-price: 0 value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements: level-xp-requirements:
- 100 - 100

View File

@@ -5,8 +5,20 @@ unlocked-by-default: true
reset-on-quit: false reset-on-quit: false
join-price: 0 join-price:
leave-price: 0 value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements: level-xp-requirements:
- 100 - 100

View File

@@ -0,0 +1,130 @@
name: "&#FD9113Lumberjack"
description: "&7&oLevel up by chopping wood"
unlocked-by-default: true
reset-on-quit: false
join-price:
value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements:
- 100
- 120
- 150
- 180
- 210
- 250
- 300
- 360
- 430
- 520
- 620
- 740
- 890
- 1000
- 1300
- 1500
- 1900
- 2200
- 2700
- 3200
- 3800
- 4600
- 5500
- 6600
- 7900
- 9500
- 11500
- 14000
- 17000
- 19000
- 24000
- 29000
- 34000
- 41000
- 50000
- 60000
- 70000
- 85000
- 100000
- 120000
- 150000
- 180000
- 210000
- 250000
- 300000
- 360000
- 440000
- 580000
- 750000
xp-gain-methods:
- trigger: mine_block
multiplier: 0.5
filters:
blocks:
- oak_log
- spruce_log
- birch_log
- jungle_log
- acacia_log
- dark_oak_log
- mangrove_log
- crimson_stem
- warped_stem
player_placed: false
conditions: [ ]
level-placeholders:
- id: "money"
value: "%level% * 3.0"
effects-description:
1:
- "&8» &7Earn &a$%money%&7 for each log chopped."
rewards-description:
1:
- "&8» &7Earn &a$%money%&7 for each log chopped."
level-up-messages:
1:
- "&8» &7Earn &a$%money%&7 for each log chopped."
level-commands: [ ]
effects:
- id: give_money
args:
amount: "%level% * 3.0"
triggers:
- mine_block
filters:
blocks:
- oak_log
- spruce_log
- birch_log
- jungle_log
- acacia_log
- dark_oak_log
- mangrove_log
- crimson_stem
- warped_stem
player_placed: false
conditions: [ ]
icon: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjk4NWEwNWQyYTMzOWExYzFiZDdlMmE3OWFlMDAyNWI5YTEwZTg0ZGI5OWFkZjQzNThkNmViZTgzNThhZDJkOCJ9fX0=

View File

@@ -5,8 +5,20 @@ unlocked-by-default: true
reset-on-quit: false reset-on-quit: false
join-price: 0 join-price:
leave-price: 0 value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements: level-xp-requirements:
- 100 - 100

View File

@@ -5,8 +5,20 @@ unlocked-by-default: true
reset-on-quit: false reset-on-quit: false
join-price: 0 join-price:
leave-price: 0 value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements: level-xp-requirements:
- 100 - 100

View File

@@ -0,0 +1,106 @@
name: "&#4B4736Smelter"
description: "&8&oLevel up by smelting items"
unlocked-by-default: true
reset-on-quit: false
join-price:
value: 0
type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
level-xp-requirements:
- 100
- 120
- 150
- 180
- 210
- 250
- 300
- 360
- 430
- 520
- 620
- 740
- 890
- 1000
- 1300
- 1500
- 1900
- 2200
- 2700
- 3200
- 3800
- 4600
- 5500
- 6600
- 7900
- 9500
- 11500
- 14000
- 17000
- 19000
- 24000
- 29000
- 34000
- 41000
- 50000
- 60000
- 70000
- 85000
- 100000
- 120000
- 150000
- 180000
- 210000
- 250000
- 300000
- 360000
- 440000
- 580000
- 750000
xp-gain-methods:
- trigger: smelt
multiplier: 0.5
conditions: [ ]
level-placeholders:
- id: "money"
value: "%level% * 0.25"
effects-description:
1:
- "&8» &8Earn &a$%money%&8 for each smelted item"
rewards-description:
1:
- "&8» &8Earn &a$%money%&8 for each smelted item"
level-up-messages:
1:
- "&8» &8Earn &a$%money%&8 for each smelted item"
level-commands: [ ]
effects:
- id: give_money
args:
amount: "%level% * 0.25"
triggers:
- smelt
conditions: [ ]
icon: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODdjMDc0ODM2MmU5YTYzZTA3YWEyYjA4YjVmN2QxMDFlNTM5NWFhMjE1MzJhNDc2NzIyZGJkZDk4YzJmMiJ9fX0=

View File

@@ -7,20 +7,27 @@
# The display name of the job # The display name of the job
name: "&#0047b3Toolsmith" name: "&#0047b3Toolsmith"
# The description of the job
description: "&8&oLevel up by crafting tools" description: "&8&oLevel up by crafting tools"
# If the job should be unlocked by default
unlocked-by-default: true unlocked-by-default: true
# If job progress should be reset when quitting
reset-on-quit: false reset-on-quit: false
# The price to join or leave this job (set to 0 to disable) join-price:
join-price: 0 value: 0
leave-price: 0 type: coins
display: "&a$%value%"
join-lore: []
leave-price:
value: 0
type: coins
display: "&a$%value%"
leave-lore:
- ""
# The xp requirements for each job level - add new levels by adding more to this list
level-xp-requirements: level-xp-requirements:
- 100 - 100
- 120 - 120
@@ -72,9 +79,6 @@ level-xp-requirements:
- 580000 - 580000
- 750000 - 750000
# An XP Gain method takes a trigger, a multiplier, conditions, and filters.
# The multiplier takes the value produced by the trigger and multiplies it
# by some value to calculate the experience that should be given
xp-gain-methods: xp-gain-methods:
- trigger: craft - trigger: craft
multiplier: 2 multiplier: 2
@@ -113,36 +117,24 @@ xp-gain-methods:
- "*shears" - "*shears"
- "*spyglass" - "*spyglass"
# Custom placeholders to be used in descriptions,
# Don't add % to the IDs, this is done automatically
# The value takes a %level% placeholder and is a mathematical expression
level-placeholders: level-placeholders:
- id: "money" - id: "money"
value: "%level% * 2" value: "%level% * 2"
# The text shown with the %effects% placeholder
# The number dictates the minimum level for this text to show for
# Adding new levels will override this text on those levels or above
effects-description: effects-description:
1: 1:
- "&8» &8Earn &a$%money%&8 for each tool crafted" - "&8» &8Earn &a$%money%&8 for each tool crafted"
# Same as above, but for %rewards%
rewards-description: rewards-description:
1: 1:
- "&8» &8Earn &a$%money%&8 for each tool crafted" - "&8» &8Earn &a$%money%&8 for each tool crafted"
# Same as above, but for %level_up_messages%
level-up-messages: level-up-messages:
1: 1:
- "&8» &8Earn &a$%money%&8 for each tool crafted" - "&8» &8Earn &a$%money%&8 for each tool crafted"
# Commands to be sent on levelup, can be formatted two ways:
# level:command (e.g. 10:eco give %player% 1000), which would execute that command for level 10
# command (e.g. eco give %player% 5000), which would execute that command for all levels
level-commands: [ ] level-commands: [ ]
# The effects for the job, has %level% as a placeholder
effects: effects:
- id: give_money - id: give_money
args: args:
@@ -183,8 +175,6 @@ effects:
triggers: triggers:
- craft - craft
# The conditions for the job, also has %level% as a placeholder
conditions: [ ] conditions: [ ]
# The icon in GUIs
icon: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODk2ZDRjODM4YTE2MDZhYzc1Nzc1NDIzMjA4NjE0OTcwOGI3OWFiYTAxYmU5NTNjNjUzOTkxMDFlODk0M2RhZiJ9fX0= icon: player_head texture:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODk2ZDRjODM4YTE2MDZhYzc1Nzc1NDIzMjA4NjE0OTcwOGI3OWFiYTAxYmU5NTNjNjUzOTkxMDFlODk0M2RhZiJ9fX0=

View File

@@ -6,8 +6,17 @@ messages:
reloaded: "Reloaded!" reloaded: "Reloaded!"
cannot-afford: "&cYou can't afford to do this! &fCost: &a$$%cost%" cannot-afford: "&cYou can't afford to do this! &fCost: &a$$%cost%"
cannot-afford-type: "&cYou can't afford to do this! &fCost: &a%cost% %type%" cannot-afford-type: "&cYou can't afford to do this! &fCost: &a%cost% %type%"
cannot-afford-price: "&cYou can't afford to do this! &fPrice: %price%"
on-cooldown: "&cThis effect is on cooldown! &fTime left: &a%seconds% seconds" on-cooldown: "&cThis effect is on cooldown! &fTime left: &a%seconds% seconds"
cannot-transmit: "&cYou can't transmit here!" cannot-transmit: "&cYou can't transmit here!"
must-specify-lrcdb-id: "&cYou must specify the ID of the config to download! Not sure what this means? Go to &alrcdb.auxilor.io"
lrcdb-import-error: "&cError importing config: &f%message%"
lrcdb-import-success: "&fImported &a%name%&f! Reload the plugin to install it"
must-specify-config-name: "&cYou must specify the config name!"
invalid-config-name: "&cInvalid config name!"
lrcdb-export-error: "&cError exporting config: &f%message%"
lrcdb-export-success: "&fExported &a%name%&f! View it on &alrcdb.auxilor.io&f, or share your config ID: &f%id%"
needs-player: "&cYou must specify a player!" needs-player: "&cYou must specify a player!"
gave-xp: "&fYou have given &a%xp% &fXP to %player%&f's %job%&f!" gave-xp: "&fYou have given &a%xp% &fXP to %player%&f's %job%&f!"
reset-xp: "&fYou have reset %player%&f's %job%&f XP!" reset-xp: "&fYou have reset %player%&f's %job%&f XP!"
@@ -24,8 +33,16 @@ 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!"
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: menu:
title: "Jobs" title: "Jobs"
infinity: "∞" infinity: "∞"
top:
name-empty: "&cEmpty"
amount-empty: "0"

View File

@@ -0,0 +1,19 @@
# Options for lrcdb (https://lrcdb.auxilor.io), a website to share configs
# with other server owners, so you can get more configs without making them
# yourself!
author: "Unknown Author" # The name attached to configs you export
# Options about automatically sharing configs you create
share-configs:
# If you want all your configs to automatically be publicly available,
# set this to true. This really helps out other users!
publicly: false
# If you don't want your configs to be usable to gather information about
# plugin usage or to improve the plugins in the future, disable this.
# Nothing identifying is shared.
enabled: true
# If you disable share-configs, you can still share select configs publicly
# with /ecojobs export <config>.

View File

@@ -7,8 +7,6 @@ website: willfp.com
load: STARTUP load: STARTUP
depend: depend:
- eco - eco
- ProtocolLib
- PlaceholderAPI
softdepend: softdepend:
- AureliumSkills - AureliumSkills
- Vault - Vault
@@ -52,6 +50,8 @@ permissions:
ecojobs.command.join: true ecojobs.command.join: true
ecojobs.command.leave: true ecojobs.command.leave: true
ecojobs.command.reset: true ecojobs.command.reset: true
ecojobs.command.import: true
ecojobs.command.export: true
ecojobs.command.reload: ecojobs.command.reload:
description: Allows reloading the config description: Allows reloading the config
@@ -71,6 +71,12 @@ permissions:
ecojobs.command.reset: ecojobs.command.reset:
description: Allows the use of /ecojobs reset. description: Allows the use of /ecojobs reset.
default: op default: op
ecojobs.command.import:
description: Allows the use of /ecojobs import.
default: op
ecojobs.command.export:
description: Allows the use of /ecojobs export.
default: op
ecojobs.command.join: ecojobs.command.join:
description: Allows the use of /jobs join. description: Allows the use of /jobs join.
default: true default: true

View File

@@ -1,4 +1,4 @@
#libreforge-updater #libreforge-updater
#Fri Oct 07 19:50:42 BST 2022 #Mon Mar 13 13:38:53 GMT 2023
version=1.15.0 version=2.1.4
plugin-name=EcoJobs plugin-name=EcoJobs