diff options
Diffstat (limited to 'src/main/kotlin')
3 files changed, 306 insertions, 0 deletions
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt new file mode 100644 index 00000000..c4b64969 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/recipes/KatRecipe.kt @@ -0,0 +1,164 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ +package io.github.moulberry.notenoughupdates.recipes + +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import io.github.moulberry.notenoughupdates.NEUManager +import io.github.moulberry.notenoughupdates.core.util.GuiElementSlider +import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay +import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay.Pet +import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe +import io.github.moulberry.notenoughupdates.util.ItemUtils +import io.github.moulberry.notenoughupdates.util.PetLeveling +import io.github.moulberry.notenoughupdates.util.Utils +import io.github.moulberry.notenoughupdates.util.toJsonArray +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.ResourceLocation +import java.time.Duration +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.roundToInt +import kotlin.math.sin + +data class KatRecipe( + val manager: NEUManager, + val inputPet: Ingredient, + val outputPet: Ingredient, + val items: List<Ingredient>, + val coins: Long, + val time: Duration, +) : NeuRecipe { + var inputLevel = 1 + val radius get() = 50 / 2 + val circleCenter get() = 33 + 110 / 2 to 19 + 110 / 2 + val textPosition get() = circleCenter.first to circleCenter.second + 90 / 2 + val sliderPos get() = 40 to 15 + val levelTextPos + get() = sliderPos.first - 4 - Minecraft.getMinecraft().fontRendererObj.getStringWidth("100") to + sliderPos.second + 16 / 2 - Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT / 2 + + val levelSlider = GuiElementSlider(0, 0, 100, 1F, 100F, 1F, inputLevel.toFloat()) { inputLevel = it.toInt() } + val coinsAdjustedForLevel: Int + get() = (coins.toInt() * (1 - 0.003F * (getOutputPetForCurrentLevel()?.petLevel?.currentLevel ?: 0))).toInt() + private val basicIngredients = items.toSet() + setOf(inputPet, Ingredient.coinIngredient(manager, coins.toInt())) + override fun getIngredients(): Set<Ingredient> = basicIngredients + + override fun getOutputs(): Set<Ingredient> { + return setOf(outputPet) + } + + fun getInputPetForCurrentLevel(): Pet? { + return PetInfoOverlay.getPetFromStack(inputPet.itemStack.tagCompound)?.also { + val petLeveling = PetLeveling.getPetLevelingForPet(it.petType, it.rarity) + it.petLevel = petLeveling.getPetLevel(petLeveling.getPetExpForLevel(inputLevel).toDouble()) + } + } + + fun getOutputPetForCurrentLevel(): Pet? { + return PetInfoOverlay.getPetFromStack(outputPet.itemStack.tagCompound)?.also { + val petLeveling = PetLeveling.getPetLevelingForPet(it.petType, it.rarity) + it.petLevel = petLeveling.getPetLevel(getInputPetForCurrentLevel()?.petLevel?.expTotal?.toDouble() ?: 0.0) + } + } + + private fun positionOnCircle(i: Int, max: Int): Pair<Int, Int> { + val radians = PI * 2 * i / max + val offsetX = cos(radians) * radius + val offsetY = sin(radians) * radius + return (circleCenter.first + offsetX).roundToInt() to (circleCenter.second + offsetY).roundToInt() + } + + override fun drawExtraInfo(gui: GuiItemRecipe, mouseX: Int, mouseY: Int) { + levelSlider.x = gui.guiLeft + sliderPos.first + levelSlider.y = gui.guiTop + sliderPos.second + levelSlider.render() + Minecraft.getMinecraft().fontRendererObj.drawString( + "$inputLevel", + gui.guiLeft + levelTextPos.first, + gui.guiTop + levelTextPos.second, + 0xFF0000 + ) + Utils.drawStringCentered( + Utils.prettyTime(time), + Minecraft.getMinecraft().fontRendererObj, + gui.guiLeft + textPosition.first.toFloat(), gui.guiTop + textPosition.second.toFloat(), + false, 0xff00ff + ) + GlStateManager.color(1F, 1F, 1F, 1F) + } + + override fun genericMouseInput(mouseX: Int, mouseY: Int) { + levelSlider.mouseInput(mouseX, mouseY) + } + + override fun getSlots(): List<RecipeSlot> { + val advancedIngredients = items.map { it.itemStack } + listOf( + ItemUtils.createPetItemstackFromPetInfo(getInputPetForCurrentLevel()), + Ingredient.coinIngredient( + manager, + coinsAdjustedForLevel + ).itemStack + ) + return advancedIngredients.mapIndexed { index, itemStack -> + val (x, y) = positionOnCircle(index, advancedIngredients.size) + RecipeSlot(x - 18 / 2, y - 18 / 2, itemStack) + } + listOf( + RecipeSlot( + circleCenter.first - 9, + circleCenter.second - 9, + ItemUtils.createPetItemstackFromPetInfo(getOutputPetForCurrentLevel()) + ) + ) + } + + override fun getType(): RecipeType = RecipeType.KAT_UPGRADE + + override fun hasVariableCost(): Boolean = false + + override fun serialize(): JsonObject { + return JsonObject().apply { + addProperty("type", type.id) + addProperty("coins", coins) + addProperty("input", inputPet.serialize()) + addProperty("output", outputPet.serialize()) + addProperty("time", time.seconds) + add("items", items.map { JsonPrimitive(it.serialize()) }.toJsonArray()) + } + } + + companion object { + @JvmStatic + fun parseRecipe(manager: NEUManager, recipe: JsonObject, output: JsonObject): NeuRecipe { + return KatRecipe( + manager, + Ingredient(manager, recipe["input"].asString), + Ingredient(manager, recipe["output"].asString), + recipe["items"]?.asJsonArray?.map { Ingredient(manager, it.asString) } ?: emptyList(), + recipe["coins"].asLong, + Duration.ofSeconds(recipe["time"].asLong) + ) + } + } + + override fun getBackground(): ResourceLocation { + return ResourceLocation("notenoughupdates:textures/gui/katting_tall.png") + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt new file mode 100644 index 00000000..95b6f1ac --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/KotlinJsonUtils.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * 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 <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.util + +import com.google.gson.JsonArray +import com.google.gson.JsonElement + + +fun Iterable<JsonElement>.toJsonArray(): JsonArray = JsonArray().also { + for (jsonElement in this) { + it.add(jsonElement) + } +} + diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt new file mode 100644 index 00000000..e7d29642 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/PetLeveling.kt @@ -0,0 +1,111 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.util + +import com.google.gson.JsonObject +import io.github.moulberry.notenoughupdates.events.RepositoryReloadEvent +import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay.Rarity +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object PetLeveling { + + data class ExpLadder( + val individualLevelCost: List<Long>, + ) { + 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<Key, ExpLadder>() + + @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 + ) + } + } + + internal fun getPetLevelingForPet0(petIdWithoutRarity: String, rarity: Rarity): ExpLadder { + val petConstants = this.petConstants ?: Constants.PETS + var levels = petConstants["pet_levels"].asJsonArray.map { it.asLong }.toMutableList() + val customLeveling = petConstants["custom_pet_leveling"].asJsonObject[petIdWithoutRarity] + val offset = petConstants["pet_rarity_offset"].asJsonObject[rarity.name].asInt + 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) + 2 -> levels = customLevels.toMutableList() + } + maxLevel = customLeveling["max_level"]?.asInt ?: maxLevel + } + return ExpLadder(levels.drop(offset).take(maxLevel)) + } + +} |