Compare commits

..
Author SHA1 Message Date
Polymeta 7ccd0bbe11 Merge branch 'multiplayer_mount_sync' into 'main'
Multiplayer mount sync

Closes #2023

See merge request cable-mc/cobblemon!2315
2026-06-22 12:23:42 +00:00
Polymeta d289880cc8 Merge branch 'tmm-fixes-n-changes' into 'main'
TMM Fixes and Changes

See merge request cable-mc/cobblemon!2316
2026-06-20 19:43:21 +00:00
Paul 6609c7d038 Added held_item_visible pokemonproperty 2026-06-20 22:29:14 +10:00
Paul 88afe05c16 Fixed NPE's when block entities aren't attached to berry blocks at time of ticking, closes #2020 2026-06-20 18:01:52 +10:00
Gertimoshka 99aa25e1bf I forgor 2026-06-19 17:50:52 +03:00
Gertimoshka 76faa50035 Fixes, tmm multiplayer syncing and cleanup 2026-06-19 17:42:50 +03:00
aeroeng15 8ce989cb6b Changelog 2026-06-18 19:03:45 -07:00
aeroeng15 02030950aa Force passenger updates 2026-06-18 18:53:36 -07:00
21 changed files with 226 additions and 487 deletions
+4 -2
View File
@@ -42,9 +42,8 @@
- 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`.
- A new Biome tag called `#cobblemon:is_stony_beach` has been added.
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
- 1 New shipwreck cove structure called the "magma_shipwreck_cove" has been added.
- 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.
@@ -57,6 +56,7 @@
- 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
- Drampa
@@ -357,6 +357,8 @@
- Fixed Guilded chests being more accessible than intended
- Fixed fished up Pokémon not being added as "seen" to the Pokedex
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokedex
- Fixed a crash involving berry trees that have been world edited.
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks
### Developer
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
@@ -10,13 +10,11 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
@@ -98,26 +96,4 @@ public class HumanoidModelMixin {
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
// Pose the right arm forward while recalling so the held Poké Ball sits out in front.
@Inject(method = "setupAnim*", at = @At("TAIL"))
public void cobblemon$poseRecallArm(
LivingEntity entity,
float limbSwing,
float limbSwingAmount,
float ageInTicks,
float netHeadYaw,
float headPitch,
CallbackInfo ci
) {
if (!(entity instanceof AbstractClientPlayer player)) return;
// Skip while riding so we don't override the mounted-riding arm animation.
if (entity.getVehicle() instanceof PokemonEntity) return;
float raise = RecallArmAnimationTracker.INSTANCE.raiseFactor(player.getUUID());
if (raise <= 0f) return;
float armForwardXRot = (float) (-Math.PI / 2.0);
this.rightArm.xRot = Mth.lerp(raise, this.rightArm.xRot, armForwardXRot);
this.rightArm.yRot = Mth.lerp(raise, this.rightArm.yRot, 0f);
this.rightArm.zRot = Mth.lerp(raise, this.rightArm.zRot, 0f);
}
}
@@ -1,45 +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.mixin.client;
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
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;
/**
* Hides the player's right-hand item while recalling so it doesn't overlap the recall Poké Ball
* drawn by PlayerRecallBallRenderer.
*/
@Mixin(ItemInHandLayer.class)
public class ItemInHandLayerMixin {
@Inject(method = "renderArmWithItem", at = @At("HEAD"), cancellable = true)
private void cobblemon$hideRecallHandItem(
LivingEntity entity,
ItemStack itemStack,
ItemDisplayContext itemDisplayContext,
HumanoidArm humanoidArm,
PoseStack poseStack,
MultiBufferSource multiBufferSource,
int light,
CallbackInfo ci
) {
if (humanoidArm == HumanoidArm.RIGHT && RecallArmAnimationTracker.INSTANCE.isRecalling(entity.getUUID())) {
ci.cancel();
}
}
}
@@ -9,8 +9,6 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.render.player.FirstPersonRecallBallRenderer;
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
@@ -33,12 +31,6 @@ public class ItemInHandRendererMixin {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
// While recalling, swap the main-hand item for the rising Poké Ball.
else if (interactionHand == InteractionHand.MAIN_HAND
&& RecallArmAnimationTracker.INSTANCE.isRecalling(abstractClientPlayer.getUUID())
&& FirstPersonRecallBallRenderer.INSTANCE.render(poseStack, multiBufferSource, j, f)) {
ci.cancel();
}
else if (abstractClientPlayer instanceof OrientationControllable controllable && controllable.getOrientationController().isActive()) {
}
@@ -108,6 +108,7 @@ import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
import com.cobblemon.mod.common.pokemon.properties.HeldItemVisibleProperty
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
@@ -350,6 +351,7 @@ object Cobblemon {
CustomPokemonProperty.register(UnaspectPropertyType)
CustomPokemonProperty.register(FreezeFrameProperty)
CustomPokemonProperty.register(NoAIProperty)
CustomPokemonProperty.register(HeldItemVisibleProperty)
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
@@ -35,6 +35,7 @@ import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SynchronizeActiveTMHandler
import com.cobblemon.mod.common.client.net.habitat.OpenHabitatBlockEditorHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
@@ -147,6 +148,7 @@ import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
@@ -375,6 +377,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PlayerInteractOptionsPacket.ID, PlayerInteractOptionsPacket::decode, PlayerInteractOptionsHandler))
list.add(PacketRegisterInfo(PokedexUIPacket.ID, PokedexUIPacket::decode, PokedexUIPacketHandler))
list.add(PacketRegisterInfo(ExpGainedDataPacket.ID, ExpGainedDataPacket::decode, ExpGainedDataPacketHandler))
list.add(PacketRegisterInfo(SynchronizeActiveTMPacket.ID, SynchronizeActiveTMPacket::decode, SynchronizeActiveTMHandler))
// Starter packets
list.add(PacketRegisterInfo(OpenStarterUIPacket.ID, OpenStarterUIPacket::decode, StarterUIPacketHandler))
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.block
//import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler.pos
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.berry.Berry
@@ -40,7 +39,11 @@ import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.LevelReader
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.BonemealableBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
@@ -128,7 +131,10 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
val newAge = curAge + 1
if (newAge > FRUIT_AGE) return
val newState = state.setValue(AGE, newAge)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: run {
world.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_CLIENTS)
return
}
if (curAge == MATURE_AGE) {
treeEntity.generateGrowthPoints(world, newState, pos, null)
determineMutation(world, random, pos, newState)
@@ -10,11 +10,13 @@ package com.cobblemon.mod.common.block.tmmachine
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonMenuType
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.server.level.ServerLevel
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.Mth
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
@@ -35,38 +37,20 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
var inventory: Container? = null
var containerData: ContainerData? = null
var currentlySelectedTM: TechnicalMachine? = null
private var lastSelectedTM: TechnicalMachine? = null
var onSelectedTMUpdate: (TechnicalMachine?) -> Unit = {}
constructor(syncId: Int, playerInventory: Inventory) : this(CobblemonMenuType.TM_MACHINE, syncId) {
this.playerInventory = playerInventory
this.inventory = SimpleContainer(5)
this.containerData = SimpleContainerData(6)
this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
this.addDataSlots(containerData!!)
inventory?.startOpen(playerInventory.player)
val startX = 15
val startY = 144
val slotWidth = 18
val slotHeight = 18
for (row in 0..2) {
for (col in 0..8) {
this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
}
}
for (col in 0..8) {
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
}
val container = inventory
container?.let {
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
for (index in INGREDIENT_SLOTS) {
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
}
}
this.addSlotListener(this)
initiateSlots()
}
constructor(syncId: Int, playerInventory: Inventory, inventory: Container, containerData: ContainerData) : this(CobblemonMenuType.TM_MACHINE, syncId) {
@@ -78,6 +62,20 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
this.addDataSlots(containerData)
inventory.startOpen(playerInventory.player)
initiateSlots()
tmMachineEntity?.let {
val move = Moves.getByName(it.activeMove)
move.let {
currentlySelectedTM = TechnicalMachines.moveToTM[it]
synchronizeSelectedTM()
}
}
this.addSlotListener(this)
}
private fun initiateSlots() {
val startX = 15
val startY = 144
val slotWidth = 18
@@ -92,7 +90,7 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
}
val container = inventory
val container = inventory!!
container.let {
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
@@ -100,7 +98,6 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
}
}
this.addSlotListener(this)
}
override fun quickMoveStack(player: Player, index: Int): ItemStack {
@@ -207,4 +204,25 @@ class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(
override fun dataChanged(containerMenu: AbstractContainerMenu, dataSlotIndex: Int, value: Int) {
broadcastChanges()
}
fun synchronizeSelectedTM() {
val serverPlayer = playerInventory?.player as? ServerPlayer ?: return
serverPlayer.sendPacket(SynchronizeActiveTMPacket(currentlySelectedTM))
lastSelectedTM = currentlySelectedTM
}
override fun sendAllDataToRemote() {
super.sendAllDataToRemote()
synchronizeSelectedTM()
}
override fun broadcastChanges() {
super.broadcastChanges()
if (lastSelectedTM != currentlySelectedTM) synchronizeSelectedTM()
}
fun clientSetSelectedTM(tm: TechnicalMachine?) {
currentlySelectedTM = tm
onSelectedTMUpdate(tm)
}
}
@@ -44,7 +44,6 @@ import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
import com.cobblemon.mod.common.client.render.layer.PlayerRecallBallRenderer
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
@@ -406,10 +405,8 @@ object CobblemonClient {
var renderer: LivingEntityRenderer<Player, PlayerModel<Player>>? =
skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
renderer = skinMap[PlayerSkin.Model.SLIM] as LivingEntityRenderer<Player, PlayerModel<Player>>?
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
}
private fun registerMenuScreens() {
@@ -32,7 +32,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer.Companion.ease
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
@@ -321,13 +320,6 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
ballOffset = 0f
ballRotOffset = 0f
sendOutPosition = null
// If a player is recalling, have them hold the matching Poké Ball out in their
// right hand for the duration of the beam (see ourRecallArmAnimationTracker class).
currentEntity.ownerUUID?.let { ownerId ->
if (currentEntity.level().getPlayerByUUID(ownerId) != null) {
RecallArmAnimationTracker.start(ownerId, currentEntity.exposedBall, emptySet())
}
}
afterOnClient(seconds = BEAM_EXTEND_TIME) {
activeSendoutScale = 1F
@@ -120,7 +120,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
fun getLogoResource(langCode: String?): ResourceLocation =
logoResourceMap.entries.find { it.value.contains(langCode?.substringBefore("_")) }?.key ?: logo
}
var selectedTM: TechnicalMachine? = null
var heldStackMove: MoveTemplate? = null
var tmList: SettableObservable<MutableList<TechnicalMachine>> = SettableObservable(mutableListOf())
var output: ItemStack? = null
@@ -159,6 +159,8 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
var oldIngredientItems: Array<Item> = arrayOf(Items.AIR, Items.AIR, Items.AIR)
var oldResultTmSlotItem: Item = Items.AIR
var moveInfoTM: TechnicalMachine? = null
override fun init() {
imageWidth = WIDTH
imageHeight = HEIGHT
@@ -171,6 +173,8 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
logoPosY = topPos + 1
scanPosY = topPos + 1
menu.onSelectedTMUpdate = { updateSelectedTM(it) }
partySlotList.clear()
CobblemonClient.storage.party.forEachIndexed { index, pokemon ->
partySlotList.add(TMPartySlotWidget(
@@ -226,7 +230,14 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
pX = leftPos + 4,
pY = topPos + 29,
tmList,
setTM = { tm, clicked -> setSelectedTM(tm, clicked) }
setTM = { tm, clicked ->
if (clicked) {
if (tm != menu.currentlySelectedTM) SetActiveTMPacket(tm).sendToServer()
setScreenFromMode(TM_BURN_MODE)
} else {
updateMoveInformation(tm)
}
}
)
if (!children().contains(movesScrollingList)) addRenderableWidget(movesScrollingList)
@@ -252,24 +263,21 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
selectedMoveButton = MoveSlotButton(
leftPos + 6,
topPos + 6,
move = selectedTM?.moveName,
pp = selectedTM?.moveName?.pp ?: 0,
move = menu.currentlySelectedTM?.moveName,
pp = menu.currentlySelectedTM?.moveName?.pp ?: 0,
ppAsFraction = false
) {
if ((it as MoveSlotButton).enabled) {
if (::movesScrollingList.isInitialized && movesScrollingList.children().isEmpty()) setMoveList()
playSound(CobblemonSounds.GUI_CLICK)
setScreenFromMode(MOVE_SELECT_MODE)
// Clear active TM
CobblemonNetwork.sendToServer(SetActiveTMPacket(null))
}
}
if (!children().contains(selectedMoveButton)) addRenderableWidget(selectedMoveButton)
startButton = StartButton(leftPos + 5F, topPos + 35F) {
if (!(it as StartButton).disabled) {
selectedTM?.let {
menu.currentlySelectedTM?.let {
val burnActive = menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0
CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
TMMachineBlockEntity.BURN_ACTIVE_INDEX,
@@ -284,17 +292,14 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
))
}*/
CobblemonNetwork.sendToServer(SetActiveTMPacket(it))
//CobblemonNetwork.sendToServer(SetActiveTMPacket(it))
if (burnActive != 1) playSound(CobblemonSounds.TM_MACHINE_START)
inventory.setChanged()
menu.broadcastChanges()
}
}
}
if (!children().contains(startButton)) addRenderableWidget(startButton)
setScreenFromMode(mode)
initScreen = false
updateSlotItemMemory()
}
@@ -306,7 +311,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
if (scanPosY + 1 >= (topPos + SCREEN_SAVER_HEIGHT) * 2) scanPosY = 0
if (mode == TM_BURN_MODE) {
selectedTM?.let { tm ->
menu.currentlySelectedTM?.let { tm ->
val resultStack = menu.inventory!!.getItem(TMMachineMenu.RESULT_SLOT)
val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
.also { TMMoveComponent.setTMMove(it, tm.moveName) }
@@ -339,7 +344,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
}
if (::selectedMoveButton.isInitialized) {
selectedMoveButton.enabled = !isBurnActive() && (heldStackMove == null || heldStackMove == selectedTM?.moveName)
selectedMoveButton.enabled = (heldStackMove == null || heldStackMove == menu.currentlySelectedTM?.moveName)
}
if (isBurnActive()) {
@@ -350,9 +355,6 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
}
}
}
} else {
// Check if screen should be switched upon opening UI
if (!initScreen) loadBurnScreenData()
}
if (heldMoveItemChanged) {
@@ -400,32 +402,6 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
return wasScreenSaverActive
}
private fun loadBurnScreenData() {
var tm: TechnicalMachine? = null
val blockPos = menu.containerData?.let {
val x = it.get(TMMachineBlockEntity.POSITION_X_INDEX)
val y = it.get(TMMachineBlockEntity.POSITION_Y_INDEX)
val z = it.get(TMMachineBlockEntity.POSITION_Z_INDEX)
BlockPos(x, y, z)
}
inventory.player.level().getBlockEntity(blockPos)?.let { blockEntity ->
if (blockEntity is TMMachineBlockEntity) {
Moves.getByName(blockEntity.activeMove)?.let {
tm = TechnicalMachines.moveToTM[it]
}
}
}
// If burn currently in progress or if active TM set, set to burn screen
if (isBurnActive() || tm != null) {
setSelectedTM(tm, true)
}
initScreen = true
}
private fun isBurnActive(): Boolean = (menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0) == 1
private fun setScreenFromMode(screen: Int) {
@@ -467,7 +443,6 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
private fun toggleTypeSelect(isVisible: Boolean) {
if (isVisible) {
setSelectedTM(null, false)
if (::moveSearchWidget.isInitialized) moveSearchWidget.value = ""
if (selectedPokemon != null) selectedPokemon = null
sortType = null
@@ -505,7 +480,6 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
}
private fun toggleTMBurn(isVisible: Boolean) {
if (!isVisible) setSelectedTM(null, false)
if (::selectedMoveButton.isInitialized) selectedMoveButton.visible = isVisible
if (::startButton.isInitialized) startButton.visible = isVisible
}
@@ -516,35 +490,40 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
return if (learnableMoves.contains(move)) TMPartySlotWidget.CAN_LEARN else TMPartySlotWidget.CANNOT_LEARN
}
fun setSelectedTM(tm: TechnicalMachine?, clicked: Boolean) {
if (selectedTM != tm) {
selectedTM = tm
updatePartySlotStatus(tm?.moveName)
fun updateSelectedTM(tm: TechnicalMachine?) {
if (!initScreen) {
initScreen = true
setScreenFromMode(TM_BURN_MODE)
tm?.let {
sortType = ElementalTypes.get(tm.type)
setMoveList()
}
}
if (::movesScrollingList.isInitialized) {
movesScrollingList.setSlotHighlighted(if (clicked) null else selectedTM?.id)
if (mode == TM_BURN_MODE) updateMoveInformation(tm)
tm?.let {
if (::selectedMoveButton.isInitialized) {
selectedMoveButton.apply {
move = it.moveName
pp = it.moveName.pp
}
}
}
}
fun updateMoveInformation(tm: TechnicalMachine?) {
moveInfoTM = tm
if (::moveDescriptionWidget.isInitialized) {
val selectedDescription = selectedTM?.moveName?.description?.string
val selectedDescription = tm?.moveName?.description?.string
moveDescriptionWidget.setText(
if (selectedDescription != null) listOf(selectedDescription) else emptyList()
)
}
if (clicked) {
CobblemonNetwork.sendToServer(SetActiveTMPacket(tm))
selectedTM?.let {
if (::selectedMoveButton.isInitialized) {
selectedMoveButton.apply {
move = it.moveName
pp = it.moveName.pp
}
setScreenFromMode(TM_BURN_MODE)
}
}
if (::movesScrollingList.isInitialized) {
movesScrollingList.setSlotHighlighted(tm?.id)
}
}
@@ -557,7 +536,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
}
fun renderDiscTypeOverlay(poseStack: PoseStack, isDiscRotating: Boolean, alpha: Float = 1F) {
selectedTM?.let {
menu.currentlySelectedTM?.let {
val primaryRgb = it.moveName.elementalType.primaryColor.toRGB()
val secondaryRgb = it.moveName.elementalType.secondaryColor.toRGB()
blitk(
@@ -706,7 +685,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
}
fun renderMoveInfo(context: GuiGraphics, mouseX: Int, mouseY: Int) {
val moveTemplate = selectedTM?.moveName
val moveTemplate = moveInfoTM?.moveName
blitk(
matrixStack = context.pose(),
@@ -1057,7 +1036,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
movesScrollingList.setDisabled(true, TechnicalMachines.moveToTM[heldStackMove])
if (mode == MOVE_SELECT_MODE) {
val heldStackTm = TechnicalMachines.moveToTM[heldStackMove]
setSelectedTM(if (tmList.get().contains(heldStackTm)) heldStackTm else null, false)
updateMoveInformation(if (tmList.get().contains(heldStackTm)) heldStackTm else null)
// Scroll to move if moves list contains held TM move
if (tmList.get().contains(heldStackTm)) {
@@ -1077,8 +1056,8 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
heldStackMove = null
// Reset selected move on move select screen
if (mode == MOVE_SELECT_MODE) setSelectedTM(null, false)
updatePartySlotStatus(selectedTM?.moveName)
if (mode == MOVE_SELECT_MODE) updateMoveInformation(null)
updatePartySlotStatus(menu.currentlySelectedTM?.moveName)
if (::movesScrollingList.isInitialized) {
movesScrollingList.setDisabled(false)
@@ -1155,7 +1134,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
if (isInventoryKeyPressed(minecraft, keyCode, scanCode)) {
if (searchFocused) { return true }
else {
Minecraft.getInstance().setScreen(null)
onClose()
return true
}
}
@@ -1166,7 +1145,7 @@ class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, ti
this.focused = null
return true
} else {
Minecraft.getInstance().setScreen(null)
onClose()
return true
}
}
@@ -19,7 +19,6 @@ import net.minecraft.world.level.block.Block
object SetActiveTMPacketHandler : ServerNetworkPacketHandler<SetActiveTMPacket> {
override fun handle(packet: SetActiveTMPacket, server: MinecraftServer, player: ServerPlayer) {
val menu = player.containerMenu as? TMMachineMenu ?: return
val inventory = menu.inventory ?: return
val tmData = Cobblemon.playerDataManager.getTMData(player)
if (packet.tm != null && (!packet.tm.isPassivelyObtained() && packet.tm.id !in tmData.learnedTMs)) {
@@ -30,9 +29,17 @@ object SetActiveTMPacketHandler : ServerNetworkPacketHandler<SetActiveTMPacket>
blockEntity.activeMove = packet.tm?.moveName?.name ?: ""
blockEntity.setChanged()
player.level().sendBlockUpdated(blockEntity.blockPos, blockEntity.blockState, blockEntity.blockState, Block.UPDATE_CLIENTS)
}
inventory.setChanged()
menu.broadcastChanges()
blockEntity.burnActive = false
blockEntity.burnProgress = 0
player.level().players().forEach { thePlayer -> //Update all players with matching menu/blockentity
val tmmMenu = thePlayer.containerMenu as? TMMachineMenu ?: return@forEach
if (tmmMenu.tmMachineEntity == blockEntity) {
tmmMenu.currentlySelectedTM = packet.tm
tmmMenu.broadcastChanges()
}
}
}
}
}
@@ -0,0 +1,21 @@
/*
* 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.net.gui
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
import net.minecraft.client.Minecraft
object SynchronizeActiveTMHandler: ClientNetworkPacketHandler<SynchronizeActiveTMPacket> {
override fun handle(packet: SynchronizeActiveTMPacket, client: Minecraft) {
val tmmMenu = client.player?.containerMenu as? TMMachineMenu ?: return
tmmMenu.clientSetSelectedTM(packet.tm)
}
}
@@ -1,127 +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.render.layer
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.model.PlayerModel
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.entity.RenderLayerParent
import net.minecraft.client.renderer.entity.layers.RenderLayer
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.world.entity.HumanoidArm
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
/**
* Draws the matching Poké Ball in a recalling player's right hand and records where the recall beam
* should start. Driven by [RecallArmAnimationTracker]; the right arm is posed forward in
* HumanoidModelMixin so [PlayerModel.translateToHand] lands on the extended hand.
*
* The local player in first person is handled by FirstPersonRecallBallRenderer instead, so it's
* skipped here.
*/
class PlayerRecallBallRenderer<T : Player>(
renderLayerParent: RenderLayerParent<T, PlayerModel<T>>
) : RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
companion object {
private const val BALL_SCALE = 0.5F
private const val HAND_X = 0.0625
private const val HAND_Y = 0.125
private const val HAND_Z = -0.625
private const val BALL_YAW_SPIN = 180F
private const val BALL_INFLATE = 1.08F
private const val BEAM_X_OFFSET = 0F
private const val BEAM_Y_OFFSET = 0.18F
private const val BEAM_Z_OFFSET = -0.30F
}
private val ballContext = RenderContext().also {
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
}
override fun render(
matrixStack: PoseStack,
buffer: MultiBufferSource,
packedLight: Int,
player: T,
limbSwing: Float,
limbSwingAmount: Float,
partialTicks: Float,
ageInTicks: Float,
netHeadYaw: Float,
headPitch: Float
) {
val anim = RecallArmAnimationTracker.get(player.uuid) ?: return
val mc = Minecraft.getInstance()
if (player === mc.player && mc.options.cameraType.isFirstPerson) return
matrixStack.pushPose()
getParentModel().translateToHand(HumanoidArm.RIGHT, matrixStack)
matrixStack.mulPose(Axis.XP.rotationDegrees(-90F))
matrixStack.mulPose(Axis.YP.rotationDegrees(180F))
matrixStack.translate(HAND_X, HAND_Y, HAND_Z)
renderBall(anim, matrixStack, buffer, packedLight, partialTicks)
matrixStack.popPose()
}
private fun renderBall(
anim: RecallArmAnimationTracker.RecallAnim,
matrixStack: PoseStack,
buffer: MultiBufferSource,
packedLight: Int,
partialTicks: Float
) {
val state = anim.ballState
val ball = anim.ball
matrixStack.pushPose()
matrixStack.scale(BALL_INFLATE, BALL_INFLATE, BALL_INFLATE)
matrixStack.translate(0.0, (-8.0 / 16.0) * BALL_SCALE, 0.0)
matrixStack.scale(BALL_SCALE, -BALL_SCALE, -BALL_SCALE)
matrixStack.mulPose(Axis.YP.rotationDegrees(BALL_YAW_SPIN))
val model = VaryingModelRepository.getPoser(ball.name, state)
val texture = VaryingModelRepository.getTexture(ball.name, state)
state.updatePartialTicks(partialTicks)
model.context = ballContext
ballContext.put(RenderContext.ASPECTS, state.aspects)
ballContext.put(RenderContext.POSABLE_STATE, state)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
state.locatorStates["beam"]?.let { beamLocator ->
matrixStack.pushPose()
matrixStack.mulPose(beamLocator.matrix)
val camRelative = matrixStack.last().pose().transformPosition(Vector3f(BEAM_X_OFFSET, BEAM_Y_OFFSET, BEAM_Z_OFFSET))
matrixStack.popPose()
val camera = Minecraft.getInstance().gameRenderer.mainCamera.position
anim.beamOrigin = Vec3(camera.x + camRelative.x, camera.y + camRelative.y, camera.z + camRelative.z)
}
val consumer = ItemRenderer.getFoilBufferDirect(buffer, RenderType.entityCutout(texture), false, false)
model.render(ballContext, matrixStack, consumer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
model.red = 1F
model.green = 1F
model.blue = 1F
model.resetLayerContext()
matrixStack.popPose()
}
}
@@ -1,76 +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.render.player
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.texture.OverlayTexture
/**
* Draws the recalling player's Poké Ball in first person, rising into view from the bottom of the
* screen in place of their held item. The world-space beam still starts near the hand (the eye/look
* fallback in PokemonRenderer), so this is cosmetic only.
*
* Called from ItemInHandRendererMixin, which cancels the vanilla main-hand item while recalling.
*/
object FirstPersonRecallBallRenderer {
/** Resting position of the ball in view space (right, up, forward). */
private const val REST_X = 0.45
private const val REST_Y = -0.55
private const val REST_Z = -.6
/** How far below REST_Y the ball starts before rising into view. */
private const val RISE_DISTANCE = 0.6
private const val BALL_SCALE = 0.45F
private val ballContext = RenderContext().also {
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
}
/**
* @return true if a recall ball was rendered (so the caller should cancel the vanilla item).
*/
fun render(poseStack: PoseStack, buffer: MultiBufferSource, packedLight: Int, partialTicks: Float): Boolean {
val player = Minecraft.getInstance().player ?: return false
val anim = RecallArmAnimationTracker.get(player.uuid) ?: return false
val rise = RecallArmAnimationTracker.raiseFactor(player.uuid)
poseStack.pushPose()
poseStack.translate(REST_X, REST_Y - RISE_DISTANCE * (1.0 - rise), REST_Z)
poseStack.scale(BALL_SCALE, -BALL_SCALE, -BALL_SCALE)
poseStack.mulPose(Axis.YP.rotationDegrees(180F))
poseStack.mulPose(Axis.XP.rotationDegrees(15F))
val state = anim.ballState
val ball = anim.ball
val model = VaryingModelRepository.getPoser(ball.name, state)
val texture = VaryingModelRepository.getTexture(ball.name, state)
state.updatePartialTicks(partialTicks)
model.context = ballContext
ballContext.put(RenderContext.ASPECTS, state.aspects)
ballContext.put(RenderContext.POSABLE_STATE, state)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
val consumer = ItemRenderer.getFoilBufferDirect(buffer, RenderType.entityCutout(texture), false, false)
model.render(ballContext, poseStack, consumer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
model.red = 1F
model.green = 1F
model.blue = 1F
model.resetLayerContext()
poseStack.popPose()
return true
}
}
@@ -1,73 +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.render.player
import com.cobblemon.mod.common.client.battle.ClientBallDisplay
import com.cobblemon.mod.common.pokeball.PokeBall
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import net.minecraft.util.Mth
import net.minecraft.world.phys.Vec3
/**
* Tracks which players are recalling a Pokémon so we can pose their arm, draw the Poké Ball in their
* hand, and start the recall beam from it.
*
* Keyed by player UUID. Entries time out on their own once the beam is done, and a new recall resets
* the timer, so nothing has to clear them.
*
* Written by PokemonClientDelegate on recall (beamMode 3); read by HumanoidModelMixin, the ball
* renderers, and PokemonRenderer.
*/
object RecallArmAnimationTracker {
// How long the arm stays raised, in seconds.
const val ARM_HOLD_SECONDS = 0.8
// Fraction of the hold spent easing the arm up at the start and down at the end.
private const val EASE_SECONDS = 0.15
class RecallAnim(
val ball: PokeBall,
val aspects: Set<String>,
val startTimeMs: Long
) {
val ballState = ClientBallDisplay(ball, aspects)
var beamOrigin: Vec3? = null
}
private val activeRecalls = ConcurrentHashMap<UUID, RecallAnim>()
fun start(playerUuid: UUID, ball: PokeBall, aspects: Set<String>) {
activeRecalls[playerUuid] = RecallAnim(ball, aspects, System.currentTimeMillis())
}
/** Returns the active recall for the player, or null if there is none or if it has expired. */
fun get(playerUuid: UUID): RecallAnim? {
val anim = activeRecalls[playerUuid] ?: return null
if (secondsSince(anim) > ARM_HOLD_SECONDS) {
activeRecalls.remove(playerUuid)
return null
}
return anim
}
fun isRecalling(playerUuid: UUID): Boolean = get(playerUuid) != null
private fun secondsSince(anim: RecallAnim) = (System.currentTimeMillis() - anim.startTimeMs) / 1000.0
/** 0..1 ramp: eases in at the start, holds at 1, eases out at the end. 0 when not recalling. */
fun raiseFactor(playerUuid: UUID): Float {
val anim = get(playerUuid) ?: return 0F
val elapsed = secondsSince(anim)
val riseIn = Mth.clamp((elapsed / EASE_SECONDS).toFloat(), 0F, 1F)
val fallOut = Mth.clamp(((ARM_HOLD_SECONDS - elapsed) / EASE_SECONDS).toFloat(), 0F, 1F)
return minOf(riseIn, fallOut)
}
}
@@ -26,7 +26,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PosableP
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker
import com.cobblemon.mod.common.client.render.pokeball.PokeBallPosableState
import com.cobblemon.mod.common.client.render.renderBeaconBeam
import com.cobblemon.mod.common.client.settings.ServerSettings
@@ -61,7 +60,6 @@ import net.minecraft.network.chat.Style
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.*
import kotlin.math.*
@@ -71,9 +69,6 @@ class PokemonRenderer(
) : MobRenderer<PokemonEntity, PosablePokemonEntityModel>(context, PosablePokemonEntityModel(), 0.5f) {
companion object {
val recallBeamColour = Vector4f(1F, 0.1F, 0.1F, 1F)
private const val CAPTURE_BEAM_X_OFFSET = 0.0
private const val CAPTURE_BEAM_Y_OFFSET = 0.2
private const val CAPTURE_BEAM_Z_OFFSET = 0.45
fun ease(x: Double): Double {
return 1 - (1 - x).pow(3)
}
@@ -147,6 +142,8 @@ class PokemonRenderer(
}
modelNow.setLayerContext(buffer, clientDelegate, VaryingModelRepository.getLayers(entity.pokemon.species.resourceIdentifier, clientDelegate))
if (entity.passengers.isNotEmpty()) {
renderRiding(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
} else {
@@ -319,15 +316,8 @@ 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.activeSendoutScale.toDouble(), 0.0)
// If a player is recalling, start the beam from the Poké Ball held in their hand
val recallBallOrigin = (beamTarget as? Player)?.let { RecallArmAnimationTracker.get(it.uuid)?.beamOrigin }
var beamSourcePosition = if (recallBallOrigin != null) {
recallBallOrigin
} else if (beamTarget is EmptyPokeBallEntity) {
(beamTarget.delegate as PokeBallPosableState).locatorStates["beam"]
?.transformPosition(Vec3(CAPTURE_BEAM_X_OFFSET, CAPTURE_BEAM_Y_OFFSET, CAPTURE_BEAM_Z_OFFSET))
?: beamTarget.position()
var beamSourcePosition = if (beamTarget is EmptyPokeBallEntity) {
(beamTarget.delegate as PokeBallPosableState).locatorStates["beam"]?.getOrigin() ?: beamTarget.position()
} else {
if (beamTarget.uuid == Minecraft.getInstance().player?.uuid) {
val lookVec = beamTarget.lookAngle.yRot((PI / 2).toFloat()).multiply(1.0, 0.0, 1.0).normalize()
@@ -64,6 +64,7 @@ class SpawnPokemonPacket(
var rideStamina: Float,
var isAlpha: Boolean,
var silent: Boolean,
var riders: List<Int>,
vanillaSpawnPacket: ClientboundAddEntityPacket,
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
@@ -98,6 +99,7 @@ class SpawnPokemonPacket(
entity.entityData.get(PokemonEntity.RIDE_STAMINA),
entity.entityData.get(PokemonEntity.IS_ALPHA),
entity.isSilent,
entity.occupiedSeats.map { entry -> entry.value.id }.toList(),
vanillaSpawnPacket
)
@@ -134,6 +136,7 @@ class SpawnPokemonPacket(
buffer.writeFloat(rideStamina)
buffer.writeBoolean(isAlpha)
buffer.writeBoolean(silent)
buffer.writeCollection(this.riders) { pb, value -> pb.writeInt(value) }
}
override fun applyData(entity: PokemonEntity, level: ClientLevel) {
@@ -184,6 +187,10 @@ class SpawnPokemonPacket(
entity.tickSpawned = this.tickSpawned
entity.delegate.updateAge(this.tickSpawned)
if (this.riders.isNotEmpty()) {
this.riders.forEach { level.getEntity(it)?.startRiding(entity) }
}
}
override fun checkType(entity: Entity): Boolean = entity is PokemonEntity
@@ -222,6 +229,7 @@ class SpawnPokemonPacket(
val rideStamina = buffer.readFloat()
val isAlpha = buffer.readBoolean()
val silent = buffer.readBoolean()
val riders = buffer.readList { it.readInt() }.toList()
val vanillaPacket = decodeVanillaPacket(buffer)
return SpawnPokemonPacket(
@@ -253,6 +261,7 @@ class SpawnPokemonPacket(
rideStamina,
isAlpha,
silent,
riders,
vanillaPacket
)
}
@@ -0,0 +1,33 @@
/*
* 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.ui
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class SynchronizeActiveTMPacket(val tm: TechnicalMachine?): NetworkPacket<SynchronizeActiveTMPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeNullable(tm?.id) { pb, value -> pb.writeResourceLocation(value) }
}
companion object {
val ID = cobblemonResource("synchronize_active_tm")
fun decode(buffer: RegistryFriendlyByteBuf): SynchronizeActiveTMPacket {
val resourceLocation = buffer.readNullable { it.readResourceLocation() }
if (resourceLocation == null) return SynchronizeActiveTMPacket(null)
val tm = TechnicalMachines.tmMap[resourceLocation]
return SynchronizeActiveTMPacket(tm)
}
}
}
@@ -0,0 +1,34 @@
/*
* 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.properties
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
object HeldItemVisibleProperty : CustomPokemonPropertyType<BooleanProperty> {
override val keys = setOf("held_item_visible")
override val needsKey = true
override fun fromString(value: String?) =
when {
value == null || value.lowercase() in listOf("true", "yes") -> heldItemVisible(true)
value.lowercase() in listOf("false", "no") -> heldItemVisible(false)
else -> null
}
fun heldItemVisible(value: Boolean) = BooleanProperty(
key = keys.first(),
value = value,
pokemonApplicator = { pokemon, _ -> pokemon.heldItemVisible = value },
entityApplicator = { pokemonEntity, _ -> pokemonEntity.pokemon.heldItemVisible = value },
pokemonMatcher = { pokemon, _ -> pokemon.heldItemVisible == value },
entityMatcher = { pokemonEntity, _ -> pokemonEntity.pokemon.heldItemVisible == value }
)
override fun examples() = setOf("yes", "no")
}
@@ -27,7 +27,6 @@
"client.EntryListWidgetMixin",
"client.GameRendererMixin",
"client.HumanoidModelMixin",
"client.ItemInHandLayerMixin",
"client.ItemInHandRendererMixin",
"client.ItemRendererMixin",
"client.LivingEntityRendererMixin",