/*
* Copyright (C) 2022 Linnea Gräf
*
* This file is part of NotEnoughUpdates.
*
* NotEnoughUpdates is free software: you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* NotEnoughUpdates is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NotEnoughUpdates. If not, see .
*/
package io.github.moulberry.notenoughupdates.util
import com.google.gson.JsonObject
import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe
import io.github.moulberry.notenoughupdates.events.RepositoryReloadEvent
import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay.Rarity
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@NEUAutoSubscribe
object PetLeveling {
data class ExpLadder(
val individualLevelCost: List,
) {
val cumulativeLevelCost = individualLevelCost.runningFold(0F) { a, b -> a + b }.map { it.toLong() }
fun getPetLevel(currentExp: Double): PetLevel {
val currentOneIndexedLevel = cumulativeLevelCost.indexOfLast { it <= currentExp } + 1
val expForNextLevel = if (currentOneIndexedLevel > individualLevelCost.size) { // Max leveled pet
individualLevelCost.last()
} else {
individualLevelCost[currentOneIndexedLevel - 1]
}
val expInCurrentLevel =
if (currentOneIndexedLevel >= cumulativeLevelCost.size)
currentExp.toFloat() - cumulativeLevelCost.last()
else
(expForNextLevel - (cumulativeLevelCost[currentOneIndexedLevel] - currentExp.toFloat())).coerceAtLeast(0F)
return PetLevel(
currentLevel = currentOneIndexedLevel,
maxLevel = cumulativeLevelCost.size,
expRequiredForNextLevel = expForNextLevel,
expRequiredForMaxLevel = cumulativeLevelCost.last(),
expInCurrentLevel = expInCurrentLevel,
expTotal = currentExp.toFloat()
)
}
fun getPetExpForLevel(level: Int): Long {
if (level < 2) return 0L
if (level >= cumulativeLevelCost.size) return cumulativeLevelCost.last()
return cumulativeLevelCost[level - 1]
}
}
data class PetLevel(
val currentLevel: Int,
val maxLevel: Int,
val expRequiredForNextLevel: Long,
val expRequiredForMaxLevel: Long,
val expInCurrentLevel: Float,
var expTotal: Float,
) {
val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel
}
private data class Key(val petIdWithoutRarity: String, val rarity: Rarity)
private val cache = mutableMapOf()
@SubscribeEvent
fun onRepoReload(event: RepositoryReloadEvent) {
cache.clear()
}
var petConstants: JsonObject? = null
@JvmStatic
fun getPetLevelingForPet(petIdWithoutRarity: String, rarity: Rarity): ExpLadder {
return cache.computeIfAbsent(Key(petIdWithoutRarity, rarity)) {
getPetLevelingForPet0(
petIdWithoutRarity,
rarity
)
}
}
val stubExpLadder by lazy {
Utils.showOutdatedRepoNotification("pets.json")
ExpLadder(listOf(1, 1))
}
internal fun getPetLevelingForPet0(petIdWithoutRarity: String, rarity: Rarity): ExpLadder {
val petConstants = this.petConstants ?: Constants.PETS ?: return stubExpLadder
var levels = petConstants["pet_levels"]?.asJsonArray?.map { it.asLong }?.toMutableList() ?: return stubExpLadder
val customLeveling = petConstants["custom_pet_leveling"]?.asJsonObject?.get(petIdWithoutRarity)
var rarityOffsets = petConstants["pet_rarity_offset"]?.asJsonObject
var maxLevel = 100
if (customLeveling is JsonObject) {
val customLevels by lazy { customLeveling["pet_levels"]?.asJsonArray?.map { it.asLong } }
when (customLeveling["type"]?.asInt ?: 0) {
1 -> levels.addAll(customLevels ?: return stubExpLadder)
2 -> levels = customLevels?.toMutableList() ?: return stubExpLadder
}
maxLevel = customLeveling["max_level"]?.asInt ?: maxLevel
rarityOffsets = customLeveling["rarity_offset"]?.asJsonObject ?: rarityOffsets
}
val offset = rarityOffsets?.get(rarity.name)?.asInt ?: return stubExpLadder
return ExpLadder(levels.drop(offset).take(maxLevel - 1))
}
}