Compare commits

...
7 changed files with 96 additions and 36 deletions
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.client.pokedex.PokedexType import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.* import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
@@ -42,6 +43,7 @@ import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
import com.cobblemon.mod.common.platform.PlatformRegistry import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.IVs import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry import net.minecraft.core.Registry
@@ -50,6 +52,7 @@ import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceKey import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.effect.MobEffectInstance import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.LivingEntity
@@ -66,6 +69,7 @@ import net.minecraft.world.item.SignItem
import net.minecraft.world.item.SmithingTemplateItem import net.minecraft.world.item.SmithingTemplateItem
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import kotlin.math.ceil
@Suppress("unused", "SameParameterValue") @Suppress("unused", "SameParameterValue")
object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<Item>>, Item>() { object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<Item>>, Item>() {
@@ -614,7 +618,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
// Medicine // Medicine
@JvmField @JvmField
val RARE_CANDY = candyItem("rare_candy", Rarity.RARE) { _, pokemon -> pokemon.getExperienceToNextLevel() } val RARE_CANDY = candyItem("rare_candy", Rarity.RARE) { player, pokemon ->
if (pokemon != null) return@candyItem pokemon.getExperienceToNextLevel()
return@candyItem -1
}
@JvmField @JvmField
val EXPERIENCE_CANDY_XS = candyItem("exp_candy_xs") { _, _ -> CandyItem.DEFAULT_XS_CANDY_YIELD } val EXPERIENCE_CANDY_XS = candyItem("exp_candy_xs") { _, _ -> CandyItem.DEFAULT_XS_CANDY_YIELD }
@JvmField @JvmField
@@ -1476,7 +1484,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return item return item
} }
private fun candyItem(name: String, rarity: Rarity = Rarity.COMMON, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(rarity, calculator)) private fun candyItem(name: String, rarity: Rarity = Rarity.COMMON, calculator: (ServerPlayer, Pokemon?) -> Int): CandyItem = this.create(name, CandyItem(rarity, calculator))
private fun pokerodItem(pokeRodId: ResourceLocation, rarity: Rarity = Rarity.COMMON): PokerodItem { private fun pokerodItem(pokeRodId: ResourceLocation, rarity: Rarity = Rarity.COMMON): PokerodItem {
val settings = Item.Properties().stacksTo(1).durability(256).rarity(rarity) val settings = Item.Properties().stacksTo(1).durability(256).rarity(rarity)
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import net.minecraft.commands.CommandSourceStack import net.minecraft.commands.CommandSourceStack
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
/** /**
@@ -25,8 +26,13 @@ interface ExperienceSource {
fun isInteraction() = this is CandyExperienceSource fun isInteraction() = this is CandyExperienceSource
fun isCommand() = this is CommandExperienceSource fun isCommand() = this is CommandExperienceSource
fun isSidemod() = this is SidemodExperienceSource fun isSidemod() = this is SidemodExperienceSource
fun isMob() = this is MobExperienceSource
} }
open class MobExperienceSource(
mob: LivingEntity,
) : ExperienceSource
open class SidemodExperienceSource( open class SidemodExperienceSource(
val sidemodId: String val sidemodId: String
) : ExperienceSource ) : ExperienceSource
@@ -344,6 +344,9 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Spawning, lang = "despawner_max_age_ticks", SERVER) @CobblemonConfigField(Category.Spawning, lang = "despawner_max_age_ticks", SERVER)
var despawnerMaxAgeTicks = 3600 var despawnerMaxAgeTicks = 3600
@CobblemonConfigField(Category.World, lang = "mob_experience_conversion_rate", SERVER)
var mobExperienceConversionRate = 50.0
fun clone(): CobblemonConfig { fun clone(): CobblemonConfig {
val newConfig = CobblemonConfig() val newConfig = CobblemonConfig()
CobblemonConfig::class.memberProperties.forEach { property -> CobblemonConfig::class.memberProperties.forEach { property ->
@@ -38,6 +38,7 @@ import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.RideBoostsDataSerializer import com.cobblemon.mod.common.api.net.serializers.RideBoostsDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.pokemon.experience.MobExperienceSource
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
@@ -1259,7 +1260,18 @@ open class PokemonEntity(
override fun doHurtTarget(target: Entity): Boolean { override fun doHurtTarget(target: Entity): Boolean {
if (beamMode != 0) return false if (beamMode != 0) return false
return super.doHurtTarget(target) val result = super.doHurtTarget(target)
if (result && !target.isAlive && target is LivingEntity && !level().isClientSide) {
val experience = target.getExperienceReward(level() as ServerLevel, this)
val source = MobExperienceSource(mob = target)
if (this.owner is ServerPlayer) {
pokemon.addExperienceWithPlayer(this.owner as ServerPlayer, source, (experience * Cobblemon.config.mobExperienceConversionRate).toInt())
}
else {
pokemon.addExperience(source, (experience * Cobblemon.config.mobExperienceConversionRate).toInt())
}
}
return result
} }
override fun hurt(source: DamageSource, amount: Float): Boolean { override fun hurt(source: DamageSource, amount: Float): Boolean {
@@ -8,17 +8,24 @@
package com.cobblemon.mod.common.item.interactive package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.events.CobblemonEvents import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.experience.CandyExperienceSource import com.cobblemon.mod.common.api.pokemon.experience.CandyExperienceSource
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.CobblemonItem import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.pokemon.Pokemon import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.InteractionResultHolder import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity import net.minecraft.world.item.Rarity
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
@@ -34,22 +41,59 @@ import net.minecraft.world.level.Level
*/ */
class CandyItem( class CandyItem(
val item_rarity: Rarity, val item_rarity: Rarity,
val calculator: Calculator val expYield: (ServerPlayer, Pokemon?) -> Int
) : CobblemonItem(Properties().apply { ) : CobblemonItem(Properties().apply {
rarity(item_rarity) rarity(item_rarity)
}), PokemonSelectingItem { food(FoodProperties.Builder()
.alwaysEdible()
.nutrition(0)
.saturationModifier(0.0f)
.build())
}) {
override val bagItem = null override fun finishUsingItem(stack: ItemStack, level: Level, livingEntity: LivingEntity): ItemStack? {
if (livingEntity !is Player) return super.finishUsingItem(stack, level, livingEntity)
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> { /* Didn't find a good way to determine the XP needed until the beginning of the next level.
if (user is ServerPlayer) { * This works but is a little jank. Feel free to investigate a better solution.
return use(user, user.getItemInHand(hand)) */
if (level.isClientSide && this == CobblemonItems.RARE_CANDY) {
val volume = if (livingEntity.experienceLevel > 30) 1.0f else livingEntity.experienceLevel.toFloat() / 30.0f
livingEntity.playSound(SoundEvents.PLAYER_LEVELUP, volume * 0.75f, 1.0f)
} }
return InteractionResultHolder.success(user.getItemInHand(hand))
if (livingEntity !is ServerPlayer) return super.finishUsingItem(stack, level, livingEntity)
if (this == CobblemonItems.RARE_CANDY) {
livingEntity.setExperienceLevels(livingEntity.experienceLevel + 1)
livingEntity.setExperiencePoints(0)
}
else {
val expYield = expYield(livingEntity, null)
val playerExperience = (expYield / Cobblemon.config.mobExperienceConversionRate).toInt().coerceAtLeast(1)
ExperienceOrb.award(livingEntity.serverLevel(), livingEntity.position(), playerExperience)
}
return super.finishUsingItem(stack, level, livingEntity)
} }
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack>? { override fun interactLivingEntity(
val experience = this.calculator.calculate(player, pokemon) stack: ItemStack,
player: Player,
interactionTarget: LivingEntity,
usedHand: InteractionHand
): InteractionResult {
if (player.level().isClientSide) return InteractionResult.FAIL
if (player.isCrouching) return InteractionResult.FAIL
if (interactionTarget !is PokemonEntity) return InteractionResult.PASS
if (interactionTarget.pokemon.getOwnerPlayer() != player) return InteractionResult.PASS
return applyToPokemon(player as ServerPlayer, stack, interactionTarget.pokemon)?.result ?: InteractionResult.FAIL
}
fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack>? {
if (!pokemon.canLevelUpFurther()) {
return InteractionResultHolder.fail(stack)
}
val experience = expYield(player, pokemon)
CobblemonEvents.EXPERIENCE_CANDY_USE_PRE.postThen( CobblemonEvents.EXPERIENCE_CANDY_USE_PRE.postThen(
event = ExperienceCandyUseEvent.Pre(player, pokemon, this, experience, experience), event = ExperienceCandyUseEvent.Pre(player, pokemon, this, experience, experience),
ifSucceeded = { preEvent -> ifSucceeded = { preEvent ->
@@ -74,29 +118,6 @@ class CandyItem(
return InteractionResultHolder.fail(stack) return InteractionResultHolder.fail(stack)
} }
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
return pokemon.isPlayerOwned()
}
/**
* Functional interface responsible for resolving the experience a candy will yield.
*
* @author Licious
* @since March 5th, 2022
*/
fun interface Calculator {
/**
* Resolves the experience the [CandyItem] will give.
*
* @param player The [ServerPlayer] using the candy.
* @param pokemon The [Pokemon] receiving experience.
* @return The experience that will be received
*/
fun calculate(player: ServerPlayer, pokemon: Pokemon): Int
}
companion object { companion object {
const val DEFAULT_XS_CANDY_YIELD = 100 const val DEFAULT_XS_CANDY_YIELD = 100
const val DEFAULT_S_CANDY_YIELD = 800 const val DEFAULT_S_CANDY_YIELD = 800
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.pokemon package com.cobblemon.mod.common.pokemon
import com.cobblemon.mod.common.Cobblemon import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers
import com.cobblemon.mod.common.CobblemonSounds import com.cobblemon.mod.common.CobblemonSounds
@@ -148,6 +149,7 @@ import net.minecraft.util.Mth.ceil
import net.minecraft.util.Mth.clamp import net.minecraft.util.Mth.clamp
import net.minecraft.util.StringRepresentable import net.minecraft.util.StringRepresentable
import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.MobSpawnType import net.minecraft.world.entity.MobSpawnType
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
@@ -2016,6 +2018,12 @@ open class Pokemon : ShowdownIdentifiable {
} }
) )
val player = getOwnerPlayer()
if (player != null && player.inventory.getArmor(3).item == CobblemonItems.EXP_SHARE) {
val playerExperience = (appliedXP / Cobblemon.config.mobExperienceConversionRate * Cobblemon.config.experienceShareMultiplier).toInt().coerceAtLeast(1)
ExperienceOrb.award(player.serverLevel(), player.position(), playerExperience)
}
experience += appliedXP experience += appliedXP
// Bound is needed here as we can still be allowed to level up but go over the current cap // Bound is needed here as we can still be allowed to level up but go over the current cap
var newLevel = experienceGroup.getLevel(experience).coerceAtMost(Cobblemon.config.maxPokemonLevel) var newLevel = experienceGroup.getLevel(experience).coerceAtMost(Cobblemon.config.maxPokemonLevel)
@@ -2621,6 +2621,8 @@
"cobblemon.config.ui.infinite_ride_stamina.tooltip": "Whether rideable Pokémon have infinite stamina.", "cobblemon.config.ui.infinite_ride_stamina.tooltip": "Whether rideable Pokémon have infinite stamina.",
"cobblemon.config.ui.remember_riding_camera": "Remember Riding Camera", "cobblemon.config.ui.remember_riding_camera": "Remember Riding Camera",
"cobblemon.config.ui.remember_riding_camera.tooltip": "Whether the camera mode will automatically switch to the last used camera mode when mounting a rideable Pokémon.", "cobblemon.config.ui.remember_riding_camera.tooltip": "Whether the camera mode will automatically switch to the last used camera mode when mounting a rideable Pokémon.",
"cobblemon.config.ui.mob_experience_conversion_rate": "Mob Experience Conversion Rate",
"cobblemon.config.ui.mob_experience_conversion_rate.tooltip": "The conversion rate for mob experience to pokemon experience when a Pokemon kills a mob.",
"cobblemon.config.ui.allow_starter_on_join": "Allow Starter on Join", "cobblemon.config.ui.allow_starter_on_join": "Allow Starter on Join",
"cobblemon.config.ui.prompt_starter_once_only": "Prompt Starter Once Only", "cobblemon.config.ui.prompt_starter_once_only": "Prompt Starter Once Only",