Compare commits

...
Author SHA1 Message Date
Plastered_Crab 3f52585386 Changes to herding system for alphas 2026-03-25 13:34:22 -07:00
Plastered_Crab 9a2443f709 Changes to herding system for alphas 2026-03-25 13:05:36 -07:00
Plastered_Crab 6d3ddece03 Level Matching of Alphas based on nearest players 2026-03-25 11:30:31 -07:00
Plastered_Crab 99a0e83962 Added Shiny Chance and Alpha Chance to Honey Slathered Trees 2026-03-25 10:02:09 -07:00
Plastered_Crab 7572530a36 Added Shiny Chance and Alpha Chance to Fossil Machine 2026-03-25 09:36:13 -07:00
Plastered_Crab 7f9ed14be7 Herd spawns can be designated held items 2026-03-24 18:48:21 -07:00
Plastered_Crab f3e6e5a94c Alpha Pokémon always retaliate if attacked 2026-03-24 18:12:32 -07:00
Plastered_Crab 325c4093eb Adjust Alpha sizes 2026-03-24 18:02:02 -07:00
Plastered_Crab 2510b23401 Add Alpha spawning via herd spawn system 2026-03-24 17:21:58 -07:00
Plastered_Crab d4b6f6e55e Pull down Showdown changes for Alpha battles 2026-03-24 15:50:04 -07:00
Plastered_Crab 3d38dc2c99 Add Alpha Pokemon Bait effect 2026-03-23 18:28:54 -07:00
Plastered_Crab 6cc384abbb Size Marks assigned if exactly min or max size variation 2026-03-23 10:19:56 -07:00
jackowes 205ec6a228 name change for sendout scaling 2026-03-22 22:25:14 -05:00
Paul 943625056a Fixed alpha and baby scaling not being respected in various places 2026-03-23 13:04:29 +10:00
jackowes 06e0bbc502 bring in alpha eyes pngs and add the utility script that did it 2026-03-22 19:26:02 -05:00
jackowes d83d5cd3da make sure the alpha eyes textures not being found doesn't cause issues 2026-03-22 18:49:09 -05:00
jackowes 6215f8afa5 Add utility script that populates all pokemon resolvers with their alpha_eyes texture entry. Script has been run as you can see :) 2026-03-22 18:47:41 -05:00
jackowes b39068283f better initial defualts for reverb length 2026-03-21 22:27:12 -05:00
jackowes 34c4cc1579 alpha cry pitch down and reverb. Beginning of alpha eye trail rendering. Adjust config defaults for alpha sizes to be larger (mechanic design discussion) 2026-03-21 22:16:09 -05:00
jackowes b2e1558e03 Make the aspect field for marks a list instead 2026-03-21 14:34:26 -05:00
jackowes 90bd717d7b switch alpha eye rendering to aspects. still need to move eye particles over to not be hardcoded. 2026-03-19 22:16:01 -05:00
Polymeta 34e64da0c6 Merge branch 'sigma-pokemon' into 'sigma-pokemon'
Alpha boost implementation

