Compare commits

..
510 changed files with 3089 additions and 6628 deletions
-11
View File
@@ -52,7 +52,6 @@
- Added a unique set of sounds for Ancient Poké Balls.
- Added a sound for using Exp Candy and Rare Candy items.
- Added raft platforms for non-swimming, non-flying Pokémon to stand on during battles that take place on the water's surface. (Flying Pokémon will fly over water in battle, and water breathing Pokémon will swim in water during battle.)
- Added `hiddenability=false` option to `/spawnpokemon` and `/pokemonedit` commands, allowing the Pokémon's ability to be reverted to a normal ability.
### Pokémon Added
#### Gen 1
@@ -397,9 +396,6 @@
- Fixed Alolan Exeggutor's tail not showing in the party UI. It will now also stand in front of all Pokémon in the party menu :)
- Fixed Timburr duplicating its log while fainting.
- Fixed hitbox sizes for Grotle and Torterra being set to default values.
- Fixed Hisuian Sneasel using Johtonian Sneasel's cry
- Fixed Trevenant t-posing after 8 seconds of sleep.
- Fixed Bewear t-posing for a bit if a wild one fainted.
- Fixed reviving items throwing an exception when used in battle.
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
@@ -430,7 +426,6 @@
- Prevent summary stats tab from making sounds when clicking on an already open tab.
- Fixed display case not being able to be fed items from underneath.
- Fixed Pokémon battling in water continuously sinking to the bottom.
- Fixed passive healing and wake from faint not disabling when their config values are set to 0.
### Developer
- `SpawnCause` is now an implementation of `SpawningInfluence`.
@@ -462,7 +457,6 @@
- Added HealingSource, an interface applied to all sources of healing from player actions, for easier tracking of healing sources.
- Move Change
- Shiny Chance Calculation Event (with player context)
- Dex Information Changed; this event is fired when the Pokédex is updated with new information.
- Rebuilt large swaths of the model animation code to simplify it.
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
- Renamed StatelessAnimation to PoseAnimation.
@@ -491,10 +485,6 @@
- Added support for conditional pose animations.
- Added a new universal locator called "top".
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
- Added missing `minYaw` configuration to the `q.look()` function for JSON posers.
- Some pose condition names have been changed:
- `isTouchingWaterOrRain` has been changed to `isInWaterOrRain`
- `isSubermegedInWater` has been changed to `isUnderWater`
### Localization
- Updated translations for:
@@ -1729,7 +1719,6 @@ Thank you so much to all of our community translators that bring the mod to the
- Lowered spawn rate of Tauros.
- Sableye now spawns near gem ores as well as amethyst.
- Added evolution stones and items to item tags
- Pokémon now play their cry animations when clicked on the starter selection screen.
### Fixes
- Fixed catch rates being entirely too difficult.
@@ -22,7 +22,7 @@ public abstract class MinecraftDedicatedServerMixin implements ServerInterface {
@Inject(method = "shouldDiscardEntity", at = @At(value = "HEAD"), cancellable = true)
public void cobblemon$allowPokemonSpawns(Entity entity, CallbackInfoReturnable<Boolean> callback) {
if (entity instanceof PokemonEntity) {
if(entity instanceof PokemonEntity) {
callback.setReturnValue(false);
}
}
@@ -81,7 +81,6 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.FlowHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
@@ -289,8 +288,6 @@ object Cobblemon {
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
FlowHandler.setup()
ifDedicatedServer {
isDedicatedServer = true
}
@@ -276,6 +276,81 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val CHISELED_BLACK_TUMBLESTONE_BRICKS = this.create("chiseled_black_tumblestone_bricks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_BUG_BLOCK = this.create("gem_bug_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_DARK_BLOCK = this.create("gem_dark_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_DRAGON_BLOCK = this.create("gem_dragon_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_ELECTRIC_BLOCK = this.create("gem_electric_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FAIRY_BLOCK = this.create("gem_fairy_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FIGHTING_BLOCK = this.create("gem_fighting_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FIRE_BLOCK = this.create("gem_fire_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FLYING_BLOCK = this.create("gem_flying_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_GHOST_BLOCK = this.create("gem_ghost_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_GRASS_BLOCK = this.create("gem_grass_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_GROUND_BLOCK = this.create("gem_ground_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_ICE_BLOCK = this.create("gem_ice_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_NORMAL_BLOCK = this.create("gem_normal_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_POISON_BLOCK = this.create("gem_poison_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_PSYCHIC_BLOCK = this.create("gem_psychic_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_ROCK_BLOCK = this.create("gem_rock_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_STEEL_BLOCK = this.create("gem_steel_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_WATER_BLOCK = this.create("gem_water_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_BUG_BUDDING = this.create("gem_bug_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_DARK_BUDDING = this.create("gem_dark_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_DRAGON_BUDDING = this.create("gem_dragon_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_ELECTRIC_BUDDING = this.create("gem_electric_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FAIRY_BUDDING = this.create("gem_fairy_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FIGHTING_BUDDING = this.create("gem_fighting_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FIRE_BUDDING = this.create("gem_fire_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_FLYING_BUDDING = this.create("gem_flying_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_GHOST_BUDDING = this.create("gem_ghost_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_GRASS_BUDDING = this.create("gem_grass_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_GROUND_BUDDING = this.create("gem_ground_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_ICE_BUDDING = this.create("gem_ice_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_NORMAL_BUDDING = this.create("gem_normal_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_POISON_BUDDING = this.create("gem_poison_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_PSYCHIC_BUDDING = this.create("gem_psychic_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_ROCK_BUDDING = this.create("gem_rock_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_STEEL_BUDDING = this.create("gem_steel_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val GEM_WATER_BUDDING = this.create("gem_water_budding", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val FIRE_STONE_BLOCK = this.create("fire_stone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.FIRE).strength(1.0F).sound(CobblemonSounds.EVOLUTION_STONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BIT).lightLevel{13}))
@JvmField
@@ -56,7 +56,6 @@ object CobblemonCommands {
AbandonMultiTeam.register(dispatcher)
FreezePokemonCommand.register(dispatcher)
ApplyPlayerTextureCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -1,80 +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
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import java.util.concurrent.ExecutionException
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
/**
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
* which can be Cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
*
* @author Hiroku
* @since February 24th, 2024
*/
object CobblemonFlows : DataRegistry {
override val id = cobblemonResource("flows")
override val observable = SimpleObservable<CobblemonFlows>()
override val type = PackType.SERVER_DATA
override fun sync(player: ServerPlayer) {
player.sendPacket(FlowRegistrySyncPacket(clientFlows.entries))
}
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
override fun reload(manager: ResourceManager) {
clientFlows.clear()
flows.clear()
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.openAsReader().use { stream ->
stream.buffered().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
if (identifier.path.startsWith("flows/client/")) {
clientFlows.getOrPut(ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)) { mutableListOf() }.add(expression)
} else {
flows.getOrPut(ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)) { mutableListOf() }.add(expression)
}
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
}
}
}
}
Cobblemon.LOGGER.info("Loaded ${CobblemonScripts.scripts.size} flows and ${CobblemonScripts.clientScripts.size} client flows")
observable.emit(this)
}
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>) {
flows[eventResourceLocation]?.forEach { it.resolve(runtime, context) }
}
}
@@ -171,6 +171,14 @@ interface CobblemonImplementation {
*/
fun server(): MinecraftServer?
/**
* Handles platform independent reloading of a [JsonDataRegistry].
*
* @param registry The [JsonDataRegistry] to reload.
* @param manager The [ResourceManager] to reload from.
*/
fun <T> reloadJsonRegistry(registry: JsonDataRegistry<T>, manager: ResourceManager): HashMap<ResourceLocation, T>
/**
* Registers an item to the [ComposterBlock].
*
@@ -54,9 +54,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
override val registry: Registry<Item> = BuiltInRegistries.ITEM
override val resourceKey: ResourceKey<Registry<Item>> = Registries.ITEM
@JvmField
val NPC_EDITOR = create("npc_editor", CobblemonItem(Item.Properties().stacksTo(1)))
@JvmField
val pokeBalls = mutableListOf<PokeBallItem>()
@JvmField
@@ -1227,6 +1224,80 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
@JvmField
val GEM_BUG_BLOCK = blockItem("gem_bug_block", CobblemonBlocks.GEM_BUG_BLOCK)
@JvmField
val GEM_DARK_BLOCK = blockItem("gem_dark_block", CobblemonBlocks.GEM_DARK_BLOCK)
@JvmField
val GEM_DRAGON_BLOCK = blockItem("gem_dragon_block", CobblemonBlocks.GEM_DRAGON_BLOCK)
@JvmField
val GEM_ELECTRIC_BLOCK = blockItem("gem_electric_block", CobblemonBlocks.GEM_ELECTRIC_BLOCK)
@JvmField
val GEM_FAIRY_BLOCK = blockItem("gem_fairy_block", CobblemonBlocks.GEM_FAIRY_BLOCK)
@JvmField
val GEM_FIGHTING_BLOCK = blockItem("gem_fighting_block", CobblemonBlocks.GEM_FIGHTING_BLOCK)
@JvmField
val GEM_FIRE_BLOCK = blockItem("gem_fire_block", CobblemonBlocks.GEM_FIRE_BLOCK)
@JvmField
val GEM_FLYING_BLOCK = blockItem("gem_flying_block", CobblemonBlocks.GEM_FLYING_BLOCK)
@JvmField
val GEM_GHOST_BLOCK = blockItem("gem_ghost_block", CobblemonBlocks.GEM_GHOST_BLOCK)
@JvmField
val GEM_GRASS_BLOCK = blockItem("gem_grass_block", CobblemonBlocks.GEM_GRASS_BLOCK)
@JvmField
val GEM_GROUND_BLOCK = blockItem("gem_ground_block", CobblemonBlocks.GEM_GROUND_BLOCK)
@JvmField
val GEM_ICE_BLOCK = blockItem("gem_ice_block", CobblemonBlocks.GEM_ICE_BLOCK)
@JvmField
val GEM_NORMAL_BLOCK = blockItem("gem_normal_block", CobblemonBlocks.GEM_NORMAL_BLOCK)
@JvmField
val GEM_POISON_BLOCK = blockItem("gem_poison_block", CobblemonBlocks.GEM_POISON_BLOCK)
@JvmField
val GEM_PSYCHIC_BLOCK = blockItem("gem_psychic_block", CobblemonBlocks.GEM_PSYCHIC_BLOCK)
@JvmField
val GEM_ROCK_BLOCK = blockItem("gem_rock_block", CobblemonBlocks.GEM_ROCK_BLOCK)
@JvmField
val GEM_STEEL_BLOCK = blockItem("gem_steel_block", CobblemonBlocks.GEM_STEEL_BLOCK)
@JvmField
val GEM_WATER_BLOCK = blockItem("gem_water_block", CobblemonBlocks.GEM_WATER_BLOCK)
@JvmField
val GEM_BUG_BUDDING = blockItem("gem_bug_budding", CobblemonBlocks.GEM_BUG_BUDDING)
@JvmField
val GEM_DARK_BUDDING = blockItem("gem_dark_budding", CobblemonBlocks.GEM_DARK_BUDDING)
@JvmField
val GEM_DRAGON_BUDDING = blockItem("gem_dragon_budding", CobblemonBlocks.GEM_DRAGON_BUDDING)
@JvmField
val GEM_ELECTRIC_BUDDING = blockItem("gem_electric_budding", CobblemonBlocks.GEM_ELECTRIC_BUDDING)
@JvmField
val GEM_FAIRY_BUDDING = blockItem("gem_fairy_budding", CobblemonBlocks.GEM_FAIRY_BUDDING)
@JvmField
val GEM_FIGHTING_BUDDING = blockItem("gem_fighting_budding", CobblemonBlocks.GEM_FIGHTING_BUDDING)
@JvmField
val GEM_FIRE_BUDDING = blockItem("gem_fire_budding", CobblemonBlocks.GEM_FIRE_BUDDING)
@JvmField
val GEM_FLYING_BUDDING = blockItem("gem_flying_budding", CobblemonBlocks.GEM_FLYING_BUDDING)
@JvmField
val GEM_GHOST_BUDDING = blockItem("gem_ghost_budding", CobblemonBlocks.GEM_GHOST_BUDDING)
@JvmField
val GEM_GRASS_BUDDING = blockItem("gem_grass_budding", CobblemonBlocks.GEM_GRASS_BUDDING)
@JvmField
val GEM_GROUND_BUDDING = blockItem("gem_ground_budding", CobblemonBlocks.GEM_GROUND_BUDDING)
@JvmField
val GEM_ICE_BUDDING = blockItem("gem_ice_budding", CobblemonBlocks.GEM_ICE_BUDDING)
@JvmField
val GEM_NORMAL_BUDDING = blockItem("gem_normal_budding", CobblemonBlocks.GEM_NORMAL_BUDDING)
@JvmField
val GEM_POISON_BUDDING = blockItem("gem_poison_budding", CobblemonBlocks.GEM_POISON_BUDDING)
@JvmField
val GEM_PSYCHIC_BUDDING = blockItem("gem_psychic_budding", CobblemonBlocks.GEM_PSYCHIC_BUDDING)
@JvmField
val GEM_ROCK_BUDDING = blockItem("gem_rock_budding", CobblemonBlocks.GEM_ROCK_BUDDING)
@JvmField
val GEM_STEEL_BUDDING = blockItem("gem_steel_budding", CobblemonBlocks.GEM_STEEL_BUDDING)
@JvmField
val GEM_WATER_BUDDING = blockItem("gem_water_budding", CobblemonBlocks.GEM_WATER_BUDDING)
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Properties()))
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Properties()))
@@ -54,7 +54,6 @@ import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
import com.cobblemon.mod.common.client.net.trade.*
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.*
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
@@ -75,7 +74,6 @@ import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonUnpasturedPacket
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
@@ -87,6 +85,7 @@ import com.cobblemon.mod.common.net.messages.client.spawn.SpawnNPCPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.storage.RemoveClientPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.SwapClientPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePartyPacket
@@ -95,13 +94,10 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemo
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.net.messages.client.trade.*
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.*
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
@@ -114,7 +110,6 @@ import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
@@ -130,10 +125,7 @@ import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonT
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
import com.cobblemon.mod.common.net.messages.server.trade.*
import com.cobblemon.mod.common.net.serverhandling.*
import com.cobblemon.mod.common.net.serverhandling.battle.*
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
@@ -147,8 +139,6 @@ import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
@@ -166,6 +156,21 @@ import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonTo
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.net.messages.server.battle.*
import com.cobblemon.mod.common.net.serverhandling.battle.*
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
import com.cobblemon.mod.common.net.messages.client.trade.*
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.trade.*
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.trade.*
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -303,7 +308,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -260,10 +260,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
@JvmField
val MOVE_PURSUIT_TARGET = this.create("move.pursuit.target")
@JvmField
val MOVE_PSYCHIC_TARGET = this.create("move.psychic.target")
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@@ -55,8 +55,6 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
val CAST_POKE_ROD = this.create("cast_poke_rod", SimpleCriterionTrigger(CastPokeRodCriterionCondition.CODEC))
val REEL_IN_POKEMON = this.create("reel_in_pokemon", SimpleCriterionTrigger(ReelInPokemonCriterionCondition.CODEC))
// Advancement criteria for [grow_tumblestone.json]
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
@@ -8,30 +8,26 @@
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.Optional
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
class CastPokeRodContext(val baitId: ResourceLocation)
class CastPokeRodCriterionCondition(
playerCtx: Optional<ContextAwarePredicate>,
val baitId: String
): SimpleCriterionCondition<CastPokeRodContext>(playerCtx) {
playerCtx: Optional<ContextAwarePredicate>,
val hasBait: Boolean
): SimpleCriterionCondition<Boolean>(playerCtx) {
companion object {
val CODEC: Codec<CastPokeRodCriterionCondition> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CastPokeRodCriterionCondition::playerCtx),
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter(CastPokeRodCriterionCondition::baitId)
).apply(it, { playerCtx, baitId -> CastPokeRodCriterionCondition(playerCtx, baitId.ifEmpty { "empty_bait" }) }) }
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CastPokeRodCriterionCondition::playerCtx),
PrimitiveCodec.BOOL.fieldOf("hasBait").forGetter(CastPokeRodCriterionCondition::hasBait)
).apply(it, ::CastPokeRodCriterionCondition) }
}
override fun matches(player: ServerPlayer, context: CastPokeRodContext): Boolean {
return (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId.equals("empty_bait"))
override fun matches(player: ServerPlayer, context: Boolean): Boolean {
return hasBait == context
}
}
}
@@ -1,39 +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.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.Optional
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
class ReelInPokemonContext(val pokemonId : ResourceLocation, val baitId: ResourceLocation)
class ReelInPokemonCriterionCondition(
playerCtx: Optional<ContextAwarePredicate>,
val pokemonId: String,
val baitId: String
) : SimpleCriterionCondition<ReelInPokemonContext>(playerCtx) {
companion object {
val CODEC: Codec<ReelInPokemonCriterionCondition> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter { it.playerCtx },
Codec.STRING.optionalFieldOf("pokemonId", "any").forGetter { it.pokemonId },
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter { it.baitId }
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId.ifEmpty { "empty_bait" }) }) }
}
override fun matches(player: ServerPlayer, context: ReelInPokemonContext): Boolean {
return (context.pokemonId == this.pokemonId.asIdentifierDefaultingNamespace() || this.pokemonId == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
}
}
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.entity.ai.LookInDirectionTask
import com.cobblemon.mod.common.util.asExpression
import net.minecraft.world.entity.LivingEntity
class LookInDirectionTaskConfig : SingleTaskConfig {
var yaw: Expression = "0".asExpression()
var pitch: Expression = "0".asExpression()
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
) = LookInDirectionTask(yaw, pitch)
}
@@ -172,15 +172,4 @@ The 'flee_attacker' type creates a task that will make the entity flee from its
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the nearest hostile. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9.
### run_script
The 'run_script' type creates a task that will run a MoLang script every tick on this entity. This has the risk of being very laggy
if the script is doing expensive things. Remember to use `q.entity.world.game_time` and `math.mod` to do intermittent actions inside
the script to avoid lagging the game.
- `script`: A resource location identifying the script to run. This should be in the `molang` datapack folder.
### look_in_direction
The 'look_in_direction' type creates a task that will make the entity look in a specific direction at all times where they aren't being told to look elsewhere.
- `yaw`: A MoLang expression that determines the yaw angle (degrees) of the entity. Defaults to `0`.
- `pitch`: A MoLang expression that determines the pitch angle (degrees) of the entity. Defaults to `0`.
- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9.
@@ -1,49 +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.ai.config.task
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
/**
* Runs a registered MoLang script (from the molang datapack folder) each tick.
*
* It's up to the user to not make expensive ass scripts. Remember you can access the game time
* from q.entity.level.game_time which can be used with a modulo operator to make something intermittent.
*
* @author Hiroku
* @since December 2nd, 2024
*/
class RunScript : SingleTaskConfig {
val script = cobblemonResource("dummy")
override fun createTask(
entity: LivingEntity,
brainConfigurationContext: BrainConfigurationContext
): BehaviorControl<in LivingEntity>? = BehaviorBuilder.create {
it.group(
it.registered(MemoryModuleType.LOOK_TARGET) // I think I need to have at least something here?
).apply(it) { _ ->
Trigger { world, entity, _ ->
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: entity.asMostSpecificMoLangValue())
return@Trigger CobblemonScripts.run(script, runtime) == DoubleValue.ONE
}
}
}
}
@@ -58,8 +58,6 @@ fun interface TaskConfig {
"calm_down" to CalmDownTaskConfig::class.java,
"flee_attacker" to FleeAttackerTaskConfig::class.java,
"flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java,
"run_script" to RunScript::class.java,
"look_in_direction" to LookInDirectionTaskConfig::class.java,
)
val runtime = MoLangRuntime().setup()
@@ -94,6 +94,7 @@ open class PokemonBattle(
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
.forEach { it.reset() }
}
actor.setupStruct()
}
}
@@ -11,7 +11,6 @@ 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.api.battles.model.PokemonBattle
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
@@ -37,7 +36,7 @@ abstract class BattleActor(
pokemonList.forEach { it.actor = this }
}
val struct = asMoLangValue()
val struct = QueryStruct(hashMapOf())
lateinit var showdownId: String
lateinit var battle: PokemonBattle
@@ -140,6 +139,10 @@ abstract class BattleActor(
battle.writeShowdownAction(">$showdownId ${showdownMessages.joinToString()}")
}
open fun setupStruct() {
struct.addFunction("uuid") { StringValue(uuid.toString()) }
}
abstract fun getName(): MutableComponent
/**
@@ -9,11 +9,8 @@
package com.cobblemon.mod.common.api.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager
@@ -45,24 +42,7 @@ interface JsonDataRegistry<T> : DataRegistry {
val resourcePath: String
override fun reload(manager: ResourceManager) {
val data = hashMapOf<ResourceLocation, T>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
}
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
try {
data[resolvedIdentifier] = gson.fromJson(reader, typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
}
}
reload(data)
this.reload(Cobblemon.implementation.reloadJsonRegistry(this, manager))
}
/**
@@ -21,13 +21,13 @@ import com.cobblemon.mod.common.util.adapters.DialogueTextAdapter
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.TranslatedTextAdapter
import com.cobblemon.mod.common.util.adapters.TextAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.PackType
/**
@@ -56,7 +56,7 @@ object Dialogues : JsonDataRegistry<Dialogue> {
.registerTypeAdapter(DialogueText::class.java, DialogueTextAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(MutableComponent::class.java, TranslatedTextAdapter)
.registerTypeAdapter(MutableComponent::class.java, TextAdapter)
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.also { gsonObservable.emit(it) }
.create()
@@ -192,8 +192,6 @@ object CobblemonEvents {
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
@JvmField
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
@JvmField
val POKEDEX_DATA_CHANGED = EventObservable<PokedexDataChangedEvent>()
// Fishing
@JvmField
@@ -19,8 +19,8 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
* @author Segfault Guy
* @since April 6th, 2023
*/
data class BattleFaintedEvent(
override val battle: PokemonBattle,
val killed: BattlePokemon,
val context: BattleContext
data class BattleFaintedEvent (
override val battle: PokemonBattle,
val killed: BattlePokemon,
val context: BattleContext
) : BattleEvent
@@ -18,6 +18,8 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
* @since March 25th 2023
*/
data class BattleFledEvent (
override val battle: PokemonBattle,
val player: PlayerBattleActor
) : BattleEvent
@@ -8,14 +8,8 @@
package com.cobblemon.mod.common.api.events.battles
import com.bedrockk.molang.runtime.value.DoubleValue
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
/**
* Event fired when a battle is won by some number of [BattleActor]s.
@@ -28,20 +22,4 @@ data class BattleVictoryEvent (
val winners : List<BattleActor>,
val losers : List<BattleActor>,
val wasWildCapture : Boolean
) : BattleEvent {
val context = mutableMapOf<String, MoValue>(
"battle" to battle.struct,
"winners" to winners.asArrayValue { it.struct },
"losers" to losers.asArrayValue { it.struct },
"was_wild_capture" to DoubleValue(wasWildCapture),
"player_winners" to winners.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"player_losers" to losers.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npc_winners" to winners.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"npc_losers" to losers.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon_winners" to winners.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"wild_pokemon_losers" to losers.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
)
}
) : BattleEvent
@@ -1,43 +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.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import com.mojang.datafixers.util.Either
import java.util.UUID
/**
* Event that fires when a Pokémon's information is gained or updated in the Pokédex.
*/
data class PokedexDataChangedEvent(
val dataSource: Either<PokedexEntityData, Pokemon>,
val knowledge: PokedexEntryProgress,
val playerUUID: UUID,
val record: FormDexRecord
) {
val pokedexManager: AbstractPokedexManager
get() = record.speciesDexRecord.pokedexManager
val context = mutableMapOf<String, MoValue>(
"player" to (playerUUID.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"pokemon" to dataSource.map({ DoubleValue.ZERO }, { it.struct }),
"data" to dataSource.map({ it.struct }, { DoubleValue.ZERO }),
"knowledge" to StringValue(knowledge.name.lowercase()),
"pokedex" to pokedexManager.struct
)
}
@@ -8,23 +8,12 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.core.registries.Registries
import net.minecraft.server.level.ServerPlayer
data class PokemonCapturedEvent (
val pokemon: Pokemon,
val player: ServerPlayer,
val pokeBallEntity: EmptyPokeBallEntity
) {
val context = mutableMapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"player" to player.asMoLangValue(),
"poke_ball" to pokeBallEntity.struct,
"item" to player.level().itemRegistry.wrapAsHolder(pokeBallEntity.pokeBall.item).asMoLangValue(Registries.ITEM)
)
}
)
@@ -14,4 +14,5 @@ import java.util.UUID
data class PokemonGainedEvent(
val playerId: UUID,
val pokemon: Pokemon
)
) {
}
@@ -9,9 +9,11 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import java.util.UUID
data class PokemonSeenEvent(
val playerId: UUID,
val pokemon: Pokemon
)
) {
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.gui
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import net.minecraft.client.gui.components.AbstractWidget
import net.minecraft.client.gui.components.Renderable
import net.minecraft.client.gui.components.events.GuiEventListener
@@ -23,9 +22,9 @@ abstract class ParentWidget(
pX: Int, pY: Int,
pWidth: Int, pHeight: Int,
component: Component
): CobblemonRenderable, AbstractWidget(pX, pY, pWidth, pHeight, component) {
): Renderable, AbstractWidget(pX, pY, pWidth, pHeight, component) {
val children: MutableList<GuiEventListener> = mutableListOf()
private val children: MutableList<GuiEventListener> = mutableListOf()
/**
* Adds Widget to the children list
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.item.ability
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.abilities.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -34,7 +33,7 @@ interface AbilityChanger<T : PotentialAbility> {
* @param pokemon The [Pokemon] being queried.
* @return A set containing all their legal abilities associated with [type], can be empty.
*/
fun queryPossible(pokemon: Pokemon): Set<Pair<AbilityTemplate, Priority>>
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
/**
* Attempts to update the ability of the given [pokemon].
@@ -22,20 +22,11 @@ import com.cobblemon.mod.common.CobblemonActivities
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonMemories
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.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
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.NPCProvider
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.api.pokedex.CaughtCount
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.PokedexManager
import com.cobblemon.mod.common.api.pokedex.SeenCount
import com.cobblemon.mod.common.api.pokedex.SeenPercent
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
@@ -46,24 +37,17 @@ import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import java.util.UUID
import kotlin.math.sqrt
import net.minecraft.commands.arguments.EntityAnchorArgument
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
@@ -75,23 +59,14 @@ import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.tags.TagKey
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.damagesource.DamageTypes
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LightningBolt
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.Level
import net.minecraft.world.level.Level.ExplosionInteraction
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.dimension.DimensionType
@@ -106,31 +81,6 @@ import net.minecraft.world.phys.Vec3
*/
object MoLangFunctions {
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>(
"print" to java.util.function.Function { params ->
val message = params.get<MoValue>(0).asString()
Cobblemon.LOGGER.info(message)
},
"set_query" to java.util.function.Function { params ->
val variable = params.getString(0)
val value = params.get<MoValue>(1)
params.environment.query.addFunction(variable) { value }
return@Function value
},
"replace" to java.util.function.Function { params ->
val text = params.getString(0)
val search = params.getString(1)
val replace = params.getString(2)
return@Function StringValue(text.replace(search, replace))
},
"is_blank" to java.util.function.Function { params ->
val arg = params.get<MoValue>(0)
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
},
"run_command" to java.util.function.Function { params ->
val command = params.getString(0)
val server = server() ?: return@Function DoubleValue.ZERO
server.commands.performPrefixedCommand(server.createCommandSourceStack(), command)
},
"is_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
@@ -165,93 +115,19 @@ object MoLangFunctions {
runtime.environment.variable = params.environment.variable
runtime.environment.context = params.environment.context
val script = params.getString(0).asIdentifierDefaultingNamespace()
CobblemonScripts.run(script, runtime) ?: DoubleValue.ZERO
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
},
"run_molang" to java.util.function.Function { params ->
val runtime = MoLangRuntime()
runtime.environment.query = params.environment.query
runtime.environment.variable = params.environment.variable
runtime.environment.context = params.environment.context
val expression = params.getString(0).asExpressionLike()
runtime.resolve(expression)
},
)
val biomeFunctions = mutableListOf<(Holder<Biome>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
val worldFunctions = mutableListOf<(Holder<Level>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ worldHolder ->
val world = worldHolder.value()
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("game_time") { _ -> DoubleValue(world.gameTime.toDouble()) }
map.put("time_of_day") {
val time = world.dayTime % 24000
return@put DoubleValue(time.toDouble())
}
map.put("server") { _ -> server()?.asMoLangValue() ?: DoubleValue.ZERO }
map.put("is_raining_at") { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
return@put DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
}
map.put("is_chunk_loaded_at") { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
return@put DoubleValue(world.isLoaded(BlockPos(x, y, z)))
}
map.put("is_thundering") { _ -> DoubleValue(world.isThundering) }
map.put("is_raining") { _ -> DoubleValue(world.isRaining) }
map.put("set_block") { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val block = world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
?: run {
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
return@put DoubleValue.ZERO
}
world.setBlock(BlockPos(x, y, z), block.defaultBlockState(), Block.UPDATE_ALL)
}
map.put("get_block") { params ->
val x = params.getInt(0)
val y = params.getInt(1)
val z = params.getInt(2)
val block = world.getBlockState(BlockPos(x, y, z)).block
return@put world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
}
map.put("spawn_explosion") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val range = params.getDouble(3).toFloat()
world.explode(null, x, y, z, range, ExplosionInteraction.valueOf(params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name))
}
map.put("spawn_lightning") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val lightning = LightningBolt(EntityType.LIGHTNING_BOLT, world)
lightning.setPos(x, y, z)
world.addFreshEntity(lightning)
return@put DoubleValue.ONE
}
map.put("get_entities_around") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val range = params.getDouble(3) * 2
val entities = world.getEntities(null, AABB.ofSize(Vec3(x, y, z), range, range, range))
return@put entities
.filterIsInstance<LivingEntity>()
.map { it.asMostSpecificMoLangValue() }
.asArrayValue()
}
return@mutableListOf map
"run_command" to java.util.function.Function { params ->
val command = params.getString(0)
server()?.let {
return@Function DoubleValue(it.commands.dispatcher.execute(command, it.createCommandSourceStack()))
} ?: return@Function DoubleValue.ZERO
}
)
val dimensionTypeFunctions = mutableListOf<(Holder<DimensionType>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
val blockFunctions = mutableListOf<(Holder<Block>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val dimensionTypeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val blockFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val playerFunctions = mutableListOf<(Player) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ player ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -265,7 +141,7 @@ object MoLangFunctions {
map.put("saturation_level") { _ -> DoubleValue(player.foodData.saturationLevel) }
map.put("tell") { params ->
val message = params.getString(0).text()
val overlay = params.getBooleanOrNull(1) == true
val overlay = params.getBooleanOrNull(1) ?: false
player.displayClientMessage(message, overlay)
}
map.put("teleport") { params ->
@@ -284,12 +160,7 @@ object MoLangFunctions {
environment.query = player.asMoLangValue()
environment
}
map.put("is_player") { DoubleValue.ONE }
if (player is ServerPlayer) {
map.put("run_command") { params ->
val command = params.getString(0)
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
}
map.put("is_party_at_full_health") { _ ->
DoubleValue(player.party().none(Pokemon::canBeHealed)) }
map.put("can_heal_at_healer") { params ->
@@ -317,46 +188,11 @@ object MoLangFunctions {
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
map.put("in_battle") { DoubleValue(player.isInBattle()) }
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
map.put("get_npc_data") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val data = Cobblemon.molangData.load(player.uuid)
if (data.map.containsKey(npcId)) {
return@put data.map[npcId]!!
} else {
val vars = VariableStruct()
data.map[npcId] = vars
return@put vars
}
}
map.put("get_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val variable = params.getString(1)
val data = Cobblemon.molangData.load(player.uuid)
if (data.map.containsKey(npcId)) {
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
} else {
return@put DoubleValue.ZERO
}
}
map.put("set_npc_variable") { params ->
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
val variable = params.getString(1)
val value = params.get<MoValue>(2)
val saveAfterwards = params.getBooleanOrNull(3) != false
val data = Cobblemon.molangData.load(player.uuid)
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
npcData.map[variable] = value
if (saveAfterwards) {
Cobblemon.molangData.save(player.uuid)
}
return@put DoubleValue.ONE
}
map.put("pokedex") { player.pokedex().struct }
}
map
}
)
val entityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val entityFunctions = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
@@ -365,28 +201,6 @@ object MoLangFunctions {
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
entity.hurt(source, amount.toFloat())
}
map.put("walk_to") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
if (entity is PathfinderMob) {
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
entity.brain.setMemory(MemoryModuleType.WALK_TARGET, WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1))
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(Vec3(x, y + entity.eyeHeight, z)))
} else {
entity.navigation.moveTo(x, y, z, speedMultiplier)
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
}
}
}
map.put("has_walk_target") { _ ->
if (entity is PathfinderMob) {
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
} else {
DoubleValue.ZERO
}
}
map.put("is_sneaking") { _ -> DoubleValue(entity.isShiftKeyDown) }
map.put("is_sprinting") { _ -> DoubleValue(entity.isSprinting) }
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
@@ -398,14 +212,8 @@ object MoLangFunctions {
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger) }
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger()) }
map.put("health") { _ -> DoubleValue(entity.health) }
map.put("distance_to_pos") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
return@put DoubleValue(sqrt(entity.distanceToSqr(Vec3(x, y, z))))
}
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
map.put("name") { _ -> StringValue(entity.effectiveName().string) }
map.put("yaw") { _ -> DoubleValue(entity.yRot.toDouble()) }
@@ -447,21 +255,6 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("get_nearby_entities") { params ->
val distance = params.getDouble(0)
val entities = entity.level().getEntities(entity, AABB.ofSize(entity.position(), distance, distance, distance))
return@put entities
.filterIsInstance<LivingEntity>()
.map { it.asMostSpecificMoLangValue() }
.asArrayValue()
}
if (entity is PosableEntity) {
map.put("play_animation") { params ->
val animation = params.getString(0)
val packet = PlayPosableAnimationPacket(entity.id, setOf(animation), emptyList())
packet.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
}
}
map
}
)
@@ -522,9 +315,9 @@ object MoLangFunctions {
}
}
return@put DoubleValue.ONE
return@put DoubleValue(1)
}
return@put DoubleValue.ZERO
return@put DoubleValue(0)
}
map.put("put_pokemon_in_healer") { params ->
val healer = npc.level().getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ZERO
@@ -543,8 +336,6 @@ object MoLangFunctions {
map.put("environment") { _ -> npc.runtime.environment }
map.put("party") { npc.party?.struct ?: DoubleValue.ZERO }
map.put("has_party") { DoubleValue(npc.party != null) }
map.put("is_npc") { DoubleValue.ONE }
map.put("can_battle") { DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
map
}
)
@@ -564,26 +355,10 @@ object MoLangFunctions {
return@put actor.struct
}
map.put("stop") { _ -> battle.stop() }
map.put("actors") { battle.actors.toList().asArrayValue { it.struct } }
map
}
)
val battleActorFunctions = mutableListOf<(BattleActor) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ battleActor ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_npc") { DoubleValue(battleActor.type == ActorType.NPC) }
map.put("is_player") { DoubleValue(battleActor.type == ActorType.PLAYER) }
map.put("is_wild") { DoubleValue(battleActor.type == ActorType.WILD) }
if (battleActor is NPCBattleActor) {
map.put("npc") { battleActor.entity.struct }
} else if (battleActor is PlayerBattleActor) {
map.put("player") { battleActor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
}
return@mutableListOf map
}
)
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ pokemon ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -608,7 +383,6 @@ object MoLangFunctions {
}
map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
map.put("is_shiny") { DoubleValue(pokemon.shiny) }
map.put("species") { pokemon.species.struct }
map.put("form") { StringValue(pokemon.form.name) }
map.put("weight") { DoubleValue(pokemon.species.weight.toDouble()) }
map.put("matches") { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
@@ -623,7 +397,6 @@ object MoLangFunctions {
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
map.put("is_pokemon") { DoubleValue.ONE }
map
}
)
@@ -762,140 +535,10 @@ object MoLangFunctions {
}
)
val serverFunctions: MutableList<(MinecraftServer) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ server ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("get_world") { params ->
val world = server.getLevel(
ResourceKey.create(
Registries.DIMENSION,
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
)
) ?: return@put DoubleValue.ZERO
return@put world
.registryAccess()
.registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(world)
.asWorldMoLangValue()
}
map.put("broadcast") { params ->
val message = params.getString(0)
server.playerList.broadcastSystemMessage(message.text(), params.getBooleanOrNull(1) == true)
return@put DoubleValue.ONE
}
map.put("get_player_by_uuid") { params ->
val uuid = UUID.fromString(params.getString(0))
val player = server.playerList.getPlayer(uuid) ?: return@put DoubleValue.ZERO
return@put player.asMoLangValue()
}
map.put("get_player_by_username") { params ->
val username = params.getString(0)
val player = server.playerList.getPlayerByName(username) ?: return@put DoubleValue.ZERO
return@put player.asMoLangValue()
}
map.put("data") { params ->
val data = Cobblemon.molangData.load(UUID(0L, 0L))
return@put data
}
map.put("save_data") {
Cobblemon.molangData.save(UUID(0L, 0L))
return@put DoubleValue.ONE
}
// Maybe later...
// map.put("stop") { params ->
// Cobblemon.LOGGER.warn("Server is being stopped from a MoLang script.")
// server.stopServer()
// }
return@mutableListOf map
}
)
val pokedexFunctions: MutableList<(AbstractPokedexManager) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ pokedex ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("get_species_record") { params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
pokedex.speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
}
map.put("has_seen") { params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
val formName = params.getStringOrNull(1)
if (formName == null) {
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.ENCOUNTERED.ordinal)
} else {
return@put DoubleValue((pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge?.ordinal ?: 0) >= PokedexEntryProgress.ENCOUNTERED.ordinal)
}
}
map.put("has_caught") { params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
val formName = params.getStringOrNull(1)
if (formName == null) {
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId) == PokedexEntryProgress.CAUGHT)
} else {
return@put DoubleValue(pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge == PokedexEntryProgress.CAUGHT)
}
}
map.put("caught_count") { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
map.put("seen_count") { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
map.put("caught_percent") { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
map.put("seen_percent") { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
if (pokedex is PokedexManager) {
map.put("player_id") { StringValue(pokedex.uuid.toString()) }
map.put("see") { params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
pokedex.encounter(pokemon)
return@put DoubleValue.ONE
}
map.put("catch") { params ->
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
pokedex.catch(pokemon)
return@put DoubleValue.ONE
}
}
map
}
)
val speciesFunctions: MutableList<(Species) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ species ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("identifier") { StringValue(species.resourceIdentifier.toString()) }
map.put("name") { StringValue(species.name) }
map.put("primary_type") { StringValue(species.primaryType.name) }
map.put("secondary_type") { StringValue(species.secondaryType?.name ?: "null") }
map.put("experience_group") { StringValue(species.experienceGroup.name) }
map.put("height") { DoubleValue(species.height) }
map.put("weight") { DoubleValue(species.weight) }
map.put("base_scale") { DoubleValue(species.baseScale) }
map.put("hitbox_width") { DoubleValue(species.hitbox.width) }
map.put("hitbox_height") { DoubleValue(species.hitbox.height) }
map.put("hitbox_fixed") { DoubleValue(species.hitbox.fixed) }
map.put("catch_rate") { DoubleValue(species.catchRate) }
map
}
)
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun MinecraftServer.asMoLangValue() = ObjectValue(this).addStandardFunctions().addFunctions(serverFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions)
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions)
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions)
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
fun Player.asMoLangValue(): ObjectValue<Player> {
val value = ObjectValue(
obj = this,
@@ -943,17 +586,6 @@ object MoLangFunctions {
return value
}
fun PokemonEntity.asMoLangValue(): ObjectValue<PokemonEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.pokemon.uuid.toString() }
)
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
value.addFunctions(pokemonFunctions.flatMap { it(this.pokemon).entries.map { it.key to it.value } }.toMap()) // Convenience
value.addFunctions(pokemonEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
val value = ObjectValue(
obj = this,
@@ -963,15 +595,6 @@ object MoLangFunctions {
return value
}
fun BattleActor.asMoLangValue(): ObjectValue<BattleActor> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
value.addFunctions(battleActorFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun SpawningContext.asMoLangValue(): ObjectValue<SpawningContext> {
val value = ObjectValue(
obj = this,
@@ -981,18 +604,6 @@ object MoLangFunctions {
return value
}
/**
* Different functions exist depending on
*/
fun LivingEntity.asMostSpecificMoLangValue(): ObjectValue<out LivingEntity> {
return when (this) {
is Player -> asMoLangValue()
is PokemonEntity -> struct
is NPCEntity -> struct
else -> ObjectValue(this).also { it.addStandardFunctions().addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) }
}
}
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
val value = ObjectValue(
obj = this,
@@ -1022,22 +633,6 @@ object MoLangFunctions {
return this
}
fun QueryStruct.addPokedexFunctions(pokedexManager: AbstractPokedexManager): QueryStruct {
val addedFunctions = pokedexFunctions
.flatMap { it.invoke(pokedexManager).entries }
.associate { it.key to it.value }
functions.putAll(addedFunctions)
return this
}
fun QueryStruct.addSpeciesFunctions(species: Species): QueryStruct {
val addedFunctions = speciesFunctions
.flatMap { it.invoke(species).entries }
.associate { it.key to it.value }
functions.putAll(addedFunctions)
return this
}
fun QueryStruct.addPokemonFunctions(pokemon: Pokemon): QueryStruct {
val addedFunctions = pokemonFunctions
.flatMap { it.invoke(pokemon).entries }
@@ -1,23 +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.moves
enum class MoveEffectiveness(val showdownId: Int, val multiplier: Double) {
NORMAL(0, 1.0),
WEAKNESS(1, 2.0),
RESISTANCE(2, 0.5),
IMMUNITY(3, 0.0);
companion object {
fun of(showdownId: Int): MoveEffectiveness {
return entries.find { it.showdownId == showdownId }
?: throw IllegalArgumentException("Unknown MoveEffectiveness: $showdownId")
}
}
}
@@ -1,48 +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.moves
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.runner.ShowdownService
object TypeChart {
private val movesEffectiveness = mutableMapOf<ElementalType, MutableMap<ElementalType, MoveEffectiveness>>()
fun load() {
movesEffectiveness.clear()
val typeChartJson = ShowdownService.service.getTypeChart()
typeChartJson.keySet().forEach { type ->
val defenderType = ElementalTypes.get(type) ?: return@forEach
val moveTypes = typeChartJson.get(type).asJsonObject.get("damageTaken").asJsonObject
moveTypes.keySet().forEach innerLoop@ {
val moveType = ElementalTypes.get(it) ?: return@innerLoop
val moveEffectiveness = moveTypes.get(it).asInt
val defenderMap = movesEffectiveness.getOrPut(moveType) { mutableMapOf() }
defenderMap[defenderType] = MoveEffectiveness.of(moveEffectiveness)
}
}
}
fun getMoveEffectiveness(moveType: ElementalType, defenderTypes: List<ElementalType?>): Double {
var multiplier = 1.0
defenderTypes.filterNotNull().forEach {
multiplier *= getMoveEffectiveness(moveType, it).multiplier
}
return multiplier
}
private fun getMoveEffectiveness(moveType: ElementalType, defenderType: ElementalType): MoveEffectiveness {
return movesEffectiveness[moveType]?.get(defenderType) ?: throw IllegalArgumentException("Unknown effectiveness type: $moveType $defenderType")
}
}
@@ -38,8 +38,7 @@ class NPCClass {
var resourceIdentifier: ResourceLocation = cobblemonResource("dummy")
var names: MutableList<Component> = mutableListOf()
var aspects: MutableSet<String> = mutableSetOf() // These only make sense when applied via presets
var hitbox = EntityDimensions.scalable(0.6F, 1.8F).withEyeHeight(1.62F)
var modelScale: Float = 0.94F
var hitbox = EntityDimensions.scalable(0.6F, 1.8F)
var battleConfiguration = NPCBattleConfiguration()
var interaction: NPCInteractConfiguration? = null
var canDespawn = true
@@ -49,7 +48,6 @@ class NPCClass {
var party: NPCPartyProvider? = null
var skill: Int = 0
var autoHealParty: Boolean = true
var randomizePartyOrder: Boolean = false
var battleTheme: ResourceLocation? = null
var ai: MutableList<BrainConfig> = mutableListOf()
var isMovable: Boolean = true
@@ -85,7 +83,6 @@ class NPCClass {
}
buffer.writeInt(skill)
buffer.writeBoolean(autoHealParty)
buffer.writeBoolean(randomizePartyOrder)
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
buffer.writeString(key)
buffer.writeString(value.asString())
@@ -130,7 +127,6 @@ class NPCClass {
}.toMutableList()
skill = buffer.readInt()
autoHealParty = buffer.readBoolean()
randomizePartyOrder = buffer.readBoolean()
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
val key = buffer.readString()
val value = buffer.readString()
@@ -70,7 +70,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
.registerTypeAdapter(NPCClass::class.java, NPCClassAdapter)
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
.registerTypeAdapter(Component::class.java, TextAdapter)
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
@@ -9,12 +9,10 @@
package com.cobblemon.mod.common.api.npc
import com.cobblemon.mod.common.api.npc.partyproviders.PoolPartyProvider
import com.cobblemon.mod.common.api.npc.partyproviders.ScriptPartyProvider
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.google.gson.JsonElement
import net.minecraft.server.level.ServerPlayer
/**
* A provider of a party for battling the NPC. Completely custom party providers will only display
@@ -27,14 +25,13 @@ interface NPCPartyProvider {
companion object {
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
SimplePartyProvider.TYPE to { SimplePartyProvider() },
PoolPartyProvider.TYPE to { PoolPartyProvider() },
ScriptPartyProvider.TYPE to { ScriptPartyProvider() }
PoolPartyProvider.TYPE to { PoolPartyProvider() }
)
}
val type: String
val isStatic: Boolean
fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer> = emptyList()): NPCPartyStore
fun provide(npc: NPCEntity, level: Int): NPCPartyStore
// Why did I opt for manual JSON loading??? I must have had a reason but I can't remember. Maybe for S2C? Use a codec doofus
fun loadFromJSON(json: JsonElement)
}
@@ -30,7 +30,6 @@ class NPCPreset {
var ai: List<BrainConfig>? = null
var skill: Int? = null
var autoHealParty: Boolean? = null
var randomizePartyOrder: Boolean? = null
var battleTheme: ResourceLocation? = null
var isMovable: Boolean? = null
var isInvulnerable: Boolean? = null
@@ -54,7 +53,6 @@ class NPCPreset {
hitbox?.let { npcClass.hitbox = it }
skill?.let { npcClass.skill = it }
autoHealParty?.let { npcClass.autoHealParty = it }
randomizePartyOrder?.let { npcClass.randomizePartyOrder = it }
battleTheme?.let { npcClass.battleTheme = it }
ai?.let { npcClass.ai.addAll(it) }
isMovable?.let { npcClass.isMovable = it }
@@ -67,7 +67,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
.registerTypeAdapter(NPCVariationProvider::class.java, NPCVariationProviderAdapter)
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
.registerTypeAdapter(Component::class.java, TextAdapter)
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
@@ -57,8 +57,8 @@ any number of presets and the properties of those presets will be merged into th
"variableName": "challenge_distance",
"displayName": "cobblemon.npc.challenge_distance",
"description": "cobblemon.npc.challenge_distance.description",
"type": "NUMBER",
"defaultValue": "5"
"type": "number",
"default": "5"
}
],
"names": [
@@ -77,7 +77,6 @@ any number of presets and the properties of those presets will be merged into th
"canDespawn": false,
"skill": 5,
"autoHealParty": true,
"randomizePartyOrder": true,
"party": {
"type": "simple",
"pokemon": [
@@ -179,19 +178,19 @@ An example situation of using this is if there is an NPC class preset that defin
challenging a player when they come within a certain range. Inside that class preset, the AI script may
need to know at what distance the player will be engaged. This can be defined in the NPC class preset as
a config variable, and therefore any NPC class that relies on this preset will have that config variable
and present it to users when they set up an NPC of that class.
and present it to users when they setup an NPC of that class.
"config": [
{
"variableName": "challenge_distance",
"displayName": "cobblemon.npc.challenge_distance.name",
"description": "cobblemon.npc.challenge_distance.desc",
"type": "NUMBER",
"defaultValue": "5"
"displayName": "cobblemon.npc.challenge_distance",
"description": "cobblemon.npc.challenge_distance.description",
"type": "number",
"default": "5"
}
]
The "type" of variable is one of: `STRING`, `NUMBER`, or `BOOLEAN`. The type is used to preset a more
The "type" of variable is one of: `string`, `number`, or `boolean`. The type is used to preset a more
appropriate input field for the user when they are setting up the NPC.
### names
@@ -283,20 +282,15 @@ is not mentioned in the Pokémon properties will be set to the seed level.
The "pool" party type is a way to define a party of Pokémon using a more complicated format that involves some
randomization and weighting to different Pokémon options.
A pool party provider can be static or dynamic depending on the value of `isStatic`. If `useFixedRandom` is true, the Pokémon it selects
from the pool will be constant for a single spawned NPC entity. This is slightly different to a static party because
the level of the Pokémon can still be modified dynamically using MoLang values inside each entry's `level` field.
The pool specifies the minimum and maximum number of Pokémon that can be in the party using `minPokemon` and `maxPokemon`.
The `pool` property dictates all the possible Pokémon with each being an object with several properties.
A pool party provider can be static or dynamic depending on the value of `isStatic`. It specifies the minimum
and maximum number of Pokémon that can be in the party using `minPokemon` and `maxPokemon`. The `pool` property dictates all of the possible Pokémon, with
each being an object with several properties.
- The `pokemon` property is the Pokémon properties as used in commands and spawn files that will generate the Pokémon.
- The `weight` property is a decimal number that represents how likely that Pokémon is to be chosen. If the weight is -1, it will be guaranteed to be selected. Defaults to 1. The larger the value, the more likely it will be chosen.
- The `selectableTimes` property is the maximum number of times that the entry can be selected in the party. Defaults to 1.
- The `npcLevels` property is a range of seed levels that the Pokémon can be generated at. For example, if the NPC is spawned with seed level 10, and the range is 5-9, the Pokémon will not be selected. The actual level of the Pokémon will be the seed level. Defaults to "1-100".
- The `levelVariation` property is the amount of variation the Pokémon's level can have from the NPC seed level. For example, a value of 2 will mean that for an NPC generated with seed level 10, the Pokémon will be generated somewhere between level 8 and 12 inclusive. Defaults to zero.
- The `level` property can be used instead of `levelVariation` to force a specific level. This is a MoLang expression.
For `minPokemon`, `maxPokemon`, `weight`, `selectableTimes`, and `levelVariation`, the values can be a simple number or be full MoLang expressions. At the time
of seeding an NPC, the cosmetic variations have already been applied. Therefore, it's possible to make these properties
@@ -304,16 +298,11 @@ depend on visual aspects of the NPC. For example, if the NPC has a variation for
certain Pokémon to be `"weight": "q.npc.has_aspect('dirty') ? -1 : 0"` which would make it a guaranteed party Pokémon for "dirty" NPCs
and impossible for all other variations of the NPC.
The MoLang environment used for the pool party provider contains q.level for the seed level, q.npc for the NPC, as well as q.players for
an array of all players that are challenging the NPC. The players list is only present for dynamic parties. If it is specifically
a single battle challenge and this is a dynamic party, there will also be a `q.player`, for convenience.
"party": {
"type": "pool",
"minPokemon": "3",
"maxPokemon": "6",
"isStatic": false,
"useFixedRandom": true,
"isStatic": true,
"pool": [
{
"pokemon": "weedle",
@@ -350,40 +339,11 @@ a single battle challenge and this is a dynamic party, there will also be a `q.p
"pokemon": "butterfree",
"weight": "5",
"npcLevels": "10-15",
"selectableTimes": "1",
"level": "q.player.party.highest_level"
"selectableTimes": "1"
}
]
}
#### script
The "script" party type is a way to define a party of Pokémon using a MoLang script. The script can modify a q.party struct,
primarily using function calls such as `q.party.add_by_properties('pikachu shiny)`.
There is an `isStatic` property to control whether the party is static or dynamic. There is a `script` property which is a
resource location pointing to the script that should be located in the `molang` datapack folder.
The MoLang environment used for the script party provider contains q.level for the seed level, q.npc for the NPC, as well as q.players for
an array of all players that are challenging the NPC. The players list is only present for dynamic parties. If it is specifically
a single battle challenge and this is a dynamic party, there will also be a `q.player`, for convenience.
"party": {
"type": "script",
"isStatic": true,
"script": "cobblemon:npc_party"
}
An example script might be:
t.use_butterfree = math.random_integer(1, 2) == 1;
t.use_butterfree ? {
q.party.add_by_properties('butterfree level=' + (q.level + 3));
} : {
q.party.add_by_properties('beedrill level=' + (q.level + 3));
};
q.party.add_by_properties('pidgey');
### autoHealParty
Whether or not the NPC's party will be healed between battles. If the NPC has a dynamic party, this option will
do nothing. If the NPC has a static party and this option is false, their Pokémon will not be healed and the
@@ -404,11 +364,6 @@ the NPC and move them using leads.
Controls whether the NPC can be hit by projectiles. Disabling this option prevents unwanted
interactions, such as players moving the NPC with fishing rods or other projectiles.
### randomizePartyOrder
Whether the NPC's party order will be randomized between battles. When true, this NPC's party (regardless of
whether it was dynamic or static) will choose its initial send-out Pokémon randomly instead of always choosing the first
Pokémon in the party.
### ai
The AI property is an array of brain configurations. This is used to configure the behaviours of the NPC. See
the documentation for brain configurations at [AI Configuration](../ai/config/README.md).
@@ -13,11 +13,6 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.readText
import com.cobblemon.mod.common.util.writeString
import com.cobblemon.mod.common.util.writeText
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
/**
@@ -35,38 +30,10 @@ class NPCConfigVariable(
val type: NPCVariableType = NPCVariableType.NUMBER,
val defaultValue: String = "0",
) {
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): NPCConfigVariable {
return NPCConfigVariable(
buffer.readString(),
buffer.readText(),
buffer.readText(),
buffer.readEnum(NPCVariableType::class.java),
buffer.readString()
)
}
}
enum class NPCVariableType {
NUMBER,
TEXT,
BOOLEAN;
fun toMoValue(value: String): MoValue {
return when (this) {
TEXT -> StringValue(value)
BOOLEAN -> DoubleValue(value.let { it == "1" || it.toBooleanStrictOrNull() == true })
else -> DoubleValue(value.toDouble())
}
}
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(variableName)
buffer.writeText(displayName)
buffer.writeText(description)
buffer.writeEnum(type)
buffer.writeString(defaultValue)
STRING,
BOOLEAN
}
fun apply(npc: NPCEntity, value: MoValue) {
@@ -74,6 +41,11 @@ class NPCConfigVariable(
}
fun applyDefault(npc: NPCEntity) {
npc.config.setDirectly(variableName, type.toMoValue(defaultValue))
val value = when (type) {
NPCVariableType.STRING -> StringValue(defaultValue)
NPCVariableType.BOOLEAN -> DoubleValue(defaultValue.toBoolean())
else -> DoubleValue(defaultValue.toDouble())
}
npc.config.set(variableName.split(".").iterator(), value)
}
}
@@ -10,14 +10,11 @@ package com.cobblemon.mod.common.api.npc.partyproviders
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.resolveInt
@@ -26,8 +23,6 @@ import com.cobblemon.mod.common.util.withQueryValue
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import kotlin.random.Random
import net.minecraft.server.level.ServerPlayer
/**
* A provider of a party for battling the NPC. It generates a party using a more complex process
@@ -51,7 +46,6 @@ class PoolPartyProvider : NPCPartyProvider {
override val type = TYPE
override var isStatic: Boolean = false
var useFixedRandom: Boolean = false
var minPokemon: Expression = "1".asExpression()
var maxPokemon: Expression = "6".asExpression()
var pool = mutableListOf<DynamicPokemon>()
@@ -65,7 +59,6 @@ class PoolPartyProvider : NPCPartyProvider {
return@map DynamicPokemon(
PokemonProperties.parse(it.asString),
"0".asExpression(),
null,
1..100,
"1".asExpression(),
"1".asExpression()
@@ -74,22 +67,19 @@ class PoolPartyProvider : NPCPartyProvider {
it as JsonObject
DynamicPokemon(
PokemonProperties.parse(it.get("pokemon").asString),
it.get("levelVariation")?.asString?.asExpression() ?: "0".asExpression(),
it.get("level")?.asString?.asExpression(),
it.get("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 1..100,
it.get("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
it.get("weight")?.asString?.asExpression() ?: "1".asExpression()
PokemonProperties.parse(it.getAsJsonPrimitive("pokemon").asString),
it.getAsJsonPrimitive("levelVariation")?.asString?.asExpression() ?: "0".asExpression(),
it.getAsJsonPrimitive("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 1..100,
it.getAsJsonPrimitive("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
it.getAsJsonPrimitive("weight")?.asString?.asExpression() ?: "1".asExpression()
)
}.toMutableList()
isStatic = json.get("isStatic").asBoolean
json.get("useFixedRandom")?.asBoolean?.let { useFixedRandom = it }
isStatic = json.getAsJsonPrimitive("isStatic").asBoolean
}
class DynamicPokemon(
val pokemon: PokemonProperties,
val levelVariation: Expression,
val level: Expression? = null,
val npcLevels: IntRange,
val selectableTimes: Expression,
val weight: Expression = "1".asExpression()
@@ -103,25 +93,18 @@ class PoolPartyProvider : NPCPartyProvider {
}
}
fun formulateParty(npc: NPCEntity, level: Int, players: List<ServerPlayer>, party: NPCPartyStore) {
fun formulateParty(npc: NPCEntity, level: Int, party: NPCPartyStore) {
val runtime = MoLangRuntime().setup().withQueryValue("npc", npc.struct)
val random = if (useFixedRandom) Random(npc.uuid.hashCode()) else Random.Default
runtime.withQueryValue("level", DoubleValue(level))
runtime.withQueryValue("players", players.asArrayValue { it.asMoLangValue() })
if (players.size == 1) {
// This is for the convenience, most cases will be pvn one player
runtime.withQueryValue("player", players.first().asMoLangValue())
}
val minPokemon = runtime.resolveInt(this.minPokemon)
val maxPokemon = runtime.resolveInt(this.maxPokemon)
var desiredPokemonCount = (minPokemon..maxPokemon).random(random)
var desiredPokemonCount = (minPokemon..maxPokemon).random()
val workingPool = pool.filter { level in it.npcLevels && runtime.resolveInt(it.selectableTimes) > 0 }.toMutableList()
val workingPool = pool.filter { level in it.npcLevels }.toMutableList()
val useCounts = mutableMapOf<DynamicPokemon, Int>()
while (desiredPokemonCount > 0 && workingPool.any { it.hasWeight(runtime) }) {
val selected = workingPool.filter { it.getWeight(runtime) == -1F }.randomOrNull(random)
?: workingPool.weightedSelection(random = random) { it.getWeight(runtime) }
while (desiredPokemonCount > 0 && workingPool.filter { it.hasWeight(runtime) }.isNotEmpty()) {
val selected = workingPool.filter { it.getWeight(runtime) == -1F }.randomOrNull()
?: workingPool.weightedSelection { it.getWeight(runtime) }
?: break
useCounts[selected] = useCounts.getOrDefault(selected, 0) + 1
val allowedSelections = runtime.resolveInt(selected.selectableTimes)
@@ -131,17 +114,16 @@ class PoolPartyProvider : NPCPartyProvider {
desiredPokemonCount--
// If the Pokémon's props specifies a level then use that, otherwise choose a random level within the range
val levelVariation = (0..runtime.resolveInt(selected.levelVariation)).random(random)
val levelVariation = (0..runtime.resolveInt(selected.levelVariation)).random()
val randomLevel = level + levelVariation
val dictatedLevel = selected.level?.let { runtime.resolveInt(it) }
val instance = selected.pokemon.copy().also { it.level = it.level ?: dictatedLevel ?: randomLevel }.create()
val instance = selected.pokemon.copy().also { it.level = it.level ?: randomLevel }.create()
party.add(instance)
}
}
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
override fun provide(npc: NPCEntity, level: Int): NPCPartyStore {
val party = NPCPartyStore(npc)
formulateParty(npc, level, players, party)
formulateParty(npc, level, party)
return party
}
}
@@ -1,64 +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.npc.partyproviders
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.toProperties
import com.cobblemon.mod.common.util.withQueryValue
import com.google.gson.JsonElement
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
class ScriptPartyProvider : NPCPartyProvider {
companion object {
const val TYPE = "script"
}
override val type = TYPE
override var isStatic: Boolean = true
var script: ResourceLocation = cobblemonResource("dummy")
override fun loadFromJSON(json: JsonElement) {
isStatic = json.asJsonObject.get("isStatic")?.asBoolean != false
script = json.asJsonObject.get("script").asString.asIdentifierDefaultingNamespace()
}
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
val runtime = MoLangRuntime().setup().withQueryValue("npc", npc.struct)
runtime.withQueryValue("level", DoubleValue(level))
runtime.withQueryValue("players", players.asArrayValue { it.asMoLangValue() })
if (players.size == 1) {
// This is for the convenience, most cases will be pvn one player
runtime.withQueryValue("player", players.first().asMoLangValue())
}
val npcParty = NPCPartyStore(npc)
runtime.withQueryValue("party", npcParty.struct)
if (!CobblemonScripts.scripts.containsKey(script)) {
Cobblemon.LOGGER.error("Script $script not found in script registry.")
npcParty.add("magikarp".toProperties().create())
return npcParty
}
CobblemonScripts.run(identifier = script, runtime = runtime)
return npcParty
}
}
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.toProperties
import com.google.gson.JsonElement
import net.minecraft.server.level.ServerPlayer
/**
* A basic party provider that just produces a [NPCPartyStore] based on a simple list of [PokemonProperties].
@@ -53,7 +52,7 @@ class SimplePartyProvider : NPCPartyProvider {
}
}
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
override fun provide(npc: NPCEntity, level: Int): NPCPartyStore {
val pokemon = pokemon.map { it.copy().also { it.level = it.level ?: level }.create() }
val party = NPCPartyStore(npc)
pokemon.forEach(party::add)
@@ -32,5 +32,5 @@ class WeightedNPCVariationProvider : NPCVariationProvider {
override val aspects: Set<String>
get() = options.flatMap(WeightedAspect::aspects).toSet()
override fun provideAspects(npcEntity: NPCEntity) = options.weightedSelection { it.weight }?.aspects ?: emptySet()
override fun provideAspects(npcEntity: NPCEntity) = options.weightedSelection(WeightedAspect::weight)?.aspects ?: emptySet()
}
@@ -82,8 +82,6 @@ object CobblemonPermissions {
val ABANDON_MULTITEAM = this.create("${COMMAND_PREFIX}abandonmultiteam", PermissionLevel.NONE)
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -8,13 +8,13 @@
package com.cobblemon.mod.common.api.pokedex
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokedexFunctions
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.resources.ResourceLocation
abstract class AbstractPokedexManager {
@@ -23,9 +23,11 @@ abstract class AbstractPokedexManager {
private val globalCalculatedValues = mutableMapOf<GlobalPokedexValueCalculator<*>, Any>()
@Transient
val struct = ObjectValue<AbstractPokedexManager>(this)
.addStandardFunctions()
.addPokedexFunctions(this)
val struct = QueryStruct(hashMapOf()).addStandardFunctions()
.addFunction("get_species_record") { params ->
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
}
fun deleteSpeciesRecord(speciesId: ResourceLocation) {
speciesRecords.remove(speciesId)
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.api.pokedex
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.PokedexDataChangedEvent
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -21,7 +19,6 @@ import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeEnumConstant
import com.cobblemon.mod.common.util.writeString
import com.google.common.collect.Sets
import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
@@ -126,10 +123,6 @@ class FormDexRecord {
}
speciesDexRecord.addInformation(pokemon, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
CobblemonEvents.POKEDEX_DATA_CHANGED.post(PokedexDataChangedEvent(Either.right(pokemon), knowledge, pokedexManager.uuid, this))
}
}
private fun addInformation(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress) {
@@ -140,10 +133,6 @@ class FormDexRecord {
}
speciesDexRecord.addInformation(pokedexEntityData, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
CobblemonEvents.POKEDEX_DATA_CHANGED.post(PokedexDataChangedEvent(Either.left(pokedexEntityData), knowledge, pokedexManager.uuid, this))
}
}
/** Returns whether the given [Pokemon] and [knowledge] would add new information to the Pokédex.*/
@@ -36,7 +36,7 @@ import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isDouble
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.isUuid
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.simplify
@@ -59,6 +59,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
@@ -186,10 +187,10 @@ open class PokemonProperties {
private fun parseIntProperty(keyPairs: MutableList<Pair<String, String?>>, labels: Iterable<String>): Int? {
val matchingKeyPair = getMatchedKeyPair(keyPairs, labels) ?: return null
val value = matchingKeyPair.second
return if (value == null || !value.isDouble()) {
return if (value == null || !value.isInt()) {
null
} else {
value.toDouble().toInt()
value.toInt()
}
}
@@ -789,7 +790,7 @@ open class PokemonProperties {
}
fun copy(): PokemonProperties {
return PokemonProperties().loadFromJSON(this.saveToJSON())
return PokemonProperties().loadFromJSON(saveToJSON())
}
// If the config value is at least 1, then do 1/x and use that as the property chance
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.api.drop.ItemDropMethod
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.TypeChart
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
@@ -126,7 +125,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
it.indicateSpeciesInitialized()
// Reload this with the mod
CobblemonHeldItemManager.load()
TypeChart.load()
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
this.observable.emit(this)
}
@@ -18,7 +18,6 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.ScriptRegistrySyncPacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.contextOrEmpty
import com.cobblemon.mod.common.util.endsWith
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@@ -65,6 +64,6 @@ object CobblemonScripts : DataRegistry {
}
fun run(identifier: ResourceLocation, runtime: MoLangRuntime): MoValue? {
return scripts[identifier]?.resolve(runtime, runtime.contextOrEmpty)
return scripts[identifier]?.resolve(runtime)
}
}
@@ -12,8 +12,6 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.world.item.Item
import net.minecraft.world.item.enchantment.EnchantmentHelper
import net.minecraft.world.item.enchantment.Enchantments
import net.minecraft.world.level.block.Block
/**
@@ -35,43 +33,9 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
return false
} else {
return true
}
if (minLureLevel != null) { // check for the lureLevel of the rod
val pokerodStack = ctx.rodStack
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(
ctx.enchantmentRegistry.getHolder(Enchantments.LURE).get(),
pokerodStack
)
if (lureLevel < minLureLevel!!) {
return false
} else if (maxLureLevel != null && lureLevel > maxLureLevel!!) {
return false
}
}
if (bait != null) { // check for the bait on the bobber
val pokerodBait = ctx.rodBait?.item
if (pokerodBait != bait) {
return false
}
}
if (rodType != null) { // check for the type of pokerod being used
val pokerodItem = ctx.rodItem
if (pokerodItem?.pokeRodId != rodType) {
return false
}
}
/*if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
val pokerodItem = (ctx as FishingSpawningContext).rodItem
// todo check if the EV yield of the berry matches the bait EV attract maybe?
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
return false
}*/
return true
}
companion object {
@@ -116,6 +116,24 @@ abstract class SpawningCondition<T : SpawningContext> {
}
) {
return false
} else if (minLureLevel != null && ctx is FishingSpawningContext) { // check for the lureLevel of the rod
val pokerodStack = (ctx as FishingSpawningContext).rodStack
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(ctx.enchantmentRegistry.getHolder(Enchantments.LURE).get(), pokerodStack)
if (lureLevel < minLureLevel!!) {
return false
} else if (maxLureLevel != null && lureLevel > maxLureLevel!!) {
return false
}
} else if (bait != null && ctx is FishingSpawningContext) { // check for the bait on the bobber
val pokerodBait = (ctx as FishingSpawningContext).rodBait?.item
if (pokerodBait != bait) {
return false
}
} else if (rodType != null && ctx is FishingSpawningContext) { // check for the type of pokerod being used
val pokerodItem = (ctx as FishingSpawningContext).rodItem
if (pokerodItem?.pokeRodId != rodType) {
return false
}
} else if (isSlimeChunk != null && isSlimeChunk != false) {
val isSlimeChunk = WorldgenRandom.seedSlimeChunk(ctx.position.x shr 4, ctx.position.z shr 4, ctx.world.seed, 987234911L).nextInt(10) == 0
@@ -134,6 +152,15 @@ abstract class SpawningCondition<T : SpawningContext> {
return random.nextInt(10) == 0*/
}
/*else if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
val pokerodItem = (ctx as FishingSpawningContext).rodItem
// todo check if the EV yield of the berry matches the bait EV attract maybe?
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
return false
}*/
return true
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
import com.cobblemon.mod.common.util.weightedSelection
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
/**
* A [SpawnAction] that will spawn a single [PokemonEntity].
@@ -269,7 +269,7 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
BattlePokemon.playerOwned(it)
}.also { if (healPokemon) it.effectedPokemon.heal() }
}.toMutableList()
if (leadingPokemon != null) {
if(leadingPokemon != null) {
Collections.rotate(result, result.size - this.indexOfFirst { it.uuid == leadingPokemon })
}
return result
@@ -102,32 +102,20 @@ open class PlayerPartyStore(
for (pokemon in this) {
// Awake from fainted
if (pokemon.isFainted()) {
//Skip awaken logic check if config value is 0
if (Cobblemon.config.faintAwakenHealthPercent > 0) {
pokemon.faintedTimer -= 1
if (pokemon.faintedTimer <= -1) {
val php = ceil(pokemon.maxHealth * Cobblemon.config.faintAwakenHealthPercent)
pokemon.currentHealth = php.toInt()
player.sendSystemMessage(
Component.translatable(
"cobblemon.party.faintRecover",
pokemon.getDisplayName()
)
)
}
pokemon.faintedTimer -= 1
if (pokemon.faintedTimer <= -1) {
val php = ceil(pokemon.maxHealth * Cobblemon.config.faintAwakenHealthPercent)
pokemon.currentHealth = php.toInt()
player.sendSystemMessage(Component.translatable("cobblemon.party.faintRecover", pokemon.getDisplayName()))
}
}
// Passive healing while less than full health
else if (pokemon.currentHealth < pokemon.maxHealth) {
//Skip passive healing logic check if config value is 0
if (Cobblemon.config.healPercent > 0) {
pokemon.healTimer--
if (pokemon.healTimer <= -1) {
pokemon.healTimer = Cobblemon.config.healTimer
val healAmount =
1.0.coerceAtLeast(pokemon.maxHealth.toDouble() * Cobblemon.config.healPercent)
pokemon.currentHealth = pokemon.currentHealth + round(healAmount).toInt()
}
pokemon.healTimer--
if (pokemon.healTimer <= -1) {
pokemon.healTimer = Cobblemon.config.healTimer
val healAmount = 1.0.coerceAtLeast(pokemon.maxHealth.toDouble() * Cobblemon.config.healPercent)
pokemon.currentHealth = pokemon.currentHealth + round(healAmount).toInt()
}
}
@@ -216,4 +204,4 @@ open class PlayerPartyStore(
super.set(position, pokemon)
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, this) }
}
}
}
@@ -100,10 +100,6 @@ object BattleBuilder {
)
}
if (actor.pokemonList.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)
}
@@ -182,10 +178,6 @@ object BattleBuilder {
)
}
if (actor.pokemonList.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)
}
@@ -256,7 +248,7 @@ object BattleBuilder {
val wildActor = PokemonBattleActor(pokemonEntity.pokemon.uuid, BattlePokemon(pokemonEntity.pokemon), fleeDistance)
val errors = ErroredBattleStart()
if (playerTeam.isNotEmpty() && playerTeam[0].health <= 0){
if(playerTeam.isNotEmpty() && playerTeam[0].health <= 0){
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
actorEntity = player,
requiredCount = battleFormat.battleType.slotsPerActor,
@@ -272,10 +264,6 @@ object BattleBuilder {
)
}
if (playerActor.pokemonList.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)
}
@@ -327,7 +315,7 @@ object BattleBuilder {
): BattleStartResult {
val playerTeam = party.toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemon = leadingPokemon)
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
val npcParty = npcEntity.getPartyForChallenge(listOf(player))
val npcParty = npcEntity.getPartyForChallenge(player)
val errors = ErroredBattleStart()
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
@@ -360,10 +348,6 @@ object BattleBuilder {
)
}
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
}
playerActor.battleTheme = npcEntity.getBattleTheme()
return if (errors.isEmpty) {
@@ -410,7 +394,7 @@ interface BattleStartError {
fun alreadyInBattle(pokemonEntity: PokemonEntity) = AlreadyInBattleError(pokemonEntity.uuid, pokemonEntity.effectiveName())
fun alreadyInBattle(actor: BattleActor) = AlreadyInBattleError(actor.uuid, actor.getName())
fun noParty(npcEntity: NPCEntity) = NoPartyError(npcEntity)
fun targetIsBusy(targetName: Component) = BusyError(targetName)
fun targetIsBusy(targetName: MutableComponent) = BusyError(targetName)
fun insufficientPokemon(
actorEntity: Entity,
requiredCount: Int,
@@ -490,7 +474,7 @@ class AlreadyInBattleError(
}
}
class BusyError(
val targetName: Component
val targetName: MutableComponent
): BattleStartError {
override fun getMessageFor(entity: Entity) = battleLang("errors.busy", targetName)
}
@@ -11,7 +11,6 @@ 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 com.google.gson.JsonObject
import java.util.UUID
/**
@@ -31,7 +30,6 @@ interface ShowdownService {
fun getAbilityIds(): JsonArray
fun getMoves(): JsonArray
fun getItemIds(): JsonArray
fun getTypeChart(): JsonObject
fun registerSpecies()
fun registerBagItems()
fun indicateSpeciesInitialized() {}
@@ -40,4 +38,4 @@ interface ShowdownService {
companion object {
val service: ShowdownService by lazy { GraalShowdownService() }
}
}
}
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import java.io.File
import java.io.IOException
import java.net.URI
@@ -153,12 +152,6 @@ class GraalShowdownService : ShowdownService {
return gson.fromJson(arrayResult, JsonArray::class.java)
}
override fun getTypeChart(): JsonObject {
val getCobbledItemIdsFn = context.getBindings("js").getMember("getTypeChart")
val arrayResult = getCobbledItemIdsFn.execute().asString()
return gson.fromJson(arrayResult, JsonObject::class.java)
}
override fun registerSpecies() {
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
val jsArray = this.context.eval("js", "new Array();")
@@ -18,7 +18,6 @@ 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 com.google.gson.JsonObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStreamWriter
@@ -124,13 +123,6 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
return gson.fromJson(response, JsonArray::class.java)
}
override fun getTypeChart(): JsonObject {
writer.write(">getTypeChart")
writer.flush()
val response = readMessage()
return gson.fromJson(response, JsonObject::class.java)
}
private fun sendSpeciesData(species: Species, form: FormData?) {
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
acknowledge()
@@ -157,7 +157,7 @@ class GildedChestBlock(settings: Properties, val type: Type = Type.RED) : BaseEn
// The yaw based on the block's facing direction
val yaw = facingToYaw[state.getValue(HorizontalDirectionalBlock.FACING)] ?: 0.0F
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, yaw)
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, facingToYaw[state.getValue(HorizontalDirectionalBlock.FACING)])
val offsetDir = state.getValue(HorizontalDirectionalBlock.FACING)
val vec = pos.toVec3d().add(offsetDir.stepX * 0.1 + 0.5, 0.0, offsetDir.stepZ * 0.1 + 0.5)
entity.moveTo(vec.x, vec.y, vec.z, yaw, entity.xRot)
@@ -125,7 +125,6 @@ object ClientMoLangFunctions {
val maxPitch = params.getDoubleOrNull(3) ?: 70F
val minPitch = params.getDoubleOrNull(4) ?: -45F
val maxYaw = params.getDoubleOrNull(5) ?: 45F
val minYaw = params.getDoubleOrNull(6) ?: -45F
ObjectValue(
SingleBoneLookAnimation(
bone = model.getPart(boneName),
@@ -133,8 +132,7 @@ object ClientMoLangFunctions {
yawMultiplier = yawMultiplier.toFloat(),
maxPitch = maxPitch.toFloat(),
minPitch = minPitch.toFloat(),
maxYaw = maxYaw.toFloat(),
minYaw = minYaw.toFloat()
maxYaw = maxYaw.toFloat()
)
)
}
@@ -1,13 +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.client.gui
import net.minecraft.client.gui.components.Renderable
interface CobblemonRenderable : Renderable
@@ -18,7 +18,7 @@ import net.minecraft.network.chat.Component
class ExitButton(
pX: Int, pY: Int,
onPress: OnPress
): Button(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Component.literal("Exit"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
): Button(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Component.literal("Exit"), onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 26F
@@ -31,7 +31,7 @@ abstract class ScrollingWidget<T : ObjectSelectionList.Entry<T>>(
height, // Height
top, // Top
slotHeight // Slot Height
), CobblemonRenderable {
) {
override fun renderListBackground(guiGraphics: GuiGraphics) {}
override fun renderSelection(context: GuiGraphics, y: Int, entryWidth: Int, entryHeight: Int, borderColor: Int, fillColor: Int) {}
override fun renderDecorations(context: GuiGraphics, mouseX: Int, mouseY: Int) {}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.battles.ShowdownActionResponse
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBattleActor
import com.cobblemon.mod.common.client.battle.SingleActionRequest
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleActionSelection
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleBackButton
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleGeneralActionSelection
@@ -30,7 +29,7 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
class BattleGUI : Screen(battleLang("gui.title")) {
companion object {
const val OPTION_VERTICAL_SPACING = 3
const val OPTION_HORIZONTAL_SPACING = 3
@@ -9,19 +9,18 @@
package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.GuiGraphics
class BattleBackButton(val x: Float, val y: Float) : CobblemonRenderable {
class BattleBackButton(val x: Float, val y: Float) {
companion object {
const val WIDTH = 58
const val HEIGHT = 34
const val SCALE = 0.5F
}
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(),
texture = cobblemonResource("textures/gui/battle/battle_back.png"),
@@ -11,11 +11,12 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.TypeChart
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.gold
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
import com.cobblemon.mod.common.client.battle.SingleActionRequest
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.TypeIcon
@@ -23,16 +24,12 @@ import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.toRGB
import com.google.common.collect.Lists
import net.fabricmc.loader.impl.util.StringUtil
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.util.Mth.floor
class BattleMoveSelection(
@@ -94,16 +91,16 @@ class BattleMoveSelection(
open val response: MoveActionResponse get() = MoveActionResponse(move.id, targetPnx)
open val selectable: Boolean get() = !move.disabled
val target: Targetable? get() = targetList?.let { targets ->
val targetPnx: String? get() = targetList?.let { targets ->
return@let when {
targets.isEmpty() -> null
targets.size == 1 -> targets[0]
targets.size == 1 -> targets[0].getPNX()
else -> null // TODO: multi-battles
}
}
val targetPnx: String? get() = target?.getPNX()
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val selectConditionOpacity = moveSelection.opacity * if (!selectable) 0.5F else 1F
blitk(
@@ -176,8 +173,6 @@ class BattleMoveSelection(
opacity = moveSelection.opacity,
centered = true
)
drawTooltip(context, mouseX, mouseY)
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX >= x && mouseX <= x + MOVE_WIDTH && mouseY >= y && mouseY <= y + MOVE_HEIGHT
@@ -187,41 +182,6 @@ class BattleMoveSelection(
moveSelection.playDownSound(Minecraft.getInstance().soundManager)
moveSelection.battleGUI.selectAction(moveSelection.request, response)
}
private fun drawTooltip(context: GuiGraphics, mouseX: Int, mouseY: Int) {
if (!isHovered(mouseX.toDouble(), mouseY.toDouble())) return
val textTooltip: MutableList<MutableComponent> = mutableListOf()
for (descriptionLine in StringUtil.wrapLines(moveTemplate.description.string, 80).split("\n")) {
textTooltip.add(Component.literal(descriptionLine).gray())
}
textTooltip.add(Component.literal(""))
if (moveTemplate.power > 0) textTooltip.add(lang("ui.power").append(String.format(": %d", moveTemplate.power.toInt())))
if (moveTemplate.accuracy > 0) textTooltip.add(lang("ui.accuracy").append(String.format(": %d%%", moveTemplate.accuracy.toInt())))
if (moveTemplate.power > 0 && moveTemplate.target != MoveTarget.self && target is ActiveClientBattlePokemon) {
val species = (target as ActiveClientBattlePokemon).battlePokemon?.species
if (species != null) {
val moveEffectiveness: Double = TypeChart.getMoveEffectiveness(elementalType, listOf(species.primaryType, species.secondaryType))
textTooltip.add(when {
moveEffectiveness == 0.0 -> lang("battle.ui.effectiveness.immunity", moveEffectiveness).gray()
moveEffectiveness < 1.0 -> lang("battle.ui.effectiveness.resistance", moveEffectiveness).gray()
moveEffectiveness > 1.0 -> lang("battle.ui.effectiveness.super_effective", moveEffectiveness).yellow()
else -> lang("battle.ui.effectiveness.normal", moveEffectiveness).white()
})
}
}
context.renderTooltip(
Minecraft.getInstance().font,
Lists.transform(textTooltip, Component::getVisualOrderText),
mouseX,
mouseY
)
}
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.battle.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.battle.ClientBattleMessageQueue
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
@@ -33,7 +32,7 @@ class BattleMessagePane(
FRAME_HEIGHT, // height
1, // top
LINE_HEIGHT
), CobblemonRenderable {
) {
var opacity = 1F
private var scrolling = false
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.battle.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.render.drawScaledText
import net.minecraft.client.gui.GuiGraphics
@@ -29,7 +28,7 @@ class BattleOptionTile(
val resource: ResourceLocation,
val text: MutableComponent,
val onClick: () -> Unit
) : CobblemonRenderable, GuiEventListener, NarratableEntry {
) : Renderable, GuiEventListener, NarratableEntry {
companion object {
const val OPTION_WIDTH = 90
const val OPTION_HEIGHT = 26
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueBox
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialoguePortraitWidget
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueNameWidget
@@ -37,7 +36,7 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()), CobblemonRenderable {
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()) {
val speakers = dialogueDTO.speakers?.mapNotNull { (key, value) ->
val name = value.name
when (val face = value.face) {
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.gui.dialogue.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
@@ -23,7 +22,7 @@ class DialogueNameWidget(
val width: Int,
val height: Int,
val text: MutableComponent?
) : CobblemonRenderable, GuiEventListener {
) : Renderable, GuiEventListener {
companion object {
val nameResource = cobblemonResource("textures/gui/dialogue/dialogue_name.png")
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.gui.dialogue.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
@@ -23,7 +22,7 @@ class DialoguePortraitWidget(
val y: Int,
val width: Int,
val height: Int
) : CobblemonRenderable, GuiEventListener {
) : Renderable, GuiEventListener {
companion object {
val DIALOGUE_ARROW_HEIGHT = 11
val DIALOGUE_ARROW_WIDTH = 6
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.dialogue.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawCenteredText
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
@@ -37,7 +36,7 @@ class DialogueTextInputWidget(
width,
height,
"gui_dialogue_text_input".text()
), CobblemonRenderable {
) {
init {
setMaxLength(maxLength)
isFocused = true
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.dialogue.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
@@ -23,7 +22,7 @@ class DialogueTimerWidget(
val y: Int,
val width: Int,
val height: Int,
) : CobblemonRenderable, GuiEventListener {
) : Renderable, GuiEventListener {
companion object {
val timerResource = cobblemonResource("textures/gui/dialogue/dialogue_timer_bar.png")
}
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.battle.ClientBattleChallenge
import com.cobblemon.mod.common.client.battle.ClientTeamRequest
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.server.*
@@ -36,7 +35,7 @@ class BattleConfigureGUI(
private val packet: PlayerInteractOptionsPacket,
private val activeRequest: ClientBattleChallenge? = null,
private val activeTeamRequest: ClientTeamRequest? = null
) : Screen(lang("ui.challenge.challenge_title")), CobblemonRenderable {
) : Screen(lang("ui.challenge.challenge_title")) {
companion object {
const val SIZE = 113
private val backgroundResource = cobblemonResource("textures/gui/interact/request/battle_request.png")
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.interact.battleRequest
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -26,7 +25,7 @@ class BattleRequestNavigationButton(
private val forwardIcon: ResourceLocation = forwardButtonResource,
private val backwardIcon: ResourceLocation = backwardsButtonResource,
onPress: OnPress
): Button(pX.toInt(), pY.toInt(), (WIDTH * SCALE).toInt(), (clickHeight * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
): Button(pX.toInt(), pY.toInt(), (WIDTH * SCALE).toInt(), (clickHeight * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
companion object {
const val HEIGHT = 16
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.interact.battleRequest
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -22,7 +21,7 @@ class BattleResponseButton(
pX: Int, pY: Int,
private val accept: Boolean,
onPress: OnPress
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 34F
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
@@ -25,7 +24,7 @@ class BattleRequestButton(
pX: Int, pY: Int,
var text: MutableComponent,
onPress: OnPress
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), text, onPress, DEFAULT_NARRATION), CobblemonRenderable {
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), text, onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 69F
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.callback.MoveSelectDTO
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
@@ -36,7 +35,7 @@ class MoveSelectConfiguration(
class MoveSelectGUI(
val config: MoveSelectConfiguration
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable {
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")) {
companion object {
const val WIDTH = 122
const val HEIGHT = 133
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.api.text.gold
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.render.drawScaledText
@@ -36,7 +35,7 @@ class MoveSlotButton(
val ppMax: Int,
val enabled: Boolean = true,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }) {
companion object {
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.callback.PartySelectPokemonDTO
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
@@ -38,7 +37,7 @@ class PartySelectConfiguration(
)
class PartySelectGUI(
val config: PartySelectConfiguration
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable {
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")) {
companion object {
const val WIDTH = 163
const val HEIGHT = 132
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.summary.widgets.PartySlotWidget
import com.cobblemon.mod.common.client.render.drawScaledText
@@ -43,7 +42,7 @@ class PartySlotButton(
val enabled: Boolean = true,
val parent: PartySelectGUI,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Pokemon"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Pokemon"), onPress, CreateNarration { "".text() }) {
companion object {
private val slotResource = cobblemonResource("textures/gui/interact/party_select_slot.png")
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.gui.interact.wheel
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -30,7 +29,7 @@ class InteractWheelButton(
private val colour: () -> Vector3f?,
onPress: OnPress,
private val canHover: (Double, Double) -> Boolean
) : Button(x, y, BUTTON_SIZE, BUTTON_SIZE, Component.literal("Interact"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
) : Button(x, y, BUTTON_SIZE, BUTTON_SIZE, Component.literal("Interact"), onPress, DEFAULT_NARRATION) {
companion object {
const val BUTTON_SIZE = 69
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.gui.interact.wheel
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.startselection.widgets.preview.ArrowButton
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.Multimap
@@ -21,7 +20,7 @@ import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import kotlin.math.max
class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelOption>, title: Component) : Screen(title), CobblemonRenderable {
class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelOption>, title: Component) : Screen(title) {
companion object {
const val SIZE = 140
const val OPTION_SIZE = 71
@@ -1,109 +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.client.gui.npc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.MutableComponent
class NPCEditorButton(
var buttonX: Float,
var buttonY: Float,
var label: MutableComponent,
var cycleButtonState: Boolean? = null,
var buttonWidth: Int = Minecraft.getInstance().font.width(label) + (BUTTON_PADDING * 2),
val silent: Boolean = false,
val alignRight: Boolean = false,
val clickAction: OnPress
): Button(if (alignRight) (buttonX - buttonWidth).toInt() else buttonX.toInt(), buttonY.toInt(), buttonWidth, HEIGHT, "".text(), clickAction, DEFAULT_NARRATION) {
companion object {
val HEIGHT = 16
val BORDER_WIDTH = 2
val BUTTON_PADDING = 6
val buttonResource = cobblemonResource("textures/gui/npc/button_base.png")
val buttonBorderResource = cobblemonResource("textures/gui/npc/button_border.png")
}
var isWidgetActive = false
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
val matrices = context.pose()
// Left border
blitk(
matrixStack = matrices,
texture = buttonBorderResource,
x = x,
y = y,
width = BORDER_WIDTH,
height = HEIGHT,
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble()) || isWidgetActive) HEIGHT else 0,
textureWidth = 3,
textureHeight = HEIGHT * 2,
)
// Base
blitk(
matrixStack = matrices,
texture = buttonResource,
x = x + BORDER_WIDTH,
y = y,
width = buttonWidth - (BORDER_WIDTH * 2),
height = HEIGHT,
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble()) || isWidgetActive) HEIGHT else 0,
textureHeight = HEIGHT * 2,
)
// Right border
blitk(
matrixStack = matrices,
texture = buttonBorderResource,
x = x + (buttonWidth - BORDER_WIDTH),
y = y,
width = BORDER_WIDTH,
height = HEIGHT,
uOffset = 1,
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble()) || isWidgetActive) HEIGHT else 0,
textureWidth = 3,
textureHeight = HEIGHT * 2,
)
var buttonLabel = if (cycleButtonState != null) "options.${if (cycleButtonState as Boolean) "on" else "off"}.composed".asTranslated(label) else label
drawScaledText(
context = context,
text = buttonLabel,
x = x + (buttonWidth / 2),
y = y + 4,
centered = true,
shadow = true
)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (active && isMouseOver(mouseX, mouseY)) {
super.mouseClicked(mouseX, mouseY, button)
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
if (active && !this.silent) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
}
}
@@ -9,35 +9,26 @@
package com.cobblemon.mod.common.client.gui.npc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawText
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.npc.widgets.ConfigVariableList
import com.cobblemon.mod.common.client.gui.npc.widgets.NPCRenderWidget
import com.cobblemon.mod.common.client.gui.npc.widgets.NPCRenderWidget.Companion.HEIGHT
import com.cobblemon.mod.common.client.gui.npc.widgets.NPCRenderWidget.Companion.WIDTH
import com.cobblemon.mod.common.client.gui.npc.widgets.SimpleNPCTextInputWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.net.messages.client.npc.dto.NPCConfigurationDTO
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.screens.Screen
import java.util.UUID
class NPCEditorScreen(
val npcId: Int,
val dto: NPCConfigurationDTO
) : Screen("gui.npc_editor.title".asTranslated()), CobblemonRenderable {
) : Screen("gui.npc_editor.title".asTranslated()) {
companion object {
const val BASE_WIDTH = 360
const val BASE_HEIGHT = 220
const val BASE_WIDTH = 331
const val BASE_HEIGHT = 161
val baseResource = cobblemonResource("textures/gui/npc/base.png")
private val baseResource = cobblemonResource("textures/gui/npc/editor_base.png")
}
val middleX: Int
@@ -52,52 +43,42 @@ class NPCEditorScreen(
override fun init() {
super.init()
addRenderableOnly(NPCRenderWidget(leftX + 12, topY + 35, dto.npcClass, dto.aspects))
addRenderableOnly(NPCRenderWidget(leftX + 6, topY + 32, dto.npcClass, dto.aspects))
addRenderableWidget(SimpleNPCTextInputWidget(
texture = cobblemonResource("textures/gui/npc/basic_text_input.png"),
getter = { dto.npcName.string },
setter = { dto.npcName = it.text() },
posX = leftX + 12,
posY = topY + 9,
width = 192,
height = 22,
maxLength = 32
x = leftX + 79,
y = topY + 14,
width = 130,
height = 22
))
addRenderableWidget(SimpleNPCTextInputWidget(
texture = cobblemonResource("textures/gui/npc/aspects_text_input.png"),
getter = { dto.aspects.joinToString() },
setter = {
dto.aspects.clear()
dto.aspects.addAll(it.split(",").map { it.trim() })
},
posX = leftX + 12,
posY = topY + 155,
width = 116,
x = leftX + 79,
y = topY + 36,
width = 130,
height = 40,
maxLength = 72,
maxLength = 75,
wrap = true
))
addRenderableWidget(
NPCEditorButton(
buttonX = leftX + 348F,
buttonY = topY + 201F,
label = lang("ui.generic.save"),
alignRight = true
) {
SaveNPCPacket(npcId, dto).sendToServer()
this.minecraft!!.setScreen(null)
}
Button
.builder("Save".text()) {
SaveNPCPacket(npcId, dto).sendToServer()
this.minecraft!!.setScreen(null)
}
.pos(leftX + BASE_WIDTH - 42, topY + BASE_HEIGHT - 18)
.size(40, 16)
.build()
)
addRenderableWidget(ConfigVariableList(leftX + 134, topY + 35, this))
}
override fun renderBlurredBackground(delta: Float) {}
override fun renderMenuBackground(context: GuiGraphics) {}
override fun isPauseScreen() = false
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(),
@@ -107,18 +88,6 @@ class NPCEditorScreen(
height = BASE_HEIGHT,
width = BASE_WIDTH
)
var extendedId = npcId.toString()
while (extendedId.length < 12) extendedId = "0$extendedId"
drawScaledTextJustifiedRight(
context = context,
text = extendedId.text(),
x = leftX + 348,
y = topY + 9,
shadow = true
)
super.render(context, mouseX, mouseY, delta)
}
}
@@ -1,217 +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.client.gui.npc.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.npc.NPCClasses
import com.cobblemon.mod.common.api.npc.configuration.NPCConfigVariable
import com.cobblemon.mod.common.api.npc.configuration.NPCConfigVariable.NPCVariableType
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.npc.NPCEditorButton
import com.cobblemon.mod.common.client.gui.npc.NPCEditorScreen
import com.cobblemon.mod.common.client.gui.npc.widgets.ConfigVariableList.ConfigVariable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.ContainerObjectSelectionList
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.narration.NarratableEntry
class ConfigVariableList(
val listX: Int,
var listY: Int,
val parent: NPCEditorScreen
) : ContainerObjectSelectionList<ConfigVariable>(
Minecraft.getInstance(),
WIDTH, // width
HEIGHT, // height
0, // top
SLOT_HEIGHT + SLOT_SPACING
), CobblemonRenderable {
companion object {
const val WIDTH = 211
const val HEIGHT = 160
const val SLOT_WIDTH = 196
const val SLOT_HEIGHT = 33
const val SLOT_SPACING = 0
val textInputBackground = cobblemonResource("textures/gui/npc/text_input_base.png")
}
private var scrolling = false
override fun getRowWidth() = SLOT_WIDTH
init {
listY += 4
this.x = listX
this.y = listY
correctSize()
setRenderHeader(false, 0)
val npcClass = NPCClasses.getByIdentifier(parent.dto.npcClass)
if (npcClass != null) {
npcClass.config.forEach { variable ->
val value = parent.dto.variables[variable.variableName] ?: variable.defaultValue
addEntry(ConfigVariable(variable, value, this))
}
}
}
override fun getScrollbarPosition() = x + width - 3
public override fun addEntry(entry: ConfigVariable) = super.addEntry(entry)
public override fun removeEntry(entry: ConfigVariable) = super.removeEntry(entry)
override fun renderListSeparators(guiGraphics: GuiGraphics) {}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
correctSize()
super.renderWidget(context, mouseX, mouseY, partialTicks)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
updateScrollingState(mouseX, mouseY)
if (scrolling) {
focused = getEntryAtPosition(mouseX, mouseY)
isDragging = true
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
if (scrolling) {
if (mouseY < this.listY) {
scrollAmount = 0.0
} else if (mouseY > bottom) {
scrollAmount = maxScroll.toDouble()
} else {
scrollAmount += deltaY
}
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
private fun updateScrollingState(mouseX: Double, mouseY: Double) {
scrolling = mouseX >= this.scrollbarPosition.toDouble()
&& mouseX < (this.scrollbarPosition + 3).toDouble()
&& mouseY >= listY
&& mouseY < bottom
}
override fun renderListBackground(context: GuiGraphics) {}
private fun correctSize() {
setRectangle(WIDTH, HEIGHT, listX, (listY - 4))
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
class ConfigVariable(val variable: NPCConfigVariable, val value: String, private val parent: ConfigVariableList) : Entry<ConfigVariable>() {
val client: Minecraft = Minecraft.getInstance()
var _focused = false
var children = mutableListOf<GuiEventListener>()
var textValue = value
var booleanValue = value.let { it.toDoubleOrNull() == 1.0 || it.toBooleanStrictOrNull() == true }
val editBox = EditBox(
client.font,
parent.listX,
parent.listY + 8,
SLOT_WIDTH - 8,
SLOT_HEIGHT - 8,
variable.displayName,
).also {
it.isBordered = false
it.tooltip = Tooltip.create(variable.description)
it.height = SLOT_HEIGHT - 16
it.setMaxLength(250)
it.value = value
it.setResponder {
textValue = it
parent.parent.dto.variables[variable.variableName] = it
}
if (variable.type == NPCVariableType.NUMBER) {
it.setFilter { value -> value.toDoubleOrNull() != null || value.isBlank() || value == "." || value == "-" }
}
}
val toggleButton = NPCEditorButton(
parent.listX.toFloat(),
parent.listY + 6F,
variable.displayName.copy(),
booleanValue,
SLOT_WIDTH
) {
booleanValue = !booleanValue
parent.parent.dto.variables[variable.variableName] = if (booleanValue) "1" else "0"
(it as NPCEditorButton).cycleButtonState = booleanValue
}.also {
it.tooltip = Tooltip.create(variable.description)
}
init {
if (variable.type == NPCVariableType.BOOLEAN) {
children.add(toggleButton) // children.add(cycleButton)
} else {
children.add(editBox)
}
}
override fun isFocused() = _focused
override fun setFocused(focused: Boolean) {
this._focused = focused
}
override fun children() = children
override fun narratables(): List<NarratableEntry> {
return children.filterIsInstance<NarratableEntry>()
}
override fun render(
context: GuiGraphics,
index: Int,
rowTop: Int,
rowLeft: Int,
rowWidth: Int,
rowHeight: Int,
mouseX: Int,
mouseY: Int,
isHovered: Boolean,
partialTicks: Float
) {
val x = rowLeft - 4
val y = rowTop
if (variable.type == NPCVariableType.BOOLEAN) {
toggleButton.x = x
toggleButton.y = y + 9
toggleButton.render(context, mouseX, mouseY, partialTicks)
} else {
drawScaledText(
context = context,
text = variable.displayName.visualOrderText,
x = x,
y = y + 4,
shadow = true
)
blitk(matrixStack = context.pose(), texture = textInputBackground, x = x, y = y + 16, height = 12, width = 1)
blitk(matrixStack = context.pose(), texture = textInputBackground, x = x + 1, y = y + 15, height = 14, width = SLOT_WIDTH - 2)
blitk(matrixStack = context.pose(), texture = textInputBackground, x = x + SLOT_WIDTH - 1, y = y + 16, height = 12, width = 1)
editBox.x = x + 4
editBox.y = y + 18
editBox.render(context, mouseX, mouseY, partialTicks)
}
}
}
}
@@ -8,10 +8,11 @@
package com.cobblemon.mod.common.client.gui.npc.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawProfile
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Renderable
import net.minecraft.client.gui.components.events.GuiEventListener
@@ -22,40 +23,41 @@ class NPCRenderWidget(
val y: Int,
var identifier: ResourceLocation,
val aspects: MutableSet<String>
) : CobblemonRenderable, GuiEventListener {
) : Renderable, GuiEventListener {
val state = FloatingState().also {
it.currentAspects = aspects
}
companion object {
const val WIDTH = 116
const val HEIGHT = 112
val profileBackgroundResource = cobblemonResource("textures/gui/npc/profile_background.png")
const val WIDTH = 66
const val HEIGHT = 66
}
override fun isFocused() = false
override fun setFocused(focused: Boolean) {}
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
context.enableScissor(
x,
y,
x + WIDTH,
y + HEIGHT
blitk(
matrixStack = context.pose(),
texture = profileBackgroundResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT
)
context.pose().pushPose()
// Decrease on Z-axis to prevent model from rendering above other components like tooltips
context.pose().translate(x + (WIDTH / 2F), y + HEIGHT + (HEIGHT / 4F), -500F)
context.pose().translate(x + WIDTH / 2F, y + HEIGHT / 2F, 0F)
drawProfile(
repository = NPCModelRepository,
resourceIdentifier = identifier,
matrixStack = context.pose(),
partialTicks = delta,
scale = 120F,
scale = 30F,
state = state
)
context.pose().popPose()
context.disableScissor()
}
}
@@ -9,142 +9,89 @@
package com.cobblemon.mod.common.client.gui.npc.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawCenteredText
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import net.minecraft.Util
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.RenderType
import net.minecraft.network.chat.Style
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.contents.PlainTextContents.LiteralContents
import net.minecraft.resources.ResourceLocation
import kotlin.math.floor
class SimpleNPCTextInputWidget(
getter: () -> String,
val texture: ResourceLocation? = null,
val texture: ResourceLocation,
private val setter: (String) -> Unit,
posX: Int,
posY: Int,
x: Int,
y: Int,
width: Int,
height: Int,
maxLength: Int = 100,
val shadow: Boolean = true,
val wrap: Boolean = false
) : EditBox(
Minecraft.getInstance().font,
posX + TEXT_PADDING,
posY,
x,
y,
width,
height,
"input".text()
), CobblemonRenderable {
companion object {
const val TEXT_PADDING = 4
}
var focusedTime: Long = 0
) {
init {
setMaxLength(maxLength)
isFocused = true
focusedTime = Util.getMillis()
value = getter()
setResponder { setter(it) }
setFocused(false)
isBordered = false
}
override fun setFocused(focused: Boolean) {
if (focused) focusedTime = Util.getMillis()
super.setFocused(focused)
this.setResponder { setter(it) }
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
val clickedState = super.mouseClicked(mouseX, mouseY, button)
if (wrap) {
val splitLines = Minecraft.getInstance().font.splitter.splitLines(value, width - 8, Style.EMPTY)
val clickedLineIndex = floor((mouseY - y)/11).toInt()
if (clickedLineIndex >= 0 && splitLines.size > clickedLineIndex) {
val lineAtIndex = splitLines.get(clickedLineIndex)
var lineSubstring = ""
for (char in lineAtIndex.string) {
lineSubstring += char
val startToLineWidth = Minecraft.getInstance().font.width(lineSubstring.text())
if ((mouseX - x) <= startToLineWidth) break
}
var unwrappedLine = ""
splitLines.forEachIndexed { index, line ->
if (index == clickedLineIndex) {
unwrappedLine += lineSubstring
} else if (index < clickedLineIndex) {
unwrappedLine += line.string
}
}
this.moveCursorTo(maxOf(0, unwrappedLine.length - 1), Screen.hasShiftDown())
}
return if (mouseX.toInt() in x..(x + width) && mouseY.toInt() in y..(y + height)) {
isFocused = true
true
} else {
false
}
return clickedState
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (texture != null) {
blitk(
matrixStack = context.pose(),
x = x,
y = y,
width = width,
height = height,
texture = texture
)
if (cursorPosition != value.length) {
moveCursorToEnd(false)
}
blitk(
matrixStack = context.pose(),
x = x,
y = y,
width = width,
height = height,
texture = texture
)
context.pose().pushPose()
val scale = 0.8F
context.pose().scale(scale, scale, 1F)
if (wrap) {
val showCursor = isFocused && ((Util.getMillis() - this.focusedTime) / 300L % 2L == 0L)
val input = (if (isFocused) "${value}${if ((cursorPosition == value.length) && showCursor) "_" else ""}" else value)
var cursorLine = ""
var cursorLineIndex = -1
var cursorLinePos = 0
var accumlatedLength = 0
val wrappedLines = Minecraft.getInstance().font.splitter.splitLines(input, width - (TEXT_PADDING * 2), Style.EMPTY)
val wrappedLines = Minecraft.getInstance().font.split(MutableComponent.create(LiteralContents(value)), ((width - 8) / scale).toInt())
for ((index, line) in wrappedLines.withIndex()) {
accumlatedLength += line.string.length
if (cursorLineIndex < 0 && accumlatedLength > cursorPosition) {
cursorLineIndex = index
cursorLine = line.string
cursorLinePos = if (cursorLineIndex > 0) cursorPosition - (accumlatedLength - line.string.length) else cursorPosition
}
drawScaledText(
context = context,
text = line.string.text(),
x = x,
y = y + index * 10 + 1,
colour = 0xDDDDDD,
shadow = true
)
}
if (showCursor && !value.isEmpty() && cursorPosition != value.length) {
val startToCursorWidth = Minecraft.getInstance().font.width((cursorLine.substring(0, cursorLinePos).text()))
context.fill(
RenderType.guiTextHighlight(),
x + startToCursorWidth - 1,
y + cursorLineIndex * 10 + 1,
x + startToCursorWidth,
y + (cursorLineIndex * 10 + 1) + 9,
-3092272
context.drawString(
Minecraft.getInstance().font,
line,
((x + 4) / scale).toInt(),
((y + index * 10 + 5) / scale).toInt(),
0xFFFFFF,
false
)
}
} else {
super.renderWidget(context, mouseX, mouseY, delta)
drawCenteredText(
context = context,
text = value.text(),
x = (x + width / 2F) / scale,
y = (y + height / 2 - 4) / scale,
shadow = true,
colour = 0xFFFFFF
)
}
context.pose().popPose()
}
}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.pc.StorageSlot
import com.cobblemon.mod.common.client.gui.summary.widgets.PartySlotWidget
@@ -41,7 +40,7 @@ class PasturePokemonScrollList(
HEIGHT, // height
0, // top
SLOT_HEIGHT + SLOT_SPACING
), CobblemonRenderable {
) {
companion object {
const val WIDTH = 70
const val HEIGHT = 120
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.gui.pasture
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -19,7 +18,7 @@ import net.minecraft.network.chat.Component
class PastureSlotIconButton(
var xPos: Int, var yPos: Int,
onPress: OnPress
) : Button(xPos, yPos, (SIZE * SCALE).toInt(), (SIZE * SCALE).toInt(), Component.literal("Pasture Move"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
) : Button(xPos, yPos, (SIZE * SCALE).toInt(), (SIZE * SCALE).toInt(), Component.literal("Pasture Move"), onPress, DEFAULT_NARRATION) {
companion object {
const val SIZE = 14
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pasture
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
@@ -23,7 +22,7 @@ import net.minecraft.network.chat.Component
class RecallButton(
x: Int, y: Int,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Retrieve"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Retrieve"), onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 70
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -22,7 +21,7 @@ class NavigationButton(
pX: Int, pY: Int,
private val forward: Boolean,
onPress: OnPress
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 8F
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.Summary
@@ -41,7 +40,7 @@ class PCGUI(
val pc: ClientPC,
val party: ClientParty,
val configuration: PCGUIConfiguration
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable {
) : Screen(Component.translatable("cobblemon.ui.pc.title")) {
companion object {
const val BASE_WIDTH = 349
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
@@ -24,7 +23,7 @@ class ReleaseButton(
x: Int, y: Int,
private val parent: StorageWidget,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Release"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Release"), onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 58
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
@@ -25,7 +24,7 @@ class ReleaseConfirmButton(
private val parent: StorageWidget,
private val subKey: String,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("ReleaseConfirm"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
) : Button(x, y, WIDTH, HEIGHT, Component.literal("ReleaseConfirm"), onPress, DEFAULT_NARRATION) {
companion object {
private const val WIDTH = 30
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
import com.cobblemon.mod.common.client.render.drawScaledText
@@ -32,7 +31,7 @@ open class StorageSlot(
x: Int, y: Int,
private val parent: StorageWidget,
onPress: OnPress
) : Button(x, y, SIZE, SIZE, Component.literal("StorageSlot"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
) : Button(x, y, SIZE, SIZE, Component.literal("StorageSlot"), onPress, DEFAULT_NARRATION) {
val state = FloatingState()
companion object {
@@ -29,7 +29,6 @@ import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HEADER_BAR_HEIGHT
@@ -76,7 +75,7 @@ class PokedexGUI private constructor(
val type: PokedexType,
val initSpecies: ResourceLocation?,
val blockPos: BlockPos?
): Screen(Component.translatable("cobblemon.ui.pokedex.title")), CobblemonRenderable {
): Screen(Component.translatable("cobblemon.ui.pokedex.title")) {
companion object {
private val screenBackground = cobblemonResource("textures/gui/pokedex/pokedex_screen.png")
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pokedex
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.narration.NarrationElementOutput
@@ -28,7 +27,7 @@ class ScaledButton(
val scale: Float = 0.5F,
val silent: Boolean = false,
val clickAction: Button.OnPress
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION) {
var isWidgetActive = false

Some files were not shown because too many files have changed in this diff Show More