Added a bunch of alpha pokemon functionality, bunch temp

This commit is contained in:
Paul
2025-12-12 16:13:03 +10:00
parent 7d4e5892ba
commit 87a36020f6
21 changed files with 267 additions and 10 deletions
+9
View File
@@ -0,0 +1,9 @@
- Some kind of particle effect for them? they're meant to have glowing eyes, so maybe some red particles on the eye locator
--- global posers? stacked posers?
--- red texture seems to be what some people wanted.
/- shoulder mounts part of it
- omni boost
- roar when player comes within reach, adjust levels
- Riding settings to control what the min scale is for a pokemon to be ridable.
@@ -86,6 +86,7 @@ import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacke
import com.cobblemon.mod.common.permission.LaxPermissionValidator
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.aspects.ALPHA_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.CHARACTERISTIC_RAINBOW_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
@@ -302,6 +303,7 @@ object Cobblemon {
GENDER_ASPECT.register()
COSMETIC_SLOT_ASPECT.register()
CHARACTERISTIC_RAINBOW_ASPECT.register()
ALPHA_ASPECT.register()
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
@@ -325,6 +325,8 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(AlphaUpdatePacket.ID, AlphaUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(ScaleModifierUpdatePacket.ID, ScaleModifierUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -131,6 +131,7 @@ open class PokemonProperties {
props.gender = Gender.entries.toList().parsePropertyOfCollection(keyPairs, listOf("gender"), labelsOptional = true) { it.name.lowercase() }
props.level = parseIntProperty(keyPairs, listOf("level", "lvl", "l"))?.coerceIn(1, Cobblemon.config.maxPokemonLevel)
props.shiny = parseBooleanProperty(keyPairs, listOf("shiny", "s"))
props.isAlpha = parseBooleanProperty(keyPairs, listOf("alpha", "is_alpha"))
props.species = parseSpeciesIdentifier(keyPairs)
props.form = parseForm(keyPairs)
props.friendship = parseIntProperty(keyPairs, listOf("friendship"))?.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
@@ -359,6 +360,7 @@ open class PokemonProperties {
var heldItem: String? = null
var cosmeticItem: String? = null
var scaleModifier: Float? = null
var isAlpha: Boolean? = null
var minPerfectIVs: Int? = null
var ivs: IVs? = null
@@ -409,6 +411,7 @@ open class PokemonProperties {
shiny?.let { pokemon.shiny = it }
gender?.let { pokemon.gender = it }
level?.let { pokemon.level = it }
isAlpha?.let { pokemon.isAlpha = it }
friendship?.also { pokemon.setFriendship(it) }
fullness?.let { pokemon.currentFullness = it }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
@@ -528,6 +531,7 @@ open class PokemonProperties {
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
ability?.takeIf { it != pokemon.ability.name }?.let { return false }
isAlpha?.takeIf { it != pokemon.isAlpha }?.let { return false }
status?.takeIf { it != pokemon.status?.status?.showdownName }?.let { return false }
minPerfectIVs?.takeIf { pokemon.ivs.count { stat -> stat.value == IVs.MAX_VALUE } < it }?.let { return false }
ivs?.forEach{ stat ->
@@ -587,6 +591,7 @@ open class PokemonProperties {
friendship?.takeIf { it != properties.friendship }?.let { return false }
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
nature?.takeIf { it != properties.nature }?.let { return false }
isAlpha?.takeIf { it != properties.isAlpha }?.let { return false }
ability?.takeIf { it != properties.ability }?.let { return false }
status?.takeIf { it != properties.status }?.let { return false }
minPerfectIVs?.takeIf { it != properties.minPerfectIVs }?.let { return false }
@@ -630,13 +635,13 @@ open class PokemonProperties {
@JvmOverloads
fun roll(pokemon: Pokemon, player: ServerPlayer? = null) {
val baseTypes = pokemon.form.types.toList()
var shinyRate = Cobblemon.config.shinyRate
var shinyRate = config.shinyRate
CobblemonEvents.SHINY_CHANCE_CALCULATION.post(ShinyChanceCalculationEvent(shinyRate, pokemon)) { event ->
shinyRate = event.calculate(player)
}
if (this.shiny == null) pokemon.shiny = shinyRate.checkRate()
if (this.teraType == null) pokemon.teraType =
if (Cobblemon.config.teraTypeRate.checkRate()) {
if (config.teraTypeRate.checkRate()) {
var picked = TeraTypes.random(true)
while (picked is ElementalTypeTeraType && picked.type in pokemon.types) {
picked = TeraTypes.random(true)
@@ -644,6 +649,10 @@ open class PokemonProperties {
picked
}
else TeraTypes.forElementalType(baseTypes.random())
isAlpha?.let { pokemon.isAlpha = it }
if (this.scaleModifier == null) {
pokemon.initializeScale()
}
}
@JvmOverloads
@@ -666,6 +675,7 @@ open class PokemonProperties {
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
isAlpha?.let { nbt.putBoolean(DataKeys.POKEMON_ALPHA, it) }
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
minPerfectIVs?.let { nbt.putInt(DataKeys.POKEMON_MIN_PERFECT_IVS, it) }
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, IVs.CODEC.encodeStart(NbtOps.INSTANCE, it).result().get()) }
@@ -700,6 +710,7 @@ open class PokemonProperties {
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
isAlpha = if (tag.contains(DataKeys.POKEMON_ALPHA)) tag.getBoolean(DataKeys.POKEMON_ALPHA) else null
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
minPerfectIVs = if (tag.contains(DataKeys.POKEMON_MIN_PERFECT_IVS)) tag.getInt(DataKeys.POKEMON_MIN_PERFECT_IVS) else null
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) IVs.CODEC.decode(NbtOps.INSTANCE, tag.getCompound(DataKeys.POKEMON_IVS)).result().getOrNull()?.first else null
@@ -735,6 +746,7 @@ open class PokemonProperties {
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
isAlpha?.let { json.addProperty(DataKeys.POKEMON_ALPHA, it) }
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
minPerfectIVs?.let { json.addProperty(DataKeys.POKEMON_MIN_PERFECT_IVS, it) }
ivs?.let { json.add(DataKeys.POKEMON_IVS, IVs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()) }
@@ -769,6 +781,7 @@ open class PokemonProperties {
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
isAlpha = json.get(DataKeys.POKEMON_ALPHA)?.asBoolean
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
minPerfectIVs = json.get(DataKeys.POKEMON_MIN_PERFECT_IVS)?.asInt
ivs = json.getAsJsonObject(DataKeys.POKEMON_IVS)?.let { IVs.CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first }
@@ -803,6 +816,7 @@ open class PokemonProperties {
pokeball?.let { pieces.add("pokeball=$it") }
nature?.let { pieces.add("nature=$it") }
ability?.let { pieces.add("ability=$it") }
isAlpha?.let { pieces.add("alpha=$it") }
status?.let { pieces.add("status=$it") }
minPerfectIVs?.let { pieces.add("min_perfect_ivs=$it") }
ivs?.forEach{ stat ->
@@ -20,8 +20,6 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.aspectParticleMap
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.aspect.ParticleData
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.scheduling.afterOnClient
@@ -112,6 +110,8 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
// force a model update - handles edge case where the PosableState's tracked PosableModel isn't updated until the LivingEntityRenderer render is run
currentModel = VaryingModelRepository.getPoser(identifier, this)
currentEntity.refreshRiding()
} else if (data == PokemonEntity.IS_ALPHA) {
currentEntity.pokemon.isAlpha = currentEntity.entityData.get(PokemonEntity.IS_ALPHA)
} else if (data == PokemonEntity.ASPECTS) {
currentAspects = currentEntity.entityData.get(PokemonEntity.ASPECTS)
currentEntity.pokemon.shiny = currentAspects.contains("shiny")
@@ -601,6 +601,20 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
shadow = true
)
val sizeCategory = selectedPokemon.getSizeCategory()
val isAlpha = selectedPokemon.isAlpha
drawScaledText(
x = x + 62,
y = y + 32,
font = CobblemonResources.DEFAULT_LARGE,
context = context,
text = (if (isAlpha) "A".text() else sizeCategory.name.text()).bold(),
scale = 1F,
shadow = true,
centered = true
)
// Shiny Icon
if (selectedPokemon.shiny) {
blitk(
@@ -113,7 +113,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
open var properties = mutableMapOf<String, String>()
/*
* Hello future Hiro, this is past Hiro. You've gotten forgetful in your old age.
* Hello future Hiro, this is past Hiro. You've become forgetful in your old age.
*
* The profile translation is not actually necessary. The reason why you thought it was necessary
* is that there is a 1.5 block offset applied by living-entity-renderer-intended models due to
@@ -292,7 +292,7 @@ class PokemonRenderer(
}
override fun scale(pEntity: PokemonEntity, pPoseStack: PoseStack, pPartialTickTime: Float) {
val scale = pEntity.pokemon.form.baseScale * pEntity.pokemon.scaleModifier * (pEntity.delegate as PokemonClientDelegate).entityScaleModifier
val scale = pEntity.pokemon.form.baseScale * pEntity.pokemon.effectiveScale * (pEntity.delegate as PokemonClientDelegate).entityScaleModifier
pPoseStack.scale(scale, scale, scale)
}
@@ -219,6 +219,18 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Pokemon, lang = "player_damage_pokemon", SERVER)
var playerDamagePokemon = true
@CobblemonConfigField(Category.Pokemon, lang = "pokemon_size_variation", SERVER)
var pokemonSizeVariation = 0.05F
@CobblemonConfigField(Category.Pokemon, lang = "alpha_pokemon_size_multiplier", SERVER)
var alphaPokemonSizeMultiplier = 1.2F
@CobblemonConfigField(Category.Pokemon, lang = "baby_pokemon_level_duration", SERVER)
var babyPokemonLevelDuration = 9
@CobblemonConfigField(Category.Pokemon, lang = "baby_pokemon_size_multiplier", SERVER)
var babyPokemonSizeMultiplier = 0.8F
@CobblemonConfigField(Category.World, lang = "apple_leftovers_chance", SERVER)
var appleLeftoversChance = 0.025
@@ -213,6 +213,7 @@ open class PokemonEntity(
@JvmStatic var RIDE_BOOSTS = SynchedEntityData.defineId(PokemonEntity::class.java, RideBoostsDataSerializer)
@JvmStatic var RIDE_STAMINA = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.FLOAT)
@JvmStatic var SCALE_MODIFIER = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.FLOAT)
@JvmStatic var IS_ALPHA = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
const val BATTLE_LOCK = "battle"
const val EVOLUTION_LOCK = "evolving"
@@ -460,6 +461,7 @@ open class PokemonEntity(
builder.define(RIDE_BOOSTS, emptyMap())
builder.define(RIDE_STAMINA, 1F)
builder.define(SCALE_MODIFIER, 1F)
builder.define(IS_ALPHA, false)
}
override fun onSyncedDataUpdated(data: EntityDataAccessor<*>) {
@@ -1095,7 +1097,7 @@ open class PokemonEntity(
override fun getBreedOffspring(serverLevel: ServerLevel, ageableMob: AgeableMob) = null
override fun canSitOnShoulder(): Boolean {
return pokemon.form.shoulderMountable
return pokemon.form.shoulderMountable && !pokemon.isAlpha
}
override fun wantsToPickUp(stack: ItemStack): Boolean {
@@ -1253,7 +1255,7 @@ open class PokemonEntity(
}
override fun getDimensions(pose: Pose): EntityDimensions {
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.scaleModifier)
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.effectiveScale)
var result = this.exposedForm.hitbox.scale(scale)
result = result.withEyeHeight(this.exposedForm.eyeHeight(this) * result.height)
result = result.scale(this.scale)
@@ -2610,11 +2612,15 @@ open class PokemonEntity(
* represents a kind of 'responsibility' that the leader feels towards their followers - they believe in this
* leader with some amount of fervor, and this gets used to ensure that the leader doesn't choose to follow a
* different Pokémon that is of an equal or lower tier than this Pokémon is to its followers.
* - Alpha Pokémon automatically have the maximum possible tier.
*/
fun getHerdTier(): Int {
val world = level() as? ServerLevel ?: return 0
val herdLeader = this.brain.getMemorySafely(CobblemonMemories.HERD_LEADER).orElse(null)?.let(UUID::fromString)?.let(world::getEntity) as? PokemonEntity
return if (herdLeader == null) {
if (pokemon.isAlpha) {
return Int.MAX_VALUE
}
if (!brain.hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)) {
return 0
}
@@ -2626,6 +2632,9 @@ open class PokemonEntity(
it.behaviour.herd.bestMatchLeader(follower = it, possibleLeader = this)?.tier ?: 0
} ?: 0
} else {
if (herdLeader.pokemon.isAlpha) {
return Int.MAX_VALUE
}
herdLeader.behaviour.herd.bestMatchLeader(follower = this, possibleLeader = herdLeader)?.tier ?: 0
}
}
@@ -243,6 +243,9 @@ class PokemonServerDelegate : PokemonSideDelegate {
if (entity.entityData.get(PokemonEntity.MARK) !=trackedMark) {
entity.entityData.set(PokemonEntity.MARK, trackedMark)
}
if (entity.entityData.get(PokemonEntity.IS_ALPHA) != entity.pokemon.isAlpha) {
entity.entityData.set(PokemonEntity.IS_ALPHA, entity.pokemon.isAlpha)
}
entity.entityData.set(PokemonEntity.ASPECTS, trackedAspects)
entity.entityData.set(PokemonEntity.LABEL_LEVEL, entity.pokemon.level)
@@ -0,0 +1,41 @@
/*
* 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.net.messages.client.pokemon.update
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import net.minecraft.network.RegistryFriendlyByteBuf
/**
* Update packet for the alpha (as in the big scary pack leader) status of a Pokémon.
*
* @author Hiroku
* @since December 1st, 2025
*/
class AlphaUpdatePacket(pokemon: () -> Pokemon?, value: Boolean): SingleUpdatePacket<Boolean, AlphaUpdatePacket>(pokemon, value) {
override val id = ID
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
buffer.writeBoolean(this.value)
}
override fun set(pokemon: Pokemon, value: Boolean) {
pokemon.isAlpha = value
}
companion object {
val ID = cobblemonResource("alpha_update")
fun decode(buffer: RegistryFriendlyByteBuf): CaughtBallUpdatePacket {
val pokemon = decodePokemon(buffer)
val pokeBall = PokeBalls.getPokeBall(buffer.readIdentifier()) ?: PokeBalls.POKE_BALL
return CaughtBallUpdatePacket(pokemon, pokeBall)
}
}
}
@@ -0,0 +1,39 @@
/*
* 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.net.messages.client.pokemon.update
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
/**
* Update packet for a Pokémon's scale modifier.
*
* @author Hiroku
* @since December 1st, 2025
*/
class ScaleModifierUpdatePacket(pokemon: () -> Pokemon?, value: Float): SingleUpdatePacket<Float, ScaleModifierUpdatePacket>(pokemon, value) {
override val id = ID
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
buffer.writeFloat(this.value)
}
override fun set(pokemon: Pokemon, value: Float) {
pokemon.scaleModifier = value
}
companion object {
val ID = cobblemonResource("scale_modifier_update")
fun decode(buffer: RegistryFriendlyByteBuf): ScaleModifierUpdatePacket {
val pokemon = decodePokemon(buffer)
val scaleModifier = buffer.readFloat()
return ScaleModifierUpdatePacket(pokemon, scaleModifier)
}
}
}
@@ -61,6 +61,7 @@ class SpawnPokemonPacket(
var tickSpawned: Int,
var rideBoosts: Map<RidingStat, Float>,
var rideStamina: Float,
var isAlpha: Boolean,
vanillaSpawnPacket: ClientboundAddEntityPacket,
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
@@ -93,6 +94,7 @@ class SpawnPokemonPacket(
entity.tickCount,
entity.entityData.get(PokemonEntity.RIDE_BOOSTS),
entity.entityData.get(PokemonEntity.RIDE_STAMINA),
entity.entityData.get(PokemonEntity.IS_ALPHA),
vanillaSpawnPacket
)
@@ -127,6 +129,7 @@ class SpawnPokemonPacket(
{ _, value -> buffer.writeFloat(value) }
)
buffer.writeFloat(rideStamina)
buffer.writeBoolean(isAlpha)
}
override fun applyData(entity: PokemonEntity, level: ClientLevel) {
@@ -162,6 +165,7 @@ class SpawnPokemonPacket(
entity.entityData.set(PokemonEntity.RIDE_BOOSTS, rideBoosts)
entity.entityData.set(PokemonEntity.RIDE_STAMINA, rideStamina)
entity.entityData.set(PokemonEntity.SCALE_MODIFIER, scaleModifier)
entity.entityData.set(PokemonEntity.IS_ALPHA, isAlpha)
entity.ejectPassengers()
passengers.forEach {
@@ -206,6 +210,7 @@ class SpawnPokemonPacket(
{ buffer.readFloat() }
)
val rideStamina = buffer.readFloat()
val isAlpha = buffer.readBoolean()
val vanillaPacket = decodeVanillaPacket(buffer)
return SpawnPokemonPacket(
@@ -235,6 +240,7 @@ class SpawnPokemonPacket(
tickSpawned,
rideBoosts,
rideStamina,
isAlpha,
vanillaPacket
)
}
@@ -110,6 +110,7 @@ import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP3
import com.cobblemon.mod.common.util.codec.internal.PokemonP1
import com.cobblemon.mod.common.util.codec.internal.PokemonP2
import com.cobblemon.mod.common.util.codec.internal.PokemonP3
import com.cobblemon.mod.common.util.nextBetween
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.setPositionSafely
@@ -238,6 +239,15 @@ open class Pokemon : ShowdownIdentifiable {
var characteristic: Characteristic = Characteristic.calculate(ivs, uuid)
private set
var isAlpha: Boolean = false
set(value) {
if (field != value) {
field = value
updateAspects()
onChange(AlphaUpdatePacket({ this }, value))
}
}
fun setIV(stat : Stat, value : Int) {
val quotient = clamp(currentHealth / maxHealth.toFloat(), 0F, 1F)
ivs[stat] = value
@@ -246,6 +256,11 @@ open class Pokemon : ShowdownIdentifiable {
}
}
fun initializeScale() {
val variation = Cobblemon.config.pokemonSizeVariation
scaleModifier = Random.nextBetween(1 - variation, 1 + variation)
}
fun hyperTrainIV(stat: Stat, value: Int) {
val quotient = clamp(currentHealth / maxHealth.toFloat(), 0F, 1F)
CobblemonEvents.HYPER_TRAINED_IV_PRE.postThen(
@@ -550,7 +565,23 @@ open class Pokemon : ShowdownIdentifiable {
val speed: Int
get() = getStat(Stats.SPEED)
var scaleModifier = 1F
var scaleModifier: Float = 1F
set(value) {
if (field != value) {
field = value
onChange(ScaleModifierUpdatePacket({ this }, value))
}
}
val effectiveScale: Float
get() {
val babyPokemonMultiplier = if (level - Cobblemon.config.babyPokemonLevelDuration < 1) {
Cobblemon.config.babyPokemonSizeMultiplier
} else {
1F
}
return babyPokemonMultiplier * if (this.isAlpha) Cobblemon.config.alphaPokemonSizeMultiplier else scaleModifier
}
var caughtBall: PokeBall = PokeBalls.POKE_BALL
set(value) {
@@ -2001,6 +2032,10 @@ open class Pokemon : ShowdownIdentifiable {
return createPokemonProperties(*extractors.toTypedArray())
}
fun getSizeCategory(): PokemonSizeCategory {
return PokemonSizeCategory.fromScale(scaleModifier)
}
fun addExperience(source: ExperienceSource, xp: Int): AddExperienceResult {
if (xp < 0 || !this.canLevelUpFurther()) {
return AddExperienceResult(level, level, emptySet(), 0) // no negatives!
@@ -0,0 +1,32 @@
/*
* 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.pokemon
import com.cobblemon.mod.common.Cobblemon
enum class PokemonSizeCategory {
XS,
S,
M,
L,
XL;
companion object {
// Find which size category corresponds to the given scale modifier.
// The scale modifier should be between 1 - Cobblemon.config.pokemonSizeVariation and 1 + Cobblemon.config.pokemonSizeVariation
// First 20% -> XS, etc.
fun fromScale(scaleModifier: Float): PokemonSizeCategory {
val range = Cobblemon.config.pokemonSizeVariation * 2
val segmentSize = range / PokemonSizeCategory.entries.size
val adjustedScale = scaleModifier - (1 - Cobblemon.config.pokemonSizeVariation)
val index = (adjustedScale / segmentSize).toInt().coerceIn(0, PokemonSizeCategory.entries.size - 1)
return PokemonSizeCategory.entries[index]
}
}
}
@@ -53,6 +53,26 @@ val COSMETIC_SLOT_ASPECT = object : AspectProvider {
override fun provide(properties: PokemonProperties) = emptySet<String>()
}
val ALPHA_ASPECT = object : AspectProvider {
val ASPECT = "alpha"
override fun provide(pokemon: Pokemon): Set<String> {
return if (pokemon.isAlpha) {
setOf(ASPECT)
} else {
emptySet()
}
}
override fun provide(properties: PokemonProperties): Set<String> {
return if (properties.isAlpha == true) {
setOf(ASPECT)
} else {
emptySet()
}
}
}
val CHARACTERISTIC_RAINBOW_ASPECT = object : AspectProvider {
fun calculateColourAspect(nature: Nature, characteristic: Characteristic): String? {
return when (nature.increasedStat to characteristic.relevantStat) {
@@ -20,6 +20,7 @@ object DataKeys {
const val POKEMON_FRIENDSHIP = "Friendship"
const val POKEMON_FULLNESS = "Fullness"
const val POKEMON_INTERACTION_COOLDOWN = "InteractionCooldown"
const val POKEMON_ALPHA = "Alpha"
const val POKEMON_IVS = "IVs"
const val POKEMON_IVS_BASE = "Base"
@@ -35,6 +35,7 @@ internal data class ClientPokemonP3(
val rideBoosts: Map<String, Float>,
val currentFullness: Int,
val interactionCooldowns: Map<ResourceLocation, Int>,
val isAlpha: Boolean,
) : Partial<Pokemon> {
override fun into(other: Pokemon): Pokemon {
@@ -53,6 +54,7 @@ internal data class ClientPokemonP3(
this.rideBoosts.let { other.setRideBoosts(it.mapKeys { RidingStat.valueOf(it.key) }) }
other.currentFullness = this.currentFullness
other.interactionCooldowns = this.interactionCooldowns.toMutableMap()
other.isAlpha = isAlpha
return other
}
@@ -75,6 +77,7 @@ internal data class ClientPokemonP3(
Codec.unboundedMap(Codec.STRING, Codec.FLOAT).fieldOf(DataKeys.POKEMON_RIDE_BOOSTS).forGetter(ClientPokemonP3::rideBoosts),
Codec.intRange(0, 100).fieldOf(DataKeys.POKEMON_FULLNESS).forGetter(ClientPokemonP3::currentFullness),
Codec.unboundedMap(ResourceLocation.CODEC, Codec.INT).fieldOf(DataKeys.POKEMON_INTERACTION_COOLDOWN).forGetter(ClientPokemonP3::interactionCooldowns),
Codec.BOOL.optionalFieldOf(DataKeys.POKEMON_ALPHA, false).forGetter(ClientPokemonP3::isAlpha)
).apply(instance, ::ClientPokemonP3)
}
@@ -92,6 +95,7 @@ internal data class ClientPokemonP3(
pokemon.getRideBoosts().mapKeys { it.key.name },
pokemon.currentFullness,
pokemon.interactionCooldowns,
pokemon.isAlpha,
)
}
}
@@ -43,6 +43,7 @@ internal data class PokemonP3(
val rideStamina: Float,
val currentFullness: Int,
val interactionCooldowns: Map<ResourceLocation, Int>,
val isAlpha: Boolean
) : Partial<Pokemon> {
override fun into(other: Pokemon): Pokemon {
@@ -79,6 +80,7 @@ internal data class PokemonP3(
other.rideStamina = this.rideStamina
other.currentFullness = this.currentFullness
other.interactionCooldowns = this.interactionCooldowns.toMutableMap()
other.isAlpha = isAlpha
other.recalculateCharacteristic()
other.updateAspects()
return other
@@ -102,6 +104,7 @@ internal data class PokemonP3(
Codec.FLOAT.optionalFieldOf(DataKeys.POKEMON_RIDE_STAMINA, 1F).forGetter(PokemonP3::rideStamina),
Codec.intRange(0, 100).optionalFieldOf(DataKeys.POKEMON_FULLNESS, 0).forGetter(PokemonP3::currentFullness),
Codec.unboundedMap(ResourceLocation.CODEC, Codec.INT).optionalFieldOf(DataKeys.POKEMON_INTERACTION_COOLDOWN, emptyMap<ResourceLocation, Int>()).forGetter(PokemonP3::interactionCooldowns),
Codec.BOOL.optionalFieldOf(DataKeys.POKEMON_ALPHA, false).forGetter(PokemonP3::isAlpha)
).apply(instance) {
originalTrainerType,
originalTrainer,
@@ -117,7 +120,8 @@ internal data class PokemonP3(
rideBoosts,
rideStamina,
currentFullness,
interactionCooldown -> PokemonP3(
interactionCooldown,
isAlpha -> PokemonP3(
originalTrainerType,
originalTrainer,
forcedAspects.toSet(),
@@ -133,6 +137,7 @@ internal data class PokemonP3(
rideStamina,
currentFullness,
interactionCooldown,
isAlpha
)
}
}
@@ -157,6 +162,7 @@ internal data class PokemonP3(
pokemon.rideStamina,
pokemon.currentFullness,
pokemon.interactionCooldowns,
pokemon.isAlpha
)
}
}
@@ -2522,6 +2522,14 @@
"cobblemon.config.ui.capture_calculator.tooltip": "The ID of the capture calculator. Can be added by another mod. If the ID doesn't exist it uses the default capture calculator.",
"cobblemon.config.ui.player_damage_pokemon": "Player Damage Pokémon",
"cobblemon.config.ui.player_damage_pokemon.tooltip": "Whether Players can attack Pokémon like normal Minecraft entities.",
"cobblemon.config.ui.pokemon_size_variation": "Pokémon Size Variation",
"cobblemon.config.ui.pokemon_size_variation.tooltip": "How much variation in size is possible for spawned Pokémon.",
"cobblemon.config.ui.alpha_pokemon_size_multiplier": "Alpha Pokémon Size Multiplier",
"cobblemon.config.ui.alpha_pokemon_size_multiplier.tooltip": "The size multiplier applied to alpha Pokémon.",
"cobblemon.config.ui.baby_pokemon_level_duration": "Baby Pokémon Level Duration",
"cobblemon.config.ui.baby_pokemon_level_duration.tooltip": "The number of levels that a Pokémon is considered a baby for the purposes of appearing smaller than usual.",
"cobblemon.config.ui.baby_pokemon_size_multiplier": "Baby Pokémon Minimum Size",
"cobblemon.config.ui.baby_pokemon_size_multiplier.tooltip": "The size multiplier applied to a level 1 Pokémon.",
"cobblemon.config.ui.apple_leftovers_chance": "Apple Leftovers Chance",
"cobblemon.config.ui.apple_leftovers_chance.tooltip": "The chance to drop Leftovers when a Player eats an item that has #cobblemon:held/leaves_leftovers.",
"cobblemon.config.ui.max_roots_in_area": "Max Roots in Area",