Compare commits

..
12 Commits
Author SHA1 Message Date
Test c346f84844 speed 2026-02-21 21:17:26 -05:00
Paul d1b8094539 Validate party positions of traded Pokémon upon trading 2026-02-16 11:23:24 +10:00
Paul b8d79af911 Migrate to a more unique tag name for data and config on scriptable entities, closes #1869 2026-02-16 10:57:11 +10:00
Hiroku a7198fd324 Merge branch 'fossil_crash' into 'main'
Adjust fossil creation

Closes #1801

See merge request cable-mc/cobblemon!2150
2026-02-08 01:16:45 +00:00
aeroeng15 bc69ce91f3 Changelog 2026-02-07 16:00:00 -07:00
aeroeng15 1de1d8cdc3 Adjust ordering of party modification 2026-02-07 15:26:36 -07:00
Hiroku 087bf75500 Merge branch 'party-selection-toast' into 'main'
Party Selection Tutorial Toasts

See merge request cable-mc/cobblemon!2148
2026-02-02 05:58:58 +00:00
CeejayandHiroku bac435ba7b Party Selection Tutorial Toasts 2026-02-02 05:58:58 +00:00
Hiroku 74a6106eb6 Merge branch 'snack-stack-fix' into 'main'
Fixes odd stacking behavior with the poke_bait item.

