Merge branch 'form-move-fix' into 'implement_pokemon'

Form Move Fix

See merge request cable-mc/cobblemon!2420
This commit is contained in:
Polymeta
2026-10-03 22:33:27 +00:00
6 changed files with 45 additions and 16 deletions
+3
View File
@@ -15,6 +15,9 @@
- Wearable items that can be placed down are exempt from this behaviour.
- New Cosmetic Item for Pikachu: Paper
### Fixes
- Fixed #updateMovesOnFormChange not properly initializing a form-swapped Pokemon's non-form_change moves.
### TEST
### Pokémon Added
#### Generation 4
- Snover
@@ -211,8 +211,9 @@ open class Pokemon : ShowdownIdentifiable {
// Species updates already update HP but just a form change may require it
// Moved to before the field was set else it won't actually do the hp calc proper <3
val quotient = clamp(currentHealth / maxHealth.toFloat(), 0F, 1F)
val oldForm = field
field = value
this.updateMovesOnFormChange(value)
this.updateMovesOnFormChange(oldForm)
// Evo proxy is already cleared on species update but the form may be changed by itself, this is fine and no unnecessary packets will be sent out
this.evolutionProxy.current().clear()
checkGender()
@@ -2301,15 +2302,16 @@ open class Pokemon : ShowdownIdentifiable {
*
* E.g, Removing Overheat from Rotom when it is no longer Rotom-Heat and the target form is not compatible with Overheat.
*/
private fun updateMovesOnFormChange(newForm: FormData) {
private fun updateMovesOnFormChange(oldForm: FormData) {
if (this.isClient) return
val oldFormChangeMoves = this.form.moves.formChangeMoves.toSet()
val newForm = this.form
val oldFormChangeMoves = oldForm.moves.formChangeMoves.toSet()
// Only remove moves that were provided by the old form's form-change moves AND are not learnable by the new form
for (i in 0 until MoveSet.MOVE_COUNT) {
val move = this.moveSet[i]
if (move != null && move.template in oldFormChangeMoves) {
if (move != null && move.template in oldFormChangeMoves && !LearnsetQuery.ANY.canLearn(move.template, newForm.moves)) {
this.moveSet.setMove(i, null)
}
}
@@ -2322,16 +2324,8 @@ open class Pokemon : ShowdownIdentifiable {
}
}
// Add new form's form-change moves
newForm.moves.formChangeMoves.forEach { move ->
if (this.moveSet.filterNotNull().none { it.template == move }) {
if (this.moveSet.hasSpace()) {
this.moveSet.add(move.create())
} else {
this.benchedMoves.add(BenchedMove(move, 0))
}
}
}
// Add the new form's form-change moves (skipping any the Pokémon isn't high enough level for yet).
grantEligibleFormChangeMoves()
// If moveset is empty try to find one valid move to fill it
if (this.moveSet.filterNotNull().isEmpty()) {
@@ -2343,6 +2337,30 @@ open class Pokemon : ShowdownIdentifiable {
}
}
/**
* Grants any of the current form's `form_change` moves that this Pokémon doesn't already know and is high
* enough level for (form-change moves that are also level-up moves, e.g. Glaciate on Kyurem, shouldn't be
* granted until that level is reached).
*/
private fun grantEligibleFormChangeMoves() {
if (this.isClient) return
this.form.moves.formChangeMoves.forEach { move ->
if (this.moveSet.filterNotNull().none { it.template == move } && this.benchedMoves.none { it.moveTemplate == move }) {
val requiredLevel = this.form.moves.levelUpMoves.entries.firstOrNull { move in it.value }?.key
if (requiredLevel != null && this.level < requiredLevel) {
return@forEach
}
if (this.moveSet.hasSpace()) {
this.moveSet.add(move.create())
} else {
this.benchedMoves.add(BenchedMove(move, 0))
}
}
}
}
companion object {
/**
* The [FriendshipMutationCalculator] used when a Pokémon levels up.
@@ -94,6 +94,7 @@
"19:hypnosis",
"23:dizzypunch",
"28:suckerpunch",
"32:teeterdance",
"37:uproar",
"41:psychup",
"46:doubleedge",
@@ -163,7 +164,6 @@
"tm:sunnyday",
"tm:superpower",
"tm:swift",
"tm:teeterdance",
"tm:terablast",
"tm:thief",
"tm:thunderpunch",
@@ -66,6 +66,7 @@
"72:outrage",
"80:glaciate",
"88:sheercold",
"form_change:glaciate",
"tm:aerialace",
"tm:ancientpower",
"tm:avalanche",
@@ -235,6 +236,8 @@
"72:outrage",
"80:iceburn",
"88:sheercold",
"form_change:fusionflare",
"form_change:iceburn",
"tm:aerialace",
"tm:ancientpower",
"tm:avalanche",
@@ -427,6 +430,8 @@
"72:outrage",
"80:freezeshock",
"88:sheercold",
"form_change:fusionbolt",
"form_change:freezeshock",
"tm:aerialace",
"tm:ancientpower",
"tm:avalanche",
@@ -69,6 +69,7 @@
"68:nastyplot",
"75:psychic",
"85:hyperspacehole",
"form_change:hyperspacehole",
"tm:allyswitch",
"tm:block",
"tm:brickbreak",
@@ -194,7 +195,6 @@
"legacy:doubleteam",
"legacy:frustration",
"legacy:hiddenpower",
"legacy:hyperspacefury",
"legacy:return",
"legacy:round",
"legacy:secretpower",
@@ -249,6 +249,7 @@
"55:darkpulse",
"75:psychic",
"85:hyperspacefury",
"form_change:hyperspacefury",
"tm:allyswitch",
"tm:block",
"tm:brickbreak",
@@ -222,6 +222,7 @@
"72:healpulse",
"80:solarbeam",
"88:futuresight",
"form_change:glaciallance",
"tm:agility",
"tm:allyswitch",
"tm:assurance",
@@ -448,6 +449,7 @@
"72:healpulse",
"80:solarbeam",
"88:futuresight",
"form_change:astralbarrage",
"tm:agility",
"tm:allyswitch",
"tm:assurance",