Compare commits

...
18 Commits
Author SHA1 Message Date
CeeJay 874d1d9329 Battle ends if teleport succeeds. Don't broadcast messages for ended battles 2025-09-25 00:18:59 -04:00
CeeJay d29e688064 rollback InstructionSet.execute() changes 2025-09-25 00:18:03 -04:00
CeeJay b6802069d8 Allow battle to continue if Teleport didn't succeed 2025-09-24 18:50:36 -04:00
CeeJay a4b8aafa27 Merge branch 'main' into wild-teleporting 2025-09-23 01:10:12 -04:00
CeeJay c46a4d5936 try to offset vertically if teleport location is invalid 2025-09-04 01:08:11 -04:00
CeeJay c3c57f3f33 try using randomTeleport rather than homemade teleportation 2025-09-03 17:48:04 -04:00
CeeJay 0dba1e9f82 teleporting changelog 2025-08-22 13:51:25 -04:00
CeeJay 41457d8da8 battle properly ends after wild pokemon teleport
Resolves #391
2025-08-22 13:48:54 -04:00
CeeJay a8db098652 Merge remote-tracking branch 'origin/wild-teleporting' into wild-teleporting
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/interpreter/instructions/MoveInstruction.kt
2025-08-21 18:05:47 -04:00
CeeJay 1f241fc8dc add teleport sound 2025-08-21 18:03:23 -04:00
CeeJay 3921295fee send message in chat rather than battle ui 2025-08-21 15:17:17 -04:00
CeeJay 790e5e6c8b en_us and en_pt battleLang for a wild pokemon fleeing 2025-08-21 15:17:17 -04:00
CeeJay 2957cabf86 only end battle if pokemon successfully teleported away 2025-08-21 15:17:16 -04:00
CeeJay 6909c5ad6f wild pokemon use teleport -> teleports away 2025-08-21 15:17:16 -04:00
CeeJay c65d6bd83e send message in chat rather than battle ui 2025-08-21 15:16:33 -04:00
CeeJay dd6a526a49 en_us and en_pt battleLang for a wild pokemon fleeing 2025-08-21 14:28:31 -04:00
CeeJay b30b03fffd only end battle if pokemon successfully teleported away 2025-08-21 12:03:38 -04:00
CeeJay 1953538db5 wild pokemon use teleport -> teleports away 2025-08-21 01:38:55 -04:00
6 changed files with 97 additions and 12 deletions
+1
View File
@@ -457,6 +457,7 @@
- MoLang `run_action_effect` now works on Pokémon.
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
- Added entity (as `q.entity`) that executed the command to the `executemolangscript` command.
- A wild Pokémon using Teleport now ends the battle and causes the Pokémon to teleport a short distance away. Teleport still behaves the same in PvP battles.
- Refactored the following events to `.Pre` and `.Post` for consistency:
- `PokemonSentEvent`
- `ExperienceGainedEvent`
@@ -219,6 +219,7 @@ open class PokemonBattle(
}
fun broadcastChatMessage(component: Component) {
if (ended) {return}
chatLog.add(component)
sendSpectatorUpdate(BattleMessagePacket(component))
return actors.forEach { it.sendMessage(component) }
@@ -181,7 +181,7 @@ object ShowdownInterpreter {
while (iterator.hasNext()) {
val message = iterator.next()
val id = message.id.replace("|", "")
val instruction = updateInstructionParser[id]?.invoke(battle, instructionSet, message, iterator) ?: UnknownInstruction(message)
val instruction: InterpreterInstruction = updateInstructionParser[id]?.invoke(battle, instructionSet, message, iterator) ?: UnknownInstruction(message)
instructionSet.instructions.add(instruction)
}
}
@@ -15,6 +15,9 @@ import com.cobblemon.mod.common.CobblemonMemories
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.EntityBackedBattleActor
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.addBattleMessageFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
@@ -22,19 +25,24 @@ import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.*
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundEvents
import java.util.concurrent.CompletableFuture
import net.minecraft.world.entity.LivingEntity
import java.lang.Math.*
import kotlin.math.roundToInt
/**
* Format: |move|POKEMON|MOVE|TARGET
@@ -72,17 +80,27 @@ class MoveInstruction(
val pokemonName = userPokemon.getName()
ShowdownInterpreter.lastCauser[battle.battleId] = message
// For Spread targets the message data only gives the pnx strings. So we can't know what pokemon are actually targeted until the previous messages have been interpreted
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull() { poke -> poke.getPNX() == it }?.battlePokemon }
val spreadTargetPokemon =
spreadTargets.map { battle.activePokemon.firstOrNull() { poke -> poke.getPNX() == it }?.battlePokemon }
val targetPokemonEntity = targetPokemon?.entity
if (targetPokemonEntity != null) {
userPokemon.entity?.brain?.setMemory(CobblemonMemories.TARGETED_BATTLE_POKEMON, targetPokemonEntity.uuid)
userPokemon.entity?.brain?.setMemory(
CobblemonMemories.TARGETED_BATTLE_POKEMON,
targetPokemonEntity.uuid
)
}
userPokemon.effectedPokemon.let { pokemon ->
if (UseMoveEvolutionProgress.supports(pokemon, move)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
progress.updateProgress(UseMoveEvolutionProgress.Progress(move, progress.currentProgress().amount + 1))
val progress = pokemon.evolutionProxy.current()
.progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
progress.updateProgress(
UseMoveEvolutionProgress.Progress(
move,
progress.currentProgress().amount + 1
)
)
}
}
@@ -104,7 +122,7 @@ class MoveInstruction(
val providers = mutableListOf<Any>(battle)
userPokemon.effectedPokemon.entity?.let { entity ->
runtime.environment.query.addFunction("user") { entity.asMostSpecificMoLangValue()}
runtime.environment.query.addFunction("user") { entity.asMostSpecificMoLangValue() }
UsersProvider(entity)
}?.let(providers::add)
@@ -128,6 +146,24 @@ class MoveInstruction(
level = battle.players.firstOrNull()?.level()
)
if (isWildTeleport(battle)) {
var teleportSuccess = false
val teleportingEntity = userPokemon.originalPokemon.entity
if (teleportingEntity != null) {
teleportSuccess = tryTeleport(battle.players.firstOrNull()?.level() as ServerLevel, teleportingEntity, 10, 30, 32)
}
if (teleportSuccess){
battle.ended = true
battle.dispatchWaiting(1.0F) {
battle.actors.filterIsInstance<EntityBackedBattleActor<*>>().mapNotNull { it.entity }
.forEach { it.sendSystemMessage(battleLang("wild_flee", pokemonName).yellow()) }
battle.end()
}
}
}
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
val hitCountInstruction = subsequentInstructions.filterIsInstance<HitCountInstruction>().firstOrNull()
@@ -141,7 +177,8 @@ class MoveInstruction(
}
}
val hurtTargets = subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.expectedTarget }
val hurtTargets =
subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.expectedTarget }
runtime.environment.query.addFunction("hurt") { params ->
if (params.params.size == 0) {
return@addFunction DoubleValue(hurtTargets.isNotEmpty())
@@ -164,8 +201,52 @@ class MoveInstruction(
return@dispatch UntilDispatch { "effects" !in holds }.andThen {
val userPokemonId = userPokemon.entity?.uuid ?: return@andThen
val targets = hurtTargets.mapNotNull { it.entity }
userPokemonId.let { id -> targets.forEach { it.brain.setMemory(CobblemonMemories.TARGETED_BATTLE_POKEMON, id) } }
userPokemonId.let { id ->
targets.forEach {
it.brain.setMemory(
CobblemonMemories.TARGETED_BATTLE_POKEMON,
id
)
}
}
}
}
}
fun tryTeleport(
level: ServerLevel,
pokemon: PokemonEntity,
minRadius: Int,
maxRadius: Int,
attempts: Int
): Boolean {
val origin = pokemon.blockPosition()
val rng = level.random
var success = false
for (i in 0 until attempts) {
val dist = rng.nextInt(minRadius, maxRadius + 1)
val angle = rng.nextDouble() * (PI * 2.0)
val dx = (cos(angle) * dist)
val dz = (sin(angle) * dist)
val x = (origin.x + dx)
val yOffsetMax = rng.nextInt(5, 15)
val z = (origin.z + dz)
val yOffsets = (0..yOffsetMax).flatMap { listOf(it, -it) }.distinct()
//iterate over possible y distance, tries least vertical movement first
for (yOffset in yOffsets){
success = pokemon.randomTeleport(x, pokemon.position().y + yOffset, z, false)
if (success) {
level.playSoundServer(origin.toVec3d(), SoundEvents.PLAYER_TELEPORT)
return true
}
}
}
return false
}
fun isWildTeleport(battle: PokemonBattle) : Boolean {
return move.name == "teleport" && message.battlePokemon(0, battle)?.originalPokemon?.isWild() ?: false
}
}
@@ -900,6 +900,7 @@
"cobblemon.battle.lose": "%1$s be out of usable monsters!",
"cobblemon.battle.forfeit": "%1$s forfeited th' battle!",
"cobblemon.battle.flee": "Th' battle was fled.",
"cobblemon.battle.wild_flee": "Th' scallywag, %1$s abandoned ship.",
"cobblemon.battle.switch_option": "Switch monsters: ",
"cobblemon.battle.your_team": "Ye crew:",
"cobblemon.battle.must_switch": "Choose a monster to switch in",
@@ -2700,6 +2700,7 @@
"cobblemon.battle.lose": "%1$s is out of usable Pokémon!",
"cobblemon.battle.forfeit": "%1$s forfeited the battle!",
"cobblemon.battle.flee": "The battle was fled.",
"cobblemon.battle.wild_flee": "The wild %1$s fled.",
"cobblemon.battle.switch_option": "Switch Pokémon: ",
"cobblemon.battle.your_team": "Your Team:",
"cobblemon.battle.must_switch": "Choose a Pokémon to switch in",