Compare commits

..
24 changed files with 230 additions and 161 deletions
-3
View File
@@ -36,9 +36,6 @@
- Fixed friendship resetting to the default value when evolving a Pokémon.
- Fixed a bunch of incompatibilities with NeoForge mods
- Fixed a bug where wind charges hitting pasture blocks would recall all pokémon in it.
- Home on the Range advancement shouldn't trigger when opening the Pokemon Menu (shift interact one, mr changelog guy)
- Recipes using concrete should now work on NeoForge
- Fixed saccharine hanging signs sometimes dropping their oak wood counterpart when being broken
### Developer
@@ -37,16 +37,16 @@ public class ClientPacketListenerMixin {
passengers.add(this.level.getEntity(passenger));
}
Arrays
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
passengers.stream()
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
ci.cancel();
}
}
@@ -215,7 +215,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_WALL_HANGING_SIGN = this.create("saccharine_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
@@ -23,7 +23,6 @@ import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
@@ -43,7 +42,6 @@ import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
@@ -52,7 +50,6 @@ import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.LivingEntity
@@ -69,7 +66,6 @@ import net.minecraft.world.item.SignItem
import net.minecraft.world.item.SmithingTemplateItem
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import kotlin.math.ceil
@Suppress("unused", "SameParameterValue")
object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<Item>>, Item>() {
@@ -618,11 +614,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
// Medicine
@JvmField
val RARE_CANDY = candyItem("rare_candy", Rarity.RARE) { player, pokemon ->
if (pokemon != null) return@candyItem pokemon.getExperienceToNextLevel()
return@candyItem -1
}
val RARE_CANDY = candyItem("rare_candy", Rarity.RARE) { _, pokemon -> pokemon.getExperienceToNextLevel() }
@JvmField
val EXPERIENCE_CANDY_XS = candyItem("exp_candy_xs") { _, _ -> CandyItem.DEFAULT_XS_CANDY_YIELD }
@JvmField
@@ -1484,7 +1476,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return item
}
private fun candyItem(name: String, rarity: Rarity = Rarity.COMMON, calculator: (ServerPlayer, Pokemon?) -> Int): CandyItem = this.create(name, CandyItem(rarity, calculator))
private fun candyItem(name: String, rarity: Rarity = Rarity.COMMON, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(rarity, calculator))
private fun pokerodItem(pokeRodId: ResourceLocation, rarity: Rarity = Rarity.COMMON): PokerodItem {
val settings = Item.Properties().stacksTo(1).durability(256).rarity(rarity)
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import net.minecraft.commands.CommandSourceStack
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack
/**
@@ -26,13 +25,8 @@ interface ExperienceSource {
fun isInteraction() = this is CandyExperienceSource
fun isCommand() = this is CommandExperienceSource
fun isSidemod() = this is SidemodExperienceSource
fun isMob() = this is MobExperienceSource
}
open class MobExperienceSource(
mob: LivingEntity,
) : ExperienceSource
open class SidemodExperienceSource(
val sidemodId: String
) : ExperienceSource
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import net.minecraft.client.Minecraft
import net.minecraft.client.player.LocalPlayer
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -344,9 +344,6 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Spawning, lang = "despawner_max_age_ticks", SERVER)
var despawnerMaxAgeTicks = 3600
@CobblemonConfigField(Category.World, lang = "mob_experience_conversion_rate", SERVER)
var mobExperienceConversionRate = 50.0
fun clone(): CobblemonConfig {
val newConfig = CobblemonConfig()
CobblemonConfig::class.memberProperties.forEach { property ->
@@ -38,7 +38,6 @@ import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.RideBoostsDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.pokemon.experience.MobExperienceSource
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
@@ -1260,18 +1259,7 @@ open class PokemonEntity(
override fun doHurtTarget(target: Entity): Boolean {
if (beamMode != 0) return false
val result = super.doHurtTarget(target)
if (result && !target.isAlive && target is LivingEntity && !level().isClientSide) {
val experience = target.getExperienceReward(level() as ServerLevel, this)
val source = MobExperienceSource(mob = target)
if (this.owner is ServerPlayer) {
pokemon.addExperienceWithPlayer(this.owner as ServerPlayer, source, (experience * Cobblemon.config.mobExperienceConversionRate).toInt())
}
else {
pokemon.addExperience(source, (experience * Cobblemon.config.mobExperienceConversionRate).toInt())
}
}
return result
return super.doHurtTarget(target)
}
override fun hurt(source: DamageSource, amount: Float): Boolean {
@@ -8,24 +8,17 @@
package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.experience.CandyExperienceSource
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.food.FoodProperties
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.Level
@@ -41,59 +34,22 @@ import net.minecraft.world.level.Level
*/
class CandyItem(
val item_rarity: Rarity,
val expYield: (ServerPlayer, Pokemon?) -> Int
val calculator: Calculator
) : CobblemonItem(Properties().apply {
rarity(item_rarity)
food(FoodProperties.Builder()
.alwaysEdible()
.nutrition(0)
.saturationModifier(0.0f)
.build())
}) {
}), PokemonSelectingItem {
override fun finishUsingItem(stack: ItemStack, level: Level, livingEntity: LivingEntity): ItemStack? {
if (livingEntity !is Player) return super.finishUsingItem(stack, level, livingEntity)
override val bagItem = null
/* Didn't find a good way to determine the XP needed until the beginning of the next level.
* This works but is a little jank. Feel free to investigate a better solution.
*/
if (level.isClientSide && this == CobblemonItems.RARE_CANDY) {
val volume = if (livingEntity.experienceLevel > 30) 1.0f else livingEntity.experienceLevel.toFloat() / 30.0f
livingEntity.playSound(SoundEvents.PLAYER_LEVELUP, volume * 0.75f, 1.0f)
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
if (user is ServerPlayer) {
return use(user, user.getItemInHand(hand))
}
if (livingEntity !is ServerPlayer) return super.finishUsingItem(stack, level, livingEntity)
if (this == CobblemonItems.RARE_CANDY) {
livingEntity.setExperienceLevels(livingEntity.experienceLevel + 1)
livingEntity.setExperiencePoints(0)
}
else {
val expYield = expYield(livingEntity, null)
val playerExperience = (expYield / Cobblemon.config.mobExperienceConversionRate).toInt().coerceAtLeast(1)
ExperienceOrb.award(livingEntity.serverLevel(), livingEntity.position(), playerExperience)
}
return super.finishUsingItem(stack, level, livingEntity)
return InteractionResultHolder.success(user.getItemInHand(hand))
}
override fun interactLivingEntity(
stack: ItemStack,
player: Player,
interactionTarget: LivingEntity,
usedHand: InteractionHand
): InteractionResult {
if (player.level().isClientSide) return InteractionResult.FAIL
if (player.isCrouching) return InteractionResult.FAIL
if (interactionTarget !is PokemonEntity) return InteractionResult.PASS
if (interactionTarget.pokemon.getOwnerPlayer() != player) return InteractionResult.PASS
return applyToPokemon(player as ServerPlayer, stack, interactionTarget.pokemon)?.result ?: InteractionResult.FAIL
}
fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack>? {
if (!pokemon.canLevelUpFurther()) {
return InteractionResultHolder.fail(stack)
}
val experience = expYield(player, pokemon)
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon): InteractionResultHolder<ItemStack>? {
val experience = this.calculator.calculate(player, pokemon)
CobblemonEvents.EXPERIENCE_CANDY_USE_PRE.postThen(
event = ExperienceCandyUseEvent.Pre(player, pokemon, this, experience, experience),
ifSucceeded = { preEvent ->
@@ -118,6 +74,29 @@ class CandyItem(
return InteractionResultHolder.fail(stack)
}
override fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
return pokemon.isPlayerOwned()
}
/**
* Functional interface responsible for resolving the experience a candy will yield.
*
* @author Licious
* @since March 5th, 2022
*/
fun interface Calculator {
/**
* Resolves the experience the [CandyItem] will give.
*
* @param player The [ServerPlayer] using the candy.
* @param pokemon The [Pokemon] receiving experience.
* @return The experience that will be received
*/
fun calculate(player: ServerPlayer, pokemon: Pokemon): Int
}
companion object {
const val DEFAULT_XS_CANDY_YIELD = 100
const val DEFAULT_S_CANDY_YIELD = 800
@@ -24,10 +24,19 @@ class ClientboundUpdateRidingStatePacket(
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
if (state == null) error("Expected state to be populated for encoding")
//We need to allow for encoding the data buffer instead of state for Flashback compat
//Flashback needs to be able to call encode on the packet it gets, which only has data, not state yet.
if (state == null && data == null) error("Expected state or data to be populated for encoding")
buffer.writeInt(entity)
buffer.writeResourceLocation(behaviour)
state.encode(buffer)
if (state != null) {
state.encode(buffer)
}
else if (data != null) {
buffer.writeBytes(data)
//So the buffer can be read in the future
data.resetReaderIndex()
}
}
companion object {
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers
import com.cobblemon.mod.common.CobblemonSounds
@@ -149,7 +148,6 @@ import net.minecraft.util.Mth.ceil
import net.minecraft.util.Mth.clamp
import net.minecraft.util.StringRepresentable
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.ExperienceOrb
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.MobSpawnType
import net.minecraft.world.entity.player.Player
@@ -2018,12 +2016,6 @@ open class Pokemon : ShowdownIdentifiable {
}
)
val player = getOwnerPlayer()
if (player != null && player.inventory.getArmor(3).item == CobblemonItems.EXP_SHARE) {
val playerExperience = (appliedXP / Cobblemon.config.mobExperienceConversionRate * Cobblemon.config.experienceShareMultiplier).toInt().coerceAtLeast(1)
ExperienceOrb.award(player.serverLevel(), player.position(), playerExperience)
}
experience += appliedXP
// Bound is needed here as we can still be allowed to level up but go over the current cap
var newLevel = experienceGroup.getLevel(experience).coerceAtMost(Cobblemon.config.maxPokemonLevel)
@@ -2621,8 +2621,6 @@
"cobblemon.config.ui.infinite_ride_stamina.tooltip": "Whether rideable Pokémon have infinite stamina.",
"cobblemon.config.ui.remember_riding_camera": "Remember Riding Camera",
"cobblemon.config.ui.remember_riding_camera.tooltip": "Whether the camera mode will automatically switch to the last used camera mode when mounting a rideable Pokémon.",
"cobblemon.config.ui.mob_experience_conversion_rate": "Mob Experience Conversion Rate",
"cobblemon.config.ui.mob_experience_conversion_rate.tooltip": "The conversion rate for mob experience to pokemon experience when a Pokemon kills a mob.",
"cobblemon.config.ui.allow_starter_on_join": "Allow Starter on Join",
"cobblemon.config.ui.prompt_starter_once_only": "Prompt Starter Once Only",
@@ -1,25 +1,20 @@
{
"display": {
"icon": {
"id": "minecraft:shears"
},
"icon": {
"id": "minecraft:shears"
},
"title": {
"translate": "advancements.cobblemon.interact_with_pokemon"
},
"description": {
"translate": "advancements.cobblemon.interact_with_pokemon.description"
},
"hidden": false
"hidden": false
},
"parent": "cobblemon:use_pasture",
"criteria": {
"bucket_interactable_pokemon": {
"conditions": {
"player": {
"flags": {
"is_sneaking": false
}
},
"entity": [
{
"condition": "minecraft:entity_properties",
@@ -37,11 +32,6 @@
},
"shear_interactable_pokemon": {
"conditions": {
"player": {
"flags": {
"is_sneaking": false
}
},
"entity": [
{
"condition": "minecraft:entity_properties",
@@ -59,11 +49,6 @@
},
"bottle_interactable_pokemon": {
"conditions": {
"player": {
"flags": {
"is_sneaking": false
}
},
"entity": [
{
"condition": "minecraft:entity_properties",
@@ -81,11 +66,6 @@
},
"brush_interactable_pokemon": {
"conditions": {
"player": {
"flags": {
"is_sneaking": false
}
},
"entity": [
{
"condition": "minecraft:entity_properties",
@@ -103,11 +83,6 @@
},
"bonemeal_interactable_pokemon": {
"conditions": {
"player": {
"flags": {
"is_sneaking": false
}
},
"entity": [
{
"condition": "minecraft:entity_properties",
@@ -125,11 +100,6 @@
},
"bowl_interactable_pokemon": {
"conditions": {
"player": {
"flags": {
"is_sneaking": false
}
},
"entity": [
{
"condition": "minecraft:entity_properties",
@@ -146,15 +116,6 @@
"trigger": "minecraft:player_interacted_with_entity"
}
},
"requirements": [
[
"bucket_interactable_pokemon",
"shear_interactable_pokemon",
"bottle_interactable_pokemon",
"brush_interactable_pokemon",
"bonemeal_interactable_pokemon",
"bowl_interactable_pokemon"
]
],
"requirements": [[ "bucket_interactable_pokemon", "shear_interactable_pokemon", "bottle_interactable_pokemon", "brush_interactable_pokemon", "bonemeal_interactable_pokemon", "bowl_interactable_pokemon" ]],
"sends_telemetry_event": false
}
@@ -10,7 +10,7 @@
"item": "cobblemon:green_mint_leaf"
},
"C": {
"tag": "c:concretes"
"tag": "c:concrete"
},
"D": {
"tag": "c:gems/diamond"
@@ -10,7 +10,7 @@
"item": "cobblemon:pink_mint_leaf"
},
"C": {
"tag": "c:concretes"
"tag": "c:concrete"
},
"D": {
"tag": "c:gems/diamond"
@@ -10,7 +10,7 @@
"item": "cobblemon:blue_mint_leaf"
},
"C": {
"tag": "c:concretes"
"tag": "c:concrete"
},
"D": {
"tag": "c:gems/diamond"
@@ -10,7 +10,7 @@
"item": "cobblemon:red_mint_leaf"
},
"C": {
"tag": "c:concretes"
"tag": "c:concrete"
},
"D": {
"tag": "c:gems/diamond"
@@ -10,7 +10,7 @@
"item": "cobblemon:cyan_mint_leaf"
},
"C": {
"tag": "c:concretes"
"tag": "c:concrete"
},
"D": {
"tag": "c:gems/diamond"
@@ -10,7 +10,7 @@
"item": "cobblemon:white_mint_leaf"
},
"C": {
"tag": "c:concretes"
"tag": "c:concrete"
},
"D": {
"tag": "c:gems/diamond"
+8
View File
@@ -38,6 +38,7 @@ repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.terraformersmc.com/")
maven(url = "https://api.modrinth.com/maven")
}
dependencies {
@@ -58,6 +59,13 @@ dependencies {
modImplementation(libs.bundles.fabric.integrations.implementation)
modRuntimeOnly(libs.bundles.fabric.integrations.runtimeOnly)
modImplementation("maven.modrinth:flashback:0.38.2-fabric,1.21.1")
// Lattice (config)
modImplementation("com.moulberry:lattice:1.2.18") {
attributes {
attribute(Attribute.of("earth.terrarium.cloche.modLoader", String::class.java), "fabric")
}
}
// modImplementation(libs.flywheelFabric)
// include(libs.flywheelFabric)
@@ -0,0 +1,45 @@
/*
* 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.fabric.mixin.compat;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
import com.llamalad7.mixinextras.sugar.Local;
import com.moulberry.flashback.record.Recorder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.world.entity.player.Player;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(Recorder.class)
public abstract class RecorderMixin {
@Inject(method = "writeLocalData()V", at = @At(value = "INVOKE", target = "Lcom/moulberry/flashback/io/AsyncReplaySaver;writeGamePackets(Lnet/minecraft/network/codec/StreamCodec;Ljava/util/List;)V"))
public void cobblemon$writeLocalData(
CallbackInfo ci,
@Local(name = "gamePackets") List<Packet<? super ClientGamePacketListener>> gamePackets
) {
// The server will not send a ClientboundUpdateDriverInputPacket to the player that initially set the driver input
// so Flashback needs to save that data itself. For stuff like players holding space on Pokemon mounts
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return;
// Gather driver Input
float vertInput = player.jumping ? 1.0f : (player.isShiftKeyDown() ? -1.0f : 0.0f);
Vector3f driverInput = new Vector3f(Math.signum(player.xxa), vertInput, Math.signum(player.zza));
gamePackets.add(new ClientboundCustomPayloadPacket(new ClientboundUpdateDriverInputPacket(driverInput, player.getId())));
}
}
@@ -0,0 +1,106 @@
/*
* 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.fabric.mixin.compat;
import com.cobblemon.mod.common.api.mark.Mark;
import com.cobblemon.mod.common.api.mark.Marks;
import com.cobblemon.mod.common.api.pokeball.PokeBalls;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnExtraDataEntityPacket;
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket;
import com.cobblemon.mod.common.pokeball.PokeBall;
import com.cobblemon.mod.common.pokemon.Pokemon;
import com.cobblemon.mod.common.pokemon.Species;
import com.moulberry.flashback.playback.ReplayGamePacketHandler;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.client.Minecraft;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
@Mixin(ReplayGamePacketHandler.class)
public abstract class ReplayGamePacketHandlerMixin {
@Shadow
public abstract void handleAddEntity(ClientboundAddEntityPacket clientboundAddEntityPacket);
@Shadow
public abstract ServerLevel level();
@Shadow(remap = false)
private Int2ObjectMap<Entity> pendingEntities;
@Inject(method = "handleCustomPayload", at = @At("HEAD"))
public void cobblemon$handleSpawnEntityWithExtraData(ClientboundCustomPayloadPacket packet, CallbackInfo ci) {
if (packet.payload() instanceof SpawnExtraDataEntityPacket<?, ?> cobblemonPacket) {
ClientboundAddEntityPacket addEntityPacket = cobblemonPacket.getVanillaSpawnPacket();
this.handleAddEntity(addEntityPacket);
Entity entity = pendingEntities.get(addEntityPacket.getId());
if (entity instanceof PokemonEntity pokemonEntity && cobblemonPacket instanceof SpawnPokemonPacket spawnPokemonPacket) {
Pokemon pokemon = pokemonEntity.getPokemon();
pokemon.setUuid(spawnPokemonPacket.getPokemonUUID());
Species species = Optional.ofNullable(PokemonSpecies.INSTANCE.getByIdentifier(spawnPokemonPacket.getSpeciesId()))
.orElse(PokemonSpecies.INSTANCE.random());
pokemon.setSpecies(species);
pokemon.setScaleModifier(spawnPokemonPacket.getScaleModifier());
pokemon.setGender(spawnPokemonPacket.getGender());
pokemon.setShiny(spawnPokemonPacket.getShiny());
pokemon.setForm(pokemon.getSpecies().getFormByShowdownId(spawnPokemonPacket.getFormName()));
pokemon.setForcedAspects(spawnPokemonPacket.getAspects());
pokemon.setNickname(spawnPokemonPacket.getNickname());
ResourceLocation markId = spawnPokemonPacket.getMark();
if (markId != null) {
pokemon.setActiveMark(Marks.INSTANCE.getByIdentifier(spawnPokemonPacket.getMark()));
}
PokeBall caughtBall = PokeBalls.INSTANCE.getPokeBall(spawnPokemonPacket.getCaughtBall());
if (caughtBall != null) {
pokemon.setCaughtBall(caughtBall);
}
}
}
}
@Inject(method = "handleSetEntityPassengersPacket", at = @At("HEAD"), cancellable = true)
public void cobblemon$handleSetEntityPassengersPacket(ClientboundSetPassengersPacket packet, CallbackInfo ci) {
Entity entity = this.level().getEntity(packet.getVehicle());
if (entity instanceof PokemonEntity pokemon) {
var passengers = new ArrayList<Entity>();
for (int passenger : packet.getPassengers()) {
passengers.add(this.level().getEntity(passenger));
}
Arrays
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
passengers.stream()
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
ci.cancel();
}
}
}
@@ -9,7 +9,9 @@
"GameRendererMixin",
"GuiMixin",
"RecipeBookCategoriesMixin",
"pokeball.ModelLoaderMixin"
"pokeball.ModelLoaderMixin",
"compat.ReplayGamePacketHandlerMixin",
"compat.RecorderMixin"
],
"mixins": [
"EntityMixin",
+1 -1
View File
@@ -100,7 +100,7 @@ kotlin = [ "kotlin-stdlib", "kotlin-reflect" ]
fabric = [ "fabric-kotlin", "fabric-permissions" ]
common-integrations-compileOnly = [ "adorn-fabric", "jei-api" ]
fabric-integrations-implementation = [ "mod-menu" ]
fabric-integrations-runtimeOnly = [ "jei-fabric", "c2me-fabric" ]
fabric-integrations-runtimeOnly = [ "jei-fabric" ]
neoforge-integrations-runtimeOnly = [ "jei-neoforge", "c2me-neoforge" ]
mongo = [ "mongo-driver-core", "mongo-driver-sync" ]
unitTesting = [ "junit-fabric", "junit-params", "mockito-core", "mock-kotlin", "classgraph" ]