Compare commits

...
Author SHA1 Message Date
Guilherme Pereira 1986cb480b add moves tooltips 2024-12-15 22:47:47 -03:00
8 changed files with 144 additions and 8 deletions
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves
enum class MoveEffectiveness(val showdownId: Int, val multiplier: Double) {
NORMAL(0, 1.0),
WEAKNESS(1, 2.0),
RESISTANCE(2, 0.5),
IMMUNITY(3, 0.0);
companion object {
fun of(showdownId: Int): MoveEffectiveness {
return entries.find { it.showdownId == showdownId }
?: throw IllegalArgumentException("Unknown MoveEffectiveness: $showdownId")
}
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.runner.ShowdownService
object TypeChart {
private val movesEffectiveness = mutableMapOf<ElementalType, MutableMap<ElementalType, MoveEffectiveness>>()
fun load() {
movesEffectiveness.clear()
val typeChartJson = ShowdownService.service.getTypeChart()
typeChartJson.keySet().forEach { type ->
val defenderType = ElementalTypes.get(type) ?: return@forEach
val moveTypes = typeChartJson.get(type).asJsonObject.get("damageTaken").asJsonObject
moveTypes.keySet().forEach innerLoop@ {
val moveType = ElementalTypes.get(it) ?: return@innerLoop
val moveEffectiveness = moveTypes.get(it).asInt
val defenderMap = movesEffectiveness.getOrPut(moveType) { mutableMapOf() }
defenderMap[defenderType] = MoveEffectiveness.of(moveEffectiveness)
}
}
}
fun getMoveEffectiveness(moveType: ElementalType, defenderTypes: List<ElementalType?>): Double {
var multiplier = 1.0
defenderTypes.filterNotNull().forEach {
multiplier *= getMoveEffectiveness(moveType, it).multiplier
}
return multiplier
}
private fun getMoveEffectiveness(moveType: ElementalType, defenderType: ElementalType): MoveEffectiveness {
return movesEffectiveness[moveType]?.get(defenderType) ?: throw IllegalArgumentException("Unknown effectiveness type: $moveType $defenderType")
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.TypeChart
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
@@ -125,6 +126,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
it.indicateSpeciesInitialized()
// Reload this with the mod
CobblemonHeldItemManager.load()
TypeChart.load()
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
this.observable.emit(this)
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.runner
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.util.UUID
/**
@@ -30,6 +31,7 @@ interface ShowdownService {
fun getAbilityIds(): JsonArray
fun getMoves(): JsonArray
fun getItemIds(): JsonArray
fun getTypeChart(): JsonObject
fun registerSpecies()
fun registerBagItems()
fun indicateSpeciesInitialized() {}
@@ -38,4 +40,4 @@ interface ShowdownService {
companion object {
val service: ShowdownService by lazy { GraalShowdownService() }
}
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.io.File
import java.io.IOException
import java.net.URI
@@ -152,6 +153,12 @@ class GraalShowdownService : ShowdownService {
return gson.fromJson(arrayResult, JsonArray::class.java)
}
override fun getTypeChart(): JsonObject {
val getCobbledItemIdsFn = context.getBindings("js").getMember("getTypeChart")
val arrayResult = getCobbledItemIdsFn.execute().asString()
return gson.fromJson(arrayResult, JsonObject::class.java)
}
override fun registerSpecies() {
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
val jsArray = this.context.eval("js", "new Array();")
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStreamWriter
@@ -123,6 +124,13 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
return gson.fromJson(response, JsonArray::class.java)
}
override fun getTypeChart(): JsonObject {
writer.write(">getTypeChart")
writer.flush()
val response = readMessage()
return gson.fromJson(response, JsonObject::class.java)
}
private fun sendSpeciesData(species: Species, form: FormData?) {
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
acknowledge()
@@ -11,12 +11,11 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.gold
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.api.moves.TypeChart
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
import com.cobblemon.mod.common.client.battle.SingleActionRequest
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.TypeIcon
@@ -24,12 +23,16 @@ import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.toRGB
import com.google.common.collect.Lists
import net.fabricmc.loader.impl.util.StringUtil
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.util.Mth.floor
class BattleMoveSelection(
@@ -91,16 +94,16 @@ class BattleMoveSelection(
open val response: MoveActionResponse get() = MoveActionResponse(move.id, targetPnx)
open val selectable: Boolean get() = !move.disabled
val targetPnx: String? get() = targetList?.let { targets ->
val target: Targetable? get() = targetList?.let { targets ->
return@let when {
targets.isEmpty() -> null
targets.size == 1 -> targets[0].getPNX()
targets.size == 1 -> targets[0]
else -> null // TODO: multi-battles
}
}
val targetPnx: String? get() = target?.getPNX()
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val selectConditionOpacity = moveSelection.opacity * if (!selectable) 0.5F else 1F
blitk(
@@ -173,6 +176,8 @@ class BattleMoveSelection(
opacity = moveSelection.opacity,
centered = true
)
drawTooltip(context, mouseX, mouseY)
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX >= x && mouseX <= x + MOVE_WIDTH && mouseY >= y && mouseY <= y + MOVE_HEIGHT
@@ -182,6 +187,41 @@ class BattleMoveSelection(
moveSelection.playDownSound(Minecraft.getInstance().soundManager)
moveSelection.battleGUI.selectAction(moveSelection.request, response)
}
private fun drawTooltip(context: GuiGraphics, mouseX: Int, mouseY: Int) {
if (!isHovered(mouseX.toDouble(), mouseY.toDouble())) return
val textTooltip: MutableList<MutableComponent> = mutableListOf()
for (descriptionLine in StringUtil.wrapLines(moveTemplate.description.string, 80).split("\n")) {
textTooltip.add(Component.literal(descriptionLine).gray())
}
textTooltip.add(Component.literal(""))
if (moveTemplate.power > 0) textTooltip.add(lang("ui.power").append(String.format(": %d", moveTemplate.power.toInt())))
if (moveTemplate.accuracy > 0) textTooltip.add(lang("ui.accuracy").append(String.format(": %d%%", moveTemplate.accuracy.toInt())))
if (moveTemplate.power > 0 && moveTemplate.target != MoveTarget.self && target is ActiveClientBattlePokemon) {
val species = (target as ActiveClientBattlePokemon).battlePokemon?.species
if (species != null) {
val moveEffectiveness: Double = TypeChart.getMoveEffectiveness(elementalType, listOf(species.primaryType, species.secondaryType))
textTooltip.add(when {
moveEffectiveness == 0.0 -> lang("battle.ui.effectiveness.immunity", moveEffectiveness).gray()
moveEffectiveness < 1.0 -> lang("battle.ui.effectiveness.resistance", moveEffectiveness).gray()
moveEffectiveness > 1.0 -> lang("battle.ui.effectiveness.super_effective", moveEffectiveness).yellow()
else -> lang("battle.ui.effectiveness.normal", moveEffectiveness).white()
})
}
}
context.renderTooltip(
Minecraft.getInstance().font,
Lists.transform(textTooltip, Component::getVisualOrderText),
mouseX,
mouseY
)
}
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
@@ -1268,6 +1268,7 @@
"cobblemon.ui.status.psn": "PSN",
"cobblemon.ui.status.slp": "SLP",
"cobblemon.ui.status.tox": "PSN",
"cobblemon.battle.ui.fight": "Fight",
"cobblemon.battle.ui.switch": "Switch",
"cobblemon.battle.ui.capture": "Catch",
@@ -1277,6 +1278,11 @@
"cobblemon.battle.ui.hide_label": "Press %1$s to move around.",
"cobblemon.battle.ui.actions_label": "You need to choose an action. Press %1$s.",
"cobblemon.battle.ui.effectiveness.normal": "%1$sx Normal",
"cobblemon.battle.ui.effectiveness.super_effective": "%1$sx Super Effective",
"cobblemon.battle.ui.effectiveness.resistance": "%1$sx Resistance",
"cobblemon.battle.ui.effectiveness.immunity": "%1$sx Immunity",
"cobblemon.ui.evolve": "Evolve",
"cobblemon.ui.evolution": "Evolution",
"cobblemon.ui.evolve.hint": "%1$s seems ready to evolve!",