diff --git a/CHANGELOG.md b/CHANGELOG.md index f385b4bed7..db17d75762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,35 @@ ### Particles #### New Move Animations +- Fixed shiny pokemon under certain circumstances forgetting taught TMs +- Fixed a potential world generation stall caused by Type Gem clusters. +- Fixed Pokémon on shoulders not animating if they are visibly holding a held item. +- Fixed NPCs getting their own TM progress file. +- Fixed odd NeoForge crash due to a dependency being outdated. It will now correctly flag the dependency for being outdated. +- Fixed the TM Machine refusing move selections that the `unlockAllMoveDexMovesByDefault` config had made visible, leaving the move slot blank and the craft button unresponsive. +- Fixed the Beast Ball not applying its 0.1× penalty to Pokémon that are not Ultra Beasts. +- Fixed the Moon Ball reading the world's total elapsed time instead of the time of day, so its bonus applied during the day instead. +- Fixed the Love Ball never awarding its 2.5× opposite gender bonus, and matching species by display name instead of identifier. +- Fixed the Timer Ball being weaker than a regular Poké Ball on early turns and only reaching 4× on turn 14 instead of turn 10. +- Fixed the Luxury Ball only doubling Friendship gains of 1, leaving level up and berry gains unaffected. +- Fixed Rufflet, Braviary, and Hisuian Braviary flooding logs with errors about missing sound events. +- Fixed Pokémon property typos in Pangoro and Dedenne herd spawn files. +- Fixed unintentional watermelon cosmetic for Hisuian Electrode. +- Fixed Blastoise having an unintentional passenger seat. +- Fixed form typo in Rockruff-Dusk spawn, preventing it from being able to spawn. +- Fixed the `stat_compare` and `stat_equal` evolution requirements crashing the game when given `special_attack` or `special_defence`, and made them treat any non-permanent stat as a failed requirement instead of crashing. +- Fixed Rowap and Jacoba berries not correctly altering Pokémon sizes when used as bait. +- Fixed Ampharos' eyes not emitting particles. +- Fixed Alolan Raticate's cry not playing. +- Fixed several incorrect light-based spawning conditions for ghosts and bugs. +- Removed unused, placeholder habitat pools. +- Fixed Mantyke, Mantine, and Quaxly getting stuck in a recoil 'expression' during battles due to having an expression assigned as a recoil animation. +- Fixed Lucario's aura particles not emitting. +- Fixed Lycanroc's head movement. +- Added head movement to Archaludon. +- Fixed Duraludon line not having their sleep animations. +- Fixed Diglett line not having their faint animations. +- Fixed Kingambit moving its legs while walking. ### Localization - Updated translations for: \ No newline at end of file diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP1.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP1.kt index cf3c32ce07..82fa8a0ae0 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP1.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP1.kt @@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.abilities.Abilities import com.cobblemon.mod.common.api.abilities.Ability import com.cobblemon.mod.common.api.moves.BenchedMoves import com.cobblemon.mod.common.api.moves.MoveSet +import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures import com.cobblemon.mod.common.pokemon.EVs import com.cobblemon.mod.common.pokemon.FormData import com.cobblemon.mod.common.pokemon.Gender @@ -21,20 +22,22 @@ import com.cobblemon.mod.common.pokemon.Pokemon import com.cobblemon.mod.common.pokemon.Species import com.cobblemon.mod.common.util.DataKeys import com.cobblemon.mod.common.util.codec.CodecUtils +import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURES +import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURE_ID import com.mojang.serialization.Codec import com.mojang.serialization.MapCodec import com.mojang.serialization.codecs.RecordCodecBuilder -import java.util.Optional import java.util.UUID import net.minecraft.core.UUIDUtil -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.StringTag +import net.minecraft.nbt.Tag internal class PokemonP1( val uuid: UUID, val species: Species, val form: FormData, - val nickname: Optional, + val features: List, val level: Int, val experience: Int, val friendship: Int, @@ -55,7 +58,22 @@ internal class PokemonP1( other.ability = this.ability other.species = this.species other.form = this.form - this.nickname.ifPresent { other.nickname = it.copy() } + this.features.forEach { featureNbt -> + val featureId = featureNbt.getString(FEATURE_ID) + if (featureId.isEmpty()) { + return@forEach + } + val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(other.species) + val feature = speciesFeatureProviders.firstNotNullOfOrNull { provider -> provider(featureNbt) } ?: return@forEach + if ( + featureNbt.contains("keys", Tag.TAG_STRING.toInt()) && + !featureNbt.getList("keys", Tag.TAG_STRING.toInt()).contains(StringTag.valueOf(featureId)) + ) { + return@forEach + } + other.features.removeIf { it.name == feature.name } + other.features.add(feature) + } other.level = this.level other.experience = this.experience other.setFriendship(this.friendship) @@ -91,7 +109,7 @@ internal class PokemonP1( UUIDUtil.LENIENT_CODEC.fieldOf(DataKeys.POKEMON_UUID).forGetter(PokemonP1::uuid), Species.BY_IDENTIFIER_CODEC.fieldOf(DataKeys.POKEMON_SPECIES_IDENTIFIER).forGetter(PokemonP1::species), Codec.STRING.fieldOf(DataKeys.POKEMON_FORM_ID).forGetter { pokemon -> pokemon.form.formOnlyShowdownId() }, - ComponentSerialization.CODEC.optionalFieldOf(DataKeys.POKEMON_NICKNAME).forGetter(PokemonP1::nickname), + Codec.list(CompoundTag.CODEC).optionalFieldOf(FEATURES, emptyList()).forGetter(PokemonP1::features), CodecUtils.dynamicIntRange(1) { Cobblemon.config.maxPokemonLevel }.fieldOf(DataKeys.POKEMON_LEVEL).forGetter(PokemonP1::level), Codec.intRange(0, Int.MAX_VALUE).fieldOf(DataKeys.POKEMON_EXPERIENCE).forGetter(PokemonP1::experience), CodecUtils.dynamicIntRange(0) { Cobblemon.config.maxPokemonFriendship }.fieldOf(DataKeys.POKEMON_FRIENDSHIP).forGetter(PokemonP1::friendship), @@ -104,9 +122,9 @@ internal class PokemonP1( Codec.FLOAT.fieldOf(DataKeys.POKEMON_SCALE_MODIFIER).forGetter(PokemonP1::scaleModifier), Codec.BOOL.fieldOf(DataKeys.POKEMON_SHINY).forGetter(PokemonP1::shiny), Ability.CODEC.fieldOf(DataKeys.POKEMON_ABILITY).forGetter(PokemonP1::ability) - ).apply(instance) { uuid, species, formId, nickname, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability -> + ).apply(instance) { uuid, species, formId, features, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability -> val form = species.forms.firstOrNull { it.formOnlyShowdownId() == formId } ?: species.standardForm - PokemonP1(uuid, species, form, nickname, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability) + PokemonP1(uuid, species, form, features, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability) } } @@ -114,7 +132,11 @@ internal class PokemonP1( pokemon.uuid, pokemon.species, pokemon.form, - Optional.ofNullable(pokemon.nickname), + pokemon.features.map { feature -> + val nbt = CompoundTag() + nbt.putString(FEATURE_ID, feature.name) + feature.saveToNBT(nbt) + }, pokemon.level, pokemon.experience, pokemon.friendship, diff --git a/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP3.kt b/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP3.kt index 84184514b0..e56f92c1fd 100644 --- a/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP3.kt +++ b/common/src/main/kotlin/com/cobblemon/mod/common/util/codec/internal/PokemonP3.kt @@ -14,15 +14,12 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat import com.cobblemon.mod.common.pokemon.OriginalTrainerType import com.cobblemon.mod.common.pokemon.Pokemon import com.cobblemon.mod.common.util.DataKeys -import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURES -import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURE_ID import com.cobblemon.mod.common.util.codec.optionalFieldOfWithDefault import com.mojang.serialization.Codec import com.mojang.serialization.MapCodec import com.mojang.serialization.codecs.RecordCodecBuilder -import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.StringTag -import net.minecraft.nbt.Tag +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.ItemStack import java.util.* @@ -31,7 +28,7 @@ internal data class PokemonP3( val originalTrainerType: OriginalTrainerType, val originalTrainer: Optional, val forcedAspects: Set, - val features: List, + val nickname: Optional, val heldItemVisible: Optional, val canDropHeldItem: Optional, val cosmeticItem: ItemStack, @@ -51,22 +48,7 @@ internal data class PokemonP3( this.originalTrainer.ifPresent { other.originalTrainer = it } other.refreshOriginalTrainer() other.forcedAspects = this.forcedAspects - this.features.forEach { featureNbt -> - val featureId = featureNbt.getString(FEATURE_ID) - if (featureId.isEmpty()) { - return@forEach - } - val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(other.species) - val feature = speciesFeatureProviders.firstNotNullOfOrNull { provider -> provider(featureNbt) } ?: return@forEach - if ( - featureNbt.contains("keys", Tag.TAG_STRING.toInt()) && - !featureNbt.getList("keys", Tag.TAG_STRING.toInt()).contains(StringTag.valueOf(featureId)) - ) { - return@forEach - } - other.features.removeIf { it.name == feature.name } - other.features.add(feature) - } + this.nickname.ifPresent { other.nickname = it.copy() } this.heldItemVisible.ifPresent { other.heldItemVisible = it } this.canDropHeldItem.ifPresent { other.canDropHeldItem = it } this.cosmeticItem.let { other.cosmeticItem = it } @@ -92,7 +74,7 @@ internal data class PokemonP3( OriginalTrainerType.CODEC.optionalFieldOfWithDefault(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, OriginalTrainerType.NONE).forGetter(PokemonP3::originalTrainerType), Codec.STRING.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER).forGetter(PokemonP3::originalTrainer), Codec.list(Codec.STRING).optionalFieldOf(DataKeys.POKEMON_FORCED_ASPECTS, emptyList()).forGetter { it.forcedAspects.toMutableList() }, - Codec.list(CompoundTag.CODEC).optionalFieldOf(FEATURES, emptyList()).forGetter(PokemonP3::features), + ComponentSerialization.CODEC.optionalFieldOf(DataKeys.POKEMON_NICKNAME).forGetter(PokemonP3::nickname), Codec.BOOL.optionalFieldOf(DataKeys.HELD_ITEM_VISIBLE).forGetter(PokemonP3::heldItemVisible), Codec.BOOL.optionalFieldOf(DataKeys.HELD_ITEM_AI_DROPPABLE).forGetter(PokemonP3::canDropHeldItem), ItemStack.CODEC.optionalFieldOf(DataKeys.POKEMON_COSMETIC_ITEM).forGetter { Optional.ofNullable(it.cosmeticItem.takeIf { !it.isEmpty }) }, @@ -109,7 +91,7 @@ internal data class PokemonP3( originalTrainerType, originalTrainer, forcedAspects, - features, + nickname, heldItemVisible, canDropHeldItem, cosmeticItem, @@ -125,7 +107,7 @@ internal data class PokemonP3( originalTrainerType, originalTrainer, forcedAspects.toSet(), - features, + nickname, heldItemVisible, canDropHeldItem, cosmeticItem.orElse(ItemStack.EMPTY), @@ -146,11 +128,7 @@ internal data class PokemonP3( pokemon.originalTrainerType, Optional.ofNullable(pokemon.originalTrainer), pokemon.forcedAspects, - pokemon.features.map { feature -> - val nbt = CompoundTag() - nbt.putString(FEATURE_ID, feature.name) - feature.saveToNBT(nbt) - }, + Optional.ofNullable(pokemon.nickname), Optional.ofNullable(pokemon.heldItemVisible), Optional.ofNullable(pokemon.canDropHeldItem), pokemon.cosmeticItem,