See merge request cable-mc/cobblemon!2143
2026-02-01 03:29:20 +00:00
Neovitalism 5ec993ad3c Update CHANGELOG.md 2026-01-31 21:13:10 -06:00
Paul 73a03d90da Resnapshot 2026-01-31 13:22:11 +10:00
Neovitalism 246d00aa3f Fixes odd stacking behavior with the poke_bait item. 2026-01-27 21:45:05 -06:00
30 changed files with 403 additions and 105 deletions
+18 -13
View File
@@ -1,23 +1,28 @@
# Changelog
## [1.7.3 (January 31st, 2026)](#1-7-3)
## [1.7.4 (MONTH Xth, 2026)](#1-7-4)
### Additions
- 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??!
### New rideable Pokémon
### Pokémon Added
### Model updates for the following Pokémon
### Animation updates for the following Pokémon
### Changes
### Fixes
- Fixed mounted Pokémon not playing sounds.
- Fixed players failing to join servers randomly.
- Fixed some Pokémon having no stamina for flying.
- Fixed `/pctake` not respecting the actual size of the PC.
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
- Fix occasional crash on retrieving revived fossils
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
- Fixed improper validation of Pokémon when performing a trade.
### Developer
- Poké Ball GUI textures now respect the ball's namespace, enabling proper texture loading for custom balls from other mods.
### Molang & Datapacks
- Added a `chance` requirement type for Pokémon interactions.
### Localization
- Updated translations for:
- French
- Canadian French
- Korean
- Portuguese
- Simplified Chinese
- Spanish
+23
View File
@@ -0,0 +1,23 @@
# Changelog
## [1.7.3 (January 31st, 2026)](#1-7-3)
### Fixes
- Fixed mounted Pokémon not playing sounds.
- Fixed players failing to join servers randomly.
- Fixed some Pokémon having no stamina for flying.
- Fixed `/pctake` not respecting the actual size of the PC.
### Developer
- Poké Ball GUI textures now respect the ball's namespace, enabling proper texture loading for custom balls from other mods.
### Molang & Datapacks
- Added a `chance` requirement type for Pokémon interactions.
### Localization
- Updated translations for:
- French
- Canadian French
- Korean
- Portuguese
- Simplified Chinese
- Spanish
@@ -35,6 +35,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts;
import static com.google.common.primitives.Floats.min;
import static net.minecraft.util.Mth.lerp;
@@ -85,11 +86,13 @@ public abstract class MouseHandlerMixin {
CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
PartyTutorialToasts.INSTANCE.onHoldKeyScrollSwitchedSlot();
} else if (i < 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
PartyTutorialToasts.INSTANCE.onHoldKeyScrollSwitchedSlot();
}
}
}
@@ -65,7 +65,11 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.dispenser.DispenserBehaviorRegistry
@@ -76,7 +80,12 @@ import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.*
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.CallbackHandler
import com.cobblemon.mod.common.events.EntityCallbackHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.events.StatHandler
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.permission.LaxPermissionValidator
import com.cobblemon.mod.common.platform.events.PlatformEvents
@@ -90,11 +99,23 @@ import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatu
import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatureProvider
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.*
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.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.requestWallpapers
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -102,6 +123,17 @@ import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.resources.ResourceKey
@@ -114,17 +146,6 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.storage.LevelResource
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
object Cobblemon {
const val MODID = CobblemonBuildDetails.MOD_ID
@@ -451,7 +472,7 @@ object Cobblemon {
server()?.getLevel(dimension)
} else {
val mc = Minecraft.getInstance()
return mc.singleplayerServer?.getLevel(dimension) ?: mc.level
mc.singleplayerServer?.getLevel(dimension) ?: mc.level
}
}
@@ -140,13 +140,7 @@ import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
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
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
@@ -189,6 +183,7 @@ import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRide
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.PartyTutorialPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
@@ -225,6 +220,7 @@ import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandl
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.ServerboundUpdateOrientationHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPacketHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.SetPastureConflictHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
@@ -529,7 +525,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ReleasePCPokemonPacket.ID, ReleasePCPokemonPacket::decode, ReleasePCPokemonHandler))
list.add(PacketRegisterInfo(UnlinkPlayerFromPCPacket.ID, UnlinkPlayerFromPCPacket::decode, UnlinkPlayerFromPCHandler))
list.add(PacketRegisterInfo(RequestRenamePCBoxPacket.Companion.ID, RequestRenamePCBoxPacket.Companion::decode, RequestRenamePCBoxHandler))
list.add(PacketRegisterInfo(RequestRenamePCBoxPacket.ID, RequestRenamePCBoxPacket::decode, RequestRenamePCBoxHandler))
list.add(PacketRegisterInfo(PCBoxWallpapersPacket.ID, PCBoxWallpapersPacket::decode, PCBoxWallpapersHandler))
list.add(PacketRegisterInfo(RequestChangePCBoxWallpaperPacket.ID, RequestChangePCBoxWallpaperPacket::decode, RequestChangePCBoxWallpaperHandler))
list.add(PacketRegisterInfo(MarkPCBoxWallpapersSeenPacket.ID, MarkPCBoxWallpapersSeenPacket::decode, MarkPCBoxWallpapersSeenHandler))
@@ -587,6 +583,9 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PartyPokemonMoveSelectedPacket.ID, PartyPokemonMoveSelectedPacket::decode, PartyPokemonMoveSelectedHandler))
list.add(PacketRegisterInfo(PartyMoveSelectCancelledPacket.ID, PartyMoveSelectCancelledPacket::decode, PartyMoveSelectCancelledHandler))
//Party move tutorial toasts packets
list.add(PacketRegisterInfo(PartyTutorialPacket.ID, PartyTutorialPacket::decode, PartyTutorialPacketHandler))
// Dialogue packets
list.add(PacketRegisterInfo(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler))
list.add(PacketRegisterInfo(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler))
@@ -2815,8 +2815,8 @@ object MoLangFunctions {
}
variable
}
else -> null
} ?: throw IllegalArgumentException("Invalid NBT element type: ${nbt.type}")
else -> VariableStruct()
}
}
}
@@ -28,6 +28,7 @@ data class GeneralPlayerData(
var starterUUID: UUID?,
var keyItems: MutableSet<ResourceLocation>,
var battleTheme: ResourceLocation?,
var partySelectTutorialDone: Boolean = false,
val extraData: MutableMap<String, PlayerDataExtension>,
) : InstancedPlayerData {
var advancementData: PlayerAdvancementData = PlayerAdvancementData()
@@ -53,7 +54,8 @@ data class GeneralPlayerData(
starterSelected,
starterUUID,
advancementData.totalBattleVictoryCount == 0,
battleTheme
battleTheme,
partySelectTutorialDone
)
}
@@ -36,7 +36,8 @@ class PlayerDataJsonBackend: JsonBackedPlayerDataStoreBackend<GeneralPlayerData>
starterUUID = null,
keyItems = Cobblemon.config.defaultKeyItems,
extraData = mutableMapOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
battleTheme = CobblemonSounds.PVP_BATTLE.location,
partySelectTutorialDone = false
)}
override val gson = GsonBuilder()
@@ -37,7 +37,8 @@ class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, col
starterUUID = null,
keyItems = Cobblemon.config.defaultKeyItems,
extraData = mutableMapOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.location
battleTheme = CobblemonSounds.PVP_BATTLE.location,
partySelectTutorialDone = false
)}
override val gson = GsonBuilder()
@@ -34,7 +34,8 @@ data class ClientGeneralPlayerData(
var starterSelected: Boolean = false,
var starterUUID: UUID? = null,
var showChallengeLabel: Boolean = true,
val battleTheme: ResourceLocation? = null
val battleTheme: ResourceLocation? = null,
var partySelectTutorialDone: Boolean = false
) : ClientInstancedPlayerData {
override fun encode(buf: RegistryFriendlyByteBuf) {
@@ -46,6 +47,7 @@ data class ClientGeneralPlayerData(
buf.writeNullable(starterUUID) { pb, value -> pb.writeString(value.toString()) }
buf.writeNullable(resetStarters) { pb, value -> pb.writeBoolean(value) }
buf.writeNullable(battleTheme) {pb, value -> pb.writeIdentifier(value)}
buf.writeBoolean(partySelectTutorialDone)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
@@ -57,6 +59,7 @@ data class ClientGeneralPlayerData(
val starterUUID = buffer.readNullable { it.readString() }?.let { UUID.fromString(it) }
val resetStarterPrompt = buffer.readNullable { it.readBoolean() }
val battleTheme = buffer.readNullable { it.readIdentifier() }
val partySelectTutorialDone = buffer.readBoolean()
val data = ClientGeneralPlayerData(
resetStarterPrompt,
promptStarter,
@@ -64,7 +67,8 @@ data class ClientGeneralPlayerData(
starterSelected,
starterUUID,
showChallengeLabel,
battleTheme
battleTheme,
partySelectTutorialDone
)
//Weird to do this, but since the flag doesn't get passed to the decoded obj, do it here
//Should be fine, as long as decode doesn't get run on the server for some reason
@@ -128,11 +128,11 @@ class FossilMultiblockStructure (
if(pokemon != null) {
pokemon.caughtBall = ballType
player.party().add(pokemon)
this.fossilState.growthState = "Taken"
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, 1.0F, 1.0F)
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, player))
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, pokemon)
player.party().add(pokemon)
}
// Turn the monitor off
@@ -24,9 +24,10 @@ import com.cobblemon.mod.common.block.entity.TintBlockEntity
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
import com.cobblemon.mod.common.client.gui.RideOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
@@ -111,7 +112,7 @@ object CobblemonClient {
val overlay: PartyOverlay by lazy { PartyOverlay() }
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
val rideControlsOverlay: RideOverlay by lazy { RideOverlay() }
fun onLogin() {
clientPlayerData = ClientGeneralPlayerData()
@@ -128,6 +129,8 @@ object CobblemonClient {
battleOverlay.onLogout()
ClientTaskTracker.clear()
checkedStarterScreen = false
overlay.resetAttachedToast()
PartyTutorialToasts.reset()
// CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
@@ -165,13 +168,12 @@ object CobblemonClient {
PlatformEvents.CLIENT_TICK_POST.subscribe { event ->
val player = event.client.player
if (player != null) {
var selectedItem = player.inventory.getItem(player.inventory.selected)
val selectedItem = player.inventory.getItem(player.inventory.selected)
if (pokedexUsageContext.scanningGuiOpen &&
!(selectedItem.`is`(CobblemonItemTags.POKEDEX)) &&
!(player.offhandItem.`is`(CobblemonItemTags.POKEDEX) &&
player.isUsingItem == true &&
player.usedItemHand == InteractionHand.OFF_HAND
)
player.isUsingItem &&
player.usedItemHand == InteractionHand.OFF_HAND)
) {
// Stop using Pokédex in main hand if player switches to a different slot in hotbar
pokedexUsageContext.stopUsing(PokedexUsageContext.OPEN_SCANNER_BUFFER_TICKS + 1)
@@ -216,7 +218,7 @@ object CobblemonClient {
}
fun registerColors() {
this.implementation.registerBlockColors(BlockColor { blockState, view, blockPos, tintIndex ->
this.implementation.registerBlockColors(BlockColor { _, view, blockPos, _ ->
blockPos?.let { pos ->
view?.getBlockEntity(pos)?.let { blockEntity ->
if (blockEntity is TintBlockEntity) return@BlockColor blockEntity.getTint()
@@ -18,10 +18,10 @@ import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.gui.toast.CobblemonToast
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
import com.cobblemon.mod.common.client.keybind.keybinds.*
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
@@ -150,6 +150,9 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
val portraitFrameOffsetY = 2
val selectedSlot = CobblemonClient.storage.selectedSlot
val partySize = party.slots.count { it != null }
PartyTutorialToasts.onOverlayRender(partySize)
party.forEachIndexed { index, pokemon ->
if (pokemon != null) {
val selectedOffsetX = if (selectedSlot == index) 6 else 0
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
@@ -19,6 +21,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinState
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asTranslated
@@ -30,17 +33,28 @@ import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.ChatScreen
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.player.Input
import net.minecraft.client.player.LocalPlayer
import net.minecraft.client.renderer.BiomeColors
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FastColor
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.BlockAndTintGetter
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.round
class RideControlsOverlay : Gui(Minecraft.getInstance()) {
class RideOverlay : Gui(Minecraft.getInstance()) {
companion object {
private const val CONTROLS_FADE_FRAMES = 20F
private const val BASELINE_FPS = 60F
private const val MOTION_FADE_FRAMES = 10
private const val MOTION_FRAMES = 10
private const val MOTION_OVERLAY_MAX_OPACITY = 0.6F
private const val HALF_SCALE = 0.5F
private val mouseResource = cobblemonResource("textures/gui/riding/mouse.png")
@@ -59,6 +73,8 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
private val keyJumpResource = cobblemonResource("textures/gui/riding/key_jump.png")
private val keySneakWideResource = cobblemonResource("textures/gui/riding/key_sneak_wide.png")
private val keyJumpWideResource = cobblemonResource("textures/gui/riding/key_jump_wide.png")
private val motionLinesResource = cobblemonResource("textures/gui/riding/motion_lines_overlay.png")
}
private val screenExemptions: List<Class<out Screen>> = listOf(ChatScreen::class.java)
@@ -67,6 +83,9 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
private var controlsDurationFrames = controlsMaxDurationFrames
private var controlsFadeFrames = CONTROLS_FADE_FRAMES
private var motionFrameIntervals = 0F
private var motionFadeFrames = 0
private var lastMouseX = 0
private var lastMouseY = 0
@@ -81,43 +100,70 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
override fun render(context: GuiGraphics, tickCounter: DeltaTracker) {
val minecraft = Minecraft.getInstance()
// Hiding if a Screen is open and not exempt
minecraft.screen?.let { screen ->
if ((!screenExemptions.contains(screen.javaClass as Class<out Screen>))) return
}
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) return
minecraft.player?.let { player ->
// Hiding if a Screen is open and not exempt
minecraft.screen?.let { screen ->
if ((!screenExemptions.contains(screen.javaClass as Class<out Screen>))) return
}
if (minecraft.options.hideGui || minecraft.debugOverlay.showDebugScreen()) return
val riddenEntity = player.vehicle
if (riddenEntity != null && riddenEntity is PokemonEntity) {
// Only show controls to driver
if (riddenEntity === player.controlledVehicle) {
renderControls(minecraft, context, tickCounter, riddenEntity)
}
val ridingBehaviourSettings = riddenEntity.ridingController?.context?.settings ?: return
val ridingBehaviourState = riddenEntity.ridingController?.context?.state ?: return
setCurrentRideBehaviour(ridingBehaviourSettings, ridingBehaviourState)
val tickDelta = tickCounter.realtimeDeltaTicks.takeIf { !minecraft.isPaused } ?: 0F
val updateInterval = (tickDelta / 20F) * BASELINE_FPS
renderMotionOverlay(minecraft, player, riddenEntity, context, updateInterval, ridingBehaviourSettings, ridingBehaviourState)
renderRideControls(minecraft, context, updateInterval, ridingBehaviourSettings, ridingBehaviourState)
} else {
resetControlsOverlayState()
}
}
}
private fun renderControls(minecraft: Minecraft, context: GuiGraphics, tickCounter: DeltaTracker, riddenEntity: PokemonEntity) {
// Update max duration if user has changed config
val configDuration = Cobblemon.config.displayControlSeconds * BASELINE_FPS
if (controlsMaxDurationFrames != configDuration) {
controlsMaxDurationFrames = configDuration
resetControlsOverlayState()
fun renderMotionOverlay(minecraft: Minecraft, player: LocalPlayer, riddenEntity: Entity, context: GuiGraphics, updateInterval: Float, ridingBehaviourSettings: RidingBehaviourSettings, ridingBehaviourState: RidingBehaviourState) {
if (currentBehaviourKey == JetBehaviour.KEY || currentBehaviourKey == DolphinBehaviour.KEY) {
val isBoosting = if (currentBehaviourKey == JetBehaviour.KEY) (ridingBehaviourState as JetState).boosting.get()
else (ridingBehaviourState as DolphinState).boosting.get()
val interval = round(updateInterval).toInt()
motionFadeFrames = (motionFadeFrames + (interval * (if (isBoosting) 1 else -1))).coerceIn(0, MOTION_FADE_FRAMES)
val opacity = (motionFadeFrames * (1F / MOTION_FADE_FRAMES)).coerceIn(0F, MOTION_OVERLAY_MAX_OPACITY)
val tint = if (player.isUnderWater && currentBehaviourKey == DolphinBehaviour.KEY) BiomeColors.getAverageWaterColor(minecraft.level as BlockAndTintGetter, riddenEntity.blockPosition()) else 0xFFFFFF
motionFrameIntervals += updateInterval * (if (minecraft.options.cameraType.isMirrored) -1 else 1)
val frameIndex = floor(motionFrameIntervals).toInt() % MOTION_FRAMES
blitk(
matrixStack = context.pose(),
texture = motionLinesResource,
x = 0,
y = 0,
textureHeight = 2560,
width = minecraft.window.guiScaledWidth,
height = minecraft.window.guiScaledHeight,
vOffset = 256 * frameIndex,
alpha = opacity,
red = FastColor.ARGB32.red(tint) / 255F,
green = FastColor.ARGB32.green(tint) / 255F,
blue = FastColor.ARGB32.blue(tint) / 255F
)
} else if (motionFadeFrames > 0) {
motionFadeFrames = 0
}
}
// Don't render if duration config set to 0 or less
if (controlsMaxDurationFrames <= 0F) return
private fun setCurrentRideBehaviour(ridingBehaviourSettings: RidingBehaviourSettings, ridingBehaviourState: RidingBehaviourState) {
val rideBehaviourKey = ridingBehaviourSettings.key
val rideBehaviourSettings = riddenEntity.ridingController?.context?.settings ?: return
val ridingBehaviourState = riddenEntity.ridingController?.context?.state ?: return
val rideBehaviourKey = rideBehaviourSettings.key
// Update current ride behaviour if changed
// If composite, get active behaviour instead
// Update current ride behaviour if changed.
// If behaviour is composite, get active behaviour instead
if (rideBehaviourKey == CompositeBehaviour.KEY) {
val activeBehaviour = (ridingBehaviourState as CompositeState).activeBehaviour.get()
if (currentBehaviourKey != activeBehaviour) {
@@ -128,12 +174,21 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
currentBehaviourKey = rideBehaviourKey
resetControlsOverlayState()
}
}
private fun renderRideControls(minecraft: Minecraft, context: GuiGraphics, updateInterval: Float, ridingBehaviourSettings: RidingBehaviourSettings, ridingBehaviourState: RidingBehaviourState) {
// Update max duration if user has changed config
val configDuration = Cobblemon.config.displayControlSeconds * BASELINE_FPS
if (controlsMaxDurationFrames != configDuration) {
controlsMaxDurationFrames = configDuration
resetControlsOverlayState()
}
// Don't render if duration config set to 0 or less
if (controlsMaxDurationFrames <= 0F) return
// Render elements if entity has ride behaviour and if display duration has not ended yet
if (rideBehaviourSettings != null && controlsFadeFrames > 0) {
val tickDelta = tickCounter.realtimeDeltaTicks.takeIf { !minecraft.isPaused } ?: 0F
val updateInterval = (tickDelta / 20F) * BASELINE_FPS
if (ridingBehaviourSettings != null && controlsFadeFrames > 0) {
controlsDurationFrames = max(controlsDurationFrames - updateInterval, 0F)
if (controlsDurationFrames == 0F && controlsFadeFrames > 0) controlsFadeFrames = max(controlsFadeFrames - updateInterval, 0F)
@@ -0,0 +1,99 @@
/*
* 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.party
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.text.darkGray
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.toast.CobblemonToast
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.DownShiftPartyBinding
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
import com.cobblemon.mod.common.client.keybind.keybinds.UpShiftPartyBinding
import com.cobblemon.mod.common.net.messages.server.storage.party.PartyTutorialPacket
import com.cobblemon.mod.common.util.lang
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.components.toasts.Toast
import net.minecraft.client.gui.components.toasts.AdvancementToast
import net.minecraft.network.chat.Component
import net.minecraft.util.Mth
object PartyTutorialToasts {
private var toastAttached = false
val upKey = UpShiftPartyBinding.boundKey()
val downKey = DownShiftPartyBinding.boundKey()
val upDisplay =
if (upKey.type == InputConstants.Type.KEYSYM && upKey.value == InputConstants.KEY_UP)
Component.literal("↑")
else upKey.displayName
val downDisplay =
if (downKey.type == InputConstants.Type.KEYSYM && downKey.value == InputConstants.KEY_DOWN)
Component.literal("↓")
else downKey.displayName
private val toast = CobblemonToast(
Mth.createInsecureUUID(),
CobblemonItems.POKEDEX_RED.defaultInstance,
lang("ui.party.party_select_title").red(),
lang(
"ui.party.party_select_description",
upDisplay,
downDisplay,
PartySendBinding.boundKey().displayName
).darkGray(),
AdvancementToast.BACKGROUND_SPRITE,
-1F,
0
)
fun reset() {
toastAttached = false
}
fun onOverlayRender(partySize: Int) {
if (partySize <= 1) return
if (!CobblemonClient.clientPlayerData.partySelectTutorialDone) {
showToast()
}
}
fun onArrowKeySwitchedSlot() {
if (CobblemonClient.clientPlayerData.partySelectTutorialDone) return
if (CobblemonClient.storage.party.count { it != null } < 2) return
PartyTutorialPacket().sendToServer()
hideToast()
}
fun onHoldKeyScrollSwitchedSlot() {
if (CobblemonClient.clientPlayerData.partySelectTutorialDone) return
if (CobblemonClient.storage.party.count { it != null } < 2) return
PartyTutorialPacket().sendToServer()
hideToast()
}
private fun showToast() {
val minecraft = Minecraft.getInstance()
if (!toastAttached) {
toast.nextVisibility = Toast.Visibility.SHOW
minecraft.toasts.addToast(toast)
toastAttached = true
}
}
private fun hideToast() {
if (toastAttached) toast.nextVisibility = Toast.Visibility.HIDE
}
}
@@ -20,6 +20,8 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import java.util.*
import kotlin.math.min
import net.minecraft.client.gui.Font
import net.minecraft.util.FormattedCharSequence
@Environment(EnvType.CLIENT)
class CobblemonToast(
@@ -38,11 +40,18 @@ class CobblemonToast(
private var lastTime = 0L
internal var nextVisibility: Toast.Visibility = Toast.Visibility.SHOW
fun getLines(font: Font, maxWidth: Int): List<FormattedCharSequence> {
return font.split(this.description, maxWidth)
}
override fun render(context: GuiGraphics, manager: ToastComponent, startTime: Long): Toast.Visibility {
context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height())
val maxWidth = 125
val textRenderer = manager.minecraft.font
context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height() + (getLines(textRenderer, maxWidth).size - 1) * 10)
context.drawString(textRenderer, this.title, 30, 7, this.title.style.color?.value ?: -1, false)
context.drawString(textRenderer, this.description, 30, 18, this.description.style.color?.value ?: -1, false)
context.drawWordWrap(textRenderer, this.description, 30, 18, maxWidth, this.description.style.color?.value ?: -1)
context.renderFakeItem(this.icon, 8, 8)
if (this.hasProgressBar()) {
context.fill(3, 28, 157, 29, -1)
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.keybind.keybinds
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinding
import com.cobblemon.mod.common.client.keybind.KeybindCategories
import com.mojang.blaze3d.platform.InputConstants
@@ -21,5 +22,6 @@ object DownShiftPartyBinding : CobblemonKeyBinding(
) {
override fun onPress() {
CobblemonClient.storage.shiftSelected(true)
PartyTutorialToasts.onArrowKeySwitchedSlot()
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.keybind.keybinds
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinding
import com.cobblemon.mod.common.client.keybind.KeybindCategories
import com.mojang.blaze3d.platform.InputConstants
@@ -21,5 +22,6 @@ object UpShiftPartyBinding : CobblemonKeyBinding(
) {
override fun onPress() {
CobblemonClient.storage.shiftSelected(false)
PartyTutorialToasts.onArrowKeySwitchedSlot()
}
}
@@ -90,8 +90,10 @@ interface MoLangScriptingEntity {
behavioursAreCustom = nbt.getBoolean(DataKeys.SCRIPTED_BEHAVIOURS_ARE_CUSTOM)
behaviours.clear()
behaviours.addAll(nbt.getList(DataKeys.SCRIPTED_BEHAVIOURS, ListTag.TAG_STRING.toInt()).map { ResourceLocation.parse(it.asString) })
data = MoLangFunctions.readMoValueFromNBT(nbt.getCompound(DataKeys.SCRIPTED_DATA)) as VariableStruct
config = if (nbt.contains(DataKeys.SCRIPTED_CONFIG)) MoLangFunctions.readMoValueFromNBT(nbt.getCompound(DataKeys.SCRIPTED_CONFIG)) as VariableStruct else VariableStruct()
val dataKey = if (nbt.contains(DataKeys.SCRIPTED_DATA)) DataKeys.SCRIPTED_DATA else "Data" // migrate old use of "Data"
data = MoLangFunctions.readMoValueFromNBT(nbt.getCompound(dataKey)) as VariableStruct
val configKey = if (nbt.contains(DataKeys.SCRIPTED_CONFIG)) DataKeys.SCRIPTED_CONFIG else "Config" // migrate old use of "Config"
config = if (nbt.contains(configKey)) MoLangFunctions.readMoValueFromNBT(nbt.getCompound(configKey)) as VariableStruct else VariableStruct()
}
fun registerFunctionsForScripting(struct: QueryStruct) {
@@ -31,7 +31,7 @@ class BaitEffectsComponent(
}
override fun equals(other: Any?): Boolean {
return other is BaitEffectsComponent && effects.size == other.effects.size && effects.all { it in other.effects }
return other is BaitEffectsComponent && effects == other.effects
}
override fun hashCode(): Int {
@@ -49,9 +49,7 @@ class FoodColourComponent(
}
override fun equals(other: Any?): Boolean {
return other is FoodColourComponent &&
colours.size == other.colours.size &&
colours.mapIndexed { index, value -> value == other.colours[index] }.all { it }
return other is FoodColourComponent && colours == other.colours // List == List in kotlin checks for element order and index.
}
fun getColoursAsARGB(): List<Int> {
@@ -0,0 +1,24 @@
/*
* 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.server.storage.party
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class PartyTutorialPacket : NetworkPacket<PartyTutorialPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {}
companion object {
val ID = cobblemonResource("party_select_tutorial")
fun decode(buffer: RegistryFriendlyByteBuf): PartyTutorialPacket = PartyTutorialPacket()
}
}
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.serverhandling.storage.party
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.net.messages.server.storage.party.PartyTutorialPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object PartyTutorialPacketHandler : ServerNetworkPacketHandler<PartyTutorialPacket> {
override fun handle(packet: PartyTutorialPacket, server: MinecraftServer, player: ServerPlayer) {
val pd = Cobblemon.playerDataManager.getGenericData(player)
if (!pd.partySelectTutorialDone) {
pd.partySelectTutorialDone = true
Cobblemon.playerDataManager.saveSingle(pd, PlayerInstancedDataStoreTypes.GENERAL)
pd.sendToPlayer(player)
}
}
}
@@ -45,14 +45,15 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
fun performTrade(tradeParticipant: TradeParticipant) {
val offer = getOpposingOffer(tradeParticipant)
if (offer.accepted && getOffer(tradeParticipant).accepted) {
TradeManager.performTrade(
activeTrade = this,
player1 = player1,
player2 = player2,
pokemon1 = player1Offer.pokemon!!,
pokemon2 = player2Offer.pokemon!!
)
completeTrade()
}
}
@@ -98,11 +98,23 @@ object TradeManager : RequestManager<TradeManager.TradeRequest>() {
}
}
fun performTrade(player1: TradeParticipant, pokemon1: Pokemon, player2: TradeParticipant, pokemon2: Pokemon) {
CobblemonEvents.TRADE_EVENT_PRE.postThen(TradeEvent.Pre(player1, pokemon2, player2, pokemon1)) {
val party1 = player1.party
val party2 = player2.party
fun performTrade(
activeTrade: ActiveTrade,
player1: TradeParticipant,
pokemon1: Pokemon,
player2: TradeParticipant,
pokemon2: Pokemon
) {
val party1 = player1.party
val party2 = player2.party
if (pokemon1 !in party1 || pokemon2 !in party2) {
Cobblemon.LOGGER.warn("Attempted to perform a trade with Pokémon that aren't in the party anymore. Could be attempted duping between ${player1.name} and ${player2.name}")
activeTrade.cancelTrade()
return
}
CobblemonEvents.TRADE_EVENT_PRE.postThen(TradeEvent.Pre(player1, pokemon2, player2, pokemon1)) {
party1.remove(pokemon1)
party2.remove(pokemon2)
@@ -120,6 +132,8 @@ object TradeManager : RequestManager<TradeManager.TradeRequest>() {
it.attemptEvolution(pokemon2, pokemon1)
}
activeTrade.completeTrade()
CobblemonEvents.TRADE_EVENT_POST.post(TradeEvent.Post(player1, pokemon2, player2, pokemon1))
}
}
@@ -261,8 +261,8 @@ object DataKeys {
const val SCRIPTED_BEHAVIOURS_ARE_CUSTOM = "BehavioursAreCustom"
const val SCRIPTED_BEHAVIOURS = "Behaviours"
const val SCRIPTED_DATA = "Data"
const val SCRIPTED_CONFIG = "Config"
const val SCRIPTED_DATA = "ScriptingData"
const val SCRIPTED_CONFIG = "ScriptingConfig"
// PokemonProperties
const val ELEMENTAL_TYPE = "ElementalType"
@@ -547,6 +547,8 @@
"cobblemon.trade.request_sent": "Offer'd to trade w'th %1$s",
"cobblemon.trade.offer": "%1$s offer'd to trade crewmates with ye.",
"cobblemon.ui.party": "Crew",
"cobblemon.ui.party.party_select_title": "Pick Yer Party Pal",
"cobblemon.ui.party.party_select_description": "Slam %1$s/%2$s...",
"cobblemon.ui.power": "Damage",
"cobblemon.ui.accuracy": "Shot rate",
"cobblemon.ui.effect": "Bonus",
@@ -824,10 +824,6 @@
"effect.cobblemon.cleanse_negative": "Negative Effect Cleanse",
"effect.cobblemon.mental_restoration": "Mental Restoration",
"effect.cobblemon.cleanse_all": "Effect Cleanse",
"effect.cobblemon.cleanse_negative": "Negative Effect Cleanse",
"effect.cobblemon.mental_restoration": "Mental Restoration",
"cobblemon.fossil.armor": "Armor",
"cobblemon.fossil.bird": "Bird",
"cobblemon.fossil.claw": "Claw",
@@ -1214,6 +1210,8 @@
"cobblemon.trade.error.duplicate": "You already sent a trade request to %1$s!",
"cobblemon.ui.party": "Party",
"cobblemon.ui.party.party_select_title": "Change Party Selection",
"cobblemon.ui.party.party_select_description": "Press %1$s/%2$s or hold %3$s and scroll.",
"cobblemon.ui.power": "Power",
"cobblemon.ui.accuracy": "Accuracy",
"cobblemon.ui.effect": "Effect",
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+2 -2
View File
@@ -13,7 +13,7 @@ parchment_version = 2024.07.28@zip
java_version = 21
mod_id=cobblemon
mod_version=1.7.3
snapshot=false
mod_version=1.7.4
snapshot=true
fabric.loom.multiProjectOptimisation=false