Compare commits

..
46 changed files with 298 additions and 815 deletions
+1 -267
View File
@@ -2,273 +2,7 @@
## [1.8.x (MONTH xth, 2026)](#1-8-x)
### Additions
- Added riding statistics: `Distance by Pokémon on Land`, `Distance by Pokémon in Air` and `Distance by Pokémon in Liquid`.
- Added the Partner Mark, which party Pokémon have a very small chance to earn as the player walks, provided they have enough friendship.
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
- Added brewing recipe for Throat Spray.
- Added mood particles! Pokémon now visually react to certain actions:
- Angry particles when attacking a mob.
- Annoyed particles when a player tries to feed them while they’re already full.
- Sweat droplets when they run out of stamina while being ridden.
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
- Added missing crops to Botany Pots integration
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
- Added Configs for Infinite Use TMs and Unlocking all TMs
- Added Type Gem blocks
- Added Type Gem Clusters
- Added TMs
- Added TM Machine
- Added Deepslate Core Block
- Added /givetm Command
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
- Added Move Dex to Pokédex
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
- Added intrinsic scaling to all Pokémon (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
- Added Alpha Pokémon
- Added alpha scaling (alphas are larger than their normal counterparts)
- Alphas will always lead herds and will spawn in herds
- Alphas spawn with two random TM moves from their learnset
- Alpha Pokémon will always defend themselves from players that attack them
- Added alpha level matching and stat boost (based on level)
- Added pitch-down and reverb to alpha cries
- Added alpha Pokémon bait effect
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
- Added cobweb block slowness immunity for spider Pokémon
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
- Added 10 new ruin structures.
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
- A new shipwreck cove structure called the "magma_shipwreck_cove" has been added.
- Shipwreck coves now generate slightly more frequently.
- Added equipment loot tables for shipwreck cove enemies.
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
- Added 7 new advancements
- Pressing the inventory key now closes the Pokémon interact wheel GUI
- Pokémon cries now play when clicking Pokémon in the summary screen.
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens
- Ice type Pokémon are now immune to Packed Snow
- Held item visibility can now be controlled by the held_item_visible command argument.
### New rideable Pokémon
- Crobat
- Skarmory
- Milotic
- Archeops
- Goodra
- Goodra (Hisuian Form)
- Drampa
- Duraludon
- Archaludon
### Pokémon Added
#### Generation 3
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Skitty
- Delcatty
- Seviper
- Zangoose
#### Generation 4
- Cherubi
- Cherrim
- Stunky
- Skuntank
#### Generation 5
- Audino
- Pawniard
- Bisharp
#### Generation 6
- Pancham (+ Cosmetic with Black Glasses)
- Pangoro (+ Cosmetic with Black Glasses)
#### Generation 7
- Rockruff
- Lycanroc
#### Generation 8
- Rolycoly
- Carkol
- Coalossal
- Runerigus
- Snom
- Frosmoth
- Indeedee
- Duraludon
- Applin
- Flapple
- Appletun
#### Generation 9
- Nymble
- Lokix
- Kingambit
- Dipplin
- Archaludon
- Hydrapple
#### Regional Forms
- Sandshrew (Alolan Form)
- Sandslash (Alolan Form)
- Yamask (Galarian Form)
- Avalugg (Hisuian Form)
### Model updates for the following Pokémon
- Sandshrew
- Sandslash
- Nidoking
- Zubat
- Golbat
- Crobat
- Ditto
- Porygon
- Porygon2
- Porygon-Z
- Skarmory
- Smoochum
- Jynx
- Popplio
- Brionne
- Primarina
- Fletchling
- Fletchinder
- Talonflame
- Sableye
- Mawile
- Goomy
- Sliggoo
- Goodra
- Nacli
- Naclstack
- Garganacl
- Bidoof
- Cranidos
- Chatot
- Yamask
- Cofagrigus
- Tyranitar
- Turtonator
- Golett
- Corsola
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
- ... and Substitute
#### Model changes to the regional bias resource pack:
- Added Mime Jr. (Galar Bias)
- Added Bergmite (Hisui Bias)
- Changed models for every existing regional bias
### Animation updates for the following Pokémon
- Kakuna
- Grimer
- Nidoran♀ (+ New shoulder mount)
- Nidorina
- Nidoran♂ (+ New shoulder mount)
- Sandshrew
- Diglett
- Dugtrio
- Graveler
- Happiny
- Chansey
- Blissey
- Poliwhirl
- Poliwrath
- Zubat
- Vulpix
- Drowzee
- Hypno
- Noctowl
- Skarmory
- Skitty
- Flygon
- Shellos
- Riolu
- Lucario
- Purrloin
- Liepard
- Joltik
- Galvantula
- Nidoking
- Fletchling
- Fletchinder
- Talonflame
- Archeops
- Drampa
- Sewaddle
- Swadloon
- Leavanny
- Goomy
- Sliggoo
- Goodra
- Shedinja
- Skwovet
- Greedent
- Bidoof
- Cranidos
- Chatot
- Yamask
- Cofagrigus
- Luvdisc
- Staryu
- Starmie
- Togetic
- Rellor
- Rabsca
- Yungoos
- Gumshoos
- Cottonee
- Whimsicott
- Corsola
#### Added sounds to the following moves
- Absorb
- Aurora Beam
- Double Team
- Draco Meteor
- Fire Punch
- Fire Spin
- Flame Charge
- Flame Wheel
- Giga Drain
- Ice Punch
- Leech Life
- Magical Leaf
- Mega Drain
- Minimize
- Poison Gas
- Rock Throw
- Scary Face
- Seed bomb
- Smokescreen
- String Shot
- Tail Whip
- You can now view the biome, level, and date you caught a Pokémon.
### Changes
@@ -344,6 +344,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(AlphaUpdatePacket.ID, AlphaUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(ScaleModifierUpdatePacket.ID, ScaleModifierUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(MetDataUpdatePacket.ID, MetDataUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -92,17 +92,13 @@ interface PokemonAndMoveSelectingItem {
}
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
if (pokemon.entity == null && stack.`is`(CobblemonItemTags.POKE_FOOD) && pokemon.isFull()) {
return false
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
return !pokemon.isFull()
}
return true
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean {
return bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
fun canUseOnMove(stack: ItemStack, pokemon: Pokemon, move: Move): Boolean = canUseOnMove(stack, move)
fun canUseOnMove(stack: ItemStack, move: Move): Boolean
@@ -107,16 +107,13 @@ interface PokemonSelectingItem {
}
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
if (pokemon.entity == null && stack.`is`(CobblemonItemTags.POKE_FOOD) && pokemon.isFull()) {
return false
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
return !pokemon.isFull()
}
return true
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean {
return bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
fun interactWithSpecificBattle(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon): InteractionResultHolder<ItemStack> {
return if (canUseOnBattlePokemon(stack, battlePokemon)) {
@@ -55,9 +55,8 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
}
functions["feed_pokemon"] = { params ->
val amount = params.getDouble(0)
val playSound = params.getBooleanOrNull(1) != false
val playMoodParticles = params.getBooleanOrNull(2) != false
DoubleValue(pokemon.feedPokemon(amount.toInt(), playSound, playMoodParticles))
val playSound = params.getBooleanOrNull(1) ?: true
pokemon.feedPokemon(amount.toInt(), playSound)
}
functions["behaviour"] = { pokemon.form.behaviour.struct }
functions["behavior"] = { pokemon.form.behaviour.struct } // Inferior
@@ -44,7 +44,6 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
ActionEffectKeyframe.register<EntityMoLangActionEffectKeyframe>("entity_molang")
ActionEffectKeyframe.register<MoLangActionEffectKeyframe>("molang")
ActionEffectKeyframe.register<ParallelActionEffectKeyframe>("parallel")
ActionEffectKeyframe.register<LoopActionEffectKeyframe>("loop")
ActionEffectKeyframe.register<CanInterruptActionEffectKeyframe>("can_interrupt")
ActionEffectKeyframe.register<CannotInterruptActionEffectKeyframe>("cannot_interrupt")
ActionEffectKeyframe.register<RemoveHoldsActionEffectKeyframe>("remove_holds")
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
class LoopActionEffectKeyframe(
val keyframe: ActionEffectKeyframe = PauseActionEffectKeyframe()
) : ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
runIteration(context, future)
return future
}
private fun runIteration(context: ActionEffectContext, future: CompletableFuture<Unit>) {
context.currentKeyframes.add(keyframe)
keyframe.play(context)
.thenRun { context.currentKeyframes.remove(keyframe) }
.thenAccept {
if (condition.resolveBoolean(context.runtime)) {
runIteration(context, future)
} else {
future.complete(Unit)
}
}
.exceptionally {
future.completeExceptionally(it)
null
}
}
}
@@ -6,23 +6,59 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
package com.cobblemon.mod.common.client.gui.summary
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.drawScaledText
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.ObjectSelectionList
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.util.Mth
class MoveDescriptionScrollList(
class ScrollableTextBoxEntry(
var line : MutableComponent
) :
ObjectSelectionList.Entry<ScrollableTextBoxEntry>() {
override fun render(
context: GuiGraphics,
index: Int,
rowTop: Int,
rowLeft: Int,
rowWidth: Int,
rowHeight: Int,
mouseX: Int,
mouseY: Int,
isHovered: Boolean,
partialTicks: Float
) {
drawScaledText(
context = context,
text = line,
x = rowLeft + 82,
y = rowTop,
scale = 0.5F,
shadow = true
)
}
override fun getNarration(): Component {
return line
}
}
class ScrollableTextBox(
private val listX: Int,
private val listY: Int,
height: Int,
width: Int,
slotHeight: Int
) : ObjectSelectionList<MoveDescriptionEntry>(
) : ObjectSelectionList<ScrollableTextBoxEntry>(
Minecraft.getInstance(),
60, // width
30, // height
width, // width
height, // height
0, // top
slotHeight
), CobblemonRenderable {
@@ -38,8 +74,31 @@ class MoveDescriptionScrollList(
return x + width - 3
}
override fun getMaxPosition(): Int {
return super.getMaxPosition() + 3 // add bottom padding
}
// we do our own scrollbar rendering, and this being true renders the vanilla one
override fun scrollbarVisible() = false
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, f: Float) {
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
if (maxScroll > 0) {
val right = x + width
val bottom = y + height
val darkGray = 0xFF5B595B.toInt()
val lightGray = 0xFF8C8C8C.toInt()
context.fill(right - 2, y + 2, right - 4, bottom - 2, darkGray)
// vanilla scrollbar mumbo jumbo
var scrollHeight = ((height * height).toFloat() / maxPosition.toFloat()).toInt()
scrollHeight = Mth.clamp(scrollHeight, 32, height - 8)
var scrollY = scrollAmount.toInt() * (height - scrollHeight + 2) / maxScroll + y - 1
if (scrollY < y) { scrollY = y }
context.fill(right - 2, scrollY + 2, right - 4, scrollY + scrollHeight - 3, lightGray)
}
context.enableScissor(
x,
y,
@@ -79,7 +138,7 @@ class MoveDescriptionScrollList(
&& mouseY < bottom
}
fun setMoveDescription(moveDescription: MutableComponent) {
fun setText(moveDescription: MutableComponent) {
clearEntries()
val splitWidth = 100
val text = moveDescription.string
@@ -103,7 +162,8 @@ class MoveDescriptionScrollList(
}
for (part in splitText) {
addEntry(MoveDescriptionEntry(Component.literal(part)))
addEntry(ScrollableTextBoxEntry(Component.literal(part)))
}
scrollAmount = 0.0
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.gui.MultiLineLabelK
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.ScrollableTextBox
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNatureTextIfMinted
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget.Companion.SCALE
@@ -27,6 +28,7 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
class InfoWidget(
pX: Int,
@@ -45,6 +47,25 @@ class InfoWidget(
private val infoBaseResource = cobblemonResource("textures/gui/summary/summary_info_base.png")
}
private var metDataWidget: ScrollableTextBox = ScrollableTextBox(x + 5, y + 123, 20, 61, 5)
init {
pokemon.metData?.let { metData ->
val biome = ResourceLocation.parse(metData.biome.registeredName)
val biomeName = Component.translatable("biome.${biome.namespace}.${biome.path}")
metDataWidget.setText(lang("ui.info.met_data",
biomeName,
metData.date.monthValue,
metData.date.dayOfMonth,
metData.date.year,
metData.level
))
} ?: run {
metDataWidget.setText(lang("ui.info.met_data_unavailable"))
}
addWidget(metDataWidget)
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val matrices = context.pose()
// Base texture
@@ -163,6 +184,17 @@ class InfoWidget(
)
matrices.popPose()
metDataWidget.render(context, pMouseX, pMouseY, pPartialTicks)
// val textLines = mutableListOf<MutableComponent>()
// pokemon.metData?.let { metData ->
// }
//
// metDataWidget.textLines = textLines
// metDataWidget.render(context, pMouseX, pMouseY, pPartialTicks)
drawScaledText(
context = context,
text = lang("ui.info.experience_points"),
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
import com.cobblemon.mod.common.client.render.drawScaledText
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.ObjectSelectionList
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
class MoveDescriptionEntry(
var line : MutableComponent
) :
ObjectSelectionList.Entry<MoveDescriptionEntry>() {
override fun render(
context: GuiGraphics,
index: Int,
rowTop: Int,
rowLeft: Int,
rowWidth: Int,
rowHeight: Int,
mouseX: Int,
mouseY: Int,
isHovered: Boolean,
partialTicks: Float
) {
drawScaledText(
context = context,
text = line,
x = rowLeft + 82,
y = rowTop,
scale = 0.5F,
shadow = true
)
}
override fun getNarration(): Component {
return line
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.summary.ScrollableTextBox
import com.cobblemon.mod.common.client.gui.summary.Summary
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
@@ -70,9 +71,11 @@ class MovesWidget(
addWidget(it)
}
private var descriptionScrollList = MoveDescriptionScrollList(
x + 69,
private var descriptionScrollList = ScrollableTextBox(
x + 68,
y + 113,
30,
61,
5
)
@@ -215,7 +218,7 @@ class MovesWidget(
fun selectMove(move: Move?) {
selectedMove = if (selectedMove == move || move == null) null else move
descriptionScrollList.setMoveDescription(selectedMove?.description ?: "".text())
descriptionScrollList.setText(selectedMove?.description ?: "".text())
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
@@ -9,10 +9,7 @@
package com.cobblemon.mod.common.entity.ai
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
@@ -32,23 +29,12 @@ object FleeFromAttackerTask {
Trigger { _, entity, _ ->
val hurtByEntity = it.get(hurtByEntity)
val avoidTarget = it.tryGet(avoidTarget).orElse(null)
if (avoidTarget != null && avoidTarget == hurtByEntity) {
if (avoidTarget != null && avoidTarget == hurtByEntity.uuid) {
return@Trigger false
}
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val avoidDurationTicks = mainThreadRuntime.resolveInt(avoidDurationTicks)
val pokemonEntity = entity as? PokemonEntity
pokemonEntity?.let {
it.after(1.0f) {
it.playMoodParticle("mood_stress")
}
}
entity.brain.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, hurtByEntity, avoidDurationTicks.toLong())
return@Trigger true
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.entity.ai
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
@@ -25,16 +24,10 @@ object GetAngryAtAttackerTask {
Trigger { _, entity, _ ->
val hurtByEntity = it.get(hurtByEntity)
val angryAt = it.tryGet(angryAt).orElse(null)
if (angryAt != null && angryAt == hurtByEntity.uuid) {
return@Trigger false
}
val becomingAngry = angryAt == null
entity.brain.setMemory(MemoryModuleType.ANGRY_AT, hurtByEntity.uuid)
if (becomingAngry) {
(entity as PokemonEntity?)?.playMoodParticle("mood_anger_steam")
}
return@Trigger true
}
}
@@ -47,6 +47,7 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleCaptureStartPac
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.PokemonMetData
import com.cobblemon.mod.common.pokemon.ai.PokemonBrain
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.*
@@ -74,6 +75,9 @@ import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.EntityHitResult
import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3
import java.time.LocalDate
import java.time.ZoneId
import java.util.Date
import java.util.concurrent.CompletableFuture
class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragModifier, Schedulable {
@@ -360,6 +364,12 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
party.add(pokemon.pokemon)
val leashHolder = pokemon.leashHolder
pokemon.pokemon.metData = PokemonMetData(
this.level().getBiome(this.blockPosition()),
LocalDate.now(ZoneId.systemDefault()),
pokemon.pokemon.level
)
if (leashHolder != null) {
val blockPos = leashHolder.blockPosition()
val blockState = level().getBlockState(blockPos)
@@ -34,9 +34,6 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.molang.ObjectValue
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.net.serializers.PlatformTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.RideBoostsDataSerializer
@@ -428,9 +425,6 @@ open class PokemonEntity(
var occupiedSeats = mutableMapOf<Seat, Entity>()
var moodParticleLock = false
var playedZeroStaminaParticle = false
init {
delegate.initialize(this)
delegate.changePokemon(pokemon)
@@ -910,27 +904,6 @@ open class PokemonEntity(
return future
}
fun playMoodParticle(particleName: String) {
if (moodParticleLock) return
val actionEffect = ActionEffects.actionEffects[cobblemonResource(particleName)] ?: return
cry()
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(UsersProvider(this)),
runtime = runtime,
level = level()
)
actionEffect.run(context)
moodParticleLock = true
after(5.0f) {
moodParticleLock = false
}
}
override fun saveWithoutId(nbt: CompoundTag): CompoundTag {
val tethering = this.tethering
if (tethering != null) {
@@ -2342,8 +2315,6 @@ open class PokemonEntity(
ifRidingAvailable { _, _, state ->
state.stamina.set(entityData.get(RIDE_STAMINA))
}
playedZeroStaminaParticle = false
}
}
@@ -2372,26 +2343,12 @@ open class PokemonEntity(
override fun tickRidden(driver: Player, movementInput: Vec3) {
super.tickRidden(driver, movementInput)
ifRidingAvailable { behaviour, settings, state ->
behaviour.tick(settings, state, this, driver, movementInput)
if (entityData.get(RIDE_STAMINA) == -1F) {
state.stamina.set(1F, forced = true)
}
if (!level().isClientSide) {
val stamina = state.stamina.get()
if (stamina <= 0f) {
if (!playedZeroStaminaParticle) {
playMoodParticle("mood_stress")
playedZeroStaminaParticle = true
}
} else {
playedZeroStaminaParticle = false
}
}
if (!this.level().isClientSide) {
val pose = behaviour.pose(settings, state, this)
if (pose != this.entityData.get(POSE_TYPE)) {
@@ -44,11 +44,6 @@ class DefendOwnerSensor : Sensor<PokemonEntity>(10) {
return@findClosest false
}.map { mob -> mob as LivingEntity }
val previousAttacker = entity.brain.getMemorySafely(CobblemonMemories.NEAREST_VISIBLE_ATTACKER)
val isNewAttacker = nearestAttacker.isPresent && previousAttacker.isEmpty
entity.brain.setMemory(CobblemonMemories.NEAREST_VISIBLE_ATTACKER, nearestAttacker)
if (isNewAttacker) {
entity.playMoodParticle("mood_anger_steam")
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getMemorySafely
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.ai.behavior.OneShot
@@ -61,12 +60,7 @@ object AttackHostileMobsTask {
}
if (hostile != null) {
val isNewTarget = currentTarget == null
entity.brain.setMemory(MemoryModuleType.ATTACK_TARGET, hostile)
if (isNewTarget) {
(entity as? PokemonEntity)?.playMoodParticle("mood_anger_steam")
}
true
} else {
false
@@ -91,7 +91,7 @@ class EatHeldItemTask(entity: PokemonEntity) : Behavior<PokemonEntity>(
}
this.timelastEaten = time
if (itemConfig != null && itemConfig.fullnessValue > 0) {
entity.pokemon.feedPokemon(itemConfig.fullnessValue, false, false)
entity.pokemon.feedPokemon(itemConfig.fullnessValue, false)
}
// Check if the result item is something that should be dropped
resultItemStack = entity.pokemon.heldItem()
@@ -13,9 +13,7 @@ import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.client.pot.CookingQuality
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
@@ -94,9 +92,7 @@ class AprijuiceItem(val type: Apricorn): CobblemonItem(Properties().stacksTo(16)
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
val boosts = getBoosts(stack)
pokemon.addRideBoosts(boosts.mapValues { it.value.toFloat() })
@@ -96,9 +96,8 @@ class PokePuffItem : Item(Properties().stacksTo(64)), PokemonSelectingItem {
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(4)) {
return InteractionResultHolder.fail(stack)
}
// Feed the Pokémon 4 fullness points
pokemon.feedPokemon(4)
val friendshipChange = PokePuffUtils.calculateFriendshipChange(stack, pokemon.nature)
@@ -54,16 +54,14 @@ class FriendshipRaisingBerryItem(block: BerryBlock, val stat: Stat) : BerryItem(
val evLowerAmount = max(genericRuntime.resolveInt(CobblemonMechanics.berries.evLowerAmount), 0)
val decreasedEVs = pokemon.evs.add(stat, -evLowerAmount, ItemEvSource(player, stack, pokemon)) != 0
if (!increasedFriendship && !decreasedEVs) {
return InteractionResultHolder.pass(stack)
}
return if (increasedFriendship || decreasedEVs) {
pokemon.feedPokemon(1)
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
stack.consume(1, player)
InteractionResultHolder.success(stack)
} else {
InteractionResultHolder.pass(stack)
}
stack.consume(1, player)
return InteractionResultHolder.success(stack)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -54,10 +54,7 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
pokemon.currentHealth = Integer.min(pokemon.currentHealth + genericRuntime.resolveInt(amount(), pokemon), pokemon.maxHealth)
stack.consume(1, player)
return InteractionResultHolder.success(stack)
@@ -65,7 +62,7 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -48,26 +48,21 @@ class PPRestoringBerryItem(block: BerryBlock, val amount: () -> ExpressionLike):
&& super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon, move: Move) {
if (!canUseOnPokemon(stack, pokemon) && canUseOnMove(stack, move)) {
return
}
if (canUseOnPokemon(stack, pokemon) && canUseOnMove(stack, move)) {
pokemon.feedPokemon(1)
val moveToRecover = pokemon.moveSet.find { it.template == move.template }
if (moveToRecover != null && moveToRecover.currentPp < moveToRecover.maxPp) {
moveToRecover.currentPp =
min(moveToRecover.maxPp, moveToRecover.currentPp + genericRuntime.resolveInt(amount(), pokemon))
if (!pokemon.feedPokemon(1)) {
return
}
val moveToRecover = pokemon.moveSet.find { it.template == move.template }
if (moveToRecover != null && moveToRecover.currentPp < moveToRecover.maxPp) {
moveToRecover.currentPp =
min(moveToRecover.maxPp, moveToRecover.currentPp + genericRuntime.resolveInt(amount(), pokemon))
stack.consume(1, player)
stack.consume(1, player)
}
}
}
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon, move: Move) {
super.applyToBattlePokemon(player, stack, battlePokemon, move)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -59,9 +59,7 @@ class PortionHealingBerryItem(block: BerryBlock, val canCauseConfusion: Boolean,
}
// count these berries as multiple feedings due to the amount they heal
if (!pokemon.feedPokemon(5)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(5)
var amount = Integer.min(pokemon.currentHealth + (genericRuntime.resolveFloat(portion(), pokemon) * pokemon.maxHealth).toInt(), pokemon.maxHealth)
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(pokemon, amount, this), { cancelledEvent -> return InteractionResultHolder.fail(stack)}) { event ->
@@ -75,7 +73,7 @@ class PortionHealingBerryItem(block: BerryBlock, val canCauseConfusion: Boolean,
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(5, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(5)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -58,23 +58,19 @@ class StatusCuringBerryItem(block: BerryBlock, vararg val status: Status): Berry
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack>? {
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
return if (canUseOnPokemon(stack, pokemon)) {
pokemon.feedPokemon(1)
pokemon.status = null
stack.consume(1, player)
InteractionResultHolder.success(stack)
} else {
InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.status = null
stack.consume(1, player)
return InteractionResultHolder.success(stack)
}
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -53,10 +53,7 @@ class BerryJuiceItem : CobblemonItem(Properties()), PokemonSelectingItem, Healin
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
var amount = Integer.min(pokemon.currentHealth + 20, pokemon.maxHealth)
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(pokemon, amount, this), { cancelledEvent -> return InteractionResultHolder.fail(stack)}) { event ->
@@ -77,9 +74,7 @@ class BerryJuiceItem : CobblemonItem(Properties()), PokemonSelectingItem, Healin
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
if (!player.hasInfiniteMaterials()) {
val woodenBowlItemStack = ItemStack(Items.BOWL)
if (!player.inventory.add(woodenBowlItemStack)) {
@@ -33,9 +33,7 @@ class FreshStartMochiItem : CobblemonItem(Properties()), PokemonSelectingItem {
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
pokemon.evs.forEach {
pokemon.setEV(it.key, 0)
@@ -27,9 +27,7 @@ class MochiItem(override val stat: Stat): CobblemonItem(Properties()), EVIncreas
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
return super.applyToPokemon(player, stack, pokemon)
}
@@ -0,0 +1,44 @@
package com.cobblemon.mod.common.net.messages.client.pokemon.update
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.PokemonMetData
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.registries.Registries
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import java.time.Instant
import java.time.ZoneId
import kotlin.jvm.optionals.getOrNull
class MetDataUpdatePacket(pokemon: () -> Pokemon?, value: PokemonMetData): SingleUpdatePacket<PokemonMetData, MetDataUpdatePacket>(pokemon, value) {
override val id = ID
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
val biome = value.biome.unwrapKey().getOrNull()
?: ResourceKey.create(Registries.BIOME, ResourceLocation.parse("minecraft:plains"))
buffer.writeResourceKey(biome)
buffer.writeLong(value.date.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli())
buffer.writeInt(value.level)
}
override fun set(
pokemon: Pokemon,
value: PokemonMetData
) {
pokemon.metData = value
}
companion object {
val ID = cobblemonResource("met_data_update")
fun decode(buffer: RegistryFriendlyByteBuf): MetDataUpdatePacket {
val pokemon = decodePokemon(buffer)
val biomeKey = buffer.readResourceKey(Registries.BIOME)
val dateTime = buffer.readLong()
val date = Instant.ofEpochMilli(dateTime).atZone(ZoneId.systemDefault()).toLocalDate()
val level = buffer.readInt()
val biome = buffer.registryAccess().lookupOrThrow(Registries.BIOME).getOrThrow(biomeKey)
return MetDataUpdatePacket(pokemon, PokemonMetData(biome, date, level))
}
}
}
@@ -550,6 +550,12 @@ open class Pokemon : ShowdownIdentifiable {
onChange(OriginalTrainerUpdatePacket({ this }, value))
}
var metData: PokemonMetData? = null
set(value) {
field = value
value?.let { onChange(MetDataUpdatePacket({ this }, it)) }
}
/**
* Cache friendship on trade for OT
*/
@@ -991,10 +997,6 @@ open class Pokemon : ShowdownIdentifiable {
this.recall()
}
fun playMoodParticle(particleName: String) {
entity?.playMoodParticle(particleName)
}
fun heal() {
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(this)) { event ->
this.currentHealth = maxHealth
@@ -1066,13 +1068,23 @@ open class Pokemon : ShowdownIdentifiable {
else -> 0 // For weights less than 0.1
}
fun feedPokemon(feedCount: Int, playSound: Boolean = true, playMoodParticle: Boolean = true): Boolean {
playFullnessEffects(playSound, playMoodParticle)
fun feedPokemon(feedCount: Int, playSound: Boolean = true) {
// play sounds from the entity first (itemstack is consumed outside of this function)
if (this.entity != null && playSound) {
val fullnessPercent = ((this.currentFullness).toFloat() / (this.getMaxFullness()).toFloat()) * (.5f)
if (this.currentFullness >= this.getMaxFullness()) {
this.entity?.playSound(CobblemonSounds.BERRY_EAT_FULL, 1F, 1F)
}
else {
this.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F + fullnessPercent)
}
}
// if it is already full we don't need to do anything
// TODO this can happen when feeding in battle since we are temporarily ignoring fullness checks for pp/hp/status berries
if (isFull()) {
return false
return
}
this.currentFullness = (this.currentFullness + feedCount).coerceIn(0, this.getMaxFullness())
@@ -1081,31 +1093,6 @@ open class Pokemon : ShowdownIdentifiable {
if (this.currentFullness == 1) {
this.resetMetabolismCycle()
}
return true
}
private fun playFullnessEffects(playSound: Boolean = true, playMoodParticle: Boolean = true) {
if (!playSound && !playMoodParticle) return
val pokemonEntity = entity ?: return
val fullnessPercent = ((this.currentFullness).toFloat() / (this.getMaxFullness()).toFloat()) * (.5f)
if (!isFull()) {
if (playSound) {
pokemonEntity.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F + fullnessPercent)
}
return
}
if (playSound) {
pokemonEntity.playSound(CobblemonSounds.BERRY_EAT_FULL, 1F, 1F)
if (playMoodParticle) {
pokemonEntity.playMoodParticle("mood_annoyed")
}
}
}
// decrease a pokemon's Fullness value by a certain amount
@@ -1546,6 +1533,7 @@ open class Pokemon : ShowdownIdentifiable {
this.potentialMarks.clear()
this.potentialMarks += other.marks
this.markings = other.markings
this.metData = other.metData
this.recalculateCharacteristic()
this.updateAspects()
this.refreshOriginalTrainer()
@@ -0,0 +1,47 @@
package com.cobblemon.mod.common.pokemon
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.Holder
import net.minecraft.world.level.biome.Biome
import java.time.LocalDate
/**
* Stores information about the circumstances of a player's meeting with a [Pokemon]
*
* @param biome A [Holder] for the [Biome] the [Pokemon] was caught in
* @param date A [LocalDate] of the real life date the [Pokemon] was caught
* @param level The level the [Pokemon] was when caught
*/
data class PokemonMetData(
val biome: Holder<Biome>,
val date: LocalDate,
val level: Int
) {
companion object {
private val LOCAL_DATE_CODEC: Codec<LocalDate> = Codec.STRING.xmap(
LocalDate::parse,
LocalDate::toString
)
val CODEC: Codec<PokemonMetData> = RecordCodecBuilder.create { instance ->
instance.group(
Biome.CODEC
.fieldOf("biome")
.forGetter { it.biome },
LOCAL_DATE_CODEC
.fieldOf("date")
.forGetter { it.date },
Codec.INT
.fieldOf("level")
.forGetter { it.level }
).apply(instance) { biome, date, level ->
PokemonMetData(
biome = biome,
date = date,
level = level
)
}
}
}
}
@@ -86,6 +86,7 @@ object DataKeys {
const val POKEMON_ORIGINAL_TRAINER_NAME = "PokemonOriginalTrainerName"
const val POKEMON_ORIGINAL_TRAINER_TYPE = "PokemonOriginalTrainerType"
const val POKEMON_ORIGINAL_TRAINER_FRIENDSHIP = "PokemonOriginalTrainerFriendship"
const val POKEMON_MET_DATA = "PokemonMetData"
const val POKEMON_PROPERTIES_MOVES = "Moves"
const val POKEMON_PROPERTIES_HELDITEM = "HeldItem"
@@ -8,25 +8,23 @@
package com.cobblemon.mod.common.util.codec.internal
import com.cobblemon.mod.common.api.mark.Marks
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.client.settings.ServerSettings
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.PokemonMetData
import com.cobblemon.mod.common.util.DataKeys
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import java.util.*
internal data class ClientPokemonP4(
val originalTrainerFriendship: Optional<Int>
val originalTrainerFriendship: Optional<Int>,
val pokemonMetData: Optional<PokemonMetData>
) : Partial<Pokemon> {
override fun into(other: Pokemon): Pokemon {
this.originalTrainerFriendship.ifPresent { other.originalTrainerFriendship = it }
other.metData = this.pokemonMetData.orElse(null)
return other
}
@@ -36,12 +34,18 @@ internal data class ClientPokemonP4(
*/
internal val CODEC: MapCodec<ClientPokemonP4> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(ClientPokemonP4::originalTrainerFriendship)
).apply(instance, ::ClientPokemonP4)
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(ClientPokemonP4::originalTrainerFriendship),
PokemonMetData.CODEC.optionalFieldOf(DataKeys.POKEMON_MET_DATA).forGetter(ClientPokemonP4::pokemonMetData)
).apply(instance) {
originalTrainerFriendship, pokemonMetData -> ClientPokemonP4(
originalTrainerFriendship, pokemonMetData
)
}
}
internal fun from(pokemon: Pokemon): ClientPokemonP4 = ClientPokemonP4(
Optional.ofNullable(pokemon.originalTrainerFriendship)
Optional.ofNullable(pokemon.originalTrainerFriendship),
Optional.ofNullable(pokemon.metData)
)
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.util.codec.internal
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.PokemonMetData
import com.cobblemon.mod.common.util.DataKeys
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
@@ -16,28 +17,32 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.*
internal data class PokemonP4(
val originalTrainerFriendship: Optional<Int>
val originalTrainerFriendship: Optional<Int>,
val pokemonMetData: Optional<PokemonMetData>
) : Partial<Pokemon> {
override fun into(other: Pokemon): Pokemon {
this.originalTrainerFriendship.ifPresent { other.originalTrainerFriendship = it }
other.refreshOriginalTrainer()
other.metData = this.pokemonMetData.orElse(null)
return other
}
companion object {
internal val CODEC: MapCodec<PokemonP4> = RecordCodecBuilder.mapCodec { instance ->
instance.group(
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(PokemonP4::originalTrainerFriendship)
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(PokemonP4::originalTrainerFriendship),
PokemonMetData.CODEC.optionalFieldOf(DataKeys.POKEMON_MET_DATA).forGetter(PokemonP4::pokemonMetData)
).apply(instance) {
originalTrainerFriendship -> PokemonP4(
originalTrainerFriendship
originalTrainerFriendship, pokemonMetData -> PokemonP4(
originalTrainerFriendship, pokemonMetData
)
}
}
internal fun from(pokemon: Pokemon): PokemonP4 = PokemonP4(
Optional.ofNullable(pokemon.originalTrainerFriendship)
Optional.ofNullable(pokemon.originalTrainerFriendship),
Optional.ofNullable(pokemon.metData)
)
}
}
@@ -1,43 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_anger_steam",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/steam_center_double"
}
},
"components": {
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"offset": [0, "math.clamp((v.entity_radius*1.2),0.48,0.96)*0.75", 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1
},
"minecraft:particle_initial_speed": 0,
"minecraft:particle_motion_dynamic": {},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*1.5),0.9,2.4)", "math.clamp((v.entity_radius*0.5),0.3,0.8)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 48,
"texture_height": 176,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [48, 16],
"step_UV": [0, 16],
"max_frame": 11,
"stretch_to_lifetime": true
}
}
}
}
}
}
@@ -1,64 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_anger_veins",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/vein_red"
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.particle_size = math.clamp((v.entity_radius*0.75),0.15,0.4);"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"direction": ["math.random(-1,1)", 1.5, "math.random(-1,1)"]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1.25
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(-1,2)*90"
},
"minecraft:particle_initial_speed": "v.particle_size*13",
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, 0.75, 0],
"linear_drag_coefficient": "v.particle_size*15"
},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*0.75),0.15,0.3)", "math.clamp((v.entity_radius*0.75),0.15,0.3)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 22,
"texture_height": 11,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [11, 11],
"step_UV": [11, 0],
"frames_per_second": 6,
"max_frame": 2,
"loop": true
}
}
},
"minecraft:particle_motion_collision": {},
"minecraft:particle_appearance_lighting": {},
"minecraft:particle_appearance_tinting": {
"color": {
"interpolant": "v.particle_age/v.particle_lifetime",
"gradient": {
"0.86": "#ffffffff",
"1.0": "#FF000000"
}
}
}
}
}
}
@@ -1,49 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_annoyed",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/scribble_purple"
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.particle_size = math.clamp((v.entity_radius*0.75),0.2,0.5);"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 2
},
"minecraft:emitter_shape_point": {
"direction": [0, 1, 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1
},
"minecraft:particle_initial_speed": "v.particle_size*10",
"minecraft:particle_motion_dynamic": {
"linear_drag_coefficient": 3
},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*0.75),0.2,0.5)", "math.clamp((v.entity_radius*0.75),0.2,0.5)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 168,
"texture_height": 14,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [14, 14],
"step_UV": [14, 0],
"frames_per_second": 10,
"max_frame": 12
}
}
},
"minecraft:particle_appearance_lighting": {}
}
}
}
@@ -1,47 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_stress",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/sweat_blue"
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.particle_height = math.clamp((v.entity_radius*1.2),0.48,0.96);"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_looping": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"offset": [0, "math.clamp((v.entity_radius*1.2),0.48,0.96)*0.75", 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.45
},
"minecraft:particle_initial_speed": 0,
"minecraft:particle_motion_dynamic": {},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*1.2),0.48,0.96)", "math.clamp((v.entity_radius*0.65),0.26,0.52)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 216,
"texture_height": 13,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [24, 13],
"step_UV": [24, 0],
"max_frame": 9,
"stretch_to_lifetime": true
}
}
},
"minecraft:particle_appearance_lighting": {}
}
}
}
@@ -1267,6 +1267,8 @@
"cobblemon.ui.info.ability": "Ability",
"cobblemon.ui.info.experience_points": "Exp. Points",
"cobblemon.ui.info.to_next_level": "To Next Lv.",
"cobblemon.ui.info.met_data": "Met in %1$s on %2$s/%3$s/%4$s at Lv.%5$s",
"cobblemon.ui.info.met_data_unavailable": "Met a long time ago",
"cobblemon.ui.trade": "Trade",
"cobblemon.trade.expired.sender": "Your request to trade with %1$s has timed out!",
"cobblemon.trade.expired.receiver": "The trade request from %1$s has timed out!",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

@@ -1,27 +0,0 @@
{
"timeline": [
{
"type": "entity_particles",
"effect": "cobblemon:mood_anger_steam",
"locators": ["head", "top"]
},
{
"type": "loop",
"condition": "q.entity.has_memory_value('minecraft:angry_at') || q.entity.has_memory_value('minecraft:attack_target') || q.entity.has_memory_value('cobblemon:nearest_visible_attacker')",
"keyframe": {
"type": "sequence",
"keyframes": [
{
"type": "entity_particles",
"effect": "cobblemon:mood_anger_veins",
"locators": ["head", "top"]
},
{
"type": "pause",
"pause": 1
}
]
}
}
]
}
@@ -1,9 +0,0 @@
{
"timeline": [
{
"type": "entity_particles",
"effect": "cobblemon:mood_annoyed",
"locators": ["head", "top"]
}
]
}
@@ -1,9 +0,0 @@
{
"timeline": [
{
"type": "entity_particles",
"effect": "cobblemon:mood_stress",
"locators": ["head", "top"]
}
]
}