Skip to content

Commit 2fb21a3

Browse files
authored
Merge pull request #58 from Exanthiax/master
Modernizes XP Requirements and Level Effects
2 parents af4e1b3 + f709379 commit 2fb21a3

File tree

14 files changed

+363
-272
lines changed

14 files changed

+363
-272
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ allprojects {
4040
}
4141

4242
dependencies {
43-
compileOnly("com.willfp:eco:6.55.0")
43+
compileOnly("com.willfp:eco:6.56.0")
4444
compileOnly("org.jetbrains:annotations:23.0.0")
4545
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.0")
4646
}

eco-core/core-plugin/src/main/kotlin/com/willfp/ecopets/api/EcoPetsAPI.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ interface EcoPetsAPI {
102102
fun getPetXPRequired(
103103
player: OfflinePlayer,
104104
pet: Pet
105-
): Int
105+
): Double
106106

107107
/**
108108
* Get experience to the next level.

eco-core/core-plugin/src/main/kotlin/com/willfp/ecopets/pets/Pet.kt

Lines changed: 86 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,39 @@ import com.willfp.eco.core.recipe.Recipes
1616
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
1717
import com.willfp.eco.core.registry.Registrable
1818
import com.willfp.eco.util.NumberUtils
19+
import com.willfp.eco.util.NumberUtils.evaluateExpression
20+
import com.willfp.eco.core.placeholder.context.placeholderContext
1921
import com.willfp.eco.util.formatEco
2022
import com.willfp.eco.util.toNiceString
2123
import com.willfp.ecopets.EcoPetsPlugin
2224
import com.willfp.ecopets.api.event.PlayerPetExpGainEvent
2325
import com.willfp.ecopets.api.event.PlayerPetLevelUpEvent
2426
import com.willfp.ecopets.pets.entity.PetEntity
27+
import com.willfp.ecopets.util.LevelInjectable
2528
import com.willfp.libreforge.ViolationContext
2629
import com.willfp.libreforge.conditions.ConditionList
2730
import com.willfp.libreforge.conditions.Conditions
2831
import com.willfp.libreforge.counters.Counters
2932
import com.willfp.libreforge.effects.EffectList
3033
import com.willfp.libreforge.effects.Effects
34+
import com.willfp.libreforge.effects.executors.impl.NormalExecutorFactory
3135
import org.bukkit.Bukkit
3236
import org.bukkit.OfflinePlayer
37+
import org.bukkit.configuration.InvalidConfigurationException
3338
import org.bukkit.entity.Player
3439
import org.bukkit.inventory.ItemStack
3540
import org.bukkit.persistence.PersistentDataType
3641
import java.util.Objects
37-
import java.util.concurrent.TimeUnit
3842
import kotlin.math.abs
3943

4044
class Pet(
4145
val id: String,
4246
val config: Config,
4347
private val plugin: EcoPetsPlugin
4448
) : Registrable {
49+
4550
val name = config.getFormattedString("name")
51+
4652
val description = config.getFormattedString("description")
4753

4854
val levelKey: PersistentDataKey<Int> = PersistentDataKey(
@@ -52,9 +58,7 @@ class Pet(
5258
)
5359

5460
val xpKey: PersistentDataKey<Double> = PersistentDataKey(
55-
EcoPetsPlugin.instance.namespacedKeyFactory.create("${id}_xp"),
56-
PersistentDataKeyType.DOUBLE,
57-
0.0
61+
EcoPetsPlugin.instance.namespacedKeyFactory.create("${id}_xp"), PersistentDataKeyType.DOUBLE, 0.0
5862
)
5963

6064
private val spawnEggBacker: ItemStack? = run {
@@ -115,25 +119,27 @@ class Pet(
115119

116120
val entityTexture = config.getString("entity-texture")
117121

118-
private val levelXpRequirements = listOf(0) + config.getInts("level-xp-requirements")
122+
private val xpFormula = config.getStringOrNull("xp-formula")
123+
124+
private val levelXpRequirements = config.getDoublesOrNull("level-xp-requirements")
119125

120-
val maxLevel = levelXpRequirements.size
126+
val maxLevel = config.getIntOrNull("max-level") ?: levelXpRequirements?.size ?: Int.MAX_VALUE
121127

122128
val levelGUI = PetLevelGUI(plugin, this)
123129

124130
private val baseItem: ItemStack = Items.lookup(config.getString("icon")).item
125131

126132
private val effects: EffectList
133+
127134
private val conditions: ConditionList
128135

129-
private val levels = Caffeine.newBuilder()
130-
.build<Int, PetLevel>()
131-
private val effectsDescription = Caffeine.newBuilder()
132-
.build<Int, List<String>>()
133-
private val rewardsDescription = Caffeine.newBuilder()
134-
.build<Int, List<String>>()
135-
private val levelUpMessages = Caffeine.newBuilder()
136-
.build<Int, List<String>>()
136+
private val levels = Caffeine.newBuilder().build<Int, PetLevel>()
137+
138+
private val effectsDescription = Caffeine.newBuilder().build<Int, List<String>>()
139+
140+
private val rewardsDescription = Caffeine.newBuilder().build<Int, List<String>>()
141+
142+
private val levelUpMessages = Caffeine.newBuilder().build<Int, List<String>>()
137143

138144
private val levelCommands = mutableMapOf<Int, MutableList<String>>()
139145

@@ -150,13 +156,14 @@ class Pet(
150156
}
151157

152158
private val petXpGains = config.getSubsections("xp-gain-methods").mapNotNull {
153-
Counters.compile(
154-
it,
155-
ViolationContext(plugin, "Pet $id XP Gain methods")
156-
)
159+
Counters.compile(it, ViolationContext(plugin, "Pet $id"))
157160
}
158161

159162
init {
163+
if (xpFormula == null && levelXpRequirements == null) {
164+
throw InvalidConfigurationException("Pet $id has no requirements or xp formula")
165+
}
166+
160167
config.injectPlaceholders(
161168
PlayerStaticPlaceholder(
162169
"level"
@@ -175,24 +182,7 @@ class Pet(
175182
ViolationContext(plugin, "Pet $id")
176183
)
177184

178-
for (string in config.getStrings("level-commands")) {
179-
val split = string.split(":")
180-
181-
if (split.size == 1) {
182-
for (level in 1..maxLevel) {
183-
val commands = levelCommands[level] ?: mutableListOf()
184-
commands.add(string)
185-
levelCommands[level] = commands
186-
}
187-
} else {
188-
val level = split[0].toInt()
189-
190-
val command = string.removePrefix("$level:")
191-
val commands = levelCommands[level] ?: mutableListOf()
192-
commands.add(command)
193-
levelCommands[level] = commands
194-
}
195-
}
185+
manageLevelCommands(config)
196186

197187
PlayerPlaceholder(
198188
plugin,
@@ -237,6 +227,37 @@ class Pet(
237227
}.register()
238228
}
239229

230+
@Deprecated("Use level-up-effects instead")
231+
private fun manageLevelCommands(config: Config) {
232+
if (config.getStrings("level-commands").isNotEmpty()) {
233+
plugin.logger.warning("$id pet: The `level-commands` key is deprecated and will be removed in future versions. Switch to `level-up-effects` instead. Refer to the wiki for more info.")
234+
}
235+
for (string in config.getStrings("level-commands")) {
236+
val split = string.split(":")
237+
238+
if (split.size == 1) {
239+
for (level in 1..maxLevel) {
240+
val commands = levelCommands[level] ?: mutableListOf()
241+
commands.add(string)
242+
levelCommands[level] = commands
243+
}
244+
} else {
245+
val level = split[0].toInt()
246+
247+
val command = string.removePrefix("$level:")
248+
val commands = levelCommands[level] ?: mutableListOf()
249+
commands.add(command)
250+
levelCommands[level] = commands
251+
}
252+
}
253+
}
254+
255+
val levelUpEffects = Effects.compileChain(
256+
config.getSubsections("level-up-effects"),
257+
NormalExecutorFactory.create(),
258+
ViolationContext(plugin, "Job $id level-up-effects")
259+
)
260+
240261
fun makePetEntity(): PetEntity {
241262
return PetEntity.create(this)
242263
}
@@ -313,14 +334,7 @@ class Pet(
313334
.map {
314335
it.replace("%percentage_progress%", (player.getPetProgress(this) * 100).toNiceString())
315336
.replace("%current_xp%", player.getPetXP(this).toNiceString())
316-
.replace("%required_xp%", this.getExpForLevel(player.getPetLevel(this) + 1).let { req ->
317-
if (req == Int.MAX_VALUE) {
318-
plugin.langYml.getFormattedString("infinity")
319-
} else {
320-
req.toNiceString()
321-
}
322-
}
323-
)
337+
.replace("%required_xp%", this.getFormattedExpForLevel(player.getPetLevel(this) + 1))
324338
.replace("%description%", this.description)
325339
.replace("%pet%", this.name)
326340
.replace("%level%", (forceLevel ?: player.getPetLevel(this)).toString())
@@ -395,14 +409,36 @@ class Pet(
395409
.build()
396410
}
397411

398-
fun getExpForLevel(level: Int): Int {
399-
if (level < 1 || level > maxLevel) {
400-
return Int.MAX_VALUE
412+
/**
413+
* Get the XP required to reach the next level, if currently at [level].
414+
*/
415+
fun getExpForLevel(level: Int): Double {
416+
if (xpFormula != null) {
417+
return evaluateExpression(
418+
xpFormula,
419+
placeholderContext(
420+
injectable = LevelInjectable(level)
421+
)
422+
)
401423
}
402424

403-
return levelXpRequirements[level - 1]
425+
if (levelXpRequirements != null) {
426+
return levelXpRequirements.getOrNull(level) ?: Double.POSITIVE_INFINITY
427+
}
428+
429+
return Double.POSITIVE_INFINITY
404430
}
405431

