Compare commits

..
Author SHA1 Message Date
JazzMcNade 6ba2804a46 Added type checking 2024-05-14 00:46:40 -05:00
JazzMcNade d13d5183c8 Implemented missing functions. 2024-05-14 00:24:09 -05:00
755 changed files with 807 additions and 13638 deletions
-1
View File
@@ -36,7 +36,6 @@ common/src/main/resources/assets/cobblemon/showdown
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
common/.fabric
# MacOS
.DS_Store
+2 -32
View File
@@ -1,41 +1,11 @@
# Changelog
## [1.6.0 - The Bass Pro Fishing Update (Month Xth, 2024)](#1-6-0)
#### "Now that there's a fishing mechanic, the mod is actually good!"
### Additions
- Added Pokémon (and item) fishing using modified fishing rods - Poké Rods! You'll need a Poké Rod smithing template, a fishing rod, and some type of Poké Ball. Each Poké Ball makes a differently themed rod. Why not?
- Added Lure Ball functionality, increasing the catch rate of Pokémon that were caught on a fishing rod.
- Added property chaining support for duplicate CustomPokemonPropertyType elements.
- Added aspect and unaspect PokemonProperty arguments (which also includes commands such as /pokemonedit, /spawnpokemon, and /givepokemon) to allow forcing or un-forcing an aspect on a Pokémon.
### Developer
- SpawnCause is now an implementation of SpawningInfluence.
- Added "forcedAspects" to Pokemon to make it easier to easily add basic aspects to a Pokémon in a way that persists. We already know that everyone is going to overuse this.
- Made the Pokemon.aspects setter private. This could technically break sidemods but if you are affected by this then you were using it wrong! Use Pokemon.forcedAspects to fix it.
## [1.5.1](#1-5-1)
### Additions
- Added unique send out particles for Cherish, Dream, Beast, and Ancient Origin balls.
### Changes
- Sounds for Relic Coin Sacks have been correctly renamed. Relic Coin Pouches received new sounds for breaking and placing.
### Fixes
- Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
- Fixed a niche issue where some properties of entities were not initialized correctly, causing Pokémon that appeared to be level 1 until you battle them.
- Fixed Fossilized Drake being missing from the Fossils item tag.
- Fixed PCStore#resize not allowing PC boxes size reduction.
-Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
### Developer
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. More information this way.
### Data Pack & Resource Pack Creators
- Added support for MoLang conditions for quirks and poses.
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual.
## [1.5.0 - The Ruins and Revival Update (May 12th, 2024)](#1-5-0)
## [1.5.0 - The Ruins and Revival Update (May Xth, 2024)](#1-5-0)
#### "You're telling me that Mojang has added archaeology to the game? Hmm... that gives me an idea."
### Additions
@@ -1,24 +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.cobblemon.mod.common.entity.npc.NPCEntity;
import net.minecraft.entity.ai.brain.Brain;
import net.minecraft.entity.ai.brain.MemoryModuleType;
import net.minecraft.entity.ai.brain.sensor.Sensor;
import net.minecraft.entity.ai.brain.sensor.SensorType;
import java.util.Collection;
public class GenericsCheatClass {
// For some reason, doing this in the NPCEntity class in Kotlin gives confusing generic errors I could not resolve.
public static Brain.Profile<NPCEntity> createNPCBrain(Collection<MemoryModuleType<?>> memories, Collection<SensorType<? extends Sensor<? super NPCEntity>>> sensors) {
return Brain.createProfile(memories, sensors);
}
}
@@ -27,7 +27,6 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.net.serializers.IdentifierDataSerializer
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.UUIDSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
import com.cobblemon.mod.common.api.permission.PermissionValidator
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
@@ -50,13 +49,8 @@ import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.WorldSlice
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningInput
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
@@ -93,7 +87,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.ServerTickHandler
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
@@ -105,16 +98,20 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -129,7 +126,6 @@ import net.minecraft.item.Items
import net.minecraft.item.NameTagItem
import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
@@ -258,8 +254,6 @@ object Cobblemon {
TrackedDataHandlerRegistry.register(StringSetDataSerializer)
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
TrackedDataHandlerRegistry.register(UUIDSetDataSerializer)
// Lowest priority because this applies after luxury ball bonus as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(Priority.LOWEST) { event ->
@@ -297,10 +291,6 @@ object Cobblemon {
CustomPokemonProperty.register(UncatchableProperty)
CustomPokemonProperty.register(PokemonFlagProperty)
CustomPokemonProperty.register(HiddenAbilityPropertyType)
CustomPokemonProperty.register(AspectPropertyType)
CustomPokemonProperty.register(UnaspectPropertyType)
FlowHandler.setup()
ifDedicatedServer {
isDedicatedServer = true
@@ -13,7 +13,6 @@ import net.minecraft.entity.ai.brain.Activity
object CobblemonActivities {
val activities = mutableListOf<Activity>()
val BATTLING_ACTIVITY = Activity("pokemon_battling")
val NPC_BATTLING = Activity("npc_battling")
fun register(activity: Activity): Activity {
activities.add(activity)
@@ -234,7 +234,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_pouch",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS)
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f)
.nonOpaque(), true
@@ -245,7 +245,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_sack",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.RELIC_COIN_SACK_SOUNDS)
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f), false
))
@@ -48,7 +48,6 @@ object CobblemonCommands {
PokemonRestartCommand.register(dispatcher)
BedrockParticleCommand.register(dispatcher)
OpenDialogueCommand.register(dispatcher)
NPCEditCommand.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
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.boat.CobblemonBoatEntity
import com.cobblemon.mod.common.entity.boat.CobblemonChestBoatEntity
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -68,15 +66,6 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(CHEST_BOAT_KEY.toString())
)
@JvmField
val POKE_BOBBER_KEY = cobblemonResource("poke_bobber")
@JvmField
val POKE_BOBBER: EntityType<PokeRodFishingBobberEntity> = this.create(
POKE_BOBBER_KEY.path,
EntityType.Builder.create(::PokeRodFishingBobberEntity, SpawnGroup.MISC).setDimensions(0.5F, 0.5F).maxTrackingRange(10)
.build(POKE_BOBBER_KEY.toString())
)
@JvmField
val GENERIC_BEDROCK_ENTITY_KEY = cobblemonResource("generic_bedrock")
@JvmField
@@ -86,16 +75,8 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(GENERIC_BEDROCK_ENTITY_KEY.toString())
)
@JvmField
val NPC_KEY = cobblemonResource("npc")
@JvmField
val NPC: EntityType<NPCEntity> = create(
NPC_KEY.path,
EntityType.Builder.create({ _, world -> NPCEntity(world) }, SpawnGroup.CREATURE).build("$NPC_KEY")
)
fun registerAttributes(consumer: (EntityType<out LivingEntity>, DefaultAttributeContainer.Builder) -> Unit) {
consumer(POKEMON, PokemonEntity.createAttributes())
consumer(NPC, NPCEntity.createAttributes())
}
}
@@ -1,74 +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.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.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import java.util.concurrent.ExecutionException
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
/**
* 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 = ResourceType.SERVER_DATA
override fun sync(player: ServerPlayerEntity) {}
val runtime by lazy { MoLangRuntime().setup() }
val clientFlows = hashMapOf<Identifier, MutableList<ExpressionLike>>()
val flows = hashMapOf<Identifier, MutableList<ExpressionLike>>()
override fun reload(manager: ResourceManager) {
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
manager.findResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.inputStream.use { stream ->
stream.bufferedReader().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(Identifier(identifier.namespace, event)) { mutableListOf() }.add(expression)
} else {
flows.getOrPut(Identifier(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(eventIdentifier: Identifier, context: Map<String, MoValue>) {
flows[eventIdentifier]?.forEach { it.resolve(runtime, context) }
}
}
@@ -14,8 +14,6 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.blue
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
@@ -47,7 +45,6 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.world.World
@@ -935,121 +932,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
Text.translatable("item.minecraft.fishing_rod").blue(),
Text.translatable("item.cobblemon.smithing_template.pokerod.ingredients").blue(),
Text.translatable("upgrade.cobblemon.pokerod").gray(),
Text.translatable("item.cobblemon.smithing_template.pokerod.base_slot_description"),
Text.translatable("item.cobblemon.smithing_template.pokerod.additions_slot_description"),
listOf(cobblemonResource("item/empty_slot_fishing_rod")),
listOf(cobblemonResource("item/empty_slot_pokeball"))
))
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
SmithingTemplateItem.of(CobblemonArmorTrims.AUTOMATON)
)
val pokeRods = mutableListOf<PokerodItem>()
@JvmField
val POKE_ROD = pokerodItem(cobblemonResource("poke_rod"))
@JvmField
val CITRINE_ROD = pokerodItem(cobblemonResource("citrine_rod"))
@JvmField
val VERDANT_ROD = pokerodItem(cobblemonResource("verdant_rod"))
@JvmField
val AZURE_ROD = pokerodItem(cobblemonResource("azure_rod"))
@JvmField
val ROSEATE_ROD = pokerodItem(cobblemonResource("roseate_rod"))
@JvmField
val SLATE_ROD = pokerodItem(cobblemonResource("slate_rod"))
@JvmField
val PREMIER_ROD = pokerodItem(cobblemonResource("premier_rod"))
@JvmField
val GREAT_ROD = pokerodItem(cobblemonResource("great_rod"))
@JvmField
val ULTRA_ROD = pokerodItem(cobblemonResource("ultra_rod"))
@JvmField
val SAFARI_ROD = pokerodItem(cobblemonResource("safari_rod"))
@JvmField
val FAST_ROD = pokerodItem(cobblemonResource("fast_rod"))
@JvmField
val LEVEL_ROD = pokerodItem(cobblemonResource("level_rod"))
@JvmField
val LURE_ROD = pokerodItem(cobblemonResource("lure_rod"))
@JvmField
val HEAVY_ROD = pokerodItem(cobblemonResource("heavy_rod"))
@JvmField
val LOVE_ROD = pokerodItem(cobblemonResource("love_rod"))
@JvmField
val FRIEND_ROD = pokerodItem(cobblemonResource("friend_rod"))
@JvmField
val MOON_ROD = pokerodItem(cobblemonResource("moon_rod"))
@JvmField
val SPORT_ROD = pokerodItem(cobblemonResource("sport_rod"))
@JvmField
val PARK_ROD = pokerodItem(cobblemonResource("park_rod"))
@JvmField
val NET_ROD = pokerodItem(cobblemonResource("net_rod"))
@JvmField
val DIVE_ROD = pokerodItem(cobblemonResource("dive_rod"))
@JvmField
val NEST_ROD = pokerodItem(cobblemonResource("nest_rod"))
@JvmField
val REPEAT_ROD = pokerodItem(cobblemonResource("repeat_rod"))
@JvmField
val TIMER_ROD = pokerodItem(cobblemonResource("timer_rod"))
@JvmField
val LUXURY_ROD = pokerodItem(cobblemonResource("luxury_rod"))
@JvmField
val DUSK_ROD = pokerodItem(cobblemonResource("dusk_rod"))
@JvmField
val HEAL_ROD = pokerodItem(cobblemonResource("heal_rod"))
@JvmField
val QUICK_ROD = pokerodItem(cobblemonResource("quick_rod"))
@JvmField
val DREAM_ROD = pokerodItem(cobblemonResource("dream_rod"))
@JvmField
val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"))
@JvmField
val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"))
@JvmField
val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"))
@JvmField
val ANCIENT_POKE_ROD = pokerodItem(cobblemonResource("ancient_poke_rod"))
@JvmField
val ANCIENT_CITRINE_ROD = pokerodItem(cobblemonResource("ancient_citrine_rod"))
@JvmField
val ANCIENT_VERDANT_ROD = pokerodItem(cobblemonResource("ancient_verdant_rod"))
@JvmField
val ANCIENT_AZURE_ROD = pokerodItem(cobblemonResource("ancient_azure_rod"))
@JvmField
val ANCIENT_ROSEATE_ROD = pokerodItem(cobblemonResource("ancient_roseate_rod"))
@JvmField
val ANCIENT_SLATE_ROD = pokerodItem(cobblemonResource("ancient_slate_rod"))
@JvmField
val ANCIENT_IVORY_ROD = pokerodItem(cobblemonResource("ancient_ivory_rod"))
@JvmField
val ANCIENT_GREAT_ROD = pokerodItem(cobblemonResource("ancient_great_rod"))
@JvmField
val ANCIENT_ULTRA_ROD = pokerodItem(cobblemonResource("ancient_ultra_rod"))
@JvmField
val ANCIENT_FEATHER_ROD = pokerodItem(cobblemonResource("ancient_feather_rod"))
@JvmField
val ANCIENT_WING_ROD = pokerodItem(cobblemonResource("ancient_wing_rod"))
@JvmField
val ANCIENT_JET_ROD = pokerodItem(cobblemonResource("ancient_jet_rod"))
@JvmField
val ANCIENT_HEAVY_ROD = pokerodItem(cobblemonResource("ancient_heavy_rod"))
@JvmField
val ANCIENT_LEADEN_ROD = pokerodItem(cobblemonResource("ancient_leaden_rod"))
@JvmField
val ANCIENT_GIGATON_ROD = pokerodItem(cobblemonResource("ancient_gigaton_rod"))
@JvmField
val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"))
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@@ -1115,13 +1003,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(calculator))
private fun pokerodItem(pokeRodId: Identifier): PokerodItem {
val settings = Item.Settings().maxCount(1).maxDamage(Items.FISHING_ROD.maxDamage)
val item = create(pokeRodId.path, PokerodItem(pokeRodId, settings))
pokeRods.add(item)
return item
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Settings()).also {
@@ -10,13 +10,11 @@ package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.mechanics.BerriesMechanic
import com.cobblemon.mod.common.mechanics.PotionsMechanic
import com.cobblemon.mod.common.mechanics.RemediesMechanic
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import net.minecraft.resource.ResourceManager
@@ -31,7 +29,6 @@ object CobblemonMechanics : DataRegistry {
val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.create()
var remedies = RemediesMechanic()
@@ -1,35 +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.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import java.util.Optional
import net.minecraft.entity.ai.brain.MemoryModuleType
import net.minecraft.util.Uuids
object CobblemonMemories {
val memories = mutableMapOf<String, MemoryModuleType<*>>()
val BATTLING_POKEMON = register("battling_pokemon", ListCodec(Uuids.INT_STREAM_CODEC))
val NPC_BATTLING = register("npc_battling", PrimitiveCodec.BOOL)
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
val memoryModule = MemoryModuleType(Optional.of(codec))
memories[id] = memoryModule
return memoryModule
}
fun <U> register(id: String): MemoryModuleType<U> {
val memoryModule = MemoryModuleType<U>(Optional.empty())
memories[id] = memoryModule
return memoryModule
}
}
@@ -20,15 +20,13 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
@@ -66,16 +64,13 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPa
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.NPCRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.data.*
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
@@ -83,8 +78,6 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityP
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
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
@@ -122,12 +115,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
@@ -139,7 +127,6 @@ import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMove
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
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
@@ -176,7 +163,6 @@ import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokem
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
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
@@ -203,12 +189,12 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.util.server
import kotlin.reflect.KClass
import net.minecraft.network.PacketByteBuf
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* Registers Cobblemon network packets.
@@ -314,7 +300,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NPCRegistrySyncPacket.ID, NPCRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
@@ -322,8 +307,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PokeRodRegistrySyncPacket.ID, PokeRodRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -367,10 +350,6 @@ object CobblemonNetwork : NetworkManager {
// Dialogue packets
this.createClientBound(DialogueClosedPacket.ID, DialogueClosedPacket::decode, DialogueClosedHandler)
this.createClientBound(DialogueOpenedPacket.ID, DialogueOpenedPacket::decode, DialogueOpenedHandler)
// NPCs
this.createClientBound(CloseNPCEditorPacket.ID, CloseNPCEditorPacket::decode, CloseNPCEditorHandler)
this.createClientBound(OpenNPCEditorPacket.ID, OpenNPCEditorPacket::decode, OpenNPCEditorHandler)
}
override fun registerServerBound() {
@@ -440,9 +419,6 @@ object CobblemonNetwork : NetworkManager {
// Dialogue packets
this.createServerBound(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler)
this.createServerBound(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler)
// NPC packets
this.createServerBound(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler)
}
private inline fun <reified T : NetworkPacket<T>> createClientBound(identifier: Identifier, noinline decoder: (PacketByteBuf) -> T, handler: ClientNetworkPacketHandler<T>) {
@@ -1,29 +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.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
import java.util.function.Supplier
import net.minecraft.entity.Entity
import net.minecraft.entity.ai.brain.sensor.Sensor
import net.minecraft.entity.ai.brain.sensor.SensorType
object CobblemonSensors {
val sensors = mutableMapOf<String, SensorType<*>>()
val NPC_BATTLING = register("npc_battling", ::NPCBattlingSensor)
val BATTLING_POKEMON = register("battling_pokemon", ::BattlingPokemonSensor)
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
val sensor = SensorType(supplier)
sensors[id] = sensor
return sensor
}
}
@@ -122,26 +122,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
@JvmField
val RELIC_COIN_SACK_BREAK = this.create("relic_coin_sack.break")
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
@JvmField
val RELIC_COIN_SACK_HIT = this.create("relic_coin_sack.hit")
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
@JvmField
val RELIC_COIN_SACK_STEP = this.create("relic_coin_sack.step")
val COIN_POUCH_STEP = this.create("coin_pouch.step")
@JvmField
val RELIC_COIN_SACK_PLACE = this.create("relic_coin_sack.place")
@JvmField
val RELIC_COIN_POUCH_BREAK = this.create("relic_coin_pouch.break")
@JvmField
val RELIC_COIN_POUCH_PLACE = this.create("relic_coin_pouch.place")
@JvmField
val FISHING_NOTIFICATION = this.create("fishing.notification")
@JvmField
val FISHING_SPLASH_BIG = this.create("fishing.splash_big")
@JvmField
val FISHING_SPLASH_SMALL = this.create("fishing.splash_small")
@JvmField
val FISHING_BOBBER_LAND = this.create("fishing.bobber_land")
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -202,22 +189,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val RELIC_COIN_SACK_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_SACK_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_SACK_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
COIN_POUCH_BREAK,
COIN_POUCH_STEP,
COIN_POUCH_PLACE,
COIN_POUCH_HIT,
COIN_POUCH_STEP
)
@JvmField
val RELIC_COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_POUCH_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_POUCH_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
)
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
@@ -32,14 +32,7 @@ object CobblemonTradeOffers {
* @return The resulting list.
*/
fun tradeOffersFor(profession: VillagerProfession): List<VillagerTradeOffer> = when (profession) {
VillagerProfession.FARMER -> listOf(
VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6)))
)
VillagerProfession.FISHERMAN -> listOf(
VillagerTradeOffer(VillagerProfession.FISHERMAN, 5, listOf(
TradeOffers.SellItemFactory(CobblemonItems.POKEROD_SMITHING_TEMPLATE, 12, 3, 30)
))
)
VillagerProfession.FARMER -> listOf(VillagerTradeOffer(VillagerProfession.FARMER, 3, listOf(TradeOffers.SellItemFactory(CobblemonItems.VIVICHOKE_SEEDS, 24, 1, 1, 6))))
else -> emptyList()
}
@@ -113,13 +113,6 @@ object CobblemonCriteria {
)
)
val CAST_POKE_ROD = this.create(
SimpleCriterionTrigger(
cobblemonResource("cast_poke_rod"),
CastPokeRodCriterionCondition::class.java
)
)
// Advancement criteria for [grow_tumblestone.json]
val PLANT_TUMBLESTONE = this.create(
SimpleCriterionTrigger(
@@ -1,30 +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.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class CastPokeRodCriterionCondition(id: Identifier, predicate: LootContextPredicate): SimpleCriterionCondition<Boolean>(id, predicate) {
var hasBait: Boolean = false
override fun toJson(json: JsonObject) {
json.addProperty("hasBait", hasBait)
}
override fun fromJson(json: JsonObject) {
hasBait = json.get("hasBait").asBoolean
}
override fun matches(player: ServerPlayerEntity, context: Boolean): Boolean {
return hasBait == context
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.battles.model
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
@@ -21,8 +20,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
@@ -49,10 +46,8 @@ import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHit
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.withQueryValue
import java.io.File
import java.util.UUID
import java.util.concurrent.CompletableFuture
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.util.concurrent.ConcurrentLinkedDeque
@@ -71,10 +66,6 @@ open class PokemonBattle(
) {
/** Whether logging will be silenced for this battle. */
var mute = true
val struct = this.asMoLangValue()
val runtime = MoLangRuntime().also { it.environment.structs["query"] = struct }
val onEndHandlers: MutableList<(PokemonBattle) -> Unit> = mutableListOf()
init {
side1.battle = this
@@ -106,8 +97,6 @@ open class PokemonBattle(
val battleLog = mutableListOf<String>()
val chatLog = mutableListOf<Text>()
var started = false
var winners = listOf<BattleActor>()
var losers = listOf<BattleActor>()
var ended = false
// TEMP battle showcase stuff
var announcingRules = false
@@ -277,7 +266,6 @@ open class PokemonBattle(
}
sendUpdate(BattleEndPacket())
BattleRegistry.closeBattle(this)
onEndHandlers.forEach { it(this) }
}
fun finishCaptureAction(captureAction: BattleCaptureAction) {
@@ -365,14 +353,6 @@ open class PokemonBattle(
}
}
fun dispatchFuture(future: () -> CompletableFuture<*>) {
val dispatch = BattleDispatch {
val generatedFuture = future()
return@BattleDispatch DispatchResult { generatedFuture.isDone }
}
dispatches.add(dispatch)
}
fun dispatchInsert(dispatcher: () -> Iterable<BattleDispatch>) {
dispatch {
val newDispatches = dispatcher()
@@ -145,7 +145,4 @@ abstract class BattleActor(
}
open fun awardExperience(battlePokemon: BattlePokemon, experience: Int) {}
open fun sendUpdate(packet: NetworkPacket<*>) {}
open fun win(otherWinners: List<BattleActor>, losers: List<BattleActor>) {}
open fun lose(winners: List<BattleActor>, otherLosers: List<BattleActor>) {}
}
@@ -27,13 +27,10 @@ object DialogueManager {
val activeDialogues = mutableMapOf<UUID, ActiveDialogue>()
fun startDialogue(playerEntity: ServerPlayerEntity, dialogue: Dialogue) {
startDialogue(ActiveDialogue(playerEntity, dialogue))
}
fun startDialogue(activeDialogue: ActiveDialogue) {
activeDialogues[activeDialogue.playerEntity.uuid] = activeDialogue
val packet = DialogueOpenedPacket(activeDialogue, true)
activeDialogue.playerEntity.sendPacket(packet)
val activeDialogue = ActiveDialogue(playerEntity, dialogue)
activeDialogues[playerEntity.uuid] = activeDialogue
val packet = DialogueOpenedPacket(activeDialogue, includeFaces = true)
playerEntity.sendPacket(packet)
}
fun stopDialogue(playerEntity: ServerPlayerEntity) {
@@ -12,7 +12,6 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
@@ -29,10 +28,6 @@ object EntityDimensionsAdapter : JsonSerializer<EntityDimensions>, JsonDeseriali
const val WIDTH = "width"
const val HEIGHT = "height"
val templates = mutableMapOf(
"player" to { EntityDimensions(0.6F, 1.8F, false) }
)
override fun serialize(dimensions: EntityDimensions, type: Type, ctx: JsonSerializationContext): JsonElement {
val json = JsonObject()
json.addProperty(WIDTH, dimensions.width)
@@ -41,9 +36,6 @@ object EntityDimensionsAdapter : JsonSerializer<EntityDimensions>, JsonDeseriali
}
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): EntityDimensions {
if (json is JsonPrimitive) {
return templates[json.asString]?.invoke() ?: throw IllegalStateException("Dimensions provided by template name $json but no such template is set.")
}
json as JsonObject
return EntityDimensions(json.get(WIDTH).asFloat, json.get(HEIGHT).asFloat, false)
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.entity
import com.bedrockk.molang.runtime.struct.QueryStruct
import net.minecraft.entity.Entity
import net.minecraft.entity.data.TrackedData
@@ -19,5 +18,4 @@ interface EntitySideDelegate<T : Entity> {
fun initialize(entity: T) {}
fun tick(entity: T) {}
fun onTrackedDataSet(data: TrackedData<*>) {}
fun addToStruct(struct: QueryStruct) {}
}
@@ -1,19 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.entity
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* A delegate for some actions that can be different for an NPC on client versus server.
*
* @author Hiroku
* @since August 25th, 2023
*/
interface NPCSideDelegate : EntitySideDelegate<NPCEntity>
@@ -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.entity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import java.util.concurrent.CompletableFuture
/**
* Interface for custom entities that can send out and recall Pokémon. They get notified
* when either of those actions are occurring.
*
* @author Hiroku
* @since August 25th, 2023
*
*/
interface PokemonSender {
fun sendingOut(): CompletableFuture<Unit>
fun recalling(pokemonEntity: PokemonEntity): CompletableFuture<Unit>
}
@@ -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.registry.RegistryKeys
import net.minecraft.server.network.ServerPlayerEntity
data class PokemonCapturedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity,
val pokeBallEntity: EmptyPokeBallEntity
) {
val context = mapOf<String, MoValue>(
"pokemon" to pokemon.struct,
"player" to player.asMoLangValue(),
"poke_ball" to pokeBallEntity.struct,
"item" to player.world.itemRegistry.getEntry(pokeBallEntity.pokeBall.item).asMoLangValue(RegistryKeys.ITEM)
)
}
)
@@ -1,84 +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.fishing
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.util.Identifier
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtString
data class FishingBait(
val item: Identifier,
val effects: List<Effect>,
) {
data class Effect(
val type: Identifier,
val subcategory: Identifier?,
val chance: Double = 0.0,
val value: Double = 0.0
) {
fun toNbt(): NbtCompound {
val nbt = NbtCompound()
nbt.putString("Type", type.toString())
subcategory?.let { nbt.putString("Subcategory", it.toString()) }
nbt.putDouble("Chance", chance)
nbt.putDouble("Value", value)
return nbt
}
companion object {
fun fromNbt(nbt: NbtCompound): Effect {
val type = Identifier(nbt.getString("Type"))
val subcategory = if (nbt.contains("Subcategory")) Identifier(nbt.getString("Subcategory")) else null
val chance = nbt.getDouble("Chance")
val value = nbt.getDouble("Value")
return Effect(type, subcategory, chance, value)
}
}
}
fun toNbt(): NbtCompound {
val nbt = NbtCompound()
nbt.putString("Item", item.toString())
val effectsList = NbtList()
effects.forEach { effectsList.add(it.toNbt()) }
nbt.put("Effects", effectsList)
return nbt
}
companion object {
fun fromNbt(nbt: NbtCompound): FishingBait {
val item = Identifier(nbt.getString("Item"))
val effectsList = nbt.getList("Effects", 10) // 10 is the type for NbtCompound
val effects = mutableListOf<Effect>()
for (i in 0 until effectsList.size) {
effects.add(Effect.fromNbt(effectsList.getCompound(i)))
}
return FishingBait(item, effects)
}
}
object Effects {
val NATURE = cobblemonResource("nature")
val IV = cobblemonResource("iv")
val EV = cobblemonResource("ev")
val BITE_TIME = cobblemonResource("bite_time")
val GENDER_CHANCE = cobblemonResource("gender_chance")
val LEVEL_RAISE = cobblemonResource("level_raise")
val TERA = cobblemonResource("tera")
val SHINY_REROLL = cobblemonResource("shiny_reroll")
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
val FRIENDSHIP = cobblemonResource("friendship")
val INERT = cobblemonResource("inert")
}
}
@@ -1,105 +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.fishing
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fishing.FishingBaitRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object FishingBaits : JsonDataRegistry<FishingBait>{
override val id = cobblemonResource("fishing_baits")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<FishingBaits>()
override val typeToken: TypeToken<FishingBait> = TypeToken.get(FishingBait::class.java)
override val resourcePath = "fishing_baits"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
private val itemMap = mutableMapOf<Identifier, FishingBait>()
override fun sync(player: ServerPlayerEntity) {
FishingBaitRegistrySyncPacket(this.itemMap.values.toList()).sendToPlayer(player)
}
override fun reload(data: Map<Identifier, FishingBait>) {
itemMap.clear()
data.forEach { id, bait ->
itemMap[bait.item] = bait
}
}
fun getFromItemStack(stack: ItemStack): FishingBait? {
return itemMap[Registries.ITEM.getId(stack.item)]
}
fun isFishingBait(stack: ItemStack) = itemMap.containsKey(Registries.ITEM.getId(stack.item))
//
//
// fun getBaitSubcategory(item: ItemStack): String? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.subcategory
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.subcategory
// }
// return null
// }
//
// fun getBaitSuccessChance(item: ItemStack): Double? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.chance
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.chance
// }
// return null
// }
//
// fun getBaitEffectValue(item: ItemStack): Double? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.value
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.value
// }
// return null
// }
//
//
// fun getBaitNote(item: ItemStack): String? {
// val itemId = Registries.ITEM.getId(item.item)
// if (itemId in itemMap.keys) {
// return itemMap[itemId]?.note
// }
// val tag = tagMap.keys.firstOrNull { it.fits(item.item, Registries.ITEM) }
// if (tag != null) {
// return tagMap[tag]?.note
// }
// return null
// }
}
@@ -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.fishing
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.pokeball.PokeBall
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
/**
* Base poke rod object
* It is intended that there is one poke rod object initialized for a given poke rod type.
*
* @property name the poke rod registry name
* @property pokeBallId The [Identifier] of the pokeball that is used as the bobber for this rod
* @property lineColor list of [RGB] values that apply to the fishing line of the Pokérod
*/
data class PokeRod(
val pokeBallId: Identifier,
//Hex string of color
val lineColor: String,
var name: Identifier?
) {
internal fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(name)
buffer.writeIdentifier(pokeBallId)
buffer.writeString(lineColor)
}
companion object {
internal fun decode(buffer: PacketByteBuf): PokeRod {
val name = buffer.readIdentifier()
val pokeBallId = buffer.readIdentifier()
val lineColor = buffer.readString()
return PokeRod(pokeBallId, lineColor, name)
}
}
fun getPokeBall(): PokeBall? {
return PokeBalls.getPokeBall(pokeBallId)
}
}
@@ -1,62 +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.fishing
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.PokeRodRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
/**
* The data registry for [PokeRod]s.
* All the pokerod fields are guaranteed to exist
*/
object PokeRods : JsonDataRegistry<PokeRod> {
override val id = cobblemonResource("pokerods")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<PokeRods>()
// ToDo once datapack pokerod is implemented add required adapters here
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.setPrettyPrinting()
.create()
override val typeToken: TypeToken<PokeRod> = TypeToken.get(PokeRod::class.java)
override val resourcePath = "pokerods"
private val rods = mutableMapOf<Identifier, PokeRod>()
override fun reload(data: Map<Identifier, PokeRod>) {
data.forEach {
it.value.name = it.key
rods[it.key] = it.value
}
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
PokeRodRegistrySyncPacket(rods.values).sendToPlayer(player)
}
/**
* Gets a Pokerod from registry name.
* @return the pokerod object if found otherwise null.
*/
fun getPokeRod(name : Identifier): PokeRod? = rods[name]
}
@@ -10,11 +10,12 @@ package com.cobblemon.mod.common.api.gui
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Species
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
@@ -28,17 +29,14 @@ import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.Entity
import net.minecraft.text.MutableText
import net.minecraft.text.OrderedText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.RotationAxis
import org.joml.Matrix4f
import org.joml.Quaternionf
import org.joml.Vector3f
@JvmOverloads
fun blitk(
matrixStack: MatrixStack,
texture: Identifier? = null,
@@ -102,7 +100,6 @@ fun drawRectangle(
BufferRenderer.drawWithGlobalProgram(bufferbuilder.end())
}
@JvmOverloads
fun drawCenteredText(
context: DrawContext,
font: Identifier? = null,
@@ -117,7 +114,6 @@ fun drawCenteredText(
context.drawText(textRenderer, comp, x.toInt() - textRenderer.getWidth(comp) / 2, y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawText(
context: DrawContext,
font: Identifier? = null,
@@ -149,7 +145,6 @@ fun drawText(
return isHovered
}
@JvmOverloads
fun drawText(
context: DrawContext,
text: OrderedText,
@@ -168,7 +163,6 @@ fun drawText(
context.drawText(textRenderer, text, tweakedX.toInt(), y.toInt(), colour, shadow)
}
@JvmOverloads
fun drawString(
context: DrawContext,
text: String,
@@ -187,30 +181,22 @@ fun drawString(
context.drawText(textRenderer, comp, x.toInt(), y.toInt(), colour, shadow)
}
@JvmOverloads
fun <T : Entity, M : PoseableEntityModel<T>> drawPoseablePortrait(
identifier: Identifier,
fun drawPortraitPokemon(
species: Species,
aspects: Set<String>,
matrixStack: MatrixStack,
scale: Float = 13F,
contextScale: Float = 1F,
reversed: Boolean = false,
state: PoseableEntityState<T>? = null,
repository: VaryingModelRepository<T, M>,
partialTicks: Float,
limbSwing: Float = 0F,
limbSwingAmount: Float = 0F,
ageInTicks: Float = 0F,
headYaw: Float = 0F,
headPitch: Float = 0F
state: PoseableEntityState<PokemonEntity>? = null,
partialTicks: Float
) {
val model = repository.getPoser(identifier, aspects)
val texture = repository.getTexture(identifier, aspects, state?.animationSeconds ?: 0F)
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state?.animationSeconds ?: 0F)
val context = RenderContext()
repository.getTextureNoSubstitute(identifier, aspects, 0f).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, contextScale)
context.put(RenderContext.SPECIES, identifier)
PokemonModelRepository.getTextureNoSubstitute(species.resourceIdentifier, aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
context.put(RenderContext.SPECIES, species.resourceIdentifier)
context.put(RenderContext.ASPECTS, aspects)
val renderType = model.getLayer(texture)
@@ -226,7 +212,7 @@ fun <T : Entity, M : PoseableEntityModel<T>> drawPoseablePortrait(
model.getPose(PoseType.PORTRAIT)?.let { state.setPose(it.poseName) }
state.timeEnteredPose = 0F
state.updatePartialTicks(partialTicks)
model.setupAnimStateful(null, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch)
model.setupAnimStateful(null, state, 0F, 0F, 0F, 0F, 0F)
originalPose?.let { state.setPose(it) }
}
@@ -248,7 +234,7 @@ fun <T : Entity, M : PoseableEntityModel<T>> drawPoseablePortrait(
val buffer = immediate.getBuffer(renderType)
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(immediate, state, repository.getLayers(identifier, aspects)) {
model.withLayerContext(immediate, state, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
immediate.draw()
}
@@ -256,58 +242,5 @@ fun <T : Entity, M : PoseableEntityModel<T>> drawPoseablePortrait(
matrixStack.pop()
model.setDefault()
DiffuseLighting.enableGuiDepthLighting()
}
fun <E : Entity, M : PoseableEntityModel<E>> drawProfile(
repository: VaryingModelRepository<E, M>,
resourceIdentifier: Identifier,
aspects: Set<String>,
matrixStack: MatrixStack,
state: PoseableEntityState<E>,
partialTicks: Float,
scale: Float = 20F
) {
val model = repository.getPoser(resourceIdentifier, aspects)
val texture = repository.getTexture(resourceIdentifier, aspects, state.animationSeconds)
val context = RenderContext()
repository.getTextureNoSubstitute(resourceIdentifier, aspects, 0f).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, 1F)
context.put(RenderContext.SPECIES, resourceIdentifier)
context.put(RenderContext.ASPECTS, aspects)
val renderType = model.getLayer(texture)
RenderSystem.applyModelViewMatrix()
matrixStack.scale(scale, scale, -scale)
model.getPose(PoseType.PROFILE)?.let { state.setPose(it.poseName) }
state.timeEnteredPose = 0F
state.updatePartialTicks(partialTicks)
model.setupAnimStateful(null, state, 0F, 0F, 0F, 0F, 0F)
matrixStack.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
matrixStack.scale(model.profileScale, model.profileScale, 1 / model.profileScale)
// matrixStack.multiply(rotation)
val quaternion1 = RotationAxis.POSITIVE_Y.rotationDegrees(-32F * if (false) -1F else 1F)
val quaternion2 = RotationAxis.POSITIVE_X.rotationDegrees(5F)
matrixStack.multiply(quaternion1)
matrixStack.multiply(quaternion2)
DiffuseLighting.method_34742()
val entityRenderDispatcher = MinecraftClient.getInstance().entityRenderDispatcher
entityRenderDispatcher.setRenderShadows(true)
val bufferSource = MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers
val buffer = bufferSource.getBuffer(renderType)
val light1 = Vector3f(-1F, 1F, 1.0F)
val light2 = Vector3f(1.3F, -1F, 1.0F)
RenderSystem.setShaderLights(light1, light2)
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, aspects)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
bufferSource.draw()
}
model.setDefault()
entityRenderDispatcher.setRenderShadows(true)
DiffuseLighting.enableGuiDepthLighting()
}
@@ -19,10 +19,8 @@ import com.bedrockk.molang.runtime.value.MoValue
* @since October 22nd, 2023
*/
interface ExpressionLike {
override fun toString(): String
/** Produces a [MoValue] for a [MoLangRuntime] to supply an environment. */
fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue> = emptyMap()): MoValue
fun getString(): String = toString()
fun resolve(runtime: MoLangRuntime): MoValue
fun resolveDouble(runtime: MoLangRuntime) = resolve(runtime).asDouble()
fun resolveFloat(runtime: MoLangRuntime) = resolveDouble(runtime).toFloat()
@@ -30,5 +28,4 @@ interface ExpressionLike {
fun resolveInt(runtime: MoLangRuntime) = resolveDouble(runtime).toInt()
fun resolveBoolean(runtime: MoLangRuntime) = resolveDouble(runtime) == 1.0
fun resolveObject(runtime: MoLangRuntime) = resolve(runtime) as ObjectValue<*>
}
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.resolve
/**
@@ -21,6 +19,5 @@ import com.cobblemon.mod.common.util.resolve
* @since October 22nd, 2023
*/
class ListExpression(val exprs: List<Expression>): ExpressionLike {
override fun toString() = exprs.joinToString("\n") { it.getString().let { if (it.endsWith(";")) it else "$it;" } }
override fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue>) = exprs.resolve(runtime, context)
override fun resolve(runtime: MoLangRuntime) = exprs.resolve(runtime)
}
@@ -18,27 +18,12 @@ 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.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.itemRegistry
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.worldRegistry
import com.mojang.datafixers.util.Either
import net.minecraft.block.Block
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.damage.DamageSource
import net.minecraft.entity.damage.DamageTypes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtDouble
import net.minecraft.nbt.NbtElement
@@ -50,7 +35,6 @@ import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.registry.tag.TagKey
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.Identifier
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
@@ -64,11 +48,6 @@ import net.minecraft.world.dimension.DimensionType
*/
object MoLangFunctions {
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>(
"run_command" to java.util.function.Function { params ->
val command = params.getString(0)
val server = server() ?: return@Function DoubleValue(0)
server.commandManager.executeWithPrefix(server.commandSource, 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) },
@@ -96,134 +75,23 @@ object MoLangFunctions {
val array = ArrayStruct(hashMapOf())
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
return@Function array
},
"run_script" to java.util.function.Function { params ->
val runtime = MoLangRuntime()
runtime.environment.structs.putAll(params.environment.structs)
val script = params.getString(0).asIdentifierDefaultingNamespace()
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
}
)
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<(PlayerEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val playerFunctions = mutableListOf<(ServerPlayerEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ player ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("username") { _ -> StringValue(player.gameProfile.name) }
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
map.put("data") { _ -> return@put if (player is ServerPlayerEntity) Cobblemon.molangData.load(player.uuid) else DoubleValue(0) }
map.put("save_data") { _ -> if (player is ServerPlayerEntity) Cobblemon.molangData.save(player.uuid) else DoubleValue(0) }
map.put("main_held_item") { _ -> player.world.itemRegistry.getEntry(player.mainHandStack.item).asMoLangValue(RegistryKeys.ITEM) }
map.put("off_held_item") { _ -> player.world.itemRegistry.getEntry(player.offHandStack.item).asMoLangValue(RegistryKeys.ITEM) }
map.put("face") { _ -> ObjectValue(PlayerDialogueFaceProvider(player.uuid)) }
map.put("swing_hand") { _ -> player.swingHand(player.activeHand) }
map.put("food_level") { _ -> DoubleValue(player.hungerManager.foodLevel) }
map.put("saturation_level") { _ -> DoubleValue(player.hungerManager.saturationLevel) }
map.put("tell") { params ->
val message = params.getString(0).text()
val overlay = params.getBooleanOrNull(1) ?: false
player.sendMessage(message, overlay)
}
map.put("teleport") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
val z = params.getDouble(2)
player.teleport(x, y, z)
}
map.put("heal") { params ->
val amount = params.getDoubleOrNull(0) ?: player.maxHealth
player.heal(amount.toFloat())
}
map.put("environment") {
val environment = MoLangEnvironment()
environment.structs["query"] = player.asMoLangValue()
environment
}
map.put("run_command") { params ->
val command = params.getString(0)
val server = server() ?: return@put DoubleValue(0)
server.commandManager.executeWithPrefix(player.commandSource, command)
}
map
}
)
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("damage") { params ->
val amount = params.getDouble(0)
val source = DamageSource(entity.world.registryManager.get(RegistryKeys.DAMAGE_TYPE).getEntry(DamageTypes.GENERIC).get())
entity.damage(source, amount.toFloat())
}
map.put("is_sneaking") { _ -> DoubleValue(entity.isSneaking) }
map.put("is_sprinting") { _ -> DoubleValue(entity.isSprinting) }
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
map.put("is_in_water") { _ -> DoubleValue(entity.isSubmergedInWater) }
map.put("is_touching_water_or_rain") { _ -> DoubleValue(entity.isWet) }
map.put("is_touching_water") { _ -> DoubleValue(entity.isTouchingWater) }
map.put("is_in_lava") { _ -> DoubleValue(entity.isInLava) }
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.hasVehicle()) }
map.put("health") { _ -> DoubleValue(entity.health) }
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
map.put("name") { _ -> StringValue(entity.entityName) }
map.put("yaw") { _ -> DoubleValue(entity.yaw.toDouble()) }
map.put("pitch") { _ -> DoubleValue(entity.pitch.toDouble()) }
map.put("x") { _ -> DoubleValue(entity.x) }
map.put("y") { _ -> DoubleValue(entity.y) }
map.put("z") { _ -> DoubleValue(entity.z) }
map.put("velocity_x") { _ -> DoubleValue(entity.velocity.x) }
map.put("velocity_y") { _ -> DoubleValue(entity.velocity.y) }
map.put("velocity_z") { _ -> DoubleValue(entity.velocity.z) }
map.put("horizontal_velocity") { _ -> DoubleValue(entity.velocity.horizontalLength()) }
map.put("is_on_ground") { _ -> DoubleValue(entity.isOnGround) }
map.put("world") { _ -> entity.world.worldRegistry.getEntry(entity.world).asWorldMoLangValue() }
map.put("biome") { _ -> entity.world.getBiome(entity.blockPos).asBiomeMoLangValue() }
map
}
)
val npcFunctions = mutableListOf<(NPCEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ npc ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("class") { StringValue(npc.npc.resourceIdentifier.toString()) }
map.put("name") { StringValue(npc.name.string) }
map.put("face") { ObjectValue(ReferenceDialogueFaceProvider(npc.id)) }
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
map.put("run_script_on_client") { params ->
val world = npc.world
if (world is ServerWorld) {
val script = params.getString(0)
val packet = RunPosableMoLangPacket(npc.id, setOf("q.run_script('$script')"))
packet.sendToPlayers(world.players.toList())
}
Unit
}
map.put("run_script") { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
val environment = params.environment
val runtime = MoLangRuntime()
runtime.environment.structs.putAll(environment.structs)
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
}
map.put("environment") { _ -> npc.runtime.environment }
map
}
)
val battleFunctions = mutableListOf<(PokemonBattle) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ battle ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("battle_id") { StringValue(battle.battleId.toString()) }
map.put("is_pvn") { DoubleValue(battle.isPvN) }
map.put("is_pvp") { DoubleValue(battle.isPvP) }
map.put("is_pvw") { DoubleValue(battle.isPvW) }
map.put("battle_type") { StringValue(battle.format.toString()) }
map.put("environment") { battle.runtime.environment }
map
hashMapOf(
"username" to java.util.function.Function { _ -> StringValue(player.gameProfile.name) },
"uuid" to java.util.function.Function { _ -> StringValue(player.gameProfile.id.toString()) },
"data" to java.util.function.Function { _ -> return@Function Cobblemon.molangData.load(player.uuid) },
"save_data" to java.util.function.Function { _ -> Cobblemon.molangData.save(player.uuid) },
"main_held_item" to java.util.function.Function { _ -> player.world.itemRegistry.getEntry(player.mainHandStack.item).asMoLangValue(RegistryKeys.ITEM) },
"off_held_item" to java.util.function.Function { _ -> player.world.itemRegistry.getEntry(player.offHandStack.item).asMoLangValue(RegistryKeys.ITEM) },
"face" to java.util.function.Function { _ -> ObjectValue(PlayerDialogueFaceProvider(player.uuid)) }
)
}
)
@@ -231,35 +99,15 @@ object MoLangFunctions {
fun RegistryEntry<World>.asWorldMoLangValue() = asMoLangValue(RegistryKeys.WORLD).addFunctions(worldFunctions)
fun RegistryEntry<Block>.asBlockMoLangValue() = asMoLangValue(RegistryKeys.BLOCK).addFunctions(blockFunctions)
fun RegistryEntry<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(RegistryKeys.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
fun PlayerEntity.asMoLangValue(): ObjectValue<PlayerEntity> {
fun ServerPlayerEntity.asMoLangValue(): ObjectValue<ServerPlayerEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.entityName }
)
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
value.addFunctions(playerFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun NPCEntity.asMoLangValue(): ObjectValue<NPCEntity> {
val value = ObjectValue(
obj = this,
stringify = { it.name.string }
)
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
value.addFunctions(npcFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
val value = ObjectValue(
obj = this,
stringify = { it.battleId.toString() }
)
value.addFunctions(battleFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
fun <T> RegistryEntry<T>.asMoLangValue(key: RegistryKey<Registry<T>>): ObjectValue<RegistryEntry<T>> {
val value = ObjectValue(
obj = this,
@@ -286,6 +134,11 @@ object MoLangFunctions {
return this
}
fun <T : QueryStruct> T.addFunction(name: String, function: java.util.function.Function<MoParams, Any>): T {
this.functions[name] = function
return this
}
fun MoLangEnvironment.getQueryStruct(name: String = "query") = structs.getOrPut(name) { QueryStruct(hashMapOf()) } as QueryStruct
fun MoLangRuntime.setup(): MoLangRuntime {
@@ -340,6 +193,4 @@ object MoLangFunctions {
else -> null
} ?: throw IllegalArgumentException("Invalid NBT element type: ${nbt.type}")
}
}
fun Either<Identifier, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
}
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.api.molang
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.resolve
/**
@@ -22,6 +20,5 @@ import com.cobblemon.mod.common.util.resolve
* @since October 22nd, 2023
*/
class SingleExpression(val expr: Expression) : ExpressionLike {
override fun toString() = expr.getString()
override fun resolve(runtime: MoLangRuntime, context: Map<String, MoValue>) = runtime.resolve(expr, context)
override fun resolve(runtime: MoLangRuntime) = runtime.resolve(expr)
}
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
@@ -11,11 +11,9 @@ package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asExpressions
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import java.util.concurrent.CompletableFuture
import net.minecraft.entity.Entity
@@ -31,7 +29,7 @@ import net.minecraft.entity.Entity
*/
class ActionEffectTimeline(
val timeline: List<ActionEffectKeyframe> = mutableListOf(),
val condition: ExpressionLike = "true".asExpressionLike()
val condition: Expression = "true".asExpression()
) {
companion object {
val NONE = ActionEffectTimeline()
@@ -49,6 +49,6 @@ class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityC
players.forEach { it.sendPacket(pkt) }
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -46,6 +46,6 @@ class EntityMoLangActionEffectKeyframe : ConditionalActionEffectKeyframe(), Enti
players.forEach { it.sendPacket(pkt) }
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -51,6 +51,6 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
packet.sendToPlayers(players)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -45,6 +45,6 @@ class EntitySoundActionEffectKeyframe : ConditionalActionEffectKeyframe(), Entit
entity.playSound(soundEvent, 1f, 1f)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -25,6 +25,6 @@ class MoLangActionEffectKeyframe : ActionEffectKeyframe {
val delay: ExpressionLike = "0".asExpressionLike()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
expressions.resolve(context.runtime)
return delayedFuture(seconds = delay.resolveFloat(context.runtime))
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -14,5 +14,5 @@ import com.cobblemon.mod.common.util.asExpressionLike
class PauseActionEffectKeyframe : ConditionalActionEffectKeyframe() {
val pause = "1".asExpressionLike()
override fun playWhenTrue(context: ActionEffectContext) = delayedFuture(seconds = pause.resolveFloat(context.runtime))
override fun playWhenTrue(context: ActionEffectContext) = delayedFuture(seconds = pause.resolveFloat(context.runtime), serverThread = true)
}
@@ -24,7 +24,7 @@ class RemoveHoldsActionEffectKeyframe : ActionEffectKeyframe {
val delay = 0F
val holds = mutableSetOf<String>()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
return delayedFuture(seconds = delay).thenApply {
return delayedFuture(seconds = delay, serverThread = true).thenApply {
if (holds.isEmpty()) {
context.holds.clear()
} else {
@@ -56,7 +56,7 @@ class ReturnToPositionActionEffectKeyframe : ActionEffectKeyframe {
}
)
after(seconds = timeout.resolveFloat(context.runtime)) {
after(seconds = timeout.resolveFloat(context.runtime), serverThread = true) {
if (!future.isDone && !timedOut) {
timedOut = true
timeoutEffect.run(context).thenApply { future.complete(Unit) }
@@ -29,4 +29,5 @@ object StringSetDataSerializer : TrackedDataHandler<Set<String>> {
}
override fun copy(set: Set<String>) = set.toSet()
}
@@ -1,33 +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.net.serializers
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import java.util.UUID
import net.minecraft.entity.data.TrackedDataHandler
import net.minecraft.network.PacketByteBuf
object UUIDSetDataSerializer : TrackedDataHandler<Set<UUID>> {
override fun write(buffer: PacketByteBuf, set: Set<UUID>) {
buffer.writeSizedInt(IntSize.U_BYTE, set.size)
set.forEach(buffer::writeUuid)
}
override fun read(buffer: PacketByteBuf): Set<UUID> {
val set = mutableSetOf<UUID>()
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
set.add(buffer.readUuid())
}
return set
}
override fun copy(set: Set<UUID>) = set.toSet()
}
@@ -1,82 +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
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.ExpressionLike
import com.cobblemon.mod.common.api.npc.configuration.NPCBattleConfiguration
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.readMapK
import com.cobblemon.mod.common.util.writeMapK
import com.mojang.datafixers.util.Either
import net.minecraft.entity.EntityDimensions
import net.minecraft.network.PacketByteBuf
import net.minecraft.text.MutableText
import net.minecraft.util.Identifier
/**
* A class of NPC. This can contain a lot of preset information about the NPC's behaviour. Consider this the Pokémon
* species but for NPCs.
*
* @author Hiroku
* @since August 14th, 2023
*/
class NPCClass {
@Transient
lateinit var resourceIdentifier: Identifier
var names = mutableListOf<MutableText>(lang("npc.name.default"))
var hitbox = EntityDimensions(0.6F, 1.8F, false)
var battleConfiguration = NPCBattleConfiguration()
var interaction: Either<Identifier, ExpressionLike>? = null
var variables = mutableMapOf<String, MoValue>()
fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(names, PacketByteBuf::writeText)
buffer.writeFloat(this.hitbox.width)
buffer.writeFloat(this.hitbox.height)
buffer.writeBoolean(this.hitbox.fixed)
battleConfiguration.encode(buffer)
buffer.writeNullable(interaction) { _, value ->
buffer.writeBoolean(value.map({ true }, { false }))
buffer.writeString(value.map({ it.toString() }, { it.toString() }))
}
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
buffer.writeString(key)
buffer.writeString(value.asString())
}
}
fun decode(buffer: PacketByteBuf) {
names = buffer.readList { buffer.readText().copy() }.toMutableList()
hitbox = EntityDimensions(buffer.readFloat(), buffer.readFloat(), buffer.readBoolean())
battleConfiguration = NPCBattleConfiguration()
battleConfiguration.decode(buffer)
interaction = buffer.readNullable {
if (buffer.readBoolean()) {
Either.left(Identifier(buffer.readString()))
} else {
Either.right(buffer.readString().asExpressionLike())
}
}
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
val key = buffer.readString()
val value = buffer.readString()
if (value.toDoubleOrNull() != null) {
return@readMapK key to DoubleValue(value.toDouble())
} else {
return@readMapK key to StringValue(value)
}
}
}
}
@@ -1,128 +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
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.ai.SleepDepth
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.drop.DropEntry
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.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.net.messages.client.data.NPCRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import net.minecraft.block.Block
import net.minecraft.entity.EntityDimensions
import net.minecraft.item.Item
import net.minecraft.nbt.NbtCompound
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.world.biome.Biome
object NPCClasses : JsonDataRegistry<NPCClass> {
override val id = cobblemonResource("npc")
override val type = ResourceType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(TimeRange::class.java, IntRangesAdapter(TimeRange.timeRanges) { TimeRange(*it) })
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(NbtCompound::class.java, NbtCompoundAdapter)
.registerTypeAdapter(NPCPartyProvider::class.java, NPCPartyProviderAdapter)
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Either::class.java, Identifier::class.java, ExpressionLike::class.java).type, NPCScriptAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
override val typeToken: TypeToken<NPCClass> = TypeToken.get(NPCClass::class.java)
override val resourcePath = "npcs"
override val observable = SimpleObservable<NPCClasses>()
private val npcClassesByIdentifier = hashMapOf<Identifier, NPCClass>()
val classes: Collection<NPCClass>
get() = this.npcClassesByIdentifier.values
init {
// NPC additions would be useful
// SpeciesAdditions.observable.subscribe {
// this.species.forEach(Species::initialize)
// this.species.forEach(Species::resolveEvolutionMoves)
// }
}
/**
* Finds an NPC class by the pathname of their [Identifier].
* This method exists for the convenience of finding Cobble default NPC classes.
* This uses [getByIdentifier] using the [Cobblemon.MODID] as the namespace and the [name] as the path.
*
* @param name The path of the NPC class asset.
* @return The [NPCClass] if existing.
*/
fun getByName(name: String) = this.getByIdentifier(cobblemonResource(name))
/**
* Finds an [NPCClass] by its unique [Identifier].
*
* @param identifier The unique [NPCClass.resourceIdentifier] of the [NPCClass].
* @return The [NPCClass] if existing.
*/
fun getByIdentifier(identifier: Identifier) = this.npcClassesByIdentifier[identifier]
/**
* Counts the currently loaded NPC classes.
*
* @return The loaded NPC class amount.
*/
fun count() = this.npcClassesByIdentifier.size
/**
* Picks a random [NPCClass].
*
* @throws [NoSuchElementException] if there are no NPC classes loaded.
*
* @return A randomly selected [Species].
*/
fun random(): NPCClass = this.npcClassesByIdentifier.values.random()
override fun reload(data: Map<Identifier, NPCClass>) {
this.npcClassesByIdentifier.clear()
data.forEach { (identifier, species) ->
species.resourceIdentifier = identifier
this.npcClassesByIdentifier[identifier] = species
}
}
override fun sync(player: ServerPlayerEntity) {
NPCRegistrySyncPacket(npcClassesByIdentifier.values.toList()).sendToPlayer(player)
}
}
@@ -1,30 +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
import com.cobblemon.mod.common.api.npc.configuration.NPCBattleConfiguration
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* An NPC configuration is like a snapshot of an NPC setup. It can be taken from an existing NPC and loaded onto
* others. Useful as a backup mechanism or as shorthand for copying an NPC's configuration without building an
* entire class for them.
*
* @author Hiroku
* @since August 16th, 2023
*/
class NPCConfiguration {
var battle: NPCBattleConfiguration? = null
fun applyTo(npc: NPCEntity) {
}
fun loadFrom(npc: NPCEntity) {
}
}
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.npc
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.google.gson.JsonElement
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
/**
* A provider of a party for battling the NPC. Completely custom party providers will only display
* as text labels in any GUIs.
*
* @author Hiroku
* @since August 16th, 2023
*/
interface NPCPartyProvider {
companion object {
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
SimplePartyProvider.TYPE to { SimplePartyProvider() }
)
}
val type: String
fun provide(npc: NPCEntity, challengers: List<ServerPlayerEntity>): PartyStore
fun encode(buffer: PacketByteBuf)
fun decode(buffer: PacketByteBuf)
fun saveToNBT(nbt: NbtCompound)
fun loadFromNBT(nbt: NbtCompound)
fun loadFromJSON(json: JsonElement)
}
@@ -1,18 +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
import net.minecraft.util.Identifier
class NPCPreset {
lateinit var id: Identifier
var aspects: Set<String>? = null
var party: NPCPartyProvider? = null
// var mergeMode = MergeMode.KEEP
}
@@ -1,70 +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.configuration
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
import com.cobblemon.mod.common.util.DataKeys
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
class NPCBattleConfiguration {
var canChallenge = false
var party: NPCPartyProvider? = null
var simultaneousBattles = false
var healAfterwards = true
fun encode(buffer: PacketByteBuf) {
buffer.writeBoolean(canChallenge)
buffer.writeBoolean(simultaneousBattles)
buffer.writeBoolean(healAfterwards)
buffer.writeNullable(party) { _, provider ->
buffer.writeString(provider.type)
provider.encode(buffer)
}
}
fun decode(buffer: PacketByteBuf) {
canChallenge = buffer.readBoolean()
simultaneousBattles = buffer.readBoolean()
healAfterwards = buffer.readBoolean()
party = buffer.readNullable {
val type = buffer.readString()
val providerBuilder = NPCPartyProvider.types[type]
?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
providerBuilder(type)
}
}
fun saveToNBT(nbt: NbtCompound) {
nbt.putBoolean(DataKeys.NPC_CAN_CHALLENGE, canChallenge)
nbt.putBoolean(DataKeys.NPC_SIMULTANEOUS_BATTLES, simultaneousBattles)
nbt.putBoolean(DataKeys.NPC_HEAL_AFTERWARDS, healAfterwards)
val party = party
if (party != null) {
val partyNBT = NbtCompound()
partyNBT.putString(DataKeys.NPC_PARTY_TYPE, party.type)
party.saveToNBT(partyNBT)
nbt.put(DataKeys.NPC_PARTY, partyNBT)
}
}
fun loadFromNBT(nbt: NbtCompound) {
canChallenge = nbt.getBoolean(DataKeys.NPC_CAN_CHALLENGE)
simultaneousBattles = nbt.getBoolean(DataKeys.NPC_SIMULTANEOUS_BATTLES)
healAfterwards = nbt.getBoolean(DataKeys.NPC_HEAL_AFTERWARDS)
val partyNBT = nbt.getCompound(DataKeys.NPC_PARTY)
if (!partyNBT.isEmpty) {
val type = partyNBT.getString(DataKeys.NPC_PARTY_TYPE)
val providerBuilder = NPCPartyProvider.types[type]
?: throw IllegalArgumentException("Failed to load NPC party provider of type: $type")
party = providerBuilder(type).also { it.loadFromNBT(partyNBT) }
}
}
}
@@ -1,32 +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.configuration
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
class NPCBehaviourConfiguration {
var canBeHurt = true
fun encode(buffer: PacketByteBuf) {
}
fun decode(buffer: PacketByteBuf) {
}
fun saveToNBT(nbt: NbtCompound) {
}
fun loadFromNBT(nbt: NbtCompound) {
}
}
@@ -1,22 +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.configuration
import net.minecraft.server.network.ServerPlayerEntity
/**
* A type of interaction handler for when a player right clicks the NPC.
*
* @author Hiroku
* @since August 19th, 2023
*/
interface NPCInteractConfiguration {
val type: String
fun interact(player: ServerPlayerEntity): Boolean
}
@@ -1,77 +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.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.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.network.ServerPlayerEntity
class SimplePartyProvider : NPCPartyProvider {
companion object {
const val TYPE = "simple"
}
@Transient
override val type = TYPE
val pokemon = mutableListOf<PokemonProperties>()
override fun encode(buffer: PacketByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, pokemon.size)
for (pokemon in this.pokemon) {
buffer.writeString(pokemon.originalString)
}
}
override fun decode(buffer: PacketByteBuf) {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
pokemon.add(PokemonProperties.parse(buffer.readString()))
}
}
override fun saveToNBT(nbt: NbtCompound) {
for ((index, pokemon) in this.pokemon.withIndex()) {
nbt.putString(DataKeys.NPC_PARTY_POKEMON + index, pokemon.originalString)
}
}
override fun loadFromNBT(nbt: NbtCompound) {
var index = 0
while (nbt.contains(DataKeys.NPC_PARTY_POKEMON + index)) {
this.pokemon.add(PokemonProperties.parse(nbt.getString(DataKeys.POKEMON_PROPERTIES + index)))
index++
}
}
override fun loadFromJSON(json: JsonElement) {
json as JsonObject
if (json.has(DataKeys.NPC_PARTY_POKEMON.lowercase())) {
json.get(DataKeys.NPC_PARTY_POKEMON.lowercase()).asJsonArray.forEach { pokemon.add(PokemonProperties.parse(it.asString)) }
}
}
override fun provide(npc: NPCEntity, challengers: List<ServerPlayerEntity>): PartyStore {
return NPCPartyStore(npc).apply {
for (properties in pokemon) {
add(properties.create())
}
}
}
}
@@ -70,8 +70,6 @@ object CobblemonPermissions {
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -168,7 +168,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
createDefault("level_ball", CatchRateModifiers.LEVEL)
// ToDo we will need fishing context here once fishing is implemented for a multiplier
createDefault("lure_ball", CatchRateModifiers.LURE)
createDefault("lure_ball", CatchRateModifiers.typeBoosting(2F, ElementalTypes.WATER))
createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
createDefault("love_ball", CatchRateModifiers.LOVE)
createDefault("friend_ball", effects = listOf(CaptureEffects.friendshipSetter(150)))
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.pokeball.catching.modifiers
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -122,18 +121,6 @@ object CatchRateModifiers {
1F
}
/**
* Used by [PokeBalls.LURE_BALL].
* Checks if the entity has an aspect for being spawned from fishing.
* If yes boosts the catch rate by *4
*/
val LURE = WorldStateModifier { _, entity ->
if (FishingSpawnCause.FISHED_ASPECT in entity.aspects)
4f
else
1F
}
/**
* Used by [PokeBalls.HEAVY_BALL].
* The base implementation of the heavy ball modifier mechanics.
@@ -59,9 +59,9 @@ open class PokemonProperties {
val props = PokemonProperties()
props.originalString = string
val keyPairs = string.splitMap(delimiter, assigner)
props.customProperties = CustomPokemonProperty.properties.flatMap { property ->
val matchedKeyPairs = keyPairs.filter { it.first.lowercase() in property.keys }
if (matchedKeyPairs.isEmpty()) {
props.customProperties = CustomPokemonProperty.properties.mapNotNull { property ->
val matchedKeyPair = keyPairs.find { it.first.lowercase() in property.keys }
if (matchedKeyPair == null) {
if (!property.needsKey) {
var savedProperty: CustomPokemonProperty? = null
val keyPair = keyPairs.find { keyPair ->
@@ -71,20 +71,13 @@ open class PokemonProperties {
if (keyPair != null) {
keyPairs.remove(keyPair)
}
return@flatMap savedProperty?.let { listOf(it) } ?: emptyList()
return@mapNotNull savedProperty
} else {
return@flatMap emptyList()
return@mapNotNull null
}
} else {
val properties = mutableListOf<CustomPokemonProperty>()
for ((customKey, customValue) in matchedKeyPairs) {
val property = property.fromString(customValue)
keyPairs.remove(customKey to customValue)
if (property != null) {
properties.add(property)
}
}
return@flatMap properties
keyPairs.remove(matchedKeyPair)
return@mapNotNull property.fromString(matchedKeyPair.second)
}
}.toMutableList()
props.gender = Gender.values().toList().parsePropertyOfCollection(keyPairs, listOf("gender"), labelsOptional = true) { it.name.lowercase() }
@@ -96,7 +96,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect>(Registries::STATUS_EFFECT))
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
@@ -51,7 +51,6 @@ enum class Stats(override val identifier: Identifier, override val displayName:
/** Gets the [Stat] from the respective Showdown id. */
fun getStat(statKey: String) = when(statKey) {
"hp" -> HP
"atk" -> ATTACK
"def" -> DEFENCE
"spa" -> SPECIAL_ATTACK
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.scheduling
import java.util.concurrent.CompletableFuture
/**
* An interface for which an implementation must provide a [SchedulingTracker] so that the standard scheduling
* functions can be executed.
@@ -54,13 +52,5 @@ interface Schedulable {
}
}
fun delayedFuture(seconds: Float): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
after(seconds = seconds) {
future.complete(Unit)
}
return future
}
fun taskBuilder(): ScheduledTask.Builder = ScheduledTask.Builder().tracker(schedulingTracker)
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.scheduling
import java.util.concurrent.CompletableFuture
import com.cobblemon.mod.common.util.runOnServer
@Deprecated("Use afterOnServer or afterOnClient; ambiguous side is not good for your health")
@JvmOverloads
@@ -46,12 +45,4 @@ fun lerp(seconds: Float = 0F, serverThread: Boolean = false, action: (Float) ->
fun lerpOnServer(seconds: Float = 0F, action: (Float) -> Unit) = ServerTaskTracker.lerp(seconds = seconds, action = action)
@JvmOverloads
fun lerpOnClient(seconds: Float = 0F, action: (Float) -> Unit) = ClientTaskTracker.lerp(seconds = seconds, action = action)
fun taskBuilder() = ScheduledTask.Builder()
fun delayedFuture(seconds: Float): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
afterOnServer(seconds = seconds) {
future.complete(Unit)
}
return future
}
fun taskBuilder() = ScheduledTask.Builder()
@@ -1,69 +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.scripting
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.Cobblemon
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.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.endsWith
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object CobblemonScripts : DataRegistry {
const val MOLANG_EXTENSION = ".molang"
override val id = cobblemonResource("molang")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<CobblemonScripts>()
val clientScripts = mutableMapOf<Identifier, ExpressionLike>()
val scripts = mutableMapOf<Identifier, ExpressionLike>()
override fun reload(manager: ResourceManager) {
manager.findResources("molang") { path -> path.endsWith(MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.inputStream.use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
try {
val expression = reader.readText().asExpressionLike()
if (identifier.path.startsWith("molang/client/")) {
clientScripts[resolvedIdentifier] = expression
} else {
scripts[resolvedIdentifier] = expression
}
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script: $identifier", exception)
}
}
}
}
Cobblemon.LOGGER.info("Loaded ${scripts.size} server scripts and ${clientScripts.size} client scripts")
observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(ScriptRegistrySyncPacket(clientScripts.entries))
}
fun run(identifier: Identifier, runtime: MoLangRuntime): MoValue? {
return scripts[identifier]?.resolve(runtime)
}
}
@@ -12,13 +12,11 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.FishingSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
@@ -31,11 +29,9 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawnin
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SubmergedSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
@@ -87,7 +83,6 @@ object BestSpawner {
var config = BestSpawnerConfig()
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
fun loadConfig() {
LOGGER.info("Starting the Best Spawner...")
@@ -96,7 +91,6 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -110,12 +104,10 @@ object BestSpawner {
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
SpawnDetail.registerSpawnType(name = PokemonSpawnDetail.TYPE, PokemonSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
config = BestSpawnerConfig.load()
@@ -126,6 +118,5 @@ object BestSpawner {
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.spawning
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
@@ -25,7 +24,6 @@ import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawningCo
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.SpawnDetailSelectorAdapter
import com.cobblemon.mod.common.util.adapters.SpawnRuleComponentAdapter
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
@@ -52,7 +50,6 @@ object CobblemonSpawnRules : JsonDataRegistry<SpawnRule> {
.registerTypeAdapter(SpawningContextSelector::class.java, SpawningContextSelectorAdapter)
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(Text::class.java, TextAdapter)
.create()
@@ -8,22 +8,11 @@
package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.util.server
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityType
open class SpawnCause(
val spawner: Spawner,
val bucket: SpawnBucket,
entity: Entity? = null
): SpawningInfluence {
val entityWorldId = entity?.world?.registryKey
val entityId = entity?.id
val entityUUID = entity?.uuid
val entityType = entity?.type
val entity: Entity?
get() = if (entityType == EntityType.PLAYER) server()?.playerManager?.getPlayer(entityUUID) else server()?.getWorld(entityWorldId)?.getEntityById(entityId!!)
}
val entity: Entity? = null
)
@@ -1,44 +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.spawning.condition
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.block.Block
import net.minecraft.item.Item
/**
* A spawning condition that applies to [FishingSpawningContext]s.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
override fun contextClass() = FishingSpawningContext::class.java
var rod: RegistryLikeCondition<Item>? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: FishingSpawningContext): Boolean {
if (!super.fits(ctx)) {
return false
} else if (rod != null && !rod!!.fits(ctx.rodItem ?: return false, ctx.world.itemRegistry)) {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
return false
} else {
return true
}
}
companion object {
const val NAME = "fishing"
}
}
@@ -11,17 +11,12 @@ package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.util.Merger
import com.cobblemon.mod.common.util.math.orMax
import com.cobblemon.mod.common.util.math.orMin
import com.mojang.datafixers.util.Either
import net.minecraft.enchantment.EnchantmentHelper
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.world.biome.Biome
@@ -61,10 +56,6 @@ abstract class SpawningCondition<T : SpawningContext> {
var isThundering: Boolean? = null
var timeRange: TimeRange? = null
var structures: MutableList<Either<Identifier, TagKey<Structure>>>? = null
var minLureLevel: Int? = null
var maxLureLevel: Int? = null
var bait: String? = null
var rodType: String? = null
@Transient
var appendages = mutableListOf<AppendageCondition>()
@@ -117,34 +108,8 @@ 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
if (EnchantmentHelper.getLure(pokerodStack) < minLureLevel!!)
return false
if (maxLureLevel != null && EnchantmentHelper.getLure(pokerodStack) > maxLureLevel!!)
return false
} else if (bait != null && ctx is FishingSpawningContext) { // check for the bait on the bobber
val pokerodBait = (ctx as FishingSpawningContext).rodBait
if (Registries.ITEM.getId(PokerodItem.getBait(pokerodBait)?.item).path != 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?.path != rodType)
return false
}
/*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
}
@@ -56,10 +56,9 @@ interface AreaContextResolver {
if (context != null) {
contexts.add(context)
// The position BlockPos has been used in a context, editing the same one
// will cause entities to spawn at the wrong location (buried in walls, usually).
// I made it so that built-in context calculators explicitly take a copy of the
// BlockPos but it'd still be exposed in custom contexts so fixing it here too so
// custom context calculators don't have to remember to do it. - Hiroku
// will cause entities to spawn at the wrong location (buried in walls, usually)
// I made it so that our context calculators specifically take a copy of the
// BlockPos but it'd still be exposed in custom contexts so fixing it here too.
pos = BlockPos.Mutable(1, 2, 3)
input.position = pos
}
@@ -77,51 +76,4 @@ interface AreaContextResolver {
return contexts
}
// fun resolveFishing(
// spawner: Spawner,
// contextCalculators: List<AreaSpawningContextCalculator<*>>,
// slice: WorldSlice
// ): List<AreaSpawningContext> {
// var pos = BlockPos.Mutable(1, 2, 3)
// val input = AreaSpawningInput(spawner, pos, slice)
// val contexts = mutableListOf<AreaSpawningContext>()
//
// var x = slice.baseX
// var y = slice.baseY
// var z = slice.baseZ
//
// while (x < slice.baseX + slice.length) {
// while (y < slice.baseY + slice.height) {
// while (z < slice.baseZ + slice.width) {
// pos.set(x, y, z)
// val vec = pos.toVec3d()
// if (slice.nearbyEntityPositions.none { it.isInRange(vec, 50.0) && it != slice.cause.entity }) {
// val fittedContextCalculator = contextCalculators
// .firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
// if (fittedContextCalculator != null) {
// val context = fittedContextCalculator.calculate(input)
// if (context != null) {
// contexts.add(context)
// // The position BlockPos has been used in a context, editing the same one
// // will cause entities to spawn at the wrong location (buried in walls, usually)
// // I made it so that our context calculators specifically take a copy of the
// // BlockPos but it'd still be exposed in custom contexts so fixing it here too.
// pos = BlockPos.Mutable(1, 2, 3)
// input.position = pos
// }
// }
// }
// z++
// }
// y++
// z = slice.baseZ
// }
// x++
// y = slice.baseY
// z = slice.baseZ
// }
//
// return contexts
// }
}
@@ -1,45 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.util.getBlockStates
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
/**
* A spawning context that was generated for a fishing action.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningContext(
cause: FishingSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
val nearbyBlocks = world.getBlockStates(Box.of(pos.toVec3d(), 10.0, 10.0, 10.0))
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.mapNotNull { it.block }.distinct() }
val rodStack = cause.rodStack
val rodItem = cause.rodItem
val rodBait = cause.bait
}
@@ -68,7 +68,7 @@ abstract class SpawningContext {
abstract val skyLight: Int
/** Whether or not the sky is visible at this location. */
abstract val canSeeSky: Boolean
/** A list of [SpawningInfluence]s that apply due to this specific context. This generally shouldn't be consumed; use [getAllInfluences].*/
/** A list of [SpawningInfluence]s that apply due to this specific context. */
abstract val influences: MutableList<SpawningInfluence>
/** Gets a cache of structures by block coordinates, grouped by chunk. */
abstract fun getStructureCache(pos: BlockPos): StructureChunkCache
@@ -147,7 +147,7 @@ abstract class SpawningContext {
*/
open fun preFilter(detail: SpawnDetail): Boolean {
/** Returns true if none of the influences.affectSpawnable return false */
return !anyForInfluences { !it.affectSpawnable(detail, this) }
return influences.none { !it.affectSpawnable(detail, this) }
}
/**
@@ -157,12 +157,14 @@ abstract class SpawningContext {
open fun postFilter(detail: SpawnDetail): Boolean = true
open fun afterSpawn(entity: Entity) {
applyInfluences { it.affectSpawn(entity) }
influences.forEach { it.affectSpawn(entity) }
}
open fun getWeight(detail: SpawnDetail): Float {
var weight = detail.weight
applyInfluences(extraInfluences = detail.weightMultipliers) { weight = it.affectWeight(detail, this, weight) }
for (influence in influences + detail.weightMultipliers) {
weight = influence.affectWeight(detail, this, weight)
}
return weight
}
@@ -182,34 +184,4 @@ abstract class SpawningContext {
structCompiled = true
return struct
}
/**
* Gets all influences that apply to this context, including the cause, and does something with them.
*
* Technically we could simple make a list of influences + cause and run that, but influences MUST be able
* to run extremely frequently in performance-critical code, so we don't want to have to allocate a list.
*/
fun applyInfluences(extraInfluences: List<SpawningInfluence>? = null, usage: (SpawningInfluence) -> Unit) {
influences.forEach(usage)
usage(cause)
extraInfluences?.forEach(usage)
}
/**
* Iterates through every operable influence to find if any match the predicate.
*/
fun anyForInfluences(extraInfluences: List<SpawningInfluence>? = null, usage: (SpawningInfluence) -> Boolean): Boolean {
if (influences.any(usage)) {
return true
}
if (usage(cause)) {
return true
}
extraInfluences?.forEach {
if (usage(it)) {
return true
}
}
return false
}
}
@@ -1,34 +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.spawning.context
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawning context relating to triggered spawning. Uses a single-point structure cache which is
* the only contribution of this implementation.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class TriggerSpawningContext(
override val cause: SpawnCause,
override val world: ServerWorld,
override val position: BlockPos,
override val light: Int,
override val skyLight: Int,
override val canSeeSky: Boolean,
override val influences: MutableList<SpawningInfluence>
) : SpawningContext() {
val triggerStructureCache = StructureChunkCache()
override fun getStructureCache(pos: BlockPos) = triggerStructureCache
}
@@ -1,27 +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.spawning.detail
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.npc.NPCEntity
/**
* A [SpawnAction] for creating [NPCEntity]s.
*
* @author Hiroku
* @since October 8th, 2023
*/
class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SpawnAction<NPCEntity>(ctx, detail) {
override fun run(): NPCEntity {
val npc = NPCEntity(ctx.world)
npc.npc = detail.npcClass
npc.appliedAspects.addAll(detail.aspects)
return npc
}
}
@@ -1,37 +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.spawning.detail
import com.cobblemon.mod.common.api.npc.NPCClass
import com.cobblemon.mod.common.api.npc.NPCConfiguration
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.google.gson.annotations.SerializedName
/**
* A [SpawnDetail] describing an [NPCEntity] spawn.
*
* @author Hiroku
* @since October 8th, 2023
*/
class NPCSpawnDetail : SpawnDetail() {
companion object {
val TYPE = "npc"
val blankClass = NPCClass()
}
override val type = TYPE
@SerializedName(value = "npcClass", alternate = ["class", "npc"])
val npcClass: NPCClass = blankClass
val configuration: NPCConfiguration? = null
val aspects: Set<String> = emptySet()
override fun doSpawn(ctx: SpawningContext) = NPCSpawnAction(ctx, this)
}
@@ -45,7 +45,6 @@ class PokemonSpawnAction(
null
}?.createStack(ctx)
val entity = props.createEntity(ctx.world)
entity.spawnCause = ctx.cause
SeasonFeatureHandler.updateSeason(entity.pokemon, Cobblemon.seasonResolver(ctx.world, ctx.position))
if (heldItem != null) {
entity.pokemon.swapHeldItem(heldItem)
@@ -34,19 +34,15 @@ abstract class SpawnAction<R>(
/**
* Runs the spawn action and processes any non-null result.
*/
open fun complete(): R? {
if (future.isDone) {
return null
}
ctx.applyInfluences { it.affectAction(this) }
open fun complete(): Boolean {
ctx.influences.forEach { it.affectAction(this) }
val result = run()
if (result != null) {
return if (result != null) {
future.complete(result)
true
} else {
future.completeExceptionally(Exception("Nothing was spawned."))
false
}
return result
}
}
@@ -1,71 +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.spawning.fishing
import com.cobblemon.mod.common.api.fishing.FishingBait
import com.cobblemon.mod.common.api.fishing.FishingBaits
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.pokemon.Species
import net.minecraft.entity.Entity
import net.minecraft.item.FishingRodItem
import net.minecraft.item.ItemStack
/**
* A spawning cause that is embellished with fishing information. Could probably also
* have the bobber entity or something.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?,
val rodStack: ItemStack
) : SpawnCause(spawner, bucket, entity) {
companion object {
const val FISHED_ASPECT = "fished"
}
val rodItem = rodStack.item as? PokerodItem
val bait = PokerodItem.getBait(rodStack)
override fun affectSpawn(entity: Entity) {
super.affectSpawn(entity)
if (entity is PokemonEntity) {
entity.pokemon.forcedAspects += FISHED_ASPECT
}
}
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
if (!ItemStack.areItemsEqual(ItemStack.EMPTY, bait) && bait != null ){
val bait = FishingBaits.getFromItemStack(bait)
if (detail is PokemonSpawnDetail) {
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
val baitEVEffect = bait!!.effects.firstOrNull() { it.type == FishingBait.Effects.EV && detailSpecies?.evYield?.get(Stats.getStat(it.subcategory?.path.toString()))!! > 0 }
if(detailSpecies != null && baitEVEffect != null) {
return super.affectWeight(detail, ctx, weight * baitEVEffect.value.toFloat())
}
}
}
return super.affectWeight(detail, ctx, weight)
}
}
@@ -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.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawner that takes in a [FishingSpawnCause] and spawns things.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class FishingSpawner(
override val name: String = "fishing",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FishingSpawnCause>(name, pool) {
override fun parseContext(cause: FishingSpawnCause, world: ServerWorld, pos: BlockPos): FishingSpawningContext? {
// Maybe confirm that it's water we're fishing in?
val ctx = FishingSpawningContext(
cause = cause,
world = world,
pos = pos,
influences = influences
)
return ctx
}
}
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
@@ -50,7 +49,7 @@ class LocationRuleCalculator : SpawnRuleComponent {
@Transient
private lateinit var reusableDimensionTypeValue: ObjectValue<RegistryEntry<DimensionType>>
val allow = "true".asExpressionLike()
val allow = "true".asExpression()
override fun isAllowedPosition(
world: ServerWorld,
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.api.spawning.rules.selector.AllSpawningContextSe
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveFloat
/**
@@ -31,7 +30,7 @@ import com.cobblemon.mod.common.util.resolveFloat
class WeightTweakRuleComponent : SpawnRuleComponent {
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val weight = "v.weight".asExpressionLike()
val weight: Expression = "v.weight".asExpression()
@Transient
val runtime = MoLangRuntime().setup()
@@ -12,13 +12,12 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
class ExpressionSpawnDetailSelector : SpawnDetailSelector {
@Transient
val runtime = MoLangRuntime().setup()
var expression = "true".asExpressionLike()
var expression = "true".asExpression()
override fun selects(spawnDetail: SpawnDetail): Boolean {
runtime.environment.setSimpleVariable("spawn", spawnDetail.struct)
@@ -12,13 +12,12 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
class ExpressionSpawningContextSelector : SpawningContextSelector {
@Transient
val runtime = MoLangRuntime().setup()
var expression = "true".asExpressionLike()
var expression = "true".asExpression()
override fun selects(ctx: SpawningContext): Boolean {
runtime.environment.setSimpleVariable("context", ctx.getOrSetupStruct())
@@ -1,51 +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.spawning.spawner
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.selection.FlatContextWeightedSelector
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A type of spawner that occurs at a single point and provides its own means of generating a
* context from a particular cause and position. The trigger spawner must be directly called to
* spawn things (almost as if it was being triggered) as opposed to being scheduled on a ticker.
*
* @author Hiroku
* @since February 3rd, 2024
*/
abstract class TriggerSpawner<T : SpawnCause>(override val name: String, var spawns: SpawnPool) : Spawner {
private var selector: SpawningSelector = FlatContextWeightedSelector()
override val influences = mutableListOf<SpawningInfluence>()
open fun run(cause: T, world: ServerWorld, pos: BlockPos): CompletableFuture<*>? {
val context = parseContext(cause, world, pos) ?: return null
selector.select(this, listOf(context))?.let { (_, spawn) ->
val action = spawn.doSpawn(context)
action.complete()
return action.future
}
return null
}
/** Parses a context, if possible, from the given cause and position. */
abstract fun parseContext(cause: T, world: ServerWorld, pos: BlockPos): SpawningContext?
override fun canSpawn() = true
override fun getSpawningSelector() = selector
override fun setSpawningSelector(selector: SpawningSelector) { this.selector = selector }
override fun getSpawnPool() = spawns
override fun setSpawnPool(spawnPool: SpawnPool) { spawns = spawnPool }
}
@@ -1,15 +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.storage.party
import com.cobblemon.mod.common.entity.npc.NPCEntity
class NPCPartyStore(val npc: NPCEntity) : PartyStore(npc.uuid) {
val isLiving: Boolean get() = npc.isLiving
}
@@ -99,7 +99,7 @@ open class PCStore(
this.lockedSize = lockNewSize
if (boxes.size > newSize) {
// reduce
val slicedBoxes = boxes.slice(newSize until boxes.size)
val slicedBoxes = boxes.slice(boxes.size until newSize)
boxes.removeAll(slicedBoxes)
slicedBoxes.flatMap { it.asIterable() }.forEach(overflowHandler)
} else {
@@ -9,20 +9,19 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getBattleTheme
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.update
import java.util.Optional
import java.util.UUID
import net.minecraft.entity.Entity
import net.minecraft.server.network.ServerPlayerEntity
@@ -64,7 +63,6 @@ object BattleBuilder {
}
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(player1Actor),
@@ -72,9 +70,7 @@ object BattleBuilder {
).ifSuccessful {
player1Actor.battleTheme = player2.getBattleTheme()
player2Actor.battleTheme = player1.getBattleTheme()
result = SuccessfulBattleStart(it)
}
result
} else {
errors
}
@@ -133,7 +129,6 @@ object BattleBuilder {
}
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActor),
@@ -143,72 +138,7 @@ object BattleBuilder {
pokemonEntity.battleId = it.battleId
}
playerActor.battleTheme = pokemonEntity.getBattleTheme()
result = SuccessfulBattleStart(it)
}
result
} else {
errors
}
}
/**
* Attempts to create a PvE battle against the given Pokémon.
*
* @param player The player battling the wild Pokémon.
* @param pokemonEntity The Pokémon to battle.
* @param leadingPokemon The Pokémon in the player's party to send out first. If null, it uses the first in the party.
* @param battleFormat The format to use for the battle. By default it is [BattleFormat.GEN_9_SINGLES].
* @param cloneParties Whether the player's party should be cloned so that damage will not affect their party afterwards. Defaults to false.
* @param healFirst Whether the player's Pokémon should be healed before the battle starts. Defaults to false.
* @param fleeDistance How far away the player must get to flee the Pokémon. If the value is -1, it cannot be fled.
* @param party The party of the player to use for the battle. This does not need to be their actual party. Defaults to it though.
*/
@JvmOverloads
fun pvn(
player: ServerPlayerEntity,
npcEntity: NPCEntity,
leadingPokemon: UUID? = null,
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
cloneParties: Boolean = false,
healFirst: Boolean = false,
party: PartyStore = player.party()
): BattleStartResult {
val playerTeam = party.toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemon = leadingPokemon)
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
val party = npcEntity.getBattleConfiguration().party?.provide(npcEntity, listOf(player)) ?: TODO("Deal with this issue!")
val npcActor = NPCBattleActor(npcEntity, party)
val errors = ErroredBattleStart()
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
player = player,
requiredCount = battleFormat.battleType.slotsPerActor,
hadCount = playerActor.pokemonList.size
)
}
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
errors.participantErrors[playerActor] += BattleStartError.alreadyInBattle(playerActor)
}
// if (npcEntity.battleIds.get().isPresent) {
// errors.participantErrors[npcActor] += BattleStartError.alreadyInBattle(npcActor)
// }
return if (errors.isEmpty) {
var result: BattleStartResult = errors
BattleRegistry.startBattle(
battleFormat = battleFormat,
side1 = BattleSide(playerActor),
side2 = BattleSide(npcActor)
).ifSuccessful { battle ->
// TODO NPC battle themes
// playerActor.battleTheme = pokemonEntity.getBattleTheme()
npcEntity.dataTracker.update(NPCEntity.BATTLE_IDS) { it + battle.battleId }
result = SuccessfulBattleStart(battle)
}
result
} else {
errors
}
@@ -11,17 +11,13 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.entity.PokemonSender
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.net.messages.client.battle.BattleFaintPacket
import com.cobblemon.mod.common.util.battleLang
import java.util.concurrent.CompletableFuture
/**
* Format: |faint|POKEMON
@@ -36,28 +32,18 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
override fun invoke(battle: PokemonBattle) {
battle.dispatchFuture {
val (pnx, _) = message.pnxAndUuid(0) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchFuture CompletableFuture.completedFuture(Unit)
battle.dispatchWaiting(waitTime) {
val (pnx, _) = message.pnxAndUuid(0) ?: return@dispatchWaiting
battle.sendUpdate(BattleFaintPacket(pnx))
val actor = pokemon.actor
pokemon.effectedPokemon.currentHealth = 0
val preamble = if (actor is EntityBackedBattleActor<*>) {
(actor.entity as? PokemonSender)?.let { sender -> pokemon.entity?.recallWithAnimation()}
} else {
null
} ?: delayedFuture(seconds = waitTime)
faintingPokemon.effectedPokemon.currentHealth = 0
faintingPokemon.sendUpdate()
val context = ShowdownInterpreter.getContextFromFaint(faintingPokemon, battle)
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, faintingPokemon, context))
preamble.thenAccept {
faintingPokemon.effectedPokemon.currentHealth = 0
faintingPokemon.sendUpdate()
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context))
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
faintingPokemon.contextManager.add(context)
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
battle.majorBattleActions[faintingPokemon.uuid] = message
}
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
faintingPokemon.contextManager.add(context)
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
battle.majorBattleActions[faintingPokemon.uuid] = message
}
battle.dispatchWaiting(0.5F) {
val faintMessage = battleLang("fainted", faintingPokemon.getName()).red()
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.interpreter.Effect
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.Moves
@@ -29,11 +29,17 @@ class FossilAnalyzerBlockEntity(
class FossilAnalyzerInventory(val analyzerEntity: FossilAnalyzerBlockEntity) : SidedInventory {
override fun clear() {
TODO("Not yet implemented")
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
fossilMultiblockStructure.fossilInventory.clear()
}
}
override fun size(): Int {
TODO("Not yet implemented")
if (analyzerEntity.multiblockStructure != null) {
return 3
}
return 0
}
override fun isEmpty(): Boolean {
@@ -41,19 +47,37 @@ class FossilAnalyzerBlockEntity(
}
override fun getStack(slot: Int): ItemStack {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory[slot]
}
}
return ItemStack.EMPTY
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory[slot]
}
}
return ItemStack.EMPTY
}
override fun removeStack(slot: Int): ItemStack {
if(analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory.removeAt(slot)
}
}
return ItemStack.EMPTY
}
override fun setStack(slot: Int, stack: ItemStack) {
if (analyzerEntity.multiblockStructure != null) {
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val struct = analyzerEntity.multiblockStructure as FossilMultiblockStructure
analyzerEntity.world?.let {
struct.insertFossil(stack, it)
@@ -72,6 +96,9 @@ class FossilAnalyzerBlockEntity(
}
override fun getAvailableSlots(side: Direction?): IntArray {
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
return intArrayOf(0,1,2)
}
return IntArray(1)
}
@@ -14,7 +14,6 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.client.MinecraftClient
import net.minecraft.client.sound.PositionedSoundInstance
@@ -32,13 +31,6 @@ object ClientMoLangFunctions {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, pitch))
}
},
"is_time" to java.util.function.Function { params ->
val time = (MinecraftClient.getInstance().world?.timeOfDay ?: 0) % 24000
val min = params.getInt(0)
val max = params.getInt(1)
time in min..max
},
"say" to java.util.function.Function { params -> MinecraftClient.getInstance().player?.sendMessage(params.getString(0).text()) ?: Unit },
)
fun MoLangRuntime.setupClient(): MoLangRuntime {
@@ -10,12 +10,12 @@ package com.cobblemon.mod.common.client
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonClientImplementation
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.client.battle.ClientBattle
@@ -23,13 +23,12 @@ import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
@@ -37,13 +36,8 @@ import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
import com.cobblemon.mod.common.client.sound.battle.BattleMusicController
@@ -53,6 +47,9 @@ import com.cobblemon.mod.common.client.trade.ClientTrade
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asTranslated
@@ -267,14 +264,8 @@ object CobblemonClient {
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
LOGGER.info("Registering Boat with Chest renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
LOGGER.info("Registering Generic Bedrock renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
LOGGER.info("Registering NPC renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.NPC, ::NPCRenderer)
LOGGER.info("Registering Generic Bedrock Entity renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
LOGGER.info("Registering PokeRod Bobber renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.POKE_BOBBER) { ctx -> PokeBobberEntityRenderer(ctx) }
}
fun reloadCodedAssets(resourceManager: ResourceManager) {
@@ -282,11 +273,10 @@ object CobblemonClient {
BedrockParticleEffectRepository.loadEffects(resourceManager)
BedrockAnimationRepository.loadAnimations(
resourceManager = resourceManager,
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories + NPCModelRepository.animationDirectories
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories
)
PokemonModelRepository.reload(resourceManager)
PokeBallModelRepository.reload(resourceManager)
NPCModelRepository.reload(resourceManager)
BerryModelRepository.reload(resourceManager)
FossilModelRepository.reload(resourceManager)
BlockEntityModelRepository.reload(resourceManager)
@@ -40,7 +40,11 @@ class EmptyPokeBallClientDelegate : PokeBallPoseableState(), EntitySideDelegate<
this.currentEntity = entity
age = entity.age
initSubscriptions()
this.runtime.environment.getQueryStruct().addFunctions(getEntity().struct.functions)
this.runtime.environment.getQueryStruct().addFunctions(mapOf(
"pokeball_type" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokeBall.name.toString())
}
))
}
override fun tick(entity: EmptyPokeBallEntity) {
@@ -1,35 +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.entity
import com.cobblemon.mod.common.api.entity.NPCSideDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.entity.npc.NPCEntity
class NPCClientDelegate : PoseableEntityState<NPCEntity>(), NPCSideDelegate {
lateinit var npcEntity: NPCEntity
override val schedulingTracker
get() = npcEntity.schedulingTracker
override fun initialize(entity: NPCEntity) {
this.npcEntity = entity
this.age = entity.age
}
override fun tick(entity: NPCEntity) {
super.tick(entity)
updateLocatorPosition(entity.pos)
incrementAge(entity)
}
override fun getEntity() = npcEntity
override fun updatePartialTicks(partialTicks: Float) {
this.currentPartialTicks = partialTicks
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.entity
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
@@ -19,7 +18,6 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.scheduling.afterOnClient
import com.cobblemon.mod.common.api.scheduling.lerpOnClient
import com.cobblemon.mod.common.client.ClientMoLangFunctions
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
@@ -52,7 +50,6 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
override val schedulingTracker: SchedulingTracker
get() = currentEntity.schedulingTracker
lateinit var currentEntity: PokemonEntity
var phaseTarget: Entity? = null
var entityScaleModifier = 1F
@@ -120,7 +117,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
currentEntity.isInvisible = true
ballDone = false
var soundPos = currentEntity.pos
currentEntity.ownerUuid?.let{
currentEntity.pokemon.getOwnerUUID()?.let{
currentEntity.world.getPlayerByUuid(it)?.let {
val offset = it.pos.subtract(currentEntity.pos.add(0.0, 2.0 - (ballOffset.toDouble()/10f), 0.0)).normalize().multiply(-ease(ballOffset.toDouble()))
with(it.pos.subtract(currentEntity.pos)) {
@@ -134,7 +131,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
val client = MinecraftClient.getInstance()
val sound = MovingSoundInstance(SoundEvent.of(CobblemonSounds.POKE_BALL_TRAIL.id), SoundCategory.PLAYERS, { sendOutPosition?.add(sendOutOffset) }, 0.1f, 1f, false, 20, 0)
if (!playedThrowingSound){
if(!playedThrowingSound){
client.soundManager.play(sound)
playedThrowingSound = true
}
@@ -148,31 +145,33 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
}
currentEntity.ownerUuid?.let {
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
/// create end rod particles in a 0.1 radius around the soundPos with a count of 50 and a random velocity of 0.1
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect -> ParticleStorm(effect, wrapper, world).spawn() }
ballsendsparkle?.let { effect -> ParticleStorm(effect, wrapper, world).spawn() }
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect -> ParticleStorm(effect, wrapper, world).spawn() }
// Display ball-specific particles
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.lowercase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
ballsendsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
}
}
@@ -251,13 +250,6 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
pokemon.isClient = true
}
override fun addToStruct(struct: QueryStruct) {
super.addToStruct(struct)
struct.addFunctions(functions.functions)
struct.addFunctions(ClientMoLangFunctions.clientFunctions)
runtime.environment.structs["query"] = struct
}
override fun initialize(entity: PokemonEntity) {
this.currentEntity = entity
this.age = entity.age
@@ -280,9 +272,6 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
},
"weight" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokemon.species.weight.toDouble())
},
"friendship" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokemon.friendship.toDouble())
}
))
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPoseablePortrait
import com.cobblemon.mod.common.api.gui.drawPortraitPokemon
import com.cobblemon.mod.common.api.text.darkGray
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
@@ -23,7 +23,6 @@ import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
@@ -149,14 +148,7 @@ class PartyOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.ge
0.0
)
drawPoseablePortrait(
identifier = pokemon.species.resourceIdentifier,
aspects = pokemon.aspects,
matrixStack = matrices,
partialTicks = partialDeltaTicks,
contextScale = pokemon.form.baseScale,
repository = PokemonModelRepository
)
drawPortraitPokemon(pokemon.species, pokemon.aspects, matrices, partialTicks = partialDeltaTicks)
matrices.pop()
context.disableScissor()
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.gui.battle
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.gui.drawPoseablePortrait
import com.cobblemon.mod.common.api.gui.drawPortraitPokemon
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.text.bold
@@ -25,7 +25,6 @@ import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
@@ -239,13 +238,11 @@ class BattleOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.g
)
} else {
matrixStack.push()
drawPoseablePortrait(
identifier = species.resourceIdentifier,
drawPortraitPokemon(
species = species,
aspects = aspects,
matrixStack = matrixStack,
scale = 18F * (ballState?.scale ?: 1F),
contextScale = species.getForm(aspects).baseScale,
repository = PokemonModelRepository,
reversed = reversed,
state = state,
partialTicks = partialTicks
@@ -28,6 +28,7 @@ import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueDTO
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asExpressions
import com.cobblemon.mod.common.util.asTranslated
@@ -115,7 +116,7 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
.associate { it.key to it.value }
)
dialogueDTO.currentPageDTO.clientActions.forEach { runtime.resolve(it.asExpressionLike()) }
dialogueDTO.currentPageDTO.clientActions.forEach { runtime.resolve(it.asExpression()) }
val centerX = scaledWidth / 2F
val boxMinY = (scaledHeight / 2F - BOX_HEIGHT / 2F) - 10
val boxMaxY = boxMinY + BOX_HEIGHT
@@ -9,17 +9,12 @@
package com.cobblemon.mod.common.client.gui.dialogue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.drawPoseablePortrait
import com.cobblemon.mod.common.api.gui.drawPortraitPokemon
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.npc.NPCFloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonFloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.entity.npc.NPCEntity
import java.util.UUID
import kotlin.math.atan
import net.minecraft.client.MinecraftClient
@@ -44,8 +39,8 @@ class PlayerRenderableFace(val playerId: UUID) : RenderableFace {
// All of the maths below is shamelessly stolen from InventoryScreen.drawEntity.
// the -20 and 5 divided by 40 are for configuring the yaw and pitch tilt of the body and head respectively.
// For more information, pray for divine inspiration or something idk.
val f = atan((-40 / 40.0f).toDouble()).toFloat()
val g = atan((-10 / 40.0f).toDouble()).toFloat()
val f = atan((-20 / 40.0f).toDouble()).toFloat()
val g = atan((5 / 40.0f).toDouble()).toFloat()
val quaternionf = Quaternionf().rotateZ(Math.PI.toFloat())
val quaternionf2 = Quaternionf().rotateX(g * 20.0f * (Math.PI.toFloat() / 180))
quaternionf.mul(quaternionf2)
@@ -55,14 +50,14 @@ class PlayerRenderableFace(val playerId: UUID) : RenderableFace {
val oldPrevHeadYaw = entity.prevHeadYaw
val oldHeadYaw = entity.headYaw
// Modifies the entity for rendering based on our f and g values
entity.bodyYaw = 180.0F + f * 20.0F
entity.setYaw(180.0F + f * 40.0F)
entity.setPitch(0F)
entity.bodyYaw = 180.0f + f * 20.0f
entity.setYaw(180.0f + f * 40.0f)
entity.setPitch(-g * 20.0f)
entity.headYaw = entity.yaw
entity.prevHeadYaw = entity.yaw
val size = 34
val size = 37
val xOffset = 0
val yOffset = 72
val yOffset = 75
InventoryScreen.drawEntity(drawContext, xOffset, yOffset, size, quaternionf, quaternionf2, entity)
// Resets the entity
entity.bodyYaw = oldBodyYaw
@@ -73,36 +68,18 @@ class PlayerRenderableFace(val playerId: UUID) : RenderableFace {
}
}
class ReferenceRenderableFace(val entity: Poseable): RenderableFace {
class ReferenceRenderableFace(entity: Poseable): RenderableFace {
val state = entity.delegate as PoseableEntityState<*>
override fun render(drawContext: DrawContext, partialTicks: Float) {
val state = this.state
if (state is PokemonClientDelegate) {
drawPoseablePortrait(
identifier = state.currentEntity.pokemon.species.resourceIdentifier,
drawPortraitPokemon(
species = state.currentEntity.pokemon.species,
aspects = state.currentEntity.pokemon.aspects,
repository = PokemonModelRepository,
contextScale = state.currentEntity.pokemon.form.baseScale,
matrixStack = drawContext.matrices,
state = state,
partialTicks = 0F // It's already being rendered potentially so we don't need to tick the state.
)
} else if (state is NPCClientDelegate) {
entity as NPCEntity
val limbSwing = entity.limbAnimator.getPos(partialTicks)
val limbSwingAmount = entity.limbAnimator.getSpeed(partialTicks)
drawPoseablePortrait(
identifier = state.npcEntity.npc.resourceIdentifier,
aspects = state.npcEntity.aspects,
repository = NPCModelRepository,
matrixStack = drawContext.matrices,
state = state,
partialTicks = 0F, // It's already being rendered potentially so we don't need to tick the state.
limbSwing = limbSwing,
limbSwingAmount = limbSwingAmount,
ageInTicks = entity.age.toFloat(),
)
}
}
}
@@ -112,10 +89,12 @@ class ArtificialRenderableFace(
val identifier: Identifier,
val aspects: Set<String>
): RenderableFace {
val species = PokemonSpecies.getByIdentifier(identifier) ?: run {
Cobblemon.LOGGER.error("Unable to find species for $identifier for a dialogue face. Defaulting to first species.")
PokemonSpecies.species.first()
}
val state: PoseableEntityState<*> = if (modelType == "pokemon") {
PokemonFloatingState()
} else if (modelType == "npc") {
NPCFloatingState()
} else {
throw IllegalArgumentException("Unknown model type: $modelType")
}
@@ -123,26 +102,11 @@ class ArtificialRenderableFace(
override fun render(drawContext: DrawContext, partialTicks: Float) {
val state = this.state
if (state is PokemonFloatingState) {
val species = PokemonSpecies.getByIdentifier(identifier) ?: run {
Cobblemon.LOGGER.error("Unable to find species for $identifier for a dialogue face. Defaulting to first species.")
PokemonSpecies.species.first()
}
drawPoseablePortrait(
identifier = species.resourceIdentifier,
aspects = aspects,
matrixStack = drawContext.matrices,
contextScale = species.getForm(aspects).baseScale,
state = state,
repository = PokemonModelRepository,
partialTicks = partialTicks
)
} else if (state is NPCFloatingState) {
drawPoseablePortrait(
identifier = identifier,
drawPortraitPokemon(
species = species,
aspects = aspects,
matrixStack = drawContext.matrices,
state = state,
repository = NPCModelRepository,
partialTicks = partialTicks
)
}

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