See merge request cable-mc/cobblemon!2114
2026-03-02 23:38:04 +00:00
gruxor 7327ae14fd Implemented ActionEffectInstruction for StartInstruction, relocated resource 2026-02-28 04:38:03 -05:00
Aramandgruxor ae05be2ce2 Slippy can Cook now! 2026-02-14 00:35:17 -05:00
Paulandgruxor 5f2984a3d0 Fixed woopsies in changelog 2026-02-14 00:35:17 -05:00
Paulandgruxor de41fdf871 Fixed multi-spawns in a single pass using the same bucket for all of them 2026-02-14 00:35:17 -05:00
Paulandgruxor af491f02a2 Added more molang expressions to a bunch of composed pool properties, fixed some bugs 2026-02-14 00:35:17 -05:00
Paulandgruxor e40a3331e2 Added egg move selector 2026-02-14 00:35:17 -05:00
Paulandgruxor 6dac494a0f more doco changes [skip-ci] 2026-02-14 00:35:17 -05:00
Paulandgruxor 246ddc7a54 Change silly default example weight to the actual default weight 2026-02-14 00:35:17 -05:00
Paulandgruxor f7c560db78 Rearrange doco 2026-02-14 00:35:17 -05:00
Paulandgruxor bae7ad5ce1 Added npcAspects condition for party pool entries 2026-02-14 00:35:17 -05:00
Paulandgruxor 2e59fa4f68 Documentation 2026-02-14 00:35:17 -05:00
Paulandgruxor 474cf1c6db Added last_suitable_offensive move selector, added alpha moveset builder 2026-02-14 00:35:17 -05:00
Paulandgruxor 8cd46a5a49 Stack all the cobblemon:move_weights.json from any datapacks onto one big map, like tags 2026-02-14 00:35:16 -05:00
Paulandgruxor 382bf67866 Added more advanced moveset and party construction for NPCs 2026-02-14 00:35:16 -05:00
Plastered_Crab c7d93ebfc5 Merge branch '1.8.0' into sigma-pokemon 2026-02-13 21:13:58 -08:00
gruxor 002e68b599 Remove unnecessary fail lang, change remaining one to "start" 2026-02-11 20:53:51 -05:00
gruxor 2ac2a57e12 Alpha boost implementation 2026-02-11 20:53:51 -05:00
gruxor b84002d142 Callback changes/restoration 2026-02-11 19:56:20 -05:00
Plastered_Crab f96ea92a4c Merge branch 'main' into sigma-pokemon
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/client/spawn/SpawnPokemonPacket.kt
2026-02-11 00:08:52 -08:00
Plastered_Crab a1e10bf3b1 Pokemon that spawn as an alpha are given and assigned the Alpha Mark which is set as the active mark. Mark being present controls the eye overlay and particles 2026-02-11 00:06:23 -08:00
Plastered_Crab eccb4bb7af Add Post Spawn Event and callback 2026-02-11 00:04:18 -08:00
Plastered_Crab 254d5e1343 Alpha eye particles 2026-02-10 21:37:04 -08:00
Plastered_Crab a3cb3e8d9e oops staged only half the files 2026-02-10 14:49:31 -08:00
Plastered_Crab 69354fac58 Alphas got them red eyes boi 2026-02-10 14:47:07 -08:00
Plastered_Crab d40f3599b9 Pokemon with different size variation scale back to 1.0 scale when shoulder mounted and Alphas cannot be shoulder mounted 2026-02-10 08:09:22 -08:00
Plastered_Crab 531ed8b827 Pokemon grow in size from level 1 to 10 as if they are aging 2026-02-09 21:27:11 -08:00
Plastered_Crab fc844fca9e Pokemon spawn with size variation and Dex Scanner reveals that info 2026-02-09 14:20:05 -08:00
Plastered_Crab 23bfa090d2 Alpha Pokemon Size Variation driven by hitbox size and configs 2026-02-09 12:25:36 -08:00
Paul 87a36020f6 Added a bunch of alpha pokemon functionality, bunch temp 2025-12-12 16:13:03 +10:00
1459 changed files with 16919 additions and 2258 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.
+105
View File
@@ -0,0 +1,105 @@
{
"lastSavedVersion": "1.7.1",
"maxPokemonLevel": 100,
"maxPokemonFriendship": 255,
"announceDropItems": true,
"defaultDropItemMethod": "on-entity",
"dropAfterDeathAnimation": false,
"ambientPokemonCryTicks": 1080,
"defaultKeyItems": [],
"defaultBoxCount": 30,
"pokemonSaveIntervalSeconds": 30,
"storageFormat": "nbt",
"preventCompletePartyDeposit": false,
"mongoDBConnectionString": "mongodb://localhost:27017",
"mongoDBDatabaseName": "cobblemon",
"maxVerticalCorrectionBlocks": 64,
"minimumLevelRangeMax": 10,
"enableSpawning": true,
"minimumDistanceBetweenEntities": 8.0,
"maxNearbyBlocksHorizontalRange": 4,
"maxNearbyBlocksVerticalRange": 2,
"maxVerticalSpace": 8,
"spawningZoneDiameter": 8,
"spawningZoneHeight": 16,
"ticksBetweenSpawnAttempts": 20.0,
"minimumSpawningZoneDistanceFromPlayer": 16.0,
"maximumSpawningZoneDistanceFromPlayer": 64.0,
"maximumSpawnsPerPass": 8,
"exportSpawnConfig": false,
"savePokemonToWorld": true,
"exportStarterConfig": false,
"autoUpdateShowdown": true,
"defaultFleeDistance": 32.0,
"allowExperienceFromPvP": true,
"experienceShareMultiplier": 0.5,
"luckyEggMultiplier": 1.5,
"allowSpectating": true,
"experienceMultiplier": 2.0,
"pokemonPerChunk": 1.0,
"pokeSnackPokemonPerChunk": 2.0,
"passiveStatuses": {
"cobblemon:poison": "180-300",
"cobblemon:poisonbadly": "180-300",
"cobblemon:paralysis": "180-300",
"cobblemon:frozen": "180-300",
"cobblemon:sleep": "180-300",
"cobblemon:burn": "180-300"
},
"infiniteHealerCharge": false,
"maxHealerCharge": 6.0,
"secondsToChargeHealingMachine": 900.0,
"defaultFaintTimer": 300,
"faintAwakenHealthPercent": 0.2,
"healPercent": 0.05,
"healTimer": 60,
"baseApricornTreeGenerationChance": 0.1,
"displayEntityLevelLabel": true,
"displayEntityNameLabel": true,
"displayNameForUnknownPokemon": false,
"displayEntityLabelsWhenCrouchingOnly": false,
"shinyRate": 8192.0,
"shinyNoticeParticlesDistance": 24.0,
"captureCalculator": "cobblemon",
"playerDamagePokemon": true,
"appleLeftoversChance": 0.025,
"maxRootsInArea": 5,
"bigRootPropagationChance": 0.1,
"energyRootChance": 0.25,
"maxDynamaxLevel": 10,
"teraTypeRate": 20.0,
"defaultPasturedPokemonLimit": 16,
"pastureBlockUpdateTicks": 40,
"pastureMaxWanderDistance": 64,
"pastureMaxPerChunk": 4.0,
"maxInsertedFossilItems": 2,
"walkingInBattleAnimations": false,
"battleWildMaxDistance": 12.0,
"tradeMaxDistance": 12.0,
"battlePvPMaxDistance": 32.0,
"battleSpectateMaxDistance": 64.0,
"maxPokedexScanningDetectionRange": 10.0,
"hideUnimplementedPokemonInThePokedex": false,
"summaryPokemonFollowCursor": true,
"partyPortraitAnimations": "NEVER_ANIMATE",
"pcProfileAnimations": "ANIMATE_SELECTED",
"summaryProfileAnimations": "ANIMATE_SELECTED",
"animateBattleTiles": false,
"thirdPersonViewBobbing": true,
"invertRoll": false,
"invertPitch": false,
"invertYaw": false,
"xAxisSensitivity": 1.0,
"yAxisSensitivity": 1.0,
"swapXAndYAxes": false,
"rightingDelay": -1.0,
"disableRoll": false,
"displayControlSeconds": 3,
"infiniteRideStamina": false,
"rememberRidingCamera": false,
"enableDebugKeys": true,
"despawnerNearDistance": 32.0,
"despawnerFarDistance": 96.0,
"despawnerMinAgeTicks": 600,
"despawnerMaxAgeTicks": 3600
}
@@ -13,5 +13,6 @@ public interface ChannelDuck {
void cobblemon$inverseAttenuation(float rolloffFactor);
void cobblemon$clearFilters();
// void cobblemon$exponentialAttenuation(float rolloff, float refDistance, float maxDistance);
void cobblemon$applyReverb(float decayTime, float gain, float diffusion, float density);
void cobblemon$clearReverb();
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.duck.ChannelDuck;
import com.mojang.blaze3d.audio.Channel;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.AL11;
import org.lwjgl.openal.EXTEfx;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -21,8 +22,10 @@ public abstract class ChannelMixin implements ChannelDuck {
@Shadow
private int source;
// Id of filter applied when sound is obstructed
// Ids of effects applied to the channel
private int cobblemon$lowPassFilterId = 0;
private int cobblemon$reverbEffectId = 0;
private int cobblemon$reverbSlotId = 0;
@Override
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
@@ -67,4 +70,54 @@ public abstract class ChannelMixin implements ChannelDuck {
cobblemon$lowPassFilterId = 0;
}
}
@Override
public void cobblemon$applyReverb(float decayTime, float gain, float diffusion, float density) {
// Clean up existing reverb first if it exists
cobblemon$clearReverb();
// Create the effect object
int effectId = EXTEfx.alGenEffects();
if (!EXTEfx.alIsEffect(effectId)) {
System.err.println("Failed to create OpenAL reverb effect");
return;
}
EXTEfx.alEffecti(effectId, EXTEfx.AL_EFFECT_TYPE, EXTEfx.AL_EFFECT_REVERB);
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DECAY_TIME, decayTime);
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_GAIN, gain);
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DIFFUSION, diffusion);
EXTEfx.alEffectf(effectId, EXTEfx.AL_REVERB_DENSITY, density);
// Create auxiliary effect slot and load effect into it
int slotId = EXTEfx.alGenAuxiliaryEffectSlots();
if (!EXTEfx.alIsAuxiliaryEffectSlot(slotId)) {
System.err.println("Failed to create OpenAL auxiliary effect slot");
EXTEfx.alDeleteEffects(effectId);
return;
}
EXTEfx.alAuxiliaryEffectSloti(slotId, EXTEfx.AL_EFFECTSLOT_EFFECT, effectId);
// Route source through the slot — send 0, no direct filter
AL11.alSource3i(source, EXTEfx.AL_AUXILIARY_SEND_FILTER, slotId, 0, EXTEfx.AL_FILTER_NULL);
cobblemon$reverbEffectId = effectId;
cobblemon$reverbSlotId = slotId;
}
@Override
public void cobblemon$clearReverb() {
if (cobblemon$reverbSlotId != 0) {
// Detach source from slot
AL11.alSource3i(source, EXTEfx.AL_AUXILIARY_SEND_FILTER,
EXTEfx.AL_EFFECTSLOT_NULL, 0, EXTEfx.AL_FILTER_NULL);
EXTEfx.alDeleteAuxiliaryEffectSlots(cobblemon$reverbSlotId);
cobblemon$reverbSlotId = 0;
}
if (cobblemon$reverbEffectId != 0) {
EXTEfx.alDeleteEffects(cobblemon$reverbEffectId);
cobblemon$reverbEffectId = 0;
}
}
}
@@ -90,6 +90,8 @@ 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.MARK_ASPECT
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
@@ -319,6 +321,8 @@ object Cobblemon {
GENDER_ASPECT.register()
COSMETIC_SLOT_ASPECT.register()
CHARACTERISTIC_RAINBOW_ASPECT.register()
ALPHA_ASPECT.register()
MARK_ASPECT.register()
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
@@ -321,6 +321,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()))
@@ -37,6 +37,9 @@ object CobblemonSensors {
@JvmField
val POKEMON_DEFEND_OWNER = register("pokemon_owner_under_attack", ::DefendOwnerSensor)
@JvmField
val ALPHA_LEVEL_MATCHING = register("alpha_level_matching", ::AlphaLevelMatchingSensor)
@JvmField
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
@@ -46,6 +46,9 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
): PokemonEntity? {
var bestLeader: PokemonEntity? = null
val (currentLeaderEntity, currentLeaderDefinition) = currentLeader ?: Pair(null, null)
if (currentLeaderEntity?.pokemon?.isAlpha == true) { // don't swap to a new alpha leader (I worry about mons constantly swapping between alphas if more are close by...)
return currentLeaderEntity
}
var bestTier = currentLeaderDefinition?.tier?.let { it + 1 } ?: 0 // Only upgrade to a higher tier leader
var bestDistance = Float.MAX_VALUE
nearbyEntities.findAll {
@@ -53,6 +56,7 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
it != currentLeader &&
it.pokemon.storeCoordinates.get()?.store !is PartyStore &&
it.getHerdSize() < it.behaviour.herd.maxSize &&
//!it.brain.hasMemoryValue(CobblemonMemories.HERD_LEADER) &&
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
}.forEach { possibleLeader ->
possibleLeader as PokemonEntity
@@ -65,13 +69,21 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
val distance = entity.distanceTo(possibleLeader)
if (bestMatchingHerdLeader.tier < bestTier) {
return@forEach // No need to check further if this leader is worse than the best found so far
} else if ((distance > bestDistance && !possibleLeaderIsAlreadyALeader) && bestMatchingHerdLeader.tier == bestTier) {
return@forEach // No need to check further if this leader is farther than the best found so far, unless they are already a leader - I'd prefer to join a pack
} else {
bestLeader = possibleLeader
bestDistance = distance
bestTier = bestMatchingHerdLeader.tier
} else if (bestMatchingHerdLeader.tier == bestTier) {
if (possibleLeader.pokemon.isAlpha && bestLeader?.pokemon?.isAlpha == true) {
if (entity.random.nextBoolean()) { // choose random alpha if multiple are present
bestLeader = possibleLeader
bestDistance = distance
}
return@forEach
}
if (distance > bestDistance && !possibleLeaderIsAlreadyALeader) {
return@forEach // No need to check further if this leader is farther than the best found so far, unless they are already a leader - I'd prefer to join a pack
}
}
bestLeader = possibleLeader
bestDistance = distance
bestTier = bestMatchingHerdLeader.tier
}
return bestLeader ?: currentLeaderEntity
}
@@ -124,4 +136,4 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
}
}
}
}
}
@@ -65,9 +65,8 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
}
val leader = instance.get(herdLeader).let(UUID::fromString).let(world::getEntity) as? PokemonEntity
val currentLeaderDefinition = leader?.let { leader ->
entity.behaviour.herd.toleratedLeaders.find { it.pokemon.matches(leader) }
}
val currentLeaderDefinition = leader?.let {
entity.behaviour.herd.bestMatchLeader(entity, it) }
if (leader == null || currentLeaderDefinition == null) {
entity.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
return@Trigger true // No leader, so we erase the memory
@@ -70,6 +70,7 @@ class SpawnBait(
val SHINY_REROLL = cobblemonResource("shiny_reroll")
val DROPS_REROLL = cobblemonResource("drops_reroll")
val HIDDEN_ABILITY_CHANCE = cobblemonResource("ha_chance")
val ALPHA_CHANCE = cobblemonResource("alpha_chance")
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
val FRIENDSHIP = cobblemonResource("friendship")
val RARITY_BUCKET = cobblemonResource("rarity_bucket")
@@ -90,6 +91,7 @@ class SpawnBait(
registerEffect(GENDER_CHANCE) { entity, effect -> FishingSpawnCause.alterGenderAttempt(entity, effect) }
registerEffect(LEVEL_RAISE) { entity, effect -> FishingSpawnCause.alterLevelAttempt(entity, effect) }
registerEffect(HIDDEN_ABILITY_CHANCE) { entity, _ -> FishingSpawnCause.alterHAAttempt(entity) }
registerEffect(ALPHA_CHANCE) { entity, effect -> FishingSpawnCause.alterAlphaAttempt(entity) }
registerEffect(FRIENDSHIP) { entity, effect -> FishingSpawnCause.alterFriendshipAttempt(entity, effect) }
CobblemonEvents.BAIT_EFFECT_REGISTRATION.post(BaitEffectFunctionRegistryEvent()) { event ->
event.functions.forEach { (type, function) -> registerEffect(type, function) }
@@ -27,7 +27,8 @@ class Mark(
val replace: List<ResourceLocation>?,
val group: String?,
val chance: Float = 0F,
val indexNumber: Int?
val indexNumber: Int?,
val aspects: List<String> = emptyList()
): StringRepresentable {
companion object {
@@ -42,7 +43,8 @@ class Mark(
buffer.readNullable { buffer.readList { buffer.readResourceLocation() } },
buffer.readNullable { buffer.readString() },
buffer.readFloat(),
buffer.readNullable { buffer.readInt() }
buffer.readNullable { buffer.readInt() },
buffer.readList { buffer.readString() }
)
}
}
@@ -83,5 +85,6 @@ class Mark(
buffer.writeNullable(group) { _, v -> buffer.writeString(v) }
buffer.writeFloat(chance)
buffer.writeNullable(indexNumber) { _, v -> buffer.writeInt(v) }
buffer.writeCollection(aspects) { _, v -> buffer.writeString(v) }
}
}
@@ -1803,6 +1803,7 @@ object MoLangFunctions {
}
map.put("nature") { StringValue(pokemon.nature.toString()) }
map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
map.put("is_alpha") { DoubleValue(pokemon.isAlpha) }
map.put("is_shiny") { DoubleValue(pokemon.shiny) }
map.put("is_in_party") { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
map.put("species") { pokemon.species.struct }
@@ -2819,4 +2820,4 @@ object MoLangFunctions {
}
}
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) = map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
@@ -132,6 +132,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)
@@ -368,6 +369,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
@@ -418,6 +420,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 } }
@@ -544,6 +547,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 ->
@@ -603,6 +607,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 }
@@ -646,13 +651,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)
@@ -660,6 +665,11 @@ open class PokemonProperties {
picked
}
else TeraTypes.forElementalType(baseTypes.random())
isAlpha?.let { pokemon.isAlpha = it }
if (this.scaleModifier == null) {
pokemon.initializeScale()
}
pokemon.assignSizeMarks() // check size of mon and assign marks to it
}
@JvmOverloads
@@ -682,6 +692,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()) }
@@ -717,6 +728,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
@@ -753,6 +765,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()) }
@@ -788,6 +801,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 }
@@ -823,6 +837,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 ->
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.ai.config.task.PollinateFlowerTaskConfig
import com.cobblemon.mod.common.api.spawning.influence.SaccharineLogSlatheredInfluence
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.particles.BlockParticleOption
import net.minecraft.core.particles.ParticleOptions
import net.minecraft.core.particles.ParticleTypes
@@ -28,7 +29,14 @@ val aspectParticleMap: Map<String, ParticleData> = mapOf(
ParticleTypes.BLOCK,
CobblemonBlocks.POKE_SNACK.defaultBlockState()
), 0.05, 3),
PollinateFlowerTaskConfig.HAS_NECTAR_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_NECTAR, 0.075, 1)
PollinateFlowerTaskConfig.HAS_NECTAR_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_NECTAR, 0.075, 1),
// "alpha" to ParticleData.SnowstormParticle(
// particle = cobblemonResource("alpha_eyes"),
// chance = 1.0,
// amount = 1,
// locators = listOf("eye1", "eye2", "eye_left", "eye_right", "eye", "locator_eye_left", "locator_eye_right"),
// cooldownMs = 1000L
// )
)
sealed class ParticleData {
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.SpawnBucket
@@ -15,6 +16,10 @@ import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.util.weightedSelection
import com.google.gson.annotations.SerializedName
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
/**
* A spawn detail that spawns a herd of Pokémon. This relies on the same [PokemonSpawnAction]s as
@@ -53,6 +58,8 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
var isLeader: Boolean? = null
var weight: Float = 1F
var maxTimes = 10
var isAlpha: Boolean? = null
var heldItem: ResourceLocation? = null
}
override fun isValid() = super.isValid() && herdablePokemon.all { it.pokemon.hasSpecies() && it.weight > 0F && it.maxTimes > 0 }
@@ -109,13 +116,25 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
min..max
} ?: level..level
herdable.pokemon.isAlpha = herdable.isAlpha
val heldItem = herdable.heldItem?.let { heldItemId ->
val item = spawnablePosition.world.registryAccess().registryOrThrow(Registries.ITEM).get(heldItemId)
if (item == Items.AIR) {
LOGGER.error("Unable to find matching herd held item for ID: $heldItemId")
null
} else {
ItemStack(item, 1)
}
}
return PokemonSpawnAction(
spawnablePosition = spawnablePosition,
bucket = bucket,
detail = this,
props = herdable.pokemon,
drops = herdable.dropTable,
heldItem = null,
heldItem = heldItem,
levelRange = levelRange
).also {
if (herdable.isLeader == true) {
@@ -128,6 +128,14 @@ class FishingSpawnCause(
}
fun alterAlphaAttempt(pokemonEntity: PokemonEntity) {
if (pokemonEntity.pokemon.isAlpha) {
return
}
pokemonEntity.pokemon.isAlpha = true
pokemonEntity.entityData.set(PokemonEntity.IS_ALPHA, true)
}
fun alterFriendshipAttempt(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
if (pokemonEntity.pokemon.friendship + effect.value > Cobblemon.config.maxPokemonFriendship)
pokemonEntity.pokemon.setFriendship(Cobblemon.config.maxPokemonFriendship)
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
@@ -15,7 +16,9 @@ import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.toVec3d
import kotlin.random.Random
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerLevel
@@ -51,6 +54,8 @@ class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInflu
if (Math.random() <= HIDDEN_ABILITY_CHANCE) {
FishingSpawnCause.alterHAAttempt(entity)
}
rollShiny(entity.pokemon)
rollAlpha(entity.pokemon)
if (!activated) {
val logPos = pos
@@ -137,4 +142,21 @@ class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInflu
}
return null
}
fun rollAlpha(pokemon: Pokemon) {
val chance = Cobblemon.config.honeySlatherAlphaChance
if (chance > 0 && Random.nextInt(chance) == 0) {
pokemon.isAlpha = true
}
}
fun rollShiny(pokemon: Pokemon) {
if (pokemon.shiny) {
return
}
val chance = Cobblemon.config.honeySlatherShinyChance
if (chance > 0 && Random.nextInt(chance) == 0) {
pokemon.shiny = true
}
}
}
@@ -202,7 +202,7 @@ open class PlayerPartyStore(
val shoulderEntity = if(isLeft) player.shoulderEntityLeft else player.shoulderEntityRight
val pokemon = find { it.uuid == shoulderEntity.getCompound("Pokemon").getUUID(DataKeys.POKEMON_UUID) }
// No longer valid if (in order): not in party, not the correct shoulder, no longer shoulder mountable
if (pokemon == null || (pokemon.state as? ShoulderedState)?.isLeftShoulder != isLeft || !pokemon.form.shoulderMountable) {
if (pokemon == null || (pokemon.state as? ShoulderedState)?.isLeftShoulder != isLeft || !pokemon.form.shoulderMountable || pokemon.isAlpha) {
return false
}
player.updateShoulderNbt(pokemon)
@@ -282,9 +282,13 @@ object BattleBuilder {
}
playerActor.battleTheme = pokemonEntity.getBattleTheme()
val finalFormat = if (pokemonEntity.pokemon.isAlpha && BattleRules.WILD_ALPHA !in battleFormat.ruleSet) {
BattleFormat.setBattleRules(battleFormat, battleFormat.ruleSet + BattleRules.WILD_ALPHA)
} else battleFormat
return if (errors.isEmpty) {
BattleRegistry.startBattle(
battleFormat = battleFormat,
battleFormat = finalFormat,
side1 = BattleSide(playerActor),
side2 = BattleSide(wildActor)
).ifSuccessful {
@@ -18,4 +18,5 @@ object BattleRules {
const val CANCEL_MOD = "Cancel Mod"
const val SLEEP_CLAUSE = "Sleep Clause"
const val HP_PERCENTAGE_MOD = "HP Percentage Mod"
const val WILD_ALPHA = "Wild Alpha"
}
@@ -8,16 +8,23 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
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.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.chat.Component
import java.util.concurrent.CompletableFuture
/**
* Format: |-start|POKEMON|EFFECT
@@ -26,25 +33,59 @@ import net.minecraft.network.chat.Component
* @author Deltric
* @since January 21st, 2022
*/
class StartInstruction(val message: BattleMessage): InterpreterInstruction {
class StartInstruction(val message: BattleMessage): ActionEffectInstruction, InterpreterInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("start")
override fun invoke(battle: PokemonBattle) {
override fun preActionEffect(battle: PokemonBattle)
{
val pokemon = message.battlePokemon(0, battle) ?: return
val effectID = message.effectAt(1)?.id ?: return
// skip adding contexts for every time the perish counter decrements
if (!effectID.contains("perish")) {
// don't need to add unique: showdown won't send -start instruction if volatile status is already present
pokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.VOLATILE, battle))
}
battle.dispatch{
battle.minorBattleActions[pokemon.uuid] = message
GO
}
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
val actionEffect = ActionEffects.actionEffects[cobblemonResource("start_${effectID}")] ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers,
level = battle.players.firstOrNull()?.level()
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
val optionalEffect = message.effect()
val optionalPokemon = message.battlePokemonFromOptional(battle)
val optionalPokemonName = optionalPokemon?.getName()
val extraEffect = message.effectAt(2)?.typelessData ?: Component.literal("UNKOWN")
// skip adding contexts for every time the perish counter decrements
if (!effectID.contains("perish")) {
// don't need to add unique: showdown won't send -start instruction if volatile status is already present
pokemon.contextManager.add(ShowdownInterpreter.getContextFromAction(message, BattleContext.Type.VOLATILE, battle))
}
battle.minorBattleActions[pokemon.uuid] = message
if (!message.hasOptionalArgument("silent")) {
val lang = if (optionalEffect?.id == "reflecttype" && optionalPokemonName != null)
battleLang("start.reflecttype", pokemon.getName(), optionalPokemonName)
@@ -59,7 +100,8 @@ class StartInstruction(val message: BattleMessage): InterpreterInstruction {
}
battle.broadcastChatMessage(lang)
}
WaitDispatch(1F)
//We check holds here so the chat msg + particle effect happen more concurrently, instead of sequentially
UntilDispatch { "effects" !in holds}
}
}
}
@@ -38,6 +38,7 @@ import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.server
import java.util.UUID
import kotlin.math.ceil
import kotlin.random.Random
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
@@ -124,7 +125,7 @@ class FossilMultiblockStructure (
val ballType = (stack.item as PokeBallItem).pokeBall
stack?.consume(1, player)
val pokemon = this.resultingFossil?.result?.create(player)
val pokemon = this.resultingFossil?.result?.create(player)?.also(::rollShiny)?.also(::rollAlpha)
if(pokemon != null) {
pokemon.caughtBall = ballType
@@ -302,7 +303,7 @@ class FossilMultiblockStructure (
override fun onTriggerEvent(state: BlockState?, world: ServerLevel?, pos: BlockPos?, random: RandomSource?) {
// instantiate the pokemon as a new entity and spawn it at the location of the machine
if(this.protectionTime <= 0) {
val wildPokemon: Pokemon = if (hasCreatedPokemon) resultingFossil?.result?.create() ?: return else return
val wildPokemon: Pokemon = if (hasCreatedPokemon) resultingFossil?.result?.create()?.also(::rollShiny)?.also(::rollAlpha) ?: return else return
val direction = state?.getValue(HorizontalDirectionalBlock.FACING)?.opposite
if(pos != null && direction != null && world != null) {
val success = this.spawn(world, pos, direction, wildPokemon)
@@ -327,7 +328,7 @@ class FossilMultiblockStructure (
val tankTopEntity = world.getBlockEntity(tankBasePos.above()) as? MultiblockEntity
val tankBaseBlockState = tankBaseEntity?.blockPos?.let { world.getBlockState(it) }
val direction = if (tankBaseBlockState?.block == CobblemonBlocks.RESTORATION_TANK) tankBaseBlockState.getValue(HorizontalDirectionalBlock.FACING).opposite else Direction.UP
val wildPokemon: Pokemon? = if (hasCreatedPokemon) resultingFossil?.result?.create() else null
val wildPokemon: Pokemon? = if (hasCreatedPokemon) resultingFossil?.result?.create()?.also(::rollShiny)?.also(::rollAlpha) else null
monitorEntity?.multiblockStructure = null
analyzerEntity?.multiblockStructure = null
@@ -376,6 +377,24 @@ class FossilMultiblockStructure (
}
}
fun rollAlpha(pokemon: Pokemon) {
val chance = Cobblemon.config.fossilMachineAlphaChance
if (Random.nextInt(chance) == 0) {
pokemon.isAlpha = true
}
}
fun rollShiny(pokemon: Pokemon) {
if (pokemon.shiny) {
return
}
val chance = Cobblemon.config.fossilMachineShinyChance
if (Random.nextInt(chance) == 0) {
pokemon.shiny = true
}
}
override fun tick(world: Level) {
if (protectionTime > 0) protectionTime--
if (protectionTime == 0) {
@@ -672,4 +691,4 @@ class FossilMultiblockStructure (
}
}
}
}
@@ -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
@@ -62,6 +60,8 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
const val BEAM_EXTEND_TIME = 0.2F
const val POKEBALL_AIR_TIME = 0.5F
const val SHINY_PARTICLE_COOLDOWN = 3.5F
private const val ALPHA_EYE_PARTICLE_COOLDOWN_MS = 1000L
private val ALPHA_MARK_ID = cobblemonResource("mark_alpha").toString()
}
override val schedulingTracker: SchedulingTracker
@@ -69,7 +69,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
lateinit var currentEntity: PokemonEntity
var phaseTarget: Entity? = null
var entityScaleModifier = 1F
var activeSendoutScale = 1F // The current scale used for the send-out animation
override fun getEntity() = currentEntity
@@ -89,6 +89,8 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
var sendOutOffset: Vec3? = null
var playedSendOutSound: Boolean = false
var playedThrowingSound: Boolean = false
private var lastAlphaEyeParticle = 0L
val eyeTrailPositions = mutableMapOf<String, ArrayDeque<Pair<Vec3, Long>>>() // List of eye locators that are tracking their position overtime for alpha eye trail rendering
val secondsSinceBeamEffectStarted: Float
get() = (System.currentTimeMillis() - beamStartTime) / 1000F
@@ -112,6 +114,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")
@@ -130,7 +134,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
null
} ?: return
val primaryAnimation = PrimaryAnimation(animation)
after(seconds = 3F) { entityScaleModifier = 0F }
after(seconds = 3F) { activeSendoutScale = 0F }
this.addPrimaryAnimation(primaryAnimation)
}
} else if (data == PokemonEntity.BEAM_MODE) {
@@ -148,7 +152,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
// the duplicate particle effects.
if (ballDone) {
playedSendOutSound = false
entityScaleModifier = 0F
activeSendoutScale = 0F
beamStartTime = System.currentTimeMillis()
ballStartTime = System.currentTimeMillis()
currentEntity.isInvisible = true
@@ -259,7 +263,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
currentEntity.after(seconds = POKEBALL_AIR_TIME) {
// Skip scaling task if the Pokémon is already being recalled
if (scaleAnimTask == null || scaleAnimTask!!.expired) {
scaleAnimTask = lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
scaleAnimTask = lerpOnClient(BEAM_SHRINK_TIME) { activeSendoutScale = it }
currentEntity.isInvisible = false
currentEntity.isSilent = false
currentEntity.after(seconds = POKEBALL_AIR_TIME * 2) {
@@ -276,7 +280,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
// Adding the ballDone check to here as well so that the send out sound doesn't play twice when putting pokemon into pasture block.
if (ballDone) {
playedSendOutSound = false
entityScaleModifier = 0F
activeSendoutScale = 0F
currentEntity.isInvisible = false
currentEntity.isSilent = false
ballDone = false
@@ -304,7 +308,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
)
playedSendOutSound = true
}
scaleAnimTask = lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
scaleAnimTask = lerpOnClient(BEAM_SHRINK_TIME) { activeSendoutScale = it }
currentEntity.after(seconds = BEAM_SHRINK_TIME * 2) {
ballOffset = 0f
ballRotOffset = 0f
@@ -319,12 +323,12 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ballRotOffset = 0f
sendOutPosition = null
afterOnClient(seconds = BEAM_EXTEND_TIME) {
entityScaleModifier = 1F
activeSendoutScale = 1F
// Cancel any ongoing scale animation tasks
scaleAnimTask?.expire()
scaleAnimTask = lerpOnClient(BEAM_SHRINK_TIME) {
entityScaleModifier = (1 - it)
activeSendoutScale = (1 - it)
}
}
}
@@ -478,6 +482,32 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
}
}
// TODO: use the aspect
private fun spawnAlphaEyeParticles() {
if (currentEntity.entityData.get(PokemonEntity.MARK) != ALPHA_MARK_ID) {
return
}
val now = System.currentTimeMillis()
if (now - lastAlphaEyeParticle < ALPHA_EYE_PARTICLE_COOLDOWN_MS) {
return
}
val locatorCandidates = mutableListOf("eye1", "eye2", "eye_left", "eye_right", "eye")
locatorCandidates.addAll(getMatchingLocators("eye"))
locatorCandidates.addAll(listOf("locator_eye_left", "locator_eye_right"))
val locators = locatorCandidates.distinct().filter { locatorStates[it] != null }
if (locators.isEmpty()) {
return
}
locators.forEach { locator ->
runtime.resolve("q.particle('cobblemon:alpha_eyes', '$locator')".asExpressionLike())
}
lastAlphaEyeParticle = now
}
fun cry() {
val model = currentModel ?: return
if (cryAnimation != null && (cryAnimation in activeAnimations || cryAnimation == primaryAnimation)) {
@@ -525,4 +555,4 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
this.getEntity().z + offset.z
)
}
}
}
@@ -42,6 +42,7 @@ import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokem
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.PokemonSizeCategory
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isInventoryKeyPressed
import com.cobblemon.mod.common.util.lang
@@ -602,6 +603,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) "cobblemon.ui.pokedex.scan.alpha".text() else PokemonSizeCategory.translationKey(sizeCategory).text()).bold(),
scale = 1F,
shadow = true,
centered = true
)
// Shiny Icon
if (selectedPokemon.shiny) {
blitk(
@@ -751,4 +766,4 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
}
super.onClose()
}
}
}
@@ -70,7 +70,7 @@ class ParticleStorm(
val pokemon = pokeEntity?.pokemon
//Use form data if available, species as fall back
val baseScale = pokemon?.form?.baseScale ?: pokemon?.species?.baseScale ?: 1.0F
val pokemonScale = pokemon?.scaleModifier ?: 1.0F
val pokemonScale = pokemon?.effectiveScale ?: 1.0F
val entityScale = pokeEntity?.scale ?: 1.0F
DoubleValue(baseScale * pokemonScale * entityScale)
}
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.PokemonSizeCategory
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.mojang.blaze3d.systems.RenderSystem
@@ -86,7 +87,7 @@ class PokedexScannerRenderer {
var infoDisplayedCounter = 0
usageContext.availableInfoFrames.forEachIndexed {index, isLeftSide ->
if (isLeftSide !== null) {
if (infoDisplayedCounter > 1 && !usageContext.isPokemonInFocusOwned) return@forEachIndexed
if (infoDisplayedCounter > 2 && !usageContext.isPokemonInFocusOwned) return@forEachIndexed
infoDisplayedCounter++
// Frames
val isInnerFrame = index == 1 || index == 2
@@ -179,6 +180,29 @@ class PokedexScannerRenderer {
}
if (infoDisplayedCounter == 3) {
val pokemon = pokedexEntityData.pokemon
val entityAlpha = (usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)
?.entityData
?.get(PokemonEntity.IS_ALPHA)
val isAlpha = entityAlpha ?: pokemon.isAlpha
val sizeText = if (isAlpha) {
lang("ui.pokedex.scan.alpha").bold()
} else {
val sizeCategoryText = PokemonSizeCategory.translationKey(pokemon.getSizeCategory()).text()
lang("ui.pokedex.scan.size", sizeCategoryText).bold()
}
drawScaledText(
context = graphics,
font = CobblemonResources.DEFAULT_LARGE,
text = sizeText,
x = centerX + xOffset + xOffsetText,
y = centerY + yOffset + yOffsetText,
shadow = true,
centered = true
)
}
if (infoDisplayedCounter == 4) {
val typeText = lang("type.suffix", pokedexEntityData.getApparentForm().types.map { it.displayName.copy() }.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
val typeWidth = Minecraft.getInstance().font.width(typeText.font(CobblemonResources.DEFAULT_LARGE))
// Split into 2 lines if text width is too long
@@ -438,4 +462,4 @@ class PokedexScannerRenderer {
renderScanOverlay(graphics, tickDelta)
}
}
}
}
@@ -117,7 +117,7 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
model.context = context
context.put(RenderContext.SPECIES, shoulderData.species.resourceIdentifier)
context.put(RenderContext.ASPECTS, shoulderData.aspects)
val scale = shoulderData.form.baseScale * shoulderData.scaleModifier
val scale = shoulderData.form.baseScale
val width = shoulderData.form.hitbox.width
val heightOffset = -1.5 * scale
val widthOffset = width / 2 - 0.7
@@ -222,7 +222,7 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
it.species,
it.form,
it.aspects,
it.scaleModifier,
it.effectiveScale,
it.heldItem
)
}, { null })
@@ -268,4 +268,4 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
return null
}
}
}
}
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.SimpleQuirk
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.util.exists
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
@@ -113,7 +114,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
@@ -461,6 +462,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
}
else {
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
if (!texture.exists()) continue
renderLayer = getLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
}
val consumer = provider.getBuffer(renderLayer)
@@ -568,9 +570,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
// Is there any reason why we should actually change the pose?
if (entity != null && (poseName == null || currentPose == null || !currentPose.isSuitable(state) || entityPoseType !in currentPose.poseTypes)) {
val desirablePose = getFirstSuitablePose(state, entityPoseType)
if (desirablePose == currentPose) {
return currentPose
}
// If this if succeeds then it just no longer fits this pose
if (currentPose != null) {
// Don't apply pose correction until the current primary animation is complete.
@@ -747,7 +746,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
var yRot = 0f
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
if (entity is PokemonEntity) {
scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
scale = entity.pokemon.form.baseScale * entity.pokemon.effectiveScale * (entity.delegate as PokemonClientDelegate).activeSendoutScale
// If scale is 0 we start getting NaNs
scale.coerceAtLeast(0.01f)
if (entity.passengers.isNotEmpty() && entity.controllingPassenger is OrientationControllable
@@ -18,23 +18,25 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.sound.instances.AlphaCrySoundInstance
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.genericRuntime
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.resolveDouble
import java.util.SortedMap
import java.util.TreeMap
import net.minecraft.CrashReport
import net.minecraft.ReportedException
import net.minecraft.client.Minecraft
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import java.util.*
data class BedrockAnimationGroup(
val formatVersion: String,
@@ -110,7 +112,13 @@ class BedrockSoundKeyframe(
if (soundEvent != null) {
if (entity != null) {
if (!entity.isSilent) {
entity.level().playLocalSound(entity, soundEvent, entity.soundSource, 1F, 1F)
// If this entity is an alpha then play their cry as such
if (entity is PokemonEntity && entity.pokemon.isAlpha) {
Minecraft.getInstance().soundManager.play(
AlphaCrySoundInstance(soundEvent, entity.soundSource, 1.5f, 0.7f, entity, entity.level().random.nextLong()))
} else {
entity.level().playLocalSound(entity, soundEvent, entity.soundSource, 1F, 1F)
}
}
} else {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1F, 1F))
@@ -0,0 +1,130 @@
/*
* 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.render.pokemon
import com.cobblemon.mod.common.client.render.MatrixWrapper
import net.minecraft.client.renderer.RenderType
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
/**
*
* Renders an alpha eye trail based on a list of previous eye locator positions
*
* @author Jackson
* @since March 21st, 2026
*/
private const val TRAIL_MAX_WIDTH = 0.015f
private const val TRAIL_MAX_ALPHA = 0.8f
private val TRAIL_COLOR = Vector3f(0.7f, 0.0f, 0.0f)
private const val TRAIL_DURATION_MS = 250 // lookback distance in ms for previous eye positions for trail rendering
private val EYE_LOCATOR_NAMES = setOf(
"eye_left", "eye_right", "eye",
"locator_eye_left", "locator_eye_right",
"eye1", "eye2"
)
/**
* Updates the eye trail positions based on the current entity position and locator states
*/
fun updateEyeTrail(
aspects: Set<String>,
locatorStates: Map<String, MatrixWrapper>,
entityPos: Vec3,
trailPositions: MutableMap<String, ArrayDeque<Pair<Vec3, Long>>>
) {
if ("alpha_eyes" !in aspects) {
trailPositions.clear()
return
}
for (locatorName in EYE_LOCATOR_NAMES) {
val wrapper = locatorStates[locatorName] ?: continue
val pos = wrapper.matrix.getTranslation(Vector3f())
val worldPos = Vec3(
entityPos.x + pos.x,
entityPos.y + pos.y,
entityPos.z + pos.z
)
val now = System.currentTimeMillis()
val deque = trailPositions.getOrPut(locatorName) { ArrayDeque() }
deque.addLast(worldPos to now)
while (deque.isNotEmpty() && now - deque.first().second > TRAIL_DURATION_MS) {
deque.removeFirst()
}
}
}
fun renderEyeTrail(
positions: ArrayDeque<Pair<Vec3, Long>>,
entityPos: Vec3,
camPos: Vec3,
poseStack: PoseStack,
bufferSource: MultiBufferSource
) {
if (positions.size < 2) return
val consumer = bufferSource.getBuffer(RenderType.lightning())
val matrix = poseStack.last().pose()
val n = positions.size
for (i in 0 until n - 1) {
val curr = positions[i].first
val next = positions[i + 1].first
val tCurr = i.toFloat() / (n - 1)
val tNext = (i + 1).toFloat() / (n - 1)
val wCurr = TRAIL_MAX_WIDTH * tCurr
val wNext = TRAIL_MAX_WIDTH * tNext
val aCurr = TRAIL_MAX_ALPHA * tCurr
val aNext = TRAIL_MAX_ALPHA * tNext
val localCurr = curr.subtract(entityPos)
val localNext = next.subtract(entityPos)
val dir = next.subtract(curr).normalize()
val toCamera = camPos.subtract(curr).normalize()
val perp = dir.cross(toCamera).normalize()
val px = perp.x.toFloat()
val py = perp.y.toFloat()
val pz = perp.z.toFloat()
val r = TRAIL_COLOR.x
val g = TRAIL_COLOR.y
val b = TRAIL_COLOR.z
consumer.addVertex(matrix,
(localCurr.x - px * wCurr).toFloat(),
(localCurr.y - py * wCurr).toFloat(),
(localCurr.z - pz * wCurr).toFloat()
).setColor(r, g, b, aCurr)
consumer.addVertex(matrix,
(localCurr.x + px * wCurr).toFloat(),
(localCurr.y + py * wCurr).toFloat(),
(localCurr.z + pz * wCurr).toFloat()
).setColor(r, g, b, aCurr)
consumer.addVertex(matrix,
(localNext.x + px * wNext).toFloat(),
(localNext.y + py * wNext).toFloat(),
(localNext.z + pz * wNext).toFloat()
).setColor(r, g, b, aNext)
consumer.addVertex(matrix,
(localNext.x - px * wNext).toFloat(),
(localNext.y - py * wNext).toFloat(),
(localNext.z - pz * wNext).toFloat()
).setColor(r, g, b, aNext)
}
}
@@ -42,7 +42,6 @@ import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.DoubleRange
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import com.google.common.primitives.Floats
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.ChatFormatting
@@ -104,7 +103,7 @@ class PokemonRenderer(
packedLight: Int
) {
val clientDelegate = entity.delegate as PokemonClientDelegate
shadowRadius = (min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier)/entity.scale
shadowRadius = (min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).activeSendoutScale)/entity.scale
model.posableModel = VaryingModelRepository.getPoser(entity.pokemon.species.resourceIdentifier, clientDelegate)
model.posableModel.context = model.context
model.setupEntityTypeContext(entity)
@@ -134,12 +133,12 @@ class PokemonRenderer(
drawPlatform(
poseMatrix,
entity,
(entity.delegate as PokemonClientDelegate).entityScaleModifier,
(entity.delegate as PokemonClientDelegate).activeSendoutScale,
buffer,
packedLight,
)
// keeps the pokemon's root on the raft
poseMatrix.translate(0.0, 0.25 * (entity.delegate as PokemonClientDelegate).entityScaleModifier, 0.0)
poseMatrix.translate(0.0, 0.25 * (entity.delegate as PokemonClientDelegate).activeSendoutScale, 0.0)
}
modelNow.setLayerContext(buffer, clientDelegate, VaryingModelRepository.getLayers(entity.pokemon.species.resourceIdentifier, clientDelegate))
@@ -151,6 +150,26 @@ class PokemonRenderer(
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
}
// Call rendering for alpha eye trail
updateEyeTrail(
aspects = clientDelegate.currentAspects,
locatorStates = clientDelegate.locatorStates,
entityPos = entity.getPosition(partialTicks),
trailPositions = clientDelegate.eyeTrailPositions
)
if (clientDelegate.eyeTrailPositions.isNotEmpty()) {
val camPos = Minecraft.getInstance().gameRenderer.mainCamera.position
for ((_, positions) in clientDelegate.eyeTrailPositions) {
renderEyeTrail(
positions = positions,
entityPos = entity.getPosition(partialTicks),
camPos = camPos,
poseStack = poseMatrix,
bufferSource = buffer
)
}
}
modelNow.green = 1F
modelNow.blue = 1F
modelNow.resetLayerContext()
@@ -292,7 +311,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).activeSendoutScale
pPoseStack.scale(scale, scale, scale)
}
@@ -308,7 +327,7 @@ class PokemonRenderer(
*/
fun renderBeam(matrixStack: PoseStack, partialTicks: Float, entity: PokemonEntity, beamTarget: Entity, buffer: MultiBufferSource, offset: Vec3) {
val clientDelegate = entity.delegate as PokemonClientDelegate
val pokemonPosition = entity.position().add(0.0, entity.bbHeight / 2.0 * clientDelegate.entityScaleModifier.toDouble(), 0.0)
val pokemonPosition = entity.position().add(0.0, entity.bbHeight / 2.0 * clientDelegate.activeSendoutScale.toDouble(), 0.0)
var beamSourcePosition = if (beamTarget is EmptyPokeBallEntity) {
(beamTarget.delegate as PokeBallPosableState).locatorStates["beam"]?.getOrigin() ?: beamTarget.position()
} else {
@@ -0,0 +1,28 @@
/*
* 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.sound.instances
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
/**
* A class that acts as a "marker" for alpha cries. This is to avoid having to pass in entity info
* to the [NeoforgeSoundEngineMixin] and [FabricSoundEngineMixin]. The play() mixin can just check for it
* being an instance of [AlphaCrySoundInstance]
*/
class AlphaCrySoundInstance(
sound: SoundEvent,
source: SoundSource,
volume: Float,
pitch: Float,
entity: Entity,
seed: Long
) : EntityBoundSoundInstance(sound, source, volume, pitch, entity, seed)
@@ -20,7 +20,6 @@ import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.MobSpawnType
object SpawnAllPokemon {
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.util.alias
import com.cobblemon.mod.common.util.commandLang
import com.cobblemon.mod.common.util.permission
import com.cobblemon.mod.common.util.toBlockPos
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
@@ -76,9 +75,7 @@ object SpawnPokemon {
pokemonEntity.moveTo(pos.x, pos.y, pos.z, pokemonEntity.yRot, pokemonEntity.xRot)
pokemonEntity.entityData.set(PokemonEntity.SPAWN_DIRECTION, pokemonEntity.random.nextFloat() * 360F)
pokemonEntity.finalizeSpawn(world, world.getCurrentDifficultyAt(blockPos), MobSpawnType.COMMAND, null)
if (world.addFreshEntity(pokemonEntity)) {
return Command.SINGLE_SUCCESS
}
world.addFreshEntity(pokemonEntity)
throw FAILED_SPAWN_EXCEPTION.create()
} catch (e: Exception) {
e.printStackTrace()
@@ -210,6 +210,18 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Spawning, lang = "shiny_rate", SERVER)
var shinyRate = 8192F
@CobblemonConfigField(Category.Pokemon, lang = "fossil_machine_alpha_chance", SERVER)
var fossilMachineAlphaChance = 100
@CobblemonConfigField(Category.Pokemon, lang = "fossil_machine_shiny_chance", SERVER)
var fossilMachineShinyChance = 500
@CobblemonConfigField(Category.Pokemon, lang = "honey_slather_alpha_chance", SERVER)
var honeySlatherAlphaChance = 1000
@CobblemonConfigField(Category.Pokemon, lang = "honey_slather_shiny_chance", SERVER)
var honeySlatherShinyChance = 8192
@CobblemonConfigField(Category.Pokemon, lang = "shiny_notice_particles_distance", CLIENT)
var shinyNoticeParticlesDistance = 24F
@@ -219,6 +231,30 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Pokemon, lang = "player_damage_pokemon", SERVER)
var playerDamagePokemon = true
@CobblemonConfigField(Category.Pokemon, lang = "pokemon_intrinsic_size_min", SERVER)
var pokemonIntrinsicSizeMin = 0.95F
@CobblemonConfigField(Category.Pokemon, lang = "pokemon_intrinsic_size_max", SERVER)
var pokemonIntrinsicSizeMax = 1.05F
@CobblemonConfigField(Category.Pokemon, lang = "alpha_pokemon_size_small_multiplier", SERVER)
var alphaPokemonSizeSmallMultiplier = 1.70F
@CobblemonConfigField(Category.Pokemon, lang = "alpha_pokemon_size_medium_multiplier", SERVER)
var alphaPokemonSizeMediumMultiplier = 1.55F
@CobblemonConfigField(Category.Pokemon, lang = "alpha_pokemon_size_large_multiplier", SERVER)
var alphaPokemonSizeLargeMultiplier = 1.40F
@CobblemonConfigField(Category.Pokemon, lang = "alpha_pokemon_size_extra_large_multiplier", SERVER)
var alphaPokemonSizeExtraLargeMultiplier = 1.25F
@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
@@ -360,4 +396,4 @@ class CobblemonConfig {
}
return newConfig
}
}
}
@@ -84,6 +84,7 @@ object CobblemonDataProvider : DataProvider {
this.register(CobblemonMovesetBuilders, reloadable = false)
this.register(Abilities, reloadable = false)
this.register(CobblemonBehaviours, reloadable = false)
this.register(Marks, reloadable = false)
this.register(PokemonSpecies, reloadable = false)
this.register(SpeciesAdditions, reloadable = false)
this.register(PokeBalls, reloadable = false)
@@ -107,7 +108,6 @@ object CobblemonDataProvider : DataProvider {
this.register(CobblemonCosmeticItems, reloadable = true)
this.register(CobblemonCallbacks, reloadable = true)
this.register(CobblemonUnlockableWallpapers, reloadable = true)
this.register(Marks, reloadable = false)
this.register(StarterDataLoader, reloadable = true)
CobblemonSpawnPools.load()
@@ -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 {
@@ -1252,7 +1254,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)
@@ -1531,7 +1533,7 @@ open class PokemonEntity(
}
fun tryMountingShoulder(player: ServerPlayer): Boolean {
if (this.pokemon.belongsTo(player) && this.hasRoomToMount(player)) {
if (this.pokemon.belongsTo(player) && this.hasRoomToMount(player) && this.canSitOnShoulder()) {
CobblemonEvents.SHOULDER_MOUNT.postThen(
ShoulderMountEvent(
player,
@@ -1589,7 +1591,7 @@ open class PokemonEntity(
nbt.putString(DataKeys.SHOULDER_SPECIES, this.pokemon.species.resourceIdentifier.toString())
nbt.putString(DataKeys.SHOULDER_FORM, this.pokemon.form.name)
nbt.put(DataKeys.SHOULDER_ASPECTS, this.pokemon.aspects.map(StringTag::valueOf).toNbtList())
nbt.putFloat(DataKeys.SHOULDER_SCALE_MODIFIER, this.pokemon.scaleModifier)
nbt.putFloat(DataKeys.SHOULDER_SCALE_MODIFIER, this.pokemon.effectiveScale)
nbt.put(
DataKeys.SHOULDER_ITEM,
this.level().registryAccess()
@@ -2615,11 +2617,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
}
@@ -2631,7 +2637,10 @@ 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
}
}
}
}
@@ -230,18 +230,17 @@ class PokemonServerDelegate : PokemonSideDelegate {
fun updateTrackedValues() {
val trackedSpecies = mock?.species ?: entity.pokemon.species.resourceIdentifier.toString()
val trackedNickname = mock?.nickname ?: entity.pokemon.nickname ?: Component.empty()
val trackedMark = entity.pokemon.activeMark?.identifier.toString()
val trackedAspects = mock?.aspects ?: entity.pokemon.aspects
val trackedBall = mock?.pokeball ?: entity.pokemon.caughtBall.name.toString()
val trackedScaleModifier = mock?.scaleModifier ?: entity.pokemon.scaleModifier
val trackedScaleModifier = mock?.scaleModifier ?: entity.pokemon.effectiveScale
entity.ownerUUID = entity.pokemon.getOwnerUUID()
entity.entityData.set(PokemonEntity.SPECIES, trackedSpecies)
if (entity.entityData.get(PokemonEntity.NICKNAME) != trackedNickname) {
entity.entityData.set(PokemonEntity.NICKNAME, trackedNickname)
}
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)
@@ -266,6 +265,11 @@ class PokemonServerDelegate : PokemonSideDelegate {
entity.entityData.set(PokemonEntity.RIDE_BOOSTS, newRideBoosts)
}
val trackedMark = entity.pokemon.activeMark?.identifier.toString()
if (entity.entityData.get(PokemonEntity.MARK) != trackedMark) {
entity.entityData.set(PokemonEntity.MARK, trackedMark)
}
updateShownItem()
updatePoseType()
}
@@ -0,0 +1,69 @@
/*
* 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.entity.pokemon.ai.sensors
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.party
import java.util.UUID
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.Sensor
class AlphaLevelMatchingSensor : Sensor<PokemonEntity>(20) {
private var lastMatchingPlayerId: UUID? = null
override fun requires() = setOf(MemoryModuleType.NEAREST_PLAYERS)
override fun doTick(world: ServerLevel, entity: PokemonEntity) {
if (!entity.pokemon.isAlpha || !entity.pokemon.isWild() || entity.isBattling) {
lastMatchingPlayerId = null
return
}
val nearestPlayer = entity.brain.getMemorySafely(MemoryModuleType.NEAREST_PLAYERS)
.orElse(emptyList())
.filterIsInstance<ServerPlayer>()
.minByOrNull { it.distanceToSqr(entity) }
if (nearestPlayer == null || nearestPlayer.distanceToSqr(entity) > RADIUS_SQR) {
lastMatchingPlayerId = null
return
}
if (lastMatchingPlayerId != nearestPlayer.uuid) {
lastMatchingPlayerId = nearestPlayer.uuid
entity.cry()
// test particles to make sure the level matching is working
SpawnSnowstormEntityParticlePacket(cobblemonResource("shiny_ring"), entity.id, listOf("shiny_particles", "middle"))
.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, world.dimension())
}
val strongestPartyLevel = nearestPlayer.party().maxOfOrNull { it.level } ?: return
val targetLevel = when {
strongestPartyLevel < 20 -> strongestPartyLevel + 5
strongestPartyLevel in 31..40 -> strongestPartyLevel + 10
strongestPartyLevel in 51..65 -> strongestPartyLevel + 15
strongestPartyLevel >= 66 -> strongestPartyLevel + 20
else -> strongestPartyLevel
}.coerceAtMost(100)
if (entity.pokemon.level != targetLevel) {
entity.pokemon.level = targetLevel
}
}
companion object {
private const val RADIUS_SQR = 32.0 * 32.0
}
}
@@ -39,7 +39,7 @@ class IllusionEffect(
constructor(disguise: Pokemon) : this(
mock = disguise.createPokemonProperties(PokemonPropertyExtractor.ILLUSION),
scale = disguise.form.baseScale * disguise.scaleModifier
scale = disguise.form.baseScale * disguise.effectiveScale
)
override fun apply(entity: PokemonEntity, future: CompletableFuture<PokemonEntity>) {
@@ -36,7 +36,7 @@ class TransformEffect(
constructor(mimic: Pokemon, doCry: Boolean = true) : this(
mock = mimic.createPokemonProperties(PokemonPropertyExtractor.TRANSFORM),
scale = mimic.form.baseScale * mimic.scaleModifier,
scale = mimic.form.baseScale * mimic.effectiveScale,
doCry = doCry
)
@@ -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)
}
}
}
@@ -62,6 +62,7 @@ class SpawnPokemonPacket(
var tickSpawned: Int,
var rideBoosts: Map<RidingStat, Float>,
var rideStamina: Float,
var isAlpha: Boolean,
var silent: Boolean,
vanillaSpawnPacket: ClientboundAddEntityPacket,
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
@@ -95,6 +96,7 @@ class SpawnPokemonPacket(
entity.tickCount,
entity.entityData.get(PokemonEntity.RIDE_BOOSTS),
entity.entityData.get(PokemonEntity.RIDE_STAMINA),
entity.entityData.get(PokemonEntity.IS_ALPHA),
entity.isSilent,
vanillaSpawnPacket
)
@@ -130,6 +132,7 @@ class SpawnPokemonPacket(
{ _, value -> buffer.writeFloat(value) }
)
buffer.writeFloat(rideStamina)
buffer.writeBoolean(isAlpha)
buffer.writeBoolean(silent)
}
@@ -176,6 +179,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.isSilent = silent
entity.tickSpawned = this.tickSpawned
@@ -216,6 +220,7 @@ class SpawnPokemonPacket(
{ buffer.readFloat() }
)
val rideStamina = buffer.readFloat()
val isAlpha = buffer.readBoolean()
val silent = buffer.readBoolean()
val vanillaPacket = decodeVanillaPacket(buffer)
@@ -246,6 +251,7 @@ class SpawnPokemonPacket(
tickSpawned,
rideBoosts,
rideStamina,
isAlpha,
silent,
vanillaPacket
)
@@ -164,7 +164,7 @@ class PokedexUsageContext {
// Randomize info frames for render
if (focusIntervals == 0F) {
availableInfoFrames = mutableListOf(null, null, null, null)
for (i in 0..2) {
for (i in 0..3) {
var randomIndex = Random.nextInt(availableInfoFrames.size)
if (availableInfoFrames[randomIndex] !== null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
availableInfoFrames.set(randomIndex, Random.nextBoolean())
@@ -240,4 +240,4 @@ class PokedexUsageContext {
fun playSound(soundEvent: SoundEvent) {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
}
}
}
@@ -112,6 +112,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
@@ -243,6 +244,21 @@ open class Pokemon : ShowdownIdentifiable {
var characteristic: Characteristic = Characteristic.calculate(ivs, uuid)
private set
var isAlpha: Boolean = false
set(value) {
if (field != value) {
field = value
val alphaMark = Marks.getByIdentifier(cobblemonResource("mark_alpha"))!!
if (value) { activeMark = alphaMark }
else { scaleModifier = 1F }
exchangeMark(alphaMark, value)
updateAspects()
onChange(AlphaUpdatePacket({ this }, value))
}
}
fun setIV(stat : Stat, value : Int) {
val quotient = clamp(currentHealth / maxHealth.toFloat(), 0F, 1F)
ivs[stat] = value
@@ -251,6 +267,31 @@ open class Pokemon : ShowdownIdentifiable {
}
}
fun initializeScale() {
if (isAlpha) {
scaleModifier = 1F
return
}
val config = Cobblemon.config
setIntrinsicScale(Random.nextBetween(config.pokemonIntrinsicSizeMin, config.pokemonIntrinsicSizeMax))
}
fun assignSizeMarks() {
// this is where we should assign a mark based on min or max intrinsic scale
val miniMark = Marks.getByIdentifier(cobblemonResource("mark_mini"))!!
val jumboMark = Marks.getByIdentifier(cobblemonResource("mark_jumbo"))!!
val config = Cobblemon.config
if (scaleModifier == config.pokemonIntrinsicSizeMin) { // minimum size
exchangeMark(miniMark, true)
activeMark = miniMark
}
if (scaleModifier == config.pokemonIntrinsicSizeMax) { // maximum size
exchangeMark(jumboMark, true)
activeMark = jumboMark
}
}
fun hyperTrainIV(stat: Stat, value: Int) {
val quotient = clamp(currentHealth / maxHealth.toFloat(), 0F, 1F)
CobblemonEvents.HYPER_TRAINED_IV_PRE.postThen(
@@ -555,7 +596,46 @@ 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 <= 1) {
Cobblemon.config.babyPokemonSizeMultiplier
} else if (Cobblemon.config.babyPokemonLevelDuration <= 1 || level >= Cobblemon.config.babyPokemonLevelDuration) {
1F
} else {
val minMultiplier = Cobblemon.config.babyPokemonSizeMultiplier
val maxLevel = Cobblemon.config.babyPokemonLevelDuration
val t = (level - 1).toFloat() / (maxLevel - 1).toFloat()
minMultiplier + (1F - minMultiplier) * t
}
return babyPokemonMultiplier * if (this.isAlpha) getAlphaScaleMultiplier() else scaleModifier
}
private fun getAlphaScaleMultiplier(): Float {
val config = Cobblemon.config
val hitbox = form.hitbox
val baseHitboxSize = max(hitbox.width, hitbox.height) * form.baseScale
return when {
baseHitboxSize < 0.5F -> config.alphaPokemonSizeSmallMultiplier
baseHitboxSize < 1.5F -> config.alphaPokemonSizeMediumMultiplier
baseHitboxSize < 10F -> config.alphaPokemonSizeLargeMultiplier
else -> config.alphaPokemonSizeExtraLargeMultiplier
}
}
private fun setIntrinsicScale(value: Float) {
val deltaPercent = (value - 1F) * 100F
val roundedPercent = (deltaPercent * 10F).roundToInt() / 10F
scaleModifier = 1F + (roundedPercent / 100F)
}
var caughtBall: PokeBall = PokeBalls.POKE_BALL
set(value) {
@@ -575,6 +655,7 @@ open class Pokemon : ShowdownIdentifiable {
var activeMark: Mark? = null
set(value) {
field = value
updateAspects()
onChange(ActiveMarkUpdatePacket({ this }, value))
}
@@ -2022,6 +2103,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,36 @@
/*
* 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 the intrinsic min and max size.
fun fromScale(scaleModifier: Float): PokemonSizeCategory {
val config = Cobblemon.config
val minScale = config.pokemonIntrinsicSizeMin
val maxScale = config.pokemonIntrinsicSizeMax
val range = (maxScale - minScale).coerceAtLeast(0.0001F)
val segmentSize = range / PokemonSizeCategory.entries.size
val adjustedScale = (scaleModifier - minScale).coerceIn(0F, range)
val index = (adjustedScale / segmentSize).toInt().coerceIn(0, PokemonSizeCategory.entries.size - 1)
return PokemonSizeCategory.entries[index]
}
fun translationKey(category: PokemonSizeCategory) = "cobblemon.ui.size_category.${category.name.lowercase()}"
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.pokemon.ai
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
class HerdBehaviour {
@@ -39,6 +40,9 @@ class HerdBehaviour {
}
}
}
if (bestLeader != null && possibleLeader.pokemon.isAlpha) {
return ToleratedHerdLeader(PokemonProperties(), 9001, ignoresLevel = true) // WHAT?? OVER 9000???
}
return bestLeader
}
@@ -69,6 +69,7 @@ object PokemonBrain {
SensorType.NEAREST_LIVING_ENTITIES,
SensorType.HURT_BY,
SensorType.NEAREST_PLAYERS,
CobblemonSensors.ALPHA_LEVEL_MATCHING,
CobblemonSensors.POKEMON_DISTURBANCE,
CobblemonSensors.POKEMON_DEFEND_OWNER,
CobblemonSensors.POKEMON_DROWSY,
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.pokemon.aspects
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -53,6 +54,37 @@ val COSMETIC_SLOT_ASPECT = object : AspectProvider {
override fun provide(properties: PokemonProperties) = emptySet<String>()
}
/**
* Provides the aspect(s) of the active mark, if any.
*/
val MARK_ASPECT = object : AspectProvider {
override fun provide(pokemon: Pokemon): Set<String> {
return pokemon.activeMark?.aspects?.toSet() ?: emptySet()
}
override fun provide(properties: PokemonProperties): Set<String> = emptySet()
}
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"
@@ -488,6 +488,6 @@ fun ServerPlayer.updateShoulderNbt(pokemon: Pokemon) {
nbt.putString(DataKeys.SHOULDER_SPECIES, pokemon.species.resourceIdentifier.toString())
nbt.putString(DataKeys.SHOULDER_FORM, pokemon.form.name)
nbt.put(DataKeys.SHOULDER_ASPECTS, pokemon.aspects.map(StringTag::valueOf).toNbtList())
nbt.putFloat(DataKeys.SHOULDER_SCALE_MODIFIER, pokemon.scaleModifier)
nbt.putFloat(DataKeys.SHOULDER_SCALE_MODIFIER, pokemon.effectiveScale)
if ((pokemon.state as ShoulderedState).isLeftShoulder) shoulderEntityLeft = nbt else shoulderEntityRight = nbt
}
@@ -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
)
}
}
@@ -0,0 +1,71 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:alpha_eyes",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/vanilla/generic"
}
},
"curves": {
"variable.size": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [1, 0.5]
}
},
"events": {
"event_outer": {
"particle_effect": {
"effect": "cobblemon:alpha_eyes_outer",
"type": "emitter_bound"
}
}
},
"components": {
"minecraft:emitter_local_space": {
"position": true,
"rotation": false
},
"minecraft:emitter_rate_steady": {
"spawn_rate": 64,
"max_particles": 200
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_lifetime_events": {
"creation_event": "event_outer"
},
"minecraft:emitter_shape_sphere": {
"radius": "0.04 * math.clamp(q.entity_size, 0.5,1.2)",
"direction": "outwards"
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.1
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(1,4) * 90"
},
"minecraft:particle_initial_speed": 0,
"minecraft:particle_motion_dynamic": {
"linear_drag_coefficient": -3.5
},
"minecraft:particle_appearance_billboard": {
"size": [0.1, 0.1],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 64,
"texture_height": 8,
"uv": [40, 0],
"uv_size": [8, 8]
}
},
"minecraft:particle_appearance_tinting": {
"color": [1, 0, 0, 1]
}
}
}
}
@@ -0,0 +1,69 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:alpha_eyes_outer",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/vanilla/generic"
}
},
"curves": {
"variable.size": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [1, 0]
}
},
"components": {
"minecraft:emitter_rate_steady": {
"spawn_rate": 24,
"max_particles": 200
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_sphere": {
"radius": 0.04,
"direction": [0, 1, 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.7
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(1,4) * 90"
},
"minecraft:particle_initial_speed": 0,
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, 4, 0],
"linear_drag_coefficient": 6
},
"minecraft:particle_appearance_billboard": {
"size": [0.06, 0.06],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 64,
"texture_height": 8,
"flipbook": {
"base_UV": [56, 0],
"size_UV": [8, 8],
"step_UV": [-8, 0],
"frames_per_second": 12,
"max_frame": 8
}
}
},
"minecraft:particle_appearance_tinting": {
"color": {
"interpolant": "v.particle_age / v.particle_lifetime",
"gradient": {
"0.0": "#FFFF0000",
"0.22": "#FFFF0000",
"1.0": "#00FF0000"
}
}
}
}
}
}
@@ -295,7 +295,7 @@
"rotation": [-7.5, 0, 0],
"cubes": [
{"origin": [-2, 48.49, -4], "size": [4, 4, 2], "uv": [0, 51]},
{"origin": [-1.5, 48.5, -1.995], "size": [3, 4, 0], "uv": [90, 0]},
{"origin": [-1.5, 48.5, -2], "size": [3, 4, 0], "uv": [90, 0]},
{"origin": [1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [1.5, 52.52782, -2], "rotation": [45, 0, -7.5], "uv": [37, 39]},
{"origin": [-1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [-1.5, 52.52782, -2], "rotation": [45, 0, 7.5], "uv": [37, 38]}
]
@@ -29,8 +29,7 @@
"poseTypes": ["PORTRAIT", "PROFILE"],
"isBattle": false,
"isTouchingWater": false,
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -47,8 +46,7 @@
"poseTypes": ["STAND"],
"isBattle": true,
"isTouchingWater": false,
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -70,8 +68,7 @@
"battle-flying": {
"poseTypes": ["HOVER"],
"isBattle": true,
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -96,8 +93,7 @@
"jump": {
"poseTypes": ["STAND", "WALK"],
"condition": "q.in_air",
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -119,8 +115,7 @@
"isBattle": false,
"isTouchingWater": false,
"condition": "!q.in_air && q.is_sprinting",
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -144,8 +139,7 @@
"isBattle": false,
"isTouchingWater": false,
"condition": "!q.in_air",
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -167,8 +161,7 @@
"isBattle": false,
"isTouchingWater": false,
"condition": "!q.in_air && !q.is_sprinting",
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -190,8 +183,7 @@
"hover": {
"poseTypes": ["HOVER"],
"isBattle": false,
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -210,8 +202,7 @@
"poseTypes": ["FLY"],
"isBattle": false,
"condition": "!q.is_gliding",
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -235,8 +226,7 @@
"poseTypes": ["FLY"],
"isBattle": false,
"condition": "q.is_gliding",
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -260,8 +250,7 @@
"poseTypes": ["STAND", "FLOAT"],
"isTouchingWater": true,
"isUnderWater": false,
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -286,8 +275,7 @@
"poseTypes": ["WALK", "SWIM"],
"isTouchingWater": true,
"isUnderWater": false,
"animations": [
"q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"animations": ["q.look('neck', 0.5, 0.5, 15, -15, 30, -30)",
"q.look('neck2', 0.5, 0.5, 10, -10, 25, -25)",
"q.look('neck3', 0.45, 0.45, 10, -10, 20, -20)",
"q.look('neck4', 0.35, 0.35, 5, -5, 15, -15)",
@@ -313,8 +301,7 @@
"namedAnimations": {
"cry": "q.bedrock_stateful('dummy', 'cry')"
},
"animations": [
"q.bedrock('charizard', 'sleep')",
"animations": ["q.bedrock('charizard', 'sleep')",
{
"condition": "q.is_holding_item",
"animation": "q.bedrock('charizard', 'hold_item')"
@@ -326,3 +313,6 @@
}
}
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0001_bulbasaur/bulbasaur_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0001_bulbasaur/bulbasaur_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0002_ivysaur/ivysaur_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0002_ivysaur/ivysaur_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0003_venusaur/venusaur_male_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0003_venusaur/venusaur_alpha.png",
"emissive": true
}
]
}
]
}
@@ -26,7 +26,9 @@
]
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0004_charmander/charmander_shiny.png",
"layers": [
{
@@ -45,6 +47,18 @@
"translucent": true
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0004_charmander/charmander_alpha.png",
"emissive": true
}
]
}
]
}
@@ -26,7 +26,9 @@
]
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0005_charmeleon/charmeleon_shiny.png",
"layers": [
{
@@ -45,6 +47,18 @@
"translucent": true
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0005_charmeleon/charmeleon_alpha.png",
"emissive": true
}
]
}
]
}
@@ -45,6 +45,16 @@
"translucent": true
}
]
},
{
"aspects": ["alpha_eyes"],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0006_charizard/charizard_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,11 +10,15 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0007_squirtle/squirtle_shiny.png"
},
{
"aspects": ["cosmetic_item-black_glasses"],
"aspects": [
"cosmetic_item-black_glasses"
],
"layers": [
{
"texture": "cobblemon:textures/pokemon/0007_squirtle/squirtle_triangleglasses.png"
@@ -22,12 +26,26 @@
]
},
{
"aspects": ["cosmetic_item-wise_glasses"],
"aspects": [
"cosmetic_item-wise_glasses"
],
"layers": [
{
"texture": "cobblemon:textures/pokemon/0007_squirtle/squirtle_circleglasses.png"
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0007_squirtle/squirtle_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,11 +10,15 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0008_wartortle/wartortle_shiny.png"
},
{
"aspects": ["cosmetic_item-black_glasses"],
"aspects": [
"cosmetic_item-black_glasses"
],
"layers": [
{
"texture": "cobblemon:textures/pokemon/0008_wartortle/wartortle_triangleglasses.png"
@@ -22,12 +26,26 @@
]
},
{
"aspects": ["cosmetic_item-wise_glasses"],
"aspects": [
"cosmetic_item-wise_glasses"
],
"layers": [
{
"texture": "cobblemon:textures/pokemon/0008_wartortle/wartortle_circleglasses.png"
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0008_wartortle/wartortle_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,11 +10,15 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0009_blastoise/blastoise_shiny.png"
},
{
"aspects": ["cosmetic_item-black_glasses"],
"aspects": [
"cosmetic_item-black_glasses"
],
"layers": [
{
"texture": "cobblemon:textures/pokemon/0009_blastoise/blastoise_triangleglasses.png"
@@ -22,12 +26,26 @@
]
},
{
"aspects": ["cosmetic_item-wise_glasses"],
"aspects": [
"cosmetic_item-wise_glasses"
],
"layers": [
{
"texture": "cobblemon:textures/pokemon/0009_blastoise/blastoise_circleglasses.png"
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0009_blastoise/blastoise_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0010_caterpie/caterpie_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0010_caterpie/caterpie_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0011_metapod/metapod_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0011_metapod/metapod_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0012_butterfree/butterfree_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0012_butterfree/butterfree_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0013_weedle/weedle_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0013_weedle/weedle_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0014_kakuna/kakuna_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0014_kakuna/kakuna_alpha.png",
"emissive": true
}
]
}
]
}
@@ -9,8 +9,22 @@
"texture": "cobblemon:textures/pokemon/0015_beedrill/beedrill.png"
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0015_beedrill/beedrill_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0015_beedrill/beedrill_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0016_pidgey/pidgey_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0016_pidgey/pidgey_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0017_pidgeotto/pidgeotto_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0017_pidgeotto/pidgeotto_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0018_pidgeot/pidgeot_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0018_pidgeot/pidgeot_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0019_rattata/rattata_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0019_rattata/rattata_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0020_raticate/raticate_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0020_raticate/raticate_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0021_spearow/spearow_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0021_spearow/spearow_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0022_fearow/fearow_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0022_fearow/fearow_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0023_ekans/ekans_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0023_ekans/ekans_alpha.png",
"emissive": true
}
]
}
]
}
@@ -16,7 +16,9 @@
]
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0024_arbok/arbok_shiny.png",
"layers": [
{
@@ -27,7 +29,9 @@
]
},
{
"aspects": ["snake-pattern-attack"],
"aspects": [
"snake-pattern-attack"
],
"layers": [
{
"name": "pattern",
@@ -37,7 +41,9 @@
]
},
{
"aspects": ["snake-pattern-dark"],
"aspects": [
"snake-pattern-dark"
],
"layers": [
{
"name": "pattern",
@@ -47,7 +53,9 @@
]
},
{
"aspects": ["snake-pattern-elusive"],
"aspects": [
"snake-pattern-elusive"
],
"layers": [
{
"name": "pattern",
@@ -57,7 +65,9 @@
]
},
{
"aspects": ["snake-pattern-heart"],
"aspects": [
"snake-pattern-heart"
],
"layers": [
{
"name": "pattern",
@@ -67,7 +77,9 @@
]
},
{
"aspects": ["snake-pattern-legacy"],
"aspects": [
"snake-pattern-legacy"
],
"layers": [
{
"name": "pattern",
@@ -77,7 +89,9 @@
]
},
{
"aspects": ["snake-pattern-sound"],
"aspects": [
"snake-pattern-sound"
],
"layers": [
{
"name": "pattern",
@@ -87,7 +101,9 @@
]
},
{
"aspects": ["snake-pattern-speed"],
"aspects": [
"snake-pattern-speed"
],
"layers": [
{
"name": "pattern",
@@ -97,7 +113,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-attack"],
"aspects": [
"shiny",
"snake-pattern-attack"
],
"layers": [
{
"name": "pattern",
@@ -107,7 +126,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-dark"],
"aspects": [
"shiny",
"snake-pattern-dark"
],
"layers": [
{
"name": "pattern",
@@ -117,7 +139,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-elusive"],
"aspects": [
"shiny",
"snake-pattern-elusive"
],
"layers": [
{
"name": "pattern",
@@ -127,7 +152,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-heart"],
"aspects": [
"shiny",
"snake-pattern-heart"
],
"layers": [
{
"name": "pattern",
@@ -137,7 +165,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-legacy"],
"aspects": [
"shiny",
"snake-pattern-legacy"
],
"layers": [
{
"name": "pattern",
@@ -147,7 +178,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-sound"],
"aspects": [
"shiny",
"snake-pattern-sound"
],
"layers": [
{
"name": "pattern",
@@ -157,7 +191,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-speed"],
"aspects": [
"shiny",
"snake-pattern-speed"
],
"layers": [
{
"name": "pattern",
@@ -167,7 +204,10 @@
]
},
{
"aspects": ["shiny", "snake-pattern-classic"],
"aspects": [
"shiny",
"snake-pattern-classic"
],
"layers": [
{
"name": "pattern",
@@ -175,6 +215,18 @@
"emissive": false
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0024_arbok/arbok_alpha.png",
"emissive": true
}
]
}
]
}
@@ -17,7 +17,9 @@
]
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu_shiny.png",
"layers": [
{
@@ -29,7 +31,9 @@
]
},
{
"aspects": ["female"],
"aspects": [
"female"
],
"poser": "cobblemon:pikachu",
"model": "cobblemon:pikachu_female.geo",
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu.png",
@@ -43,7 +47,10 @@
]
},
{
"aspects": ["shiny", "female"],
"aspects": [
"shiny",
"female"
],
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu_shiny.png",
"layers": [
{
@@ -55,7 +62,9 @@
]
},
{
"aspects": ["cosmetic_item-rage_candy_bar"],
"aspects": [
"cosmetic_item-rage_candy_bar"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -65,7 +74,9 @@
]
},
{
"aspects": ["cosmetic_item-pewter_crunchies"],
"aspects": [
"cosmetic_item-pewter_crunchies"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -75,7 +86,9 @@
]
},
{
"aspects": ["cosmetic_item-lava_cookie"],
"aspects": [
"cosmetic_item-lava_cookie"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -85,7 +98,9 @@
]
},
{
"aspects": ["cosmetic_item-old_gateau"],
"aspects": [
"cosmetic_item-old_gateau"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -95,7 +110,9 @@
]
},
{
"aspects": ["cosmetic_item-casteliacone"],
"aspects": [
"cosmetic_item-casteliacone"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -105,7 +122,9 @@
]
},
{
"aspects": ["cosmetic_item-lumiose_galette"],
"aspects": [
"cosmetic_item-lumiose_galette"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -115,7 +134,9 @@
]
},
{
"aspects": ["cosmetic_item-big_malasada"],
"aspects": [
"cosmetic_item-big_malasada"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -125,7 +146,9 @@
]
},
{
"aspects": ["cosmetic_item-smoked_tail_curry"],
"aspects": [
"cosmetic_item-smoked_tail_curry"
],
"model": "cobblemon:pikachu_cap_male.geo",
"layers": [
{
@@ -135,7 +158,9 @@
]
},
{
"aspects": ["cosmetic_item-compass"],
"aspects": [
"cosmetic_item-compass"
],
"model": "cobblemon:pikachu_captain_male.geo",
"layers": [
{
@@ -145,11 +170,15 @@
]
},
{
"aspects": ["cosmetic_item-shears"],
"aspects": [
"cosmetic_item-shears"
],
"model": "cobblemon:pikachu_fluff_male.geo"
},
{
"aspects": ["cosmetic_item-leather_helmet"],
"aspects": [
"cosmetic_item-leather_helmet"
],
"model": "cobblemon:pikachu_detective_male.geo",
"layers": [
{
@@ -159,7 +188,9 @@
]
},
{
"aspects": ["cosmetic_item-sinister_tea"],
"aspects": [
"cosmetic_item-sinister_tea"
],
"model": "cobblemon:pikachu_detective_male.geo",
"layers": [
{
@@ -169,7 +200,9 @@
]
},
{
"aspects": ["cosmetic_item-cocoa_beans"],
"aspects": [
"cosmetic_item-cocoa_beans"
],
"model": "cobblemon:pikachu_detective_male.geo",
"layers": [
{
@@ -179,7 +212,10 @@
]
},
{
"aspects": ["cosmetic_item-rage_candy_bar", "female"],
"aspects": [
"cosmetic_item-rage_candy_bar",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -189,7 +225,10 @@
]
},
{
"aspects": ["cosmetic_item-pewter_crunchies", "female"],
"aspects": [
"cosmetic_item-pewter_crunchies",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -199,7 +238,10 @@
]
},
{
"aspects": ["cosmetic_item-lava_cookie", "female"],
"aspects": [
"cosmetic_item-lava_cookie",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -209,7 +251,10 @@
]
},
{
"aspects": ["cosmetic_item-old_gateau", "female"],
"aspects": [
"cosmetic_item-old_gateau",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -219,7 +264,10 @@
]
},
{
"aspects": ["cosmetic_item-casteliacone", "female"],
"aspects": [
"cosmetic_item-casteliacone",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -229,7 +277,10 @@
]
},
{
"aspects": ["cosmetic_item-lumiose_galette", "female"],
"aspects": [
"cosmetic_item-lumiose_galette",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -239,7 +290,10 @@
]
},
{
"aspects": ["cosmetic_item-big_malasada", "female"],
"aspects": [
"cosmetic_item-big_malasada",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -249,7 +303,10 @@
]
},
{
"aspects": ["cosmetic_item-smoked_tail_curry", "female"],
"aspects": [
"cosmetic_item-smoked_tail_curry",
"female"
],
"model": "cobblemon:pikachu_cap_female.geo",
"layers": [
{
@@ -259,7 +316,10 @@
]
},
{
"aspects": ["cosmetic_item-compass", "female"],
"aspects": [
"cosmetic_item-compass",
"female"
],
"model": "cobblemon:pikachu_captain_female.geo",
"layers": [
{
@@ -269,11 +329,17 @@
]
},
{
"aspects": ["cosmetic_item-shears", "female"],
"aspects": [
"cosmetic_item-shears",
"female"
],
"model": "cobblemon:pikachu_fluff_female.geo"
},
{
"aspects": ["cosmetic_item-leather_helmet", "female"],
"aspects": [
"cosmetic_item-leather_helmet",
"female"
],
"model": "cobblemon:pikachu_detective_female.geo",
"layers": [
{
@@ -283,7 +349,10 @@
]
},
{
"aspects": ["cosmetic_item-sinister_tea", "female"],
"aspects": [
"cosmetic_item-sinister_tea",
"female"
],
"model": "cobblemon:pikachu_detective_female.geo",
"layers": [
{
@@ -293,7 +362,10 @@
]
},
{
"aspects": ["cosmetic_item-cocoa_beans", "female"],
"aspects": [
"cosmetic_item-cocoa_beans",
"female"
],
"model": "cobblemon:pikachu_detective_female.geo",
"layers": [
{
@@ -301,6 +373,18 @@
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu_detective.png"
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu_alpha.png",
"emissive": true
}
]
}
]
}
@@ -17,7 +17,9 @@
]
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu_shiny.png",
"layers": [
{
@@ -29,7 +31,9 @@
]
},
{
"aspects": ["female"],
"aspects": [
"female"
],
"poser": "cobblemon:raichu",
"model": "cobblemon:raichu_female.geo",
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu.png",
@@ -43,7 +47,10 @@
]
},
{
"aspects": ["female","shiny"],
"aspects": [
"female",
"shiny"
],
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu_shiny.png",
"layers": [
{
@@ -55,7 +62,9 @@
]
},
{
"aspects": ["cosmetic_item-rage_candy_bar"],
"aspects": [
"cosmetic_item-rage_candy_bar"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -65,7 +74,9 @@
]
},
{
"aspects": ["cosmetic_item-pewter_crunchies"],
"aspects": [
"cosmetic_item-pewter_crunchies"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -75,7 +86,9 @@
]
},
{
"aspects": ["cosmetic_item-lava_cookie"],
"aspects": [
"cosmetic_item-lava_cookie"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -85,7 +98,9 @@
]
},
{
"aspects": ["cosmetic_item-old_gateau"],
"aspects": [
"cosmetic_item-old_gateau"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -95,7 +110,9 @@
]
},
{
"aspects": ["cosmetic_item-casteliacone"],
"aspects": [
"cosmetic_item-casteliacone"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -105,7 +122,9 @@
]
},
{
"aspects": ["cosmetic_item-lumiose_galette"],
"aspects": [
"cosmetic_item-lumiose_galette"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -115,7 +134,9 @@
]
},
{
"aspects": ["cosmetic_item-big_malasada"],
"aspects": [
"cosmetic_item-big_malasada"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -125,7 +146,9 @@
]
},
{
"aspects": ["cosmetic_item-smoked_tail_curry"],
"aspects": [
"cosmetic_item-smoked_tail_curry"
],
"model": "cobblemon:raichu_cap_male.geo",
"layers": [
{
@@ -135,7 +158,9 @@
]
},
{
"aspects": ["cosmetic_item-compass"],
"aspects": [
"cosmetic_item-compass"
],
"model": "cobblemon:raichu_captain_male.geo",
"layers": [
{
@@ -145,11 +170,15 @@
]
},
{
"aspects": ["cosmetic_item-shears"],
"aspects": [
"cosmetic_item-shears"
],
"model": "cobblemon:raichu_fluff_male.geo"
},
{
"aspects": ["cosmetic_item-leather_helmet"],
"aspects": [
"cosmetic_item-leather_helmet"
],
"model": "cobblemon:raichu_detective_male.geo",
"layers": [
{
@@ -159,7 +188,9 @@
]
},
{
"aspects": ["cosmetic_item-sinister_tea"],
"aspects": [
"cosmetic_item-sinister_tea"
],
"model": "cobblemon:raichu_detective_male.geo",
"layers": [
{
@@ -169,7 +200,9 @@
]
},
{
"aspects": ["cosmetic_item-cocoa_beans"],
"aspects": [
"cosmetic_item-cocoa_beans"
],
"model": "cobblemon:raichu_detective_male.geo",
"layers": [
{
@@ -179,7 +212,10 @@
]
},
{
"aspects": ["cosmetic_item-rage_candy_bar", "female"],
"aspects": [
"cosmetic_item-rage_candy_bar",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -189,7 +225,10 @@
]
},
{
"aspects": ["cosmetic_item-pewter_crunchies", "female"],
"aspects": [
"cosmetic_item-pewter_crunchies",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -199,7 +238,10 @@
]
},
{
"aspects": ["cosmetic_item-lava_cookie", "female"],
"aspects": [
"cosmetic_item-lava_cookie",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -209,7 +251,10 @@
]
},
{
"aspects": ["cosmetic_item-old_gateau", "female"],
"aspects": [
"cosmetic_item-old_gateau",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -219,7 +264,10 @@
]
},
{
"aspects": ["cosmetic_item-casteliacone", "female"],
"aspects": [
"cosmetic_item-casteliacone",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -229,7 +277,10 @@
]
},
{
"aspects": ["cosmetic_item-lumiose_galette", "female"],
"aspects": [
"cosmetic_item-lumiose_galette",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -239,7 +290,10 @@
]
},
{
"aspects": ["cosmetic_item-big_malasada", "female"],
"aspects": [
"cosmetic_item-big_malasada",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -249,7 +303,10 @@
]
},
{
"aspects": ["cosmetic_item-smoked_tail_curry", "female"],
"aspects": [
"cosmetic_item-smoked_tail_curry",
"female"
],
"model": "cobblemon:raichu_cap_female.geo",
"layers": [
{
@@ -259,7 +316,10 @@
]
},
{
"aspects": ["cosmetic_item-compass", "female"],
"aspects": [
"cosmetic_item-compass",
"female"
],
"model": "cobblemon:raichu_captain_female.geo",
"layers": [
{
@@ -269,11 +329,17 @@
]
},
{
"aspects": ["cosmetic_item-shears", "female"],
"aspects": [
"cosmetic_item-shears",
"female"
],
"model": "cobblemon:raichu_fluff_female.geo"
},
{
"aspects": ["cosmetic_item-leather_helmet", "female"],
"aspects": [
"cosmetic_item-leather_helmet",
"female"
],
"model": "cobblemon:raichu_detective_female.geo",
"layers": [
{
@@ -283,7 +349,10 @@
]
},
{
"aspects": ["cosmetic_item-sinister_tea", "female"],
"aspects": [
"cosmetic_item-sinister_tea",
"female"
],
"model": "cobblemon:raichu_detective_female.geo",
"layers": [
{
@@ -293,7 +362,10 @@
]
},
{
"aspects": ["cosmetic_item-cocoa_beans", "female"],
"aspects": [
"cosmetic_item-cocoa_beans",
"female"
],
"model": "cobblemon:raichu_detective_female.geo",
"layers": [
{
@@ -301,6 +373,18 @@
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu_detective.png"
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0027_sandshrew/sandshrew_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0027_sandshrew/sandshrew_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0028_sandslash/sandslash_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0028_sandslash/sandslash_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0029_nidoranf/nidoranf_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0029_nidoranf/nidoranf_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0030_nidorina/nidorina_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0030_nidorina/nidorina_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0031_nidoqueen/nidoqueen_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0031_nidoqueen/nidoqueen_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0032_nidoranm/nidoranm_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0032_nidoranm/nidoranm_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0033_nidorino/nidorino_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0033_nidorino/nidorino_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0034_nidoking/nidoking_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0034_nidoking/nidoking_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0035_clefairy/clefairy_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0035_clefairy/clefairy_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0036_clefable/clefable_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0036_clefable/clefable_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0037_vulpix/vulpix_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0037_vulpix/vulpix_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0038_ninetales/ninetales_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0038_ninetales/ninetales_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,17 +10,33 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0039_jigglypuff/jigglypuff_shiny.png"
},
{
"aspects": ["color-black"],
"aspects": [
"color-black"
],
"layers": [
{
"name": "pokemopolis",
"texture": "cobblemon:textures/pokemon/0039_jigglypuff/jigglypuff_pokemopolis.png"
}
]
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0039_jigglypuff/jigglypuff_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0040_wigglytuff/wigglytuff_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0040_wigglytuff/wigglytuff_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0041_zubat/zubat_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0041_zubat/zubat_alpha.png",
"emissive": true
}
]
}
]
}
@@ -10,8 +10,22 @@
"layers": []
},
{
"aspects": ["shiny"],
"aspects": [
"shiny"
],
"texture": "cobblemon:textures/pokemon/0042_golbat/golbat_shiny.png"
},
{
"aspects": [
"alpha_eyes"
],
"layers": [
{
"name": "alpha_eyes",
"texture": "cobblemon:textures/pokemon/0042_golbat/golbat_alpha.png",
"emissive": true
}
]
}
]
}

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