432+
fun getFormattedExpForLevel(level: Int): String {
433+
val required = getExpForLevel(level)
434+
return if (required.isInfinite()) {
435+
plugin.langYml.getFormattedString("infinity")
436+
} else {
437+
required.toNiceString()
438+
}
439+
}
440+
441+
@Deprecated("Use level-up-effects instead")
406442
fun executeLevelCommands(player: Player, level: Int) {
407443
val commands = levelCommands[level] ?: emptyList()
408444

@@ -492,45 +528,8 @@ fun OfflinePlayer.getPetXP(pet: Pet): Double =
492528
fun OfflinePlayer.setPetXP(pet: Pet, xp: Double) =
493529
this.profile.write(pet.xpKey, xp)
494530

495-
fun OfflinePlayer.getPetXPRequired(pet: Pet): Int =
496-
pet.getExpForLevel(this.getPetLevel(pet) + 1)
497-
498-
private val expMultiplierCache = Caffeine.newBuilder()
499-
.expireAfterWrite(10, TimeUnit.SECONDS)
500-
.build<Player, Double> {
501-
it.cacheSkillExperienceMultiplier()
502-
}
503-
504-
val Player.petExperienceMultiplier: Double
505-
get() = expMultiplierCache.get(this)!!
506-
507-
private fun Player.cacheSkillExperienceMultiplier(): Double {
508-
if (this.hasPermission("ecopets.xpmultiplier.quadruple")) {
509-
return 4.0
510-
}
511-
512-
if (this.hasPermission("ecopets.xpmultiplier.triple")) {
513-
return 3.0
514-
}
515-
516-
if (this.hasPermission("ecopets.xpmultiplier.double")) {
517-
return 2.0
518-
}
519-
520-
if (this.hasPermission("ecopets.xpmultiplier.50percent")) {
521-
return 1.5
522-
}
523-
524-
val prefix = "ecopets.xpmultiplier."
525-
for (permissionAttachmentInfo in this.effectivePermissions) {
526-
val permission = permissionAttachmentInfo.permission
527-
if (permission.startsWith(prefix)) {
528-
return ((permission.substring(permission.lastIndexOf(".") + 1).toDoubleOrNull() ?: 100.0) / 100) + 1
529-
}
530-
}
531-
532-
return 1.0
533-
}
531+
fun OfflinePlayer.getPetXPRequired(pet: Pet) =
532+
this.profile.read(pet.xpKey)
534533

535534
fun Player.givePetExperience(pet: Pet, experience: Double, noMultiply: Boolean = false) {
536535
val exp = abs(if (noMultiply) experience else experience * this.petExperienceMultiplier)

eco-core/core-plugin/src/main/kotlin/com/willfp/ecopets/pets/PetLevelListener.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.willfp.ecopets.pets
22

33
import com.willfp.ecopets.EcoPetsPlugin
44
import com.willfp.ecopets.api.event.PlayerPetLevelUpEvent
5+
import com.willfp.libreforge.toDispatcher
56
import org.bukkit.Sound
67
import org.bukkit.event.EventHandler
78
import org.bukkit.event.EventPriority
@@ -16,6 +17,7 @@ class PetLevelListener(
1617
val player = event.player
1718
val level = event.level
1819

20+
pet.levelUpEffects?.trigger(player.toDispatcher())
1921
pet.executeLevelCommands(player, level)
2022

2123
if (this.plugin.configYml.getBool("level-up.sound.enabled")) {

eco-core/core-plugin/src/main/kotlin/com/willfp/ecopets/pets/PetXPAccumulator.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.willfp.ecopets.pets
22

3+
import com.github.benmanes.caffeine.cache.Caffeine
34
import com.willfp.libreforge.counters.Accumulator
45
import org.bukkit.entity.Player
6+
import java.util.concurrent.TimeUnit
57

68
class PetXPAccumulator(
79
private val pet: Pet
@@ -14,3 +16,40 @@ class PetXPAccumulator(
1416
player.givePetExperience(pet, count)
1517
}
1618
}
19+
20+
private val expMultiplierCache = Caffeine.newBuilder()
21+
.expireAfterWrite(10, TimeUnit.SECONDS)
22+
.build<Player, Double> {
23+
it.cachePetExperienceMultiplier()
24+
}
25+
26+
val Player.petExperienceMultiplier: Double
27+
get() = expMultiplierCache.get(this)!!
28+
29+
private fun Player.cachePetExperienceMultiplier(): Double {
30+
if (this.hasPermission("ecopets.xpmultiplier.quadruple")) {
31+
return 4.0
32+
}
33+
34+
if (this.hasPermission("ecopets.xpmultiplier.triple")) {
35+
return 3.0
36+
}
37+
38+
if (this.hasPermission("ecopets.xpmultiplier.double")) {
39+
return 2.0
40+
}
41+
42+
if (this.hasPermission("ecopets.xpmultiplier.50percent")) {
43+
return 1.5
44+
}
45+
46+
val prefix = "ecopets.xpmultiplier."
47+
for (permissionAttachmentInfo in this.effectivePermissions) {
48+
val permission = permissionAttachmentInfo.permission
49+
if (permission.startsWith(prefix)) {
50+
return ((permission.substring(permission.lastIndexOf(".") + 1).toDoubleOrNull() ?: 100.0) / 100) + 1
51+
}
52+
}
53+
54+
return 1.0
55+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.willfp.ecopets.util
2+
3+
import com.willfp.eco.core.placeholder.InjectablePlaceholder
4+
import com.willfp.eco.core.placeholder.PlaceholderInjectable
5+
import com.willfp.eco.core.placeholder.StaticPlaceholder
6+
7+
class LevelInjectable(
8+
level: Int
9+
) : PlaceholderInjectable {
10+
private val placeholders = listOf(
11+
StaticPlaceholder(
12+
"level"
13+
) { level.toString() }
14+
)
15+
16+
override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
17+
return placeholders
18+
}
19+
20+
override fun addInjectablePlaceholder(p0: Iterable<InjectablePlaceholder>) {
21+
return
22+
}
23+
24+
override fun clearInjectedPlaceholders() {
25+
return
26+
}
27+
}

0 commit comments

Comments
 (0)