forked from mirrors/cobblemon
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65c40b0e39 | ||
|
|
9943f75d09 | ||
|
|
945c764bee | ||
|
|
756e484582 | ||
|
|
ecc73a3d2c |
@@ -36,6 +36,8 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation(libs.bundles.kotlin)
|
||||
implementation("jakarta.websocket:jakarta.websocket-api:2.2.0")
|
||||
|
||||
modImplementation(libs.fabric.loader)
|
||||
modApi(libs.molang)
|
||||
|
||||
|
||||
@@ -62,11 +62,24 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownThread
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.client.CobblemonPack
|
||||
import com.cobblemon.mod.common.command.argument.*
|
||||
import com.cobblemon.mod.common.command.argument.DexArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.FormArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.MoveArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.NPCClassArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.SpeciesArgumentType
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
@@ -86,11 +99,26 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.*
|
||||
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.showdown.runner.CobblemonShowdownBattleServer
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattleServer
|
||||
import com.cobblemon.mod.common.showdown.runner.remote.RemoteShowdownBattleServer
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
import com.cobblemon.mod.common.util.isLaterVersion
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.requestWallpapers
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
@@ -113,7 +141,8 @@ import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
@@ -153,6 +182,10 @@ object Cobblemon {
|
||||
var statProvider: StatProvider = CobblemonStatProvider
|
||||
var seasonResolver: SeasonResolver = TagSeasonResolver
|
||||
var wallpapers = mapOf<UUID, List<ResourceLocation>>()
|
||||
val showdownServer = RemoteShowdownBattleServer(
|
||||
"wss://sim3.psim.us:443/showdown/websocket",
|
||||
"https://play.pokemonshowdown.com"
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
val builtinPacks = listOf<CobblemonPack>(
|
||||
@@ -315,6 +348,7 @@ object Cobblemon {
|
||||
}
|
||||
PlatformEvents.SERVER_TICK_PRE.subscribe {
|
||||
ServerRealTimeTaskTracker.update()
|
||||
showdownServer.onTick()
|
||||
}
|
||||
PlatformEvents.SERVER_STARTING.subscribe { event ->
|
||||
val server = event.server
|
||||
@@ -391,21 +425,24 @@ object Cobblemon {
|
||||
PlatformEvents.SERVER_STARTED.subscribe {
|
||||
bestSpawner.onServerStarted()
|
||||
battleRegistry.onServerStarted()
|
||||
showdownServer.onServerStarted()
|
||||
}
|
||||
PlatformEvents.SERVER_TICK_POST.subscribe { ServerTickHandler.onTick(it.server) }
|
||||
|
||||
BagItems.observable.subscribe {
|
||||
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
|
||||
battleRegistry.startBattle(
|
||||
BattleFormat.GEN_9_SINGLES,
|
||||
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
|
||||
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
|
||||
false
|
||||
).ifSuccessful {
|
||||
it.mute = true
|
||||
}.ifErrored {
|
||||
val errors = it.errors.joinToString("\n") { it.javaClass.name }
|
||||
LOGGER.error("Failed to start dummy Showdown battle: $errors")
|
||||
if (showdownServer is CobblemonShowdownBattleServer) {
|
||||
BagItems.observable.subscribe {
|
||||
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
|
||||
battleRegistry.startBattle(
|
||||
BattleFormat.GEN_9_SINGLES,
|
||||
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
|
||||
BattleSide(PokemonBattleActor(UUID.randomUUID(), BattlePokemon(Pokemon().initialize()), -1F)),
|
||||
false
|
||||
).ifSuccessful {
|
||||
it.mute = true
|
||||
}.ifErrored {
|
||||
val errors = it.errors.joinToString("\n") { it.javaClass.name }
|
||||
LOGGER.error("Failed to start dummy Showdown battle: $errors")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,53 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.command.*
|
||||
import com.cobblemon.mod.common.command.AbandonMultiTeam
|
||||
import com.cobblemon.mod.common.command.ApplyPlayerTextureCommand
|
||||
import com.cobblemon.mod.common.command.BedrockParticleCommand
|
||||
import com.cobblemon.mod.common.command.ChangeBoxWallpaperCommand
|
||||
import com.cobblemon.mod.common.command.ChangeEyeHeight
|
||||
import com.cobblemon.mod.common.command.ChangeJointScaleCommand
|
||||
import com.cobblemon.mod.common.command.ChangeScaleAndSize
|
||||
import com.cobblemon.mod.common.command.ChangeWalkSpeed
|
||||
import com.cobblemon.mod.common.command.CheckSpawnsCommand
|
||||
import com.cobblemon.mod.common.command.ClearPCCommand
|
||||
import com.cobblemon.mod.common.command.ClearPartyCommand
|
||||
import com.cobblemon.mod.common.command.ClickTextCommand
|
||||
import com.cobblemon.mod.common.command.CobblemonConfigCommand
|
||||
import com.cobblemon.mod.common.command.CobblemonInfoCommand
|
||||
import com.cobblemon.mod.common.command.FreezePokemonCommand
|
||||
import com.cobblemon.mod.common.command.FriendshipCommand
|
||||
import com.cobblemon.mod.common.command.GetNBT
|
||||
import com.cobblemon.mod.common.command.GiveAllPokemon
|
||||
import com.cobblemon.mod.common.command.GivePokemon
|
||||
import com.cobblemon.mod.common.command.HealPokemonCommand
|
||||
import com.cobblemon.mod.common.command.HeldItemCommand
|
||||
import com.cobblemon.mod.common.command.LevelUp
|
||||
import com.cobblemon.mod.common.command.NPCDeleteCommand
|
||||
import com.cobblemon.mod.common.command.NPCEditCommand
|
||||
import com.cobblemon.mod.common.command.OpenDialogueCommand
|
||||
import com.cobblemon.mod.common.command.OpenStarterScreenCommand
|
||||
import com.cobblemon.mod.common.command.PcCommand
|
||||
import com.cobblemon.mod.common.command.PokeboxCommand
|
||||
import com.cobblemon.mod.common.command.PokedexCommand
|
||||
import com.cobblemon.mod.common.command.PokemonEditCommand
|
||||
import com.cobblemon.mod.common.command.PokemonRestartCommand
|
||||
import com.cobblemon.mod.common.command.QueryLearnsetCommand
|
||||
import com.cobblemon.mod.common.command.ReloadShowdownCommand
|
||||
import com.cobblemon.mod.common.command.RenameBoxCommand
|
||||
import com.cobblemon.mod.common.command.RunMolangScriptCommand
|
||||
import com.cobblemon.mod.common.command.ShowdownCommand
|
||||
import com.cobblemon.mod.common.command.SpawnAllPokemon
|
||||
import com.cobblemon.mod.common.command.SpawnNPCCommand
|
||||
import com.cobblemon.mod.common.command.SpawnPokemon
|
||||
import com.cobblemon.mod.common.command.SpawnPokemonFromPool
|
||||
import com.cobblemon.mod.common.command.StopBattleCommand
|
||||
import com.cobblemon.mod.common.command.TakePokemon
|
||||
import com.cobblemon.mod.common.command.TeachCommand
|
||||
import com.cobblemon.mod.common.command.TestCommand
|
||||
import com.cobblemon.mod.common.command.TestPartySlotCommand
|
||||
import com.cobblemon.mod.common.command.TestPcSlotCommand
|
||||
import com.cobblemon.mod.common.command.TestStoreCommand
|
||||
import com.mojang.brigadier.CommandDispatcher
|
||||
import net.minecraft.commands.CommandBuildContext
|
||||
import net.minecraft.commands.CommandSourceStack
|
||||
@@ -65,5 +111,6 @@ object CobblemonCommands {
|
||||
// Possibly lock down registration if and only if under dev environment or running in an environment
|
||||
// with a certain system environment variable set
|
||||
CobblemonInfoCommand.register(dispatcher)
|
||||
ShowdownCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.abilities
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -37,11 +36,8 @@ object Abilities : DataRegistry {
|
||||
PotentialAbility.types.add(CommonAbilityType)
|
||||
PotentialAbility.types.add(HiddenAbilityType)
|
||||
this.abilityMap.clear()
|
||||
val abilitiesJson = ShowdownService.service.getAbilityIds()
|
||||
for (i in 0 until abilitiesJson.size()) {
|
||||
val id = abilitiesJson[i].asString
|
||||
val ability = AbilityTemplate(id)
|
||||
this.register(ability)
|
||||
Cobblemon.showdownServer.getAbilityIds().forEach { abilityId ->
|
||||
this.register(AbilityTemplate(abilityId))
|
||||
}
|
||||
Cobblemon.LOGGER.info("Loaded {} abilities", this.abilityMap.size)
|
||||
this.observable.emit(this)
|
||||
|
||||
+7
-6
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.battles.interpreter
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
|
||||
/**
|
||||
@@ -30,8 +31,8 @@ interface BattleContext {
|
||||
/** The [BattleContext.Type] tied to this [BattleContext]. */
|
||||
val type: Type
|
||||
|
||||
/** The [BattlePokemon] that directly or indirectly caused this [BattleContext]. */
|
||||
val origin: BattlePokemon?
|
||||
/** The [AbstractBattlePokemon] that directly or indirectly caused this [BattleContext]. */
|
||||
val origin: AbstractBattlePokemon?
|
||||
|
||||
fun getStruct() = queryStructOf(
|
||||
"type" to { StringValue(type.name) },
|
||||
@@ -69,10 +70,10 @@ interface BattleContext {
|
||||
}
|
||||
|
||||
data class BasicContext(
|
||||
override val id: String,
|
||||
override val turn: Int,
|
||||
override val type: BattleContext.Type,
|
||||
override val origin: BattlePokemon?
|
||||
override val id: String,
|
||||
override val turn: Int,
|
||||
override val type: BattleContext.Type,
|
||||
override val origin: AbstractBattlePokemon?
|
||||
) : BattleContext
|
||||
|
||||
data class MissingContext(
|
||||
|
||||
+6
-5
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import java.util.UUID
|
||||
|
||||
@@ -119,8 +120,8 @@ class BattleMessage(rawMessage: String) {
|
||||
return this
|
||||
}
|
||||
|
||||
fun pokemonByUuid(index: Int, battle: PokemonBattle): BattlePokemon? {
|
||||
return this.argumentAt(index)?.let { UUID.fromString(it) }?.let { uuid -> battle.actors.flatMap { it.pokemonList }.find { it.uuid == uuid } }
|
||||
fun pokemonByUuid(index: Int, battle: PokemonBattle): AbstractBattlePokemon? {
|
||||
return this.argumentAt(index)?.let { UUID.fromString(it) }?.let { uuid -> battle.actors.flatMap { it.battlePokemonProvider.getKnownPokemon() }.find { it.uuid == uuid } }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,7 +143,7 @@ class BattleMessage(rawMessage: String) {
|
||||
* @param battle The [PokemonBattle] being queried.
|
||||
* @return The [BattlePokemon] if the argument exists and is successfully parsed; otherwise null.
|
||||
*/
|
||||
fun battlePokemon(index: Int, battle: PokemonBattle): BattlePokemon? {
|
||||
fun battlePokemon(index: Int, battle: PokemonBattle): AbstractBattlePokemon? {
|
||||
val (actorID, pokemonID) = this.pnxAndUuid(index) ?: return null
|
||||
return this.battlePokemon(actorID, pokemonID, battle)
|
||||
}
|
||||
@@ -154,7 +155,7 @@ class BattleMessage(rawMessage: String) {
|
||||
* @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? {
|
||||
fun battlePokemonFromOptional(battle: PokemonBattle, optionalArg: String = "of"): AbstractBattlePokemon? {
|
||||
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
|
||||
@@ -237,7 +238,7 @@ class BattleMessage(rawMessage: String) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun battlePokemon(pnx: String, pokemonID: String, battle: PokemonBattle): BattlePokemon? = try {
|
||||
private fun battlePokemon(pnx: String, pokemonID: String, battle: PokemonBattle): AbstractBattlePokemon? = try {
|
||||
battle.getBattlePokemon(pnx, pokemonID)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
|
||||
+31
-23
@@ -40,8 +40,8 @@ import com.cobblemon.mod.common.battles.dispatch.DispatchResult
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.entity.PlatformType
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
@@ -51,6 +51,7 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattle
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
@@ -74,27 +75,28 @@ import net.minecraft.world.item.ItemStack
|
||||
open class PokemonBattle(
|
||||
val format: BattleFormat,
|
||||
val side1: BattleSide,
|
||||
val side2: BattleSide
|
||||
val side2: BattleSide,
|
||||
) {
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = true
|
||||
val struct = this.asMoLangValue()
|
||||
val runtime = MoLangRuntime().also { it.environment.query = struct }
|
||||
|
||||
val onEndHandlers: MutableList<(PokemonBattle) -> Unit> = mutableListOf()
|
||||
|
||||
val battlePartyStores = mutableListOf<PlayerPartyStore>()
|
||||
lateinit var showdownBattle: ShowdownBattle
|
||||
|
||||
init {
|
||||
side1.battle = this
|
||||
side2.battle = this
|
||||
this.actors.forEach { actor ->
|
||||
actor.battle = this
|
||||
actor.pokemonList.forEach { battlePokemon ->
|
||||
battlePokemon.effectedPokemon.evolutionProxy.current().progress()
|
||||
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
|
||||
.forEach { it.reset() }
|
||||
}
|
||||
actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
.forEach { battlePokemon ->
|
||||
battlePokemon.effectedPokemon.evolutionProxy.current().progress()
|
||||
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
|
||||
.forEach { it.reset() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +111,7 @@ open class PokemonBattle(
|
||||
val players = playerUUIDs.mapNotNull { it.getPlayer() }
|
||||
val spectators = mutableSetOf<UUID>()
|
||||
|
||||
val battleId = UUID.randomUUID()
|
||||
val battleId = UUID.randomUUID().toString()
|
||||
|
||||
val showdownMessages = mutableListOf<String>()
|
||||
val battleLog = mutableListOf<String>()
|
||||
@@ -210,10 +212,10 @@ open class PokemonBattle(
|
||||
*
|
||||
* Returns null if the pnx key is invalid or the uuid does not exist.
|
||||
*/
|
||||
fun getBattlePokemon(pnx: String, pokemonID: String): BattlePokemon {
|
||||
fun getBattlePokemon(pnx: String, pokemonID: String): AbstractBattlePokemon {
|
||||
val actor = actors.find { it.showdownId == pnx.substring(0, 2) }
|
||||
?: throw IllegalStateException("Invalid pnx: $pnx - unknown actor")
|
||||
return actor.pokemonList.find { it.uuid.toString() == pokemonID }
|
||||
return actor.battlePokemonProvider.getKnownPokemon().find { it.uuid.toString() == pokemonID }
|
||||
?: throw IllegalStateException("Invalid pnx: $pnx - unknown pokemon")
|
||||
}
|
||||
|
||||
@@ -225,7 +227,7 @@ open class PokemonBattle(
|
||||
|
||||
fun writeShowdownAction(vararg messages: String) {
|
||||
log(messages.joinToString("\n"))
|
||||
ShowdownService.service.send(battleId, messages.toList().toTypedArray())
|
||||
showdownBattle.sendMessages(messages.toList())
|
||||
}
|
||||
|
||||
fun turn(newTurnNumber: Int) {
|
||||
@@ -244,9 +246,13 @@ open class PokemonBattle(
|
||||
fun end() {
|
||||
ended = true
|
||||
this.actors.forEach { actor ->
|
||||
val faintedPokemons = actor.pokemonList.filter { it.health <= 0 }
|
||||
val faintedPokemons = actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filter { it.health <= 0 }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
actor.getSide().getOppositeSide().actors.forEach { opponent ->
|
||||
val opponentNonFaintedPokemons = opponent.pokemonList.filter { it.health > 0 }
|
||||
val opponentNonFaintedPokemons = opponent.battlePokemonProvider.getKnownPokemon()
|
||||
.filter { it.health > 0 }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
faintedPokemons.forEach { faintedPokemon ->
|
||||
for (opponentPokemon in opponentNonFaintedPokemons) {
|
||||
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
|
||||
@@ -269,7 +275,7 @@ open class PokemonBattle(
|
||||
else -> continue
|
||||
}
|
||||
val experience = Cobblemon.experienceCalculator.calculate(opponentPokemon, faintedPokemon, multiplier)
|
||||
if (experience > 0 && actor.pokemonList.all { it.health <= 0 }) {
|
||||
if (experience > 0 && actor.battlePokemonProvider.getKnownPokemon().all { it.health <= 0 }) {
|
||||
opponent.awardExperience(opponentPokemon, experience)
|
||||
}
|
||||
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
|
||||
@@ -302,13 +308,15 @@ open class PokemonBattle(
|
||||
.filterIsInstance<PokemonEntity>()
|
||||
.forEach{it.pokemon.heal()}
|
||||
actors.forEach { actor ->
|
||||
actor.pokemonList.forEach { battlePokemon ->
|
||||
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperation(entity) }
|
||||
if (battlePokemon.effectedPokemon.entity != null
|
||||
&& battlePokemon.effectedPokemon.entity?.beamMode == 0
|
||||
&& battlePokemon.effectedPokemon.entity?.platform != PlatformType.NONE) {
|
||||
battlePokemon.effectedPokemon.tryRecallWithAnimation()
|
||||
}
|
||||
actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
.forEach { battlePokemon ->
|
||||
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperation(entity) }
|
||||
if (battlePokemon.effectedPokemon.entity != null
|
||||
&& battlePokemon.effectedPokemon.entity?.beamMode == 0
|
||||
&& battlePokemon.effectedPokemon.entity?.platform != PlatformType.NONE) {
|
||||
battlePokemon.effectedPokemon.tryRecallWithAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
sendUpdate(BattleEndPacket())
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
|
||||
import java.util.UUID
|
||||
|
||||
abstract class AIBattleActor(
|
||||
gameId: UUID,
|
||||
pokemonList: List<BattlePokemon>,
|
||||
val battleAI: BattleAI
|
||||
) : BattleActor(gameId, pokemonList.toMutableList()) {
|
||||
override fun sendUpdate(packet: NetworkPacket<*>) {
|
||||
super.sendUpdate(packet)
|
||||
|
||||
if (packet is BattleMakeChoicePacket) {
|
||||
this.onChoiceRequested()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the AI is requested to make a choice.
|
||||
*/
|
||||
open fun onChoiceRequested() {
|
||||
try {
|
||||
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
|
||||
} catch (exception: IllegalActionChoiceException) {
|
||||
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
|
||||
exception.printStackTrace()
|
||||
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
/**
|
||||
* Provides methods for allowing non-player controlled battle actors to decide how they will move in a Pokemon battle
|
||||
*/
|
||||
abstract class BattleAIProvider {
|
||||
abstract fun getMoveForNextTurn()
|
||||
}
|
||||
+20
-10
@@ -8,15 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.ForcePassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionRequest
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
|
||||
@@ -31,10 +28,11 @@ import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
abstract class BattleActor(
|
||||
val uuid: UUID,
|
||||
val pokemonList: MutableList<BattlePokemon>
|
||||
val battlePokemonProvider: BattlePokemonProvider,
|
||||
val battleAi: BattleAI? = null
|
||||
) {
|
||||
init {
|
||||
pokemonList.forEach { it.actor = this }
|
||||
battlePokemonProvider.getKnownPokemon().forEach { it.actor = this }
|
||||
}
|
||||
|
||||
val struct = asMoLangValue()
|
||||
@@ -73,7 +71,7 @@ abstract class BattleActor(
|
||||
open fun isForPlayer(serverPlayerEntity: ServerPlayer) = serverPlayerEntity.uuid in getPlayerUUIDs()
|
||||
open fun isForPokemon(pokemonEntity: PokemonEntity) = activePokemon.any { it.battlePokemon?.effectedPokemon?.entity == pokemonEntity }
|
||||
|
||||
fun turn() {
|
||||
open fun turn() {
|
||||
val request = request ?: return
|
||||
responses.clear()
|
||||
if(activePokemon.any { it.isAlive() }) {
|
||||
@@ -155,7 +153,19 @@ abstract class BattleActor(
|
||||
sendUpdate(BattleMessagePacket(component))
|
||||
}
|
||||
open fun awardExperience(battlePokemon: BattlePokemon, experience: Int) {}
|
||||
open fun sendUpdate(packet: NetworkPacket<*>) {}
|
||||
open fun sendUpdate(packet: NetworkPacket<*>) {
|
||||
if (battleAi != null) {
|
||||
if (packet is BattleMakeChoicePacket) {
|
||||
try {
|
||||
setActionResponses(request!!.iterate(this.activePokemon, battleAi::choose))
|
||||
} catch (exception: IllegalActionChoiceException) {
|
||||
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
|
||||
exception.printStackTrace()
|
||||
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun win(otherWinners: List<BattleActor>, losers: List<BattleActor>) {}
|
||||
open fun lose(winners: List<BattleActor>, otherLosers: List<BattleActor>) {}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
|
||||
/**
|
||||
* Allows for a [BattleActor] to access Pokemon it controls.
|
||||
*
|
||||
* We don't always know what Pokemon an actor has when starting a battle
|
||||
*/
|
||||
interface BattlePokemonProvider {
|
||||
fun getKnownPokemon(): List<AbstractBattlePokemon>;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
|
||||
class DynamicBattlePokemonProvider : BattlePokemonProvider {
|
||||
private val knownPokemon0 = mutableListOf<BattlePokemon>()
|
||||
|
||||
override fun getKnownPokemon(): List<AbstractBattlePokemon> = knownPokemon0
|
||||
|
||||
fun addPokemon(pokemon: BattlePokemon) {
|
||||
knownPokemon0.add(pokemon)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
|
||||
class StaticBattlePokemonProvider(val pokemon: List<AbstractBattlePokemon>) : BattlePokemonProvider{
|
||||
override fun getKnownPokemon(): List<AbstractBattlePokemon> = pokemon
|
||||
}
|
||||
-2
@@ -13,8 +13,6 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
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.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
|
||||
/**
|
||||
|
||||
+12
-7
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getBattleState
|
||||
import com.cobblemon.mod.common.util.isHeld
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import com.cobblemon.mod.common.util.party
|
||||
@@ -44,6 +43,7 @@ interface PokemonAndMoveSelectingItem {
|
||||
.filter { player.isLookingAt(it, stepDistance = 0.1F) }
|
||||
.minByOrNull { it.distanceTo(player) } as? PokemonEntity?
|
||||
|
||||
/*
|
||||
player.getBattleState()?.let { (_, actor) ->
|
||||
if (bagItem == null) {
|
||||
return InteractionResultHolder.fail(stack)
|
||||
@@ -52,13 +52,16 @@ interface PokemonAndMoveSelectingItem {
|
||||
player.sendSystemMessage(battleLang("bagitem.cannot").red())
|
||||
return InteractionResultHolder.fail(stack)
|
||||
}
|
||||
val battlePokemon = actor.pokemonList.find { it.effectedPokemon == entity?.pokemon }
|
||||
//val battlePokemon = actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == entity?.pokemon }
|
||||
if (entity == null) {
|
||||
return interactGeneralBattle(player, stack, actor)
|
||||
} else if (battlePokemon != null) {
|
||||
return interactWithSpecificBattle(player, stack, battlePokemon)
|
||||
}
|
||||
} ?: run {
|
||||
|
||||
} ?:
|
||||
*/
|
||||
run {
|
||||
if (entity != null) {
|
||||
val pokemon = entity.pokemon
|
||||
if (entity.ownerUUID == player.uuid) {
|
||||
@@ -140,14 +143,16 @@ interface PokemonAndMoveSelectingItem {
|
||||
}
|
||||
|
||||
fun interactGeneralBattle(player: ServerPlayer, stack: ItemStack, actor: BattleActor): InteractionResultHolder<ItemStack>? {
|
||||
/*
|
||||
PartyMoveSelectCallbacks.createFromPokemon(
|
||||
player = player,
|
||||
pokemon = actor.pokemonList.map { it.effectedPokemon },
|
||||
moves = { pk -> actor.pokemonList.find { it.effectedPokemon == pk }!!.moveSet.getMoves() },
|
||||
canSelectPokemon = { pk -> canUseOnBattlePokemon(actor.pokemonList.find { it.effectedPokemon == pk }!!) },
|
||||
pokemon = actor.battlePokemonProvider.getKnownPokemon().map { it.effectedPokemon },
|
||||
moves = { pk -> actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == pk }!!.moveSet.getMoves() },
|
||||
canSelectPokemon = { pk -> canUseOnBattlePokemon(actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == pk }!!) },
|
||||
canSelectMove = ::canUseOnMove,
|
||||
handler = { pk, mv -> applyToBattlePokemon(player, stack, actor.pokemonList.find { it.effectedPokemon == pk }!!, mv) }
|
||||
handler = { pk, mv -> applyToBattlePokemon(player, stack, actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == pk }!!, mv) }
|
||||
)
|
||||
*/
|
||||
|
||||
return InteractionResultHolder.success(stack)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getBattleState
|
||||
import com.cobblemon.mod.common.util.isHeld
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
@@ -46,7 +50,7 @@ interface PokemonSelectingItem {
|
||||
|
||||
player.getBattleState()?.let { (_, actor) ->
|
||||
if (bagItem == null) return InteractionResultHolder.fail(stack)
|
||||
val battlePokemon = actor.pokemonList.find { it.effectedPokemon == entity?.pokemon }
|
||||
// val battlePokemon = actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == entity?.pokemon }
|
||||
|
||||
if (!actor.canFitForcedAction()) {
|
||||
player.sendSystemMessage(battleLang("bagitem.cannot").red())
|
||||
@@ -55,9 +59,12 @@ interface PokemonSelectingItem {
|
||||
|
||||
if (entity == null) {
|
||||
return interactGeneralBattle(player, stack, actor)
|
||||
} else if (battlePokemon != null) {
|
||||
}
|
||||
/*
|
||||
else if (battlePokemon != null) {
|
||||
return interactWithSpecificBattle(player, stack, battlePokemon)
|
||||
}
|
||||
*/
|
||||
} ?: run {
|
||||
if (!player.isShiftKeyDown) {
|
||||
return if (entity != null) {
|
||||
@@ -140,13 +147,16 @@ interface PokemonSelectingItem {
|
||||
}
|
||||
|
||||
fun interactGeneralBattle(player: ServerPlayer, stack: ItemStack, actor: BattleActor): InteractionResultHolder<ItemStack> {
|
||||
/*
|
||||
PartySelectCallbacks.createBattleSelect(
|
||||
player = player,
|
||||
pokemon = actor.pokemonList,
|
||||
canSelect = { pk -> canUseOnBattlePokemon(actor.pokemonList.find { it.effectedPokemon == pk.effectedPokemon }!!) },
|
||||
handler = { pk -> applyToBattlePokemon(player, stack, actor.pokemonList.find { it.effectedPokemon == pk.effectedPokemon }!!) }
|
||||
pokemon = actor.battlePokemonProvider.getKnownPokemon(),
|
||||
canSelect = { pk -> canUseOnBattlePokemon(actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == pk.effectedPokemon }!!) },
|
||||
handler = { pk -> applyToBattlePokemon(player, stack, actor.battlePokemonProvider.getKnownPokemon().find { it.effectedPokemon == pk.effectedPokemon }!!) }
|
||||
)
|
||||
|
||||
*/
|
||||
|
||||
return InteractionResultHolder.success(stack)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
@@ -21,7 +22,10 @@ import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
@@ -118,6 +122,56 @@ open class MoveTemplate(
|
||||
ElementalTypes.DRAGON,
|
||||
ElementalTypes.DARK
|
||||
)
|
||||
|
||||
fun decodeJsonMove(json: JsonObject, id: String? = null): MoveTemplate {
|
||||
val id = id ?: json.get("id").asString
|
||||
val num = json.get("num").asInt
|
||||
val elementalType = ElementalTypes.getOrException(json.get("type").asString)
|
||||
val damageCategory = DamageCategories.getOrException(json.get("category").asString)
|
||||
val power = json.get("basePower").asDouble
|
||||
val target = MoveTarget.fromShowdownId(json.get("target").asString)
|
||||
// If not a double it's always true
|
||||
val accuracyJson = json.get("accuracy").asJsonPrimitive
|
||||
val accuracy = if (accuracyJson.isNumber) accuracyJson.asDouble else -1.0
|
||||
val pp = json.get("pp").asInt
|
||||
val priority = json.get("priority").asInt
|
||||
val critRatio = json.get("critRatio")?.asDouble ?: 1.0
|
||||
val effectChances = arrayListOf<Double>()
|
||||
val secondariesMember = json.get("secondaries")
|
||||
val secondaryMember = json.get("secondary")
|
||||
if (secondariesMember != null && secondariesMember is JsonArray) {
|
||||
for (j in 0 until secondariesMember.size()) {
|
||||
val element = secondariesMember[j].asJsonObject
|
||||
// They declare moves without data on secondary effects for sheer force compatibility
|
||||
if (element.has("chance")) {
|
||||
effectChances += element.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
} else if (secondaryMember != null && secondaryMember is JsonObject) {
|
||||
// They declare moves without data on secondary effects for sheer force compatibility
|
||||
if (secondaryMember.has("chance")) {
|
||||
effectChances += secondaryMember.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
val actionEffect = ActionEffects.actionEffects[id.asIdentifierDefaultingNamespace()]
|
||||
?: run {
|
||||
ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
|
||||
}
|
||||
return MoveTemplate(
|
||||
id,
|
||||
num,
|
||||
elementalType,
|
||||
damageCategory,
|
||||
power,
|
||||
target,
|
||||
accuracy,
|
||||
pp,
|
||||
priority,
|
||||
critRatio,
|
||||
effectChances.toTypedArray(),
|
||||
actionEffect
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,17 +10,9 @@ package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
@@ -40,57 +32,7 @@ object Moves : DataRegistry {
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.allMoves.clear()
|
||||
this.idMapping.clear()
|
||||
val movesJson = ShowdownService.service.getMoves()
|
||||
for (i in 0 until movesJson.size()) {
|
||||
val jsMove = movesJson[i].asJsonObject
|
||||
val id = jsMove.get("id").asString
|
||||
try {
|
||||
val num = jsMove.get("num").asInt
|
||||
val elementalType = ElementalTypes.getOrException(jsMove.get("type").asString)
|
||||
val damageCategory = DamageCategories.getOrException(jsMove.get("category").asString)
|
||||
val power = jsMove.get("basePower").asDouble
|
||||
val target = MoveTarget.fromShowdownId(jsMove.get("target").asString)
|
||||
// If not a double it's always true
|
||||
val accuracyJson = jsMove.get("accuracy").asJsonPrimitive
|
||||
val accuracy = if (accuracyJson.isNumber) accuracyJson.asDouble else -1.0
|
||||
val pp = jsMove.get("pp").asInt
|
||||
val priority = jsMove.get("priority").asInt
|
||||
val critRatio = jsMove.get("critRatio")?.asDouble ?: 1.0
|
||||
val effectChances = arrayListOf<Double>()
|
||||
val secondariesMember = jsMove.get("secondaries")
|
||||
val secondaryMember = jsMove.get("secondary")
|
||||
if (secondariesMember != null && secondariesMember is JsonArray) {
|
||||
for (j in 0 until secondariesMember.size()) {
|
||||
val element = secondariesMember[j].asJsonObject
|
||||
// They declare moves without data on secondary effects for sheer force compatibility
|
||||
if (element.has("chance")) {
|
||||
effectChances += element.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (secondaryMember != null && secondaryMember is JsonObject) {
|
||||
// They declare moves without data on secondary effects for sheer force compatibility
|
||||
if (secondaryMember.has("chance")) {
|
||||
effectChances += secondaryMember.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
val actionEffect = ActionEffects.actionEffects[id.asIdentifierDefaultingNamespace()]
|
||||
?: run {
|
||||
ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
|
||||
// if (damageCategory == DamageCategories.STATUS) {
|
||||
// ActionEffects.actionEffects[cobblemonResource("status")]
|
||||
// } else {
|
||||
// val type = elementalType.name.lowercase()
|
||||
// val category = damageCategory.name.lowercase()
|
||||
// ActionEffects.actionEffects["${category}_$type".asIdentifierDefaultingNamespace()]
|
||||
// }
|
||||
}
|
||||
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray(), actionEffect)
|
||||
this.register(move)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
|
||||
}
|
||||
}
|
||||
Cobblemon.showdownServer.getMoves().forEach(::register)
|
||||
Cobblemon.LOGGER.info("Loaded {} moves", this.allMoves.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
@@ -47,8 +47,29 @@ import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.showdown.runner.CobblemonShowdownBattleServer
|
||||
import com.cobblemon.mod.common.util.adapters.AbilityPoolAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.AbilityTemplateAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BiomeLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BlockLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BoxAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.DropEntryAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.EggGroupAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.LazySetAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.LearnsetAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.NbtCompoundAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RegistryElementAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RideControllerAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RidingStatDefinitionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.StructureLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.common.collect.HashBasedTable
|
||||
import com.google.gson.Gson
|
||||
@@ -130,8 +151,10 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
this.species.forEach(Species::initialize)
|
||||
this.species.forEach(Species::resolveEvolutionMoves)
|
||||
Cobblemon.showdownThread.queue {
|
||||
it.registerSpecies()
|
||||
it.indicateSpeciesInitialized()
|
||||
if (it is CobblemonShowdownBattleServer) {
|
||||
it.registerSpecies()
|
||||
it.indicateSpeciesInitialized()
|
||||
}
|
||||
// Reload this with the mod
|
||||
CobblemonHeldItemManager.load()
|
||||
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
|
||||
|
||||
+1
-1
@@ -15,8 +15,8 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.helditem.BaseCobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonEmptyHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
* Responsible for querying [BattlePokemon] for the presence of held items and their consumption.
|
||||
|
||||
@@ -13,9 +13,9 @@ import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.battles.BagItems.bagItems
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.item.battle.BagItemLike
|
||||
import com.cobblemon.mod.common.showdown.runner.CobblemonShowdownBattleServer
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -42,7 +42,11 @@ object BagItems : DataRegistry {
|
||||
|
||||
init {
|
||||
observable.subscribe {
|
||||
Cobblemon.showdownThread.queue(ShowdownService::registerBagItems)
|
||||
if (Cobblemon.showdownServer is CobblemonShowdownBattleServer) {
|
||||
Cobblemon.showdownThread.queue {
|
||||
(Cobblemon.showdownServer as CobblemonShowdownBattleServer).registerBagItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
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.storage.party.PartyStore
|
||||
@@ -25,29 +24,15 @@ import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.getBattleTheme
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import java.util.UUID
|
||||
import com.cobblemon.mod.common.util.swap
|
||||
import com.cobblemon.mod.common.util.update
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.Iterable
|
||||
import kotlin.collections.MutableSet
|
||||
import kotlin.collections.all
|
||||
import kotlin.collections.filterIsInstance
|
||||
import kotlin.collections.first
|
||||
import kotlin.collections.flatMap
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.isNotEmpty
|
||||
import kotlin.collections.mapNotNull
|
||||
import kotlin.collections.mutableSetOf
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.plusAssign
|
||||
import java.util.UUID
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.sortedBy
|
||||
|
||||
object BattleBuilder {
|
||||
@JvmOverloads
|
||||
@@ -92,18 +77,21 @@ object BattleBuilder {
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
for ((player, actor) in arrayOf(player1 to player1Actor, player2 to player2Actor)) {
|
||||
if (actor.pokemonList.filter { it.health > 0 }.size < battleFormat.battleType.slotsPerActor) {
|
||||
/*
|
||||
if (actor.battlePokemonProvider.getKnownPokemon().filter { it.health > 0 }.size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[actor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = actor.pokemonList.filter { it.health > 0 }.size
|
||||
hadCount = actor.battlePokemonProvider.getKnownPokemon().filter { it.health > 0 }.size
|
||||
)
|
||||
}
|
||||
|
||||
if (actor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
if (actor.battlePokemonProvider.getKnownPokemon().any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[actor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
errors.participantErrors[actor] += BattleStartError.alreadyInBattle(player)
|
||||
}
|
||||
@@ -171,18 +159,21 @@ object BattleBuilder {
|
||||
}
|
||||
|
||||
for ((player, actor) in players.zip(playerActors)) {
|
||||
if (actor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
/*
|
||||
if (actor.battlePokemonProvider.getKnownPokemon().size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[actor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = actor.pokemonList.size
|
||||
hadCount = actor.battlePokemonProvider.getKnownPokemon().size
|
||||
)
|
||||
}
|
||||
|
||||
if (actor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
if (actor.battlePokemonProvider.getKnownPokemon().any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[actor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
errors.participantErrors[actor] += BattleStartError.alreadyInBattle(player)
|
||||
}
|
||||
@@ -252,26 +243,30 @@ object BattleBuilder {
|
||||
val wildActor = PokemonBattleActor(pokemonEntity.pokemon.uuid, BattlePokemon(pokemonEntity.pokemon), fleeDistance)
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
/*
|
||||
if (playerTeam.isNotEmpty() && playerTeam[0].health <= 0){
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
hadCount = playerActor.battlePokemonProvider.getKnownPokemon().size
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
if (playerActor.battlePokemonProvider.getKnownPokemon().size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
hadCount = playerActor.battlePokemonProvider.getKnownPokemon().size
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
if (playerActor.battlePokemonProvider.getKnownPokemon().any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.alreadyInBattle(playerActor)
|
||||
}
|
||||
@@ -280,6 +275,8 @@ object BattleBuilder {
|
||||
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
playerActor.battleTheme = pokemonEntity.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
@@ -318,16 +315,16 @@ object BattleBuilder {
|
||||
healFirst: Boolean = false,
|
||||
party: PartyStore = player.party()
|
||||
): BattleStartResult {
|
||||
/*
|
||||
val playerTeam = party.toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemon = leadingPokemon)
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
val npcParty = npcEntity.getPartyForChallenge(listOf(player))
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
if (playerActor.battlePokemonProvider.getKnownPokemon().size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = playerActor.pokemonList.size
|
||||
hadCount = playerActor.battlePokemonProvider.getKnownPokemon().size
|
||||
)
|
||||
}
|
||||
|
||||
@@ -340,20 +337,20 @@ object BattleBuilder {
|
||||
return errors
|
||||
}
|
||||
|
||||
val npcActor = NPCBattleActor(npcEntity, npcParty, npcEntity.skill ?: npcEntity.npc.skill)
|
||||
val npcActor = NPCBattleActor(npcEntity, npcParty)
|
||||
// if (npcEntity.battleIds.get().isPresent) {
|
||||
// errors.participantErrors[npcActor] += BattleStartError.alreadyInBattle(npcActor)
|
||||
// }
|
||||
|
||||
if (npcActor.pokemonList.filter { it.health > 0 }.size < battleFormat.battleType.slotsPerActor) {
|
||||
if (npcActor.battlePokemonProvider.getKnownPokemon().filter { it.health > 0 }.size < battleFormat.battleType.slotsPerActor) {
|
||||
errors.participantErrors[npcActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = npcEntity,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
hadCount = npcActor.pokemonList.size
|
||||
hadCount = npcActor.battlePokemonProvider.getKnownPokemon().size
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
if (playerActor.battlePokemonProvider.getKnownPokemon().any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
@@ -370,6 +367,8 @@ object BattleBuilder {
|
||||
} else {
|
||||
errors
|
||||
}
|
||||
*/
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +397,7 @@ interface BattleStartError {
|
||||
companion object {
|
||||
fun alreadyInBattle(player: ServerPlayer) = AlreadyInBattleError(player.uuid, player.effectiveName())
|
||||
fun alreadyInBattle(pokemonEntity: PokemonEntity) = AlreadyInBattleError(pokemonEntity.uuid, pokemonEntity.effectiveName())
|
||||
fun alreadyInBattle(actor: BattleActor) = AlreadyInBattleError(actor.uuid, actor.getName())
|
||||
//fun alreadyInBattle(actor: BattleActor) = AlreadyInBattleError(actor.uuid, actor.getName())
|
||||
fun noParty(npcEntity: NPCEntity) = NoPartyError(npcEntity)
|
||||
fun targetIsBusy(targetName: Component) = BusyError(targetName)
|
||||
fun insufficientPokemon(
|
||||
|
||||
@@ -17,11 +17,11 @@ import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.showdown.runner.remote.RemoteShowdownBattle
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object BattleRegistry {
|
||||
|
||||
@@ -29,7 +29,7 @@ object BattleRegistry {
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
|
||||
.create()
|
||||
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
|
||||
private val battleMap = ConcurrentHashMap<String, PokemonBattle>()
|
||||
|
||||
fun onServerStarted() {
|
||||
battleMap.clear()
|
||||
@@ -161,22 +161,23 @@ object BattleRegistry {
|
||||
repeat(battle.format.battleType.slotsPerActor) {
|
||||
actor.activePokemon.add(ActiveBattlePokemon(actor))
|
||||
}
|
||||
val entities = actor.pokemonList.mapNotNull { it.entity }
|
||||
val entities = actor.battlePokemonProvider.getKnownPokemon().mapNotNull { it.entity }
|
||||
entities.forEach { it.battleId = battle.battleId }
|
||||
}
|
||||
|
||||
// -> Add the players and team
|
||||
for (actor in battle.actors.sortedBy { it.showdownId }) {
|
||||
messages.add(""">player ${actor.showdownId} {"name":"${actor.uuid}","team":"${actor.pokemonList.packTeam()}"}""")
|
||||
//messages.add(""">player ${actor.showdownId} {"name":"${actor.uuid}","team":"${actor.battlePokemonProvider.getKnownPokemon().packTeam()}"}""")
|
||||
}
|
||||
|
||||
// -> Set team size
|
||||
for (actor in battle.actors.sortedBy { it.showdownId }) {
|
||||
messages.add(">${actor.showdownId} team ${actor.pokemonList.count()}")
|
||||
messages.add(">${actor.showdownId} team ${actor.battlePokemonProvider.getKnownPokemon().count()}")
|
||||
}
|
||||
|
||||
// Compiles the request and sends it off
|
||||
ShowdownService.service.startBattle(battle, messages.toTypedArray())
|
||||
val showdownBattle = Cobblemon.showdownServer.initializeNewBattle(battle.battleId.toString(), messages.toTypedArray())
|
||||
battle.showdownBattle = showdownBattle
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,12 +215,32 @@ object BattleRegistry {
|
||||
return ErroredBattleStart(mutableSetOf(BattleStartError.canceledByEvent(preBattleEvent.reason)))
|
||||
}
|
||||
|
||||
fun initializeShowdownControlledBattle(showdownBattle: RemoteShowdownBattle, playerId: UUID) {
|
||||
/*
|
||||
val battleId = showdownBattle.id
|
||||
val battle = PokemonBattle(
|
||||
BattleFormat.GEN_9_SINGLES,
|
||||
BattleSide(
|
||||
PlayerBattleActor(playerId, )
|
||||
)
|
||||
)
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
private fun createBattlePokemonFromShowdownJson(jsonObject: JsonObject): BattlePokemon {
|
||||
val pokemon = PokemonProperties.parse("")
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
fun closeBattle(battle: PokemonBattle) {
|
||||
battle.onEndHandlers.forEach { it(battle) }
|
||||
battleMap.remove(battle.battleId)
|
||||
}
|
||||
|
||||
fun getBattle(id: UUID) : PokemonBattle? {
|
||||
fun getBattle(id: String) : PokemonBattle? {
|
||||
return battleMap[id]
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ data class SwitchActionResponse(var newPokemonId: UUID) : ShowdownActionResponse
|
||||
}
|
||||
|
||||
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
|
||||
val pokemon = activeBattlePokemon.actor.pokemonList.find { it.uuid == newPokemonId }
|
||||
val pokemon = activeBattlePokemon.actor.battlePokemonProvider.getKnownPokemon().find { it.uuid == newPokemonId }
|
||||
return when {
|
||||
pokemon == null -> false // No such Pokémon
|
||||
(!activeBattlePokemon.actor.request?.side?.pokemon?.any{ it.uuid == activeBattlePokemon.battlePokemon?.uuid && it.reviving }!! && pokemon.health <= 0) -> false // Checks if the active Pokémon is reviving, if so ignore this check. If not, return false if dead
|
||||
@@ -223,7 +223,7 @@ data class SwitchActionResponse(var newPokemonId: UUID) : ShowdownActionResponse
|
||||
}
|
||||
|
||||
override fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?): String {
|
||||
return "switch ${activeBattlePokemon.actor.pokemonList.indexOfFirst { it.uuid == newPokemonId } + 1}"
|
||||
return "switch ${activeBattlePokemon.actor.battlePokemonProvider.getKnownPokemon().indexOfFirst { it.uuid == newPokemonId } + 1}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.*
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.runOnServer
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import kotlin.collections.Iterator
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.forEach
|
||||
@@ -38,7 +38,7 @@ import kotlin.collections.toTypedArray
|
||||
@Suppress("KotlinPlaceholderCountMatchesArgumentCount", "UNUSED_PARAMETER")
|
||||
object ShowdownInterpreter {
|
||||
// Stores a reference to the previous ability, activate, or move message in a battle so a minor action can refer back to it (Battle UUID : BattleMessage)
|
||||
val lastCauser = mutableMapOf<UUID, BattleMessage>()
|
||||
val lastCauser = mutableMapOf<String, BattleMessage>()
|
||||
|
||||
private val updateInstructionParser = mutableMapOf<String, (PokemonBattle, InstructionSet, BattleMessage, Iterator<BattleMessage>) -> InterpreterInstruction>()
|
||||
private val splitInstructionParser = mutableMapOf<String, (PokemonBattle, BattleActor, InstructionSet, BattleMessage, BattleMessage, Iterator<BattleMessage>) -> InterpreterInstruction>()
|
||||
@@ -218,7 +218,7 @@ object ShowdownInterpreter {
|
||||
|
||||
|
||||
|
||||
fun interpretMessage(battleId: UUID, message: String) {
|
||||
fun interpretMessage(battleId: String, message: String) {
|
||||
// Check key map and use function if matching
|
||||
if (message.startsWith("{\"winner\":\"")) {
|
||||
// The post-win message is something we don't care about just yet. It's basically a summary of what happened in the battle.
|
||||
@@ -290,13 +290,13 @@ object ShowdownInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fun broadcastOptionalAbility(battle: PokemonBattle, effect: Effect?, pokemon: BattlePokemon) {
|
||||
fun broadcastOptionalAbility(battle: PokemonBattle, effect: Effect?, pokemon: AbstractBattlePokemon) {
|
||||
if (effect == null) return
|
||||
broadcastAbility(battle, effect, pokemon)
|
||||
}
|
||||
|
||||
// Broadcasts a generic lang to notify players of ability activations (effects are broadcasted separately)
|
||||
fun broadcastAbility(battle: PokemonBattle, effect: Effect, pokemon: BattlePokemon) {
|
||||
fun broadcastAbility(battle: PokemonBattle, effect: Effect, pokemon: AbstractBattlePokemon) {
|
||||
if (effect.type != Effect.Type.ABILITY) return
|
||||
battle.dispatchWaiting(0.5F) {
|
||||
val lang = battleLang("ability.generic", pokemon.getName(), effect.typelessData).yellow()
|
||||
|
||||
@@ -8,28 +8,30 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import java.util.*
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattleServer
|
||||
import java.util.LinkedList
|
||||
import java.util.Queue
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
class ShowdownThread : Thread("Cobblemon Showdown") {
|
||||
|
||||
private val latch = CountDownLatch(1)
|
||||
|
||||
private val whenReady : Queue<(ShowdownService) -> Unit> = LinkedList()
|
||||
private val whenReady : Queue<(ShowdownBattleServer) -> Unit> = LinkedList()
|
||||
|
||||
fun launch() {
|
||||
this.start()
|
||||
this.latch.await()
|
||||
for (action in whenReady) {
|
||||
action(ShowdownService.service)
|
||||
action(Cobblemon.showdownServer)
|
||||
}
|
||||
}
|
||||
|
||||
fun queue(action: (ShowdownService) -> Unit) {
|
||||
fun queue(action: (ShowdownBattleServer) -> Unit) {
|
||||
if (this.latch.count == 0L) {
|
||||
action(ShowdownService.service)
|
||||
action(Cobblemon.showdownServer)
|
||||
} else {
|
||||
this.whenReady.add(action)
|
||||
}
|
||||
@@ -37,7 +39,7 @@ class ShowdownThread : Thread("Cobblemon Showdown") {
|
||||
|
||||
override fun run() {
|
||||
LOGGER.info("Starting showdown service...")
|
||||
ShowdownService.service.openConnection()
|
||||
Cobblemon.showdownServer.connect()
|
||||
LOGGER.info("Showdown has been started!")
|
||||
this.latch.countDown()
|
||||
}
|
||||
|
||||
+8
-10
@@ -8,21 +8,19 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.actor
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.StaticBattlePokemonProvider
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.ai.RandomBattleAI
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import java.util.UUID
|
||||
|
||||
class MultiPokemonBattleActor(
|
||||
pokemonList: List<BattlePokemon>,
|
||||
artificialDecider: BattleAI = RandomBattleAI(),
|
||||
uuid: UUID = UUID.randomUUID()
|
||||
) : AIBattleActor(uuid, pokemonList, artificialDecider) {
|
||||
open class MultiPokemonBattleActor(
|
||||
uuid: UUID = UUID.randomUUID(),
|
||||
pokemonList: List<BattlePokemon>
|
||||
) : BattleActor(uuid, StaticBattlePokemonProvider(pokemonList)) {
|
||||
override fun getName(): MutableComponent = "Wild Pokémon".text() // TODO probably remove by making it nullable
|
||||
override fun nameOwned(name: String): MutableComponent = Component.literal(name)
|
||||
override val type = ActorType.WILD
|
||||
|
||||
@@ -13,24 +13,25 @@ import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.StaticBattlePokemonProvider
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.BattleExperienceSource
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.pokemon.AbstractBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
class PlayerBattleActor(
|
||||
uuid: UUID,
|
||||
pokemonList: List<BattlePokemon>,
|
||||
) : BattleActor(uuid, pokemonList.toMutableList()), EntityBackedBattleActor<ServerPlayer> {
|
||||
pokemonList: List<AbstractBattlePokemon>,
|
||||
) : BattleActor(uuid, StaticBattlePokemonProvider(pokemonList.toMutableList())), EntityBackedBattleActor<ServerPlayer> {
|
||||
|
||||
override val initialPos: Vec3?
|
||||
override val entity: ServerPlayer?
|
||||
|
||||
@@ -8,28 +8,24 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.actor
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
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.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.battles.ai.RandomBattleAI
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
|
||||
import java.util.UUID
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
class PokemonBattleActor(
|
||||
uuid: UUID,
|
||||
val pokemon: BattlePokemon,
|
||||
override val fleeDistance: Float,
|
||||
artificialDecider: BattleAI = RandomBattleAI()
|
||||
) : AIBattleActor(uuid, listOf(pokemon), artificialDecider), EntityBackedBattleActor<PokemonEntity>, FleeableBattleActor {
|
||||
) : MultiPokemonBattleActor(uuid, listOf(pokemon)), EntityBackedBattleActor<PokemonEntity>, FleeableBattleActor {
|
||||
|
||||
override val initialPos: Vec3?
|
||||
init {
|
||||
@@ -56,7 +52,7 @@ class PokemonBattleActor(
|
||||
if (packet is BattleEndPacket) {
|
||||
// Do some shit
|
||||
val entity = entity ?: return
|
||||
entity.battleId = null
|
||||
entity.battleId = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.actor
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.*
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
@@ -21,8 +19,7 @@ class TrainerBattleActor(
|
||||
val trainerName: String,
|
||||
uuid: UUID,
|
||||
pokemonList: List<BattlePokemon>,
|
||||
artificialDecider: BattleAI
|
||||
) : AIBattleActor(uuid, pokemonList, artificialDecider) {
|
||||
) : BattleActor(uuid, StaticBattlePokemonProvider(pokemonList)) {
|
||||
override fun getName() = trainerName.asTranslated()
|
||||
override fun nameOwned(name: String): MutableComponent = battleLang("owned_pokemon", this.getName(), name)
|
||||
override val type = ActorType.NPC
|
||||
|
||||
@@ -8,21 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.ai
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.DefaultActionResponse
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.battles.SwitchActionResponse
|
||||
|
||||
/**
|
||||
* AI that randomly chooses a move from its moveset at a random target.
|
||||
*
|
||||
* @since January 16th, 2022
|
||||
* @author Deltric, Hiroku
|
||||
*/
|
||||
/*
|
||||
class RandomBattleAI : BattleAI {
|
||||
override fun choose(
|
||||
activeBattlePokemon: ActiveBattlePokemon,
|
||||
@@ -30,7 +22,7 @@ class RandomBattleAI : BattleAI {
|
||||
forceSwitch: Boolean
|
||||
): ShowdownActionResponse {
|
||||
if (forceSwitch || activeBattlePokemon.isGone()) {
|
||||
val switchTo = activeBattlePokemon.actor.pokemonList.filter { it.canBeSentOut() }.randomOrNull()
|
||||
val switchTo = activeBattlePokemon.actor.battlePokemonProvider.getKnownPokemon().filter { it.canBeSentOut() }.randomOrNull()
|
||||
?: return DefaultActionResponse() //throw IllegalStateException("Need to switch but no Pokémon to switch to")
|
||||
switchTo.willBeSwitchedIn = true
|
||||
return SwitchActionResponse(switchTo.uuid)
|
||||
@@ -54,4 +46,6 @@ class RandomBattleAI : BattleAI {
|
||||
MoveActionResponse(move.id, (chosenTarget as ActiveBattlePokemon).getPNX())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -10,32 +10,31 @@ package com.cobblemon.mod.common.battles.ai
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.*
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Status
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.DefaultActionResponse
|
||||
import com.cobblemon.mod.common.battles.InBattleMove
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionRequest
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.battles.SwitchActionResponse
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.util.UUID
|
||||
import kotlin.random.Random
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* AI that tries to choose the best move for the given situations. Based off of the Pokemon Trainer Tournament Simulator Github
|
||||
@@ -351,7 +350,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
forceSwitch: Boolean
|
||||
): ShowdownActionResponse {
|
||||
if (forceSwitch || activeBattlePokemon.isGone()) {
|
||||
val switchTo = activeBattlePokemon.actor.pokemonList.filter { it.canBeSentOut() }.randomOrNull()
|
||||
val switchTo = activeBattlePokemon.actor.battlePokemonProvider.getKnownPokemon().filter { it.canBeSentOut() }.randomOrNull()
|
||||
?: return DefaultActionResponse() //throw IllegalStateException("Need to switch but no Pokémon to switch to")
|
||||
switchTo.willBeSwitchedIn = true
|
||||
return SwitchActionResponse(switchTo.uuid)
|
||||
@@ -629,14 +628,19 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
if (forceSwitch || activeBattlePokemon.isGone()) {
|
||||
if ((forceSwitch && battle.turn == 1) || (activeBattlePokemon.isGone() && battle.turn == 1)) {
|
||||
val switchTo = activeBattlePokemon.actor.pokemonList.filter { it.canBeSentOut() }.randomOrNull()
|
||||
val switchTo = activeBattlePokemon.actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
.filter { it.canBeSentOut() }
|
||||
.randomOrNull()
|
||||
?: return DefaultActionResponse() //throw IllegalStateException("Need to switch but no Pokémon to switch to")
|
||||
switchTo.willBeSwitchedIn = true
|
||||
return SwitchActionResponse(switchTo.uuid)
|
||||
}
|
||||
else if ((forceSwitch && battle.turn > 1) || (activeBattlePokemon.isGone() && battle.turn > 1)) {
|
||||
|
||||
val availableSwitches = activeBattlePokemon.actor.pokemonList.filter { (it.health > 0) && (it.uuid != (activeBattlePokemon.battlePokemon?.uuid ?: true)) } //it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
val availableSwitches = activeBattlePokemon.actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
.filter { (it.health > 0) && (it.uuid != (activeBattlePokemon.battlePokemon?.uuid ?: true)) } //it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
|
||||
// this gets null error after NPC faints
|
||||
val bestEstimation = availableSwitches.maxOfOrNull { estimatePartyMatchup(request, battle, it.effectedPokemon) }
|
||||
@@ -651,7 +655,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
//return SwitchActionResponse(switchTo.uuid)
|
||||
|
||||
/*val availableSwitches = p2Actor.pokemonList.filter { it.health > 0 } //it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
/*val availableSwitches = p2Actor.battlePokemonProvider.getKnownPokemon().filter { it.health > 0 } //it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
val bestEstimation = availableSwitches.maxOfOrNull { estimateMatchup(request, battle, it.effectedPokemon) }
|
||||
*//*availableSwitches.forEach {
|
||||
estimateMatchup(request, battle, it.effectedPokemon)
|
||||
@@ -774,8 +778,10 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
// switch out based on current matchup on the field
|
||||
if (checkSwitchOutSkill(skill) && shouldSwitchOut(request, battle, activeBattlePokemon, moveset)) {
|
||||
val availableSwitches = p2Actor.pokemonList.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
val availablePlayerSwitches = p1Actor.pokemonList.filter { it.uuid != opponent.pokemon!!.uuid && it.health > 0 }
|
||||
val availableSwitches = p2Actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
val availablePlayerSwitches = p1Actor.battlePokemonProvider.getKnownPokemon().filter { it.uuid != opponent.pokemon!!.uuid && it.health > 0 }
|
||||
|
||||
// todo try to detect a player switch-in based on if they do that a lot
|
||||
// todo if player is in bad matchup against current AI pokemon, and they have switched out before, then they have a chance of switching to a favorable matchup
|
||||
@@ -850,10 +856,12 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
// Deal with non-weather related field changing effects
|
||||
for (move in moveset.moves.filter { !it.disabled }) {
|
||||
val availableSwitches = p2Actor.pokemonList.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
val availableSwitches = p2Actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
|
||||
// Tailwind
|
||||
if (move.pp > 0 && move.id == "tailwind" && move.id != npcSideTailwindCondition && p2Actor.pokemonList.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }.size > 2) {
|
||||
if (move.pp > 0 && move.id == "tailwind" && move.id != npcSideTailwindCondition && p2Actor.battlePokemonProvider.getKnownPokemon().filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }.size > 2) {
|
||||
return chooseMove(move, activeBattlePokemon)
|
||||
}
|
||||
|
||||
@@ -874,7 +882,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
// Light Screen
|
||||
if (move.pp > 0 && move.id == "lightscreen" && move.id != npcSideScreenCondition
|
||||
&& getBaseStats(opponent.pokemon!!, "spa") > getBaseStats(opponent.pokemon!!, "atk")
|
||||
&& p2Actor.pokemonList.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }.size > 1) {
|
||||
&& p2Actor.battlePokemonProvider.getKnownPokemon().filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }.size > 1) {
|
||||
return chooseMove(move, activeBattlePokemon)
|
||||
}
|
||||
|
||||
@@ -882,7 +890,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
// Reflect
|
||||
if (move.pp > 0 && move.id == "reflect" && move.id != npcSideScreenCondition
|
||||
&& getBaseStats(opponent.pokemon!!, "atk") > getBaseStats(opponent.pokemon!!, "spa")
|
||||
&& p2Actor.pokemonList.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }.size > 1) {
|
||||
&& p2Actor.battlePokemonProvider.getKnownPokemon().filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }.size > 1) {
|
||||
return chooseMove(move, activeBattlePokemon)
|
||||
}
|
||||
}
|
||||
@@ -1275,7 +1283,9 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
// switch out
|
||||
if (shouldSwitchOut(request, battle, activeBattlePokemon, moveset)) {
|
||||
val availableSwitches = p1Actor.pokemonList.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
val availableSwitches = p1Actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filter { it.uuid != mon.pokemon!!.uuid && it.health > 0 }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
val bestEstimation = availableSwitches.maxOfOrNull { estimateMatchup(activeBattlePokemon, request, battle, it.effectedPokemon) }
|
||||
/*availableSwitches.forEach {
|
||||
estimateMatchup(request, battle, it.effectedPokemon)
|
||||
@@ -1477,7 +1487,9 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
//val requestActor = request.side?.
|
||||
//val availableSwitches = request.side?.pokemon?.filter { !it.active && getHpFraction(it.condition) != 0.0 }
|
||||
//val playerAvailableSwitches =
|
||||
val availableSwitches = p2Actor.pokemonList.filter { it.uuid != mon.activePokemon.pokemon!!.uuid && it.health > 0 }
|
||||
val availableSwitches = p2Actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filter { it.uuid != mon.activePokemon.pokemon!!.uuid && it.health > 0 }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
|
||||
val currentNPCAbility = request.side?.pokemon!!.first().ability
|
||||
|
||||
@@ -1784,7 +1796,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
val p1Boosts = battle.side1.activePokemon.firstOrNull()?.battlePokemon?.statChanges
|
||||
val playerSide1 = battle.side1.actors.first()
|
||||
val numPlayerPokemon = playerSide1.pokemonList.count()
|
||||
val numPlayerPokemon = playerSide1.battlePokemonProvider.getKnownPokemon().count()
|
||||
|
||||
|
||||
/*val lastMajorBattleMessage = if (battle.majorBattleActions.entries.isNotEmpty()) battle.majorBattleActions?.entries?.last()?.value?.rawMessage else ""
|
||||
@@ -1829,9 +1841,9 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
val pokemon2 = battle.side2.activePokemon.firstOrNull()?.battlePokemon?.effectedPokemon
|
||||
val p2Boosts = battle.side2.activePokemon.firstOrNull()?.battlePokemon?.statChanges
|
||||
val playerSide2 = battle.side2.actors.first()
|
||||
val numNPCPokemon = playerSide2.pokemonList.count()
|
||||
val numNPCPokemon = playerSide2.battlePokemonProvider.getKnownPokemon().count()
|
||||
|
||||
//val nextAvailablePokemon = playerSide2.pokemonList.filter { it.health != 0 }.first().effectedPokemon.uuid
|
||||
//val nextAvailablePokemon = playerSide2.battlePokemonProvider.getKnownPokemon().filter { it.health != 0 }.first().effectedPokemon.uuid
|
||||
// todo make nice function for knowing what is the best switchout
|
||||
|
||||
// convert p2Boosts to a regular Map rather than a MutableMap
|
||||
@@ -1858,7 +1870,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
//mon.stats = pokemon.stats
|
||||
p1.activePokemon.moves = pokemon1.moveSet.getMoves()
|
||||
p1.nRemainingMons = battle.side1.actors.sumOf { actor ->
|
||||
actor.pokemonList.count { pokemon ->
|
||||
actor.battlePokemonProvider.getKnownPokemon().count { pokemon ->
|
||||
pokemon.health != 0
|
||||
}
|
||||
}
|
||||
@@ -1871,7 +1883,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
p1.party[partyPokemonIndex] = p1.activePokemon
|
||||
}
|
||||
|
||||
//p1.availableSwitches = playerSide1.pokemonList.filter { it.uuid != p1.pokemon.uuid && it.health > 0 }
|
||||
//p1.availableSwitches = playerSide1.battlePokemonProvider.getKnownPokemon().filter { it.uuid != p1.pokemon.uuid && it.health > 0 }
|
||||
//p1.sideConditions = pokemon.sideConditions //todo what the hell does this mean
|
||||
|
||||
p2.activePokemon = getActiveTrackerPokemon(p2, pokemon2?.uuid)
|
||||
@@ -1889,7 +1901,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
//mon.stats = pokemon.stats
|
||||
p2.activePokemon.moves = pokemon2.moveSet.getMoves()
|
||||
p2.nRemainingMons = battle.side2.actors.sumOf { actor ->
|
||||
actor.pokemonList.count { pokemon ->
|
||||
actor.battlePokemonProvider.getKnownPokemon().count { pokemon ->
|
||||
pokemon.health != 0
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -11,9 +11,7 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
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.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
@@ -23,8 +21,8 @@ import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.network.chat.Component
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |-activate|POKEMON|EFFECT
|
||||
@@ -59,7 +57,7 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
|
||||
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
//pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
@@ -81,6 +79,7 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Component.literal("UNKNOWN")
|
||||
|
||||
/*
|
||||
if (effect.id == "sketch" && pokemon.effectedPokemon.moveSet.any { it.name == "sketch" } && extraEffect is String) {
|
||||
// Apply Sketch to the pokemon's current moveset if it was successfully used in battle
|
||||
val moveTemplate = Moves.getByName(extraEffect.replace(" ", ""))
|
||||
@@ -92,6 +91,8 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
val lang = when (effect.id) {
|
||||
// Includes a 3rd argument being the magnitude level as a number
|
||||
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
|
||||
|
||||
+2
-4
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.battles.interpreter.InvalidInstructionExcept
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
@@ -52,12 +51,11 @@ class BoostInstruction(battle: PokemonBattle, val message: BattleMessage, val is
|
||||
battle.dispatch {
|
||||
val actionEffect = if (isBoost) BOOST_EFFECT else UNBOOST_EFFECT
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
// pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers,
|
||||
level = battle.players.firstOrNull()?.level()
|
||||
providers = providers
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
|
||||
+2
-4
@@ -12,7 +12,6 @@ import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
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
|
||||
@@ -47,13 +46,12 @@ class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
|
||||
val status = Statuses.getStatus(effectID)
|
||||
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
// pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers,
|
||||
level = battle.players.firstOrNull()?.level()
|
||||
providers = providers
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
|
||||
+5
-3
@@ -11,11 +11,10 @@ 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
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |-crit|POKEMON
|
||||
@@ -39,10 +38,13 @@ class CritInstruction(val message: BattleMessage, val instructionSet: Instructio
|
||||
} else {
|
||||
battle.broadcastChatMessage(battleLang("crit").yellow())
|
||||
}
|
||||
/*
|
||||
if (LastBattleCriticalHitsEvolutionProgress.supports(battlePokemon.effectedPokemon)) {
|
||||
val progress = battlePokemon.effectedPokemon.evolutionProxy.current().progressFirstOrCreate({ it is LastBattleCriticalHitsEvolutionProgress }) { LastBattleCriticalHitsEvolutionProgress() }
|
||||
progress.updateProgress(LastBattleCriticalHitsEvolutionProgress.Progress(progress.currentProgress().amount + 1))
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class CureStatusInstruction(val message: BattleMessage): InterpreterInstruction
|
||||
|
||||
battle.dispatchWaiting {
|
||||
val pokemonName = pokemon.getName()
|
||||
pokemon.effectedPokemon.status = null
|
||||
// pokemon.effectedPokemon.status = null
|
||||
pokemon.sendUpdate()
|
||||
|
||||
if (maybeActivePokemon != null) {
|
||||
|
||||
+14
-8
@@ -25,16 +25,14 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.network.chat.Component
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |-damage|POKEMON|HP STATUS
|
||||
@@ -59,7 +57,9 @@ class DamageInstruction(
|
||||
val recoiling = privateMessage.optionalArgument("from")?.equals("recoil", true) == true
|
||||
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
|
||||
if (recoiling) {
|
||||
doRecoilEvoChecks(battlePokemon)
|
||||
if (battlePokemon is BattlePokemon) {
|
||||
doRecoilEvoChecks(battlePokemon)
|
||||
}
|
||||
if (lastCauser is MoveInstruction) {
|
||||
battle.dispatch {
|
||||
//Is the recoil hold currently used anywhere?
|
||||
@@ -98,9 +98,12 @@ class DamageInstruction(
|
||||
var status = effect?.id?.let { Statuses.getStatus(it) }
|
||||
battle.dispatch {
|
||||
val pokemon = privateMessage.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
//FIXME: This should be tracked on the AbstractBattlePokemon
|
||||
//Showdown doesnt tell us on damage if its poison or toxic so we gotta consult the entity
|
||||
if (status is PoisonStatus) {
|
||||
status = pokemon.effectedPokemon.status?.status ?: status
|
||||
if (pokemon is BattlePokemon) {
|
||||
status = pokemon.effectedPokemon.status?.status ?: status
|
||||
}
|
||||
}
|
||||
|
||||
// Damage effects without a action effect receive the generic damage effect
|
||||
@@ -109,7 +112,7 @@ class DamageInstruction(
|
||||
} ?: return@dispatch GO
|
||||
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
battlePokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
battlePokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
@@ -167,7 +170,7 @@ class DamageInstruction(
|
||||
if (causedFaint) {
|
||||
newHealthRatio = 0F
|
||||
battle.dispatch {
|
||||
battlePokemon.effectedPokemon.currentHealth = 0
|
||||
battlePokemon.health = 0
|
||||
battlePokemon.sendUpdate()
|
||||
GO
|
||||
}
|
||||
@@ -177,14 +180,17 @@ class DamageInstruction(
|
||||
val difference = maxHealth - remainingHealth
|
||||
newHealthRatio = remainingHealth.toFloat() / maxHealth
|
||||
battle.dispatchToFront {
|
||||
battlePokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
//battlePokemon.curr = remainingHealth
|
||||
if (difference > 0) {
|
||||
/*
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
if (DamageTakenEvolutionProgress.supports(pokemon)) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { DamageTakenEvolutionProgress() }
|
||||
progress.updateProgress(DamageTakenEvolutionProgress.Progress(progress.currentProgress().amount + difference))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
battlePokemon.sendUpdate()
|
||||
GO
|
||||
|
||||
+3
-2
@@ -13,7 +13,6 @@ 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
|
||||
@@ -39,7 +38,7 @@ class DragInstruction(val instructionSet: InstructionSet, val battleActor: Battl
|
||||
|
||||
val newHealth = privateMessage.argumentAt(2)!!.split(" ")[0]
|
||||
val remainingHealth = newHealth.split("/")[0].toInt()
|
||||
pokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
// pokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
|
||||
battle.broadcastChatMessage(battleLang("dragged_out", pokemon.getName()))
|
||||
activePokemon.battlePokemon?.let { oldPokemon ->
|
||||
@@ -50,6 +49,7 @@ class DragInstruction(val instructionSet: InstructionSet, val battleActor: Battl
|
||||
|
||||
val entity = if (battleActor is EntityBackedBattleActor<*>) battleActor.entity else null
|
||||
setOf(
|
||||
/*
|
||||
BattleDispatch {
|
||||
if (entity != null) {
|
||||
SwitchInstruction.createEntitySwitch(battle, battleActor, entity, pnx, activePokemon, pokemon, illusion, imposter)
|
||||
@@ -57,6 +57,7 @@ class DragInstruction(val instructionSet: InstructionSet, val battleActor: Battl
|
||||
SwitchInstruction.createNonEntitySwitch(battle, battleActor, pnx, activePokemon, pokemon, illusion)
|
||||
}
|
||||
}
|
||||
*/
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -11,7 +11,6 @@ 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
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ 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
|
||||
@@ -34,7 +32,7 @@ class EndItemInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
// All logic regarding broadcasting battle messages is handled in CobblemonHeldItemManager
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val itemEffect = message.effectAt(1) ?: return@dispatchWaiting
|
||||
battlePokemon.heldItemManager.handleEndInstruction(battlePokemon, battle, message)
|
||||
//battlePokemon.heldItemManager.handleEndInstruction(battlePokemon, battle, message)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battlePokemon.contextManager.remove(itemEffect.id, BattleContext.Type.ITEM)
|
||||
if (message.hasOptionalArgument("eat")) {
|
||||
|
||||
+5
-8
@@ -13,11 +13,8 @@ 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.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSender
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
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
|
||||
@@ -41,20 +38,20 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
|
||||
battle.sendUpdate(BattleFaintPacket(pnx))
|
||||
val actor = pokemon.actor
|
||||
pokemon.effectedPokemon.currentHealth = 0
|
||||
//pokemon.effectedPokemon.currentHealth = 0
|
||||
val preamble = if (actor is EntityBackedBattleActor<*>) {
|
||||
(actor.entity as? PokemonSender)?.let { sender -> pokemon.entity?.recallWithAnimation()}
|
||||
} else {
|
||||
null
|
||||
} ?: delayedFuture(seconds = waitTime)
|
||||
val context = ShowdownInterpreter.getContextFromFaint(faintingPokemon, battle)
|
||||
//val context = ShowdownInterpreter.getContextFromFaint(faintingPokemon, battle)
|
||||
|
||||
preamble.thenAccept {
|
||||
faintingPokemon.effectedPokemon.currentHealth = 0
|
||||
//faintingPokemon.effectedPokemon.currentHealth = 0
|
||||
faintingPokemon.sendUpdate()
|
||||
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context))
|
||||
//CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context))
|
||||
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
|
||||
faintingPokemon.contextManager.add(context)
|
||||
//faintingPokemon.contextManager.add(context)
|
||||
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
battle.majorBattleActions[faintingPokemon.uuid] = message
|
||||
}
|
||||
|
||||
+1
-3
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.FormeChangeEvent
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
@@ -37,7 +35,7 @@ class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction
|
||||
battle.dispatchWaiting {
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
|
||||
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
|
||||
//CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val lang = when(formName) {
|
||||
"busted", "hero", "complete" -> return@dispatchWaiting
|
||||
|
||||
+7
-7
@@ -17,9 +17,6 @@ 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
|
||||
|
||||
/**
|
||||
@@ -53,9 +50,9 @@ class HealInstruction(val actor: BattleActor, val publicMessage: BattleMessage,
|
||||
privateMessage.hasOptionalArgument("wisher") -> {
|
||||
val name = privateMessage.optionalArgument("wisher")!!
|
||||
val showdownId = name.lowercase().replace(ShowdownIdentifiable.REGEX, "")
|
||||
val wisher = actor.pokemonList.firstOrNull { it.effectedPokemon.showdownId() == showdownId }
|
||||
//val wisher = actor.battlePokemonProvider.getKnownPokemon().firstOrNull { it.effectedPokemon.showdownId() == showdownId }
|
||||
// If no Pokémon is found this is a nickname
|
||||
battleLang("heal.wish", wisher?.getName() ?: actor.nameOwned(name))
|
||||
//battleLang("heal.wish", wisher?.getName() ?: actor.nameOwned(name))
|
||||
}
|
||||
privateMessage.hasOptionalArgument("from") -> {
|
||||
when (effect!!.type) {
|
||||
@@ -76,14 +73,15 @@ class HealInstruction(val actor: BattleActor, val publicMessage: BattleMessage,
|
||||
battleLang("heal.generic", battlePokemon.getName())
|
||||
}
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
//battle.broadcastChatMessage(lang)
|
||||
}
|
||||
battle.minorBattleActions[battlePokemon.uuid] = privateMessage
|
||||
battlePokemon.effectedPokemon.currentHealth = newHealth[0].toInt()
|
||||
//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)
|
||||
activePokemon?.let { battle.sendUpdate(BattlePersistentStatusPacket(it.getPNX(), status)) }
|
||||
@@ -91,6 +89,8 @@ class HealInstruction(val actor: BattleActor, val publicMessage: BattleMessage,
|
||||
status.applyMessage.let { battle.broadcastChatMessage(it.asTranslated(battlePokemon.getName())) }
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
@@ -16,6 +15,7 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleInitializePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
|
||||
@@ -40,12 +40,12 @@ class InitializeInstruction(val instructionSet: InstructionSet, val message: Bat
|
||||
// If a Pokémon entity is being recalled with an animation,
|
||||
// wrap up the animation and recall the Pokémon immediately.
|
||||
if (it.beamMode == 3 && battle.battlePartyStores.isEmpty()) {
|
||||
pokemon.effectedPokemon.recall()
|
||||
// pokemon.effectedPokemon.recall()
|
||||
}
|
||||
// If already on the field, initialize for the BattleInitializePacket
|
||||
else {
|
||||
activePokemon.battlePokemon = pokemon
|
||||
activePokemon.illusion = illusion
|
||||
// activePokemon.battlePokemon = pokemon
|
||||
// activePokemon.illusion = illusion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,12 @@ class InitializeInstruction(val instructionSet: InstructionSet, val message: Bat
|
||||
}
|
||||
|
||||
battle.actors.forEach { actor ->
|
||||
actor.sendUpdate(BattleSetTeamPokemonPacket(actor.pokemonList.map { it.effectedPokemon }))
|
||||
actor.sendUpdate(BattleSetTeamPokemonPacket(
|
||||
actor.battlePokemonProvider.getKnownPokemon()
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
.map { it.effectedPokemon }
|
||||
.toList()
|
||||
))
|
||||
val req = actor.request ?: return@forEach
|
||||
actor.sendUpdate(BattleQueueRequestPacket(req))
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class ItemInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
battlePokemon.heldItemManager.handleStartInstruction(battlePokemon, battle, message)
|
||||
//battlePokemon.heldItemManager.handleStartInstruction(battlePokemon, battle, message)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battlePokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.ITEM, battle))
|
||||
}
|
||||
|
||||
+1
-3
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.MegaEvolutionEvent
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
@@ -30,7 +28,7 @@ class MegaInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
battle.dispatchWaiting {
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow())
|
||||
CobblemonEvents.MEGA_EVOLUTION.post(MegaEvolutionEvent(battle, battlePokemon))
|
||||
//CobblemonEvents.MEGA_EVOLUTION.post(MegaEvolutionEvent(battle, battlePokemon))
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -54,8 +54,8 @@ class MoveInstruction(
|
||||
var targetPokemon: BattlePokemon? = null
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
userPokemon = message.battlePokemon(0, battle)!!
|
||||
targetPokemon = message.battlePokemon(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()
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ class PpUpdateInstruction( val message: BattleMessage): InterpreterInstruction {
|
||||
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()
|
||||
// val move = pokemon.effectedPokemon.moveSet.firstOrNull { move -> move.name.equals(moveId, true) } ?: return@dispatch GO
|
||||
// move.currentPp = movePp.toInt()
|
||||
}
|
||||
GO
|
||||
}
|
||||
|
||||
+3
-4
@@ -10,11 +10,7 @@ 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.BattleReplacePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleTransformPokemonPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format: |replace|POKEMON|DETAILS|HP STATUS
|
||||
@@ -35,12 +31,15 @@ class ReplaceInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
val entity = pokemon.entity
|
||||
entity?.let { it.effects.mockEffect?.end(it) }
|
||||
|
||||
/*
|
||||
battle.sendSidedUpdate(
|
||||
source = actor,
|
||||
allyPacket = BattleReplacePokemonPacket(pnx, pokemon, true),
|
||||
opponentPacket = BattleReplacePokemonPacket(pnx, pokemon, false)
|
||||
)
|
||||
|
||||
*/
|
||||
|
||||
activePokemon.illusion = null
|
||||
//lang done by EndInstruction
|
||||
}
|
||||
|
||||
+1
-2
@@ -14,7 +14,6 @@ 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
|
||||
@@ -31,7 +30,7 @@ class SetHpInstruction(val actor: BattleActor, val publicMessage: BattleMessage,
|
||||
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()
|
||||
// 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")) {
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ class StatusInstruction( val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
battle.dispatchWaiting {
|
||||
if (status is PersistentStatus) {
|
||||
pokemon.effectedPokemon.applyStatus(status)
|
||||
//pokemon.effectedPokemon.applyStatus(status)
|
||||
battle.sendUpdate(BattlePersistentStatusPacket(pnx, status))
|
||||
pokemon.sendUpdate()
|
||||
}
|
||||
|
||||
-1
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
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.entity.pokemon.effects.IllusionEffect
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwapPokemonPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.setPositionSafely
|
||||
|
||||
+19
-11
@@ -19,21 +19,22 @@ import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.*
|
||||
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
|
||||
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.dispatch.WaitDispatch
|
||||
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.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_DURATION
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_STAGGER_BASE_DURATION
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_STAGGER_RANDOM_MAX_DURATION
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.swap
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |switch|POKEMON|DETAILS|HP STATUS
|
||||
@@ -63,11 +64,12 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
val pokemonEntity = pokemon.entity
|
||||
if (pokemonEntity != null && actor !is PokemonBattleActor) { // pokemon entities starting on the field should already have battlePokemon init; see InitializeInstruction
|
||||
|
||||
illusion?.let { IllusionEffect(it.effectedPokemon).start(pokemonEntity) }
|
||||
broadcastSwitch(battle, actor, pokemon, illusion)
|
||||
//illusion?.let { IllusionEffect(it.effectedPokemon).start(pokemonEntity) }
|
||||
//broadcastSwitch(battle, actor, pokemon, illusion)
|
||||
WaitDispatch(0.5F)
|
||||
}
|
||||
else if (pokemonEntity == null && entity != null) {
|
||||
/*
|
||||
activePokemon.battlePokemon = pokemon
|
||||
activePokemon.illusion = illusion
|
||||
val targetPos = ShowdownInterpreter.getSendoutPosition(battle, activePokemon, battleActor)
|
||||
@@ -90,8 +92,11 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
WaitDispatch(0.5F) // we're already waiting 1.5 seconds. this prevents flooding from consecutive SwitchInstructions
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
GO
|
||||
|
||||
}
|
||||
|
||||
val futureSwitches = instructionSet.getSubsequentInstructions(this).filterIsInstance<SwitchInstruction>()
|
||||
@@ -120,7 +125,7 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
battle.dispatchInsert {
|
||||
val newHealth = privateMessage.argumentAt(2)!!.split(" ")[0]
|
||||
val remainingHealth = newHealth.split("/")[0].toInt()
|
||||
pokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
// pokemon.effectedPokemon.currentHealth = remainingHealth
|
||||
pokemon.sendUpdate()
|
||||
|
||||
if (activePokemon.battlePokemon == pokemon) {
|
||||
@@ -139,6 +144,7 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
battle.majorBattleActions[pokemon.uuid] = publicMessage
|
||||
|
||||
setOf(
|
||||
/*
|
||||
BattleDispatch {
|
||||
if (entity != null) {
|
||||
createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon, illusion, imposter)
|
||||
@@ -146,6 +152,8 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon, illusion)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -167,7 +175,7 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
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))
|
||||
//actor.battlePokemonProvider.getKnownPokemon().swap(actor.activePokemon.indexOf(activePokemon), actor.battlePokemonProvider.getKnownPokemon().indexOf(newPokemon))
|
||||
activePokemon.battlePokemon = newPokemon
|
||||
activePokemon.illusion = illusion
|
||||
battle.sendSidedUpdate(actor, BattleSwitchPokemonPacket(pnx, newPokemon, true, illusion), BattleSwitchPokemonPacket(pnx, newPokemon, false, illusion))
|
||||
@@ -201,7 +209,7 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
}
|
||||
|
||||
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))
|
||||
//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))
|
||||
|
||||
+1
-3
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.TerastallizationEvent
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraTypes
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
@@ -32,7 +30,7 @@ class TerastallizeInstruction(val message: BattleMessage): InterpreterInstructio
|
||||
battle.dispatchWaiting {
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.broadcastChatMessage(battleLang("terastallize", pokemonName, type.displayName).yellow())
|
||||
CobblemonEvents.TERASTALLIZATION.post(TerastallizationEvent(battle, battlePokemon, type))
|
||||
//CobblemonEvents.TERASTALLIZATION.post(TerastallizationEvent(battle, battlePokemon, type))
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -13,9 +13,6 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
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.UntilDispatch
|
||||
import com.cobblemon.mod.common.entity.pokemon.effects.TransformEffect
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleTransformPokemonPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
@@ -40,9 +37,10 @@ class TransformInstruction(val battle: PokemonBattle, val message: BattleMessage
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemon)
|
||||
|
||||
battle.dispatch {
|
||||
val entity = pokemon.entity ?: return@dispatch GO
|
||||
val future = TransformEffect(targetPokemon.effectedPokemon, battle.started).start(entity)
|
||||
UntilDispatch { future?.isDone != false }
|
||||
GO
|
||||
//val entity = pokemon.entity ?: return@dispatch GO
|
||||
//val future = TransformEffect(targetPokemon.effectedPokemon, battle.started).start(entity)
|
||||
//UntilDispatch { future?.isDone != false }
|
||||
}
|
||||
|
||||
battle.dispatchWaiting {
|
||||
@@ -51,11 +49,14 @@ class TransformInstruction(val battle: PokemonBattle, val message: BattleMessage
|
||||
val targetPokemonName = targetPokemon.getName()
|
||||
|
||||
mock?.let {
|
||||
/*
|
||||
battle.sendSidedUpdate(
|
||||
source = actor,
|
||||
allyPacket = BattleTransformPokemonPacket(pnx, pokemon, it, true),
|
||||
opponentPacket = BattleTransformPokemonPacket(pnx, pokemon, it, false)
|
||||
)
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
val lang = battleLang("transform", pokemonName, targetPokemonName)
|
||||
|
||||
+4
-5
@@ -21,7 +21,6 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
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.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import java.util.UUID
|
||||
|
||||
@@ -47,11 +46,11 @@ class WinInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
// 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()
|
||||
// val wildPokemon: BattlePokemon = nonPlayerActor.battlePokemonProvider.getKnownPokemon().first()
|
||||
|
||||
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
|
||||
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
|
||||
}
|
||||
// if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
|
||||
// wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
|
||||
//}
|
||||
}
|
||||
|
||||
// broadcast victory / defeat
|
||||
|
||||
+1
-3
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.instruction.ZMoveUsedEvent
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
@@ -30,7 +28,7 @@ class ZPowerInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
battle.dispatchWaiting {
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.broadcastChatMessage(battleLang("zpower", pokemonName).yellow())
|
||||
CobblemonEvents.ZPOWER_USED.post(ZMoveUsedEvent(battle, battlePokemon))
|
||||
//CobblemonEvents.ZPOWER_USED.post(ZMoveUsedEvent(battle, battlePokemon))
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.cobblemon.mod.common.battles.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import java.util.UUID
|
||||
import java.util.function.Function
|
||||
|
||||
abstract class AbstractBattlePokemon {
|
||||
lateinit var actor: BattleActor
|
||||
val struct = QueryStruct(
|
||||
hashMapOf(
|
||||
"actor" to Function { actor.struct },
|
||||
"battle" to Function { actor.battle.struct },
|
||||
"uuid" to Function { StringValue(uuid.toString()) },
|
||||
"health" to Function { DoubleValue(health.toDouble()) },
|
||||
"max_health" to Function { DoubleValue(maxHealth.toDouble()) },
|
||||
"nature" to Function { StringValue(nature.name.toString()) },
|
||||
"moveset" to Function { moveSet.toStruct() }
|
||||
))
|
||||
|
||||
val statChanges = mutableMapOf<Stat, Int>()
|
||||
//For when a wild pokemon is being captured in a ball
|
||||
var gone = false
|
||||
|
||||
abstract val uuid: UUID
|
||||
abstract var health: Int
|
||||
abstract val maxHealth: Int
|
||||
abstract val nature: Nature
|
||||
abstract val moveSet: MoveSet
|
||||
abstract val entity: PokemonEntity?
|
||||
abstract val heldItemManager: HeldItemManager
|
||||
|
||||
abstract fun canBeSentOut(): Boolean
|
||||
abstract fun getName(): MutableComponent
|
||||
abstract fun sendUpdate()
|
||||
|
||||
/** The set of all the BattlePokemon that they faced during the battle (for exp purposes) */
|
||||
val facedOpponents = mutableSetOf<BattlePokemon>()
|
||||
val contextManager = ContextManager()
|
||||
|
||||
fun isSentOut() = actor.battle.activePokemon.any { it.battlePokemon == this }
|
||||
fun getIllusion(): AbstractBattlePokemon? = this.actor.activePokemon.find { it.battlePokemon == this }?.illusion
|
||||
}
|
||||
@@ -8,28 +8,19 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asStruct
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.battles.actor.MultiPokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleUpdateTeamPokemonPacket
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import java.util.function.Function
|
||||
|
||||
@@ -37,8 +28,48 @@ open class BattlePokemon(
|
||||
val originalPokemon: Pokemon,
|
||||
val effectedPokemon: Pokemon = originalPokemon,
|
||||
val postBattleEntityOperation: (PokemonEntity) -> Unit = {}
|
||||
) {
|
||||
lateinit var actor: BattleActor
|
||||
) : AbstractBattlePokemon() {
|
||||
override val uuid = effectedPokemon.uuid
|
||||
override var health = effectedPokemon.currentHealth
|
||||
override val maxHealth = effectedPokemon.maxHealth
|
||||
override val nature = effectedPokemon.nature
|
||||
override val moveSet = effectedPokemon.moveSet
|
||||
override val entity = effectedPokemon.entity
|
||||
override val heldItemManager: HeldItemManager by lazy {
|
||||
HeldItemProvider.provide(this)
|
||||
}
|
||||
|
||||
var willBeSwitchedIn = false
|
||||
|
||||
override fun canBeSentOut(): Boolean {
|
||||
return if (actor.request?.side?.pokemon?.any { it.reviving } == true) {
|
||||
!isSentOut() && !willBeSwitchedIn && health <= 0
|
||||
} else {
|
||||
!isSentOut() && !willBeSwitchedIn && health > 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun getName(): MutableComponent {
|
||||
val displayPokemon = ((getIllusion() ?: this) as BattlePokemon).effectedPokemon
|
||||
return if (actor is PokemonBattleActor || actor is MultiPokemonBattleActor) {
|
||||
displayPokemon.getDisplayName()
|
||||
} else {
|
||||
battleLang("owned_pokemon", actor.getName(), displayPokemon.getDisplayName())
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendUpdate() {
|
||||
actor.sendUpdate(BattleUpdateTeamPokemonPacket(effectedPokemon))
|
||||
}
|
||||
|
||||
init {
|
||||
struct.addFunctions(mutableMapOf(
|
||||
"pokemon" to Function { effectedPokemon.asStruct() },
|
||||
"original_pokemon" to Function {
|
||||
originalPokemon.asStruct()
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
|
||||
@@ -61,74 +92,4 @@ open class BattlePokemon(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val struct = QueryStruct(
|
||||
hashMapOf(
|
||||
"pokemon" to Function { effectedPokemon.asStruct() },
|
||||
"original_pokemon" to Function {
|
||||
originalPokemon.asStruct()
|
||||
},
|
||||
"actor" to Function { actor.struct },
|
||||
"battle" to Function { actor.battle.struct },
|
||||
"uuid" to Function { StringValue(uuid.toString()) },
|
||||
"health" to Function { DoubleValue(health.toDouble()) },
|
||||
"max_health" to Function { DoubleValue(maxHealth.toDouble()) },
|
||||
"ivs" to Function { effectedPokemon.ivs.struct },
|
||||
"nature" to Function { StringValue(effectedPokemon.nature.name.toString()) },
|
||||
"moveset" to Function { effectedPokemon.moveSet.toStruct() }
|
||||
))
|
||||
|
||||
val uuid: UUID
|
||||
get() = effectedPokemon.uuid
|
||||
val health: Int
|
||||
get() = effectedPokemon.currentHealth
|
||||
val maxHealth: Int
|
||||
get() = effectedPokemon.maxHealth
|
||||
val ivs: IVs
|
||||
get() = effectedPokemon.ivs
|
||||
val nature: Nature
|
||||
get() = effectedPokemon.nature
|
||||
val moveSet: MoveSet
|
||||
get() = effectedPokemon.moveSet
|
||||
val statChanges = mutableMapOf<Stat, Int>()
|
||||
var gone = false
|
||||
// etc
|
||||
|
||||
val entity: PokemonEntity?
|
||||
get() = effectedPokemon.entity
|
||||
|
||||
var willBeSwitchedIn = false
|
||||
|
||||
/** A set of all the BattlePokemon that they faced during the battle (for exp purposes) */
|
||||
val facedOpponents = mutableSetOf<BattlePokemon>()
|
||||
|
||||
/**
|
||||
* The [HeldItemManager] backing this [BattlePokemon].
|
||||
*/
|
||||
val heldItemManager: HeldItemManager by lazy { HeldItemProvider.provide(this) }
|
||||
|
||||
val contextManager = ContextManager()
|
||||
|
||||
open fun getName(): MutableComponent {
|
||||
val displayPokemon = getIllusion()?.effectedPokemon ?: effectedPokemon
|
||||
return if (actor is PokemonBattleActor || actor is MultiPokemonBattleActor) {
|
||||
displayPokemon.getDisplayName()
|
||||
} else {
|
||||
battleLang("owned_pokemon", actor.getName(), displayPokemon.getDisplayName())
|
||||
}
|
||||
}
|
||||
|
||||
fun sendUpdate() {
|
||||
actor.sendUpdate(BattleUpdateTeamPokemonPacket(effectedPokemon))
|
||||
}
|
||||
|
||||
fun isSentOut() = actor.battle.activePokemon.any { it.battlePokemon == this }
|
||||
fun canBeSentOut() =
|
||||
if (actor.request?.side?.pokemon?.any { it.reviving } == true) {
|
||||
!isSentOut() && !willBeSwitchedIn && health <= 0
|
||||
} else {
|
||||
!isSentOut() && !willBeSwitchedIn && health > 0
|
||||
}
|
||||
|
||||
fun getIllusion(): BattlePokemon? = this.actor.activePokemon.find { it.battlePokemon == this }?.illusion
|
||||
}
|
||||
@@ -1,41 +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.runner
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
|
||||
import com.google.gson.JsonArray
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service.
|
||||
*
|
||||
* All outgoing calls to showdown should be done through this layer to decouple the mod from a specific implementation.
|
||||
* To get an instance of the service, use the singleton provided via `ShowdownService.get()`
|
||||
*
|
||||
* @since February 27th, 2023
|
||||
* @author landonjw
|
||||
*/
|
||||
interface ShowdownService {
|
||||
fun openConnection()
|
||||
fun closeConnection()
|
||||
fun startBattle(battle: PokemonBattle, messages: Array<String>)
|
||||
fun send(battleId: UUID, messages: Array<String>)
|
||||
fun getAbilityIds(): JsonArray
|
||||
fun getMoves(): JsonArray
|
||||
fun getItemIds(): JsonArray
|
||||
fun registerSpecies()
|
||||
fun registerBagItems()
|
||||
fun indicateSpeciesInitialized() {}
|
||||
|
||||
|
||||
companion object {
|
||||
val service: ShowdownService by lazy { GraalShowdownService() }
|
||||
}
|
||||
}
|
||||
-169
@@ -1,169 +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.runner.socket
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
||||
import java.nio.charset.Charset
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service via
|
||||
* a socket client.
|
||||
*
|
||||
* This is primarily used for debugging purposes, but could be extended in the future to provide
|
||||
* a means of connecting to a remote Showdown server. This does not provide any fault handling in the
|
||||
* event that the server goes down.
|
||||
*
|
||||
* When messages are sent to showdown, this will await a response from showdown.
|
||||
* The protocol for messages sent from showdown is <length of characters in payload><payload>,
|
||||
* and a payload length of 0 indicates that there is no response.
|
||||
*
|
||||
* @see {@code sim/cobbled-debug-server.ts} within cobblemon-showdown repository
|
||||
* @since February 27, 2023
|
||||
* @author landonjw
|
||||
*/
|
||||
class SocketShowdownService(val host: String = "localhost", val port: Int = 18468, val localPort: Int = 0) : ShowdownService {
|
||||
|
||||
private lateinit var socket: Socket
|
||||
private lateinit var writer: OutputStreamWriter
|
||||
private lateinit var reader: BufferedReader
|
||||
val gson = Gson()
|
||||
|
||||
override fun openConnection() {
|
||||
socket = Socket(InetAddress.getLocalHost(), port, InetAddress.getLocalHost(), localPort)
|
||||
writer = socket.getOutputStream().writer(charset = Charset.forName("ascii"))
|
||||
reader = BufferedReader(InputStreamReader(socket.getInputStream()))
|
||||
}
|
||||
|
||||
override fun closeConnection() {
|
||||
socket.close()
|
||||
}
|
||||
|
||||
override fun startBattle(battle: PokemonBattle, messages: Array<String>) {
|
||||
writer.write(">startbattle ${battle.battleId}\n")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to start battle!") }
|
||||
send(battle.battleId, messages)
|
||||
}
|
||||
|
||||
override fun send(battleId: UUID, messages: Array<String>) {
|
||||
for (message in messages) {
|
||||
writer.write("$battleId~$message\n")
|
||||
writer.flush()
|
||||
readBattleInput().forEach { interpretMessage(battleId, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun read(reader: BufferedReader, size: Int): String {
|
||||
val buffer = CharArray(size)
|
||||
while (true) {
|
||||
if(reader.read(buffer) == 0) continue
|
||||
return String(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readMessage(): String {
|
||||
val payloadSize = read(reader, 8).toInt()
|
||||
val payload = read(reader, payloadSize)
|
||||
return payload
|
||||
}
|
||||
|
||||
private fun readBattleInput(): List<String> {
|
||||
val lines = mutableListOf<String>()
|
||||
val numLines = read(reader, 8).toInt()
|
||||
if (numLines != 0) {
|
||||
for (i in 0 until numLines) {
|
||||
lines.add(readMessage())
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
private fun interpretMessage(battleId: UUID, message: String) {
|
||||
ShowdownInterpreter.interpretMessage(battleId, message)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
writer.write(">getCobbledAbilityIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
writer.write(">getCobbledMoves\n")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
writer.write(">getCobbledItemIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
private fun sendBagItem(itemId: String, js: String) {
|
||||
writer.write(">receiveBagItemData $itemId $js")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send bag item to Showdown: $itemId") }
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
writer.write(">resetSpeciesData\n")
|
||||
acknowledge()
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
sendSpeciesData(species, null)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
sendSpeciesData(species, form)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun acknowledge(ifFails: () -> Unit = {}) {
|
||||
writer.flush()
|
||||
val ack = CharArray(3)
|
||||
reader.read(ack)
|
||||
if (String(ack) != "ACK") {
|
||||
ifFails()
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerBagItems() {
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
sendBagItem(itemId, js.replace("\n", " "))
|
||||
}
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
writer.write(">afterCobbledSpeciesInit")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,12 +72,10 @@ import net.minecraft.client.renderer.blockentity.SignRenderer
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
||||
import net.minecraft.client.resources.PlayerSkin
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.AABB
|
||||
import java.util.UUID
|
||||
|
||||
object CobblemonClient {
|
||||
|
||||
@@ -329,7 +327,7 @@ object CobblemonClient {
|
||||
|
||||
fun endBattle() {
|
||||
battle = null
|
||||
battleOverlay.lastKnownBattle = null
|
||||
battleOverlay.lastKnownBattle = ""
|
||||
BattleMusicController.endMusic()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPa
|
||||
import java.util.UUID
|
||||
|
||||
class ClientBattle(
|
||||
val battleId: UUID,
|
||||
val battleId: String,
|
||||
val battleFormat: BattleFormat
|
||||
) {
|
||||
var minimised = true
|
||||
|
||||
@@ -108,7 +108,7 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
get() = (opacity - MIN_OPACITY) / (MAX_OPACITY - MIN_OPACITY)
|
||||
var passedSeconds = 0F
|
||||
|
||||
var lastKnownBattle: UUID? = null
|
||||
var lastKnownBattle: String = ""
|
||||
lateinit var messagePane: BattleMessagePane
|
||||
var hidePortraits = false
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
@@ -530,7 +530,7 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
|
||||
fun onLogout() {
|
||||
this.opacity = MIN_OPACITY
|
||||
this.passedSeconds = 0F
|
||||
this.lastKnownBattle = null
|
||||
this.lastKnownBattle = ""
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,6 @@ import net.minecraft.commands.arguments.selector.EntitySelector
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
object PokedexCommand {
|
||||
|
||||
private const val NAME = "pokedex"
|
||||
private const val GRANT_NAME = "grant"
|
||||
private const val REVOKE_NAME = "revoke"
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.command
|
||||
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.showdown.runner.CobblemonShowdownBattleServer
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.CommandDispatcher
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
@@ -27,10 +28,13 @@ object ReloadShowdownCommand {
|
||||
|
||||
private fun execute(context: CommandContext<CommandSourceStack>): Int {
|
||||
try {
|
||||
ShowdownService.service.closeConnection()
|
||||
ShowdownService.service.openConnection()
|
||||
ShowdownService.service.registerSpecies()
|
||||
ShowdownService.service.registerBagItems()
|
||||
Cobblemon.showdownServer.disconnect()
|
||||
Cobblemon.showdownServer.connect()
|
||||
val showdownServer = Cobblemon.showdownServer
|
||||
if (showdownServer is CobblemonShowdownBattleServer) {
|
||||
showdownServer.registerSpecies()
|
||||
showdownServer.registerBagItems()
|
||||
}
|
||||
context.source.sendSystemMessage(Component.literal("Reloaded showdown"))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.cobblemon.mod.common.command
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.showdown.runner.remote.RemoteShowdownBattleServer
|
||||
import com.cobblemon.mod.common.showdown.runner.remote.ShowdownGameServerLoginManager
|
||||
import com.cobblemon.mod.common.showdown.runner.remote.ShowdownGameServerLoginState
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.CommandDispatcher
|
||||
import com.mojang.brigadier.arguments.StringArgumentType
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import net.minecraft.commands.CommandSourceStack
|
||||
import net.minecraft.commands.Commands.argument
|
||||
import net.minecraft.commands.Commands.literal
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
object ShowdownCommand {
|
||||
private const val NAME = "showdown"
|
||||
|
||||
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
|
||||
dispatcher.register(
|
||||
literal(NAME)
|
||||
.then(
|
||||
literal("setnickname").then(
|
||||
argument("name", StringArgumentType.string()).executes(::executeSetNick)
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun executeSetNick(context: CommandContext<CommandSourceStack>): Int {
|
||||
val name = StringArgumentType.getString(context, "name")
|
||||
val showdownServer = Cobblemon.showdownServer
|
||||
if (showdownServer !is RemoteShowdownBattleServer) {
|
||||
context.source.sendFailure(Component.literal("Configured showdown server is not a showdown game server!!"))
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
val assertion = ShowdownGameServerLoginManager.loginGuest(name, showdownServer.getLoginState())
|
||||
showdownServer.setLoginState(ShowdownGameServerLoginState(assertion = assertion, username = name))
|
||||
showdownServer.sendServerMessage("|/trn $name,0,$assertion")
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.entity.npc
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.*
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.battles.ai.StrongBattleAI
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.chainFutures
|
||||
@@ -25,13 +20,8 @@ import java.util.concurrent.CompletableFuture
|
||||
|
||||
class NPCBattleActor(
|
||||
val npc: NPCEntity,
|
||||
pokemonList: List<BattlePokemon>,
|
||||
val skill: Int
|
||||
) : AIBattleActor(
|
||||
gameId = npc.uuid,
|
||||
pokemonList = pokemonList.let { if (npc.npc.randomizePartyOrder) it.shuffled() else it },
|
||||
battleAI = StrongBattleAI(skill)
|
||||
), EntityBackedBattleActor<NPCEntity> {
|
||||
pokemonProvider: BattlePokemonProvider
|
||||
) : BattleActor(npc.uuid, pokemonProvider), EntityBackedBattleActor<NPCEntity> {
|
||||
override val entity = npc
|
||||
override val type = ActorType.NPC
|
||||
override fun getName() = npc.effectiveName().copy()
|
||||
@@ -40,19 +30,17 @@ class NPCBattleActor(
|
||||
|
||||
constructor(
|
||||
npc: NPCEntity,
|
||||
party: PartyStore,
|
||||
skill: Int
|
||||
party: PartyStore
|
||||
): this(
|
||||
npc,
|
||||
party.toBattleTeam(healPokemon = npc.npc.autoHealParty),
|
||||
skill
|
||||
StaticBattlePokemonProvider(party.toBattleTeam(healPokemon = npc.npc.autoHealParty))
|
||||
)
|
||||
|
||||
override fun sendUpdate(packet: NetworkPacket<*>) {
|
||||
super.sendUpdate(packet)
|
||||
if (packet is BattleEndPacket) {
|
||||
if (npc.isAlive) {
|
||||
val allEntities = pokemonList.mapNotNull { it.entity }.toMutableList()
|
||||
val allEntities = battlePokemonProvider.getKnownPokemon().mapNotNull { it.entity }.toMutableList()
|
||||
val finalFuture = CompletableFuture<Unit>()
|
||||
chainFutures(allEntities.map { pokemonEntity -> { pokemonEntity.recallWithAnimation() } }.iterator(), finalFuture)
|
||||
if (allEntities.isEmpty()) {
|
||||
|
||||
@@ -164,7 +164,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
val aspects: Set<String>
|
||||
get() = entityData.get(ASPECTS)
|
||||
|
||||
val battleIds: Set<UUID>
|
||||
val battleIds: Set<String>
|
||||
get() = entityData.get(BATTLE_IDS)
|
||||
|
||||
var actionEffect: ActionEffectContext? = null
|
||||
@@ -207,7 +207,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
val NPC_CLASS = SynchedEntityData.defineId(NPCEntity::class.java, IdentifierDataSerializer)
|
||||
val ASPECTS = SynchedEntityData.defineId(NPCEntity::class.java, StringSetDataSerializer)
|
||||
val POSE_TYPE = SynchedEntityData.defineId(NPCEntity::class.java, PoseTypeDataSerializer)
|
||||
val BATTLE_IDS = SynchedEntityData.defineId(NPCEntity::class.java, UUIDSetDataSerializer)
|
||||
val BATTLE_IDS = SynchedEntityData.defineId(NPCEntity::class.java, StringSetDataSerializer)
|
||||
val NPC_PLAYER_TEXTURE = SynchedEntityData.defineId(NPCEntity::class.java, NPCPlayerTextureSerializer)
|
||||
val LEVEL = SynchedEntityData.defineId(NPCEntity::class.java, EntityDataSerializers.INT)
|
||||
val HIDE_NAME_TAG = SynchedEntityData.defineId(NPCEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
return drop() // Weird, shouldn't be possible
|
||||
}
|
||||
|
||||
if (battle.format.battleType != BattleTypes.SINGLES || hitActor.pokemonList.count { it.health > 0 } > 1) {
|
||||
if (battle.format.battleType != BattleTypes.SINGLES || hitActor.battlePokemonProvider.getKnownPokemon().count { it.health > 0 } > 1) {
|
||||
owner.sendSystemMessage(lang("capture.not_single").red())
|
||||
return drop()
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.SuccessfulBattleStart
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.entity.PlatformType
|
||||
@@ -87,8 +88,6 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.mojang.serialization.Codec
|
||||
import java.util.EnumSet
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.math.PI
|
||||
import net.minecraft.core.BlockPos
|
||||
@@ -168,7 +167,7 @@ open class PokemonEntity(
|
||||
@JvmStatic val PHASING_TARGET_ID = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.INT)
|
||||
@JvmStatic val PLATFORM_TYPE = SynchedEntityData.defineId(PokemonEntity::class.java, PlatformTypeDataSerializer)
|
||||
@JvmStatic val BEAM_MODE = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BYTE)
|
||||
@JvmStatic val BATTLE_ID = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.OPTIONAL_UUID)
|
||||
@JvmStatic val BATTLE_ID = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.STRING)
|
||||
@JvmStatic val ASPECTS = SynchedEntityData.defineId(PokemonEntity::class.java, StringSetDataSerializer)
|
||||
@JvmStatic val DYING_EFFECTS_STARTED = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||
@JvmStatic val POSE_TYPE = SynchedEntityData.defineId(PokemonEntity::class.java, PoseTypeDataSerializer)
|
||||
@@ -229,11 +228,11 @@ open class PokemonEntity(
|
||||
get() = busyLocks.isNotEmpty()
|
||||
val aspects: Set<String>
|
||||
get() = entityData.get(ASPECTS)
|
||||
var battleId: UUID?
|
||||
get() = entityData.get(BATTLE_ID).orElse(null)
|
||||
set(value) = entityData.set(BATTLE_ID, Optional.ofNullable(value))
|
||||
var battleId: String
|
||||
get() = entityData.get(BATTLE_ID) ?: ""
|
||||
set(value) = entityData.set(BATTLE_ID, value ?: "")
|
||||
val isBattling: Boolean
|
||||
get() = entityData.get(BATTLE_ID).isPresent
|
||||
get() = entityData.get(BATTLE_ID) != ""
|
||||
val friendship: Int
|
||||
get() = entityData.get(FRIENDSHIP)
|
||||
val seats: List<Seat>
|
||||
@@ -329,7 +328,7 @@ open class PokemonEntity(
|
||||
builder.define(BEAM_MODE, 0)
|
||||
builder.define(PLATFORM_TYPE, PlatformType.NONE)
|
||||
builder.define(PHASING_TARGET_ID, -1)
|
||||
builder.define(BATTLE_ID, Optional.empty())
|
||||
builder.define(BATTLE_ID, "")
|
||||
builder.define(ASPECTS, emptySet())
|
||||
builder.define(DYING_EFFECTS_STARTED, false)
|
||||
builder.define(POSE_TYPE, PoseType.STAND)
|
||||
@@ -614,8 +613,8 @@ open class PokemonEntity(
|
||||
nbt.put(DataKeys.POKEMON, pokemon.saveToNBT(registryAccess()))
|
||||
}
|
||||
val battleIdToSave = battleId
|
||||
if (battleIdToSave != null) {
|
||||
nbt.putUUID(DataKeys.POKEMON_BATTLE_ID, battleIdToSave)
|
||||
if (battleIdToSave != "") {
|
||||
nbt.putString(DataKeys.POKEMON_BATTLE_ID, battleIdToSave)
|
||||
}
|
||||
nbt.putString(DataKeys.POKEMON_POSE_TYPE, entityData.get(POSE_TYPE).name)
|
||||
nbt.putByte(DataKeys.POKEMON_BEHAVIOUR_FLAGS, entityData.get(BEHAVIOUR_FLAGS))
|
||||
@@ -683,7 +682,7 @@ open class PokemonEntity(
|
||||
}
|
||||
|
||||
val savedBattleId =
|
||||
if (nbt.hasUUID(DataKeys.POKEMON_BATTLE_ID)) nbt.getUUID(DataKeys.POKEMON_BATTLE_ID) else null
|
||||
if (nbt.contains(DataKeys.POKEMON_BATTLE_ID)) nbt.getString(DataKeys.POKEMON_BATTLE_ID) else ""
|
||||
if (savedBattleId != null) {
|
||||
val battle = BattleRegistry.getBattle(savedBattleId)
|
||||
if (battle != null) {
|
||||
@@ -1065,9 +1064,9 @@ open class PokemonEntity(
|
||||
|
||||
val bagItemLike = BagItems.getConvertibleForStack(stack) ?: return false
|
||||
|
||||
val battlePokemon =
|
||||
battle.actors.flatMap { it.pokemonList }.find { it.effectedPokemon.uuid == pokemon.uuid }
|
||||
?: return false // Shouldn't be possible but anyway
|
||||
val battlePokemon = battle.actors.flatMap { it.battlePokemonProvider.getKnownPokemon() }
|
||||
.filterIsInstance<BattlePokemon>()
|
||||
.find { it.effectedPokemon.uuid == pokemon.uuid } ?: return false
|
||||
if (battlePokemon.actor.getSide().actors.none { it.isForPlayer(player) }) {
|
||||
return true
|
||||
}
|
||||
|
||||
+1
-11
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.Rollable
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSender
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
@@ -23,7 +22,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
|
||||
import com.cobblemon.mod.common.util.getIsSubmerged
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.update
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
@@ -34,12 +32,9 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.syncher.EntityDataAccessor
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Vector3f
|
||||
|
||||
/** Handles purely server logic for a Pokémon */
|
||||
class PokemonServerDelegate : PokemonSideDelegate {
|
||||
@@ -162,12 +157,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
// }
|
||||
|
||||
entity.entityData.update(PokemonEntity.BATTLE_ID) { opt ->
|
||||
val battleId = opt.orElse(null)
|
||||
if (battleId != null && BattleRegistry.getBattle(battleId).let { it == null || it.ended }) {
|
||||
Optional.empty()
|
||||
} else {
|
||||
opt
|
||||
}
|
||||
opt
|
||||
}
|
||||
|
||||
val battle = getBattle()
|
||||
|
||||
+2
-2
@@ -86,10 +86,10 @@ class IllusionEffect(
|
||||
val battleId = entity.battleId ?: return
|
||||
val battle = BattleRegistry.getBattle(battleId) ?: return
|
||||
val wildActor = battle.getActor(entity.pokemon.uuid) ?: return
|
||||
val battlePokemon = wildActor.pokemonList.firstOrNull { it.uuid == entity.pokemon.uuid } ?: return
|
||||
val battlePokemon = wildActor.battlePokemonProvider.getKnownPokemon().firstOrNull { it.uuid == entity.pokemon.uuid } ?: return
|
||||
// Step 2 flag all players as seeing the Pokemon
|
||||
battle.playerUUIDs.forEach { uuid ->
|
||||
CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(uuid, battlePokemon.effectedPokemon))
|
||||
//CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(uuid, battlePokemon.effectedPokemon))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,13 @@ package com.cobblemon.mod.common.events
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.*
|
||||
import com.cobblemon.mod.common.advancement.criterion.BattleCountableContext
|
||||
import com.cobblemon.mod.common.advancement.criterion.CountableContext
|
||||
import com.cobblemon.mod.common.advancement.criterion.CountablePokemonTypeContext
|
||||
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
|
||||
import com.cobblemon.mod.common.advancement.criterion.LevelUpContext
|
||||
import com.cobblemon.mod.common.advancement.criterion.PlantTumblestoneContext
|
||||
import com.cobblemon.mod.common.advancement.criterion.TradePokemonContext
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
|
||||
@@ -19,16 +25,20 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.*
|
||||
import com.cobblemon.mod.common.api.events.pokemon.CollectEggEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.HatchEggEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.LevelUpEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.PokemonCapturedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.TradeCompletedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.block.TumblestoneBlock
|
||||
import com.cobblemon.mod.common.item.TumblestoneItem
|
||||
import com.cobblemon.mod.common.platform.events.ServerPlayerEvent
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.platform.events.ServerPlayerEvent
|
||||
import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
object AdvancementHandler : EventHandler {
|
||||
override fun registerListeners() {
|
||||
@@ -110,8 +120,8 @@ object AdvancementHandler : EventHandler {
|
||||
val advancementData = playerData.advancementData
|
||||
event.battle.actors.forEach { battleActor ->
|
||||
if (!event.winners.contains(battleActor) && battleActor.type == ActorType.WILD) {
|
||||
battleActor.pokemonList.forEach { battlePokemon ->
|
||||
advancementData.updateTotalDefeatedCount(battlePokemon.originalPokemon)
|
||||
battleActor.battlePokemonProvider.getKnownPokemon().forEach { battlePokemon ->
|
||||
//advancementData.updateTotalDefeatedCount(battlePokemon.originalPokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.item.interactive
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
@@ -18,13 +17,15 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.healing.PokemonHealedEvent
|
||||
import com.cobblemon.mod.common.api.item.HealingSource
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.BagItemActionResponse
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.isHeld
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -65,12 +66,13 @@ class ReviveItem(
|
||||
val battle = BattleRegistry.getBattleByParticipatingPlayer(player)
|
||||
if (battle != null) {
|
||||
val actor = battle.getActor(player)!!
|
||||
val battlePokemon = actor.pokemonList
|
||||
val battlePokemon = actor.battlePokemonProvider.getKnownPokemon()
|
||||
if (!actor.canFitForcedAction()) {
|
||||
player.sendSystemMessage(battleLang("bagitem.cannot").red(), true)
|
||||
return InteractionResultHolder.consume(stack)
|
||||
} else {
|
||||
val turn = battle.turn
|
||||
/*
|
||||
PartySelectCallbacks.createBattleSelect(
|
||||
player = player,
|
||||
pokemon = battlePokemon,
|
||||
@@ -86,6 +88,8 @@ class ReviveItem(
|
||||
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(bp.effectedPokemon.species.resourceIdentifier, stackName))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
val pokemon = player.party().toList()
|
||||
|
||||
+4
-4
@@ -29,10 +29,10 @@ import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeMapK
|
||||
import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.ComponentSerialization
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Initializes the client's understanding of a battle. This can be for a participant or for a spectator.
|
||||
@@ -46,7 +46,7 @@ class BattleInitializePacket() : NetworkPacket<BattleInitializePacket> {
|
||||
|
||||
override val id = ID
|
||||
|
||||
lateinit var battleId: UUID
|
||||
lateinit var battleId: String
|
||||
lateinit var battleFormat: BattleFormat
|
||||
|
||||
lateinit var side1: BattleSideDTO
|
||||
@@ -79,7 +79,7 @@ class BattleInitializePacket() : NetworkPacket<BattleInitializePacket> {
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(battleId)
|
||||
buffer.writeString(battleId)
|
||||
battleFormat.saveToBuffer(buffer)
|
||||
for (side in arrayOf(side1, side2)) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, side.actors.size)
|
||||
@@ -98,7 +98,7 @@ class BattleInitializePacket() : NetworkPacket<BattleInitializePacket> {
|
||||
}
|
||||
|
||||
private fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
battleId = buffer.readUUID()
|
||||
battleId = buffer.readString()
|
||||
battleFormat = BattleFormat.loadFromBuffer(buffer)
|
||||
val sides = mutableListOf<BattleSideDTO>()
|
||||
repeat(times = 2) {
|
||||
|
||||
+3
-4
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.util.writeEnumConstant
|
||||
import com.cobblemon.mod.common.util.writeIdentifier
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.cobblemon.mod.common.util.writeText
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
|
||||
@@ -33,7 +32,7 @@ class SpawnNPCPacket(
|
||||
var npcClass: ResourceLocation,
|
||||
var aspects: Set<String>,
|
||||
var level: Int,
|
||||
var battleIds: Set<UUID>,
|
||||
var battleIds: Set<String>,
|
||||
var name: Component,
|
||||
var poseType: PoseType,
|
||||
var texture: NPCPlayerTexture,
|
||||
@@ -59,7 +58,7 @@ class SpawnNPCPacket(
|
||||
buffer.writeIdentifier(this.npcClass)
|
||||
buffer.writeCollection(this.aspects) { pb, value -> pb.writeString(value) }
|
||||
buffer.writeInt(this.level)
|
||||
buffer.writeCollection(this.battleIds) { pb, value -> pb.writeUUID(value) }
|
||||
buffer.writeCollection(this.battleIds) { pb, value -> pb.writeString(value) }
|
||||
buffer.writeText(name)
|
||||
buffer.writeEnumConstant(this.poseType)
|
||||
buffer.writeEnumConstant(this.texture.model)
|
||||
@@ -88,7 +87,7 @@ class SpawnNPCPacket(
|
||||
val npc = buffer.readIdentifier()
|
||||
val aspects = buffer.readList { buffer.readString() }.toSet()
|
||||
val level = buffer.readInt()
|
||||
val battleIds = buffer.readList { buffer.readUUID() }.toSet()
|
||||
val battleIds = buffer.readList { buffer.readString() }.toSet()
|
||||
val name = buffer.readText()
|
||||
val poseType = buffer.readEnumConstant(PoseType::class.java)
|
||||
val model = buffer.readEnumConstant(NPCPlayerModelType::class.java)
|
||||
|
||||
+14
-6
@@ -15,13 +15,21 @@ import com.cobblemon.mod.common.entity.PlatformType
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import java.util.UUID
|
||||
import net.minecraft.world.entity.Entity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readEnumConstant
|
||||
import com.cobblemon.mod.common.util.readIdentifier
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.readText
|
||||
import com.cobblemon.mod.common.util.writeEnumConstant
|
||||
import com.cobblemon.mod.common.util.writeIdentifier
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.cobblemon.mod.common.util.writeText
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
import java.util.UUID
|
||||
|
||||
class SpawnPokemonPacket(
|
||||
var ownerId: UUID?,
|
||||
@@ -31,7 +39,7 @@ class SpawnPokemonPacket(
|
||||
var shiny: Boolean,
|
||||
var formName: String,
|
||||
var aspects: Set<String>,
|
||||
var battleId: UUID?,
|
||||
var battleId: String,
|
||||
var phasingTargetId: Int,
|
||||
var beamMode: Byte,
|
||||
var platform: PlatformType,
|
||||
@@ -81,7 +89,7 @@ class SpawnPokemonPacket(
|
||||
buffer.writeBoolean(this.shiny)
|
||||
buffer.writeString(this.formName)
|
||||
buffer.writeCollection(this.aspects) { pb, value -> pb.writeString(value) }
|
||||
buffer.writeNullable(this.battleId) { pb, value -> pb.writeUUID(value) }
|
||||
buffer.writeString(this.battleId)
|
||||
buffer.writeInt(this.phasingTargetId)
|
||||
buffer.writeByte(this.beamMode.toInt())
|
||||
buffer.writeEnumConstant(this.platform)
|
||||
@@ -135,7 +143,7 @@ class SpawnPokemonPacket(
|
||||
val shiny = buffer.readBoolean()
|
||||
val formName = buffer.readString()
|
||||
val aspects = buffer.readList { it.readString() }.toSet()
|
||||
val battleId = buffer.readNullable { buffer.readUUID() }
|
||||
val battleId = buffer.readString()
|
||||
val phasingTargetId = buffer.readInt()
|
||||
val beamModeEmitter = buffer.readByte()
|
||||
val platform = buffer.readEnumConstant(PlatformType::class.java)
|
||||
|
||||
+5
-4
@@ -13,16 +13,17 @@ import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import java.util.UUID
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class BattleSelectActionsPacket(val battleId: UUID, val showdownActionResponses: List<ShowdownActionResponse>) : NetworkPacket<BattleSelectActionsPacket> {
|
||||
class BattleSelectActionsPacket(val battleId: String, val showdownActionResponses: List<ShowdownActionResponse>) : NetworkPacket<BattleSelectActionsPacket> {
|
||||
|
||||
override val id = ID
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(battleId)
|
||||
buffer.writeString(battleId)
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, showdownActionResponses.size)
|
||||
showdownActionResponses.forEach { it.saveToBuffer(buffer) }
|
||||
}
|
||||
@@ -30,7 +31,7 @@ class BattleSelectActionsPacket(val battleId: UUID, val showdownActionResponses:
|
||||
companion object {
|
||||
val ID = cobblemonResource("battle_select_actions")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): BattleSelectActionsPacket {
|
||||
val battleId = buffer.readUUID()
|
||||
val battleId = buffer.readString()
|
||||
val responses = mutableListOf<ShowdownActionResponse>()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
responses.add(ShowdownActionResponse.loadFromBuffer(buffer))
|
||||
|
||||
+5
-4
@@ -10,17 +10,18 @@ package com.cobblemon.mod.common.net.messages.server.battle
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.UUID
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class RemoveSpectatorPacket(val battleId: UUID) : NetworkPacket<RemoveSpectatorPacket> {
|
||||
class RemoveSpectatorPacket(val battleId: String) : NetworkPacket<RemoveSpectatorPacket> {
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeUUID(battleId)
|
||||
buffer.writeString(battleId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("remove_spectator")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RemoveSpectatorPacket(buffer.readUUID())
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RemoveSpectatorPacket(buffer.readString())
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.net.serverhandling
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.PokemonSeenEvent
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
@@ -69,9 +67,9 @@ object ChallengeHandler : ServerNetworkPacketHandler<BattleChallengePacket> {
|
||||
private fun flagAsSeen(battle: PokemonBattle, entity: PokemonEntity) {
|
||||
// Wild actor is tied to Pokemon UUID not entity
|
||||
val actor = battle.getActor(entity.pokemon.uuid) as? PokemonBattleActor ?: return
|
||||
val battlePokemon = actor.pokemonList.firstOrNull { it.uuid == entity.pokemon.uuid } ?: return
|
||||
val battlePokemon = actor.battlePokemonProvider.getKnownPokemon().firstOrNull { it.uuid == entity.pokemon.uuid } ?: return
|
||||
battle.playerUUIDs.forEach { uuid ->
|
||||
CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(uuid, battlePokemon.effectedPokemon))
|
||||
//CobblemonEvents.POKEMON_SEEN.post(PokemonSeenEvent(uuid, battlePokemon.effectedPokemon))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
@@ -59,7 +58,6 @@ import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
|
||||
import com.cobblemon.mod.common.api.riding.RidingProperties
|
||||
import com.cobblemon.mod.common.api.storage.StoreCoordinates
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
@@ -603,7 +601,7 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
source: LivingEntity,
|
||||
level: ServerLevel,
|
||||
position: Vec3,
|
||||
battleId: UUID? = null,
|
||||
battleId: String = "",
|
||||
doCry: Boolean = true,
|
||||
illusion: IllusionEffect? = null,
|
||||
mutation: (PokemonEntity) -> Unit = {},
|
||||
@@ -720,7 +718,7 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
player: ServerPlayer,
|
||||
level: ServerLevel,
|
||||
targetPosition: Vec3,
|
||||
battleId: UUID? = null,
|
||||
battleId: String = "",
|
||||
doCry: Boolean = true,
|
||||
illusion: IllusionEffect? = null,
|
||||
mutation: (PokemonEntity) -> Unit = {}
|
||||
|
||||
+1
-6
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.google.common.collect.HashBiMap
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.chat.Component
|
||||
@@ -32,11 +31,7 @@ abstract class BaseCobblemonHeldItemManager : HeldItemManager {
|
||||
|
||||
internal open fun load() {
|
||||
this.itemIds.clear()
|
||||
val itemsJson = ShowdownService.service.getItemIds()
|
||||
val showdownIds = hashSetOf<String>()
|
||||
for (i in 0 until itemsJson.size()) {
|
||||
showdownIds += itemsJson[i].asString
|
||||
}
|
||||
val showdownIds = Cobblemon.showdownServer.getItemIds()
|
||||
BuiltInRegistries.ITEM.forEach { item ->
|
||||
val identifier = BuiltInRegistries.ITEM.getKey(item)
|
||||
if (identifier.namespace == Cobblemon.MODID) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.cobblemon.mod.common.showdown
|
||||
|
||||
data class ShowdownSearch(
|
||||
val searching: List<String>,
|
||||
val games: Map<String, String>?
|
||||
) {
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.cobblemon.mod.common.showdown.runner
|
||||
|
||||
/**
|
||||
* Functionality specific to the Cobblemon fork of Pokemon Showdown
|
||||
*/
|
||||
interface CobblemonShowdownBattleServer : ShowdownBattleServer {
|
||||
fun registerSpecies()
|
||||
fun indicateSpeciesInitialized() {}
|
||||
fun registerBagItems() {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.cobblemon.mod.common.showdown.runner
|
||||
|
||||
abstract class ShowdownBattle {
|
||||
abstract val id: String
|
||||
|
||||
fun sendMessage(message: String) {
|
||||
sendMessages(message)
|
||||
}
|
||||
fun sendMessages(vararg messages: String) {
|
||||
return sendMessages(messages.toList())
|
||||
}
|
||||
|
||||
abstract fun sendMessages(messages: List<String>)
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.cobblemon.mod.common.showdown.runner
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
|
||||
/**
|
||||
* A service that controls the creation of individual Pokemon battles with Pokemon Showdown
|
||||
*/
|
||||
interface ShowdownBattleServer {
|
||||
fun connect()
|
||||
fun disconnect()
|
||||
fun initializeNewBattle(battleId: String, messages: Array<String>): ShowdownBattle
|
||||
fun sendServerMessage(msg: String)
|
||||
//Ideally this should be different per-battle but since we only refresh the registries at world load, that will be challenging
|
||||
fun getAbilityIds(): Collection<String>
|
||||
fun getMoves(): Collection<MoveTemplate>
|
||||
fun getItemIds(): Collection<String>
|
||||
fun onTick() {}
|
||||
fun onServerStarted()
|
||||
}
|
||||
+86
-81
@@ -6,16 +6,23 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.runner.graal
|
||||
package com.cobblemon.mod.common.showdown.runner.graal
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.showdown.runner.CobblemonShowdownBattleServer
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattle
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.HostAccess
|
||||
import org.graalvm.polyglot.HostAccess.Export
|
||||
import org.graalvm.polyglot.PolyglotAccess
|
||||
import org.graalvm.polyglot.Value
|
||||
import org.graalvm.polyglot.io.FileSystem
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
@@ -27,12 +34,6 @@ import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.attribute.FileAttribute
|
||||
import java.util.UUID
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.HostAccess
|
||||
import org.graalvm.polyglot.HostAccess.Export
|
||||
import org.graalvm.polyglot.PolyglotAccess
|
||||
import org.graalvm.polyglot.Value
|
||||
import org.graalvm.polyglot.io.FileSystem
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service via
|
||||
@@ -43,8 +44,7 @@ import org.graalvm.polyglot.io.FileSystem
|
||||
* @since February 27, 2023
|
||||
* @author Hiroku, landonjw
|
||||
*/
|
||||
class GraalShowdownService : ShowdownService {
|
||||
|
||||
class GraalCobblemonShowdownBattleServer : CobblemonShowdownBattleServer {
|
||||
@Transient
|
||||
lateinit var context: Context
|
||||
@Transient
|
||||
@@ -54,69 +54,13 @@ class GraalShowdownService : ShowdownService {
|
||||
@Transient
|
||||
val gson = Gson()
|
||||
|
||||
override fun openConnection() {
|
||||
override fun connect() {
|
||||
unbundler.attemptUnbundle()
|
||||
createContext()
|
||||
boot()
|
||||
}
|
||||
|
||||
private fun createContext() {
|
||||
val wd = Paths.get("./showdown")
|
||||
val access = HostAccess.newBuilder(HostAccess.EXPLICIT)
|
||||
.allowIterableAccess(true)
|
||||
.allowArrayAccess(true)
|
||||
.allowListAccess(true)
|
||||
.allowMapAccess(true)
|
||||
.build()
|
||||
context = Context.newBuilder("js")
|
||||
.allowIO(true)
|
||||
.fileSystem(object : FileSystem
|
||||
{
|
||||
val default = FileSystem.newDefaultFileSystem()
|
||||
override fun parsePath(uri: URI) = default.parsePath(uri)
|
||||
override fun parsePath(path: String) = default.parsePath(path)
|
||||
override fun createDirectory(dir: Path, vararg attrs: FileAttribute<*>) = default.createDirectory(dir, *attrs)
|
||||
override fun delete(path: Path) = default.delete(path)
|
||||
override fun newByteChannel(path: Path, options: MutableSet<out OpenOption>, vararg attrs: FileAttribute<*>) = default.newByteChannel(path, options, *attrs)
|
||||
override fun newDirectoryStream(dir: Path, filter: DirectoryStream.Filter<in Path>) = default.newDirectoryStream(dir, filter)
|
||||
override fun toAbsolutePath(path: Path) = default.toAbsolutePath(path)
|
||||
override fun toRealPath(path: Path, vararg linkOptions: LinkOption) = default.toRealPath(path, *linkOptions)
|
||||
override fun readAttributes(path: Path, attributes: String, vararg options: LinkOption) = default.readAttributes(path, attributes, *options)
|
||||
override fun checkAccess(path: Path, modes: MutableSet<out AccessMode>, vararg linkOptions: LinkOption) {
|
||||
if (!path.toRealPath(LinkOption.NOFOLLOW_LINKS).startsWith(wd.toRealPath(LinkOption.NOFOLLOW_LINKS))) {
|
||||
Cobblemon.LOGGER.error("Hacked JS files in datapacks or some weird file system setup that Hiroku failed to anticipate.")
|
||||
throw IOException("Someone has put hacked JS files into datapacks because file access is being attempted outside of controlled folders.")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowPolyglotAccess(PolyglotAccess.ALL)
|
||||
.allowHostAccess(access)
|
||||
.allowCreateThread(true)
|
||||
.logHandler(GraalLogger)
|
||||
.option("engine.WarnInterpreterOnly", "false")
|
||||
.option("js.commonjs-require", "true")
|
||||
.option("js.commonjs-require-cwd", "showdown")
|
||||
.option(
|
||||
"js.commonjs-core-modules-replacements",
|
||||
"buffer:buffer/,crypto:crypto-browserify,path:path-browserify"
|
||||
)
|
||||
.allowHostClassLoading(true)
|
||||
.allowNativeAccess(true)
|
||||
.allowCreateProcess(true)
|
||||
.build()
|
||||
|
||||
context.eval("js", """
|
||||
globalThis.process = {
|
||||
cwd: function() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
override fun closeConnection() {
|
||||
override fun disconnect() {
|
||||
context.close()
|
||||
}
|
||||
|
||||
@@ -125,31 +69,36 @@ class GraalShowdownService : ShowdownService {
|
||||
sendBattleMessageFunction = context.getBindings("js").getMember("sendBattleMessage")
|
||||
}
|
||||
|
||||
override fun startBattle(battle: PokemonBattle, messages: Array<String>) {
|
||||
override fun initializeNewBattle(battleId: String, messages: Array<String>): ShowdownBattle {
|
||||
val startBattleFunction = context.getBindings("js").getMember("startBattle")
|
||||
startBattleFunction.execute(this, battle.battleId.toString(), messages)
|
||||
startBattleFunction.execute(this, battleId, messages)
|
||||
return GraalShowdownBattle(battleId, sendBattleMessageFunction)
|
||||
}
|
||||
|
||||
override fun send(battleId: UUID, messages: Array<String>) {
|
||||
sendToShowdown(battleId, messages)
|
||||
override fun sendServerMessage(msg: String) {
|
||||
throw NotImplementedError("You shouldn't be sending server messages to a GraalShowdownBattleServer, all messages should be sent in a GraalShowdownBattle")
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
override fun getAbilityIds(): Collection<String> {
|
||||
val getCobbledAbilityIdsFn = context.getBindings("js").getMember("getCobbledAbilityIds")
|
||||
val arrayResult = getCobbledAbilityIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java).map { it.asString }.toSet()
|
||||
}
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
override fun getMoves(): Collection<MoveTemplate> {
|
||||
val getCobbledMovesFn = context.getBindings("js").getMember("getCobbledMoves")
|
||||
val arrayResult = getCobbledMovesFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java).map { MoveTemplate.decodeJsonMove(it.asJsonObject) }.toSet()
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
override fun getItemIds(): Collection<String> {
|
||||
val getCobbledItemIdsFn = context.getBindings("js").getMember("getCobbledItemIds")
|
||||
val arrayResult = getCobbledItemIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java).map { it.asString }.toSet()
|
||||
}
|
||||
|
||||
override fun onServerStarted() {
|
||||
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
@@ -179,13 +128,69 @@ class GraalShowdownService : ShowdownService {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createContext() {
|
||||
val wd = Paths.get("./showdown")
|
||||
val access = HostAccess.newBuilder(HostAccess.EXPLICIT)
|
||||
.allowIterableAccess(true)
|
||||
.allowArrayAccess(true)
|
||||
.allowListAccess(true)
|
||||
.allowMapAccess(true)
|
||||
.build()
|
||||
context = Context.newBuilder("js")
|
||||
.allowIO(true)
|
||||
.fileSystem(object : FileSystem
|
||||
{
|
||||
val default = FileSystem.newDefaultFileSystem()
|
||||
override fun parsePath(uri: URI) = default.parsePath(uri)
|
||||
override fun parsePath(path: String) = default.parsePath(path)
|
||||
override fun createDirectory(dir: Path, vararg attrs: FileAttribute<*>) = default.createDirectory(dir, *attrs)
|
||||
override fun delete(path: Path) = default.delete(path)
|
||||
override fun newByteChannel(path: Path, options: MutableSet<out OpenOption>, vararg attrs: FileAttribute<*>) = default.newByteChannel(path, options, *attrs)
|
||||
override fun newDirectoryStream(dir: Path, filter: DirectoryStream.Filter<in Path>) = default.newDirectoryStream(dir, filter)
|
||||
override fun toAbsolutePath(path: Path) = default.toAbsolutePath(path)
|
||||
override fun toRealPath(path: Path, vararg linkOptions: LinkOption) = default.toRealPath(path, *linkOptions)
|
||||
override fun readAttributes(path: Path, attributes: String, vararg options: LinkOption) = default.readAttributes(path, attributes, *options)
|
||||
override fun checkAccess(path: Path, modes: MutableSet<out AccessMode>, vararg linkOptions: LinkOption) {
|
||||
if (!path.toRealPath(LinkOption.NOFOLLOW_LINKS).startsWith(wd.toRealPath(LinkOption.NOFOLLOW_LINKS))) {
|
||||
Cobblemon.LOGGER.error("Hacked JS files in datapacks or some weird file system setup that Hiroku failed to anticipate.")
|
||||
throw IOException("Someone has put hacked JS files into datapacks because file access is being attempted outside of controlled folders.")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowPolyglotAccess(PolyglotAccess.ALL)
|
||||
.allowHostAccess(access)
|
||||
.allowCreateThread(true)
|
||||
.logHandler(GraalLogger)
|
||||
.option("engine.WarnInterpreterOnly", "false")
|
||||
.option("js.commonjs-require", "true")
|
||||
.option("js.commonjs-require-cwd", "showdown")
|
||||
.option(
|
||||
"js.commonjs-core-modules-replacements",
|
||||
"buffer:buffer/,crypto:crypto-browserify,path:path-browserify"
|
||||
)
|
||||
.allowHostClassLoading(true)
|
||||
.allowNativeAccess(true)
|
||||
.allowCreateProcess(true)
|
||||
.build()
|
||||
|
||||
context.eval("js", """
|
||||
globalThis.process = {
|
||||
cwd: function() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
private fun sendToShowdown(battleId: UUID, messages: Array<String>) {
|
||||
sendBattleMessageFunction.execute(battleId.toString(), messages)
|
||||
sendBattleMessageFunction.execute(battleId, messages)
|
||||
}
|
||||
|
||||
@Export
|
||||
fun sendFromShowdown(battleId: String, message: String) {
|
||||
ShowdownInterpreter.interpretMessage(UUID.fromString(battleId), message)
|
||||
ShowdownInterpreter.interpretMessage(battleId, message)
|
||||
}
|
||||
|
||||
@Export
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.runner.graal
|
||||
package com.cobblemon.mod.common.showdown.runner.graal
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import java.util.logging.Handler
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.graal
|
||||
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattle
|
||||
import org.graalvm.polyglot.Value
|
||||
|
||||
class GraalShowdownBattle(
|
||||
override val id: String,
|
||||
val sendBattleFunction: Value
|
||||
) : ShowdownBattle() {
|
||||
override fun sendMessages(messages: List<String>) {
|
||||
sendBattleFunction.execute(id, messages)
|
||||
}
|
||||
}
|
||||
+1
-6
@@ -6,20 +6,15 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.runner.graal
|
||||
package com.cobblemon.mod.common.showdown.runner.graal
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.util.FileUtils
|
||||
import com.cobblemon.mod.common.util.extractTo
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import oshi.util.FileUtil
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
/**
|
||||
* Unbundles a zipped Showdown file found within {@code resources/data/showdown.zip} for use within the GraalShowdownService.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.remote
|
||||
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattle
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
class RemoteShowdownBattle(
|
||||
override val id: String,
|
||||
val gameType: String,
|
||||
val remoteSide: String,
|
||||
val teamObject: JsonObject
|
||||
) : ShowdownBattle() {
|
||||
override fun sendMessages(messages: List<String>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
class Builder(val id: String) {
|
||||
val gson = GsonBuilder().create()
|
||||
|
||||
var gameType: String? = null
|
||||
var localSide: String? = null
|
||||
var teamStr: String? = null
|
||||
|
||||
fun gameType(gameType: String) = apply { this.gameType = gameType }
|
||||
fun remoteSide(localSide: String) = apply { this.localSide = localSide }
|
||||
fun teamStr(teamStr: String) = apply { this.teamStr = teamStr }
|
||||
fun build(): RemoteShowdownBattle {
|
||||
val teamObj = gson.fromJson(teamStr, JsonObject::class.java)
|
||||
return RemoteShowdownBattle(id, gameType!!, localSide!!, teamObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.remote
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattle
|
||||
import com.cobblemon.mod.common.showdown.runner.ShowdownBattleServer
|
||||
import javax.naming.OperationNotSupportedException
|
||||
|
||||
class RemoteShowdownBattleServer(
|
||||
val socketUrl: String,
|
||||
val websiteUrl: String,
|
||||
) : ShowdownBattleServer {
|
||||
private val client = ShowdownGameClient(socketUrl)
|
||||
private val scraper = ShowdownClientScraper(websiteUrl)
|
||||
private val state = ShowdownGameServerConnectionState()
|
||||
private val interpreter = ShowdownGameServerInterpreter(state)
|
||||
|
||||
override fun connect() {
|
||||
client.connect()
|
||||
}
|
||||
|
||||
override fun disconnect() {
|
||||
client.close()
|
||||
}
|
||||
|
||||
override fun initializeNewBattle(battleId: String, messages: Array<String>): ShowdownBattle {
|
||||
throw OperationNotSupportedException()
|
||||
}
|
||||
|
||||
override fun sendServerMessage(msg: String) {
|
||||
client.sendMessage(msg)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): Collection<String> {
|
||||
return scraper.getAbilityIds().map { it.asString }.toSet()
|
||||
}
|
||||
|
||||
override fun getMoves(): Collection<MoveTemplate> {
|
||||
val json = scraper.getMoves()
|
||||
return json.keySet().map {
|
||||
MoveTemplate.decodeJsonMove(json.get(it).asJsonObject, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemIds(): Collection<String> {
|
||||
return scraper.getItemIds().map { it.toString() }.toSet()
|
||||
}
|
||||
|
||||
override fun onServerStarted() {
|
||||
val pollSocketTask = ScheduledTask.Builder().interval(1/4F)
|
||||
.infiniteIterations()
|
||||
.execute { client.pollMessage() }
|
||||
.build()
|
||||
ServerTaskTracker.addTask(pollSocketTask)
|
||||
val interpretMessageTask = ScheduledTask.Builder().interval(1/4F)
|
||||
.infiniteIterations()
|
||||
.execute {
|
||||
var message: String? = null
|
||||
do {
|
||||
message = client.messageQueue.poll()
|
||||
if (message != null) interpreter.interpret(message)
|
||||
} while (message != null)
|
||||
}
|
||||
.build()
|
||||
ServerTaskTracker.addTask(interpretMessageTask)
|
||||
}
|
||||
|
||||
fun getLoginState(): ShowdownGameServerLoginState {
|
||||
return state.loginState
|
||||
}
|
||||
fun setLoginState(newState: ShowdownGameServerLoginState) {
|
||||
state.loginState = newState
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.remote
|
||||
|
||||
import com.cobblemon.mod.common.showdown.runner.graal.GraalLogger
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.HostAccess
|
||||
import org.graalvm.polyglot.PolyglotAccess
|
||||
import org.graalvm.polyglot.Source
|
||||
import java.net.Socket
|
||||
import java.net.URI
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.HttpRequest
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
/**
|
||||
* Allows us to load abilities and such using the pokemon showdown web client's data
|
||||
*/
|
||||
class ShowdownClientScraper(
|
||||
private val baseUrl: String
|
||||
) {
|
||||
private val context: Context by lazy { createContext() }
|
||||
private val httpClient = HttpClient.newHttpClient()
|
||||
private val gson = GsonBuilder().create()
|
||||
|
||||
fun createSocket(): Socket {
|
||||
val httpRequest = HttpRequest.newBuilder().uri(URI.create("$baseUrl/config/config.js")).GET().build()
|
||||
val httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString())
|
||||
val configurationJs = httpResponse.body()
|
||||
context.eval(Source.create("js", configurationJs))
|
||||
val serverHost = context.eval(Source.create("js","Config.defaultserver.host")).asString()
|
||||
val serverPort = context.eval(Source.create("js","Config.defaultserver.port")).asInt()
|
||||
return Socket(serverHost, serverPort)
|
||||
}
|
||||
|
||||
fun getAbilityIds(): JsonArray {
|
||||
val httpRequest = HttpRequest.newBuilder().uri(URI.create("$baseUrl/data/abilities.js")).GET().build()
|
||||
val httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString())
|
||||
val configurationJs = httpResponse.body()
|
||||
|
||||
context.eval(Source.create("js", configurationJs))
|
||||
val abilityIds = context.eval("js", "JSON.stringify(Object.keys(exports.BattleAbilities))").asString()
|
||||
return gson.fromJson(abilityIds, JsonArray::class.java)
|
||||
}
|
||||
|
||||
fun getMoves(): JsonObject {
|
||||
val httpRequest = HttpRequest.newBuilder().uri(URI.create("$baseUrl/data/moves.js")).GET().build()
|
||||
val httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString())
|
||||
val configurationJs = httpResponse.body()
|
||||
context.eval(Source.create("js", configurationJs))
|
||||
val moves = context.eval("js", "JSON.stringify(exports.BattleMovedex)").asString()
|
||||
return gson.fromJson(moves, JsonObject::class.java)
|
||||
}
|
||||
|
||||
fun getItemIds(): JsonArray {
|
||||
val httpRequest = HttpRequest.newBuilder().uri(URI.create("$baseUrl/data/items.js")).GET().build()
|
||||
val httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString())
|
||||
val configurationJs = httpResponse.body()
|
||||
context.eval(Source.create("js", configurationJs))
|
||||
val itemIds = context.eval("js", "JSON.stringify(Object.keys(exports.BattleItems))").asString()
|
||||
return gson.fromJson(itemIds, JsonArray::class.java)
|
||||
}
|
||||
|
||||
private fun createContext(): Context {
|
||||
val context = Context.newBuilder("js")
|
||||
.allowIO(false)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowPolyglotAccess(PolyglotAccess.ALL)
|
||||
.allowHostAccess(HostAccess.NONE)
|
||||
.allowCreateThread(false)
|
||||
.logHandler(GraalLogger)
|
||||
.allowHostClassLoading(false)
|
||||
.allowNativeAccess(false)
|
||||
.allowCreateProcess(false)
|
||||
.build()
|
||||
//This exports obj is set up as a global in the showdown client and referenced in some of the scripts we use later
|
||||
context.eval("js", "exports = {}")
|
||||
return context
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.remote
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import java.net.URI
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.WebSocket
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.ArrayDeque
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.CompletionStage
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Allows for connection to a Pokemon Showdown game server for battling against players using the game server
|
||||
*
|
||||
* @since March 1, 2025
|
||||
* @author Apion
|
||||
*/
|
||||
class ShowdownGameClient(val socketUri: String) {
|
||||
val messageQueue = ArrayDeque<String>()
|
||||
var socket: WebSocket? = null
|
||||
|
||||
fun connect() {
|
||||
val httpClient = HttpClient.newHttpClient()
|
||||
val wsBuilder = httpClient.newWebSocketBuilder()
|
||||
val future = wsBuilder.buildAsync(URI.create(socketUri), ClientListener(this))
|
||||
socket = future.get(30, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
fun close() {
|
||||
socket?.abort()
|
||||
}
|
||||
|
||||
fun sendMessage(msg: String) {
|
||||
socket?.sendText(msg, true)?.get()
|
||||
}
|
||||
|
||||
fun pollMessage() {
|
||||
socket?.request(1)
|
||||
}
|
||||
|
||||
private class ClientListener(val client: ShowdownGameClient) : WebSocket.Listener {
|
||||
override fun onOpen(webSocket: WebSocket?) {
|
||||
Cobblemon.LOGGER.info("Showdown websocket connected")
|
||||
}
|
||||
|
||||
override fun onClose(webSocket: WebSocket?, statusCode: Int, reason: String?): CompletionStage<*>? {
|
||||
Cobblemon.LOGGER.info("Showdown websocket closed")
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onText(webSocket: WebSocket?, data: CharSequence?, last: Boolean): CompletionStage<*>? {
|
||||
client.messageQueue.add(data.toString())
|
||||
if (!last) {
|
||||
webSocket?.request(1L)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onBinary(webSocket: WebSocket?, data: ByteBuffer?, last: Boolean): CompletionStage<*>? {
|
||||
Cobblemon.LOGGER.info("Received binary message from Showdown server")
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onError(webSocket: WebSocket?, error: Throwable?) {
|
||||
Cobblemon.LOGGER.error("Showdown websocket error", error)
|
||||
}
|
||||
|
||||
override fun onPing(webSocket: WebSocket?, message: ByteBuffer?): CompletionStage<*> {
|
||||
Cobblemon.LOGGER.info("Received showdown ping")
|
||||
return CompletableFuture.completedFuture(null)
|
||||
}
|
||||
|
||||
override fun onPong(webSocket: WebSocket?, message: ByteBuffer?): CompletionStage<*> {
|
||||
Cobblemon.LOGGER.info("Received showdown pong")
|
||||
return CompletableFuture.completedFuture(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.remote
|
||||
|
||||
/**
|
||||
* State that is tracked throughout the lifecycle of a connection to a showdown server
|
||||
*/
|
||||
data class ShowdownGameServerConnectionState(
|
||||
var loginState: ShowdownGameServerLoginState = ShowdownGameServerLoginState(),
|
||||
val battlesBeingCreated: MutableMap<String, RemoteShowdownBattle.Builder> = mutableMapOf()
|
||||
)
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package com.cobblemon.mod.common.showdown.runner.remote
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.showdown.ShowdownSearch
|
||||
import com.google.gson.GsonBuilder
|
||||
|
||||
/**
|
||||
* A variation of the [ShowdownInterpreter] that interprets messages from the showdown game server
|
||||
*/
|
||||
class ShowdownGameServerInterpreter(
|
||||
val serverConnectionState: ShowdownGameServerConnectionState
|
||||
) {
|
||||
val messageDispatchMap = mutableMapOf<String, (String) -> Unit>()
|
||||
//Maps a command name to a function that takes a room name and command
|
||||
val scopedMessageDispatchMap = mutableMapOf<String, (String, String) -> Unit>()
|
||||
//For slash commands sent in PMs
|
||||
val commandDispatchMap = mutableMapOf<String, (String, String) -> Unit>()
|
||||
|
||||
private val gson = GsonBuilder().create()
|
||||
|
||||
init {
|
||||
messageDispatchMap.put("formats", {})
|
||||
messageDispatchMap.put("challstr", ::handleChallStr)
|
||||
messageDispatchMap.put("pm", ::handlePrivateMessage)
|
||||
messageDispatchMap.put("updatesearch", ::handleUpdateSearch)
|
||||
|
||||
scopedMessageDispatchMap.put("request", ::handleRequest)
|
||||
scopedMessageDispatchMap.put("player", ::handlePlayer)
|
||||
scopedMessageDispatchMap.put("start", ::handleStart)
|
||||
scopedMessageDispatchMap.put("gametype", ::handleGameType)
|
||||
|
||||
commandDispatchMap.put("challenge", ::handleChallengeCommand)
|
||||
}
|
||||
|
||||
fun interpret(message: String) {
|
||||
Cobblemon.LOGGER.info(message)
|
||||
val lines = message.split('\n')
|
||||
val isLocal = lines[0].startsWith(">")
|
||||
val roomId = if(isLocal) lines[0].substring(1) else null
|
||||
val startIndex = if(isLocal) 1 else 0
|
||||
for (line in lines.slice(startIndex until lines.size)) {
|
||||
val args = line.split("|")
|
||||
if (args.size == 1) {
|
||||
return
|
||||
}
|
||||
val messageType = args[1]
|
||||
if (isLocal) {
|
||||
val handler = scopedMessageDispatchMap.get(messageType)
|
||||
if (handler == null) {
|
||||
Cobblemon.LOGGER.warn("Showdown server interpreter received an unknown message type for room: $messageType")
|
||||
}
|
||||
handler?.invoke(line, roomId!!)
|
||||
}
|
||||
else {
|
||||
val messageHandler = messageDispatchMap[messageType]
|
||||
if (messageHandler == null) {
|
||||
Cobblemon.LOGGER.warn("Showdown server interpreter received an unknown message type: $messageType")
|
||||
}
|
||||
messageHandler?.invoke(message)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun handleChallStr(message: String) {
|
||||
val args = message.split("|")
|
||||
val key = args[2]
|
||||
val str = args[3]
|
||||
val newLoginState = ShowdownGameServerLoginState(str, Integer.valueOf(key))
|
||||
serverConnectionState.loginState = newLoginState
|
||||
}
|
||||
|
||||
fun handlePrivateMessage(message: String) {
|
||||
val args = message.split("|")
|
||||
val sender = args[2].trim()
|
||||
val recipient = args[3].trim()
|
||||
val privateMessage = args[4]
|
||||
Cobblemon.LOGGER.info("Recieved PM from $sender: $privateMessage")
|
||||
if (privateMessage.startsWith("/")) {
|
||||
val commandParams = privateMessage.substring(1).split(" ")
|
||||
val commandHandler = commandDispatchMap[commandParams[0]]
|
||||
if (commandHandler == null) {
|
||||
Cobblemon.LOGGER.warn("Showdown server interpreter received an unknown command type: ${privateMessage.substring(1)}")
|
||||
}
|
||||
commandHandler?.invoke(message, sender)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleUpdateSearch(message: String) {
|
||||
val args = message.split("|")
|
||||
val searchObjJson = args[2]
|
||||
val searchObj = gson.fromJson(searchObjJson, ShowdownSearch::class.java)
|
||||
searchObj.searching.forEach {
|
||||
Cobblemon.LOGGER.info("Searching for $it")
|
||||
}
|
||||
searchObj.games?.forEach { key, value ->
|
||||
Cobblemon.LOGGER.info("Entered room $key")
|
||||
}
|
||||
}
|
||||
|
||||
fun handleRequest(message: String, roomId: String) {
|
||||
val params = message.split("|")
|
||||
if (params[2].isEmpty()) {
|
||||
serverConnectionState.battlesBeingCreated[roomId] = RemoteShowdownBattle.Builder(roomId)
|
||||
}
|
||||
else {
|
||||
serverConnectionState.battlesBeingCreated[roomId]?.teamStr = params[2]
|
||||
}
|
||||
}
|
||||
|
||||
fun handleStart(message: String, roomId: String) {
|
||||
println("Hit")
|
||||
}
|
||||
|
||||
fun handlePlayer(message: String, roomId: String) {
|
||||
val params = message.split("|")
|
||||
if (params[3].trim() == serverConnectionState.loginState.username) {
|
||||
val battleBuilder = serverConnectionState.battlesBeingCreated[roomId]!!
|
||||
battleBuilder.localSide = params[3]
|
||||
}
|
||||
}
|
||||
|
||||
fun handleGameType(message: String, roomId: String) {
|
||||
val params = message.split("|")
|
||||
val battleBuilder = serverConnectionState.battlesBeingCreated[roomId]!!
|
||||
battleBuilder.gameType = params[2]
|
||||
}
|
||||
|
||||
//Just auto accepts challenges
|
||||
fun handleChallengeCommand(command: String, sender: String) {
|
||||
val showdownServer = Cobblemon.showdownServer
|
||||
showdownServer.sendServerMessage(("|/utm null}"))
|
||||
showdownServer.sendServerMessage("|/accept ${sender.lowercase()}")
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user