mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-20 22:26:24 -04:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b79e73928a | ||
|
|
c89ea759a6 | ||
|
|
c6479525f9 | ||
|
|
2a7dababab | ||
|
|
7ec6e61d4a | ||
|
|
7735af5c11 | ||
|
|
18d25dcf04 | ||
|
|
e259dc18a2 | ||
|
|
7439436e93 | ||
|
|
1c51d5ca98 | ||
|
|
d94952347f | ||
|
|
7fe6141640 | ||
|
|
4b1653ca6c | ||
|
|
7265ab57b9 | ||
|
|
27e3a95b06 | ||
|
|
38de3c6267 | ||
|
|
376e5ac906 | ||
|
|
b225283dcc | ||
|
|
b90f852a71 | ||
|
|
fdf7a9703a | ||
|
|
bb41f28342 | ||
|
|
c5ee7ebe7a | ||
|
|
f6ae10e1d9 | ||
|
|
b2070b53fb | ||
|
|
7fb873dc2c | ||
|
|
641f529de3 | ||
|
|
db9fb4fefa |
@@ -86,9 +86,9 @@ import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.FORM_ASPECTS
|
||||
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
|
||||
@@ -100,6 +100,7 @@ import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.BlockClickTrigger
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -254,13 +255,8 @@ object Cobblemon {
|
||||
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { event ->
|
||||
val player = event.player
|
||||
val block = player.level().getBlockState(event.pos).block
|
||||
player.party().forEach { pokemon ->
|
||||
pokemon.lockedEvolutions
|
||||
.filterIsInstance<BlockClickEvolution>()
|
||||
.forEach { evolution ->
|
||||
evolution.attemptEvolution(pokemon, BlockClickEvolution.BlockInteractionContext(block, player.level()))
|
||||
}
|
||||
}
|
||||
val context = BlockClickTrigger.Context(block, player.level())
|
||||
player.party().forEach { pokemon -> pokemon.triggerTransformations(BlockClickTrigger::class.java, context) }
|
||||
}
|
||||
|
||||
PlatformEvents.CHANGE_DIMENSION.subscribe {
|
||||
@@ -301,6 +297,7 @@ object Cobblemon {
|
||||
|
||||
SHINY_ASPECT.register()
|
||||
GENDER_ASPECT.register()
|
||||
FORM_ASPECTS.register()
|
||||
COSMETIC_SLOT_ASPECT.register()
|
||||
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
|
||||
@@ -170,9 +170,6 @@ import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptanceP
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.PerformTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
|
||||
@@ -211,6 +208,12 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.form.AddFormPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.form.ClearFormsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.form.RemoveFormPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.AcceptFormPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.RevertFormPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
|
||||
@@ -430,6 +433,11 @@ object CobblemonNetwork {
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
|
||||
// Form packets
|
||||
list.add(PacketRegisterInfo(AddFormPacket.ID, AddFormPacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(ClearFormsPacket.ID, ClearFormsPacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RemoveFormPacket.ID, RemoveFormPacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
|
||||
|
||||
@@ -533,6 +541,10 @@ object CobblemonNetwork {
|
||||
// NPC packets
|
||||
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
|
||||
|
||||
// Form Packets
|
||||
list.add(PacketRegisterInfo(AcceptFormPacket.ID, AcceptFormPacket::decode, AcceptFormHandler))
|
||||
list.add(PacketRegisterInfo(RevertFormPacket.ID, RevertFormPacket::decode, RevertFormHandler))
|
||||
|
||||
// Riding packet(s)
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateOrientationPacket.ID, ServerboundUpdateOrientationPacket::decode, OrientationPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.abilities
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -25,7 +26,7 @@ class AbilityTemplate(
|
||||
var builder: (AbilityTemplate, forced: Boolean, priority: Priority) -> Ability = { template, forced, priority -> Ability(template, forced, priority) },
|
||||
val displayName: String = "cobblemon.ability.$name",
|
||||
val description: String = "cobblemon.ability.$name.desc"
|
||||
) {
|
||||
) : ShowdownIdentifiable {
|
||||
/**
|
||||
* Returns the Ability or if applicable the extension connected to this template
|
||||
*/
|
||||
@@ -45,6 +46,8 @@ class AbilityTemplate(
|
||||
*/
|
||||
fun create(json: JsonObject) = create().loadFromJSON(json)
|
||||
|
||||
override fun showdownId(): String = ShowdownIdentifiable.REGEX.replace(this.name.lowercase(), "")
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -48,9 +48,9 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.DefeatTransformationProgress
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.LastBattleCriticalHitsTransformationProgress
|
||||
import com.cobblemon.mod.common.pokemon.transformation.requirements.DefeatRequirement
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
@@ -77,7 +77,7 @@ open class PokemonBattle(
|
||||
val side2: BattleSide
|
||||
) {
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = true
|
||||
var mute = false
|
||||
val struct = this.asMoLangValue()
|
||||
val runtime = MoLangRuntime().also { it.environment.query = struct }
|
||||
|
||||
@@ -92,7 +92,7 @@ open class PokemonBattle(
|
||||
actor.battle = this
|
||||
actor.pokemonList.forEach { battlePokemon ->
|
||||
battlePokemon.effectedPokemon.evolutionProxy.current().progress()
|
||||
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
|
||||
.filterIsInstance<LastBattleCriticalHitsTransformationProgress>()
|
||||
.forEach { it.reset() }
|
||||
}
|
||||
}
|
||||
@@ -252,11 +252,12 @@ open class PokemonBattle(
|
||||
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
|
||||
val pokemon = opponentPokemon.effectedPokemon
|
||||
if (facedFainted) {
|
||||
// TODO refactor and apply to forms
|
||||
pokemon.lockedEvolutions.forEach { evolution ->
|
||||
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
|
||||
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
|
||||
progress.updateProgress(DefeatEvolutionProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatTransformationProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatTransformationProgress() }
|
||||
progress.updateProgress(DefeatTransformationProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.drop
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
class EvolutionItemDropEntry : ItemDropEntry() {
|
||||
val requirements: MutableSet<EvolutionRequirement> = mutableSetOf()
|
||||
val requirements: MutableSet<TransformationRequirement> = mutableSetOf()
|
||||
|
||||
override fun canDrop(pokemon: Pokemon?): Boolean = pokemon != null && requirements.all { it.check(pokemon) }
|
||||
}
|
||||
+1
-1
@@ -12,8 +12,8 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
|
||||
/**
|
||||
* Fired when an evolution is accepted.
|
||||
|
||||
+1
-1
@@ -10,8 +10,8 @@ package com.cobblemon.mod.common.api.events.pokemon.evolution
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
|
||||
/**
|
||||
* Fired after an evolution finishes.
|
||||
|
||||
+2
-2
@@ -8,9 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.EvolutionDisplay
|
||||
|
||||
/**
|
||||
* Fired before an evolution is sent to clients.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -12,8 +12,8 @@ import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.util.getBoolean
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.FossilRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.pokemon.transformation.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
|
||||
@@ -74,7 +74,7 @@ abstract class RequestManager<T : ServerPlayerActionRequest> {
|
||||
?: this.getInboundRequest(receiver.uuid, requestID)
|
||||
|
||||
/** Determines whether [player] can receive a [T]. */
|
||||
open fun isBusy(player: ServerPlayer): Boolean = player.isInBattle() || player.isTrading() || player.party().find { it.entity?.isBusy == true } != null
|
||||
open fun isBusy(player: ServerPlayer): Boolean = player.isBusy() || player.isPartyBusy()
|
||||
|
||||
/** Determines if the [target] is a valid interaction request target. */
|
||||
abstract fun isValidInteraction(player: ServerPlayer, target: ServerPlayer): Boolean
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.pokedex.*
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
@@ -57,9 +56,10 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticl
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.ItemInteractionTrigger
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.LevelUpTrigger
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.TradeTrigger
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||
@@ -1180,16 +1180,15 @@ object MoLangFunctions {
|
||||
|
||||
map.put("result") { evolution.result.asMoLangValue() }
|
||||
|
||||
if (evolution is LevelUpEvolution) {
|
||||
if (evolution.trigger is LevelUpTrigger) {
|
||||
map.put("is_level_up") { DoubleValue.ONE }
|
||||
} else if (evolution is TradeEvolution) {
|
||||
} else if (evolution.trigger is TradeTrigger) {
|
||||
map.put("is_trade") { DoubleValue.ONE }
|
||||
} else if (evolution is ItemInteractionEvolution) {
|
||||
} else if (evolution.trigger is ItemInteractionTrigger) {
|
||||
map.put("is_item") { DoubleValue.ONE }
|
||||
}
|
||||
|
||||
map.put("is_optional") { DoubleValue(evolution.optional) }
|
||||
map.put("consumes_held_item") { DoubleValue(evolution.consumeHeldItem) }
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
@@ -56,7 +57,7 @@ open class MoveTemplate(
|
||||
val critRatio: Double,
|
||||
val effectChances: Array<Double>,
|
||||
val actionEffect: ActionEffectTimeline?
|
||||
) {
|
||||
) : ShowdownIdentifiable {
|
||||
val struct: MoStruct by lazy {
|
||||
QueryStruct(hashMapOf())
|
||||
.addFunction("name") { StringValue(name) }
|
||||
@@ -192,4 +193,6 @@ open class MoveTemplate(
|
||||
}
|
||||
return this.elementalType
|
||||
}
|
||||
|
||||
override fun showdownId() = ShowdownIdentifiable.REGEX.replace(this.name.lowercase(), "")
|
||||
}
|
||||
|
||||
@@ -466,29 +466,18 @@ open class PokemonProperties {
|
||||
}
|
||||
|
||||
fun matches(properties: PokemonProperties): Boolean {
|
||||
return properties.asString() == this.asString()
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
return this.commonMatches(properties) && this.customProperties.containsAll(properties.customProperties)
|
||||
}
|
||||
|
||||
// Undefined properties will match whatever they're compared with
|
||||
private fun commonMatches(pokemon: Pokemon): Boolean {
|
||||
level?.takeIf { it != pokemon.level }?.let { return false }
|
||||
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
|
||||
gender?.takeIf { it != pokemon.gender }?.let { return false }
|
||||
species?.run {
|
||||
try {
|
||||
val species = if (this == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace()) ?: return@run
|
||||
}
|
||||
if (pokemon.species != species) {
|
||||
return false
|
||||
}
|
||||
} catch (e: ResourceLocationException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
species?.takeIf { it.asSpecies() != pokemon.species }?.let { return false }
|
||||
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemon.form.formOnlyShowdownId(), true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
|
||||
@@ -527,6 +516,36 @@ open class PokemonProperties {
|
||||
return true
|
||||
}
|
||||
|
||||
// Undefined properties will match whatever they're compared with
|
||||
private fun commonMatches(properties: PokemonProperties): Boolean {
|
||||
level?.takeIf { it != properties.level }?.let { return false }
|
||||
shiny?.takeIf { it != properties.shiny }?.let { return false }
|
||||
gender?.takeIf { it != properties.gender }?.let { return false }
|
||||
species?.takeIf { it.asSpecies() != properties.species?.asSpecies() }?.let { return false }
|
||||
nickname?.takeIf { it.string != properties.nickname?.string }?.let { return false }
|
||||
form?.takeIf { it != properties.form }?.let { return false }
|
||||
friendship?.takeIf { it != properties.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
|
||||
nature?.takeIf { it != properties.nature }?.let { return false }
|
||||
ability?.takeIf { it != properties.ability }?.let { return false }
|
||||
status?.takeIf { it != properties.status }?.let { return false }
|
||||
ivs?.forEach{ stat ->
|
||||
if (stat.value != properties.ivs?.get(stat.key)) { return false }
|
||||
}
|
||||
evs?.forEach{ stat ->
|
||||
if (stat.value != properties.evs?.get(stat.key)) { return false }
|
||||
}
|
||||
type?.takeIf { it != properties.type }?.let { return false }
|
||||
teraType?.takeIf { it != properties.teraType }?.let { return false }
|
||||
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
|
||||
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
|
||||
tradeable?.takeIf { it != properties.tradeable }?.let { return false }
|
||||
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
|
||||
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
|
||||
moves?.takeIf { it.any { move -> properties.moves?.none { it == move } == true } }?.let { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO Review the need for this, usage for us is in commands that deviate from the behavior established by multiple Pokémon query equivalent
|
||||
fun isSubSetOf(properties: PokemonProperties): Boolean {
|
||||
level?.takeIf { it != properties.level }?.let { return false }
|
||||
@@ -798,4 +817,15 @@ open class PokemonProperties {
|
||||
return potentialAbility.template.create(false, potentialAbility.priority)
|
||||
}
|
||||
|
||||
private fun String.asSpecies() =
|
||||
try {
|
||||
if (this == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace())
|
||||
}
|
||||
}
|
||||
catch (e: ResourceLocationException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
|
||||
import com.cobblemon.mod.common.api.abilities.CommonAbility
|
||||
import com.cobblemon.mod.common.api.abilities.PotentialAbility
|
||||
import com.cobblemon.mod.common.api.ai.SleepDepth
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
@@ -24,14 +26,15 @@ 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
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroupAdapter
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
@@ -42,11 +45,16 @@ import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPack
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.pokemon.abilities.SpecialAbility
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.transformation.adapters.*
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.GigantamaxForm
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.PermanentForm
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.StandardForm
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.pokemon.transformation.requirements.HeldItemRequirement
|
||||
import com.cobblemon.mod.common.pokemon.transformation.requirements.MoveSetRequirement
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -69,6 +77,7 @@ import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
import net.minecraft.world.phys.AABB
|
||||
import java.util.NoSuchElementException
|
||||
|
||||
object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
|
||||
@@ -84,10 +93,8 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(ExperienceGroup::class.java, ExperienceGroupAdapter)
|
||||
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
|
||||
.registerTypeAdapter(Learnset::class.java, LearnsetAdapter)
|
||||
.registerTypeAdapter(Evolution::class.java, CobblemonEvolutionAdapter)
|
||||
.registerTypeAdapter(AABB::class.java, BoxAdapter)
|
||||
.registerTypeAdapter(AbilityPool::class.java, AbilityPoolAdapter)
|
||||
.registerTypeAdapter(EvolutionRequirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(PreEvolution::class.java, CobblemonPreEvolutionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(Set::class.java, Evolution::class.java).type, LazySetAdapter(Evolution::class))
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
@@ -106,7 +113,10 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
|
||||
.registerTypeAdapter(TransformationTrigger::class.java, CobblemonTriggerAdapter)
|
||||
.registerTypeAdapter(TransformationRequirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(FormData::class.java, FormAdapter)
|
||||
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
|
||||
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
@@ -126,6 +136,9 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
get() = this.speciesByIdentifier.values
|
||||
val implemented = mutableListOf<Species>()
|
||||
|
||||
val forms: Collection<FormData>
|
||||
get() = this.species.flatMap { it.allForms }
|
||||
|
||||
init {
|
||||
SpeciesAdditions.observable.subscribe {
|
||||
this.species.forEach(Species::initialize)
|
||||
@@ -225,34 +238,18 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
}
|
||||
|
||||
/**
|
||||
* The representation of a [Species] and/or [FormData] in the context of showdown.
|
||||
* The representation of [FormData] in the context of Showdown.
|
||||
* This is intended as a sort of DTO that can be easily converted between JSON and Java/JS objects.
|
||||
*
|
||||
* @param species The [Species] being converted or the base species if the form is not null.
|
||||
* @param form The [FormData] being converted int o species (Showdown considers them species) will be null when dealing with the base form.
|
||||
* @param form The [FormData] being converted into a species (Showdown considers them species).
|
||||
*/
|
||||
@Suppress("unused")
|
||||
internal class ShowdownSpecies(species: Species, form: FormData?) {
|
||||
val num = species.nationalPokedexNumber
|
||||
val name = if (form != null) "${createShowdownName(species)}-${form.name}" else createShowdownName(species)
|
||||
val baseSpecies = if (form != null) createShowdownName(species) else this.name
|
||||
val forme = form?.name
|
||||
// ToDo baseForme
|
||||
val otherFormes = if (form == null && species.forms.isNotEmpty()) species.forms.map { "${this.name}-${it.name}" } else emptyList()
|
||||
val formeOrder = if (form == null && this.otherFormes.isNotEmpty()) arrayListOf(this.name, *this.otherFormes.toTypedArray()) else emptyList()
|
||||
val abilities: Map<String, String> = mapOf(
|
||||
"0" to "No Ability",
|
||||
"1" to "No Ability",
|
||||
"H" to "No Ability",
|
||||
"S" to "No Ability"
|
||||
)
|
||||
val types = (form?.types ?: species.types).map { it.name.replaceFirstChar(Char::uppercase) }
|
||||
val preevo: String? = (form?.preEvolution ?: species.preEvolution)?.let { if (it.form == it.species.standardForm) createShowdownName(it.species) else "${createShowdownName(it.species)}-${it.form.name}" }
|
||||
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
|
||||
val evos = if ((form?.evolutions ?: species.evolutions).isEmpty()) emptyList() else arrayListOf("")
|
||||
val nfe = this.evos.isNotEmpty()
|
||||
val eggGroups = (form?.eggGroups ?: species.eggGroups).map { it.showdownID }
|
||||
val gender: String? = when (form?.maleRatio ?: species.maleRatio) {
|
||||
internal open class ShowdownSpecies(form: FormData) {
|
||||
val num = form.species.nationalPokedexNumber
|
||||
val name = form.showdownName()
|
||||
val types = form.types.map { it.name.replaceFirstChar(Char::uppercase) }
|
||||
val eggGroups = form.eggGroups.map { it.showdownID }
|
||||
val gender: String? = when (form.maleRatio) {
|
||||
0F -> "F"
|
||||
1F -> "M"
|
||||
-1F, 1.125F -> "N"
|
||||
@@ -260,35 +257,132 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
}
|
||||
val genderRatio = if (this.gender == null)
|
||||
mapOf(
|
||||
"maleRatio" to (form?.maleRatio ?: species.maleRatio),
|
||||
"femaleRation" to (1F - (form?.maleRatio ?: species.maleRatio))
|
||||
"M" to (form.maleRatio),
|
||||
"F" to (1F - (form.maleRatio))
|
||||
) else null
|
||||
val baseStats = mapOf(
|
||||
"hp" to (form?.baseStats?.get(Stats.HP) ?: species.baseStats[Stats.HP] ?: 1),
|
||||
"atk" to (form?.baseStats?.get(Stats.ATTACK) ?: species.baseStats[Stats.ATTACK] ?: 1),
|
||||
"def" to (form?.baseStats?.get(Stats.DEFENCE) ?: species.baseStats[Stats.DEFENCE] ?: 1),
|
||||
"spa" to (form?.baseStats?.get(Stats.SPECIAL_ATTACK) ?: species.baseStats[Stats.SPECIAL_ATTACK] ?: 1),
|
||||
"spd" to (form?.baseStats?.get(Stats.SPECIAL_DEFENCE) ?: species.baseStats[Stats.SPECIAL_DEFENCE] ?: 1),
|
||||
"spe" to (form?.baseStats?.get(Stats.SPEED) ?: species.baseStats[Stats.SPEED] ?: 1)
|
||||
)
|
||||
val heightm = (form?.height ?: species.height) / 10
|
||||
val weightkg = (form?.weight ?: species.weight) / 10
|
||||
val baseStats = Stats.PERMANENT.associateBy({ it.showdownId }, { form.baseStats[it] ?: 1 })
|
||||
val heightm = form.height / 10
|
||||
val weightkg = form.weight / 10
|
||||
// This is ugly, but we already have it hardcoded in the mod anyway
|
||||
val maxHP = if (species.showdownId() == "shedinja") 1 else null
|
||||
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
|
||||
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
|
||||
// ToDo battleOnly
|
||||
// ToDo changesFrom
|
||||
val requiredMove = form?.requiredMove
|
||||
val requiredItem = form?.requiredItem
|
||||
val requiredItems = form?.requiredItems
|
||||
val maxHP = if (this.num == 292) 1 else null
|
||||
val cannotDynamax = form.dynamaxBlocked
|
||||
val abilities = form.showdownAbilityMapping()
|
||||
// Common fields exempt from standard forms
|
||||
val baseSpecies = if (form !is StandardForm) form.species.standardForm.showdownName() else null
|
||||
val forme = if (form !is StandardForm) form.name else null
|
||||
|
||||
/**
|
||||
* The following fields are optional and currently omitted
|
||||
*
|
||||
* cosmeticFormes (not needed, we have species features)
|
||||
* addedType (tertiary type [by name or showdownid] added by moves or abilities)
|
||||
* tier (smogon tryhard bs)
|
||||
* doublesTier (smogon tryhard bs)
|
||||
* natDexTier (smogon tryhard bs)
|
||||
* evoType (the evolution method used to evolve from 'prevo'; trade', 'useItem', 'levelMove', 'levelExtra', 'levelFriendship', 'levelHold', 'other')
|
||||
* evoMove (the move [by name or showdownid] used to evolve from 'prevo')
|
||||
* evoLevel (the minimum level required to evolve from 'prevo')
|
||||
* canHatch (flag for baby pokemon)
|
||||
* color (color, value is arbitrary)
|
||||
* tags (flags for filtering pokemon; 'Mega', 'Mythical', 'Sub-Legendary', 'Restricted Legendary', 'Paradox')
|
||||
* unreleasedHidden (whether this pokemon has an unreleased hidden ability)
|
||||
* maleOnlyHidden (whether it is only possible to get the hidden ability on a male pokemon)
|
||||
* gmaxUnreleased (whether this pokemon has an unreleased gmax form)
|
||||
* pokemonGoData (no one cares)
|
||||
*/
|
||||
}
|
||||
|
||||
private fun createShowdownName(species: Species): String {
|
||||
if (species.resourceIdentifier.namespace == Cobblemon.MODID) {
|
||||
return species.name
|
||||
/**
|
||||
* The representation of a [PermanentForm] in the context of Showdown.
|
||||
*
|
||||
* @param form The [PermanentForm] being converted into a species.
|
||||
*/
|
||||
internal open class ShowdownPermanentForm(form: PermanentForm) : ShowdownSpecies(form) {
|
||||
// species this form evolves from
|
||||
val prevo = form.preEvolution?.form?.showdownName()
|
||||
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
|
||||
val evos = form.evolutions.map { "" }
|
||||
// not fully evolved
|
||||
val nfe = this.evos.isNotEmpty()
|
||||
// whether there is a gmax form to change to
|
||||
val canGigantamax: String? = form.changeableForms.filterIsInstance<GigantamaxForm>().firstOrNull()?.name // TODO id better
|
||||
}
|
||||
|
||||
/**
|
||||
* The representation of a [StandardForm] in the context of Showdown.
|
||||
*
|
||||
* @param form The [StandardForm] being converted into a species.
|
||||
*/
|
||||
internal class ShowdownStandardForm(form: StandardForm) : ShowdownPermanentForm(form) {
|
||||
// list of all forms descending from this base species
|
||||
val otherFormes = form.flattenForms().map { it.showdownName() }
|
||||
// list of forms [by name or showdownid] in the order they appear in game [union of baseSpecies, otherFormes, and cosmeticFormes] with the exception of ending with 'Gmax' forms
|
||||
val formeOrder = if (this.otherFormes.isNotEmpty()) arrayListOf(this.name, *this.otherFormes.toTypedArray()) else null
|
||||
|
||||
// TODO we don't enforce any form order with our Species registration - right now this looks like only a problem with Mega Rayquaza, but investigate changes needed on Showdown's end to enable these lists to be more dynamic
|
||||
}
|
||||
|
||||
/**
|
||||
* The representation of a [TemporaryForm] in the context of Showdown.
|
||||
*
|
||||
* @param form The [TemporaryForm] being converted into a species.
|
||||
*/
|
||||
internal class ShowdownTemporaryForm(form: TemporaryForm) : ShowdownSpecies(form) {
|
||||
// form change can occur during a battle
|
||||
val battleOnly = if (form.battleOnly) form.parentForm.showdownName() else null
|
||||
// form change can occur outside of battle (mutually exclusive with battleOnly)
|
||||
val changesFrom = if (!form.battleOnly) form.parentForm.showdownName() else null
|
||||
// pokemon must know this move to be in this form
|
||||
val requiredMove = form.requirements.filterIsInstance<MoveSetRequirement>().firstOrNull()?.move?.showdownId() // TODO update showdown to support 'requiredMoves'
|
||||
// pokemon must hold one of these item(s) to be in this form
|
||||
val requiredItems = form.requirements.filterIsInstance<HeldItemRequirement>().firstOrNull()?.let { requirement ->
|
||||
try {
|
||||
requirement.itemCondition.items.get().map { BuiltInRegistries.ITEM.getKey(it.value()).path } // TODO held items on showdown are ignorant of namespace
|
||||
}
|
||||
catch (e: NoSuchElementException)
|
||||
{
|
||||
Cobblemon.LOGGER.error("Failed to resolve required held item for ${form.showdownId()}}") // TODO this needs to be done after tags are loaded...
|
||||
null
|
||||
}
|
||||
}
|
||||
return "${species.resourceIdentifier.namespace}:${species.name}"
|
||||
}
|
||||
|
||||
/** Accommodate for Showdown bullshit. */
|
||||
/*
|
||||
private fun TemporaryForm.showdownParentForm(): Any {
|
||||
if (this.parentOverrides.isNotEmpty())
|
||||
return this.species.standardForm.flattenForms().filter {
|
||||
parentOverrides.contains(it.showdownId())
|
||||
}.map {
|
||||
it.showdownName()
|
||||
}
|
||||
else
|
||||
return parentForm.showdownName()
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a mapping of the [PotentialAbility]s belonging to a [FormData] in Showdown identifiable format.
|
||||
*
|
||||
* @return A map of type (numeric for [CommonAbility]s, H for [HiddenAbility], S for [SpecialAbility]) to showdownid.
|
||||
*/
|
||||
private fun FormData.showdownAbilityMapping(): Map<String, String>? {
|
||||
// potential abilities
|
||||
val commonAbilities = this.abilities.filterIsInstance<CommonAbility>()
|
||||
val hiddenAbility = this.abilities.find { it is HiddenAbility }
|
||||
val specialAbility = this.abilities.find { it is SpecialAbility }
|
||||
|
||||
// can have multiple common abilities
|
||||
val abilities = mutableMapOf<String, String>()
|
||||
val mapping: (String, PotentialAbility) -> Unit = { key, ability ->
|
||||
abilities.put(key, ability.template.showdownId())
|
||||
}
|
||||
|
||||
// can only have one hidden and one special ability
|
||||
commonAbilities.forEachIndexed { index, ability -> mapping(index.toString(), ability) }
|
||||
hiddenAbility?.let { ability -> mapping("H", ability) }
|
||||
specialAbility?.let { ability -> mapping("S", ability) }
|
||||
return if (abilities.isEmpty()) null else abilities
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.aspect
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* A provider of aspects for a [SpeciesFeature].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since Feb 12th, 2024
|
||||
*/
|
||||
interface FeatureAspectProvider: AspectProvider {
|
||||
var default: String?
|
||||
var isAspect: Boolean
|
||||
|
||||
/** Returns true if this [aspect] is provided by this provider. */
|
||||
fun matches(aspect: String): Boolean
|
||||
|
||||
/** Creates a [SpeciesFeature] that provides the respective [aspect]. */
|
||||
fun from(aspect: String): SpeciesFeature<*>?
|
||||
|
||||
/** Sets the [aspect] that's provided. */
|
||||
fun set(pokemon: Pokemon, aspect: String)
|
||||
}
|
||||
+2
-2
@@ -24,11 +24,11 @@ interface SingleConditionalAspectProvider : AspectProvider {
|
||||
fun getForFeature(name: String): SingleConditionalAspectProvider {
|
||||
return object : SingleConditionalAspectProvider {
|
||||
override val aspect: String = name
|
||||
override fun meetsCondition(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == true
|
||||
override fun meetsCondition(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.value == true
|
||||
override fun meetsCondition(pokemonProperties: PokemonProperties) = pokemonProperties
|
||||
.customProperties
|
||||
.filterIsInstance<FlagSpeciesFeature>()
|
||||
.any { it.name == name && it.enabled }
|
||||
.any { it.name == name && it.value }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
|
||||
/**
|
||||
* Represents an evolution of a [Pokemon] that can only occur during specific actions and with added context.
|
||||
* For the default implementations see [ItemInteractionEvolution] & [TradeEvolution].
|
||||
*
|
||||
* @param RC The context given at runtime when querying the [Evolution].
|
||||
* @param TC The context that is serialized from JSON during species loading, this is what the [RC] is expected to match against.
|
||||
* @author Licious
|
||||
* @since March 19th, 2022
|
||||
*/
|
||||
interface ContextEvolution<RC, TC> : Evolution {
|
||||
|
||||
/**
|
||||
* The target context for this [Evolution] to even be tested.
|
||||
*/
|
||||
val requiredContext: TC
|
||||
|
||||
/**
|
||||
* Attempts to evolve the given [Pokemon] under the given context of type [RC].
|
||||
*
|
||||
* @param pokemon The [Pokemon] attempting to evolve.
|
||||
* @param context The context of this query.
|
||||
* @return If the evolution was successful.
|
||||
*/
|
||||
fun attemptEvolution(pokemon: Pokemon, context: RC): Boolean {
|
||||
if (this.testContext(pokemon, context) && super.test(pokemon)) {
|
||||
return super.evolve(pokemon)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given context is valid for the [requiredContext].
|
||||
*
|
||||
* @param pokemon The [Pokemon] attempting to evolve.
|
||||
* @param context The context of this query.
|
||||
* @return If the context matched the [requiredContext].
|
||||
*/
|
||||
fun testContext(pokemon: Pokemon, context: RC): Boolean
|
||||
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.progress.EvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.controller.ClientEvolutionController
|
||||
import com.cobblemon.mod.common.pokemon.evolution.controller.ServerEvolutionController
|
||||
|
||||
/**
|
||||
* Responsible for holding all available [EvolutionLike]s in the [Pokemon].
|
||||
* Also handles all the networking behind them.
|
||||
* For the Cobblemon default implementations see [ClientEvolutionController] & [ServerEvolutionController].
|
||||
*
|
||||
* @author Licious
|
||||
* @since April 28th, 2022
|
||||
*/
|
||||
interface EvolutionController<T : EvolutionLike, out I : PreProcessor> : MutableSet<T> {
|
||||
|
||||
/**
|
||||
* Resolves the [Pokemon] attached to this controller.
|
||||
*
|
||||
* @return The [Pokemon] this controller is attached to.
|
||||
*/
|
||||
fun pokemon(): Pokemon
|
||||
|
||||
/**
|
||||
* Starts the given evolution on this controller.
|
||||
* All the necessary networking will be handled.
|
||||
*
|
||||
* @param evolution The [EvolutionLike] starting.
|
||||
*/
|
||||
fun start(evolution: T)
|
||||
|
||||
/**
|
||||
* Returns an immutable collection of [EvolutionProgress]es attached to this controller.
|
||||
*
|
||||
* @return The currently tracked [EvolutionProgress]es.
|
||||
*/
|
||||
fun progress(): Collection<EvolutionProgress<*>>
|
||||
|
||||
/**
|
||||
* Attaches a [EvolutionProgress] to this controller.
|
||||
*
|
||||
* @param P The type of [EvolutionProgress].
|
||||
* @param progress The [EvolutionProgress] being attached for tracking.
|
||||
* @return The instance being tracked.
|
||||
*/
|
||||
fun <P : EvolutionProgress<*>> trackProgress(progress: P): P
|
||||
|
||||
/**
|
||||
* Attempts to find an [EvolutionProgress] in [progress] or creates and tracks using [trackProgress].
|
||||
*
|
||||
* @param P The type of [EvolutionProgress].
|
||||
* @param predicate The lambda attempting to find specific progress. This needs to ensure the [EvolutionProgress] is of type [P] or the cast will fail.
|
||||
* @param progressFactory The lambda creating a new instance of type [P] if [predicate] can't find an element.
|
||||
* @return The found match in [progress] or the created and now tracked with [trackProgress].
|
||||
*/
|
||||
fun <P : EvolutionProgress<*>> progressFirstOrCreate(predicate: (progress: EvolutionProgress<*>) -> Boolean, progressFactory: () -> P): P
|
||||
|
||||
fun asIntermediate(): I
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.evolution.CobblemonEvolutionDisplay
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
|
||||
/**
|
||||
* Represents an evolution of a [Pokemon], this is the client side counterpart of [Evolution].
|
||||
* This has no attachments to any data regarding the evolution itself and only serves for display purposes and basic communication.
|
||||
*
|
||||
* @author Licious
|
||||
* @since April 28th, 2022
|
||||
*/
|
||||
interface EvolutionDisplay : EvolutionLike {
|
||||
|
||||
/**
|
||||
* The [Species] of the evolution result.
|
||||
*/
|
||||
val species: Species
|
||||
|
||||
/**
|
||||
* The aspects of the evolution result.
|
||||
* These are used by the client to resolve the entity it needs to display.
|
||||
*/
|
||||
val aspects: Set<String>
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val CODEC: Codec<EvolutionDisplay> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.fieldOf("id").forGetter(EvolutionDisplay::id),
|
||||
Species.BY_IDENTIFIER_CODEC.fieldOf("species").forGetter(EvolutionDisplay::species),
|
||||
Codec.list(Codec.STRING).fieldOf("aspects").forGetter { it.aspects.toList() }
|
||||
).apply(instance) { id, species, aspects -> CobblemonEvolutionDisplay(id, species, aspects.toSet()) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution
|
||||
|
||||
/**
|
||||
* Responsible for holding the different [EvolutionController] implementations based on the logical side.
|
||||
* If you haven't already checked your side previously you can use [isClient].
|
||||
*
|
||||
* @param C The type of [EvolutionLike] on the client side.
|
||||
* @param S The type of [EvolutionLike] on the server side.
|
||||
* @param CI The type of [PreProcessor] on the client side.
|
||||
* @param SI The type of [PreProcessor] on the server side.
|
||||
*
|
||||
* @author Licious
|
||||
* @since June 18th, 2022
|
||||
*/
|
||||
interface EvolutionProxy<C : EvolutionLike, S : EvolutionLike, CI : PreProcessor, SI : PreProcessor> {
|
||||
|
||||
/**
|
||||
* Checks if the current logical side is the client.
|
||||
*
|
||||
* @return True if the current logical side is the client.
|
||||
*/
|
||||
fun isClient(): Boolean
|
||||
|
||||
/**
|
||||
* Returns the current [EvolutionController].
|
||||
* Keep in mind this will have type erasure and as such most operations will not be possible.
|
||||
* It's recommended to work with [client] or [server] when possible.
|
||||
*
|
||||
* @return The current [EvolutionController].
|
||||
*/
|
||||
fun current(): EvolutionController<out EvolutionLike, *>
|
||||
|
||||
/**
|
||||
* The client side implementation of the [EvolutionController].
|
||||
*
|
||||
* @throws [ClassCastException] if called from the server side.
|
||||
*
|
||||
* @return The client side implementation of the [EvolutionController] if possible.
|
||||
*/
|
||||
fun client(): EvolutionController<C, CI>
|
||||
|
||||
/**
|
||||
* The server side implementation of the [EvolutionController].
|
||||
*
|
||||
* @throws [ClassCastException] if called from the client side.
|
||||
*
|
||||
* @return The server side implementation of the [EvolutionController] if possible.
|
||||
*/
|
||||
fun server(): EvolutionController<S, SI>
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
|
||||
/**
|
||||
* Represents an evolution of a [Pokemon] that can occur without any additional context or actions.
|
||||
* For the default implementation see [LevelUpEvolution].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 19th, 2022
|
||||
*/
|
||||
interface PassiveEvolution : Evolution {
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] satisfies the requirements.
|
||||
* If yes the evolution will attempt to start.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being tested.
|
||||
* @return If the [Pokemon] will evolve.
|
||||
*/
|
||||
fun attemptEvolution(pokemon: Pokemon): Boolean {
|
||||
if (super.test(pokemon)) {
|
||||
return super.evolve(pokemon)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* If the evolution will disappear once the conditions are no longer met.
|
||||
*/
|
||||
val permanent: Boolean
|
||||
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Saves and loads [Evolution]s with JSON.
|
||||
* For the default implementation see [CobblemonEvolutionAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 20th, 2022
|
||||
*/
|
||||
interface EvolutionAdapter : JsonDeserializer<Evolution>, JsonSerializer<Evolution> {
|
||||
|
||||
/**
|
||||
* Registers the given type of [Evolution] to it's associated ID for deserialization.
|
||||
*
|
||||
* @param T The type of [Evolution].
|
||||
* @param id The id of the evolution event.
|
||||
* @param type The [KClass] of the type.
|
||||
*/
|
||||
fun <T : Evolution> registerType(id: String, type: KClass<T>)
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Saves and loads [EvolutionRequirement]s with JSON.
|
||||
* For the default implementation see [CobblemonRequirementAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 21st, 2022
|
||||
*/
|
||||
interface RequirementAdapter : JsonDeserializer<EvolutionRequirement>, JsonSerializer<EvolutionRequirement> {
|
||||
|
||||
/**
|
||||
* Registers the given type of [EvolutionRequirement] to it's associated ID for deserialization.
|
||||
*
|
||||
* @param T The type of [EvolutionRequirement].
|
||||
* @param id The id of the evolution event.
|
||||
* @param type The [KClass] of the type.
|
||||
*/
|
||||
fun <T : EvolutionRequirement> registerType(id: String, type: KClass<T>)
|
||||
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.pokemon.evolution.progress
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.evolution.controller.ServerEvolutionController
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.Lifecycle
|
||||
import net.minecraft.core.MappedRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
object EvolutionProgressTypes {
|
||||
|
||||
private val REGISTRY: Registry<EvolutionProgressType<*>> = MappedRegistry(
|
||||
ResourceKey.createRegistryKey(cobblemonResource("evolution_progress")), Lifecycle.stable()
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
val DAMAGE_TAKEN = this.registerType(DamageTakenEvolutionProgress.ID, EvolutionProgressType(DamageTakenEvolutionProgress.CODEC))
|
||||
@JvmStatic
|
||||
val DEFEAT = this.registerType(DefeatEvolutionProgress.ID, EvolutionProgressType(DefeatEvolutionProgress.CODEC))
|
||||
@JvmStatic
|
||||
val LAST_BATTLE_CRITICAL_HITS = this.registerType(LastBattleCriticalHitsEvolutionProgress.ID, EvolutionProgressType(LastBattleCriticalHitsEvolutionProgress.CODEC))
|
||||
@JvmStatic
|
||||
val RECOIL = this.registerType(RecoilEvolutionProgress.ID, EvolutionProgressType(RecoilEvolutionProgress.CODEC))
|
||||
@JvmStatic
|
||||
val USE_MOVE = this.registerType(UseMoveEvolutionProgress.ID, EvolutionProgressType(UseMoveEvolutionProgress.CODEC))
|
||||
|
||||
/**
|
||||
* Registers an evolution progress.
|
||||
*
|
||||
* @param T The type of the [EvolutionProgress].
|
||||
* @param id The [Identifier].
|
||||
* @param type The [EvolutionProgressType] record.
|
||||
* @return The registered [EvolutionProgressType] of type [T].
|
||||
*
|
||||
* @throws [IllegalStateException] if [Registry.register] fails.
|
||||
*/
|
||||
@Throws(IllegalStateException::class)
|
||||
@JvmStatic
|
||||
fun <T : EvolutionProgress<*>> registerType(id: ResourceLocation, type: EvolutionProgressType<T>): EvolutionProgressType<T> {
|
||||
return Registry.register(REGISTRY, id, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a codec instance of this registry.
|
||||
*
|
||||
* @return The generated [Codec] of [EvolutionProgress].
|
||||
*/
|
||||
fun codec(): Codec<EvolutionProgress<*>> = REGISTRY.byNameCodec().dispatch(
|
||||
ServerEvolutionController.ID_KEY,
|
||||
{ it.type() },
|
||||
{ it.codec }
|
||||
)
|
||||
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.LevelRequirement
|
||||
import com.cobblemon.mod.common.pokemon.transformation.requirements.LevelRequirement
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
+29
-5
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.FeatureAspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -29,11 +29,11 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
*/
|
||||
open class ChoiceSpeciesFeatureProvider(
|
||||
override var keys: List<String>,
|
||||
var default: String? = null,
|
||||
override var default: String? = null,
|
||||
var choices: List<String> = listOf(),
|
||||
var isAspect: Boolean = true,
|
||||
override var isAspect: Boolean = true,
|
||||
var aspectFormat: String = "{{choice}}"
|
||||
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
|
||||
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, FeatureAspectProvider {
|
||||
override var needsKey = true
|
||||
override var visible = false
|
||||
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
|
||||
@@ -76,6 +76,28 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
return aspects
|
||||
}
|
||||
|
||||
fun parseValue(aspect: String): String? {
|
||||
if (!matches(aspect)) return null
|
||||
val head = aspectFormat.substringBefore("{{choice}}")
|
||||
val tail = aspectFormat.substringAfter("{{choice}}").takeIf { it.isNotBlank() }
|
||||
|
||||
var value = aspect.substringAfter(head)
|
||||
tail?.let { value = value.substringBefore(it) }
|
||||
return value
|
||||
}
|
||||
|
||||
override fun matches(aspect: String): Boolean = this.isAspect && this.getAllAspects().contains(aspect)
|
||||
|
||||
override fun from(aspect: String): SpeciesFeature<*>? {
|
||||
val value = parseValue(aspect)
|
||||
return if (value != null && choices.contains(value)) this.fromString(value) else null
|
||||
}
|
||||
|
||||
override fun set(pokemon: Pokemon, aspect: String) {
|
||||
val value = parseValue(aspect)
|
||||
if (value != null && choices.contains(value)) this.get(pokemon)?.value = value
|
||||
}
|
||||
|
||||
override fun examples() = choices
|
||||
|
||||
internal constructor(): this(emptyList())
|
||||
@@ -123,7 +145,9 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect) {
|
||||
get(pokemon)?.let { setOf(getAspect(it)) } ?: emptySet()
|
||||
get(pokemon)?.let {
|
||||
setOf(getAspect(it))
|
||||
} ?: emptySet()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
+30
-21
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.FeatureAspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
@@ -30,65 +30,74 @@ import kotlin.random.Random
|
||||
* @author Hiroku
|
||||
* @since May 13th, 2022
|
||||
*/
|
||||
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature<Boolean>, CustomPokemonProperty {
|
||||
constructor(name: String, enabled: Boolean): this(name) {
|
||||
this.enabled = enabled
|
||||
this.value = enabled
|
||||
}
|
||||
|
||||
var enabled = false
|
||||
override var value: Boolean = false
|
||||
override fun saveToNBT(pokemonNBT: CompoundTag): CompoundTag {
|
||||
pokemonNBT.putBoolean(name, enabled)
|
||||
pokemonNBT.putBoolean(name, value)
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature {
|
||||
enabled = if (pokemonNBT.contains(name)) pokemonNBT.getBoolean(name) else enabled
|
||||
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature<Boolean> {
|
||||
value = if (pokemonNBT.contains(name)) pokemonNBT.getBoolean(name) else value
|
||||
return this
|
||||
}
|
||||
|
||||
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
|
||||
pokemonJSON.addProperty(name, enabled)
|
||||
pokemonJSON.addProperty(name, value)
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<Boolean> {
|
||||
val isEnabled = pokemonJSON.get(name)?.asBoolean
|
||||
enabled = isEnabled ?: this.enabled
|
||||
value = isEnabled ?: this.value
|
||||
return this
|
||||
}
|
||||
|
||||
override fun saveToBuffer(buffer: RegistryFriendlyByteBuf, toClient: Boolean) {
|
||||
buffer.writeBoolean(enabled)
|
||||
buffer.writeBoolean(value)
|
||||
}
|
||||
|
||||
override fun loadFromBuffer(buffer: RegistryFriendlyByteBuf) {
|
||||
enabled = buffer.readBoolean()
|
||||
value = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$enabled"
|
||||
override fun asString() = "$name=$value"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
|
||||
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
|
||||
val existingFeature = pokemon.getFeature<FlagSpeciesFeature>(name)
|
||||
if (existingFeature != null) {
|
||||
existingFeature.enabled = enabled
|
||||
existingFeature.value = value
|
||||
} else {
|
||||
pokemon.features.add(FlagSpeciesFeature(name, enabled))
|
||||
pokemon.features.add(FlagSpeciesFeature(name, value))
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.value == value
|
||||
}
|
||||
|
||||
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
|
||||
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, FeatureAspectProvider {
|
||||
override var keys: List<String> = emptyList()
|
||||
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
|
||||
override val needsKey get() = true
|
||||
var default: String? = null
|
||||
var isAspect = true
|
||||
override var default: String? = null
|
||||
override var isAspect = true
|
||||
|
||||
override fun matches(aspect: String): Boolean = this.isAspect && this.keys.first() == aspect
|
||||
|
||||
override fun from(aspect: String): SpeciesFeature<*>? = if (matches(aspect)) fromString(null) else null
|
||||
|
||||
override fun set(pokemon: Pokemon, aspect: String) {
|
||||
this.get(pokemon)?.value = true
|
||||
}
|
||||
|
||||
override var visible: Boolean = false
|
||||
|
||||
override fun invoke(buffer: RegistryFriendlyByteBuf, name: String): FlagSpeciesFeature? {
|
||||
@@ -170,7 +179,7 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
|
||||
}
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.enabled == true) {
|
||||
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.value == true) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
@@ -178,7 +187,7 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
|
||||
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.value == true) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
}
|
||||
|
||||
fun register(name: String, provider: SpeciesFeatureProvider<*>) = register(name, provider, isCoded = true)
|
||||
fun <T : SpeciesFeature> register(name: String, providerLambda: () -> T) {
|
||||
fun <T : SpeciesFeature<*>> register(name: String, providerLambda: () -> T) {
|
||||
register(name, object : SpeciesFeatureProvider<T> {
|
||||
override fun invoke(pokemon: Pokemon) = providerLambda()
|
||||
override fun invoke(nbt: CompoundTag) = providerLambda().apply { loadFromNBT(nbt) }
|
||||
|
||||
+4
-4
@@ -26,8 +26,8 @@ import net.minecraft.world.phys.Vec3
|
||||
* A species feature value that's just an integer. Complex stuff.
|
||||
* @author Hiroku
|
||||
*/
|
||||
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
var value = 0
|
||||
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature<Int>, CustomPokemonProperty {
|
||||
override var value = 0
|
||||
|
||||
constructor() : this("")
|
||||
constructor(name: String, value: Int) : this(name) {
|
||||
@@ -39,7 +39,7 @@ class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature,
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: CompoundTag): SynchronizedSpeciesFeature {
|
||||
override fun loadFromNBT(pokemonNBT: CompoundTag): SynchronizedSpeciesFeature<Int> {
|
||||
value = pokemonNBT.getInt(name)
|
||||
return this
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature,
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<Int> {
|
||||
value = pokemonJSON.get(name).asInt
|
||||
return this
|
||||
}
|
||||
|
||||
+4
-3
@@ -24,10 +24,11 @@ import net.minecraft.nbt.CompoundTag
|
||||
* @author Hiroku
|
||||
* @since May 13th, 2022
|
||||
*/
|
||||
interface SpeciesFeature {
|
||||
interface SpeciesFeature<T> {
|
||||
val name: String
|
||||
var value: T
|
||||
fun saveToNBT(pokemonNBT: CompoundTag): CompoundTag
|
||||
fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature
|
||||
fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature<T>
|
||||
fun saveToJSON(pokemonJSON: JsonObject): JsonObject
|
||||
fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature
|
||||
fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<T>
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ import net.minecraft.nbt.CompoundTag
|
||||
* @author Hiroku
|
||||
* @since November 29th, 2022
|
||||
*/
|
||||
interface SpeciesFeatureProvider<T : SpeciesFeature> {
|
||||
interface SpeciesFeatureProvider<T : SpeciesFeature<*>> {
|
||||
/**
|
||||
* Attempts to create a species feature for the given [Pokemon]. It is appropriate to check for an existing feature
|
||||
* that is compatible with this type and transfer / retain that feature instead of constructing a new one. For example
|
||||
|
||||
+4
-4
@@ -24,14 +24,14 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
*/
|
||||
class StringSpeciesFeature(
|
||||
override val name: String,
|
||||
var value: String
|
||||
) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
override var value: String
|
||||
) : SynchronizedSpeciesFeature<String>, CustomPokemonProperty {
|
||||
override fun saveToNBT(pokemonNBT: CompoundTag): CompoundTag {
|
||||
pokemonNBT.putString(name, value)
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature {
|
||||
override fun loadFromNBT(pokemonNBT: CompoundTag): SpeciesFeature<String> {
|
||||
value = pokemonNBT.getString(name)?.takeIf { it.isNotBlank() }?.lowercase() ?: return this
|
||||
return this
|
||||
}
|
||||
@@ -41,7 +41,7 @@ class StringSpeciesFeature(
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature<String> {
|
||||
value = pokemonJSON.get(name)?.asString?.lowercase() ?: return this
|
||||
return this
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,4 +16,4 @@ import com.cobblemon.mod.common.api.serialization.BufferSerializer
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SynchronizedSpeciesFeature : SpeciesFeature, BufferSerializer
|
||||
interface SynchronizedSpeciesFeature<T> : SpeciesFeature<T>, BufferSerializer
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature> : SpeciesFeatureProvider<T>, BufferSerializer {
|
||||
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature<*>> : SpeciesFeatureProvider<T>, BufferSerializer {
|
||||
var visible: Boolean
|
||||
operator fun invoke(buffer: RegistryFriendlyByteBuf, name: String): T?
|
||||
/** Gets the feature from this Pokémon, if it has been created yet. */
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.pokemon.transformation
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.ContextTrigger
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Represents a transformation of a [Pokemon]. This is the server side counterpart of [TransformationDisplay] and serves as
|
||||
* the driver of the transformation. These are triggered by a player-driven or world-driven event.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since Sept 8th, 2023
|
||||
*/
|
||||
interface Transformation : TransformationLike {
|
||||
|
||||
/** The [TransformationTrigger] to attempt this transformation. */
|
||||
val trigger: TransformationTrigger
|
||||
|
||||
/** The [TransformationRequirement]s that need to be satisfied for this transformation. */
|
||||
val requirements: Set<TransformationRequirement>
|
||||
|
||||
/** If this transformation can be deferred by the user and started whenever. */
|
||||
val optional: Boolean
|
||||
|
||||
/** If this transformation needs its requirements reevaluated to remain valid in a [TransformationController]. */
|
||||
val reevaluate: Boolean
|
||||
|
||||
/** Checks if all [TransformationRequirement]s are met. */
|
||||
fun checkRequirements(pokemon: Pokemon) = this.requirements.all { requirement -> requirement.check(pokemon) }
|
||||
|
||||
/**
|
||||
* Tests if the given [Pokemon] passes all the conditions and is ready to transform.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @param context The optional [ContextTrigger.Context] needed to transform.
|
||||
* @return Whether the [Transformation] can start.
|
||||
*/
|
||||
fun test(pokemon: Pokemon, context: ContextTrigger.Context?) = this.trigger.testTrigger(context) && this.checkRequirements(pokemon)
|
||||
|
||||
/**
|
||||
* Starts this transformation if conditions are met.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being transformed.
|
||||
* @param context The optional [ContextTrigger.Context] needed to transform.
|
||||
* @return Whether the [Transformation] was successful.
|
||||
*/
|
||||
fun start(pokemon: Pokemon, context: ContextTrigger.Context?): Boolean {
|
||||
if (test(pokemon, context)) {
|
||||
forceStart(pokemon)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts this transformation without evaluating the requirements.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being transformed.
|
||||
*/
|
||||
fun forceStart(pokemon: Pokemon) { this.requirements.forEach { it.fulfill(pokemon) } }
|
||||
|
||||
/**
|
||||
* Converts this transformation to its client side representation.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being transformed.
|
||||
* @return The [TransformationDisplay] representing this transformation on the client.
|
||||
*/
|
||||
fun convertToDisplay(pokemon: Pokemon): TransformationDisplay
|
||||
|
||||
/**
|
||||
* Validates whether a transformation still meets the [TransformationRequirement]s.
|
||||
* If [Transformation.reevaluate] is false, this check will be bypassed.
|
||||
*
|
||||
* @param pokemon the [Pokemon] to test against.
|
||||
* @return Whether this transformation is still valid and can be made for [pokemon].
|
||||
*/
|
||||
fun reevaluate(pokemon: Pokemon) = !this.reevaluate || this.checkRequirements(pokemon)
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.pokemon.transformation
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
|
||||
/**
|
||||
* Represents a transformation of a [Pokemon], this is the client side counterpart of [Transformation].
|
||||
* This has no attachments to any data regarding the transformation itself and only serves for display purposes and basic communication.
|
||||
*
|
||||
* @author Licious
|
||||
* @since April 28th, 2022
|
||||
*/
|
||||
interface TransformationDisplay : TransformationLike {
|
||||
|
||||
/**
|
||||
* The [Species] of the transformation result.
|
||||
*/
|
||||
val species: Species
|
||||
|
||||
/**
|
||||
* The aspects of the transformation result.
|
||||
* These are used by the client to resolve the entity it needs to display.
|
||||
*/
|
||||
val aspects: Set<String>
|
||||
}
|
||||
+6
-6
@@ -6,20 +6,20 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation
|
||||
|
||||
/**
|
||||
* Represents the base of an evolution.
|
||||
* It can either be the full data container present on the server side [Evolution].
|
||||
* Or the client side representation for display [EvolutionDisplay].
|
||||
* Represents the base of a transformation.
|
||||
* It can either be the full data container present on the server side [Transformation].
|
||||
* Or the client side representation for display [TransformationDisplay].
|
||||
*
|
||||
* @author Licious
|
||||
* @since April 28th, 2022
|
||||
*/
|
||||
interface EvolutionLike {
|
||||
interface TransformationLike {
|
||||
|
||||
/**
|
||||
* The unique id of the evolution.
|
||||
* The unique id of the transformation.
|
||||
* It should be human-readable, I.E pikachu_level
|
||||
*/
|
||||
val id: String
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
|
||||
import com.cobblemon.mod.common.pokemon.transformation.adapters.CobblemonRequirementAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Saves and loads [TransformationRequirement]s with JSON.
|
||||
* For the default implementation see [CobblemonRequirementAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 21st, 2022
|
||||
*/
|
||||
interface RequirementAdapter : JsonDeserializer<TransformationRequirement>, JsonSerializer<TransformationRequirement> {
|
||||
|
||||
/**
|
||||
* Registers the given type of [TransformationRequirement] to it's associated ID for deserialization.
|
||||
*
|
||||
* @param T The type of [TransformationRequirement].
|
||||
* @param id The id of the evolution event.
|
||||
* @param type The [KClass] of the type.
|
||||
*/
|
||||
fun <T : TransformationRequirement> registerType(id: String, type: KClass<T>)
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
|
||||
import com.cobblemon.mod.common.pokemon.transformation.adapters.CobblemonTriggerAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Saves and loads [TransformationTrigger]s with JSON.
|
||||
* For the default implementation see [CobblemonTriggerAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 20th, 2022
|
||||
*/
|
||||
interface TriggerAdapter : JsonDeserializer<TransformationTrigger>, JsonSerializer<TransformationTrigger> {
|
||||
|
||||
/**
|
||||
* Registers the given type of [TransformationTrigger] to it's associated ID for deserialization.
|
||||
*
|
||||
* @param T The type of [TransformationTrigger].
|
||||
* @param id The id of the evolution event.
|
||||
* @param type The [KClass] of the type.
|
||||
*/
|
||||
fun <T : TransformationTrigger> registerType(id: String, type: KClass<T>)
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.controller
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationLike
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.proxy.PreProcessor
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.progress.TransformationProgress
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.controller.CobblemonClientTransformationController
|
||||
import com.cobblemon.mod.common.pokemon.transformation.controller.CobblemonServerTransformationController
|
||||
|
||||
/**
|
||||
* Responsible for holding all available [TransformationLike]s in the [Pokemon].
|
||||
* Also handles all the networking behind them.
|
||||
* For the Cobblemon default implementations see [CobblemonClientTransformationController] & [CobblemonServerTransformationController].
|
||||
*
|
||||
* @author Licious
|
||||
* @since April 28th, 2022
|
||||
*/
|
||||
interface TransformationController<T : TransformationLike, out I : PreProcessor> : MutableSet<T> {
|
||||
|
||||
/**
|
||||
* Resolves the [Pokemon] attached to this controller.
|
||||
*
|
||||
* @return The [Pokemon] this controller is attached to.
|
||||
*/
|
||||
fun pokemon(): Pokemon
|
||||
|
||||
/**
|
||||
* Starts the given transformation on this controller.
|
||||
* All the necessary networking will be handled.
|
||||
*
|
||||
* @param transformation The [TransformationLike] starting.
|
||||
*/
|
||||
fun start(transformation: T)
|
||||
|
||||
/** Reverts the active [TransformationLike] for this controller's [pokemon]. */
|
||||
fun revert() // TODO dunno if i like this
|
||||
|
||||
/**
|
||||
* Returns an immutable collection of [TransformationProgress]es attached to this controller.
|
||||
*
|
||||
* @return The currently tracked [TransformationProgress]es.
|
||||
*/
|
||||
fun progress(): Collection<TransformationProgress<*>>
|
||||
|
||||
/**
|
||||
* Attaches a [TransformationProgress] to this controller.
|
||||
*
|
||||
* @param P The type of [TransformationProgress].
|
||||
* @param progress The [TransformationProgress] being attached for tracking.
|
||||
* @return The instance being tracked.
|
||||
*/
|
||||
fun <P : TransformationProgress<*>> trackProgress(progress: P): P
|
||||
|
||||
/**
|
||||
* Attempts to find an [TransformationProgress] in [progress] or creates and tracks using [trackProgress].
|
||||
*
|
||||
* @param P The type of [TransformationProgress].
|
||||
* @param predicate The lambda attempting to find specific progress. This needs to ensure the [TransformationProgress] is of type [P] or the cast will fail.
|
||||
* @param progressFactory The lambda creating a new instance of type [P] if [predicate] can't find an element.
|
||||
* @return The found match in [progress] or the created and now tracked with [trackProgress].
|
||||
*/
|
||||
fun <P : TransformationProgress<*>> progressFirstOrCreate(predicate: (progress: TransformationProgress<*>) -> Boolean, progressFactory: () -> P): P
|
||||
|
||||
fun asIntermediate(): I
|
||||
|
||||
/**
|
||||
* Guard for [start] to prevent a transformation from executing while the [pokemon] is busy or if the transformation
|
||||
* is not in the controller.
|
||||
*
|
||||
* @param transformation the [TransformationLike] to validate.
|
||||
* @return whether this controller is capable of starting the transformation.
|
||||
*/
|
||||
fun canStart(transformation: T): Boolean
|
||||
|
||||
/**
|
||||
* Whether this controller is capable of executing [start] for a transformation. This checks if the [pokemon] is busy
|
||||
* or if there is a transformation ready in the controller.
|
||||
*
|
||||
* @return whether this controller is capable of starting a transformation.
|
||||
*/
|
||||
fun canStart(): Boolean
|
||||
|
||||
/**
|
||||
* Validates the [TransformationLike] currently active for this controller's [pokemon] and determines whether it can be
|
||||
* reverted and if the pokemon is capable of reverting right now.
|
||||
*
|
||||
* @return whether the transformation can be reverted.
|
||||
*/
|
||||
fun canRevert(): Boolean
|
||||
|
||||
/**
|
||||
* Retrieves the [TransformationLike] in this controller that matches the provided id.
|
||||
*
|
||||
* @param id the identifier of the transformation.
|
||||
* @return the transformation matching this id.
|
||||
*/
|
||||
fun getById(id: String) = this.firstOrNull { transformation -> transformation.id.equals(id, true) }
|
||||
}
|
||||
+2
-2
@@ -6,11 +6,11 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation.evolution
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.evolution.StandardPreEvolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.StandardPreEvolution
|
||||
|
||||
/**
|
||||
* Represents the previous stage in the evolutionary line of a given Pokémon.
|
||||
+24
-13
@@ -6,25 +6,36 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution.progress
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation.progress
|
||||
|
||||
import com.cobblemon.mod.common.api.data.Identifiable
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionController
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.requirement.TransformationRequirement
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.TransformationTrigger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Represents a container of progression towards the completion of an evolution requirement.
|
||||
* To register an evolution progress use [EvolutionProgressTypes.registerType].
|
||||
* Represents a container of progression towards the completion of a [TransformationRequirement].
|
||||
* To register a transformation progress use [TransformationProgressTypes.registerType].
|
||||
*
|
||||
* @param T The type of the progression.
|
||||
*
|
||||
* @author Licious
|
||||
* @since January 27th, 2023
|
||||
*/
|
||||
interface EvolutionProgress<T> : Identifiable {
|
||||
interface TransformationProgress<T : TransformationProgress.Progress> : Identifiable {
|
||||
|
||||
/**
|
||||
* Represents progression towards a [TransformationRequirement].
|
||||
*
|
||||
* @property amount The value required to be complete.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since April 18th, 2025
|
||||
*/
|
||||
interface Progress {
|
||||
val amount: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current progress of type [T].
|
||||
@@ -47,7 +58,7 @@ interface EvolutionProgress<T> : Identifiable {
|
||||
fun reset()
|
||||
|
||||
/**
|
||||
* Used to decide if the given progress should be kept in the [pokemon]s [EvolutionController].
|
||||
* Used to decide if the given progress should be kept in the [pokemon]s [TransformationController].
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @return True if the progress is still required otherwise false.
|
||||
@@ -55,16 +66,16 @@ interface EvolutionProgress<T> : Identifiable {
|
||||
fun shouldKeep(pokemon: Pokemon): Boolean
|
||||
|
||||
/**
|
||||
* The [EvolutionProgressType] of this.
|
||||
* The [TransformationProgressType] of this.
|
||||
*
|
||||
* @return The [EvolutionProgressType] of this.
|
||||
* @return The [TransformationProgressType] of this.
|
||||
*/
|
||||
fun type(): EvolutionProgressType<*>
|
||||
fun type(): TransformationProgressType<*>
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun codec() = EvolutionProgressTypes.codec()
|
||||
fun codec() = TransformationProgressTypes.codec()
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -6,10 +6,10 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution.progress
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation.progress
|
||||
|
||||
import com.mojang.serialization.MapCodec
|
||||
|
||||
data class EvolutionProgressType<T : EvolutionProgress<*>>(
|
||||
data class TransformationProgressType<T : TransformationProgress<*>>(
|
||||
val codec: MapCodec<T>
|
||||
)
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.progress
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.Lifecycle
|
||||
import net.minecraft.core.MappedRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
/**
|
||||
* Registry of [TransformationProgressType]s.
|
||||
*
|
||||
* @author Licious
|
||||
* @since July 6th, 2024
|
||||
*/
|
||||
object TransformationProgressTypes {
|
||||
|
||||
private const val ID_KEY = "id"
|
||||
|
||||
private val REGISTRY: Registry<TransformationProgressType<*>> = MappedRegistry(
|
||||
ResourceKey.createRegistryKey(cobblemonResource("transformation_progress")), Lifecycle.stable()
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
val DAMAGE_TAKEN = this.registerType(DamageTakenTransformationProgress.ID, TransformationProgressType(DamageTakenTransformationProgress.CODEC))
|
||||
@JvmStatic
|
||||
val DEFEAT = this.registerType(DefeatTransformationProgress.ID, TransformationProgressType(DefeatTransformationProgress.CODEC))
|
||||
@JvmStatic
|
||||
val LAST_BATTLE_CRITICAL_HITS = this.registerType(LastBattleCriticalHitsTransformationProgress.ID, TransformationProgressType(LastBattleCriticalHitsTransformationProgress.CODEC))
|
||||
@JvmStatic
|
||||
val RECOIL = this.registerType(RecoilTransformationProgress.ID, TransformationProgressType(RecoilTransformationProgress.CODEC))
|
||||
@JvmStatic
|
||||
val USE_MOVE = this.registerType(UseMoveTransformationProgress.ID, TransformationProgressType(UseMoveTransformationProgress.CODEC))
|
||||
|
||||
/**
|
||||
* Registers a transformation progress.
|
||||
*
|
||||
* @param T The type of the [TransformationProgress].
|
||||
* @param id The [ResourceLocation].
|
||||
* @param type The [TransformationProgressType] record.
|
||||
* @return The registered [TransformationProgressType] of type [T].
|
||||
*
|
||||
* @throws [IllegalStateException] if [Registry.register] fails.
|
||||
*/
|
||||
@Throws(IllegalStateException::class)
|
||||
@JvmStatic
|
||||
fun <T : TransformationProgress<*>> registerType(id: ResourceLocation, type: TransformationProgressType<T>): TransformationProgressType<T> {
|
||||
return Registry.register(REGISTRY, id, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a codec instance of this registry.
|
||||
*
|
||||
* @return The generated [Codec] of [TransformationProgress].
|
||||
*/
|
||||
fun codec(): Codec<TransformationProgress<*>> = REGISTRY.byNameCodec().dispatch(
|
||||
ID_KEY,
|
||||
{ it.type() },
|
||||
{ it.codec }
|
||||
)
|
||||
}
|
||||
+3
-2
@@ -6,13 +6,14 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation.proxy
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
fun interface PreProcessor {
|
||||
fun create(pokemon: Pokemon): EvolutionController<*, *>
|
||||
fun create(pokemon: Pokemon): TransformationController<*, *>
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.proxy
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationDisplay
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationLike
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.controller.TransformationController
|
||||
|
||||
/**
|
||||
* Responsible for holding the different [TransformationController] implementations based on the logical side.
|
||||
* If you haven't already checked your side previously you can use [isClient].
|
||||
*
|
||||
* @param C The type of [TransformationDisplay] on the client side.
|
||||
* @param S The type of [Transformation] on the server side.
|
||||
* @param CI The type of [PreProcessor] on the client side.
|
||||
* @param SI The type of [PreProcessor] on the server side.
|
||||
*
|
||||
* @author Licious, Segfault Guy
|
||||
* @since January 19th, 2025
|
||||
*/
|
||||
interface TransformationProxy<C : TransformationDisplay, S : Transformation, CI : PreProcessor, SI : PreProcessor> {
|
||||
|
||||
/**
|
||||
* Checks if the current logical side is the client.
|
||||
*
|
||||
* @return True if the current logical side is the client.
|
||||
*/
|
||||
fun isClient(): Boolean
|
||||
|
||||
/**
|
||||
* Returns the current [TransformationController].
|
||||
* Keep in mind this will have type erasure and as such most operations will not be possible.
|
||||
* It's recommended to work with [client] or [server] when possible.
|
||||
*
|
||||
* @return The current [TransformationController].
|
||||
*/
|
||||
fun current(): TransformationController<out TransformationLike, *>
|
||||
|
||||
/**
|
||||
* The client side implementation of the [TransformationController].
|
||||
*
|
||||
* @throws [ClassCastException] if called from the server side.
|
||||
*
|
||||
* @return The client side implementation of the [TransformationController] if possible.
|
||||
*/
|
||||
fun client(): TransformationController<C, CI>
|
||||
|
||||
/**
|
||||
* The server side implementation of the [TransformationController].
|
||||
*
|
||||
* @throws [ClassCastException] if called from the client side.
|
||||
*
|
||||
* @return The server side implementation of the [TransformationController] if possible.
|
||||
*/
|
||||
fun server(): TransformationController<S, SI>
|
||||
|
||||
}
|
||||
+5
-6
@@ -6,23 +6,22 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.evolution.requirements.template
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation.requirement
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
/**
|
||||
* An [EvolutionRequirement] that expects a [LivingEntity] to be attached to the [Pokemon].
|
||||
* A [TransformationRequirement] that expects a [LivingEntity] to be attached to the [Pokemon].
|
||||
* It can be the [PokemonEntity] if present or the [LivingEntity] that owns it.
|
||||
*
|
||||
* Whenever an entity can't be resolved [EvolutionRequirement.check] will never succeed.
|
||||
* Whenever an entity can't be resolved [TransformationRequirement.check] will never succeed.
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 21st, 2022
|
||||
*/
|
||||
interface EntityQueryRequirement : EvolutionRequirement {
|
||||
interface EntityQueryRequirement : TransformationRequirement {
|
||||
override fun check(pokemon: Pokemon): Boolean {
|
||||
val queriedEntity = pokemon.entity ?: pokemon.getOwnerPlayer() ?: return false
|
||||
return this.check(pokemon, queriedEntity)
|
||||
@@ -32,7 +31,7 @@ interface EntityQueryRequirement : EvolutionRequirement {
|
||||
* Checks if the given [Pokemon] & [LivingEntity] satisfies the requirement.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @param queriedEntity The [LivingEntity] that can be compared in context of a [Level].
|
||||
* @param queriedEntity The [LivingEntity] that can be compared in context of a [net.minecraft.world.level.Level].
|
||||
* @return If the requirement was satisfied.
|
||||
*/
|
||||
fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.requirement
|
||||
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* A [TransformationRequirement] that expects the [Pokemon] to have an owner.
|
||||
* It can be the [ServerPlayer] or the [NPCEntity] that owns it.
|
||||
*
|
||||
* Whenever an owner can't be resolved [TransformationRequirement.check] will never succeed.
|
||||
*
|
||||
* @author Polymeta
|
||||
* @since May 6th, 2025
|
||||
*/
|
||||
interface OwnerQueryRequirement : TransformationRequirement {
|
||||
override fun check(pokemon: Pokemon): Boolean {
|
||||
val owner = pokemon.getOwnerEntity() ?: return false
|
||||
return when (owner) {
|
||||
is ServerPlayer -> {
|
||||
this.checkPlayer(pokemon, owner)
|
||||
}
|
||||
is NPCEntity -> {
|
||||
this.checkNPC(pokemon, owner)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] & [ServerPlayer] satisfies the requirement.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @param owner The [ServerPlayer] that owns this [Pokemon].
|
||||
* @return If the requirement was satisfied.
|
||||
*/
|
||||
fun checkPlayer(pokemon: Pokemon, owner: ServerPlayer): Boolean
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] & [NPCEntity] satisfies the requirement.
|
||||
* This method mainly exists as NPCs are different to players in a lot of ways (e.g., lack of advancements),
|
||||
* so they need to be handled differently, either by checking an NPC config or always returning true/false
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @param owner The [NPCEntity] that owns this [Pokemon].
|
||||
* @return If the requirement was satisfied.
|
||||
*/
|
||||
fun checkNPC(pokemon: Pokemon, owner: NPCEntity): Boolean
|
||||
}
|
||||
+12
-7
@@ -6,23 +6,22 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution.requirement
|
||||
package com.cobblemon.mod.common.api.pokemon.transformation.requirement
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.ContextEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.ContextTrigger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Represents a requirement for an evolution.
|
||||
* Represents a requirement for a transformation.
|
||||
* Requirements are constant and will never change regardless of the backing action.
|
||||
* For dynamic requirements see [ContextEvolution].
|
||||
*
|
||||
* See [Evolution.requirements] & [Evolution.test] for usage.
|
||||
* See [Transformation.requirements] & [Transformation.test] for usage.
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 19th, 2022
|
||||
*/
|
||||
fun interface EvolutionRequirement {
|
||||
fun interface TransformationRequirement {
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] satisfies the requirement.
|
||||
@@ -32,4 +31,10 @@ fun interface EvolutionRequirement {
|
||||
*/
|
||||
fun check(pokemon: Pokemon): Boolean
|
||||
|
||||
/**
|
||||
* Callback after the [Transformation] has been performed.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
*/
|
||||
fun fulfill(pokemon: Pokemon) {}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.trigger
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.ItemInteractionTrigger
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.TradeTrigger
|
||||
|
||||
/**
|
||||
* Represents a [TransformationTrigger] of a [Pokemon] that can only occur during specific actions and with added context.
|
||||
* For the default implementations see [ItemInteractionTrigger] & [TradeTrigger].
|
||||
*
|
||||
* @param TC The context that is serialized from JSON during species loading. This is what the [Context] provided
|
||||
* to [testTrigger] is expected to match against.
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 19th, 2022
|
||||
*/
|
||||
interface ContextTrigger<TC>: TransformationTrigger {
|
||||
|
||||
/**
|
||||
* Represents context provided for [TransformationTrigger] evaluation.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 15th, 2023
|
||||
*/
|
||||
interface Context
|
||||
|
||||
/** The target context required for this [Transformation] to be successfully triggered. */
|
||||
val requiredContext: TC
|
||||
|
||||
/**
|
||||
* Checks if the given context is valid for the [requiredContext].
|
||||
*
|
||||
* @param context The [Context] given at runtime when querying the [Transformation].
|
||||
* @return If the context matched the [requiredContext].
|
||||
*/
|
||||
fun testContext(context: Context): Boolean = false
|
||||
|
||||
/**
|
||||
* Evaluates the triggering condition of a [Transformation].
|
||||
*
|
||||
* @param context The [Context] of the [Transformation].
|
||||
* @return Whether the triggering condition is satisfied. If [context] is null, bypasses the condition check.
|
||||
*/
|
||||
override fun testTrigger(context: Context?) = context?.let { testContext(it) } ?: true
|
||||
|
||||
}
|
||||
+23
@@ -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.pokemon.transformation.trigger
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.LevelUpTrigger
|
||||
|
||||
/**
|
||||
* Represents a [TransformationTrigger] of a [Pokemon] that is attempted periodically without any additional context or actions.
|
||||
* For the default implementation see [LevelUpTrigger].
|
||||
*
|
||||
* Triggers that implement this marker will be tested every second.
|
||||
*
|
||||
* @author Licious
|
||||
* @since March 19th, 2022
|
||||
*/
|
||||
interface PassiveTrigger : TransformationTrigger
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.pokemon.transformation.trigger
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
|
||||
|
||||
/**
|
||||
* Represents a condition that starts a [Transformation].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since Sept 8th, 2023
|
||||
*/
|
||||
interface TransformationTrigger {
|
||||
|
||||
/**
|
||||
* Evaluates the triggering condition of a [Transformation].
|
||||
*
|
||||
* @param context The optional [ContextTrigger.Context] for [ContextTrigger] evaluation.
|
||||
* @return Whether the triggering condition is satisfied.
|
||||
*/
|
||||
fun testTrigger(context: ContextTrigger.Context?): Boolean = true
|
||||
|
||||
}
|
||||
+12
-12
@@ -12,16 +12,14 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.PokemonGainedEvent
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.trigger.PassiveTrigger
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.network.chat.Component
|
||||
import java.util.*
|
||||
@@ -143,14 +141,16 @@ open class PlayerPartyStore(
|
||||
}
|
||||
}
|
||||
|
||||
// Passive evolutions
|
||||
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
|
||||
val removeList = mutableListOf<Evolution>()
|
||||
pokemon.evolutionProxy.server().forEach {
|
||||
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
|
||||
removeList.add(it)
|
||||
}
|
||||
removeList.forEach { pokemon.evolutionProxy.server().remove(it) }
|
||||
// Passive evolutions and form changes
|
||||
pokemon.triggerTransformations(PassiveTrigger::class.java)
|
||||
|
||||
// Reevaluate active form
|
||||
val form = pokemon.form
|
||||
if (form is TemporaryForm && !form.reevaluate(pokemon)) pokemon.formProxy.server().revert() // TODO check locks?
|
||||
|
||||
// Reevaluate transformation requirements and remove invalid transformations from the controller
|
||||
pokemon.formProxy.server().reevaluate()
|
||||
pokemon.evolutionProxy.server().reevaluate()
|
||||
}
|
||||
// Friendship
|
||||
// ToDo expand this down the line just a very basic implementation for the first releases
|
||||
|
||||
@@ -127,7 +127,8 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
|
||||
// No PP or disabled or something
|
||||
return false
|
||||
}
|
||||
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
val availableTargets = (if (gimmickID != null && validGimmickMove) gimmickMove.target else move.target)
|
||||
.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
|
||||
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
|
||||
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.*
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
@@ -22,9 +21,6 @@ import com.cobblemon.mod.common.battles.interpreter.instructions.*
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.runOnServer
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import kotlin.collections.Iterator
|
||||
import kotlin.collections.filter
|
||||
@@ -36,7 +32,6 @@ import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.toMutableList
|
||||
import kotlin.collections.toTypedArray
|
||||
import kotlin.time.measureTime
|
||||
|
||||
@Suppress("KotlinPlaceholderCountMatchesArgumentCount", "UNUSED_PARAMETER")
|
||||
object ShowdownInterpreter {
|
||||
@@ -72,7 +67,7 @@ object ShowdownInterpreter {
|
||||
updateInstructionParser["-copyboost"] = { _, _, message, _ -> CopyBoostInstruction(message) }
|
||||
updateInstructionParser["-crit"] = { _, instructionSet, message, _ -> CritInstruction(message, instructionSet) }
|
||||
updateInstructionParser["-curestatus"] = { _, _, message, _ -> CureStatusInstruction(message) }
|
||||
updateInstructionParser["detailschange"] = { _, _, message, _ -> FormeChangeInstruction(message) }
|
||||
updateInstructionParser["detailschange"] = { _, _, message, _ -> FormChangeInstruction(message) }
|
||||
updateInstructionParser["-endability"] = { _, _, message, _ -> EndAbilityInstruction(message) }
|
||||
updateInstructionParser["-end"] = { _, _, message, _ -> EndInstruction(message) }
|
||||
updateInstructionParser["-enditem"] = { _, _, message, _ -> EndItemInstruction(message) }
|
||||
@@ -81,7 +76,7 @@ object ShowdownInterpreter {
|
||||
updateInstructionParser["-fieldactivate"] = { _, _, message, _ -> FieldActivateInstruction(message) }
|
||||
updateInstructionParser["-fieldend"] = { _, _, message, _ -> FieldEndInstruction(message) }
|
||||
updateInstructionParser["-fieldstart"] = { _, _, message, _ -> FieldStartInstruction(message) }
|
||||
updateInstructionParser["-formechange"] = { _, _, message, _ -> FormeChangeInstruction(message) }
|
||||
updateInstructionParser["-formechange"] = { _, _, message, _ -> FormChangeInstruction(message) }
|
||||
updateInstructionParser["-hitcount"] = { _, _, message, _ -> HitCountInstruction(message) }
|
||||
updateInstructionParser["-immune"] = { _, _, message, _ -> ImmuneInstruction(message) }
|
||||
updateInstructionParser["-invertboost"] = { _, _, message, _ -> InvertBoostInstruction(message) }
|
||||
|
||||
+6
-5
@@ -12,10 +12,10 @@ import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.LastBattleCriticalHitsTransformationProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-crit|POKEMON
|
||||
@@ -39,9 +39,10 @@ class CritInstruction(val message: BattleMessage, val instructionSet: Instructio
|
||||
} else {
|
||||
battle.broadcastChatMessage(battleLang("crit").yellow())
|
||||
}
|
||||
if (LastBattleCriticalHitsEvolutionProgress.supports(battlePokemon.effectedPokemon)) {
|
||||
val progress = battlePokemon.effectedPokemon.evolutionProxy.current().progressFirstOrCreate({ it is LastBattleCriticalHitsEvolutionProgress }) { LastBattleCriticalHitsEvolutionProgress() }
|
||||
progress.updateProgress(LastBattleCriticalHitsEvolutionProgress.Progress(progress.currentProgress().amount + 1))
|
||||
if (LastBattleCriticalHitsTransformationProgress.supports(battlePokemon.effectedPokemon)) {
|
||||
// TODO refactor and apply to forms
|
||||
val progress = battlePokemon.effectedPokemon.evolutionProxy.current().progressFirstOrCreate({ it is LastBattleCriticalHitsTransformationProgress }) { LastBattleCriticalHitsTransformationProgress() }
|
||||
progress.updateProgress(LastBattleCriticalHitsTransformationProgress.Progress(progress.currentProgress().amount + 1))
|
||||
}
|
||||
}
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
|
||||
+10
-9
@@ -25,10 +25,9 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.DamageTakenTransformationProgress
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.RecoilTransformationProgress
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -75,7 +74,7 @@ class DamageInstruction(
|
||||
|
||||
private fun doRecoilEvoChecks(battlePokemon: BattlePokemon) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
if (RecoilEvolutionProgress.supports(pokemon)) {
|
||||
if (RecoilTransformationProgress.supports(pokemon)) {
|
||||
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException(
|
||||
"Cant get recoil string"
|
||||
)
|
||||
@@ -84,9 +83,10 @@ class DamageInstruction(
|
||||
?: throw UnsupportedOperationException("Cant get recoil string")
|
||||
val difference = pokemon.currentHealth - newHealth
|
||||
if (difference > 0) {
|
||||
// TODO refactor and apply to forms
|
||||
val progress = pokemon.evolutionProxy.current()
|
||||
.progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
|
||||
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
|
||||
.progressFirstOrCreate({ it is RecoilTransformationProgress }) { RecoilTransformationProgress() }
|
||||
progress.updateProgress(RecoilTransformationProgress.Progress(progress.currentProgress().amount + difference))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,9 +180,10 @@ class DamageInstruction(
|
||||
battlePokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
if (difference > 0) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
if (DamageTakenEvolutionProgress.supports(pokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { DamageTakenEvolutionProgress() }
|
||||
progress.updateProgress(DamageTakenEvolutionProgress.Progress(progress.currentProgress().amount + difference))
|
||||
if (DamageTakenTransformationProgress.supports(pokemon)) {
|
||||
// TODO refactor and apply to forms
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenTransformationProgress }) { DamageTakenTransformationProgress() }
|
||||
progress.updateProgress(DamageTakenTransformationProgress.Progress(progress.currentProgress().amount + difference))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleTransformPokemonPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |-formechange|POKEMON|SPECIES|HP STATUS or |detailschange|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* POKEMON has temporarily (-formechange) or permanently (detailschange) changed form to SPECIES/DETAILS.
|
||||
* @author Segfault Guy
|
||||
* @since December 9th, 2024
|
||||
*/
|
||||
class FormChangeInstruction(val message: BattleMessage): ActionEffectInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("formchange")
|
||||
|
||||
lateinit var battlePokemon: BattlePokemon
|
||||
lateinit var speciesName: String
|
||||
lateinit var formId: String
|
||||
lateinit var typeKey: String
|
||||
lateinit var pnx: String
|
||||
lateinit var form: FormData
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
|
||||
battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
speciesName = details.substringBefore('-')
|
||||
formId = details.substringAfter('-').ifBlank { speciesName }
|
||||
typeKey = if (message.id == "detailschange") "permanent" else "temporary"
|
||||
pnx = message.pnxAndUuid(0)?.first ?: return
|
||||
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), battlePokemon)
|
||||
battle.dispatchGo {
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
form = battlePokemon.effectedPokemon.species.getFormByShowdownId(formId)
|
||||
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
battle.dispatch {
|
||||
if (form is TemporaryForm) {
|
||||
val actionEffect = (form as TemporaryForm).actionEffect
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers,
|
||||
level = battle.players.firstOrNull()?.level()
|
||||
)
|
||||
(form as TemporaryForm).forceStart(battlePokemon.effectedPokemon.entity!!, context)
|
||||
}
|
||||
else {
|
||||
battlePokemon.effectedPokemon.formProxy.server().revert()
|
||||
}
|
||||
return@dispatch GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formId))
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val lang = when (formId) {
|
||||
"busted", "hero", "complete" -> return@dispatchWaiting
|
||||
"school", "wishiwashi", "meteor", "minior" -> battleLang("formechange.$formId", pokemonName)
|
||||
speciesName -> battleLang("formechange.default.temporary.end", pokemonName)
|
||||
else -> battleLang("formechange.default.$typeKey", pokemonName, formId)
|
||||
}
|
||||
battle.sendSidedUpdate(
|
||||
source = battlePokemon.actor,
|
||||
allyPacket = BattleTransformPokemonPacket(pnx, battlePokemon, true),
|
||||
opponentPacket = BattleTransformPokemonPacket(pnx, battlePokemon, false)
|
||||
)
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-formechange|POKEMON|SPECIES|HP STATUS or |detailschange|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* POKEMON has temporarily (-formechange) or permanently (detailschange) changed form to SPECIES/DETAILS.
|
||||
* @author Segfault Guy
|
||||
* @since December 9th, 2024
|
||||
*/
|
||||
class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
|
||||
val speciesName = details.substringBefore('-')
|
||||
val formName = details.substringAfterLast('-').ifBlank { speciesName }
|
||||
val typeKey = if (message.id == "detailschange") "permanent" else "temporary"
|
||||
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), battlePokemon)
|
||||
|
||||
battle.dispatchWaiting {
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
|
||||
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val lang = when(formName) {
|
||||
"busted", "hero", "complete" -> return@dispatchWaiting
|
||||
"school", "wishiwashi", "meteor", "minior" -> battleLang("formechange.$formName", pokemonName)
|
||||
speciesName -> battleLang("formechange.default.temporary.end", pokemonName)
|
||||
else -> battleLang("formechange.default.$typeKey", pokemonName, formName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -26,7 +26,7 @@ import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.transformation.progress.UseMoveTransformationProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@@ -70,9 +70,10 @@ class MoveInstruction(
|
||||
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull() { poke -> poke.getPNX() == it }?.battlePokemon }
|
||||
|
||||
userPokemon.effectedPokemon.let { pokemon ->
|
||||
if (UseMoveEvolutionProgress.supports(pokemon, move)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
|
||||
progress.updateProgress(UseMoveEvolutionProgress.Progress(move, progress.currentProgress().amount + 1))
|
||||
// TODO refactor and apply to forms
|
||||
if (UseMoveTransformationProgress.supports(pokemon, move)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveTransformationProgress && it.currentProgress().move == move }) { UseMoveTransformationProgress() }
|
||||
progress.updateProgress(UseMoveTransformationProgress.Progress(move, progress.currentProgress().amount + 1))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import java.util.function.Function
|
||||
@@ -42,10 +42,12 @@ open class BattlePokemon(
|
||||
|
||||
companion object {
|
||||
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
|
||||
//TOOD figure out a closer registry access (might have to break some method signatures for this (1.7?)
|
||||
val effectedPokemon = pokemon.clone(registryAccess = server()?.registryAccess() ?: throw IllegalStateException("No registry access available"))
|
||||
val effectedPokemon = pokemon.clone()
|
||||
BattleCloneProperty.isBattleClone().apply(effectedPokemon)
|
||||
UncatchableProperty.uncatchable().apply(effectedPokemon)
|
||||
val shouldRevertForm = (effectedPokemon.form as? TemporaryForm)?.revertBeforeBattle ?: (effectedPokemon.form as? TemporaryForm)?.battleOnly ?: false
|
||||
if (shouldRevertForm)
|
||||
effectedPokemon.formProxy.server().revert()
|
||||
return BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = effectedPokemon,
|
||||
@@ -53,13 +55,18 @@ open class BattlePokemon(
|
||||
)
|
||||
}
|
||||
|
||||
fun playerOwned(pokemon: Pokemon): BattlePokemon = BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = pokemon,
|
||||
postBattleEntityOperation = { entity ->
|
||||
entity.effects.wipe()
|
||||
}
|
||||
)
|
||||
fun playerOwned(pokemon: Pokemon): BattlePokemon {
|
||||
val shouldRevertForm = (pokemon.form as? TemporaryForm)?.revertBeforeBattle ?: (pokemon.form as? TemporaryForm)?.battleOnly ?: false
|
||||
if (shouldRevertForm)
|
||||
pokemon.formProxy.server().revert()
|
||||
return BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = pokemon,
|
||||
postBattleEntityOperation = { entity ->
|
||||
entity.effects.wipe()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val struct = QueryStruct(
|
||||
|
||||
@@ -10,6 +10,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.cobblemon.mod.common.battles.runner.socket.SocketShowdownService
|
||||
import com.google.gson.JsonArray
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
+8
-4
@@ -157,12 +157,16 @@ class GraalShowdownService : ShowdownService {
|
||||
val jsArray = this.context.eval("js", "new Array();")
|
||||
var index = 0L
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, null)))
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, form)))
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownStandardForm(species.standardForm)))
|
||||
species.permanentForms.forEach { permForm ->
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownPermanentForm(permForm)))
|
||||
permForm.changeableForms.forEach { tempForm ->
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownTemporaryForm(tempForm)))
|
||||
}
|
||||
}
|
||||
species.temporaryForms.forEach { tempForm ->
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownTemporaryForm(tempForm)))
|
||||
}
|
||||
}
|
||||
receiveSpeciesDataFn.execute(jsArray)
|
||||
}
|
||||
|
||||
+10
-6
@@ -123,8 +123,8 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
private fun sendSpeciesData(species: PokemonSpecies.ShowdownSpecies) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(species)}\n")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
@@ -137,12 +137,16 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
writer.write(">resetSpeciesData\n")
|
||||
acknowledge()
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
sendSpeciesData(species, null)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
sendSpeciesData(species, form)
|
||||
sendSpeciesData(PokemonSpecies.ShowdownStandardForm(species.standardForm))
|
||||
species.permanentForms.forEach { permForm ->
|
||||
sendSpeciesData(PokemonSpecies.ShowdownPermanentForm(permForm))
|
||||
permForm.changeableForms.forEach { tempForm ->
|
||||
sendSpeciesData(PokemonSpecies.ShowdownTemporaryForm(tempForm))
|
||||
}
|
||||
}
|
||||
species.temporaryForms.forEach { form ->
|
||||
sendSpeciesData(PokemonSpecies.ShowdownTemporaryForm(form))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,4 +344,6 @@ object CobblemonClient {
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if the player is preoccupied and cannot accept an action. */
|
||||
fun isBusy() = battle != null || trade != null
|
||||
}
|
||||
@@ -14,7 +14,9 @@ import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.ForcePassActionResponse
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import java.util.UUID
|
||||
@@ -80,6 +82,10 @@ class ClientBattle(
|
||||
}
|
||||
}
|
||||
|
||||
fun pendingGimmick(gimmick: ShowdownMoveset.Gimmick): Boolean {
|
||||
return pendingActionRequests.none { (it.response as? MoveActionResponse)?.gimmickID == gimmick.id }
|
||||
}
|
||||
|
||||
fun getPokemonFromPNX(pnx: String): Pair<ClientBattleActor, ActiveClientBattlePokemon> {
|
||||
val actor = sides.flatMap { it.actors }.find { it.showdownId == pnx.substring(0, 2) }
|
||||
?: throw IllegalStateException("Invalid pnx: $pnx - unknown actor")
|
||||
|
||||
@@ -104,6 +104,11 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
// force a model update - handles edge case where the PosableState's tracked PosableModel isn't updated until the LivingEntityRenderer render is run
|
||||
currentModel = VaryingModelRepository.getPoser(identifier, this)
|
||||
currentEntity.refreshRiding()
|
||||
}
|
||||
else if (data == PokemonEntity.FORM) {
|
||||
val formId = currentEntity.entityData.get(PokemonEntity.FORM)
|
||||
currentEntity.pokemon.form = currentEntity.pokemon.species.getFormByShowdownId(formId)
|
||||
currentEntity.refreshRiding()
|
||||
} else if (data == PokemonEntity.ASPECTS) {
|
||||
currentAspects = currentEntity.entityData.get(PokemonEntity.ASPECTS)
|
||||
currentEntity.pokemon.shiny = currentAspects.contains("shiny")
|
||||
|
||||
+15
-7
@@ -13,11 +13,11 @@ import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleGimmickButton.GimmickTile
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
|
||||
@@ -30,7 +30,7 @@ import net.minecraft.sounds.SoundEvents
|
||||
* @author Segfault Guy
|
||||
* @since July 8th, 2023
|
||||
*/
|
||||
abstract class BattleGimmickButton(gimmick: ShowdownMoveset.Gimmick, val x: Float, val y: Float) {
|
||||
abstract class BattleGimmickButton(val gimmick: ShowdownMoveset.Gimmick, val x: Float, val y: Float) {
|
||||
|
||||
companion object {
|
||||
const val WIDTH = 36
|
||||
@@ -61,24 +61,32 @@ abstract class BattleGimmickButton(gimmick: ShowdownMoveset.Gimmick, val x: Floa
|
||||
var toggled = false
|
||||
private val sfx = SimpleSoundInstance.forUI(SoundEvents.ANVIL_LAND, 1.0F)
|
||||
private val texture = gimmick.id
|
||||
val selectable: Boolean
|
||||
get() {
|
||||
val battle = CobblemonClient.battle ?: return false
|
||||
return battle.pendingGimmick(gimmick)
|
||||
}
|
||||
|
||||
fun render(matrices: PoseStack, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
matrixStack = graphics.pose(),
|
||||
texture = cobblemonResource("textures/gui/battle/battle_gimmick_${texture}.png"),
|
||||
x = x * 2,
|
||||
y = y * 2,
|
||||
height = HEIGHT,
|
||||
width = WIDTH,
|
||||
vOffset = if (toggled || isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
vOffset = if ((toggled || isHovered(mouseX.toDouble(), mouseY.toDouble()) && selectable)) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2,
|
||||
scale = SCALE
|
||||
scale = SCALE,
|
||||
alpha = if (selectable) 1.0F else 0.5F
|
||||
)
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX >= x && mouseX <= x + XOFF && mouseY >= y && mouseY <= y + YOFF
|
||||
|
||||
fun toggle(): Boolean {
|
||||
if (!selectable)
|
||||
return toggled
|
||||
toggled = !toggled
|
||||
Minecraft.getInstance().soundManager.play(sfx)
|
||||
return toggled
|
||||
|
||||
+11
-11
@@ -74,7 +74,8 @@ class BattleMoveSelection(
|
||||
BattleGimmickButton.create(gimmick, this, backButton.x + xOff, backButton.y)
|
||||
}
|
||||
|
||||
val shiftButton = BattleShiftButton(x + 22.5F, Minecraft.getInstance().window.guiScaledHeight - 22F )
|
||||
val shiftButton = if (request.activePokemon.getFormat().battleType.slotsPerActor == 3 && (request.activePokemon.getPNX()[2] == 'a' || request.activePokemon.getPNX()[2] == 'c'))
|
||||
BattleShiftButton(x + 22.5F, Minecraft.getInstance().window.guiScaledHeight - 22F ) else null
|
||||
|
||||
open class MoveTile(
|
||||
val moveSelection: BattleMoveSelection,
|
||||
@@ -190,31 +191,30 @@ class BattleMoveSelection(
|
||||
}
|
||||
backButton.render(context, mouseX, mouseY, delta)
|
||||
gimmickButtons.forEach {
|
||||
it.render(context.pose(), mouseX, mouseY, delta)
|
||||
}
|
||||
if(this.request.activePokemon.getFormat().battleType.slotsPerActor == 3 && (request.activePokemon.getPNX()[2] == 'a' || request.activePokemon.getPNX()[2] == 'c')) {
|
||||
shiftButton.render(context, mouseX, mouseY, delta)
|
||||
it.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
shiftButton?.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
override fun mousePrimaryClicked(mouseX: Double, mouseY: Double): Boolean {
|
||||
val move = moveTiles.find { it.isHovered(mouseX, mouseY) }
|
||||
val gimmick = gimmickButtons.find { it.isHovered(mouseX, mouseY) }
|
||||
if (move != null) {
|
||||
if(this.request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
if (this.request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
move.onClick()
|
||||
} else {
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, move.move))
|
||||
return true
|
||||
} else if (move.selectable) {
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, move.move, move.response.gimmickID, move.move.gimmickMove))
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
} else if (backButton.isHovered(mouseX, mouseY)) {
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.changeActionSelection(null)
|
||||
} else if (gimmick != null) {
|
||||
} else if (gimmick != null && gimmick.selectable) {
|
||||
gimmickButtons.filter { it != gimmick }.forEach { it.toggled = false }
|
||||
moveTiles = if (gimmick.toggle()) gimmick.tiles else baseTiles
|
||||
} else if(shiftButton.isHovered(mouseX,mouseY)) {
|
||||
} else if (shiftButton?.isHovered(mouseX,mouseY) == true) {
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
battleGUI.selectAction(request, ShiftActionResponse())
|
||||
}
|
||||
|
||||
+10
-8
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.font
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.InBattleGimmickMove
|
||||
import com.cobblemon.mod.common.battles.InBattleMove
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
@@ -22,7 +23,6 @@ import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleSwitchPokemonSelection.Companion
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
@@ -41,9 +41,11 @@ import kotlin.math.floor
|
||||
import kotlin.math.sin
|
||||
|
||||
class BattleTargetSelection(
|
||||
battleGUI: BattleGUI,
|
||||
request: SingleActionRequest,
|
||||
val move: InBattleMove
|
||||
battleGUI: BattleGUI,
|
||||
request: SingleActionRequest,
|
||||
val move: InBattleMove,
|
||||
val gimmickID: String?,
|
||||
gimmickMove: InBattleGimmickMove?
|
||||
) : BattleActionSelection(
|
||||
battleGUI = battleGUI,
|
||||
request = request,
|
||||
@@ -80,10 +82,10 @@ class BattleTargetSelection(
|
||||
}
|
||||
|
||||
val targets = request.activePokemon.getAllActivePokemon()
|
||||
|
||||
val targetType = if (gimmickID != null && gimmickMove != null) gimmickMove.target else move.target
|
||||
val backButton = BattleBackButton(x + 9F, Minecraft.getInstance().window.guiScaledHeight - 22F)
|
||||
val selectableTargetList = move.target.targetList(request.activePokemon)
|
||||
val multiTargetList = if(selectableTargetList == null) request.activePokemon.getMultiTargetList(move.target) else null
|
||||
val selectableTargetList = targetType.targetList(request.activePokemon)
|
||||
val multiTargetList = if(selectableTargetList == null) request.activePokemon.getMultiTargetList(targetType) else null
|
||||
|
||||
val baseTiles = targets.mapIndexed { index, target ->
|
||||
val isAlly = target.isAllied(request.activePokemon)
|
||||
@@ -123,7 +125,7 @@ class BattleTargetSelection(
|
||||
private val isMultiTarget = multiTargetList?.firstOrNull { it.getPNX() == target.getPNX() } != null
|
||||
private val isCurrentPokemon = request.activePokemon.battlePokemon?.uuid == target.battlePokemon!!.uuid
|
||||
|
||||
open val response: MoveActionResponse get() = MoveActionResponse(targetSelection.move.id, responseTarget)
|
||||
open val response: MoveActionResponse get() = MoveActionResponse(targetSelection.move.id, responseTarget, targetSelection.gimmickID)
|
||||
val state = FloatingState()
|
||||
|
||||
open val selectable: Boolean get() = isMultiTarget || (responseTarget != null)
|
||||
|
||||
@@ -30,8 +30,11 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.screens.marks.MarksWi
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSwapScreen
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.transformations.EvolutionTestSelectScreen
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.transformations.FormSelectScreen
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.RevertFormPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
@@ -79,6 +82,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
const val PARTY = 0
|
||||
const val MOVE_SWAP = 1
|
||||
const val EVOLVE = 2
|
||||
const val FORM = 3
|
||||
|
||||
// Resources
|
||||
private val baseResource = cobblemonResource("textures/gui/summary/summary_base.png")
|
||||
@@ -93,6 +97,8 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
private val tabIconMoves = cobblemonResource("textures/gui/summary/summary_tab_icon_moves.png")
|
||||
private val tabIconStats = cobblemonResource("textures/gui/summary/summary_tab_icon_stats.png")
|
||||
private val tabIconMarks = cobblemonResource("textures/gui/summary/summary_tab_icon_marks.png")
|
||||
private val formButtonResource = cobblemonResource("textures/gui/summary/summary_form_test_button.png")
|
||||
private val revertButtonResource = cobblemonResource("textures/gui/summary/summary_revert_test_button.png")
|
||||
val iconShinyResource = cobblemonResource("textures/gui/summary/icon_shiny.png")
|
||||
val iconHeldItemResource = cobblemonResource("textures/gui/summary/icon_item_held.png")
|
||||
val iconCosmeticItemResource = cobblemonResource("textures/gui/summary/icon_item_cosmetic.png")
|
||||
@@ -176,8 +182,46 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
},
|
||||
text = lang("ui.evolve"),
|
||||
resource = evolveButtonResource,
|
||||
renderRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() && CobblemonClient.battle == null },
|
||||
clickRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() && CobblemonClient.battle == null }
|
||||
renderRequirement = { selectedPokemon.evolutionProxy.client().canStart() },
|
||||
clickRequirement = { selectedPokemon.evolutionProxy.client().canStart() }
|
||||
)
|
||||
)
|
||||
|
||||
// TODO form and revert button are temp for debugging
|
||||
|
||||
// Form Button
|
||||
addRenderableWidget(
|
||||
SummaryButton(
|
||||
buttonX = x + 4F,
|
||||
buttonY = y + 30F,
|
||||
buttonWidth = 10,
|
||||
buttonHeight = 10,
|
||||
clickAction = {
|
||||
momentarily { displaySideScreen(if (sideScreenIndex == FORM) PARTY else FORM) }
|
||||
},
|
||||
resource = formButtonResource,
|
||||
renderRequirement = { selectedPokemon.formProxy.client().canStart() },
|
||||
clickRequirement = { selectedPokemon.formProxy.client().canStart() }
|
||||
)
|
||||
)
|
||||
|
||||
// Temporary form revert button
|
||||
addRenderableWidget(
|
||||
SummaryButton(
|
||||
buttonX = x + 4F,
|
||||
buttonY = y + 50F,
|
||||
buttonWidth = 10,
|
||||
buttonHeight = 10,
|
||||
clickAction = {
|
||||
momentarily {
|
||||
Minecraft.getInstance().player?.clientSideCloseContainer()
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
sendToServer(RevertFormPacket(selectedPokemon.uuid))
|
||||
}
|
||||
},
|
||||
resource = revertButtonResource,
|
||||
renderRequirement = { selectedPokemon.formProxy.client().canRevert() },
|
||||
clickRequirement = { selectedPokemon.formProxy.client().canRevert() }
|
||||
)
|
||||
)
|
||||
|
||||
@@ -327,7 +371,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
this.party.getOrNull(newSelection)?.let { this.selectedPokemon = it }
|
||||
listenToMoveSet()
|
||||
displayMainScreen(mainScreenIndex)
|
||||
children().find { it is EvolutionSelectScreen }?.let(this::removeWidget)
|
||||
children().find { it is EvolutionTestSelectScreen }?.let(this::removeWidget)
|
||||
|
||||
if (::modelWidget.isInitialized) {
|
||||
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
|
||||
@@ -469,12 +513,22 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
EVOLVE -> {
|
||||
saveActiveMark()
|
||||
saveMarkings()
|
||||
sideScreen = EvolutionSelectScreen(
|
||||
sideScreen = EvolutionTestSelectScreen(
|
||||
x + 216,
|
||||
y + 23,
|
||||
pokemon = selectedPokemon
|
||||
)
|
||||
}
|
||||
|
||||
FORM -> {
|
||||
saveActiveMark()
|
||||
saveMarkings()
|
||||
sideScreen = FormSelectScreen(
|
||||
x + 216,
|
||||
y + 23,
|
||||
pokemon = selectedPokemon
|
||||
)
|
||||
}
|
||||
}
|
||||
val element = sideScreen
|
||||
if (element is Renderable && element is NarratableEntry) {
|
||||
@@ -674,7 +728,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE || sideScreenIndex == FORM) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
if (mainScreenIndex == MOVES) mainScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
|
||||
interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature<*>> {
|
||||
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
|
||||
val name: String
|
||||
/** Draws it at a particular position. */
|
||||
|
||||
+28
-17
@@ -6,11 +6,13 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.transformations
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.Transformation
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.TransformationDisplay
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.proxy.TransformationProxy
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
@@ -22,23 +24,24 @@ import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class EvolutionSelectScreen(
|
||||
abstract class EvolutionSelectScreen<C : TransformationDisplay, S : Transformation>(
|
||||
x: Int,
|
||||
y: Int,
|
||||
val pokemon: Pokemon
|
||||
): SummaryScrollList<EvolutionSelectScreen.EvolveSlot>(
|
||||
lang: MutableComponent,
|
||||
val pokemon: Pokemon,
|
||||
): SummaryScrollList<EvolutionSelectScreen.TransformationSlot<C, S>>(
|
||||
x,
|
||||
y,
|
||||
lang("ui.evolution"),
|
||||
lang,
|
||||
SLOT_HEIGHT + SLOT_SPACING
|
||||
) {
|
||||
companion object {
|
||||
@@ -50,24 +53,30 @@ class EvolutionSelectScreen(
|
||||
private val buttonResource = cobblemonResource("textures/gui/summary/summary_evolve_select_button.png")
|
||||
}
|
||||
|
||||
abstract fun getSlots(pokemon: Pokemon): Collection<TransformationSlot<C, S>>
|
||||
|
||||
private var entriesCreated = false
|
||||
|
||||
public override fun addEntry(entry: EvolveSlot): Int {
|
||||
public override fun addEntry(entry: TransformationSlot<C, S>): Int {
|
||||
return super.addEntry(entry)
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
if (!entriesCreated) {
|
||||
entriesCreated = true
|
||||
pokemon.evolutionProxy.client().map { EvolveSlot(pokemon, it) }.forEach { entry -> this.addEntry(entry) }
|
||||
getSlots(pokemon).forEach { entry -> this.addEntry(entry) }
|
||||
}
|
||||
super.renderWidget(context, mouseX, mouseY, partialTicks)
|
||||
}
|
||||
|
||||
class EvolveSlot(private val pokemon: Pokemon, private val evolution: EvolutionDisplay) : Entry<EvolveSlot>() {
|
||||
abstract class TransformationSlot<C : TransformationDisplay, S : Transformation>(
|
||||
private val pokemon: Pokemon,
|
||||
private val display: C,
|
||||
text: MutableComponent
|
||||
) : Entry<TransformationSlot<C, S>>() {
|
||||
val client: Minecraft = Minecraft.getInstance()
|
||||
val state = FloatingState()
|
||||
val form: FormData = evolution.species.getForm(evolution.aspects)
|
||||
val form: FormData = display.species.getForm(display.aspects)
|
||||
val selectButton: SummaryButton = SummaryButton(
|
||||
buttonX = 0F,
|
||||
buttonY = 0F,
|
||||
@@ -76,20 +85,22 @@ class EvolutionSelectScreen(
|
||||
clickAction = {
|
||||
Minecraft.getInstance().player?.clientSideCloseContainer()
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
pokemon.evolutionProxy.client().start(this.evolution)
|
||||
getProxy(pokemon).client().start(this.display)
|
||||
},
|
||||
text = lang("ui.evolve"),
|
||||
text = text,
|
||||
resource = buttonResource,
|
||||
boldText = true,
|
||||
largeText = false,
|
||||
textScale = 0.5F
|
||||
)
|
||||
|
||||
abstract fun getProxy(pokemon: Pokemon): TransformationProxy<C, S, *, *>
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
|
||||
override fun getNarration() = evolution.species.translatedName
|
||||
override fun getNarration() = display.species.translatedName
|
||||
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
@@ -107,7 +118,7 @@ class EvolutionSelectScreen(
|
||||
val y = rowTop
|
||||
val matrices = context.pose()
|
||||
|
||||
state.currentAspects = evolution.aspects
|
||||
state.currentAspects = display.aspects
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
@@ -121,7 +132,7 @@ class EvolutionSelectScreen(
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = evolution.species.translatedName.bold(),
|
||||
text = display.species.translatedName.bold(),
|
||||
x = x + 4,
|
||||
y = y + 2,
|
||||
shadow = true
|
||||
@@ -146,7 +157,7 @@ class EvolutionSelectScreen(
|
||||
matrices.translate(x + (PORTRAIT_DIAMETER / 2) + 65.0, y - 5.0, 0.0)
|
||||
matrices.scale(2.5F, 2.5F, 1F)
|
||||
drawProfilePokemon(
|
||||
species = this.evolution.species.resourceIdentifier,
|
||||
species = this.display.species.resourceIdentifier,
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.transformations
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.proxy.TransformationProxy
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
|
||||
class EvolutionTestSelectScreen(
|
||||
x: Int,
|
||||
y: Int,
|
||||
pokemon: Pokemon
|
||||
) : EvolutionSelectScreen<EvolutionDisplay, Evolution>(x, y, lang("ui.evolution"), pokemon) {
|
||||
|
||||
override fun getSlots(pokemon: Pokemon): Collection<TransformationSlot<EvolutionDisplay, Evolution>> {
|
||||
return pokemon.evolutionProxy.client().map { EvolveSlot(pokemon, it) }
|
||||
}
|
||||
|
||||
class EvolveSlot(
|
||||
pokemon: Pokemon,
|
||||
display: EvolutionDisplay
|
||||
) : TransformationSlot<EvolutionDisplay, Evolution>(pokemon, display, lang("ui.evolve")) {
|
||||
override fun getProxy(pokemon: Pokemon): TransformationProxy<EvolutionDisplay, Evolution, *, *> {
|
||||
return pokemon.evolutionProxy
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.transformations
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.proxy.TransformationProxy
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.FormDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
|
||||
class FormSelectScreen(
|
||||
x: Int,
|
||||
y: Int,
|
||||
pokemon: Pokemon
|
||||
) : EvolutionSelectScreen<FormDisplay, TemporaryForm>(x, y, lang("ui.forms"), pokemon) {
|
||||
|
||||
override fun getSlots(pokemon: Pokemon): Collection<TransformationSlot<FormDisplay, TemporaryForm>> {
|
||||
return pokemon.formProxy.client().map { FormSlot(pokemon, it) }
|
||||
}
|
||||
|
||||
class FormSlot(pokemon: Pokemon, display: FormDisplay) : TransformationSlot<FormDisplay, TemporaryForm>(pokemon, display, lang("ui.form")) {
|
||||
override fun getProxy(pokemon: Pokemon): TransformationProxy<FormDisplay, TemporaryForm, *, *> {
|
||||
return pokemon.formProxy
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,8 +46,7 @@ object ChangeEyeHeight {
|
||||
val height = FloatArgumentType.getFloat(context, "height")
|
||||
|
||||
applicator.invoke(pkm, height)
|
||||
pkm.forms.clear()
|
||||
pkm.forms.add(FormData().also { it.initialize(pkm) })
|
||||
// TODO refresh form
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,7 @@ object ChangeScaleAndSize {
|
||||
|
||||
pkm.baseScale = scale
|
||||
pkm.hitbox = EntityDimensions.scalable(width, height)
|
||||
pkm.forms.clear()
|
||||
pkm.forms.add(FormData().also { it.initialize(pkm) })
|
||||
// TODO refresh form
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,7 @@ object ChangeWalkSpeed {
|
||||
val walkSpeed = FloatArgumentType.getFloat(context, "walkSpeed")
|
||||
|
||||
pkm.behaviour.moving.walk.walkSpeed = walkSpeed.toString().asExpressionLike()
|
||||
pkm.forms.clear()
|
||||
pkm.forms.add(FormData().also { it.initialize(pkm)})
|
||||
// TODO refresh form
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -67,12 +68,12 @@ object TestCommand {
|
||||
try {
|
||||
//this.testCodecOutput(context)
|
||||
val player = context.source.entity as ServerPlayer
|
||||
player.party().forEach { it.currentHealth = it.hp / 2 }
|
||||
val npc = NPCEntity(player.level())
|
||||
npc.setPos(player.x, player.y, player.z)
|
||||
npc.npc = NPCClasses.getByName("standard")!!
|
||||
npc.initialize(50)
|
||||
player.level().addFreshEntity(npc)
|
||||
//player.party().forEach { it.currentHealth = it.hp / 2 }
|
||||
//val npc = NPCEntity(player.level())
|
||||
//npc.setPos(player.x, player.y, player.z)
|
||||
//npc.npc = NPCClasses.getByName("standard")!!
|
||||
//npc.initialize(50)
|
||||
//player.level().addFreshEntity(npc)
|
||||
// val evolutionEntity = GenericBedrockEntity(world = player.level())
|
||||
// evolutionEntity.apply {
|
||||
// category = cobblemonResource("evolution")
|
||||
@@ -126,6 +127,20 @@ object TestCommand {
|
||||
|
||||
// val player = context.source.entity as ServerPlayerEntity
|
||||
// player.giveItemStack(PokemonItem.from(PokemonSpecies.random(), "alolan"))
|
||||
val npcs = player.level().getEntities(player, player.boundingBox.inflate(20.0)) { it is NPCEntity }
|
||||
.map { NPCBattleActor(it as NPCEntity, it.getPartyForChallenge(listOf(player))!!, 0) }
|
||||
.sortedBy { player.distanceToSqr(it.entity) }
|
||||
|
||||
val playerTeam = player.party().toBattleTeam()
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = BattleFormat.GEN_9_MULTI,
|
||||
side1 = BattleSide(playerActor, npcs[0]),
|
||||
side2 = BattleSide(npcs[1], npcs[2])
|
||||
).ifSuccessful {
|
||||
it.battlePartyStores.add(player.party())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error(e)
|
||||
}
|
||||
|
||||
+2
-2
@@ -401,7 +401,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
|
||||
after(seconds = 0.1F) {
|
||||
pokemon.beamMode = 0
|
||||
pokemon.busyLocks.remove(this)
|
||||
pokemon.busyLocks.remove(PokemonEntity.CAPTURE)
|
||||
captureFuture.complete(false)
|
||||
val ballType =
|
||||
this.pokeBall.name.path.lowercase().replace("_", "")
|
||||
@@ -424,7 +424,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
override fun getDimensions(pPose: Pose) = DIMENSIONS
|
||||
|
||||
private fun attemptCatch(pokemonEntity: PokemonEntity) {
|
||||
pokemonEntity.busyLocks.add(this)
|
||||
pokemonEntity.busyLocks.add(PokemonEntity.CAPTURE)
|
||||
val displace = deltaMovement
|
||||
captureState = CaptureState.HIT
|
||||
val mul = if (random.nextBoolean()) 1 else -1
|
||||
|
||||
@@ -90,9 +90,9 @@ import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.InactivePokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.ai.FormPokemonBehaviour
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.StashHandler
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.feature.StashHandler
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.ItemInteractionTrigger
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -179,11 +179,14 @@ open class PokemonEntity(
|
||||
@JvmStatic val FRIENDSHIP = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.INT)
|
||||
@JvmStatic val FREEZE_FRAME = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.FLOAT)
|
||||
@JvmStatic val CAUGHT_BALL = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.STRING)
|
||||
@JvmStatic val EVOLUTION_STARTED = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||
@JvmStatic val TRANSFORMATION_STARTED = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||
@JvmStatic val FORM = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.STRING)
|
||||
@JvmStatic val BUSY_LOCKED = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||
@JvmStatic var SHOWN_HELD_ITEM = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.ITEM_STACK)
|
||||
|
||||
const val BATTLE_LOCK = "battle"
|
||||
const val EVOLUTION_LOCK = "evolving"
|
||||
// Cobblemon busy locks
|
||||
val SHOULDER_MOUNT_LOCK = cobblemonResource("shoulder_mount")
|
||||
val CAPTURE = cobblemonResource("capture")
|
||||
|
||||
fun createAttributes(): AttributeSupplier.Builder = createLivingAttributes()
|
||||
.add(Attributes.FOLLOW_RANGE)
|
||||
@@ -219,14 +222,27 @@ open class PokemonEntity(
|
||||
/** The player that caused this Pokémon to faint. */
|
||||
var killer: ServerPlayer? = null
|
||||
|
||||
val isEvolving: Boolean
|
||||
get() = entityData.get(EVOLUTION_STARTED)
|
||||
val isTransforming: Boolean
|
||||
get() = entityData.get(TRANSFORMATION_STARTED)
|
||||
var evolutionEntity: GenericBedrockEntity? = null
|
||||
|
||||
var ticksLived = 0
|
||||
val busyLocks = mutableListOf<Any>()
|
||||
|
||||
/**
|
||||
* Custom locks for marking an entity as busy. When populated, [isBusy] and [BUSY_LOCKED] will always evaluate as TRUE.
|
||||
*
|
||||
* NOTE: [BUSY_LOCKED] is NOT a comprehensive check of whether an entity is busy. [isBusy] evaluates Cobblemon-specific
|
||||
* busy conditions that should ALWAYS be checked.
|
||||
*/
|
||||
val busyLocks = mutableListOf<ResourceLocation>()
|
||||
|
||||
/**
|
||||
* Whether this entity is performing a blocking action.
|
||||
*
|
||||
* To track an action as blocking, add it to the [busyLocks].
|
||||
*/
|
||||
val isBusy: Boolean
|
||||
get() = busyLocks.isNotEmpty()
|
||||
get() = entityData.get(BUSY_LOCKED) || isBattling || isTransforming
|
||||
val aspects: Set<String>
|
||||
get() = entityData.get(ASPECTS)
|
||||
var battleId: UUID?
|
||||
@@ -394,7 +410,9 @@ open class PokemonEntity(
|
||||
builder.define(FRIENDSHIP, 0)
|
||||
builder.define(FREEZE_FRAME, -1F)
|
||||
builder.define(CAUGHT_BALL, "")
|
||||
builder.define(EVOLUTION_STARTED, false)
|
||||
builder.define(TRANSFORMATION_STARTED, false)
|
||||
builder.define(FORM, "")
|
||||
builder.define(BUSY_LOCKED, false)
|
||||
builder.define(SHOWN_HELD_ITEM, ItemStack.EMPTY)
|
||||
}
|
||||
|
||||
@@ -407,29 +425,11 @@ open class PokemonEntity(
|
||||
|
||||
// common SynchedEntityData handling
|
||||
when (data) {
|
||||
SPECIES -> refreshDimensions()
|
||||
SPECIES, FORM -> refreshDimensions()
|
||||
POSE_TYPE -> {
|
||||
val value = entityData.get(data) as PoseType
|
||||
isNoGravity = (value == PoseType.FLY || value == PoseType.HOVER) && passengers.isEmpty()
|
||||
}
|
||||
|
||||
BATTLE_ID -> {
|
||||
if (battleId != null) {
|
||||
busyLocks.remove(BATTLE_LOCK) // Remove in case it's hopped across to another battle, don't want extra battle locks
|
||||
busyLocks.add(BATTLE_LOCK)
|
||||
} else {
|
||||
busyLocks.remove(BATTLE_LOCK)
|
||||
}
|
||||
}
|
||||
|
||||
EVOLUTION_STARTED -> {
|
||||
if (isEvolving) {
|
||||
busyLocks.remove(EVOLUTION_LOCK)
|
||||
busyLocks.add(EVOLUTION_LOCK)
|
||||
} else {
|
||||
busyLocks.remove(EVOLUTION_LOCK)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1157,25 +1157,16 @@ open class PokemonEntity(
|
||||
return false
|
||||
}
|
||||
|
||||
// Check evolution item interaction
|
||||
// Check evolution and form change item interaction
|
||||
if (pokemon.getOwnerPlayer() == player) {
|
||||
val context = ItemInteractionEvolution.ItemInteractionContext(stack, player.level())
|
||||
pokemon.lockedEvolutions
|
||||
.filterIsInstance<ItemInteractionEvolution>()
|
||||
.forEach { evolution ->
|
||||
if (evolution.attemptEvolution(pokemon, context)) {
|
||||
if (!player.isCreative) {
|
||||
stack.shrink(1)
|
||||
}
|
||||
this.level().playSoundServer(
|
||||
position = this.position(),
|
||||
sound = CobblemonSounds.ITEM_USE,
|
||||
volume = 1F,
|
||||
pitch = 1F
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
val context = ItemInteractionTrigger.Context(stack, player.level())
|
||||
val transformed = pokemon.triggerTransformations(ItemInteractionTrigger::class.java, context)
|
||||
|
||||
if (transformed.any()) {
|
||||
if (!player.isCreative) stack.shrink(1)
|
||||
this.level().playSoundServer(position = this.position(), sound = CobblemonSounds.ITEM_USE, volume = 1F, pitch = 1F)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
(stack.item as? PokemonEntityInteraction)?.let {
|
||||
@@ -1279,10 +1270,9 @@ open class PokemonEntity(
|
||||
) {
|
||||
val dirToPlayer = player.eyePosition.subtract(position()).multiply(1.0, 0.0, 1.0).normalize()
|
||||
deltaMovement = dirToPlayer.scale(0.8).add(0.0, 0.5, 0.0)
|
||||
val lock = Any()
|
||||
busyLocks.add(lock)
|
||||
busyLocks.add(SHOULDER_MOUNT_LOCK)
|
||||
after(seconds = 0.5F) {
|
||||
busyLocks.remove(lock)
|
||||
busyLocks.remove(SHOULDER_MOUNT_LOCK)
|
||||
if (!isBusy && isAlive) {
|
||||
val isLeft = player.shoulderEntityLeft.isEmpty
|
||||
if (isLeft || player.shoulderEntityRight.isEmpty) {
|
||||
@@ -1505,7 +1495,7 @@ open class PokemonEntity(
|
||||
|
||||
val feature = pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED)
|
||||
if (feature != null) {
|
||||
feature.enabled = false
|
||||
feature.value = false
|
||||
pokemon.markFeatureDirty(feature)
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
@@ -1741,7 +1731,7 @@ open class PokemonEntity(
|
||||
override fun shear(shearedSoundCategory: SoundSource) {
|
||||
this.level().playSound(null, this, SoundEvents.SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F)
|
||||
val feature = this.pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED) ?: return
|
||||
feature.enabled = true
|
||||
feature.value = true
|
||||
this.pokemon.markFeatureDirty(feature)
|
||||
this.pokemon.updateAspects()
|
||||
val i = this.random.nextInt(3) + 1
|
||||
@@ -1776,7 +1766,7 @@ open class PokemonEntity(
|
||||
|
||||
override fun readyForShearing(): Boolean {
|
||||
val feature = this.pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED) ?: return false
|
||||
return !this.isBusy && !this.pokemon.isFainted() && !feature.enabled
|
||||
return !this.isBusy && !this.pokemon.isFainted() && !feature.value
|
||||
}
|
||||
|
||||
override fun canUsePortal(allowsVehicles: Boolean) = false
|
||||
|
||||
+15
-6
@@ -142,6 +142,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
val trackedMark = entity.pokemon.activeMark?.identifier.toString()
|
||||
val trackedAspects = mock?.aspects ?: entity.pokemon.aspects
|
||||
val trackedBall = mock?.pokeball ?: entity.pokemon.caughtBall.name.toString()
|
||||
val trackedForm = mock?.form ?: entity.pokemon.form.formOnlyShowdownId()
|
||||
|
||||
entity.ownerUUID = entity.pokemon.getOwnerUUID()
|
||||
entity.entityData.set(PokemonEntity.SPECIES, trackedSpecies)
|
||||
@@ -163,6 +164,8 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
|
||||
entity.entityData.set(PokemonEntity.FRIENDSHIP, entity.pokemon.friendship)
|
||||
entity.entityData.set(PokemonEntity.CAUGHT_BALL, trackedBall)
|
||||
entity.entityData.set(PokemonEntity.FORM, trackedForm) // have this get form from aspects...?
|
||||
entity.entityData.set(PokemonEntity.BUSY_LOCKED, entity.busyLocks.isNotEmpty())
|
||||
|
||||
updateShownItem()
|
||||
updatePoseType()
|
||||
@@ -212,13 +215,19 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
}
|
||||
|
||||
val battle = getBattle()
|
||||
if (entity.ticksLived % 20 == 0 && battle != null) {
|
||||
val activeBattlePokemon = battle
|
||||
.activePokemon
|
||||
.find { it.battlePokemon?.uuid == entity.pokemon.uuid }
|
||||
if (entity.ticksLived % 20 == 0) {
|
||||
if (battle != null) {
|
||||
val activeBattlePokemon = battle
|
||||
.activePokemon
|
||||
.find { it.battlePokemon?.uuid == entity.pokemon.uuid }
|
||||
|
||||
if (activeBattlePokemon != null) {
|
||||
activeBattlePokemon.position = entity.level() as ServerLevel to entity.position()
|
||||
if (activeBattlePokemon != null) {
|
||||
activeBattlePokemon.position = entity.level() as ServerLevel to entity.position()
|
||||
}
|
||||
}
|
||||
// reevaluate forms of wild pokemon
|
||||
else if (entity.owner == null) {
|
||||
entity.pokemon.formProxy.server().reevaluate()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
package com.cobblemon.mod.common.item.interactive
|
||||
|
||||
import com.cobblemon.mod.common.api.interaction.PokemonEntityInteraction
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.triggers.TradeTrigger
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -19,20 +20,13 @@ class LinkCableItem : CobblemonItem(Properties()), PokemonEntityInteraction {
|
||||
override val accepted = setOf(PokemonEntityInteraction.Ownership.OWNER)
|
||||
override fun processInteraction(player: ServerPlayer, entity: PokemonEntity, stack: ItemStack): Boolean {
|
||||
val pokemon = entity.pokemon
|
||||
pokemon.lockedEvolutions.filterIsInstance<TradeEvolution>().forEach { evolution ->
|
||||
// Prevent Pokémon that require a specific trading partner for evolution (e.g., Shelmet and Karrablast)
|
||||
// from evolving via link cable. These Pokémon must either be traded with another player or use an
|
||||
// alternative single-player evolution method.
|
||||
if (!evolution.requiredContext.originalString.isEmpty()) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
// If an evolution is possible non-optional or has been successfully queued we will consume the item and stop
|
||||
// validate requirements to respect required held items and such.
|
||||
if (evolution.requirements.all { it.check(pokemon) } && evolution.evolve(pokemon)) {
|
||||
this.consumeItem(player, stack)
|
||||
return true
|
||||
}
|
||||
// empty properties provided - this will fail for Pokémon that require a specific trading partner
|
||||
val context = TradeTrigger.Context(PokemonProperties())
|
||||
val transformed = pokemon.triggerTransformations(TradeTrigger::class.java, context)
|
||||
if (transformed.any()) {
|
||||
this.consumeItem(player, stack)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+6
-4
@@ -11,12 +11,14 @@ package com.cobblemon.mod.common.net.messages.client.pokemon.update
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class FormUpdatePacket(pokemon: () -> Pokemon, form: FormData) : SingleUpdatePacket<FormData, FormUpdatePacket>(pokemon, form) {
|
||||
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
|
||||
this.value.encode(buffer)
|
||||
buffer.writeString(this.value.name)
|
||||
}
|
||||
|
||||
override fun set(pokemon: Pokemon, value: FormData) {
|
||||
@@ -29,9 +31,9 @@ class FormUpdatePacket(pokemon: () -> Pokemon, form: FormData) : SingleUpdatePac
|
||||
val ID: ResourceLocation = cobblemonResource("packets/form-update")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): FormUpdatePacket {
|
||||
val pokemon = decodePokemon(buffer)
|
||||
val form = FormData()
|
||||
form.species = pokemon().species
|
||||
form.decode(buffer)
|
||||
val formName = buffer.readString()
|
||||
val species = pokemon().species
|
||||
val form = species.forms.find { it.name == formName } ?: species.standardForm
|
||||
return FormUpdatePacket(pokemon, form)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
class SpeciesFeatureUpdatePacket(pokemon: () -> Pokemon, val species: ResourceLocation, speciesFeature: SynchronizedSpeciesFeature) : SingleUpdatePacket<SynchronizedSpeciesFeature, SpeciesFeatureUpdatePacket>(pokemon, speciesFeature) {
|
||||
class SpeciesFeatureUpdatePacket(pokemon: () -> Pokemon, val species: ResourceLocation, speciesFeature: SynchronizedSpeciesFeature<*>) : SingleUpdatePacket<SynchronizedSpeciesFeature<*>, SpeciesFeatureUpdatePacket>(pokemon, speciesFeature) {
|
||||
companion object {
|
||||
val ID = cobblemonResource("species_feature_update")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): SpeciesFeatureUpdatePacket {
|
||||
@@ -50,7 +50,7 @@ class SpeciesFeatureUpdatePacket(pokemon: () -> Pokemon, val species: ResourceLo
|
||||
value.saveToBuffer(buffer, toClient = true)
|
||||
}
|
||||
|
||||
override fun set(pokemon: Pokemon, value: SynchronizedSpeciesFeature) {
|
||||
override fun set(pokemon: Pokemon, value: SynchronizedSpeciesFeature<*>) {
|
||||
pokemon.features.removeIf { it.name == value.name }
|
||||
pokemon.features.add(value)
|
||||
}
|
||||
|
||||
+4
-34
@@ -8,24 +8,18 @@
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.SingleUpdatePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.CobblemonEvolutionDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import io.netty.buffer.ByteBuf
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class AddEvolutionPacket(pokemon: () -> Pokemon, value: EvolutionDisplay) : SingleUpdatePacket<EvolutionDisplay, AddEvolutionPacket>(pokemon, value) {
|
||||
|
||||
override val id = ID
|
||||
|
||||
constructor(pokemon: Pokemon, value: Evolution, registryAccess: RegistryAccess) : this({ pokemon }, value.convertToDisplay(pokemon, registryAccess))
|
||||
constructor(pokemon: Pokemon, value: Evolution) : this({ pokemon }, value.convertToDisplay(pokemon))
|
||||
|
||||
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
|
||||
this.value.encode(buffer)
|
||||
@@ -39,30 +33,6 @@ class AddEvolutionPacket(pokemon: () -> Pokemon, value: EvolutionDisplay) : Sing
|
||||
|
||||
val ID = cobblemonResource("add_evolution")
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = AddEvolutionPacket(decodePokemon(buffer), decodeDisplay(buffer))
|
||||
|
||||
internal fun Evolution.convertToDisplay(pokemon: Pokemon, registryAccess: RegistryAccess): EvolutionDisplay {
|
||||
val result = pokemon.clone(registryAccess = registryAccess)
|
||||
this.result.apply(result)
|
||||
val expectedDisplay = CobblemonEvolutionDisplay(this.id, result)
|
||||
val event = EvolutionDisplayEvent(result, expectedDisplay, this)
|
||||
CobblemonEvents.EVOLUTION_DISPLAY.post(event)
|
||||
return event.display
|
||||
}
|
||||
|
||||
internal fun EvolutionDisplay.encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeString(this.id)
|
||||
buffer.writeIdentifier(this.species.resourceIdentifier)
|
||||
buffer.writeCollection(this.aspects) { pb, value -> pb.writeString(value) }
|
||||
}
|
||||
|
||||
internal fun decodeDisplay(buffer: RegistryFriendlyByteBuf): EvolutionDisplay {
|
||||
val id = buffer.readString()
|
||||
val speciesIdentifier = buffer.readIdentifier()
|
||||
val species = PokemonSpecies.getByIdentifier(speciesIdentifier)
|
||||
?: throw IllegalArgumentException("Cannot resolve species from $speciesIdentifier")
|
||||
val aspects = buffer.readList(ByteBuf::readString).toSet()
|
||||
return CobblemonEvolutionDisplay(id, species, aspects)
|
||||
}
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = AddEvolutionPacket(decodePokemon(buffer), EvolutionDisplay.decodeDisplay(buffer))
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -8,22 +8,18 @@
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.SingleUpdatePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket.Companion.convertToDisplay
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket.Companion.decodeDisplay
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket.Companion.encode
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class RemoveEvolutionPacket(pokemon: () -> Pokemon, value: EvolutionDisplay) : SingleUpdatePacket<EvolutionDisplay, RemoveEvolutionPacket>(pokemon, value) {
|
||||
|
||||
override val id = ID
|
||||
|
||||
constructor(pokemon: Pokemon, value: Evolution, registryAccess: RegistryAccess) : this({ pokemon }, value.convertToDisplay(pokemon, registryAccess))
|
||||
constructor(pokemon: Pokemon, value: Evolution) : this({ pokemon }, value.convertToDisplay(pokemon))
|
||||
|
||||
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
|
||||
this.value.encode(buffer)
|
||||
@@ -36,7 +32,7 @@ class RemoveEvolutionPacket(pokemon: () -> Pokemon, value: EvolutionDisplay) : S
|
||||
companion object {
|
||||
val ID = cobblemonResource("remove_evolution")
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RemoveEvolutionPacket(decodePokemon(buffer), decodeDisplay(buffer))
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RemoveEvolutionPacket(decodePokemon(buffer), EvolutionDisplay.decodeDisplay(buffer))
|
||||
|
||||
}
|
||||
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.pokemon.update.form
|
||||
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.SingleUpdatePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.FormDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class AddFormPacket(pokemon: () -> Pokemon, value: FormDisplay) : SingleUpdatePacket<FormDisplay, AddFormPacket>(pokemon, value) {
|
||||
|
||||
override val id = ID
|
||||
|
||||
constructor(pokemon: Pokemon, value: TemporaryForm) : this({ pokemon }, value.convertToDisplay(pokemon))
|
||||
|
||||
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
|
||||
this.value.encode(buffer)
|
||||
}
|
||||
|
||||
override fun set(pokemon: Pokemon, value: FormDisplay) {
|
||||
pokemon.formProxy.client().add(value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val ID = cobblemonResource("add_form")
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = AddFormPacket(decodePokemon(buffer), FormDisplay.decode(buffer))
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.pokemon.update.form
|
||||
|
||||
import com.cobblemon.mod.common.net.messages.client.PokemonUpdatePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class ClearFormsPacket(pokemon: () -> Pokemon) : PokemonUpdatePacket<ClearFormsPacket>(pokemon) {
|
||||
|
||||
override val id = ID
|
||||
|
||||
override fun encodeDetails(buffer: RegistryFriendlyByteBuf) {}
|
||||
|
||||
override fun applyToPokemon() {
|
||||
this.pokemon().formProxy.client().clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("clear_forms")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = ClearFormsPacket(decodePokemon(buffer))
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.pokemon.update.form
|
||||
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.SingleUpdatePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.FormDisplay
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class RemoveFormPacket(pokemon: () -> Pokemon, value: FormDisplay) : SingleUpdatePacket<FormDisplay, RemoveFormPacket>(pokemon, value) {
|
||||
|
||||
override val id = ID
|
||||
|
||||
constructor(pokemon: Pokemon, value: TemporaryForm) : this({ pokemon }, value.convertToDisplay(pokemon))
|
||||
|
||||
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
|
||||
this.value.encode(buffer)
|
||||
}
|
||||
|
||||
override fun set(pokemon: Pokemon, value: FormDisplay) {
|
||||
pokemon.formProxy.client().remove(value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("remove_form")
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RemoveFormPacket(decodePokemon(buffer), FormDisplay.decode(buffer))
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -31,7 +31,7 @@ class SpawnPokemonPacket(
|
||||
var speciesId: ResourceLocation,
|
||||
var gender: Gender,
|
||||
var shiny: Boolean,
|
||||
var formName: String,
|
||||
var formId: String,
|
||||
var aspects: Set<String>,
|
||||
var battleId: UUID?,
|
||||
var phasingTargetId: Int,
|
||||
@@ -87,7 +87,7 @@ class SpawnPokemonPacket(
|
||||
buffer.writeIdentifier(this.speciesId)
|
||||
buffer.writeEnumConstant(this.gender)
|
||||
buffer.writeBoolean(this.shiny)
|
||||
buffer.writeString(this.formName)
|
||||
buffer.writeString(this.formId)
|
||||
buffer.writeCollection(this.aspects) { pb, value -> pb.writeString(value) }
|
||||
buffer.writeNullable(this.battleId) { pb, value -> pb.writeUUID(value) }
|
||||
buffer.writeInt(this.phasingTargetId)
|
||||
@@ -114,7 +114,7 @@ class SpawnPokemonPacket(
|
||||
species = this@SpawnPokemonPacket.speciesId.let { PokemonSpecies.getByIdentifier(it) ?: PokemonSpecies.random() }
|
||||
gender = this@SpawnPokemonPacket.gender
|
||||
shiny = this@SpawnPokemonPacket.shiny
|
||||
form = this@SpawnPokemonPacket.formName.let { formName -> species.forms.find { it.formOnlyShowdownId() == formName }} ?: species.standardForm
|
||||
form = this@SpawnPokemonPacket.formId.let { formId -> species.forms.find { it.formOnlyShowdownId() == formId }} ?: species.standardForm
|
||||
forcedAspects = this@SpawnPokemonPacket.aspects
|
||||
nickname = this@SpawnPokemonPacket.nickname
|
||||
this@SpawnPokemonPacket.mark?.let {activeMark = Marks.getByIdentifier(it) }
|
||||
@@ -133,7 +133,7 @@ class SpawnPokemonPacket(
|
||||
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, spawnYaw)
|
||||
entity.entityData.set(PokemonEntity.FRIENDSHIP, friendship)
|
||||
entity.entityData.set(PokemonEntity.FREEZE_FRAME, freezeFrame)
|
||||
|
||||
entity.entityData.set(PokemonEntity.FORM, formId)
|
||||
entity.ejectPassengers()
|
||||
passengers.forEach {
|
||||
val passenger = level.getEntity(it) ?: return@forEach
|
||||
|
||||
+1
-1
@@ -9,8 +9,8 @@
|
||||
package com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.cobblemon.mod.common.net.messages.server.pokemon.update.form
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.FormDisplay
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import java.util.*
|
||||
|
||||
class AcceptFormPacket(val pokemonUUID: UUID, val formId: String) : NetworkPacket<AcceptFormPacket> {
|
||||
|
||||
constructor(pokemon: Pokemon, form: FormDisplay) : this(pokemon.uuid, form.id)
|
||||
|
||||
override val id = ID
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(this.pokemonUUID)
|
||||
buffer.writeString(this.formId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("accept_form")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = AcceptFormPacket(buffer.readUUID(), buffer.readString())
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.net.messages.server.pokemon.update.form
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import java.util.*
|
||||
|
||||
class RevertFormPacket(val pokemonUUID: UUID) : NetworkPacket<RevertFormPacket> {
|
||||
|
||||
override val id = ID
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(this.pokemonUUID)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("revert_form")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RevertFormPacket(buffer.readUUID())
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.cobblemon.mod.common.net.serverhandling
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.AcceptFormPacket
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object AcceptFormHandler : ServerNetworkPacketHandler<AcceptFormPacket> {
|
||||
override fun handle(packet: AcceptFormPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val pokemon = player.party().get(packet.pokemonUUID) ?: return
|
||||
val form = pokemon.formProxy.server().getById(packet.formId) ?: return
|
||||
pokemon.formProxy.server().start(form)
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.cobblemon.mod.common.net.serverhandling
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.form.RevertFormPacket
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object RevertFormHandler : ServerNetworkPacketHandler<RevertFormPacket> {
|
||||
override fun handle(packet: RevertFormPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val pokemon = player.party().get(packet.pokemonUUID) ?: return
|
||||
pokemon.formProxy.server().revert()
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.net.serverhandling.evolution
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -18,8 +17,7 @@ import net.minecraft.server.level.ServerPlayer
|
||||
object AcceptEvolutionHandler : ServerNetworkPacketHandler<AcceptEvolutionPacket> {
|
||||
override fun handle(packet: AcceptEvolutionPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val pokemon = player.party()[packet.pokemonUUID] ?: return
|
||||
if (player.isInBattle() || pokemon.entity?.isBusy == true) return
|
||||
val evolution = pokemon.evolutionProxy.server().firstOrNull { evolution -> evolution.id.equals(packet.evolutionId, true) } ?: return
|
||||
val evolution = pokemon.evolutionProxy.server().getById(packet.evolutionId) ?: return
|
||||
pokemon.evolutionProxy.server().start(evolution)
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,17 @@ import com.cobblemon.mod.common.api.abilities.CommonAbility
|
||||
import com.cobblemon.mod.common.api.abilities.PotentialAbility
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.transformation.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.riding.RidingProperties
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
@@ -36,15 +35,29 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.pokemon.ai.FormPokemonBehaviour
|
||||
import com.cobblemon.mod.common.pokemon.aspects.FORM_ASPECTS
|
||||
import com.cobblemon.mod.common.pokemon.lighthing.LightingData
|
||||
import com.cobblemon.mod.common.pokemon.transformation.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.PermanentForm
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.StandardForm
|
||||
import com.cobblemon.mod.common.pokemon.transformation.form.TemporaryForm
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class FormData(
|
||||
name: String = "Normal",
|
||||
/**
|
||||
* Represents an alternate form of a [Pokemon].
|
||||
*
|
||||
* These variations include changes to the base [Species]' stats. For strictly visual variations, see [SpeciesFeature].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since Dec 30th, 2021
|
||||
*/
|
||||
abstract class FormData(
|
||||
@SerializedName("name")
|
||||
private var _name: String? = null,
|
||||
// Internal for the sake of the base stat provider
|
||||
@SerializedName("baseStats")
|
||||
internal var _baseStats: MutableMap<Stat, Int>? = null,
|
||||
@@ -74,16 +87,12 @@ class FormData(
|
||||
private val _shoulderEffects: MutableList<ShoulderEffect>? = null,
|
||||
@SerializedName("moves")
|
||||
private var _moves: Learnset? = null,
|
||||
@SerializedName("evolutions")
|
||||
private val _evolutions: MutableSet<Evolution>? = null,
|
||||
@SerializedName("abilities")
|
||||
private var _abilities: AbilityPool? = null,
|
||||
@SerializedName("drops")
|
||||
private var _drops: DropTable? = null,
|
||||
@SerializedName("pokedex")
|
||||
private var _pokedex: MutableList<String>? = null,
|
||||
@SerializedName("preEvolution")
|
||||
private val _preEvolution: PreEvolution? = null,
|
||||
private var standingEyeHeight: Float? = null,
|
||||
private var swimmingEyeHeight: Float? = null,
|
||||
private var flyingEyeHeight: Float? = null,
|
||||
@@ -99,23 +108,16 @@ class FormData(
|
||||
private var _weight: Float? = null,
|
||||
@SerializedName("riding")
|
||||
private var _riding: RidingProperties? = null,
|
||||
val requiredMove: String? = null,
|
||||
val requiredItem: String? = null,
|
||||
/** For forms that can accept different items (e.g. Arceus-Grass: Meadow Plate or Grassium-Z). */
|
||||
val requiredItems: List<String>? = null,
|
||||
/**
|
||||
* The [MoveTemplate] of the signature attack of the G-Max form.
|
||||
* This is always null on any form aside G-Max.
|
||||
*/
|
||||
val gigantamaxMove: MoveTemplate? = null,
|
||||
@SerializedName("battleTheme")
|
||||
private var _battleTheme: ResourceLocation? = null,
|
||||
@SerializedName("lightingData")
|
||||
private var _lightingData: LightingData? = null
|
||||
// UPDATE THE SPECIES SCHEMA IF YOU MODIFY THIS! THANKS
|
||||
) : Decodable, Encodable, ShowdownIdentifiable {
|
||||
@SerializedName("name")
|
||||
var name: String = name
|
||||
private set
|
||||
|
||||
/** The name of this form. This is unique among forms belonging to the same [parentForm]. */
|
||||
val name: String
|
||||
get() = _name ?: "Standard"
|
||||
|
||||
val baseStats: Map<Stat, Int>
|
||||
get() = _baseStats ?: species.baseStats
|
||||
@@ -163,11 +165,9 @@ class FormData(
|
||||
val drops: DropTable
|
||||
get() = _drops ?: species.drops
|
||||
|
||||
/** The aspects applied by this form. For application, see [FORM_ASPECTS]. */
|
||||
var aspects = mutableListOf<String>()
|
||||
|
||||
val preEvolution: PreEvolution?
|
||||
get() = _preEvolution ?: species.preEvolution
|
||||
|
||||
val behaviour = FormPokemonBehaviour()
|
||||
|
||||
val dynamaxBlocked: Boolean
|
||||
@@ -194,12 +194,49 @@ class FormData(
|
||||
get() = _labels ?: species.labels
|
||||
|
||||
/**
|
||||
* Contains the evolutions of this form.
|
||||
* The evolution preceding this form.
|
||||
*
|
||||
* Do not access this property immediately after a species is loaded, it requires all species in the game to be loaded.
|
||||
* To be aware of this gamestage subscribe to [PokemonSpecies.observable].
|
||||
*/
|
||||
val evolutions: MutableSet<Evolution>
|
||||
get() = _evolutions ?: mutableSetOf()
|
||||
abstract val preEvolution: PreEvolution?
|
||||
|
||||
/**
|
||||
* The evolutions of this form.
|
||||
*
|
||||
* Do not access this property immediately after a species is loaded, it requires all species in the game to be loaded.
|
||||
* To be aware of this gamestage subscribe to [PokemonSpecies.observable].
|
||||
*/
|
||||
abstract val evolutions: MutableSet<Evolution>
|
||||
|
||||
/** The [TemporaryForm]s that are interchangeable with this form. */
|
||||
abstract val changeableForms: Collection<TemporaryForm>
|
||||
|
||||
/**
|
||||
* The [PermanentForm] that precedes this form. Null if a parent form does not exist.
|
||||
*
|
||||
* For example the parent of Toxtricity's 'Gmax' form can be either its 'Low-Key' or 'Standard' form.
|
||||
* The parent of Toxtricity's 'Low-Key' form is its 'Standard' form.
|
||||
* And the parent of Toxtricity's 'Standard' form is null (does not exist).
|
||||
*
|
||||
* This is initialized during species registration, and is not safe to access until [PokemonSpecies.observable] is emitted.
|
||||
*/
|
||||
@delegate:Transient
|
||||
open val parentForm: PermanentForm? by lazy { parentFormInitializer }
|
||||
|
||||
/**
|
||||
* The base [Species] of this form.
|
||||
*
|
||||
* This is initialized during species registration, and is not safe to access until [PokemonSpecies.observable] is emitted.
|
||||
*/
|
||||
@delegate:Transient
|
||||
val species: Species by lazy { speciesInitializer }
|
||||
|
||||
@Transient
|
||||
protected open var parentFormInitializer: PermanentForm? = null
|
||||
|
||||
@Transient
|
||||
private lateinit var speciesInitializer: Species
|
||||
|
||||
val battleTheme: ResourceLocation
|
||||
get() = _battleTheme ?: species.battleTheme
|
||||
@@ -236,30 +273,17 @@ class FormData(
|
||||
else -> this.standingEyeHeight
|
||||
}
|
||||
|
||||
@Transient
|
||||
lateinit var species: Species
|
||||
|
||||
fun initialize(species: Species): FormData {
|
||||
this.species = species
|
||||
open fun initialize(species: Species, parent: PermanentForm? = null): FormData {
|
||||
this.speciesInitializer = species
|
||||
this.species
|
||||
this.parentFormInitializer = parent
|
||||
this.parentForm
|
||||
this.behaviour.parent = species.behaviour
|
||||
Cobblemon.statProvider.provide(this)
|
||||
// These properties are lazy, these need all species to be reloaded but SpeciesAdditions is what will eventually trigger this after all species have been loaded
|
||||
this.preEvolution?.species
|
||||
this.preEvolution?.form
|
||||
this.evolutions.size
|
||||
this._lightingData?.let { this._lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun resolveEvolutionMoves() {
|
||||
this.evolutions.forEach { evolution ->
|
||||
if (evolution.learnableMoves.isNotEmpty() && evolution.result.species != null) {
|
||||
val pokemon = evolution.result.create()
|
||||
pokemon.form.moves.evolutionMoves += evolution.learnableMoves
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is FormData && other.showdownId() == this.showdownId()
|
||||
|
||||
override fun hashCode(): Int = this.showdownId().hashCode()
|
||||
@@ -302,7 +326,7 @@ class FormData(
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.name = buffer.readString()
|
||||
this._name = buffer.readString()
|
||||
this.aspects = buffer.readList { buffer.readString() }.toMutableList()
|
||||
buffer.readNullable { mapBuffer ->
|
||||
this._baseStats = mapBuffer.readMap(
|
||||
@@ -339,20 +363,52 @@ class FormData(
|
||||
}
|
||||
|
||||
/**
|
||||
* The literal Showdown ID of this [formOnlyShowdownId] appended to [Species.showdownId].
|
||||
* For example Alolan Vulpix becomes 'vulpixalola'
|
||||
* The literal Showdown ID of this form. This will be a lowercase version of the [name] with all the non-alphanumeric characters removed.
|
||||
* If this is a nested form, this will be prepended by the formOnlyShowdownId of the [parentForm].
|
||||
* For example Toxtricity-Low-Key-Gmax becomes 'lowkeygmax' and Vulpix-Alola becomes 'alola'.
|
||||
*
|
||||
* @return The literal Showdown ID of this species and form.
|
||||
* NOTE: this identifier is used for serializing/deserializing the form property of [PokemonProperties],
|
||||
* and should only be used for uniquely identifying forms of the SAME species.
|
||||
*
|
||||
* @return The literal Showdown ID of this form only. If this is the [Species.standardForm], this will be an empty
|
||||
* string.
|
||||
*/
|
||||
fun formOnlyShowdownId(): String = this.parentForm?.let { parent -> parent.formOnlyShowdownId() + ShowdownIdentifiable.REGEX.replace(this.name.lowercase(), "") } ?: ""
|
||||
|
||||
// TODO - will including 'standard' cause breakage?
|
||||
|
||||
/**
|
||||
* The complete Showdown ID of this form. This is a concatenation of [Species.showdownId] with [formOnlyShowdownId].
|
||||
* For example Toxtricity-Low-Key-Gmax becomes 'toxtricitylowkeygmax'
|
||||
*
|
||||
* NOTE: this identifier should be used for uniquely identifying forms of DIFFERENT species.
|
||||
*
|
||||
* @return The complete Showdown ID of this species and form.
|
||||
*/
|
||||
override fun showdownId(): String = this.species.showdownId() + this.formOnlyShowdownId()
|
||||
|
||||
/**
|
||||
* The literal Showdown ID of this form [name].
|
||||
* This will be a lowercase version of the [name] with all the non-alphanumeric characters removed.
|
||||
* For example Alolan Vulpix becomes 'alola'
|
||||
*
|
||||
* @return The literal Showdown ID of this form only.
|
||||
*/
|
||||
fun formOnlyShowdownId(): String = ShowdownIdentifiable.REGEX.replace(this.name.lowercase(), "")
|
||||
// pokemon.showdownID expects: this.species.showdownID
|
||||
|
||||
/**
|
||||
* The complete Showdown name of this form. This is a concatenation of the appended to [Species.showdownName].
|
||||
* For example form 'Gmax' of parent form 'Low-Key' of species 'Toxtricity' becomes 'Toxtricity-Low-Key-Gmax'.
|
||||
*
|
||||
* NOTE: [StandardForm]s do not exist in Showdown and will instead default to [Species.showdownName].
|
||||
*
|
||||
* @return The Showdown name of this species and form.
|
||||
*/
|
||||
open fun showdownName(): String = this.parentForm?.let { parent -> parent.showdownName() + "-" + this.name } ?: this.species.showdownName()
|
||||
|
||||
// TODO - these only work when retrieved at runtime because setting them on deserializaiton gives defaults... maybe by lazy?
|
||||
|
||||
/**
|
||||
* Checks if two forms are interchangeable with each other.
|
||||
* This covers the following scenarios:
|
||||
* 1. This form is a [PermanentForm] and contains the other as a [TemporaryForm]
|
||||
* 2. Other form is a [PermanentForm] and contains this form as a [TemporaryForm]
|
||||
* 3. Both forms are [TemporaryForm]s and have the same [parentForm]
|
||||
*
|
||||
* @return Whether [other] is interchangeable with this form.
|
||||
*/
|
||||
fun isInterchangeableWith(other: FormData) = this.changeableForms.any { it === other } || other.changeableForms.any { it === this }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user