Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d0baab2ba | ||
|
|
767e279cf1 | ||
|
|
6ace08aaa6 | ||
|
|
ba8e40d5a7 | ||
|
|
acf467c8a3 | ||
|
|
0ff5b95c44 | ||
|
|
8d7ebeef4f | ||
|
|
d9de8f2678 |
No files matched your search
+23
-20
@@ -135,28 +135,31 @@ class BattleMessage(rawMessage: String) {
|
||||
return this.actorAndActivePokemon(pnx, battle)
|
||||
}
|
||||
|
||||
/*
|
||||
Example:
|
||||
|-curestatus|p1: a8e5710e-6835-4387-99d3-331b45f848e6|slp|[msg]
|
||||
|-curestatus|p1: 0686b20d-3019-4167-92e2-786834245743|slp|[msg]
|
||||
// Function used to grab BattlePokemon (As we need to be able to grab non active pokemon to cure their status)
|
||||
// by using playerNumber(actorID) and pokemon uuid(pokemonID)
|
||||
/**
|
||||
* Queries an argument at the given [index] for a 'pnx' and uuid that will be parsed into a [BattlePokemon].
|
||||
*
|
||||
* @param index The index of the argument referencing the [BattlePokemon].
|
||||
* @param battle The [PokemonBattle] being queried.
|
||||
* @return The [BattlePokemon] if the argument exists and is successfully parsed; otherwise null.
|
||||
*/
|
||||
fun getBattlePokemon(index: Int, battle: PokemonBattle): BattlePokemon? {
|
||||
val actorAndPokemonID = this.argumentAt(index)?.takeIf { it.length >= 2 }?.split(": ") ?: return null
|
||||
if (actorAndPokemonID.count() < 2) return null
|
||||
val actorID = actorAndPokemonID[0]
|
||||
val pokemonID = actorAndPokemonID[1]
|
||||
return this.getBattlePokemon(actorID, pokemonID, battle)
|
||||
fun battlePokemon(index: Int, battle: PokemonBattle): BattlePokemon? {
|
||||
val (actorID, pokemonID) = this.pnxAndUuid(index) ?: return null
|
||||
return this.battlePokemon(actorID, pokemonID, battle)
|
||||
}
|
||||
|
||||
fun getSourceBattlePokemon(battle: PokemonBattle): BattlePokemon? {
|
||||
val sourcePokemonArgument = this.optionalArguments.get("of") ?: return null
|
||||
val actorAndPokemonID = sourcePokemonArgument.takeIf { it.length >= 2 }?.split(": ") ?: return null
|
||||
if (actorAndPokemonID.count() < 2) return null
|
||||
val actorID = actorAndPokemonID[0]
|
||||
val pokemonID = actorAndPokemonID[1]
|
||||
return this.getBattlePokemon(actorID, pokemonID, battle)
|
||||
/**
|
||||
* Queries an optional argument identified by [optionalArg] for a 'pnx' and uuid that will be parsed into a [BattlePokemon].
|
||||
*
|
||||
* @param optionalArg The id of the optional argument referencing the [BattlePokemon].
|
||||
* @param battle The [PokemonBattle] being queried.
|
||||
* @return The [BattlePokemon] if the argument exists and is successfully parsed; otherwise null.
|
||||
*/
|
||||
fun battlePokemonFromOptional(battle: PokemonBattle, optionalArg: String = "of"): BattlePokemon? {
|
||||
val optional = this.optionalArguments.get(optionalArg) ?: return null
|
||||
val pokemonID = optional.takeIf { it.length >= 2 }?.split(":")?.takeIf { it.size == 2 } ?: return null
|
||||
val pnx = pokemonID[0].takeIf { it.matches(PNX_MATCHER) || it.matches(PN_MATCHER) } ?: return null
|
||||
val uuid = pokemonID[1].trim()
|
||||
return this.battlePokemon(pnx, uuid, battle)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,7 +237,7 @@ class BattleMessage(rawMessage: String) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun getBattlePokemon(pnx: String, pokemonID: String, battle: PokemonBattle): BattlePokemon? = try {
|
||||
private fun battlePokemon(pnx: String, pokemonID: String, battle: PokemonBattle): BattlePokemon? = try {
|
||||
battle.getBattlePokemon(pnx, pokemonID)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.Environment
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
@@ -22,8 +20,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
@@ -44,7 +40,6 @@ import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
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.net.messages.client.battle.BattleMusicPacket
|
||||
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
|
||||
@@ -107,6 +102,11 @@ open class PokemonBattle(
|
||||
var turn: Int = 1
|
||||
private set
|
||||
|
||||
private var ticks: Int = 0
|
||||
set(value) { field = value.coerceAtMost(Int.MAX_VALUE) } // go outside
|
||||
|
||||
/** The current duration of the battle in seconds. */
|
||||
val time: Int get() = ticks % 20
|
||||
|
||||
var dispatchResult = GO
|
||||
val dispatches = ConcurrentLinkedDeque<BattleDispatch>()
|
||||
@@ -166,7 +166,7 @@ open class PokemonBattle(
|
||||
fun getActor(player: ServerPlayerEntity) = actors.firstOrNull { it.isForPlayer(player) }
|
||||
|
||||
/**
|
||||
* Gets a BattleActor and an [ActiveBattlePokemon] from a pnx key, e.g. p2a
|
||||
* Gets a [BattleActor] and an [ActiveBattlePokemon] from a pnx key, e.g. p2a
|
||||
*
|
||||
* Returns null if either the pn or x is invalid.
|
||||
*/
|
||||
@@ -179,6 +179,11 @@ open class PokemonBattle(
|
||||
return actor to pokemon
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a [BattlePokemon] from a pnx key and uuid.
|
||||
*
|
||||
* Returns null if the pnx key is invalid or the uuid does not exist.
|
||||
*/
|
||||
fun getBattlePokemon(pnx: String, pokemonID: String): BattlePokemon {
|
||||
val actor = actors.find { it.showdownId == pnx.substring(0, 2) }
|
||||
?: throw IllegalStateException("Invalid pnx: $pnx - unknown actor")
|
||||
@@ -390,8 +395,9 @@ open class PokemonBattle(
|
||||
return
|
||||
}
|
||||
|
||||
if (started && isPvW && !ended && dispatches.isEmpty()) {
|
||||
checkFlee()
|
||||
if (started) {
|
||||
ticks++
|
||||
if (isPvW && !ended && dispatches.isEmpty()) checkFlee()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.cobblemon.mod.common.api.entity.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.TransformEffect
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Represents a temporary alteration to how a [PokemonEntity] is rendered and behaves. May include a temporary visual effect
|
||||
* when applied.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 5th, 2024
|
||||
*/
|
||||
interface EntityEffect {
|
||||
|
||||
/**
|
||||
* Starts this effect for the provided [PokemonEntity].
|
||||
*
|
||||
* @return A [CompletableFuture] that completes after the effect has been applied. Or null if the effect failed to start.
|
||||
*/
|
||||
fun start(entity: PokemonEntity): CompletableFuture<PokemonEntity>?
|
||||
|
||||
/**
|
||||
* Ends this effect for the provided [PokemonEntity].
|
||||
*
|
||||
* @return A [CompletableFuture] that completes after the effect has been reverted. Or null if the effect failed to end.
|
||||
*/
|
||||
fun end(entity: PokemonEntity): CompletableFuture<PokemonEntity>?
|
||||
|
||||
/** Saves this effect to NBT. */
|
||||
fun saveToNbt(): NbtCompound
|
||||
|
||||
/** Loads this effect from NBT. */
|
||||
fun loadFromNBT(nbt: NbtCompound)
|
||||
|
||||
companion object {
|
||||
|
||||
private val effects = mutableMapOf<String, KClass<out EntityEffect>>()
|
||||
private val defaults = mutableMapOf<String, () -> EntityEffect>()
|
||||
|
||||
init {
|
||||
register(IllusionEffect.ID, IllusionEffect::class, ::IllusionEffect)
|
||||
register(TransformEffect.ID, TransformEffect::class, ::TransformEffect)
|
||||
}
|
||||
|
||||
fun <T : EntityEffect> register(id: String, type: KClass<T>, default: () -> T) {
|
||||
effects[id] = type
|
||||
defaults[id] = default
|
||||
}
|
||||
|
||||
fun createDefault(id: String): EntityEffect? = defaults[id]?.invoke()
|
||||
|
||||
fun loadFromNbt(nbt: NbtCompound): EntityEffect? {
|
||||
if (nbt.contains(DataKeys.ENTITY_EFFECT_ID)) {
|
||||
val id = nbt.getString(DataKeys.ENTITY_EFFECT_ID)
|
||||
return createDefault(id)?.also { it.loadFromNBT(nbt) }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** An [EntityEffect] that modifies the dimensions of a [PokemonEntity]. */
|
||||
interface PhysicalEffect : EntityEffect {
|
||||
val scale: Float
|
||||
}
|
||||
|
||||
/** An [EntityEffect] that alters the physical appearance of a [PokemonEntity] to match a [mock]. */
|
||||
interface MocKEffect : PhysicalEffect {
|
||||
val mock: PokemonProperties
|
||||
|
||||
val exposedSpecies: Species?
|
||||
get() = this.mock.species?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
|
||||
val exposedForm: FormData?
|
||||
get() = this.mock.form?.let {
|
||||
formID -> this.exposedSpecies?.forms?.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }
|
||||
?: this.exposedSpecies?.standardForm
|
||||
}
|
||||
+5
@@ -48,6 +48,11 @@ fun interface PokemonPropertyExtractor {
|
||||
@JvmField
|
||||
val STATUS = add { pokemon, properties -> properties.status = pokemon.status?.status?.showdownName }
|
||||
|
||||
@JvmField
|
||||
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY)
|
||||
@JvmField
|
||||
val TRANSFORM = mutableListOf(SPECIES, FORM, ASPECTS, GENDER)
|
||||
|
||||
fun add(extractor: PokemonPropertyExtractor): PokemonPropertyExtractor {
|
||||
ALL.add(extractor)
|
||||
return extractor
|
||||
|
||||
@@ -49,6 +49,25 @@ enum class Stats(override val identifier: Identifier, override val displayName:
|
||||
*/
|
||||
val BATTLE_ONLY: Set<Stat> = EnumSet.of(EVASION, ACCURACY)
|
||||
|
||||
/** Gets the [Stat] from the respective Showdown id. */
|
||||
fun getStat(statKey: String) = when(statKey) {
|
||||
"atk" -> ATTACK
|
||||
"def" -> DEFENCE
|
||||
"spa" -> SPECIAL_ATTACK
|
||||
"spd" -> SPECIAL_DEFENCE
|
||||
"spe" -> SPEED
|
||||
"evasion" -> EVASION
|
||||
else -> ACCURACY
|
||||
}
|
||||
|
||||
/** Gets the severity lang key from the respective boost/unboost stage. */
|
||||
fun getSeverity(stages: Int) = when(stages) {
|
||||
0 -> "cap.single"
|
||||
1 -> "slight"
|
||||
2 -> "sharp"
|
||||
else -> "severe"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -260,7 +260,7 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
return@mapNotNull if (clone) {
|
||||
BattlePokemon.safeCopyOf(it)
|
||||
} else {
|
||||
BattlePokemon(it)
|
||||
BattlePokemon.playerOwned(it)
|
||||
}
|
||||
}.sortedBy { if (it.uuid == leadingPokemon) 0 else (indexOf(it.originalPokemon) + 1) }
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class ActiveBattlePokemon(val actor: BattleActor, var battlePokemon: BattlePokem
|
||||
fun getSide() = actor.getSide()
|
||||
var position: Pair<ServerWorld, Vec3d>? = null
|
||||
|
||||
var illusion: BattlePokemon? = null
|
||||
|
||||
override fun getAllActivePokemon() = battle.activePokemon
|
||||
override fun getActorShowdownId() = actor.showdownId
|
||||
override fun getActorPokemon() = actor.activePokemon
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+8
-8
@@ -19,23 +19,23 @@ import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-ability|POKEMON|ABILITY|(from)EFFECT
|
||||
* Format: |-ability|POKEMON|ABILITY|(from)EFFECT
|
||||
*
|
||||
* The ABILITY of the POKEMON has been changed due to a move/ability EFFECT.
|
||||
* The ABILITY of POKEMON has been changed due to a move/ability EFFECT.
|
||||
*
|
||||
* Format:
|
||||
* |-ability|POKEMON|ABILITY
|
||||
* Alt format: |-ability|POKEMON|ABILITY
|
||||
*
|
||||
* POKEMON has just switched-in, and its ability ABILITY is being announced to have a long-term effect.
|
||||
* POKEMON has just switched-in, and its ABILITY is being announced to have a long-term effect.
|
||||
* @author Xylopia
|
||||
* @since January 31, 2023
|
||||
*/
|
||||
class AbilityInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val optionalEffect = message.effect()
|
||||
val optionalPokemon = message.getSourceBattlePokemon(battle)
|
||||
val optionalPokemon = message.battlePokemonFromOptional(battle)
|
||||
val optionalPokemonName = optionalPokemon?.getName()
|
||||
|
||||
// If there is an optional effect causing the activation, broadcast that instead of the standard effect
|
||||
|
||||
+8
-5
@@ -20,16 +20,19 @@ import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-activate|POKEMON|EFFECT
|
||||
* Format: |-activate|POKEMON|EFFECT
|
||||
*
|
||||
* A miscellaneous effect has activated.This is triggered whenever an effect could not be better described by one of the other minor messages.
|
||||
* A miscellaneous EFFECT has activated for POKEMON. This is triggered whenever an effect could not be better described
|
||||
* by one of the other minor messages.
|
||||
*
|
||||
* @author Hunter
|
||||
* @since September 25, 2022
|
||||
*/
|
||||
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemonName)
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |bagitem|POKEMON|ITEM
|
||||
*
|
||||
* POKEMON had ITEM used on it from the 'bag'.
|
||||
* @author landonjw
|
||||
* @since July 31, 2023
|
||||
*/
|
||||
class BagItemInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val pokemon = message.pokemonByUuid(0, battle)!!
|
||||
val item = message.argumentAt(1)!!
|
||||
|
||||
val ownerName = pokemon.actor.getName()
|
||||
val itemName = item.asTranslated()
|
||||
|
||||
battle.broadcastChatMessage(battleLang("bagitem.use", ownerName, itemName, pokemon.getName()))
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-block|POKEMON|EFFECT|MOVE|ATTACKER|(of)SOURCE
|
||||
*
|
||||
* An effect targeted at POKEMON was blocked by EFFECT. This may optionally specify that the effect was a MOVE from ATTACKER.
|
||||
*
|
||||
* An optional SOURCE will note the owner of the EFFECT, in the case that it's not EFFECT (for instance, an ally with Aroma Veil.)
|
||||
* @author jeffw773
|
||||
* @since November 6, 2023
|
||||
*/
|
||||
class BlockInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val lang = battleLang("block.$effectID", pokemonName)
|
||||
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-boost|POKEMON|STAT|AMOUNT or |-unboost|POKEMON|STAT|AMOUNT
|
||||
*
|
||||
* POKEMON has gained or lost AMOUNT in STAT, using the standard rules for stat changes in-battle.
|
||||
* STAT is a standard three-letter abbreviation fot the stat in question.
|
||||
* @author Hiroku
|
||||
* @since August 20, 2022
|
||||
*/
|
||||
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val statKey = message.argumentAt(1) ?: return
|
||||
val stages = message.argumentAt(2)?.toInt() ?: return
|
||||
val stat = Stats.getStat(statKey).displayName
|
||||
val severity = Stats.getSeverity(stages)
|
||||
val rootKey = if (isBoost) "boost" else "unboost"
|
||||
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val lang = when {
|
||||
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
|
||||
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
val boostBucket = if (isBoost) BattleContext.Type.BOOST else BattleContext.Type.UNBOOST
|
||||
val context = ShowdownInterpreter.getContextFromAction(message, boostBucket, battle)
|
||||
// TODO: replace with context that tracks detailed information such as # of stages
|
||||
repeat(stages) { pokemon.contextManager.add(context) }
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
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.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
|
||||
/**
|
||||
* Formats: |cant|POKEMON|REASON and |cant|POKEMON|REASON|MOVE
|
||||
*
|
||||
* The POKEMON could not perform a move because of the indicated REASON.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class CantInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val name = pokemon.getName()
|
||||
// Move may be null as it's not always given
|
||||
val moveName = message.moveAt(2)?.displayName ?: run { "(Unrecognized: ${message.argumentAt(2)})".text() }
|
||||
|
||||
val lang = when (effectID) {
|
||||
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
|
||||
"armortail", "damp", "dazzling", "queenlymajesty" -> battleLang("cant.generic", name, moveName)
|
||||
"par", "slp", "frz" -> {
|
||||
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatchWaiting
|
||||
lang("status.$status.is", name)
|
||||
}
|
||||
else -> battleLang("cant.$effectID", name, moveName)
|
||||
}
|
||||
|
||||
battle.broadcastChatMessage(lang.red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-clearallboost|
|
||||
*
|
||||
* Clears all boosts from all Pokémon on both sides.
|
||||
* @author Segfault Guy
|
||||
* @since April 26, 2023
|
||||
*/
|
||||
class ClearAllBoostInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
battle.activePokemon.forEach {
|
||||
it.battlePokemon?.contextManager?.clear(BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
}
|
||||
battle.broadcastChatMessage(battleLang("clearallboost"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-clearnegativeboost|POKEMON
|
||||
*
|
||||
* Clear the negative boosts from the target POKEMON (usually as the result of a zeffect).
|
||||
* @author Segfault Guy
|
||||
* @since September 10, 2023
|
||||
*/
|
||||
class ClearNegativeBoostInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val lang = when {
|
||||
message.hasOptionalArgument("zeffect") -> battleLang("clearallnegativeboost.zeffect", pokemonName)
|
||||
else -> battleLang("clearallnegativeboost", pokemonName)
|
||||
}
|
||||
if (!message.hasOptionalArgument("silent")) {
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
|
||||
battlePokemon.contextManager.clear(BattleContext.Type.UNBOOST)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-copyboost|SOURCE|TARGET|(from)EFFECT
|
||||
*
|
||||
* Copies any stat changes from the TARGET Pokémon to the SOURCE Pokémon due to EFFECT.
|
||||
* @author JMMCP
|
||||
* @since November 27, 2023
|
||||
*/
|
||||
class CopyBoostInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val targetPokemon = message.battlePokemon(1, battle) ?: return@dispatchWaiting
|
||||
val targetPokemonName = targetPokemon.getName()
|
||||
val lang = battleLang("copyboost.generic", pokemonName, targetPokemonName)
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
pokemon.contextManager.copy(targetPokemon.contextManager, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
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.text.yellow
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-crit|POKEMON
|
||||
*
|
||||
* POKEMON received a critical hit.
|
||||
* @author Hunter
|
||||
* @since August 18, 2022
|
||||
*/
|
||||
class CritInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
battle.broadcastChatMessage(battleLang("crit").yellow())
|
||||
ShowdownInterpreter.lastCauser[battle.battleId]?.let { message ->
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@let
|
||||
if (LastBattleCriticalHitsEvolutionProgress.supports(battlePokemon.effectedPokemon)) {
|
||||
val progress = battlePokemon.effectedPokemon.evolutionProxy.current().progressFirstOrCreate({ it is LastBattleCriticalHitsEvolutionProgress }) { LastBattleCriticalHitsEvolutionProgress() }
|
||||
progress.updateProgress(LastBattleCriticalHitsEvolutionProgress.Progress(progress.currentProgress().amount + 1))
|
||||
}
|
||||
}
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-curestatus|POKEMON|STATUS
|
||||
*
|
||||
* The POKEMON has recovered from its STATUS.
|
||||
* @author Hiroku
|
||||
* @since November 5, 2022
|
||||
*/
|
||||
class CureStatusInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val maybeActivePokemon = message.actorAndActivePokemon(0, battle)?.second?.battlePokemon
|
||||
val maybePartyPokemon = message.battlePokemon(0, battle)
|
||||
val pokemon = maybeActivePokemon ?: maybePartyPokemon ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val status = message.argumentAt(1)?.let(Statuses::getStatus) ?: return
|
||||
val effect = message.effect()
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemonName)
|
||||
|
||||
battle.dispatchWaiting {
|
||||
pokemon.effectedPokemon.status = null
|
||||
pokemon.sendUpdate()
|
||||
|
||||
if (maybeActivePokemon != null) {
|
||||
message.pnxAndUuid(0)?.let {
|
||||
battle.sendUpdate(BattlePersistentStatusPacket(it.first, null))
|
||||
}
|
||||
}
|
||||
val lang = when (effect?.type) {
|
||||
Effect.Type.ABILITY -> battleLang("curestatus.${effect.id}", pokemonName)
|
||||
else -> status.removeMessage.asTranslated(pokemonName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
pokemon.contextManager.remove(status.showdownName, BattleContext.Type.STATUS)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-3
@@ -19,6 +19,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.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
@@ -30,6 +31,13 @@ import java.util.concurrent.CompletableFuture
|
||||
import kotlin.math.roundToInt
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format: |-damage|POKEMON|HP STATUS
|
||||
*
|
||||
* POKEMON has taken damage and is now at HP STATUS
|
||||
* @author Hiroku
|
||||
* @since March 11, 2022
|
||||
*/
|
||||
class DamageInstruction(
|
||||
val instructionSet: InstructionSet,
|
||||
val actor: BattleActor,
|
||||
@@ -37,10 +45,10 @@ class DamageInstruction(
|
||||
val privateMessage: BattleMessage
|
||||
) : InterpreterInstruction {
|
||||
var future = CompletableFuture.completedFuture(Unit)
|
||||
val battlePokemon = publicMessage.getBattlePokemon(0, actor.battle)
|
||||
val expectedTarget = publicMessage.battlePokemon(0, actor.battle)
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battlePokemon ?: return
|
||||
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
|
||||
val recoiling = privateMessage.optionalArgument("from")?.equals("recoil", true) == true
|
||||
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
|
||||
if (recoiling) {
|
||||
@@ -59,7 +67,7 @@ class DamageInstruction(
|
||||
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
|
||||
val effect = privateMessage.effect()
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val sourceName = privateMessage.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
val sourceName = privateMessage.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
var causedFaint = newHealth == "0"
|
||||
|
||||
battle.dispatch {
|
||||
|
||||
-19
@@ -1,19 +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.battles.dispatch.InterpreterInstruction
|
||||
|
||||
class DeprecatedInstruction(val message: BattleMessage, val function: (PokemonBattle, BattleMessage, MutableList<String>) -> Unit) : InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
function(battle, message, mutableListOf())
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +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.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
class DeprecatedSplitInstruction(
|
||||
val battleActor: BattleActor,
|
||||
val publicMessage: BattleMessage,
|
||||
val privateMessage: BattleMessage,
|
||||
val function: (PokemonBattle, BattleActor, BattleMessage, BattleMessage) -> Unit
|
||||
) : InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
function(battle, battleActor, publicMessage, privateMessage)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |detailschange|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* POKEMON has changed formes permanently (i.e. Mega Evolution) to DETAILS.
|
||||
* @author Segfault Guy
|
||||
* @since September 10, 2023
|
||||
*/
|
||||
class DetailsChangeInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val formName = message.argumentAt(1)?.split(',')?.get(0)?.substringAfter('-')?.lowercase() ?: return
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("detailschange.$formName", pokemonName))
|
||||
battle.majorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
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.battles.dispatch.BattleDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |drag|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* POKEMON has switched in (if there was an old Pokémon in that position, it is switched out).
|
||||
* POKEMON|DETAILS represents all the information that can be used to tell Pokémon apart.
|
||||
* The switched Pokémon has HP health points and STATUS status.
|
||||
* @author
|
||||
*/
|
||||
class DragInstruction(val instructionSet: InstructionSet, val battleActor: BattleActor, val publicMessage: BattleMessage, val privateMessage: BattleMessage): InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchInsert {
|
||||
val (pnx, _) = publicMessage.pnxAndUuid(0)!!
|
||||
val (_, activePokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
|
||||
val illusion = publicMessage.battlePokemonFromOptional(battle , "is")
|
||||
val pokemon = publicMessage.battlePokemon(0, battle) ?: return@dispatchInsert emptySet()
|
||||
|
||||
battle.broadcastChatMessage(battleLang("dragged_out", pokemon.getName()))
|
||||
activePokemon.battlePokemon?.let { oldPokemon ->
|
||||
oldPokemon.contextManager.clear(BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
battle.majorBattleActions[oldPokemon.uuid] = publicMessage
|
||||
}
|
||||
battle.majorBattleActions[pokemon.uuid] = publicMessage
|
||||
|
||||
val entity = if (battleActor is EntityBackedBattleActor<*>) battleActor.entity else null
|
||||
setOf(
|
||||
BattleDispatch {
|
||||
if (entity != null) {
|
||||
SwitchInstruction.createEntitySwitch(battle, battleActor, entity, pnx, activePokemon, pokemon, illusion)
|
||||
} else {
|
||||
SwitchInstruction.createNonEntitySwitch(battle, battleActor, pnx, activePokemon, pokemon, illusion)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-endability|POKEMON
|
||||
*
|
||||
* The POKEMON's Ability was suppressed.
|
||||
* @author jeffw773
|
||||
* @since November 28, 2023
|
||||
*/
|
||||
class EndAbilityInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
|
||||
val lang = battleLang("endability", pokemonName)
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
|
||||
/**
|
||||
* Format: |-end|POKEMON|EFFECT
|
||||
*
|
||||
* The volatile status from EFFECT inflicted on POKEMON has ended.
|
||||
* @author Hiroku
|
||||
* @since October 3, 2022
|
||||
*/
|
||||
class EndInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
if (!message.hasOptionalArgument("silent")) {
|
||||
val lang = when (effectID) {
|
||||
"yawn" -> lang("status.sleep.apply", pokemonName)
|
||||
else -> battleLang("end.$effectID", pokemonName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
pokemon.contextManager.remove(effectID, BattleContext.Type.VOLATILE)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-enditem|POKEMON|ITEM|(from)EFFECT
|
||||
*
|
||||
* ITEM held by POKEMON has been destroyed by a move or ability EFFECT, and it now holds no item.
|
||||
*
|
||||
* Alt format: |-enditem|POKEMON|ITEM
|
||||
*
|
||||
* POKEMON's ITEM has destroyed itself (consumed or used).
|
||||
* @author Licious
|
||||
* @since December 30, 2022
|
||||
*/
|
||||
class EndItemInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
// All logic regarding broadcasting battle messages is handled in CobblemonHeldItemManager
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
val itemEffect = message.effectAt(1) ?: return@dispatchGo
|
||||
battlePokemon.heldItemManager.handleEndInstruction(battlePokemon, battle, message)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battlePokemon.contextManager.remove(itemEffect.id, BattleContext.Type.ITEM)
|
||||
if (message.hasOptionalArgument("eat")) {
|
||||
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
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.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMadeInvalidChoicePacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |error|(Invalid choice) ERROR
|
||||
*
|
||||
* The actor needs to send a different decision due to ERROR.
|
||||
* @author Yaseen
|
||||
* @since April 22, 2023
|
||||
*/
|
||||
class ErrorInstruction(val battleActor: BattleActor, val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.log("Error Instruction")
|
||||
battle.dispatchGo {
|
||||
//TODO: some lang stuff for the error messages (Whats the protocol for adding to other langs )
|
||||
//Also is it okay to ignore the team preview error for now? - You bet!
|
||||
val lang = when(message.rawMessage) {
|
||||
"|error|[Unavailable choice] Can't switch: The active Pokémon is trapped" -> battleLang("error.pokemon_is_trapped").red()
|
||||
"|error|[Invalid choice] Can't choose for Team Preview: You're not in a Team Preview phase" -> return@dispatchGo
|
||||
else -> battle.createUnimplemented(message)
|
||||
}
|
||||
battleActor.sendMessage(lang)
|
||||
battleActor.mustChoose = true
|
||||
battleActor.sendUpdate(BattleMadeInvalidChoicePacket())
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
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.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-fail|POKEMON|ACTION
|
||||
*
|
||||
* ACTION has failed against POKEMON.
|
||||
* @author Hunter
|
||||
* @since September 25, 2022
|
||||
*/
|
||||
class FailInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F){
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effectAt(1)?.id
|
||||
val cause = message.effect("from")
|
||||
val of = message.battlePokemonFromOptional(battle)
|
||||
|
||||
val lang = when (effectID) {
|
||||
null, "burnup", "doubleshock" -> battleLang("fail") // Moves that use default fail lang. (Null included for moves that fail with no effect, for example: Baton Pass.)
|
||||
"shedtail" -> battleLang("fail.substitute", pokemonName)
|
||||
"hyperspacefury", "aurawheel" -> battleLang("fail.darkvoid", pokemonName) // Moves that can only be used by one species and fail when any others try
|
||||
"corrosivegas" -> battleLang("fail.healblock", pokemonName)
|
||||
"dynamax" -> battleLang("fail.grassknot", pokemonName) // Covers weight moves that fail against dynamaxed Pokémon
|
||||
"unboost" -> {
|
||||
val statKey = message.argumentAt(2)
|
||||
val stat = statKey?.let { Stats.getStat(it).displayName }
|
||||
if (stat != null) {
|
||||
battleLang("fail.$effectID.single", pokemonName, stat)
|
||||
} else {
|
||||
battleLang("fail.$effectID", pokemonName)
|
||||
}
|
||||
}
|
||||
else -> battleLang("fail.$effectID", pokemonName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang.red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -19,9 +19,16 @@ import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleFaintPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |faint|POKEMON
|
||||
*
|
||||
* POKEMON has fainted.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : InterpreterInstruction {
|
||||
var waitTime = 2.5F
|
||||
val faintingPokemon = message.getBattlePokemon(0, battle)!!
|
||||
val faintingPokemon = message.battlePokemon(0, battle)!!
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val faintMessage = battleLang("fainted", faintingPokemon.getName()).red()
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
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.red
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-fieldactivate|EFFECT
|
||||
*
|
||||
* A miscellaneous EFFECT has activated for the entire field.
|
||||
* @author Segfault Guy
|
||||
* @since April 5, 2023
|
||||
*/
|
||||
class FieldActivateInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(2.5F) {
|
||||
val effectID = message.effectAt(0)?.id ?: return@dispatchWaiting
|
||||
val lang = battleLang("fieldactivate.$effectID")
|
||||
battle.broadcastChatMessage(lang.red())
|
||||
|
||||
// share this action with all active Pokemon
|
||||
battle.activePokemon.forEach {
|
||||
it.battlePokemon?.contextManager?.addUnique(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.VOLATILE, battle))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
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.red
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-fieldend|CONDITION
|
||||
*
|
||||
* The field CONDITION has ended.
|
||||
* @author Licious
|
||||
* @since February 8, 2023
|
||||
*/
|
||||
class FieldEndInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val effect = message.effectAt(0) ?: return@dispatchWaiting
|
||||
val lang = battleLang("fieldend.${effect.id}")
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
val type = BattleContext.Type.valueOf(effect.rawData.substringAfterLast(" ").uppercase())
|
||||
battle.contextManager.remove(effect.id, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format: |-fieldstart|CONDITION
|
||||
*
|
||||
* The field CONDITION has started.
|
||||
* @author Xylopia
|
||||
* @since January 31, 2023
|
||||
*/
|
||||
class FieldStartInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val effect = message.effectAt(0) ?: return
|
||||
val user = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, user)
|
||||
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
// Note persistent is a CAP ability only we can ignore the flag
|
||||
val lang = battleLang("fieldstart.${effect.id}", user)
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
val type = BattleContext.Type.valueOf(effect.rawData.substringAfterLast(" ").uppercase())
|
||||
battle.contextManager.add(ShowdownInterpreter.getContextFromAction(message, type, battle))
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
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.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-heal|POKEMON|HP STATUS
|
||||
*
|
||||
* POKEMON has healed damage and is now at HP STATUS.
|
||||
* @author Licious
|
||||
* @since February 6, 2023
|
||||
*/
|
||||
class HealInstruction(val actor: BattleActor, val publicMessage: BattleMessage, val privateMessage: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val pnx = privateMessage.pnxAndUuid(0)?.first
|
||||
val battlePokemon = privateMessage.battlePokemon(0, battle) ?: return
|
||||
val rawHpAndStatus = privateMessage.argumentAt(1)?.split(" ") ?: return
|
||||
val rawHpRatio = rawHpAndStatus.getOrNull(0) ?: return
|
||||
val newHealth = rawHpRatio.split("/").map { it.toFloatOrNull() ?: return }
|
||||
val newHealthRatio = rawHpRatio.split("/").map { it.toFloatOrNull()?.div(newHealth[1]) ?: return }
|
||||
val effect = privateMessage.effect()
|
||||
val pokemonName = battlePokemon.getName()
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemonName)
|
||||
|
||||
battle.dispatchWaiting {
|
||||
if (pnx != null) {
|
||||
// dynamax changes max health
|
||||
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth[0], newHealth[1]), BattleHealthChangePacket(pnx, newHealthRatio[0]))
|
||||
}
|
||||
val silent = privateMessage.hasOptionalArgument("silent")
|
||||
if (!silent) {
|
||||
val lang = when {
|
||||
privateMessage.hasOptionalArgument("zeffect") -> battleLang("heal.zeffect", battlePokemon.getName())
|
||||
privateMessage.hasOptionalArgument("wisher") -> {
|
||||
val name = privateMessage.optionalArgument("wisher")!!
|
||||
val showdownId = name.lowercase().replace(ShowdownIdentifiable.REGEX, "")
|
||||
val wisher = actor.pokemonList.firstOrNull { it.effectedPokemon.showdownId() == showdownId }
|
||||
// If no Pokémon is found this is a nickname
|
||||
battleLang("heal.wish", wisher?.getName() ?: actor.nameOwned(name))
|
||||
}
|
||||
privateMessage.hasOptionalArgument("from") -> {
|
||||
when (effect!!.type) {
|
||||
Effect.Type.ITEM -> when (effect.id) {
|
||||
"leftovers", "shellbell", "blacksludge" -> battleLang("heal.leftovers", battlePokemon.getName(), effect.typelessData)
|
||||
else -> battleLang("heal.item", battlePokemon.getName(), effect.typelessData)
|
||||
}
|
||||
else -> when (effect.id) {
|
||||
"drain" -> {
|
||||
val drained = privateMessage.battlePokemonFromOptional(battle) ?: return@dispatchWaiting
|
||||
battleLang("heal.drain", drained.getName())
|
||||
}
|
||||
else -> battleLang("heal.${effect.id}", battlePokemon.getName())
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
battleLang("heal.generic", battlePokemon.getName())
|
||||
}
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
battle.minorBattleActions[battlePokemon.uuid] = privateMessage
|
||||
battlePokemon.effectedPokemon.currentHealth = newHealth[0].toInt()
|
||||
|
||||
// This part is not always present
|
||||
val rawStatus = rawHpAndStatus.getOrNull(1) ?: return@dispatchWaiting
|
||||
val status = Statuses.getStatus(rawStatus) ?: return@dispatchWaiting
|
||||
if (status is PersistentStatus && battlePokemon.effectedPokemon.status?.status != status) {
|
||||
battlePokemon.effectedPokemon.applyStatus(status)
|
||||
if (pnx != null) {
|
||||
battle.sendUpdate(BattlePersistentStatusPacket(pnx, status))
|
||||
}
|
||||
if (!silent) {
|
||||
status.applyMessage.let { battle.broadcastChatMessage(it.asTranslated(battlePokemon.getName())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-hitcount|POKEMON|NUM
|
||||
*
|
||||
* A multi-hit move hit the POKEMON NUM times.
|
||||
* @author Licious
|
||||
* @since December 30, 2022
|
||||
*/
|
||||
class HitCountInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
val hitCount = message.argumentAt(1)?.toIntOrNull() ?: return@dispatchGo
|
||||
val lang = if (hitCount == 1) battleLang("hit_count_singular") else battleLang("hit_count", hitCount)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -11,6 +11,12 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
/**
|
||||
* Catch-all for instruction from Showdown that are not parsed.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 25, 2023
|
||||
*/
|
||||
class IgnoredInstruction : InterpreterInstruction {
|
||||
override fun invoke(pokemonBattle: PokemonBattle) = Unit
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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.text.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-immune|POKEMON
|
||||
*
|
||||
* POKEMON was immune to a move.
|
||||
* @author Hiroku
|
||||
* @since October 3, 2022
|
||||
*/
|
||||
class ImmuneInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val name = pokemon.getName()
|
||||
battle.broadcastChatMessage(battleLang("immune", name).red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BasicContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-invertboost|POKEMON
|
||||
*
|
||||
* POKEMON had its stat changes inverted.
|
||||
* @author JMMCP
|
||||
* @since November 26, 2023
|
||||
*/
|
||||
class InvertBoostInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val name = pokemon.getName()
|
||||
battle.broadcastChatMessage(battleLang("invertboost", name))
|
||||
|
||||
// update and invert BOOST and UNBOOST contexts
|
||||
val context = ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.BOOST, battle)
|
||||
val newUnboosts = pokemon.contextManager.get(BattleContext.Type.BOOST)?.map {
|
||||
BasicContext(it.id, context.turn, BattleContext.Type.UNBOOST, context.origin)
|
||||
}?.toTypedArray()
|
||||
val newBoosts = pokemon.contextManager.get(BattleContext.Type.UNBOOST)?.map {
|
||||
BasicContext(it.id, context.turn, BattleContext.Type.BOOST, context.origin)
|
||||
}?.toTypedArray()
|
||||
|
||||
pokemon.contextManager.clear(BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
newBoosts?.let { pokemon.contextManager.add(*it) }
|
||||
newUnboosts?.let { pokemon.contextManager.add(*it) }
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format: |-item|POKEMON|ITEM|(from)EFFECT
|
||||
*
|
||||
* ITEM held by POKEMON has been changed or revealed due to a move or ability EFFECT.
|
||||
*
|
||||
* Alt format: |-item|POKEMON|ITEM
|
||||
*
|
||||
* POKEMON has just switched in, and its ITEM is being announced to have a long-term effect.
|
||||
* @author Licious
|
||||
* @since December 30, 2022
|
||||
*/
|
||||
class ItemInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), sourceName)
|
||||
|
||||
battle.dispatchGo {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
battlePokemon.heldItemManager.handleStartInstruction(battlePokemon, battle, message)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battlePokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.ITEM, battle))
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-mega|POKEMON|MEGASTONE
|
||||
*
|
||||
* POKEMON used item MEGASTONE to Mega Evolve.
|
||||
* @author Segfault Guy
|
||||
* @since September 10, 2023
|
||||
*/
|
||||
class MegaInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -11,22 +11,22 @@ 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.text.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-miss|SOURCE|TARGET
|
||||
* Format: |-miss|SOURCE|TARGET
|
||||
*
|
||||
* The move used by the SOURCE Pokémon missed (maybe absent) the TARGET Pokémon.
|
||||
* @author Hiroku
|
||||
* @since October 3, 2022
|
||||
*/
|
||||
class MissInstruction(val battle: PokemonBattle, val instructionSet: InstructionSet, val message: BattleMessage): InterpreterInstruction {
|
||||
val target = message.getBattlePokemon(1, battle)
|
||||
class MissInstruction(val battle: PokemonBattle, val message: BattleMessage): InterpreterInstruction {
|
||||
val target = message.battlePokemon(1, battle)
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchGo
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
battle.broadcastChatMessage(battleLang("missed").red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
|
||||
+10
-3
@@ -31,6 +31,13 @@ import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgr
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |move|POKEMON|MOVE|TARGET
|
||||
*
|
||||
* POKEMON has used MOVE at TARGET.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class MoveInstruction(
|
||||
val instructionSet: InstructionSet,
|
||||
val message: BattleMessage
|
||||
@@ -46,8 +53,8 @@ class MoveInstruction(
|
||||
var targetPokemon: BattlePokemon? = null
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
userPokemon = message.getBattlePokemon(0, battle)!!
|
||||
targetPokemon = message.getBattlePokemon(2, battle)
|
||||
userPokemon = message.battlePokemon(0, battle)!!
|
||||
targetPokemon = message.battlePokemon(2, battle)
|
||||
val targetPokemon = targetPokemon // So smart non-null casts can happen
|
||||
|
||||
val optionalEffect = message.effect()
|
||||
@@ -103,7 +110,7 @@ class MoveInstruction(
|
||||
}
|
||||
}
|
||||
|
||||
val hurtTargets = subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.battlePokemon }
|
||||
val hurtTargets = subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.expectedTarget }
|
||||
runtime.environment.getQueryStruct().addFunction("hurt") { params ->
|
||||
if (params.params.size == 0) {
|
||||
return@addFunction DoubleValue(hurtTargets.isNotEmpty())
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-nothing
|
||||
*
|
||||
* Pathetic. Your move did absolutely nothing. You should feel bad for being bad.
|
||||
* @author Hiroku
|
||||
* @since August 20, 2022
|
||||
*/
|
||||
class NothingInstruction() : InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo { battle.broadcastChatMessage(battleLang("nothing")) }
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
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.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
/**
|
||||
* Format: |pp_update|<side_id>: <pokemon_uuid>|...<move_id>: <move_pp>
|
||||
*
|
||||
* @author Licious
|
||||
* @since September 22, 2022
|
||||
*/
|
||||
class PpUpdateInstruction( val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatch {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
val moveDatum = message.argumentAt(1)?.split(", ") ?: return@dispatch GO
|
||||
moveDatum.forEach { moveData ->
|
||||
val moveIdAndPp = moveData.split(": ")
|
||||
val moveId = moveIdAndPp[0]
|
||||
val movePp = moveIdAndPp[1]
|
||||
val move = pokemon.effectedPokemon.moveSet.firstOrNull { move -> move.name.equals(moveId, true) } ?: return@dispatch GO
|
||||
move.currentPp = movePp.toInt()
|
||||
}
|
||||
GO
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-prepare|ATTACKER|MOVE and |-prepare|ATTACKER|MOVE|DEFENDER
|
||||
*
|
||||
* ATTACKER Pokémon is preparing to use a charge MOVE on DEFENDER or an unknown target.
|
||||
* @author Renaissance
|
||||
* @since March 24, 2023
|
||||
*/
|
||||
class PrepareInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
//Prevents spam when the move Role Play is used
|
||||
val lang = when (effectID) {
|
||||
"shadowforce" -> battleLang("prepare.phantomforce", pokemonName) //Phantom Force and Shadow Force share the same text
|
||||
"solarblade" -> battleLang("prepare.solarbeam", pokemonName) //Solar Beam and Solar Blade share the same text
|
||||
else -> battleLang("prepare.$effectID", pokemonName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-mustrecharge|POKEMON
|
||||
*
|
||||
* POKEMON must spend the turn recharging from a previous move.
|
||||
* @author Hunter
|
||||
* @since September 25, 2022
|
||||
*/
|
||||
class RechargeInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(2F){
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName()))
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
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.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |replace|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* Illusion has ended for POKEMON. Syntax is the same as [SwitchInstruction].
|
||||
* @author Segfault Guy
|
||||
* @since March 18th, 2024
|
||||
*/
|
||||
class ReplaceInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val entity = pokemon.entity
|
||||
val actor = pokemon.actor
|
||||
val pnx = message.pnxAndUuid(0)?.first
|
||||
|
||||
entity?.let { it.effects.mockEffect?.end(it) }
|
||||
pnx?.let {battle.sendSidedUpdate(actor, BattleSwitchPokemonPacket(pnx, pokemon, true, null), BattleSwitchPokemonPacket(pnx, pokemon, false, null)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
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.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionRequest
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
|
||||
|
||||
/**
|
||||
* Format: |request|REQUEST
|
||||
*
|
||||
* It's time for the actor to make a decision.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class RequestInstruction(val battleActor: BattleActor, val message: BattleMessage): InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.log("Request Instruction")
|
||||
|
||||
if (message.rawMessage.contains("teamPreview")) // TODO probably change when we're allowing team preview
|
||||
return
|
||||
|
||||
// Parse Json message and update state info for actor
|
||||
val request = BattleRegistry.gson.fromJson(message.rawMessage.split("|request|")[1], ShowdownActionRequest::class.java)
|
||||
request.sanitize(battle, battleActor)
|
||||
if (battle.started) {
|
||||
battle.dispatchGo {
|
||||
// This request won't be acted on until the start of next turn
|
||||
battleActor.sendUpdate(BattleQueueRequestPacket(request))
|
||||
battleActor.request = request
|
||||
battleActor.responses.clear()
|
||||
// We need to send this out because 'upkeep' isn't received until the request is handled since the turn won't swap
|
||||
if (request.forceSwitch.contains(true)) {
|
||||
battle.doWhenClear {
|
||||
battleActor.mustChoose = true
|
||||
battleActor.sendUpdate(BattleMakeChoicePacket())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
battleActor.request = request
|
||||
battleActor.responses.clear()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-resisted|POKEMON
|
||||
*
|
||||
* POKEMON resisted the attack.
|
||||
* @author
|
||||
* @since
|
||||
*/
|
||||
class ResistedInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
battle.broadcastChatMessage(battleLang("resisted"))
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-setboost|POKEMON|STAT|AMOUNT
|
||||
*
|
||||
* Same as -boost and -unboost, but STAT is set to AMOUNT instead of boosted by AMOUNT.\
|
||||
* @author Renaissance
|
||||
* @since February 16, 2023
|
||||
*/
|
||||
class SetBoostInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effect()?.id ?: return@dispatchWaiting
|
||||
val lang = battleLang("setboost.$effectID", pokemonName)
|
||||
battle.broadcastChatMessage(lang)
|
||||
pokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.BOOST, battle))
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
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.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Format: |-sethp|POKEMON|HP
|
||||
*
|
||||
* POKEMON now has HP hit points.
|
||||
* @author Licious
|
||||
* @since February 8, 2023
|
||||
*/
|
||||
class SetHpInstruction(val actor: BattleActor, val publicMessage: BattleMessage, val privateMessage: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val (pnx, _) = privateMessage.pnxAndUuid(0) ?: return@dispatchWaiting
|
||||
val flatHp = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toFloatOrNull() ?: return@dispatchWaiting
|
||||
val ratioHp = publicMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toFloatOrNull()?.times(0.01F) ?: return@dispatchWaiting
|
||||
val battlePokemon = privateMessage.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
battlePokemon.effectedPokemon.currentHealth = flatHp.roundToInt()
|
||||
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, flatHp), BattleHealthChangePacket(pnx, ratioHp))
|
||||
// It doesn't matter which we check when silent both have it
|
||||
if (!publicMessage.hasOptionalArgument("silent")) {
|
||||
val effectID = publicMessage.effect()?.id ?: return@dispatchWaiting
|
||||
val lang = battleLang("sethp.$effectID")
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
battle.minorBattleActions[battlePokemon.uuid] = publicMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-sideend|SIDE|CONDITION
|
||||
*
|
||||
* Indicates that the side CONDITION ended for the given SIDE.
|
||||
* @author Segfault Guy
|
||||
* @since April 5, 2023
|
||||
*/
|
||||
class SideEndInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(2F) {
|
||||
val side = if (message.argumentAt(0)?.get(1) == '1') battle.side1 else battle.side2
|
||||
val effect = message.effectAt(1) ?: return@dispatchWaiting
|
||||
battle.sides.forEach {
|
||||
val subject = if (it == side) battleLang("side_subject.ally") else battleLang("side_subject.opponent")
|
||||
val lang = battleLang("sideend.${effect.id}", subject)
|
||||
it.broadcastChatMessage(lang)
|
||||
}
|
||||
|
||||
val bucket = when(effect.rawData.substringAfterLast(" ").lowercase()) {
|
||||
"reflect", "screen", "veil" -> BattleContext.Type.SCREEN
|
||||
"spikes", "rock", "web" -> BattleContext.Type.HAZARD
|
||||
"tailwind" -> BattleContext.Type.TAILWIND
|
||||
else -> BattleContext.Type.MISC
|
||||
}
|
||||
side.contextManager.remove(effect.id, bucket)
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-sidestart|SIDE|CONDITION
|
||||
*
|
||||
* A side CONDITION has started on SIDE.
|
||||
* @author Segfault Guy
|
||||
* @since April 5, 2023
|
||||
*/
|
||||
class SideStartInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(2F) {
|
||||
val side = if (message.argumentAt(0)?.get(1) == '1') battle.side1 else battle.side2
|
||||
val effect = message.effectAt(1) ?: return@dispatchWaiting
|
||||
battle.sides.forEach {
|
||||
val subject = if (it == side) battleLang("side_subject.ally") else battleLang("side_subject.opponent")
|
||||
val lang = battleLang("sidestart.${effect.id}", subject)
|
||||
it.broadcastChatMessage(lang)
|
||||
}
|
||||
|
||||
val bucket = when(effect.rawData.substringAfterLast(" ").lowercase()) {
|
||||
"reflect", "screen", "veil" -> BattleContext.Type.SCREEN
|
||||
"spikes", "rock", "web" -> BattleContext.Type.HAZARD
|
||||
"tailwind" -> BattleContext.Type.TAILWIND
|
||||
else -> BattleContext.Type.MISC
|
||||
}
|
||||
side.contextManager.add(ShowdownInterpreter.getContextFromAction(message, bucket, battle))
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
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.text.yellow
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/** Placeholder for instructions that only */
|
||||
class SilentInstruction(val instructionSet: InstructionSet, val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo { }
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-singlemove|POKEMON|MOVE
|
||||
*
|
||||
* POKEMON used MOVE which causes a temporary effect lasting the duration of the move.
|
||||
* @author Renaissance
|
||||
* @since March 24, 2023
|
||||
*/
|
||||
class SingleMoveInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val lang = battleLang("singlemove.$effectID", pokemonName)
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format: |-singleturn|POKEMON|MOVE
|
||||
*
|
||||
* POKEMON used MOVE which causes a temporary effect lasting the duration of the turn.
|
||||
* @author Renaissance
|
||||
* @since March 24, 2023
|
||||
*/
|
||||
class SingleTurnInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKOWN")
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val lang = battleLang("singleturn.$effectID", pokemonName, sourceName)
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
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.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format: |-start|POKEMON|EFFECT
|
||||
*
|
||||
* A volatile status has been inflicted on POKEMON by EFFECT.
|
||||
* @author Deltric
|
||||
* @since January 21, 2022
|
||||
*/
|
||||
class StartInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatch {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
|
||||
|
||||
val optionalEffect = message.effect()
|
||||
val optionalPokemon = message.battlePokemonFromOptional(battle)
|
||||
val optionalPokemonName = optionalPokemon?.getName()
|
||||
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKOWN")
|
||||
|
||||
// skip adding contexts for every time the perish counter decrements
|
||||
if (!effectID.contains("perish")) {
|
||||
// don't need to add unique: showdown won't send -start instruction if volatile status is already present
|
||||
pokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.VOLATILE, battle))
|
||||
}
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
|
||||
if (!message.hasOptionalArgument("silent")) {
|
||||
val lang = if (optionalEffect?.id == "reflecttype" && optionalPokemonName != null)
|
||||
battleLang("start.reflecttype", pokemon.getName(), optionalPokemonName)
|
||||
else
|
||||
when (effectID) {
|
||||
"confusion", "perish3" -> return@dispatch GO // Skip
|
||||
"perish2", "perish1", "perish0",
|
||||
"stockpile1", "stockpile2", "stockpile3" -> battleLang("start.${effectID.dropLast(1)}", pokemon.getName(), effectID.last().digitToInt())
|
||||
"dynamax" -> battleLang("start.${message.effectAt(2)?.id ?: effectID}", pokemon.getName()).yellow()
|
||||
"curse" -> battleLang("start.curse", message.battlePokemonFromOptional(battle)!!.getName(), pokemon.getName())
|
||||
else -> battleLang("start.$effectID", pokemon.getName(), extraEffect)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
WaitDispatch(1F)
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
|
||||
/**
|
||||
* Format: |-status|POKEMON|STATUS
|
||||
*
|
||||
* POKEMON has been inflicted with STATUS.
|
||||
* @author Hiroku
|
||||
* @since October 3, 2022
|
||||
*/
|
||||
class StatusInstruction( val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val (pnx, _) = message.pnxAndUuid(0) ?: return
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val otherPokemon = message.actorAndActivePokemonFromOptional(battle, "of")?.second?.battlePokemon
|
||||
val statusLabel = message.argumentAt(1) ?: return
|
||||
val status = Statuses.getStatus(statusLabel) ?: return Cobblemon.LOGGER.error("Unrecognized status: $statusLabel")
|
||||
if (otherPokemon == null)
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), pokemon.getName())
|
||||
else
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), otherPokemon.getName())
|
||||
|
||||
battle.dispatchWaiting {
|
||||
if (status is PersistentStatus) {
|
||||
pokemon.effectedPokemon.applyStatus(status)
|
||||
battle.sendUpdate(BattlePersistentStatusPacket(pnx, status))
|
||||
}
|
||||
|
||||
battle.broadcastChatMessage(status.applyMessage.asTranslated(pokemon.getName()))
|
||||
pokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.STATUS, battle))
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-supereffective|POKEMON
|
||||
*
|
||||
* A move was super effective against POKEMON.
|
||||
* @author Hunter
|
||||
* @since August 18, 2022
|
||||
*/
|
||||
class SuperEffectiveInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
battle.broadcastChatMessage(battleLang("superEffective"))
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-swapboost|SOURCE|TARGET|STATS
|
||||
*
|
||||
* Swaps the boosts from STATS between the SOURCE Pokémon and TARGET Pokémon.
|
||||
* @author Renaissance
|
||||
* @since March 24, 2023
|
||||
*/
|
||||
class SwapBoostInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(2F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val targetPokemon = message.battlePokemon(1, battle) ?: return@dispatchWaiting
|
||||
val targetPokemonName = targetPokemon.getName()
|
||||
val effectID = message.effect()?.id ?: return@dispatchWaiting
|
||||
val lang = when (effectID) {
|
||||
"guardswap", "powerswap", "heartswap" -> battleLang("swapboost.$effectID", pokemonName)
|
||||
else -> battleLang("swapboost.generic", pokemonName, targetPokemonName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
pokemon.contextManager.swap(targetPokemon.contextManager, BattleContext.Type.BOOST)
|
||||
pokemon.contextManager.swap(targetPokemon.contextManager, BattleContext.Type.UNBOOST)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
/**
|
||||
* Format: |-swapsideconditions
|
||||
*
|
||||
* Swaps side conditions between sides. Used for Court Change.
|
||||
* @author Segfault Guy
|
||||
* @since February 19, 2024
|
||||
*/
|
||||
class SwapSideConditionsInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val sides = mutableListOf<BattleSide>()
|
||||
battle.sides.forEach { side ->
|
||||
if (!sides.contains(side)) {
|
||||
val oppositeManager = side.getOppositeSide().contextManager
|
||||
side.contextManager.swap(oppositeManager, BattleContext.Type.TAILWIND, BattleContext.Type.SCREEN, BattleContext.Type.HAZARD)
|
||||
}
|
||||
sides.add(side)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
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.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.dispatch.*
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
|
||||
import com.cobblemon.mod.common.util.swap
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |switch|POKEMON|DETAILS|HP STATUS or |drag|POKEMON|DETAILS|HP STATUS
|
||||
*
|
||||
* POKEMON has switched in (if there was an old Pokémon at that position, it is switched out).
|
||||
* POKEMON|DETAILS represents all the information that can be used to tell Pokémon apart.
|
||||
* The switched Pokémon has HP health points and STATUS status.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: BattleActor, val publicMessage: BattleMessage, val privateMessage: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
|
||||
val (pnx, _) = publicMessage.pnxAndUuid(0) ?: return
|
||||
val (actor, activePokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
val entity = if (actor is EntityBackedBattleActor<*>) actor.entity else null
|
||||
|
||||
val illusion = publicMessage.battlePokemonFromOptional(battle, "is")
|
||||
val pokemon = publicMessage.battlePokemon(0, battle) ?: return
|
||||
|
||||
if (!battle.started) {
|
||||
activePokemon.battlePokemon = pokemon
|
||||
activePokemon.illusion = illusion
|
||||
val pokemonEntity = pokemon.entity
|
||||
if (pokemonEntity == null && entity != null) {
|
||||
val targetPos = battleActor.getSide().getOppositeSide().actors.filterIsInstance<EntityBackedBattleActor<*>>().firstOrNull()?.entity?.pos?.let { pos ->
|
||||
val offset = pos.subtract(entity.pos)
|
||||
val idealPos = entity.pos.add(offset.multiply(0.33))
|
||||
idealPos
|
||||
} ?: entity.pos
|
||||
|
||||
actor.stillSendingOutCount++
|
||||
pokemon.effectedPokemon.sendOutWithAnimation(
|
||||
source = entity,
|
||||
battleId = battle.battleId,
|
||||
level = entity.world as ServerWorld,
|
||||
doCry = false,
|
||||
position = targetPos,
|
||||
illusion = illusion?.let { IllusionEffect(it.effectedPokemon) }
|
||||
).thenApply {
|
||||
actor.stillSendingOutCount--
|
||||
}
|
||||
}
|
||||
else if (pokemonEntity != null) {
|
||||
illusion?.let { IllusionEffect(it.effectedPokemon).start(pokemonEntity) }
|
||||
}
|
||||
} else {
|
||||
battle.dispatchInsert {
|
||||
pokemon.sendUpdate()
|
||||
|
||||
if (activePokemon.battlePokemon == pokemon) {
|
||||
return@dispatchInsert emptySet() // Already switched in, Showdown does this if the pokemon is going to die before it can switch
|
||||
}
|
||||
|
||||
activePokemon.battlePokemon?.let { oldPokemon ->
|
||||
if (publicMessage.effect()?.id == "batonpass") oldPokemon.contextManager.swap(pokemon.contextManager, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
oldPokemon.contextManager.clear(BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
battle.majorBattleActions[oldPokemon.uuid] = publicMessage
|
||||
}
|
||||
battle.majorBattleActions[pokemon.uuid] = publicMessage
|
||||
|
||||
setOf(
|
||||
BattleDispatch {
|
||||
if (entity != null) {
|
||||
createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon, illusion)
|
||||
} else {
|
||||
createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon, illusion)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object{
|
||||
fun createEntitySwitch(battle: PokemonBattle, actor: BattleActor, entity: LivingEntity, pnx: String, activePokemon: ActiveBattlePokemon, newPokemon: BattlePokemon, illusion: BattlePokemon? = null): DispatchResult {
|
||||
val pokemonEntity = activePokemon.battlePokemon?.entity
|
||||
// If we can't find the entity for some reason then we're going to skip the recall animation
|
||||
val sendOutFuture = CompletableFuture<Unit>()
|
||||
(pokemonEntity?.recallWithAnimation() ?: CompletableFuture.completedFuture(Unit)).thenApply {
|
||||
// Queue actual swap and send-in after the animation has ended
|
||||
actor.pokemonList.swap(actor.activePokemon.indexOf(activePokemon), actor.pokemonList.indexOf(newPokemon))
|
||||
activePokemon.battlePokemon = newPokemon
|
||||
activePokemon.illusion = illusion
|
||||
battle.sendSidedUpdate(actor, BattleSwitchPokemonPacket(pnx, newPokemon, true, illusion), BattleSwitchPokemonPacket(pnx, newPokemon, false, illusion))
|
||||
if (newPokemon.entity != null) {
|
||||
illusion?.let { IllusionEffect(it.effectedPokemon).start(newPokemon.entity!!) }
|
||||
newPokemon.entity?.cry()
|
||||
sendOutFuture.complete(Unit)
|
||||
} else {
|
||||
val lastPosition = activePokemon.position
|
||||
// Send out at previous Pokémon's location if it is known, otherwise actor location
|
||||
val world = lastPosition?.first ?: entity.world as ServerWorld
|
||||
val pos = lastPosition?.second ?: entity.pos
|
||||
newPokemon.effectedPokemon.sendOutWithAnimation(
|
||||
source = entity,
|
||||
battleId = battle.battleId,
|
||||
level = world,
|
||||
position = pos,
|
||||
illusion = illusion?.let { IllusionEffect(it.effectedPokemon) }
|
||||
).thenAccept { sendOutFuture.complete(Unit) }
|
||||
}
|
||||
}
|
||||
|
||||
return UntilDispatch { sendOutFuture.isDone }
|
||||
}
|
||||
|
||||
fun createNonEntitySwitch(battle: PokemonBattle, actor: BattleActor, pnx: String, activePokemon: ActiveBattlePokemon, newPokemon: BattlePokemon, illusion: BattlePokemon? = null): DispatchResult {
|
||||
actor.pokemonList.swap(actor.activePokemon.indexOf(activePokemon), actor.pokemonList.indexOf(newPokemon))
|
||||
activePokemon.battlePokemon = newPokemon
|
||||
activePokemon.illusion = illusion
|
||||
battle.sendSidedUpdate(actor, BattleSwitchPokemonPacket(pnx, newPokemon, true, illusion), BattleSwitchPokemonPacket(pnx, newPokemon, false, illusion))
|
||||
return WaitDispatch(1.5F)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
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.text.yellow
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-terastallize|POKEMON|TYPE
|
||||
*
|
||||
* POKEMON terastallized into TYPE.
|
||||
* @author Segfault Guy
|
||||
* @since September 10, 2023
|
||||
*/
|
||||
class TerastallizeInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val type = message.effectAt(1)?.let { ElementalTypes.get(it.id) } ?: return
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("terastallize", pokemonName, type.displayName).yellow())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.TransformEffect
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-transform|POKEMON|POKEMON
|
||||
*
|
||||
* POKEMON used Transform to turn into target POKEMON.
|
||||
* @author jeffw773
|
||||
* @since November 28, 2023
|
||||
*/
|
||||
class TransformInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val pokemonName = pokemon.getName()
|
||||
val targetPokemon = message.battlePokemon(1, battle) ?: return@dispatchWaiting
|
||||
val targetPokemonName = targetPokemon.getName()
|
||||
|
||||
pokemon.entity?.let { TransformEffect(targetPokemon.effectedPokemon).start(it) }
|
||||
|
||||
val lang = battleLang("transform", pokemonName, targetPokemonName)
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -24,13 +24,17 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleSetTeamPokemonP
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |turn|NUMBER
|
||||
* Format: |turn|NUMBER
|
||||
*
|
||||
* It is now turn NUMBER.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class TurnInstruction(val message: BattleMessage) : InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
// TODO maybe tell the client that the turn number has changed
|
||||
val turnNumber = message.argumentAt(0)?.toInt() ?: return
|
||||
|
||||
if (!battle.started) {
|
||||
battle.started = true
|
||||
battle.actors.filterIsInstance<PlayerBattleActor>().forEach { actor ->
|
||||
@@ -54,9 +58,6 @@ class TurnInstruction(val message: BattleMessage) : InterpreterInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO maybe tell the client that the turn number has changed
|
||||
val turnNumber = message.argumentAt(0)?.toInt() ?: return
|
||||
|
||||
battle.dispatch {
|
||||
battle.sendToActors(BattleMakeChoicePacket())
|
||||
battle.broadcastChatMessage(battleLang("turn", turnNumber).aqua())
|
||||
|
||||
+7
@@ -12,7 +12,14 @@ 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.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
|
||||
/**
|
||||
* Catch-all for unimplemented instructions that need to be added to the [ShowdownInterpreter].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 25, 2023
|
||||
*/
|
||||
class UnknownInstruction(val battleMessage: BattleMessage) : InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo { battle.broadcastChatMessage(battleMessage.rawMessage.red()) }
|
||||
|
||||
+3
-2
@@ -13,10 +13,11 @@ import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |upkeep|
|
||||
* Format: |upkeep|
|
||||
*
|
||||
* Signals the upkeep phase of the turn where the number of turns left for field conditions are updated.
|
||||
* @author Hiroku
|
||||
* @since March 12, 2022
|
||||
*/
|
||||
class UpkeepInstruction : InterpreterInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format: |-weather|WEATHER
|
||||
*
|
||||
* Indicates WEATHER is currently in effect.
|
||||
*
|
||||
* If upkeep is present, it means that WEATHER was active previously and is still in effect that turn.
|
||||
* Otherwise, it means that the weather has changed due to a move or ability, or has expired, in which case WEATHER will be none.
|
||||
* @author Hunter
|
||||
* @since September 25, 2022
|
||||
*/
|
||||
class WeatherInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val weather = message.effectAt(0)?.id ?: return
|
||||
val user = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, message.effect(), user)
|
||||
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val lang = when {
|
||||
message.hasOptionalArgument("upkeep") -> battleLang("weather.$weather.upkeep")
|
||||
weather != "none" -> {
|
||||
battle.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.WEATHER, battle))
|
||||
battleLang("weather.$weather.start")
|
||||
}
|
||||
else -> {
|
||||
val oldWeather = battle.contextManager.get(BattleContext.Type.WEATHER)?.iterator()?.next()?.id ?: return@dispatchWaiting
|
||||
battle.contextManager.clear(BattleContext.Type.WEATHER)
|
||||
battleLang("weather.$oldWeather.end")
|
||||
}
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
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.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
import com.cobblemon.mod.common.api.text.gold
|
||||
import com.cobblemon.mod.common.api.text.plus
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Format: |win|USER
|
||||
*
|
||||
* USER has won the battle.
|
||||
* @author Deltric
|
||||
* @since January 22, 2022
|
||||
*/
|
||||
class WinInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val user = message.argumentAt(0) ?: return@dispatchGo
|
||||
val ids = user.split("&").map { it.trim() }
|
||||
val winners = ids.map { battle.getActor(UUID.fromString(it))!! }
|
||||
val losers = battle.actors.filter { !winners.contains(it) }
|
||||
val winnersText = winners.map { it.getName() }.reduce { acc, next -> acc + " & " + next }
|
||||
|
||||
battle.broadcastChatMessage(battleLang("win", winnersText).gold())
|
||||
|
||||
battle.end()
|
||||
|
||||
val wasCaught = battle.showdownMessages.any { "capture" in it }
|
||||
|
||||
// If the battle was a PvW battle, we need to set the killer of the wild Pokémon to the player
|
||||
if (battle.isPvW) {
|
||||
val nonPlayerActor = battle.actors.first { it.type == ActorType.WILD }
|
||||
val wildPokemon: BattlePokemon = nonPlayerActor.pokemonList.first()
|
||||
|
||||
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
|
||||
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
|
||||
}
|
||||
}
|
||||
|
||||
CobblemonEvents.BATTLE_VICTORY.post(BattleVictoryEvent(battle, winners, losers, wasCaught))
|
||||
|
||||
ShowdownInterpreter.lastCauser.remove(battle.battleId)
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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.text.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-zbroken|POKEMON
|
||||
*
|
||||
* A z-move has broken through protect and hit POKEMON.
|
||||
* @author Segfault Guy
|
||||
* @since September 10, 2023
|
||||
*/
|
||||
class ZBrokenInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("zbroken", pokemonName).red())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
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.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-zpower|POKEMON
|
||||
*
|
||||
* POKEMON has used the z-move variant of its move.
|
||||
* @author Segfault Guy
|
||||
* @since September 10, 2023
|
||||
*/
|
||||
class ZPowerInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.dispatchWaiting {
|
||||
battle.broadcastChatMessage(battleLang("zpower", pokemonName).yellow())
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,14 @@ open class BattlePokemon(
|
||||
effectedPokemon = pokemon.clone(),
|
||||
postBattleEntityOperation = { entity -> entity.discard() }
|
||||
)
|
||||
|
||||
fun playerOwned(pokemon: Pokemon): BattlePokemon = BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = pokemon,
|
||||
postBattleEntityOperation = { entity ->
|
||||
entity.effects.wipe()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val uuid: UUID
|
||||
@@ -72,10 +80,11 @@ open class BattlePokemon(
|
||||
val contextManager = ContextManager()
|
||||
|
||||
open fun getName(): MutableText {
|
||||
val displayPokemon = getIllusion()?.effectedPokemon ?: effectedPokemon
|
||||
return if (actor is PokemonBattleActor || actor is MultiPokemonBattleActor) {
|
||||
effectedPokemon.getDisplayName()
|
||||
displayPokemon.getDisplayName()
|
||||
} else {
|
||||
battleLang("owned_pokemon", actor.getName(), effectedPokemon.getDisplayName())
|
||||
battleLang("owned_pokemon", actor.getName(), displayPokemon.getDisplayName())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,4 +103,6 @@ open class BattlePokemon(
|
||||
fun writeVariables(struct: VariableStruct) {
|
||||
effectedPokemon.writeVariables(struct)
|
||||
}
|
||||
|
||||
fun getIllusion(): BattlePokemon? = this.actor.activePokemon.find { it.battlePokemon == this }?.illusion
|
||||
}
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.entity
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
@@ -22,7 +21,6 @@ import com.cobblemon.mod.common.api.scheduling.lerpOnClient
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
|
||||
@@ -34,11 +32,8 @@ import com.cobblemon.mod.common.util.MovingSoundInstance
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import java.lang.Float.min
|
||||
import kotlin.math.abs
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.data.TrackedData
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Hand
|
||||
|
||||
@@ -45,7 +45,7 @@ object SpawnAllPokemon {
|
||||
for (species in PokemonSpecies.implemented) {
|
||||
if (species.nationalPokedexNumber in range) {
|
||||
LOGGER.debug(species.name)
|
||||
species.create().sendOut(player.world as ServerWorld, player.pos)
|
||||
species.create().sendOut(player.world as ServerWorld, player.pos, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.events.pokemon.ShoulderMountEvent
|
||||
import com.cobblemon.mod.common.api.interaction.PokemonEntityInteraction
|
||||
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
@@ -41,6 +42,8 @@ import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonNavigation
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.goals.*
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.EffectTracker
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
|
||||
@@ -209,9 +212,16 @@ open class PokemonEntity(
|
||||
PokemonServerDelegate()
|
||||
}
|
||||
|
||||
/** The effects that are modifying this entity. */
|
||||
var effects: EffectTracker = EffectTracker(this)
|
||||
|
||||
/** The species exposed to the client and used on entity spawn. */
|
||||
val exposedSpecies: Species get() = this.effects.mockEffect?.exposedSpecies ?: this.pokemon.species
|
||||
|
||||
/** The form exposed to the client and used for calculating hitbox and height. */
|
||||
val exposedForm: FormData get() = this.effects.mockEffect?.exposedForm ?: this.pokemon.form
|
||||
|
||||
init {
|
||||
dataTracker.set(SPECIES, pokemon.species.resourceIdentifier.toString())
|
||||
dataTracker.set(NICKNAME, pokemon.nickname ?: Text.empty())
|
||||
delegate.initialize(this)
|
||||
delegate.changePokemon(pokemon)
|
||||
calculateDimensions()
|
||||
@@ -239,6 +249,11 @@ open class PokemonEntity(
|
||||
|
||||
override fun onTrackedDataSet(data: TrackedData<*>) {
|
||||
super.onTrackedDataSet(data)
|
||||
// "But it's imposs-" shut up nerd, it happens during super construction and that's before delegate is assigned by class construction
|
||||
if (delegate != null) {
|
||||
delegate.onTrackedDataSet(data)
|
||||
}
|
||||
// common datatracker handling
|
||||
when (data) {
|
||||
SPECIES -> calculateDimensions()
|
||||
POSE_TYPE -> {
|
||||
@@ -258,10 +273,6 @@ open class PokemonEntity(
|
||||
}
|
||||
}
|
||||
}
|
||||
// "But it's imposs-" shut up nerd, it happens during super construction and that's before delegate is assigned by class construction
|
||||
if (delegate != null) {
|
||||
delegate.onTrackedDataSet(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun canWalkOnFluid(state: FluidState): Boolean {
|
||||
@@ -425,6 +436,9 @@ open class PokemonEntity(
|
||||
nbt.putBoolean(DataKeys.POKEMON_COUNTS_TOWARDS_SPAWN_CAP, false)
|
||||
}
|
||||
|
||||
// save active effects
|
||||
nbt.put(DataKeys.ENTITY_EFFECTS, effects.saveToNbt())
|
||||
|
||||
CobblemonEvents.POKEMON_ENTITY_SAVE.post(PokemonEntitySaveEvent(this, nbt))
|
||||
|
||||
return super.writeNbt(nbt)
|
||||
@@ -466,9 +480,7 @@ open class PokemonEntity(
|
||||
Pokemon()
|
||||
}
|
||||
}
|
||||
dataTracker.set(SPECIES, pokemon.species.resourceIdentifier.toString())
|
||||
dataTracker.set(NICKNAME, pokemon.nickname ?: Text.empty())
|
||||
dataTracker.set(LABEL_LEVEL, pokemon.level)
|
||||
|
||||
val savedBattleId = if (nbt.containsUuid(DataKeys.POKEMON_BATTLE_ID)) nbt.getUuid(DataKeys.POKEMON_BATTLE_ID) else null
|
||||
if (savedBattleId != null) {
|
||||
val battle = BattleRegistry.getBattle(savedBattleId)
|
||||
@@ -476,6 +488,14 @@ open class PokemonEntity(
|
||||
battleId = savedBattleId
|
||||
}
|
||||
}
|
||||
|
||||
// apply active effects
|
||||
if (nbt.contains(DataKeys.ENTITY_EFFECTS)) effects.loadFromNBT(nbt.getCompound(DataKeys.ENTITY_EFFECTS))
|
||||
|
||||
// init dataTracker
|
||||
dataTracker.set(SPECIES, effects.mockEffect?.mock?.species ?: pokemon.species.resourceIdentifier.toString())
|
||||
dataTracker.set(NICKNAME, pokemon.nickname ?: Text.empty())
|
||||
dataTracker.set(LABEL_LEVEL, pokemon.level)
|
||||
dataTracker.set(POSE_TYPE, PoseType.valueOf(nbt.getString(DataKeys.POKEMON_POSE_TYPE)))
|
||||
dataTracker.set(BEHAVIOUR_FLAGS, nbt.getByte(DataKeys.POKEMON_BEHAVIOUR_FLAGS))
|
||||
|
||||
@@ -679,13 +699,14 @@ open class PokemonEntity(
|
||||
}
|
||||
|
||||
override fun getDimensions(pose: EntityPose): EntityDimensions {
|
||||
val scale = pokemon.form.baseScale * pokemon.scaleModifier
|
||||
return pokemon.form.hitbox.scaled(scale)
|
||||
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.scaleModifier)
|
||||
return this.exposedForm.hitbox.scaled(scale)
|
||||
}
|
||||
|
||||
override fun canTakeDamage() = super.canTakeDamage() && !isBusy
|
||||
override fun damage(source: DamageSource?, amount: Float): Boolean {
|
||||
return if (super.damage(source, amount)) {
|
||||
effects.mockEffect?.takeIf { it is IllusionEffect && this.battleId == null }?.end(this)
|
||||
if (this.health == 0F) {
|
||||
pokemon.currentHealth = 0
|
||||
} else {
|
||||
@@ -710,7 +731,7 @@ open class PokemonEntity(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEyeHeight(pose: EntityPose): Float = this.pokemon.form.eyeHeight(this)
|
||||
override fun getEyeHeight(pose: EntityPose): Float = this.exposedForm.eyeHeight(this)
|
||||
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
override fun getActiveEyeHeight(pose: EntityPose, dimensions: EntityDimensions): Float {
|
||||
@@ -720,7 +741,7 @@ open class PokemonEntity(
|
||||
if (this.pokemon == null) {
|
||||
return super.getActiveEyeHeight(pose, dimensions)
|
||||
}
|
||||
return this.pokemon.form.eyeHeight(this)
|
||||
return this.exposedForm.eyeHeight(this)
|
||||
}
|
||||
|
||||
fun setBehaviourFlag(flag: PokemonBehaviourFlag, on: Boolean) {
|
||||
|
||||
+23
-4
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.entity.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
@@ -36,6 +37,10 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
lateinit var entity: PokemonEntity
|
||||
var acknowledgedHPStat = -1
|
||||
|
||||
/** Mocked properties exposed to the client [PokemonEntity]. */
|
||||
private val mock: PokemonProperties?
|
||||
get() = entity.effects.mockEffect?.mock
|
||||
|
||||
override fun changePokemon(pokemon: Pokemon) {
|
||||
updatePathfindingPenalties(pokemon)
|
||||
entity.initGoals()
|
||||
@@ -157,11 +162,15 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
entity.remove(Entity.RemovalReason.DISCARDED)
|
||||
}
|
||||
|
||||
entity.dataTracker.set(PokemonEntity.SPECIES, entity.pokemon.species.resourceIdentifier.toString())
|
||||
if (entity.dataTracker.get(PokemonEntity.NICKNAME) != entity.pokemon.nickname) {
|
||||
entity.dataTracker.set(PokemonEntity.NICKNAME, entity.pokemon.nickname ?: Text.empty())
|
||||
val trackedSpecies = mock?.species ?: entity.pokemon.species.resourceIdentifier.toString()
|
||||
val trackedNickname = mock?.nickname ?: entity.pokemon.nickname ?: Text.empty()
|
||||
val trackedAspects = mock?.aspects ?: entity.pokemon.aspects
|
||||
|
||||
entity.dataTracker.set(PokemonEntity.SPECIES, trackedSpecies)
|
||||
if (entity.dataTracker.get(PokemonEntity.NICKNAME) != trackedNickname) {
|
||||
entity.dataTracker.set(PokemonEntity.NICKNAME, trackedNickname)
|
||||
}
|
||||
entity.dataTracker.set(PokemonEntity.ASPECTS, entity.pokemon.aspects)
|
||||
entity.dataTracker.set(PokemonEntity.ASPECTS, trackedAspects)
|
||||
entity.dataTracker.set(PokemonEntity.LABEL_LEVEL, entity.pokemon.level)
|
||||
entity.dataTracker.set(PokemonEntity.MOVING, !entity.navigation.isIdle)
|
||||
|
||||
@@ -197,6 +206,16 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
}
|
||||
|
||||
override fun updatePostDeath() {
|
||||
// clear active effects before proceeding
|
||||
if (entity.deathTime == 0) {
|
||||
entity.effects.wipe()
|
||||
entity.deathTime = 1
|
||||
return
|
||||
}
|
||||
else if (entity.effects.progress?.isDone == false) {
|
||||
return
|
||||
}
|
||||
|
||||
entity.dataTracker.set(PokemonEntity.DYING_EFFECTS_STARTED, true)
|
||||
++entity.deathTime
|
||||
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.effects
|
||||
|
||||
import com.cobblemon.mod.common.api.entity.pokemon.EntityEffect
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* An [EntityEffect] that's applied due to a battle-related condition.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 5th, 2024
|
||||
*/
|
||||
abstract class BattleEffect : EntityEffect {
|
||||
|
||||
open val battleOnly = false
|
||||
|
||||
override fun start(entity: PokemonEntity): CompletableFuture<PokemonEntity>? {
|
||||
val progress = entity.effects.progress?.isDone
|
||||
if (progress == true || progress == null) {
|
||||
val future = CompletableFuture<PokemonEntity>()
|
||||
entity.effects.progress = future
|
||||
apply(entity, future)
|
||||
return future
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun end(entity: PokemonEntity): CompletableFuture<PokemonEntity>? {
|
||||
val progress = entity.effects.progress?.isDone
|
||||
if (progress == true || progress == null) {
|
||||
val future = CompletableFuture<PokemonEntity>()
|
||||
entity.effects.progress = future
|
||||
revert(entity, future)
|
||||
return future
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the effect to the provided [PokemonEntity].
|
||||
*
|
||||
* @param future The future to complete once the effect is applied.
|
||||
*/
|
||||
protected abstract fun apply(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>)
|
||||
|
||||
/**
|
||||
* Reverts the effect from the provided [PokemonEntity].
|
||||
*
|
||||
* @param future The future to complete once the effect is reverted.
|
||||
*/
|
||||
protected abstract fun revert(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>)
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.effects
|
||||
|
||||
import com.cobblemon.mod.common.api.entity.pokemon.EntityEffect
|
||||
import com.cobblemon.mod.common.api.entity.pokemon.MocKEffect
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* The [EntityEffect]s that are active for a [PokemonEntity].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since March 10th, 2024
|
||||
*/
|
||||
class EffectTracker(val entity: PokemonEntity) {
|
||||
|
||||
/** The progression of the latest [EntityEffect] applied to an entity. */
|
||||
var progress: CompletableFuture<PokemonEntity>? = null
|
||||
|
||||
/** When present, alters the client-side appearance of the [PokemonEntity]. */
|
||||
var mockEffect: MocKEffect? = null
|
||||
|
||||
/** Clears all active [EntityEffect]s gracefully. */
|
||||
fun wipe(): CompletableFuture<PokemonEntity>? {
|
||||
return mockEffect?.end(entity)
|
||||
}
|
||||
|
||||
/** Abruptly clears all active [EntityEffect]s. */
|
||||
fun forceWipe() {
|
||||
mockEffect = null
|
||||
}
|
||||
|
||||
fun saveToNbt(): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
mockEffect?.let { effect -> nbt.put(DataKeys.ENTITY_EFFECT_MOCK, effect.saveToNbt()) }
|
||||
return nbt
|
||||
}
|
||||
|
||||
fun loadFromNBT(nbt: NbtCompound) {
|
||||
if (nbt.contains(DataKeys.ENTITY_EFFECT_MOCK)) {
|
||||
val mockTag = nbt.getCompound(DataKeys.ENTITY_EFFECT_MOCK)
|
||||
this.mockEffect = EntityEffect.loadFromNbt(mockTag)?.takeIf { it is MocKEffect }?.let { it as MocKEffect }
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.effects
|
||||
|
||||
import com.cobblemon.mod.common.api.entity.pokemon.*
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* A [BattleEffect] that alters a [PokemonEntity] to be disguised as a target [Pokemon].
|
||||
*
|
||||
* @param disguise The [Pokemon] to use as a disguise.
|
||||
* @author Segfault Guy
|
||||
* @since March 5th, 2024
|
||||
*/
|
||||
class IllusionEffect(
|
||||
override var mock: PokemonProperties = PokemonProperties(),
|
||||
override var scale: Float = 1.0F
|
||||
) : BattleEffect(), MocKEffect {
|
||||
|
||||
constructor(disguise: Pokemon) : this(
|
||||
mock = disguise.createPokemonProperties(PokemonPropertyExtractor.ILLUSION),
|
||||
scale = disguise.form.baseScale * disguise.scaleModifier
|
||||
)
|
||||
|
||||
override fun apply(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>) {
|
||||
entity.effects.mockEffect = this
|
||||
future.complete(entity)
|
||||
}
|
||||
|
||||
override fun revert(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>) {
|
||||
entity.effects.mockEffect = null
|
||||
afterOnServer(seconds = 1.0F) {
|
||||
entity.cry()
|
||||
future.complete(entity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveToNbt(): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
nbt.putString(DataKeys.ENTITY_EFFECT_ID, ID)
|
||||
nbt.put(DataKeys.POKEMON_ENTITY_MOCK, mock.saveToNBT())
|
||||
nbt.putFloat(DataKeys.POKEMON_ENTITY_SCALE, scale)
|
||||
return nbt
|
||||
}
|
||||
|
||||
override fun loadFromNBT(nbt: NbtCompound) {
|
||||
if (nbt.contains(DataKeys.POKEMON_ENTITY_MOCK)) this.mock = PokemonProperties().loadFromNBT(nbt.getCompound(DataKeys.POKEMON_ENTITY_MOCK))
|
||||
if (nbt.contains(DataKeys.POKEMON_ENTITY_SCALE)) this.scale = nbt.getFloat(DataKeys.POKEMON_ENTITY_SCALE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = "ILLUSION"
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.effects
|
||||
|
||||
import com.cobblemon.mod.common.api.entity.pokemon.MocKEffect
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* A [BattleEffect] that alters a [PokemonEntity] to look like a target [Pokemon].
|
||||
*
|
||||
* @param mimic The [Pokemon] to copy.
|
||||
* @author Segfault Guy
|
||||
* @since March 5th, 2024
|
||||
*/
|
||||
class TransformEffect(
|
||||
override var mock: PokemonProperties = PokemonProperties(),
|
||||
override var scale: Float = 1.0F
|
||||
) : BattleEffect(), MocKEffect {
|
||||
|
||||
constructor(mimic: Pokemon) : this(
|
||||
mock = mimic.createPokemonProperties(PokemonPropertyExtractor.TRANSFORM),
|
||||
scale = mimic.form.baseScale * mimic.scaleModifier
|
||||
)
|
||||
|
||||
override fun apply(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>) {
|
||||
entity.effects.mockEffect = this
|
||||
afterOnServer(seconds = 1.0F) {
|
||||
entity.cry()
|
||||
future.complete(entity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun revert(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>) {
|
||||
entity.effects.mockEffect = null
|
||||
entity.cry()
|
||||
future.complete(entity)
|
||||
}
|
||||
|
||||
override fun saveToNbt(): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
nbt.putString(DataKeys.ENTITY_EFFECT_MOCK, ID)
|
||||
nbt.put(DataKeys.POKEMON_ENTITY_MOCK, mock.saveToNBT())
|
||||
nbt.putFloat(DataKeys.POKEMON_ENTITY_SCALE, scale)
|
||||
return nbt
|
||||
}
|
||||
|
||||
override fun loadFromNBT(nbt: NbtCompound) {
|
||||
if (nbt.contains(DataKeys.POKEMON_ENTITY_MOCK)) this.mock = PokemonProperties().loadFromNBT(nbt.getCompound(DataKeys.POKEMON_ENTITY_MOCK))
|
||||
if (nbt.contains(DataKeys.POKEMON_ENTITY_SCALE)) this.scale = nbt.getFloat(DataKeys.POKEMON_ENTITY_SCALE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = "TRANSFORM "
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -64,7 +64,7 @@ class BattleInitializePacket() : NetworkPacket<BattleInitializePacket> {
|
||||
showdownId = actor.showdownId,
|
||||
displayName = actor.getName(),
|
||||
activePokemon = actor.activePokemon.map { it.battlePokemon?.let {
|
||||
pkm -> ActiveBattlePokemonDTO.fromPokemon(pkm, allySide == side)
|
||||
pkm -> ActiveBattlePokemonDTO.fromPokemon(pkm, allySide == side, illusion = it.illusion)
|
||||
} },
|
||||
type = actor.type
|
||||
)
|
||||
@@ -156,18 +156,19 @@ class BattleInitializePacket() : NetworkPacket<BattleInitializePacket> {
|
||||
val statChanges: MutableMap<Stat, Int>
|
||||
) {
|
||||
companion object {
|
||||
fun fromPokemon(battlePokemon: BattlePokemon, isAlly: Boolean): ActiveBattlePokemonDTO {
|
||||
fun fromPokemon(battlePokemon: BattlePokemon, isAlly: Boolean, illusion: BattlePokemon?): ActiveBattlePokemonDTO {
|
||||
val pokemon = battlePokemon.effectedPokemon
|
||||
val disguise = if (isAlly) pokemon else illusion?.effectedPokemon ?: pokemon
|
||||
val hpValue = if (isAlly) pokemon.currentHealth.toFloat() else pokemon.currentHealth.toFloat() / pokemon.hp
|
||||
return ActiveBattlePokemonDTO(
|
||||
uuid = pokemon.uuid,
|
||||
displayName = pokemon.getDisplayName(),
|
||||
properties = pokemon.createPokemonProperties(
|
||||
uuid = disguise.uuid,
|
||||
displayName = disguise.getDisplayName(),
|
||||
properties = disguise.createPokemonProperties(
|
||||
PokemonPropertyExtractor.SPECIES,
|
||||
PokemonPropertyExtractor.LEVEL,
|
||||
PokemonPropertyExtractor.GENDER
|
||||
),
|
||||
aspects = pokemon.aspects,
|
||||
aspects = disguise.aspects,
|
||||
status = pokemon.status?.status,
|
||||
hpValue = hpValue,
|
||||
maxHp = pokemon.hp.toFloat(),
|
||||
|
||||
+2
-1
@@ -26,7 +26,8 @@ class BattleSwitchPokemonPacket(val pnx: String, val newPokemon: BattleInitializ
|
||||
|
||||
override val id = ID
|
||||
|
||||
constructor(pnx: String, newPokemon: BattlePokemon, isAlly: Boolean) : this(pnx, BattleInitializePacket.ActiveBattlePokemonDTO.fromPokemon(newPokemon, isAlly), isAlly)
|
||||
constructor(pnx: String, newPokemon: BattlePokemon, isAlly: Boolean, illusion: BattlePokemon?) :
|
||||
this(pnx, BattleInitializePacket.ActiveBattlePokemonDTO.fromPokemon(newPokemon, isAlly, illusion), isAlly)
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(pnx)
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class SpawnPokemonPacket(
|
||||
constructor(entity: PokemonEntity, vanillaSpawnPacket: EntitySpawnS2CPacket) : this(
|
||||
entity.ownerUuid,
|
||||
entity.pokemon.scaleModifier,
|
||||
entity.pokemon.species,
|
||||
entity.exposedSpecies,
|
||||
entity.pokemon.form,
|
||||
entity.pokemon.aspects,
|
||||
entity.battleId,
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.cobblemon.mod.common.api.types.tera.TeraType
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraTypes
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.net.messages.client.PokemonUpdatePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_DURATION
|
||||
@@ -459,10 +460,11 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
return this.form.showdownId()
|
||||
}
|
||||
|
||||
fun sendOut(level: ServerWorld, position: Vec3d, mutation: (PokemonEntity) -> Unit = {}): PokemonEntity? {
|
||||
fun sendOut(level: ServerWorld, position: Vec3d, illusion: IllusionEffect?, mutation: (PokemonEntity) -> Unit = {}): PokemonEntity? {
|
||||
CobblemonEvents.POKEMON_SENT_PRE.postThen(PokemonSentPreEvent(this, level, position)) {
|
||||
SeasonFeatureHandler.updateSeason(this, level, position.toBlockPos())
|
||||
val entity = PokemonEntity(level, this)
|
||||
illusion?.start(entity)
|
||||
entity.setPositionSafely(position)
|
||||
mutation(entity)
|
||||
level.spawnEntity(entity)
|
||||
@@ -478,16 +480,17 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
position: Vec3d,
|
||||
battleId: UUID? = null,
|
||||
doCry: Boolean = true,
|
||||
illusion: IllusionEffect? = null,
|
||||
mutation: (PokemonEntity) -> Unit = {},
|
||||
): CompletableFuture<PokemonEntity> {
|
||||
// Handle special case of shouldered Cobblemon
|
||||
if (this.state is ShoulderedState) {
|
||||
return sendOutFromShoulder(source as ServerPlayerEntity, level, position, battleId, doCry, mutation)
|
||||
return sendOutFromShoulder(source as ServerPlayerEntity, level, position, battleId, doCry, illusion, mutation)
|
||||
}
|
||||
|
||||
// Proceed as normal for non-shouldered Cobblemon
|
||||
val future = CompletableFuture<PokemonEntity>()
|
||||
sendOut(level, position) {
|
||||
sendOut(level, position, illusion) {
|
||||
getOwnerPlayer()?.let{
|
||||
it.swingHand(Hand.MAIN_HAND, true)
|
||||
level.playSoundServer(it.pos, CobblemonSounds.POKE_BALL_THROW, volume = 0.6F)
|
||||
@@ -520,6 +523,7 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
targetPosition: Vec3d,
|
||||
battleId: UUID? = null,
|
||||
doCry: Boolean = true,
|
||||
illusion: IllusionEffect? = null,
|
||||
mutation: (PokemonEntity) -> Unit = {}
|
||||
): CompletableFuture<PokemonEntity> {
|
||||
val future = CompletableFuture<PokemonEntity>()
|
||||
@@ -534,7 +538,7 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
val rotatedOffset = Vec3d(shoulderHorizontalOffset, approxShoulderMonHight, 0.0).rotateY(-rotation * (Math.PI.toFloat() / 180f))
|
||||
val currentPosition = player.pos.add(rotatedOffset)
|
||||
|
||||
sendOut(level, currentPosition) {
|
||||
sendOut(level, currentPosition, illusion) {
|
||||
// Play some sound indicating hopping off
|
||||
level.playSoundServer(currentPosition, CobblemonSounds.PC_DROP, volume = 0.6F)
|
||||
|
||||
|
||||
+2
-2
@@ -90,7 +90,7 @@ object CobblemonHeldItemManager : BaseCobblemonHeldItemManager() {
|
||||
battle.broadcastChatMessage(battleLang("item.$itemID.start", battlerName))
|
||||
return
|
||||
}
|
||||
val sourceName = battleMessage.getSourceBattlePokemon(battle)?.getName() ?: Text.of("UNKNOWN")
|
||||
val sourceName = battleMessage.battlePokemonFromOptional(battle)?.getName() ?: Text.of("UNKNOWN")
|
||||
val itemName = this.nameOf(itemID)
|
||||
val effectId = effect.id
|
||||
val text = when (effectId) {
|
||||
@@ -126,7 +126,7 @@ object CobblemonHeldItemManager : BaseCobblemonHeldItemManager() {
|
||||
this.take(pokemon, itemID)
|
||||
return
|
||||
}
|
||||
val sourceName = battleMessage.getSourceBattlePokemon(battle)?.getName() ?: Text.of("UNKNOWN")
|
||||
val sourceName = battleMessage.battlePokemonFromOptional(battle)?.getName() ?: Text.of("UNKNOWN")
|
||||
val effect = battleMessage.effect()
|
||||
val text = when {
|
||||
effect?.id != null -> battleLang("enditem.${effect.id}", battlerName, itemName, sourceName)
|
||||
|
||||
@@ -68,6 +68,14 @@ object DataKeys {
|
||||
const val POKEMON_ORIGINAL_TRAINER = "PokemonOriginalTrainer"
|
||||
const val POKEMON_ORIGINAL_TRAINER_TYPE = "PokemonOriginalTrainerType"
|
||||
|
||||
// Entity effects
|
||||
const val ENTITY_EFFECTS = "EntityEffects"
|
||||
const val ENTITY_EFFECT_MOCK = "EntityEffectMock"
|
||||
const val ENTITY_EFFECT_ID = "EntityEffectID"
|
||||
|
||||
const val POKEMON_ENTITY_MOCK = "PokemonEntityMock"
|
||||
const val POKEMON_ENTITY_SCALE = "PokemonEntityScale"
|
||||
|
||||
// Evolution stuff
|
||||
const val POKEMON_EVOLUTIONS = "Evolutions"
|
||||
const val POKEMON_PENDING_EVOLUTIONS = "Pending"
|
||||
|
||||
@@ -1299,6 +1299,7 @@
|
||||
"cobblemon.battle.end.powertrick": "%1$s switched its Attack and Defense!",
|
||||
"cobblemon.battle.end.healblock": "%1$s's Heal Block wore off!",
|
||||
"cobblemon.battle.end.skydrop": "%1$s was freed from the Sky Drop!",
|
||||
"cobblemon.battle.end.illusion": "%1$s's illusion wore off!",
|
||||
|
||||
"cobblemon.battle.block.uproar": "But the uproar kept %1$s awake!",
|
||||
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user