Compare commits

...
Author SHA1 Message Date
Plastered_Crab 86d48df9fe Render Rotom drone camera to start orbiting around + try to avoid collisions using raycast point cloud 2025-02-28 16:59:03 -08:00
Flarkinater 3401ba9cbb Fix drone animation 2025-02-15 21:48:29 -05:00
Flarkinater dac9ccb461 Revert debug change 2025-02-15 21:47:59 -05:00
Flarkinater 191a62f693 Attempt to debug the animation issue 2025-02-15 16:50:52 -05:00
Flarkinater d8d410e539 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into stadium-block 2025-02-15 15:37:37 -05:00
Flarkinater b7c6e1ea77 Attempt to make the drone animated (WIP) 2025-02-15 15:35:48 -05:00
Hiroku 0af4588a14 README change [skip-ci] 2025-02-15 20:26:29 +00:00
Flarkinater 186812d6fd Refactor - move most drone assets into bedrock/drone 2025-02-14 12:23:27 -05:00
Polymeta df9dc8c029 Merge branch 'feat/capture-qol-feature-parity' into 'main'
Critical capture feature parity

See merge request cable-mc/cobblemon!1516
2025-02-14 16:35:16 +00:00
InaandPolymeta 51dc606246 Critical capture feature parity 2025-02-14 16:35:15 +00:00
Polymeta 790f8347f8 Merge branch 'flying_shoulder_mons' into 'main'
Fix flying shoulder mons

See merge request cable-mc/cobblemon!1530
2025-02-13 14:55:01 +00:00
JPAK 09de901b35 Fix flying shoulder mons 2025-02-13 08:14:46 -06:00
Flarkinater 1c0bdc766a Fix upside-down drone 2025-02-12 14:55:41 -05:00
Flarkinater c697bcd2e9 Render the drone in-game 2025-02-12 01:17:42 -05:00
Polymeta dcf321da8f Merge branch 'pokemon-follow-mouse' into 'main'
Add functionality for Pokémon to follow the mouse cursor

Closes #282

See merge request cable-mc/cobblemon!1361
2025-02-11 13:46:48 +00:00
lionandPolymeta c32bd4d014 Add functionality for Pokémon to follow the mouse cursor 2025-02-11 13:46:47 +00:00
Polymeta ab4e2f2540 Merge branch 'test/shoulder_caching' into 'main'
Touch up shoulder rendering cache management

Closes #1367

See merge request cable-mc/cobblemon!1499
2025-02-11 11:37:21 +00:00
aeroeng15andPolymeta 741bfb3316 Touch up shoulder rendering cache management 2025-02-11 11:37:20 +00:00
Flarkinater 9d6ff01d80 Add Rotom drone model/texture/animation placeholders 2025-02-10 20:20:24 -05:00
Flarkinater 1691853a19 Remove placeholder model, we'll just use some "dev art" for now 2025-02-10 20:20:12 -05:00
Flarkinater 4e9a4e1e5a Update deprecated methods, imports, and formatting 2025-02-10 20:20:03 -05:00
Flarkinater 6450255a0b Rebase old stadium camera test branch 2025-02-10 20:19:13 -05:00
Polymeta ce5df762f6 Merge branch 'blacklisted-items-to-hold' into 'main'
Add 'blacklisted_items_to_hold' and 'whitelisted_items_to_hold' tags

Closes #914

See merge request cable-mc/cobblemon!1526
2025-02-10 22:27:35 +00:00
lionandPolymeta c5543cbd64 Add 'blacklisted_items_to_hold' and 'whitelisted_items_to_hold' tags 2025-02-10 22:27:35 +00:00
Polymeta 4929a4a567 Merge branch 'feature/evolution-crashes' into 'main'
small fixes to evolution S2C sync

Closes #1391

See merge request cable-mc/cobblemon!1519
2025-02-10 16:57:22 +00:00
Polymeta e4591825c3 Merge branch 'fix-fling-check-remaps' into 'main'
CobblemonHeldItemManager nameof function changes to account remaps

See merge request cable-mc/cobblemon!1524
2025-02-10 16:55:53 +00:00
Polymeta 6d847e4b7d Merge branch 'feature/packet-checkup' into 'main'
tidying up network packets

See merge request cable-mc/cobblemon!1515
2025-02-09 21:11:10 +00:00
Polymeta ac9d4f49d9 tidying up network packets 2025-02-09 21:11:10 +00:00
Polymeta 26e1145562 Merge branch 'replacing-fossil-anylizer-fix' into 'main'
Fixed Breaking and Replacing Fossil Analyzer crashing the game

See merge request cable-mc/cobblemon!1509
2025-02-09 20:21:10 +00:00
BuildTools 8417be3b89 Switched to using safecast instead 2025-02-09 11:43:18 +02:00
BuildTools 53a7282c31 Fix for HeldItemManager not accounting for remaps and such causing fling battlelog to not display correct name 2025-02-09 11:23:08 +02:00
Polymeta 6349f12d8e Merge branch 'feature/pc-additions' into 'main'
PC Search, Custom Box Names, Wallpapers, & Sorting

See merge request cable-mc/cobblemon!1304
2025-02-09 00:30:03 +00:00
JustAHuman-xDandPolymeta 052b01ff00 PC Search, Custom Box Names, Wallpapers, & Sorting 2025-02-09 00:30:03 +00:00
Polymeta 36f4e4fe1b Merge branch '1431-item-rarity-implementation' into 'main'
Updated rarities for various items. Wasn't able to update the Automaton Armor...

Closes #1431

See merge request cable-mc/cobblemon!1520
2025-02-08 23:50:29 +00:00
JoshandPolymeta 06fe7568a5 Updated rarities for various items. Wasn't able to update the Automaton Armor... 2025-02-08 23:50:28 +00:00
Paul dc03f26b20 Changelog [skip-ci] 2025-02-08 18:05:49 +10:00
Paul b30ad68975 Added /changejointscale for debugging joint scales (a surprise tool we'll use later) 2025-02-08 18:02:55 +10:00
Paul cc1f40a165 Added modelpart transformations to root posable models that stack with pose modelpart transformations 2025-02-07 09:41:40 +10:00
Polymeta c1ce5491f1 Ensure EVBerries make sound on consumption as intended
Closes #1390

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-06 21:15:32 +00:00
Polymeta 588a5d4ce1 Ensure EV berries actually reduce the stat instead of raising it
Closes #1434

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-05 21:13:47 +00:00
Polymeta 58bd15b3b2 Merge branch '1403-creative-tab-lang-changes' into 'main'
Added 'Cobblemon: ' to creative tabs for clarity.  Closes #1403

Closes #1403

See merge request cable-mc/cobblemon!1518
2025-02-05 00:23:44 +00:00
JoshandPolymeta 88769ec1f2 Added 'Cobblemon: ' to creative tabs for clarity. Closes #1403 2025-02-05 00:23:44 +00:00
Hiroku aa790d0511 Merge branch 'shared-model-repository' into 'main'
Refactored VaryingModelRepository to simplify and support NPCs using Pokémon models and anims etc

See merge request cable-mc/cobblemon!1507
2025-02-04 21:02:00 +00:00
Polymeta bd0b1bca3d pass HA prio along to ensure it's not being incorrectly set as forced
Closes #1401

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-04 15:00:23 +00:00
Paul 2fa0204a20 Remove redundant comments 2025-02-04 20:34:21 +10:00
Polymeta d2d50a4c71 more consumption of held item back into its original place
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-04 00:44:14 +00:00
Polymeta e07a5ca019 fix evolutions both crashing clients and being sometimes forgotten by pokemon after passing requirements initially
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-04 00:20:37 +00:00
Polymeta 7bcd398438 whops missed a file to fix pipeline
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-03 14:29:02 +00:00
Polymeta 830d30e0de fix pipeline build
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-02-03 14:24:05 +00:00
Hiroku 21e279224d Merge branch 'fix/modern-item-conditions' into 'main'
Remove legacy NbtItemPredicate, replace with vanilla ItemCondition

See merge request cable-mc/cobblemon!1513
2025-02-03 03:25:45 +00:00
Polymeta 9b2d032c6a Merge branch 'config-gui' into 'main'
Add config screen

Closes #112

See merge request cable-mc/cobblemon!1508
2025-02-02 18:56:18 +00:00
lionandPolymeta be76f8a180 Add config screen 2025-02-02 18:56:18 +00:00
Ina 6b6d9d9ecf All my homies hate fandom 2025-02-01 22:07:12 +00:00
Hiroku 2af0324ee5 Merge branch 'feat/ev-gained-event' into 'main'
Add EvGainedEvent

See merge request cable-mc/cobblemon!1514
2025-02-01 21:41:37 +00:00
Ina 8aa4f57a33 Add EvGainedEvent 2025-02-01 16:40:08 +00:00
Ina 5f01e1a5d8 Remove legacy NbtItemPredicate, replace with vanilla ItemCondition 2025-02-01 15:47:44 +00:00
BuildTools c43e6aba4a Fixed Breaking and Replacing Fossil Analyzer crashing the game in a complete multi-structure 2025-01-31 19:27:55 +02:00
Hiroku 3599dc97cc Merge branch 'reload-cobblemon-config-command' into 'main'
Add '/cobblemonconfig reload' command

Closes #132

See merge request cable-mc/cobblemon!1391
2025-01-31 10:58:08 +00:00
lionandHiroku 8a5d895f03 Add '/cobblemonconfig reload' command 2025-01-31 10:58:08 +00:00
Paul 26ef75fba7 Teeny cleanup things 2025-01-31 14:06:36 +10:00
Paul dabc0ae859 Conflicto destructo 2025-01-31 14:01:22 +10:00
Paul 6184bd850e Merge branch 'main' into shared-model-repository
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/PokemonGuiUtils.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/npc/widgets/NPCRenderWidget.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/layer/PokemonOnShoulderRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/PokemonModelRepository.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/VaryingModelRepository.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/npc/NPCRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
2025-01-31 11:43:47 +10:00
Paul 01d2bcdfcf Squashed all of the VaryingModelRepositories down into one, allowing NPCs to use Pokemon models etc 2024-09-28 17:25:52 +10:00
Paul 8d5e2f5773 Remove dead code from fossil model repo 2024-09-28 10:34:01 +10:00
Paul 73029800e4 Significantly cleaned handling of living entity renderer offsets 2024-09-28 10:33:12 +10:00
256 changed files with 5552 additions and 1681 deletions
+16
View File
@@ -0,0 +1,16 @@
### Additions
- Added ability to rename PC Boxes by clicking on the name of a box
- Added ability to set wallpapers per PC Box by clicking the button right to the box name
- Added sorting by Name, Level and type by clicking the button left of the box name (shift reverses order)
- Added filtering/search functionality in PC UI, which also supports pokemon properties (e.g. shiny=yes shows all shinies)
- Added /changewallpaper <player> <boxNumber> <wallpaper> command to change a wallpaper through commands
- Added /renamebox <player> <boxNumber> <name> command to rename a PC box through commands
### Developer
- Added RenamePCBoxEvent.Pre/Post events to prevent players from renaming a box or change their input
- Added ChangePCBoxWallpaperEvent.Pre/Post to prevent players from changing to a wallpaper or change their selection
- Added WallpaperCollectionEvent which gets called when clients connect to a server, allowing the server which of the clients found wallpapers it's allowed to move (collected wallpapers can be removed for example to make it "vanish" client-side)
- Wallpapers are loaded from `assets/<namespace>/textures/gui/pc/wallpaper/` and all wallpapers in this folder are available to the client to choose by default
- Renamed `SetPCBoxPokemonPacket` and the respective handler to `SetPCBoxPacket`
+10
View File
@@ -0,0 +1,10 @@
### Additions
- Added support for NPCs to use Pokémon models and vice versa. This will require changes in addons that add fossil types due to naming conflicts between fossils and the Pokémon species.
- Added `/changejointscale` command to change the scale of a joint in a model. Unbelievably funny.
### Developer
- Removed all VaryingModelRepository subclasses into the parent class.
### Addons
- Added `transformedParts` to the root of poser JSONs so it now exists in both poses and the model overall.
+30
View File
@@ -1,14 +1,44 @@
# Changelog
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
- Pokémon now follow the mouse cursor on the Summary screen, with an option to disable this in the settings.
### Changes
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
- Fixed PCs always opening at box 2 instead of box 1.
- Fixed not being able to do complex item requirements aside from just NBT with evolution conditions, requirements and fossil items.
- Fixed the usage `hiddenability` in `pokegive` or other spawn commands resulting in a forced hidden ability
- Fixed the consumption of friendship berries (or EV berries) not making any noise
- Fixed Instantly breaking and replacing a fossil analyzer with any block entity crashing the game in a complete multi-structure
- Fixed players getting disconnected when sidemods update a Pokémon's teratype
- Fixed fling not using Item Names for minecraft held items that substitute Pokémon items
- Fixed evolutions sometimes preventing players from logging in to servers
- Fixed rendering of shoulder-mounted Pokémon desyncing between clients
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
- Dialogues are correctly removed from memory when they are stopped.
- Dialogues with variably-set initial pages now properly start timeout tracking.
- Added `EvGainedEvent.Pre` and `EvGainedEvent.Post`.
- Deprecated `EVs#add(Stat, Int)`, you will not be able to compile against this code please migrate to the `EVs#add(Stat, Int, EvSource)` you most likely want to use `SidemodEvSource` but please check other implementations or make your own.
- Removed the NbtItemPredicate class, all the mod usages now use the vanilla item predicate solution, this causes breaking changes on Fossil, HeldItemRequirement & ItemInteractionEvolution
- Renamed Cobblemon's creative tabs to start with "Cobblemon: " to distinguish Cobblemon's tabs from tabs for other mods.
- Various items now have a rarity value.
### Changes
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
### MoLang & Datapacks
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
- The `requiredContext` for an item interaction evolution
- The `itemCondition` for a held item evolution requirement
- The `fossils` for a fossil entry
## [1.6.1 (January 26th, 2025)](#1-6-1)
+1 -1
View File
@@ -1,6 +1,6 @@
# Cobblemon
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and Forge.
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and NeoForge.
## Getting started
+1 -1
View File
@@ -41,7 +41,7 @@ dependencies {
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
@@ -62,11 +62,7 @@ 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.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.*
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.client.CobblemonPack
@@ -89,22 +85,11 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
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.HiddenAbilityPropertyType
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.properties.*
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
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.server
import com.cobblemon.mod.common.util.*
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
@@ -112,25 +97,27 @@ 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 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
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.item.NameTagItem
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 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
@@ -164,6 +151,7 @@ object Cobblemon {
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
var statProvider: StatProvider = CobblemonStatProvider
var seasonResolver: SeasonResolver = TagSeasonResolver
var wallpapers = mapOf<UUID, List<ResourceLocation>>()
@JvmStatic
val builtinPacks = listOf<CobblemonPack>(
@@ -220,7 +208,8 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel, this.config.maxPokemonLevel, this.config.maxPokemonFriendship, this.config.maxDynamaxLevel).sendToPlayer(it)
it.requestWallpapers()
sendServerSettingsPacketToPlayer(it)
}
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
PCLinkManager.removeLink(it.player.uuid)
@@ -268,8 +257,17 @@ object Cobblemon {
}
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
}
fun sendServerSettingsPacketToPlayer(player: ServerPlayer) {
ServerSettingsPacket(
this.config.preventCompletePartyDeposit,
this.config.displayEntityLevelLabel,
this.config.displayEntityNameLabel,
this.config.maxPokemonLevel,
this.config.maxPokemonFriendship,
this.config.maxDynamaxLevel,
).sendToPlayer(player)
}
fun initialize() {
@@ -429,7 +427,23 @@ object Cobblemon {
}
}
private fun initializeConfig() {
loadCobblemonConfig()
saveConfig(this.config)
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadConfig() {
initializeConfig()
bestSpawner.init()
}
fun reloadConfig() {
initializeConfig()
bestSpawner.reloadConfig()
}
private fun loadCobblemonConfig() {
val configFile = File(CONFIG_PATH)
configFile.parentFile.mkdirs()
@@ -472,12 +486,6 @@ object Cobblemon {
} else {
this.config = CobblemonConfig()
}
config.lastSavedVersion = VERSION
this.saveConfig()
bestSpawner.loadConfig()
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadStarterConfig(): StarterConfig {
@@ -500,12 +508,14 @@ object Cobblemon {
}
}
fun saveConfig() {
fun saveConfig(config: CobblemonConfig) {
config.lastSavedVersion = VERSION
try {
val configFile = File(CONFIG_PATH)
val fileWriter = FileWriter(configFile)
// Put the config to json then flush the writer to commence writing.
CobblemonConfig.GSON.toJson(this.config, fileWriter)
CobblemonConfig.GSON.toJson(config, fileWriter)
fileWriter.flush()
fileWriter.close()
} catch (exception: Exception) {
@@ -527,4 +537,4 @@ object Cobblemon {
this.implementation.registerCommandArgument(cobblemonResource("npc_class"), NPCClassArgumentType::class, SingletonArgumentInfo.contextFree(NPCClassArgumentType::npcClass))
}
}
}
@@ -36,6 +36,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.of(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val STADIUM: BlockEntityType<StadiumBlockEntity> = this.create("stadium_terminal", BlockEntityType.Builder.of(::StadiumBlockEntity, CobblemonBlocks.STADIUM).build(null))
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@@ -354,6 +354,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
@JvmField
val STADIUM = create("stadium_terminal", StadiumBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F)))
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
@@ -23,6 +23,7 @@ object CobblemonCommands {
ChangeEyeHeight.register(dispatcher)
ChangeScaleAndSize.register(dispatcher)
ChangeWalkSpeed.register(dispatcher)
ChangeJointScaleCommand.register(dispatcher)
TestCommand.register(dispatcher)
ReloadShowdownCommand.register(dispatcher)
ClickTextCommand.register(dispatcher)
@@ -56,7 +57,10 @@ object CobblemonCommands {
AbandonMultiTeam.register(dispatcher)
FreezePokemonCommand.register(dispatcher)
ApplyPlayerTextureCommand.register(dispatcher)
RenameBoxCommand.register(dispatcher)
ChangeBoxWallpaperCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -35,7 +35,6 @@ import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component
@@ -269,10 +268,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val MONITOR = blockItem("monitor", CobblemonBlocks.MONITOR)
@JvmField
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE)
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE, Rarity.UNCOMMON)
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val STADIUM = blockItem("stadium_terminal", CobblemonBlocks.STADIUM)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@@ -398,7 +399,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
// Medicine
@JvmField
val RARE_CANDY = candyItem("rare_candy") { _, pokemon -> pokemon.getExperienceToNextLevel() }
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
@@ -883,48 +884,48 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
// Archaeology
@JvmField
val ARMOR_FOSSIL = noSettingsItem("armor_fossil")
val ARMOR_FOSSIL = itemWithRarity("armor_fossil", Rarity.UNCOMMON)
@JvmField
val FOSSILIZED_BIRD = noSettingsItem("fossilized_bird")
val FOSSILIZED_BIRD = itemWithRarity("fossilized_bird", Rarity.UNCOMMON)
@JvmField
val CLAW_FOSSIL = noSettingsItem("claw_fossil")
val CLAW_FOSSIL = itemWithRarity("claw_fossil", Rarity.UNCOMMON)
@JvmField
val COVER_FOSSIL = noSettingsItem("cover_fossil")
val COVER_FOSSIL = itemWithRarity("cover_fossil", Rarity.UNCOMMON)
@JvmField
val FOSSILIZED_DINO = noSettingsItem("fossilized_dino")
val FOSSILIZED_DINO = itemWithRarity("fossilized_dino", Rarity.UNCOMMON)
@JvmField
val DOME_FOSSIL = noSettingsItem("dome_fossil")
val DOME_FOSSIL = itemWithRarity("dome_fossil", Rarity.UNCOMMON)
@JvmField
val FOSSILIZED_DRAKE = noSettingsItem("fossilized_drake")
val FOSSILIZED_DRAKE = itemWithRarity("fossilized_drake", Rarity.UNCOMMON)
@JvmField
val FOSSILIZED_FISH = noSettingsItem("fossilized_fish")
val FOSSILIZED_FISH = itemWithRarity("fossilized_fish", Rarity.UNCOMMON)
@JvmField
val HELIX_FOSSIL = noSettingsItem("helix_fossil")
val HELIX_FOSSIL = itemWithRarity("helix_fossil", Rarity.UNCOMMON)
@JvmField
val JAW_FOSSIL = noSettingsItem("jaw_fossil")
val JAW_FOSSIL = itemWithRarity("jaw_fossil", Rarity.UNCOMMON)
@JvmField
val OLD_AMBER_FOSSIL = noSettingsItem("old_amber_fossil")
val OLD_AMBER_FOSSIL = itemWithRarity("old_amber_fossil", Rarity.UNCOMMON)
@JvmField
val PLUME_FOSSIL = noSettingsItem("plume_fossil")
val PLUME_FOSSIL = itemWithRarity("plume_fossil", Rarity.UNCOMMON)
@JvmField
val ROOT_FOSSIL = noSettingsItem("root_fossil")
val ROOT_FOSSIL = itemWithRarity("root_fossil", Rarity.UNCOMMON)
@JvmField
val SAIL_FOSSIL = noSettingsItem("sail_fossil")
val SAIL_FOSSIL = itemWithRarity("sail_fossil", Rarity.UNCOMMON)
@JvmField
val SKULL_FOSSIL = noSettingsItem("skull_fossil")
val SKULL_FOSSIL = itemWithRarity("skull_fossil", Rarity.UNCOMMON)
@JvmField
val BYGONE_SHERD = noSettingsItem("bygone_sherd")
val BYGONE_SHERD = itemWithRarity("bygone_sherd", Rarity.UNCOMMON)
@JvmField
val CAPTURE_SHERD = noSettingsItem("capture_sherd")
val CAPTURE_SHERD = itemWithRarity("capture_sherd",Rarity.UNCOMMON)
@JvmField
val DOME_SHERD = noSettingsItem("dome_sherd")
val DOME_SHERD = itemWithRarity("dome_sherd", Rarity.UNCOMMON)
@JvmField
val HELIX_SHERD = noSettingsItem("helix_sherd")
val HELIX_SHERD = itemWithRarity("helix_sherd", Rarity.UNCOMMON)
@JvmField
val NOSTALGIC_SHERD = noSettingsItem("nostalgic_sherd")
val NOSTALGIC_SHERD = itemWithRarity("nostalgic_sherd",Rarity.UNCOMMON)
@JvmField
val SUSPICIOUS_SHERD = noSettingsItem("suspicious_sherd")
val SUSPICIOUS_SHERD = itemWithRarity("suspicious_sherd", Rarity.UNCOMMON)
@JvmField
val TUMBLESTONE = this.create("tumblestone", TumblestoneItem(Item.Properties(), CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE))
@@ -1144,11 +1145,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val DREAM_ROD = pokerodItem(cobblemonResource("dream_rod"))
@JvmField
val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"))
val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"), Rarity.RARE)
@JvmField
val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"))
val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"), Rarity.EPIC)
@JvmField
val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"))
val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"), Rarity.EPIC)
@JvmField
val ANCIENT_POKE_ROD = pokerodItem(cobblemonResource("ancient_poke_rod"))
@JvmField
@@ -1180,7 +1181,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val ANCIENT_GIGATON_ROD = pokerodItem(cobblemonResource("ancient_gigaton_rod"))
@JvmField
val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"))
val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"), Rarity.EPIC)
// Misc
@JvmField
@@ -1232,10 +1233,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Properties()))
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Properties()))
private fun itemWithRarity(name: String, rarity: Rarity): CobblemonItem = this.create(name, CobblemonItem(Item.Properties().rarity(rarity)))
fun berries() = this.berries.toMap()
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
@@ -1247,10 +1250,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
return item
}
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(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): PokerodItem {
val settings = Item.Properties().stacksTo(1).durability(256)
private fun pokerodItem(pokeRodId: ResourceLocation, rarity: Rarity = Rarity.COMMON): PokerodItem {
val settings = Item.Properties().stacksTo(1).durability(256).rarity(rarity)
val item = create(pokeRodId.path, PokerodItem(pokeRodId, settings))
pokeRods.add(item)
return item
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
@@ -44,12 +45,7 @@ import com.cobblemon.mod.common.client.net.storage.party.InitializePartyHandler
import com.cobblemon.mod.common.client.net.storage.party.MoveClientPartyPokemonHandler
import com.cobblemon.mod.common.client.net.storage.party.SetPartyPokemonHandler
import com.cobblemon.mod.common.client.net.storage.party.SetPartyReferenceHandler
import com.cobblemon.mod.common.client.net.storage.pc.ClosePCHandler
import com.cobblemon.mod.common.client.net.storage.pc.InitializePCHandler
import com.cobblemon.mod.common.client.net.storage.pc.MoveClientPCPokemonHandler
import com.cobblemon.mod.common.client.net.storage.pc.OpenPCHandler
import com.cobblemon.mod.common.client.net.storage.pc.SetPCBoxPokemonHandler
import com.cobblemon.mod.common.client.net.storage.pc.SetPCPokemonHandler
import com.cobblemon.mod.common.client.net.storage.pc.*
import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
import com.cobblemon.mod.common.client.net.trade.*
import com.cobblemon.mod.common.net.PacketRegisterInfo
@@ -66,9 +62,6 @@ import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacke
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.fishing.FishingBaitRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
@@ -80,6 +73,7 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
@@ -94,6 +88,9 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.MoveClientPart
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.net.messages.client.trade.*
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
@@ -127,7 +124,11 @@ import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokem
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.PCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestChangePCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestRenamePCBoxPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SortPCBoxPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
import com.cobblemon.mod.common.net.messages.server.trade.*
@@ -160,12 +161,7 @@ import com.cobblemon.mod.common.net.serverhandling.storage.SwapPCPartyPokemonHan
import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePCPokemonToPartyHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonToPCHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
import com.cobblemon.mod.common.net.serverhandling.trade.*
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -221,6 +217,9 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(FormUpdatePacket.ID, FormUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(TeraTypeUpdatePacket.ID, TeraTypeUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(DmaxLevelUpdatePacket.ID, DmaxLevelUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(GmaxFactorUpdatePacket.ID, GmaxFactorUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -235,13 +234,18 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SetPartyReferencePacket.ID, SetPartyReferencePacket::decode, SetPartyReferenceHandler))
list.add(PacketRegisterInfo(InitializePCPacket.ID, InitializePCPacket::decode, InitializePCHandler))
list.add(PacketRegisterInfo(MoveClientPCPokemonPacket.ID, MoveClientPCPokemonPacket::decode, MoveClientPCPokemonHandler))
list.add(PacketRegisterInfo(SetPCBoxPokemonPacket.ID, SetPCBoxPokemonPacket::decode, SetPCBoxPokemonHandler))
list.add(PacketRegisterInfo(SetPCBoxPacket.ID, SetPCBoxPacket::decode, SetPCBoxHandler))
list.add(PacketRegisterInfo(SetPCPokemonPacket.ID, SetPCPokemonPacket::decode, SetPCPokemonHandler))
list.add(PacketRegisterInfo(OpenPCPacket.ID, OpenPCPacket::decode, OpenPCHandler))
list.add(PacketRegisterInfo(ClosePCPacket.ID, ClosePCPacket::decode, ClosePCHandler))
list.add(PacketRegisterInfo(SwapClientPokemonPacket.ID, SwapClientPokemonPacket::decode, SwapClientPokemonHandler))
list.add(PacketRegisterInfo(RemoveClientPokemonPacket.ID, RemoveClientPokemonPacket::decode, RemoveClientPokemonHandler))
list.add(PacketRegisterInfo(RenamePCBoxPacket.ID, RenamePCBoxPacket::decode, RenamePCBoxHandler))
list.add(PacketRegisterInfo(RequestPCBoxWallpapersPacket.ID, RequestPCBoxWallpapersPacket::decode, RequestPCBoxWallpapersHandler))
list.add(PacketRegisterInfo(SetPCBoxWallpapersPacket.ID, SetPCBoxWallpapersPacket::decode, SetPCBoxWallpapersHandler))
list.add(PacketRegisterInfo(ChangePCBoxWallpaperPacket.ID, ChangePCBoxWallpaperPacket::decode, ChangePCBoxWallpaperHandler))
// UI Packets
list.add(PacketRegisterInfo(SummaryUIPacket.ID, SummaryUIPacket::decode, SummaryUIPacketHandler))
list.add(PacketRegisterInfo(InteractPokemonUIPacket.ID, InteractPokemonUIPacket::decode, InteractPokemonUIPacketHandler))
@@ -279,11 +283,13 @@ object CobblemonNetwork {
// MultiBattleTeam Packets
list.add(PacketRegisterInfo(TeamRequestNotificationPacket.ID, TeamRequestNotificationPacket::decode, TeamRequestNotificationHandler))
list.add(PacketRegisterInfo(TeamRequestExpiredPacket.ID, TeamRequestExpiredPacket::decode, TeamRequestExpiredHandler))
list.add(PacketRegisterInfo(TeamMemberAddNotificationPacket.ID, TeamMemberAddNotificationPacket::decode, TeamMemberAddNotificationHandler))
list.add(PacketRegisterInfo(TeamMemberRemoveNotificationPacket.ID, TeamMemberRemoveNotificationPacket::decode, TeamMemberRemoveNotificationHandler))
list.add(PacketRegisterInfo(TeamJoinNotificationPacket.ID, TeamJoinNotificationPacket::decode, TeamJoinNotificationHandler))
// Settings packets
list.add(PacketRegisterInfo(ServerSettingsPacket.ID, ServerSettingsPacket::decode, ServerSettingsPacketHandler))
list.add(PacketRegisterInfo(OpenCobblemonConfigScreenPacket.ID, OpenCobblemonConfigScreenPacket::decode, OpenCobblemonConfigEditorHandler))
// Data registries
list.add(PacketRegisterInfo(AbilityRegistrySyncPacket.ID, AbilityRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -387,6 +393,10 @@ 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(PCBoxWallpapersPacket.ID, PCBoxWallpapersPacket::decode, PCBoxWallpapersHandler))
list.add(PacketRegisterInfo(RequestChangePCBoxWallpaperPacket.ID, RequestChangePCBoxWallpaperPacket::decode, RequestChangePCBoxWallpaperHandler))
// Starter packets
list.add(PacketRegisterInfo(SelectStarterPacket.ID, SelectStarterPacket::decode, SelectStarterPacketHandler))
list.add(PacketRegisterInfo(RequestStarterScreenPacket.ID, RequestStarterScreenPacket::decode, RequestStarterScreenHandler))
@@ -399,6 +409,8 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SwapPCPartyPokemonPacket.ID, SwapPCPartyPokemonPacket::decode, SwapPCPartyPokemonHandler))
list.add(PacketRegisterInfo(SortPCBoxPacket.ID, SortPCBoxPacket::decode, SortPCBoxHandler))
// Battle packets
list.add(PacketRegisterInfo(BattleSelectActionsPacket.ID, BattleSelectActionsPacket::decode, BattleSelectActionsHandler))
list.add(PacketRegisterInfo(SpectateBattlePacket.ID, SpectateBattlePacket::decode, SpectateBattleHandler))
@@ -43,7 +43,8 @@ interface SleepDepth {
"comatose" to comatose,
"normal" to normal
)
val adapter = StringIdentifiedObjectAdapter { depths[it] ?: throw IllegalArgumentException("Unknown sleep depth: $it") }
val adapter = StringIdentifiedObjectAdapter({ depths[it] ?: throw IllegalArgumentException("Unknown sleep depth: $it") })
}
fun canSleep(pokemonEntity: PokemonEntity): Boolean
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.pokemon.stats.BattleEvSource
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
@@ -272,7 +273,7 @@ open class PokemonBattle(
opponent.awardExperience(opponentPokemon, experience)
}
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
pokemon.evs.add(stat, amount)
pokemon.evs.add(stat, amount, BattleEvSource(this, opponentPokemon.facedOpponents.toList(), pokemon))
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.serialization.StringIdentifiedObjectAdapter
import com.google.gson.annotations.SerializedName
/**
* The way an item will be dropped.
@@ -28,6 +29,9 @@ enum class ItemDropMethod(val methodName: String) {
TO_INVENTORY("to-inventory");
companion object {
val adapter = StringIdentifiedObjectAdapter { str -> values().firstOrNull { it.methodName == str } }
val adapter = StringIdentifiedObjectAdapter(
{ str -> ItemDropMethod.entries.firstOrNull { it.methodName == str } },
{ value -> (value ?: ON_ENTITY).methodName },
)
}
}
@@ -42,7 +42,10 @@ import com.cobblemon.mod.common.api.events.pokemon.healing.PokemonHealedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ChangePCBoxWallpaperEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.api.events.storage.RenamePCBoxEvent
import com.cobblemon.mod.common.api.events.storage.WallpaperCollectionEvent
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
import com.cobblemon.mod.common.api.reactive.CancelableObservable
import com.cobblemon.mod.common.api.reactive.EventObservable
@@ -158,11 +161,29 @@ object CobblemonEvents {
@JvmField
val EXPERIENCE_CANDY_USE_POST = EventObservable<ExperienceCandyUseEvent.Post>()
@JvmField
val EV_GAINED_EVENT_PRE = CancelableObservable<EvGainedEvent.Pre>()
@JvmField
val EV_GAINED_EVENT_POST = EventObservable<EvGainedEvent.Post>()
@JvmField
val POKEMON_RELEASED_EVENT_PRE = CancelableObservable<ReleasePokemonEvent.Pre>()
@JvmField
val POKEMON_RELEASED_EVENT_POST = EventObservable<ReleasePokemonEvent.Post>()
@JvmField
val RENAME_PC_BOX_EVENT_PRE = CancelableObservable<RenamePCBoxEvent.Pre>()
@JvmField
val RENAME_PC_BOX_EVENT_POST = EventObservable<RenamePCBoxEvent.Post>()
@JvmField
val WALLPAPER_COLLECTION_EVENT = EventObservable<WallpaperCollectionEvent>()
@JvmField
val CHANGE_PC_BOX_WALLPAPER_EVENT_PRE = CancelableObservable<ChangePCBoxWallpaperEvent.Pre>()
@JvmField
val CHANGE_PC_BOX_WALLPAPER_EVENT_POST = EventObservable<ChangePCBoxWallpaperEvent.Post>()
@JvmField
val LOOT_DROPPED = CancelableObservable<LootDroppedEvent>()
@JvmField
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.pokemon.stats.EvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Fired during EV mutation.
*
* @see [Pre]
* @see [Post]
*/
interface EvGainedEvent {
/**
* The [Stat] having EVs added to itself.
*/
val stat: Stat
/**
* @see EvSource.pokemon
*/
val pokemon: Pokemon get() = this.source.pokemon
/**
* The [EvSource] that fired this event.
*/
val source: EvSource
/**
* Fired before EV mutation occurs, cancelling will cause the mutation to be flagged as not successful (returns 0).
*
* @property stat See [EvGainedEvent.stat].
* @property amount The amount of EVs that will be gained.
* @property source See [EvGainedEvent.source].
*/
class Pre(override val stat: Stat, var amount: Int, override val source: EvSource) : EvGainedEvent, Cancelable()
/**
* Fired after EV mutation occurs, this is purely for notification purposes.
*
* @property stat See [EvGainedEvent.stat].
* @property amount The final amount of EVs gained.
* @property source See [EvGainedEvent.source].
*/
class Post(override val stat: Stat, val amount: Int, override val source: EvSource) : EvGainedEvent
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.storage
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.storage.pc.PCBox
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
/**
* Events fired whenever a player changes their PC Wallpaper.
* Has a cancelable [Pre] event and a [Post] which gets fired after the change.
*
* @author JustAHuman-xD
* @since February 14th, 2025
*/
interface ChangePCBoxWallpaperEvent {
/**
* The [ServerPlayer] who is changing their wallpaper
*/
val player: ServerPlayer
/**
* The [PCBox] whose wallpaper is being changed
*/
val box: PCBox
/**
* The location of the wallpaper that is being changed to. Can be modified in the [Pre] event.
* NOTE: Changing this to a wallpaper that does not exist on the client, will result in a purple/black fallback texture being displayed.
*/
val wallpaper: ResourceLocation
class Pre(
override val player: ServerPlayer,
override val box: PCBox,
override var wallpaper: ResourceLocation
) : ChangePCBoxWallpaperEvent, Cancelable()
class Post(
override val player: ServerPlayer,
override val box: PCBox,
override val wallpaper: ResourceLocation
) : ChangePCBoxWallpaperEvent
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.storage
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.storage.pc.PCBox
import net.minecraft.server.level.ServerPlayer
/**
* Events fired whenever a player renames one of their PC boxes.
* Has a cancelable [Pre] event and a [Post] which gets fired after the change.
*
* @author JustAHuman-xD
* @since February 14th, 2025
*/
interface RenamePCBoxEvent {
/**
* The [ServerPlayer] who is changing their wallpaper
*/
val player: ServerPlayer
/**
* The [PCBox] whose wallpaper is being changed
*/
val box: PCBox
/**
* The new box name being used. This can be modified in the [Pre] event.
*/
val name: String
class Pre(
override val player: ServerPlayer,
override val box: PCBox,
override var name: String
) : RenamePCBoxEvent, Cancelable()
class Post(
override val player: ServerPlayer,
override val box: PCBox,
override val name: String
) : RenamePCBoxEvent
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.storage
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
/**
* Event being fired whenever the client connects to a server and "requests" what wallpapers are available for it to choose from.
* Mods can listen to this event and remove available options from the client when they connect, essentially causing them to not exist client-side
*
* NOTE: Adding wallpapers here that do not exist on the client, will result in the purple/black fallback texture being displayed client-side.
*
* @author JustAHuman-xD
* @since February 14th, 2025
*/
data class WallpaperCollectionEvent(
val player: ServerPlayer,
val wallpapers: MutableList<ResourceLocation>
)
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.api.fishing
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fishing.FishingBaitRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.FishingBaitRegistrySyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.api.fossil
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
@@ -19,7 +19,7 @@ import net.minecraft.world.item.ItemStack
class Fossil(
identifier: ResourceLocation,
val result: PokemonProperties,
val fossils: List<NbtItemPredicate>
val fossils: List<ItemPredicate>
): StringRepresentable {
@Transient
@@ -13,15 +13,15 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.net.messages.client.data.FossilRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
@@ -40,7 +40,7 @@ object Fossils: JsonDataRegistry<Fossil> {
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.create()
override val typeToken: TypeToken<Fossil> = TypeToken.get(Fossil::class.java)
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.api.fossil
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.NaturalMaterialRegistrySyncPacket
import com.cobblemon.mod.common.registry.ItemTagCondition
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
@@ -12,12 +12,10 @@ import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
import com.cobblemon.mod.common.client.render.SpriteType
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
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.entity.PoseType
import com.cobblemon.mod.common.util.toHex
import com.mojang.authlib.minecraft.client.MinecraftClient
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.platform.Lighting
import com.mojang.blaze3d.systems.RenderSystem
@@ -209,7 +207,6 @@ fun drawPosablePortrait(
contextScale: Float = 1F,
reversed: Boolean = false,
state: PosableState,
repository: VaryingModelRepository<*>,
partialTicks: Float,
limbSwing: Float = 0F,
limbSwingAmount: Float = 0F,
@@ -227,16 +224,16 @@ fun drawPosablePortrait(
matrixStack.scale(scale, scale, -scale)
matrixStack.translate(0.0, -PORTRAIT_DIAMETER / 18.0, 0.0)
val sprite = repository.getSprite(identifier, state, SpriteType.PORTRAIT);
val sprite = VaryingModelRepository.getSprite(identifier, state, SpriteType.PORTRAIT);
if (sprite == null) {
val model = repository.getPoser(identifier, state)
val model = VaryingModelRepository.getPoser(identifier, state)
state.currentModel = model
val texture = repository.getTexture(identifier, state)
val texture = VaryingModelRepository.getTexture(identifier, state)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(identifier, state).let { context.put(RenderContext.TEXTURE, it) }
VaryingModelRepository.getTextureNoSubstitute(identifier, state).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, contextScale)
context.put(RenderContext.SPECIES, identifier)
context.put(RenderContext.ASPECTS, state.currentAspects)
@@ -255,7 +252,7 @@ fun drawPosablePortrait(
matrixStack.translate(
model.portraitTranslation.x * if (reversed) -1F else 1F,
model.portraitTranslation.y,
model.portraitTranslation.y + 1.5 * model.portraitScale,
model.portraitTranslation.z - 4
)
matrixStack.scale(model.portraitScale, model.portraitScale, 1 / model.portraitScale)
@@ -272,7 +269,7 @@ fun drawPosablePortrait(
val packedLight = LightTexture.pack(11, 7)
val colour = toHex(r, g, b, a)
model.withLayerContext(immediate, state, repository.getLayers(identifier, state)) {
model.withLayerContext(immediate, state, VaryingModelRepository.getLayers(identifier, state)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, colour)
immediate.endBatch()
}
@@ -288,7 +285,6 @@ fun drawPosablePortrait(
}
fun drawProfile(
repository: VaryingModelRepository<*>,
resourceIdentifier: ResourceLocation,
matrixStack: PoseStack,
state: PosableState,
@@ -298,16 +294,16 @@ fun drawProfile(
RenderSystem.applyModelViewMatrix()
matrixStack.scale(scale, scale, -scale)
val sprite = repository.getSprite(resourceIdentifier, state, SpriteType.PROFILE)
val sprite = VaryingModelRepository.getSprite(resourceIdentifier, state, SpriteType.PROFILE)
if (sprite == null) {
val model = repository.getPoser(resourceIdentifier, state)
val texture = repository.getTexture(resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(resourceIdentifier, state)
val texture = VaryingModelRepository.getTexture(resourceIdentifier, state)
val context = RenderContext()
model.context = context
repository.getTextureNoSubstitute(resourceIdentifier, state).let { context.put(RenderContext.TEXTURE, it) }
VaryingModelRepository.getTextureNoSubstitute(resourceIdentifier, state).let { context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, 1F)
context.put(RenderContext.SPECIES, resourceIdentifier)
context.put(RenderContext.ASPECTS, state.currentAspects)
@@ -319,7 +315,11 @@ fun drawProfile(
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
state.updatePartialTicks(partialTicks)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
matrixStack.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
matrixStack.translate(
model.profileTranslation.x,
model.profileTranslation.y + 1.5 * model.profileScale,
model.profileTranslation.z - 4.0
)
matrixStack.scale(model.profileScale, model.profileScale, 1 / model.profileScale)
// matrixStack.multiply(rotation)
val quaternion1 = Axis.YP.rotationDegrees(-32F * if (false) -1F else 1F)
@@ -337,7 +337,7 @@ fun drawProfile(
RenderSystem.setShaderLights(light1, light2)
val packedLight = LightTexture.pack(11, 7)
model.withLayerContext(bufferSource, state, repository.getLayers(resourceIdentifier, state)) {
model.withLayerContext(bufferSource, state, VaryingModelRepository.getLayers(resourceIdentifier, state)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
bufferSource.endBatch()
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.permission
import com.cobblemon.mod.common.command.AbandonMultiTeam
object CobblemonPermissions {
private const val COMMAND_PREFIX = "command."
@@ -18,6 +16,7 @@ object CobblemonPermissions {
val CHANGE_EYE_HEIGHT = this.create("${COMMAND_PREFIX}changeeyeheight", PermissionLevel.ALL_COMMANDS)
val CHANGE_SCALE_AND_SIZE = this.create("${COMMAND_PREFIX}changescaleandsize", PermissionLevel.ALL_COMMANDS)
val CHANGE_WALK_SPEED = this.create("${COMMAND_PREFIX}changewalkspeed", PermissionLevel.ALL_COMMANDS)
val CHANGE_JOINT_SCALE = this.create("${COMMAND_PREFIX}changejointscale", PermissionLevel.ALL_COMMANDS)
val CHECKSPAWNS = this.create("${COMMAND_PREFIX}checkspawns", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val GET_NBT = this.create("${COMMAND_PREFIX}getnbt", PermissionLevel.ALL_COMMANDS)
@@ -64,6 +63,9 @@ object CobblemonPermissions {
val POKEBOX = this.create("${COMMAND_PREFIX}pokebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val RENAMEBOX = this.create("${COMMAND_PREFIX}renamebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CHANGE_WALLPAPER = this.create("${COMMAND_PREFIX}changewallpaper", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val TEST_STORE = this.create("${COMMAND_PREFIX}teststore", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val QUERY_LEARNSET = this.create("${COMMAND_PREFIX}querylearnset", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -84,6 +86,8 @@ object CobblemonPermissions {
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val COBBLEMON_CONFIG_RELOAD = this.create("${COMMAND_PREFIX}cobblemonconfig.reload", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokeball.catching.calculators
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.pokedex
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
/**
* An influencer on capture results based on Pokedex data.
*/
interface PokedexStatusCaptureInfluencer {
/**
* Attempts to influence the [context] of a capture.
*
* @param thrower The [LivingEntity] that threw the Pokeball
* @param target The [PokemonEntity] attempting to capture.
* @param context The calculated [CaptureContext].
* @return The final [CaptureContext].
*/
fun influence(thrower: LivingEntity, target: PokemonEntity, context: CaptureContext): CaptureContext {
if (!context.isSuccessfulCapture) {
return context
}
val player = thrower as? ServerPlayer ?: return context
val pokedex = player.pokedex()
val speciesId = target.pokemon.species.resourceIdentifier
val formName = target.pokemon.form.name
val hasCaughtStatus = pokedex.getOrCreateSpeciesRecord(speciesId)
.getOrCreateFormRecord(formName)
.knowledge == PokedexEntryProgress.CAUGHT
// In modern games when the catch is a success and the Pokémon has already been registered to the Pokédex
// the catch becomes a critical capture and only requires one shake
return if (hasCaughtStatus) {
CaptureContext(
numberOfShakes = 1,
isSuccessfulCapture = true,
isCriticalCapture = true
)
} else context
}
}
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
enum class PokemonSortMode(val comparator: Comparator<Pokemon?>, val reverseComparator: Comparator<Pokemon?>) {
NAME( { it?.getDisplayName()?.string } ),
LEVEL( { it?.level } ),
TYPE( { it?.primaryType?.name } );
constructor(mapper: (Pokemon?) -> Comparable<*>?) : this(compareBy({ it == null }, mapper),
compareBy<Pokemon?> { it == null }.thenComparing(compareBy(mapper).reversed()))
fun comparator(descending: Boolean) = if (descending) reverseComparator else comparator
}
@@ -42,8 +42,7 @@ import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
@@ -51,6 +50,7 @@ import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
@@ -99,7 +99,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
@@ -105,6 +105,9 @@ interface Evolution : EvolutionLike {
* @param pokemon The [Pokemon] being evolved.
*/
fun evolve(pokemon: Pokemon): Boolean {
if (this.consumeHeldItem) {
pokemon.swapHeldItem(ItemStack.EMPTY)
}
if (this.optional) {
// All the networking is handled under the hood, see EvolutionController.
return pokemon.evolutionProxy.server().add(this)
@@ -220,10 +223,6 @@ interface Evolution : EvolutionLike {
this.shed(pokemon)
if (this.consumeHeldItem) {
pokemon.swapHeldItem(ItemStack.EMPTY)
}
val ownerPlayer = pokemon.getOwnerPlayer()
if (ownerPlayer != null && ownerPlayer.level().gameRules.getBoolean(CobblemonGameRules.DO_POKEMON_LOOT)) {
drops.drop(
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.stats
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.item.ItemStack
interface EvSource {
/**
* The [Pokemon] being affected.
*/
val pokemon: Pokemon
/**
* Utility function that checks if the source is an implementation of [BattleEvSource].
*/
fun isBattle() = this is BattleEvSource
/**
* Utility function that checks if the source is an implementation of [ItemEvSource].
*/
fun isInteraction() = this is ItemEvSource
/**
* Utility function that checks if the source is an implementation of [SidemodEvSource].
*/
fun isSidemod() = this is SidemodEvSource
}
/**
* Triggered by sidemods.
*
* @property sidemodId The mod ID of the sidemod triggering this source.
* @property pokemon See [EvSource.pokemon].
*/
open class SidemodEvSource(
val sidemodId: String,
override val pokemon: Pokemon
) : EvSource
/**
* An Ev source fired when using Ev mutating items.
*
* @property player The [ServerPlayer] using the item.
* @property stack The [ItemStack] being consumed.
* @property pokemon See [EvSource.pokemon].
*/
open class ItemEvSource(
val player: ServerPlayer,
val stack: ItemStack,
override val pokemon: Pokemon
) : EvSource
/**
* An Ev source fired in battles.
*
* @property battle The associated [PokemonBattle].
* @property facedPokemon The [BattlePokemon]s that the [pokemon] faced.
* @property pokemon See [EvSource.pokemon], comes from the original [BattlePokemon.effectedPokemon].
*/
open class BattleEvSource(
val battle: PokemonBattle,
val facedPokemon: List<BattlePokemon>,
override val pokemon: Pokemon
) : EvSource
@@ -45,4 +45,15 @@ open class CancelableObservable<T : Cancelable> : EventObservable<T>() {
}
}
}
inline fun postThenFinally(event: T, ifCanceled: (T) -> Unit = {}, finally: (T) -> Unit = {},ifSucceeded: (T) -> Unit) {
post(event) {
if (it.isCanceled) {
ifCanceled(it)
} else {
ifSucceeded(it)
}
finally(it)
}
}
}
@@ -11,6 +11,9 @@ package com.cobblemon.mod.common.api.serialization
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -19,6 +22,13 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 18th, 2022
*/
class StringIdentifiedObjectAdapter<T>(val fromString: (String) -> T) : JsonDeserializer<T> {
class StringIdentifiedObjectAdapter<T>(
val fromString: (String) -> T,
val toString: ((T) -> String)? = null,
) : JsonDeserializer<T>, JsonSerializer<T> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext) = fromString(json.asString)
override fun serialize(src: T, type: Type?, ctx: JsonSerializationContext?): JsonElement? {
val stringValue = toString?.invoke(src) ?: throw IllegalStateException("No toString function provided for serialization.")
return JsonPrimitive(stringValue)
}
}
@@ -83,8 +83,9 @@ object BestSpawner {
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
fun loadConfig() {
fun init() {
LOGGER.info("Starting the Best Spawner...")
SpawningCondition.register(BasicSpawningCondition.NAME, BasicSpawningCondition::class.java)
SpawningCondition.register(AreaSpawningCondition.NAME, AreaSpawningCondition::class.java)
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
@@ -94,6 +95,7 @@ object BestSpawner {
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
SpawningContextCalculator.register(SeafloorSpawningContextCalculator)
SpawningContextCalculator.register(LavafloorSpawningContextCalculator)
@@ -113,14 +115,23 @@ object BestSpawner {
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
config = BestSpawnerConfig.load()
loadConfig()
SpawnDetailPresets.registerPresetType(BasicSpawnDetailPreset.NAME, BasicSpawnDetailPreset::class.java)
SpawnDetailPresets.registerPresetType(PokemonSpawnDetailPreset.NAME, PokemonSpawnDetailPreset::class.java)
}
fun loadConfig() {
config = BestSpawnerConfig.load()
}
fun reloadConfig() {
loadConfig()
spawnerManagers.forEach(SpawnerManager::onConfigReload)
}
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
}
}
}
@@ -96,4 +96,4 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
this.presets = data.toMutableMap()
Cobblemon.LOGGER.info("Loaded ${presets.size} spawn detail presets.")
}
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
import com.cobblemon.mod.common.util.server
@@ -58,6 +59,12 @@ open class SpawnerManager {
}
}
open fun onConfigReload() {
spawners.filterIsInstance<PlayerSpawner>().forEach {
it.ticksBetweenSpawns = Cobblemon.config.ticksBetweenSpawnAttempts
}
}
open fun onServerStarted() {
spawners.clear()
}
@@ -161,4 +161,4 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
return true
}
}
}
@@ -221,4 +221,4 @@ abstract class SpawningContext {
}
return false
}
}
}
@@ -64,4 +64,4 @@ open class SpawningContextInput(
val cause: SpawnCause,
/** The [Level] the spawning context exists in. */
val world: ServerLevel
)
)
@@ -103,4 +103,4 @@ abstract class SpawnDetail : ModDependant {
}
abstract fun doSpawn(ctx: SpawningContext): SpawnAction<*>
}
}
@@ -167,10 +167,10 @@ open class PlayerPartyStore(
}
// Shoulder validation code
if (player.shoulderEntityLeft.isPokemonEntity() && !validateShoulder(player.shoulderEntityLeft, true)) {
if (player.shoulderEntityLeft.isPokemonEntity() && !validateShoulder(player, true)) {
player.respawnEntityOnShoulder(player.shoulderEntityLeft)
}
if (player.shoulderEntityRight.isPokemonEntity() && !validateShoulder(player.shoulderEntityRight, false)) {
if (player.shoulderEntityRight.isPokemonEntity() && !validateShoulder(player, false)) {
player.respawnEntityOnShoulder(player.shoulderEntityRight)
}
@@ -182,11 +182,14 @@ open class PlayerPartyStore(
}
}
fun validateShoulder(shoulderEntity: CompoundTag, isLeft: Boolean): Boolean {
private fun validateShoulder(player: ServerPlayer, isLeft: Boolean): Boolean {
val shoulderEntity = if(isLeft) player.shoulderEntityLeft else player.shoulderEntityRight
val pokemon = find { it.uuid == shoulderEntity.getCompound("Pokemon").getUUID(DataKeys.POKEMON_UUID) }
if (pokemon == null || (pokemon.state as? ShoulderedState)?.isLeftShoulder != isLeft) {
// No longer valid if (in order): not in party, not the correct shoulder, no longer shoulder mountable
if (pokemon == null || (pokemon.state as? ShoulderedState)?.isLeftShoulder != isLeft || !pokemon.form.shoulderMountable) {
return false
}
player.updateShoulderNbt(pokemon)
return true
}
@@ -8,16 +8,19 @@
package com.cobblemon.mod.common.api.storage.pc
import com.cobblemon.mod.common.api.pokemon.PokemonSortMode
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.storage.StoreCoordinates
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.JsonObject
import net.minecraft.core.RegistryAccess
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
/**
@@ -34,6 +37,18 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
protected var emit = true
var name : String? = null
set(value) {
field = value
if (emit) boxChangeEmitter.emit(Unit)
}
var wallpaper : ResourceLocation = cobblemonResource("textures/gui/pc/pc_screen_overlay.png")
set(value) {
field = value
if (emit) boxChangeEmitter.emit(Unit)
}
protected val pokemon = Array<Pokemon?>(POKEMON_PER_BOX) { null }
open operator fun get(index: Int): Pokemon? {
@@ -88,6 +103,13 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
boxChangeEmitter.subscribe { pc.pcChangeObservable.emit(Unit) }
}
fun sort(sortMode: PokemonSortMode, descending: Boolean) {
pokemon.sortWith(sortMode.comparator(descending))
pokemon.forEachIndexed { slot, pokemon ->
pokemon?.storeCoordinates?.set(StoreCoordinates(pc, PCPosition(boxNumber, slot)))
}
}
fun trackPokemon(pokemon: Pokemon) {
pokemon.getChangeObservable()
.pipe(
@@ -100,10 +122,14 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
}
fun sendTo(player: ServerPlayer) {
SetPCBoxPokemonPacket(this).sendToPlayer(player)
SetPCBoxPacket(this).sendToPlayer(player)
}
open fun saveToNBT(nbt: CompoundTag, registryAccess: RegistryAccess): CompoundTag {
name?.let {
nbt.putString(DataKeys.STORE_BOX_NAME, it)
}
nbt.putString(DataKeys.STORE_BOX_WALLPAPER, wallpaper.toString())
for (slot in 0 until POKEMON_PER_BOX) {
val pokemon = pokemon[slot] ?: continue
nbt.put(DataKeys.STORE_SLOT + slot, pokemon.saveToNBT(registryAccess))
@@ -112,6 +138,10 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
}
open fun saveToJSON(json: JsonObject, registryAccess: RegistryAccess): JsonObject {
name?.let {
json.addProperty(DataKeys.STORE_BOX_NAME, it)
}
json.addProperty(DataKeys.STORE_BOX_WALLPAPER, wallpaper.toString())
for (slot in 0 until POKEMON_PER_BOX) {
val pokemon = pokemon[slot] ?: continue
json.add(DataKeys.STORE_SLOT + slot, pokemon.saveToJSON(registryAccess))
@@ -119,7 +149,16 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
return json
}
open fun loadFromJSON(json: JsonObject, registryAccess: RegistryAccess): PCBox {
if (json.has(DataKeys.STORE_BOX_NAME)) {
name = json.getAsJsonPrimitive(DataKeys.STORE_BOX_NAME).asString
}
if (json.has(DataKeys.STORE_BOX_WALLPAPER)) {
wallpaper = ResourceLocation.parse(json.getAsJsonPrimitive(DataKeys.STORE_BOX_WALLPAPER).asString)
}
for (slot in 0 until POKEMON_PER_BOX) {
if (json.has(DataKeys.STORE_SLOT + slot)) {
val pokemonJson = json.getAsJsonObject(DataKeys.STORE_SLOT + slot)
@@ -134,6 +173,14 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
}
open fun loadFromNBT(nbt: CompoundTag, registryAccess: RegistryAccess): PCBox {
if (nbt.contains(DataKeys.STORE_BOX_NAME)) {
name = nbt.getString(DataKeys.STORE_BOX_NAME)
}
if (nbt.contains(DataKeys.STORE_BOX_WALLPAPER)) {
wallpaper = ResourceLocation.parse(nbt.getString(DataKeys.STORE_BOX_WALLPAPER))
}
for (slot in 0 until POKEMON_PER_BOX) {
if (nbt.contains(DataKeys.STORE_SLOT + slot)) {
val pokemonNBT = nbt.getCompound(DataKeys.STORE_SLOT + slot)
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.pc.search
import com.cobblemon.mod.common.pokemon.Pokemon
/**
* Functional interface used in [Search]es
*/
fun interface PokemonFilter {
fun test(pokemon: Pokemon): Boolean
fun inverted(): PokemonFilter {
return PokemonFilter { pokemon -> !this.test(pokemon) }
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.storage.pc.search
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.HashSet
import java.util.Locale
import java.util.UUID
/**
* Client-side class containing currently applied filters, and cached results of which Pokémon pass or fail those filters.
* Used in [com.cobblemon.mod.common.client.gui.pc.PCGUI]
*/
class Search(
private val filters: Set<PokemonFilter>,
private val passed: MutableSet<UUID> = HashSet(),
private val failed: MutableSet<UUID> = HashSet()
) {
companion object {
val DEFAULT = Search(setOf())
fun of(search: String): Search {
if (search.isBlank()) return DEFAULT
val split = search.lowercase(Locale.ROOT).trim().split(" ")
val filters = HashSet<PokemonFilter>()
for (piece in split) {
var filter = piece
val inverted = filter.startsWith("!")
if (inverted) {
filter = filter.substring(1)
}
val pokemonFilter: PokemonFilter = when (filter) {
"holding" -> PokemonFilter { pokemon -> !pokemon.heldItem().isEmpty }
"fainted" -> PokemonFilter { pokemon -> pokemon.isFainted() }
"legendary" -> PokemonFilter { pokemon -> pokemon.isLegendary() }
"mythical" -> PokemonFilter { pokemon -> pokemon.isMythical() }
"ultrabeast", "ultra_beast" -> PokemonFilter { pokemon -> pokemon.isUltraBeast() }
else -> PokemonFilter { pokemon -> PokemonProperties.parse(filter).matches(pokemon) }
}
if (inverted) {
filters.add(pokemonFilter.inverted())
} else {
filters.add(pokemonFilter)
}
}
return Search(filters)
}
}
fun passes(pokemon: Pokemon?): Boolean {
if (pokemon == null) return false
val uuid = pokemon.uuid
if (passed.contains(uuid)) return true
if (failed.contains(uuid)) return false
val passes = filters.all { it.test(pokemon) }
val set = if (passes) passed else failed
set.add(uuid)
return passes
}
}
@@ -111,6 +111,9 @@ object CobblemonItemTags {
@JvmField val EXPERIENCE_SHARE = create("held/experience_share")
@JvmField val IS_FRIENDSHIP_BOOSTER = create("is_friendship_booster")
@JvmField val ANY_HELD_ITEM = create("held/is_held_item")
@JvmField val BLACKLISTED_ITEMS_TO_HOLD = create("held/blacklisted_items_to_hold")
@JvmField val WHITELISTED_ITEMS_TO_HOLD = create("held/whitelisted_items_to_hold")
/** Tag that flags items as being able to "create" [CobblemonItems.LEFTOVERS]. */
@JvmField val LEAVES_LEFTOVERS = create("held/leaves_leftovers")
@JvmField val LUCKY_EGG = create("held/lucky_egg")
@@ -0,0 +1,113 @@
/*
* 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.block
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.phys.BlockHitResult
class StadiumBlock(properties: Properties): BaseEntityBlock(properties) {
companion object {
val CODEC = simpleCodec(::StadiumBlock)
}
init {
registerDefaultState(stateDefinition.any().setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH))
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return StadiumBlockEntity(pos, state)
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState {
return this.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, blockPlaceContext.horizontalDirection)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HorizontalDirectionalBlock.FACING)
}
override fun rotate(blockState: BlockState, rotation: Rotation): BlockState {
return blockState.setValue(
HorizontalDirectionalBlock.FACING, rotation.rotate(blockState.getValue(
HorizontalDirectionalBlock.FACING)))
}
override fun mirror(blockState: BlockState, mirror: Mirror): BlockState {
return blockState.rotate(mirror.getRotation(blockState.getValue(HorizontalDirectionalBlock.FACING)))
}
override fun onRemove(state: BlockState, world: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.`is`(newState.block)) super.onRemove(state, world, pos, newState, moved)
}
//TODO: override getTicker
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun useWithoutItem(
blockState: BlockState,
world: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
if (player !is ServerPlayer) return InteractionResult.SUCCESS
val blockEntity = world.getBlockEntity(blockPos)
if (blockEntity !is StadiumBlockEntity) return InteractionResult.SUCCESS
// Toggle isInUse state
blockEntity.isInUse = !blockEntity.isInUse
// debug
val activateMessage = if (blockEntity.isInUse) "Stadium Activated!" else "Stadium Deactivated!"
player.sendSystemMessage(text(activateMessage).red())
if (blockEntity.isInUse) {
// Generate flight path when activated
blockEntity.generateFlightPath(world, radiusX = 5.0, radiusZ = 3.0, height = 10.0, steps = 20)
} else {
// Clear path when deactivated
blockEntity.pathPoints.clear()
}
val message = "Stadium Pos: " + blockEntity.blockPos +
"\nPlayer 1 Pos: " + blockEntity.getPlayer1Pos() +
"\nPoke 1 Pos: " + blockEntity.getPoke1Pos() +
"\nPlayer 2 Pos: " + blockEntity.getPlayer2Pos() +
"\nPoke 2 Pos: " + blockEntity.getPoke2Pos()
player.sendSystemMessage(text(message).red())
blockEntity.setChanged()
world.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
return InteractionResult.SUCCESS
}
}
@@ -42,7 +42,7 @@ open class FossilMultiblockEntity(
if(masterBlockPos != null && masterBlockPos != blockPos) {
val chunkPos = ChunkPos(masterBlockPos)
if (level?.chunkSource?.hasChunk(chunkPos.x, chunkPos.z) == true) {
val entity: FossilMultiblockEntity? = level?.getBlockEntity(masterBlockPos) as FossilMultiblockEntity?
val entity: FossilMultiblockEntity? = level?.getBlockEntity(masterBlockPos) as? FossilMultiblockEntity?
field = entity?.multiblockStructure
}
}
@@ -308,9 +308,12 @@ class HealingMachineBlockEntity(
} else {
// Recharging
if (blockEntity.healingCharge < blockEntity.maxCharge) {
val chargePerTick = (Cobblemon.config.chargeGainedPerTick).coerceAtLeast(0F)
val secondsToChargeHealingMachine = (Cobblemon.config.secondsToChargeHealingMachine).coerceAtLeast(0.0)
val totalTicks = secondsToChargeHealingMachine * 20
val chargePerTick = if (totalTicks > 0) blockEntity.maxCharge / totalTicks else 0.0
blockEntity.healingCharge =
(blockEntity.healingCharge + chargePerTick).coerceIn(0F..blockEntity.maxCharge)
(blockEntity.healingCharge + chargePerTick.toFloat()).coerceIn(0F..blockEntity.maxCharge)
blockEntity.updateBlockChargeLevel()
blockEntity.updateRedstoneSignal()
blockEntity.markUpdated()
@@ -0,0 +1,132 @@
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneState
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.Vec3
class StadiumBlockEntity(
pos: BlockPos,
blockState: BlockState
): BlockEntity(
CobblemonBlockEntities.STADIUM,
pos,
blockState
) {
var isInUse = false
val droneState = DroneState()
val pathPoints: MutableList<Vec3> = mutableListOf()
fun generateFlightPath(world: Level, radiusX: Double, radiusZ: Double, height: Double, steps: Int) {
pathPoints.clear()
for (i in 0 until steps) {
val angle = (i.toDouble() / steps) * Math.PI * 2
val xOffset = radiusX * Math.cos(angle)
val zOffset = radiusZ * Math.sin(angle)
val basePos = Vec3(blockPos.x + xOffset, blockPos.y + height, blockPos.z + zOffset)
val adjustedPos = getValidPoint(world, basePos)
pathPoints.add(adjustedPos)
}
}
private fun getValidPoint(world: Level, start: Vec3): Vec3 {
val maxHeight = 40 // Max height for drone path
val stepHeight = 1.0 // Step size for raycast
var adjustedHeight = start.y
for (h in 0..maxHeight step stepHeight.toInt()) {
val checkPos = BlockPos(start.x.toInt(), start.y.toInt() - h, start.z.toInt()) // Convert to BlockPos
if (!world.getBlockState(checkPos).isAir) { // Check if block is solid
return Vec3(start.x, start.y - h - 1, start.z) // Adjust position
}
}
return start // No obstruction found
}
fun getPlayer1Pos(): BlockPos {
return blockPos.north(10)
}
fun getPlayer2Pos(): BlockPos {
return blockPos.south(10)
}
fun getPoke1Pos(): BlockPos {
return blockPos.north(5)
}
fun getPoke2Pos(): BlockPos {
return blockPos.south(5)
}
/**
* Saves data to NBT.
*/
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
// Save the isInUse state
tag.putBoolean("IsInUse", isInUse)
// Save path points
val pathTag = ListTag()
for (point in pathPoints) {
val pointTag = CompoundTag()
pointTag.putDouble("x", point.x)
pointTag.putDouble("y", point.y)
pointTag.putDouble("z", point.z)
pathTag.add(pointTag)
}
tag.put("PathPoints", pathTag)
}
/**
* Loads data from NBT.
*/
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
// Load the isInUse state
isInUse = tag.getBoolean("IsInUse")
// Load path points
pathPoints.clear()
val pathTag = tag.getList("PathPoints", 10) // 10 is the ID for CompoundTag
for (i in 0 until pathTag.size) {
val pointTag = pathTag.getCompound(i)
val point = Vec3(pointTag.getDouble("x"), pointTag.getDouble("y"), pointTag.getDouble("z"))
pathPoints.add(point)
}
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
//println("getUpdatePacket called for NestBlockEntity at $blockPos with egg: $eggComponent")
return ClientboundBlockEntityDataPacket.create(this)
}
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
val tag = saveWithoutMetadata(registryLookup)
//println("getUpdateTag called for NestBlockEntity at $blockPos with tag: $tag")
return tag
}
}
@@ -25,13 +25,7 @@ import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
@@ -40,13 +34,8 @@ import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
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
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
@@ -77,10 +66,12 @@ import net.minecraft.client.renderer.blockentity.SignRenderer
import net.minecraft.client.renderer.entity.EntityRenderer
import net.minecraft.client.renderer.entity.LivingEntityRenderer
import net.minecraft.client.resources.PlayerSkin
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.AABB
import java.util.UUID
object CobblemonClient {
@@ -94,15 +85,6 @@ object CobblemonClient {
var checkedStarterScreen = false
var requests = ClientPlayerActionRequests()
var teamData = ClientPlayerTeamData()
var posableModelRepositories = listOf(
PokemonModelRepository,
PokeBallModelRepository,
NPCModelRepository,
FossilModelRepository,
BlockEntityModelRepository,
GenericBedrockEntityModelRepository
)
val overlay: PartyOverlay by lazy { PartyOverlay() }
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
@@ -268,7 +250,8 @@ object CobblemonClient {
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
CobblemonBlocks.GIMMIGHOUL_CHEST,
CobblemonBlocks.DISPLAY_CASE,
CobblemonBlocks.LECTERN
CobblemonBlocks.LECTERN,
CobblemonBlocks.STADIUM
)
this.createBoatModelLayers()
@@ -302,6 +285,7 @@ object CobblemonClient {
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.STADIUM, ::StadiumBlockRenderer)
}
private fun registerEntityRenderers() {
@@ -330,9 +314,9 @@ object CobblemonClient {
// Animations come next because models need them.
BedrockAnimationRepository.loadAnimations(
resourceManager = resourceManager,
directories = posableModelRepositories.flatMap { it.animationDirectories }
directories = VaryingModelRepository.animationDirectories
)
posableModelRepositories.forEach { it.reload(resourceManager) }
VaryingModelRepository.reload(resourceManager)
BerryModelRepository.reload(resourceManager)
MiscModelRepository.reload(resourceManager)
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.client.entity
import com.cobblemon.mod.common.api.entity.EntitySideDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
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.entity.generic.GenericBedrockEntity
class GenericBedrockClientDelegate : EntitySideDelegate<GenericBedrockEntity>, PosableState() {
@@ -24,7 +23,7 @@ class GenericBedrockClientDelegate : EntitySideDelegate<GenericBedrockEntity>, P
super.initialize(entity)
this.currentEntity = entity
this.age = entity.tickCount
this.currentModel = GenericBedrockEntityModelRepository.getPoser(entity.category, entity.delegate as GenericBedrockClientDelegate)
this.currentModel = VaryingModelRepository.getPoser(entity.category, entity.delegate as GenericBedrockClientDelegate)
currentModel!!.updateLocators(entity, this)
updateLocatorPosition(entity.position())
@@ -29,7 +29,7 @@ import com.cobblemon.mod.common.client.render.MatrixWrapper
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.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer.Companion.ease
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -99,7 +99,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
currentPose = null
currentEntity.pokemon.species = PokemonSpecies.getByIdentifier(identifier)!! // TODO exception handling
// force a model update - handles edge case where the PosableState's tracked PosableModel isn't updated until the LivingEntityRenderer render is run
currentModel = PokemonModelRepository.getPoser(identifier, this)
currentModel = VaryingModelRepository.getPoser(identifier, this)
} else if (data == PokemonEntity.ASPECTS) {
currentAspects = currentEntity.entityData.get(PokemonEntity.ASPECTS)
currentEntity.pokemon.shiny = currentAspects.contains("shiny")
@@ -24,7 +24,6 @@ import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
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
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
@@ -161,7 +160,6 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
matrixStack = matrices,
partialTicks = 0F, // partialDeltaTicks, //Before you get any funny ideas about party animated pokemon, make sure they each get their own state instead of sharing.
contextScale = pokemon.form.baseScale,
repository = PokemonModelRepository,
state = state
)
matrices.popPose()
@@ -12,10 +12,11 @@ import com.cobblemon.mod.common.api.gui.renderSprite
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.client.render.SpriteType
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
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.entity.PoseType
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.math.toEulerXYZDegrees
import com.cobblemon.mod.common.util.toHex
import com.mojang.blaze3d.platform.Lighting
import com.mojang.blaze3d.systems.RenderSystem
@@ -25,8 +26,10 @@ import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.atan
fun drawProfilePokemon(
renderablePokemon: RenderablePokemon,
@@ -41,7 +44,9 @@ fun drawProfilePokemon(
r: Float = 1F,
g: Float = 1F,
b: Float = 1F,
a: Float = 1F
a: Float = 1F,
headYaw: Float = 0f,
headPitch: Float = 0f,
) = drawProfilePokemon(
species = renderablePokemon.species.resourceIdentifier,
matrixStack = matrixStack,
@@ -56,6 +61,8 @@ fun drawProfilePokemon(
g = g,
b = b,
a = a,
headYaw = headYaw,
headPitch = headPitch,
)
fun drawProfilePokemon(
@@ -71,21 +78,23 @@ fun drawProfilePokemon(
r: Float = 1F,
g: Float = 1F,
b: Float = 1F,
a: Float = 1F
a: Float = 1F,
headYaw: Float = 0f,
headPitch: Float = 0f,
) {
RenderSystem.applyModelViewMatrix()
matrixStack.scale(scale, scale, -scale)
val sprite = PokemonModelRepository.getSprite(species, state, SpriteType.PROFILE)
val sprite = VaryingModelRepository.getSprite(species, state, SpriteType.PROFILE)
if (sprite == null) {
val model = PokemonModelRepository.getPoser(species, state)
val texture = PokemonModelRepository.getTexture(species, state)
val model = VaryingModelRepository.getPoser(species, state)
val texture = VaryingModelRepository.getTexture(species, state)
val context = RenderContext()
model.context = context
PokemonModelRepository.getTextureNoSubstitute(species, state).let { context.put(RenderContext.TEXTURE, it) }
VaryingModelRepository.getTextureNoSubstitute(species, state).let { context.put(RenderContext.TEXTURE, it) }
val baseScale = PokemonSpecies.getByIdentifier(species)!!.getForm(state.currentAspects).baseScale
context.put(RenderContext.SCALE, baseScale)
context.put(RenderContext.SPECIES, species)
@@ -100,9 +109,13 @@ fun drawProfilePokemon(
state.setPoseToFirstSuitable(poseType)
state.updatePartialTicks(partialTicks)
model.applyAnimations(null, state, 0F, 0F, 0F, 0F, 0F)
if (applyProfileTransform) {
matrixStack.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
matrixStack.translate(
model.profileTranslation.x,
model.profileTranslation.y + 1.5 * model.profileScale,
model.profileTranslation.z - 4.0
)
matrixStack.scale(model.profileScale, model.profileScale, 1 / model.profileScale)} else {
matrixStack.translate(0F, 0F, -4.0F)
if (applyBaseScale) {
@@ -110,6 +123,9 @@ fun drawProfilePokemon(
}
}
matrixStack.mulPose(rotation)
model.applyAnimations(null, state, 0F, 0F, 0F, headYaw, headPitch)
Lighting.setupForEntityInInventory()
val entityRenderDispatcher = Minecraft.getInstance().entityRenderDispatcher
rotation.conjugate()
@@ -124,7 +140,7 @@ fun drawProfilePokemon(
val packedLight = LightTexture.pack(11, 7)
val colour = toHex(r, g, b, a)
model.withLayerContext(bufferSource, state, PokemonModelRepository.getLayers(species, state)) {
model.withLayerContext(bufferSource, state, VaryingModelRepository.getLayers(species, state)) {
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, colour)
bufferSource.endBatch()
}
@@ -136,3 +152,29 @@ fun drawProfilePokemon(
}
}
const val HEAD_YAW_FACTOR = 40f
const val HEAD_PITCH_FACTOR = 20f
const val SMOOTHING_FACTOR = 0.05f
fun calculateHeadYawAndPitch(
xCoordinate: Float,
yCoordinate: Float,
rotation: Quaternionf,
mouseX: Int,
mouseY: Int,
currentYaw: Float,
currentPitch: Float,
resetToCenter: Boolean,
): Pair<Float, Float> {
val horizontalAngle = atan(((xCoordinate - mouseX) / 40.0f).toDouble()).toFloat()
val verticalAngle = atan(((yCoordinate - mouseY) / 40.0f).toDouble()).toFloat()
val eulerXYZRotationDegrees = rotation.toEulerXYZDegrees()
val targetYaw = if (!resetToCenter) horizontalAngle * HEAD_YAW_FACTOR - eulerXYZRotationDegrees.y else 0f
val targetPitch = if (!resetToCenter) -verticalAngle * HEAD_PITCH_FACTOR - eulerXYZRotationDegrees.x else 0f
val newYaw = Mth.rotLerp(SMOOTHING_FACTOR, currentYaw, targetYaw)
val newPitch = Mth.rotLerp(SMOOTHING_FACTOR, currentPitch, targetPitch)
return Pair(newYaw, newPitch)
}
@@ -26,9 +26,8 @@ import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
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.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
@@ -321,7 +320,6 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
matrixStack = matrixStack,
scale = 18F * (ballState?.scale ?: 1F) * if (isCompact) 0.65F else 1.0f,
contextScale = species.getForm(state.currentAspects).baseScale,
repository = PokemonModelRepository,
reversed = reversed,
state = state,
partialTicks = partialTicks
@@ -490,8 +488,8 @@ class BattleOverlay : Gui(Minecraft.getInstance()), Schedulable {
reversed: Boolean = false
) {
val context = RenderContext()
val model = PokeBallModelRepository.getPoser(state.pokeBall.name, state)
val texture = PokeBallModelRepository.getTexture(state.pokeBall.name, state)
val model = VaryingModelRepository.getPoser(state.pokeBall.name, state)
val texture = VaryingModelRepository.getTexture(state.pokeBall.name, state)
val renderType = RenderType.entityCutout(texture)//model.getLayer(texture)
RenderSystem.applyModelViewMatrix()
@@ -0,0 +1,113 @@
/*
* 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.config
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.StringWidget
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout
import net.minecraft.client.gui.layouts.LinearLayout
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.CommonComponents
class CobblemonConfigScreen(
private val previousScreen: Screen?,
) : Screen("gui.cobblemon_config.title".asTranslated()), CobblemonRenderable {
companion object {
const val HEIGHT = 240
const val HEADER_HEIGHT = 45
const val FOOTER_HEIGHT = 33
const val PADDING = 4
const val SLOT_WIDTH = 340
const val SLOT_HEIGHT = 22
const val HALF_SLOT_WIDTH = SLOT_WIDTH / 2
const val RESET_SLOT_WIDTH = 40
const val WIDGET_WIDTH = 110 - RESET_SLOT_WIDTH - PADDING
const val WIDGET_HEIGHT = SLOT_HEIGHT - PADDING
const val LABEL_Y_OFFSET = 4
const val WIDGET_Y_OFFSET = 0
}
val clonedConfig = Cobblemon.config.clone()
val defaultConfig = CobblemonConfig()
private lateinit var layout: HeaderAndFooterLayout
private lateinit var searchEdit: EditBox
private lateinit var variableList: CobblemonConfigVariableList
private lateinit var doneButton: Button
private var searchString: String = ""
override fun init() {
super.init()
if (::layout.isInitialized) layout.visitWidgets { removeWidget(it) }
layout = HeaderAndFooterLayout(this, HEADER_HEIGHT, FOOTER_HEIGHT)
if (::variableList.isInitialized) removeWidget(variableList)
variableList = CobblemonConfigVariableList(this)
variableList.filterConfigurations(searchString)
addRenderableWidget(variableList)
searchEdit = EditBox(
Minecraft.getInstance().font,
width / 2 - HALF_SLOT_WIDTH / 2,
variableList.y,
HALF_SLOT_WIDTH,
SLOT_HEIGHT,
lang("config.ui.search_configurations"),
).also {
it.height = WIDGET_HEIGHT
it.setMaxLength(250)
it.value = searchString
it.setResponder { text ->
variableList.filterConfigurations(text)
searchString = text
}
}
val linearLayout = layout.addToHeader(LinearLayout.vertical())
linearLayout.addChild(StringWidget(lang("config.ui.search_configurations"), this.font))
linearLayout.addChild(searchEdit)
layout.addToHeader(linearLayout)
doneButton = Button.builder(CommonComponents.GUI_DONE) { doneButtonClick() }
.pos(width / 2 - HALF_SLOT_WIDTH / 2, variableList.y + variableList.height + PADDING)
.width(HALF_SLOT_WIDTH).build()
layout.addToFooter(doneButton)
layout.visitWidgets { widget ->
addRenderableWidget(widget)
}
arrangeLayoutElements()
}
private fun arrangeLayoutElements() {
layout.arrangeElements()
variableList.updateSize(this.width, this.layout)
}
private fun doneButtonClick() {
Cobblemon.saveConfig(clonedConfig)
Cobblemon.reloadConfig()
onClose()
}
override fun onClose() {
minecraft?.setScreen(previousScreen)
}
}
@@ -0,0 +1,190 @@
/*
* 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.config
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.LABEL_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.PADDING
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList.CobblemonConfigVariableListEntry
import com.cobblemon.mod.common.client.gui.config.variable.*
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.Category
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.CobblemonConfigField
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.SharedConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.ContainerObjectSelectionList
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.narration.NarratableEntry
import kotlin.math.min
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
class CobblemonConfigVariableList(
val parent: CobblemonConfigScreen,
) : ContainerObjectSelectionList<CobblemonConfigVariableListEntry>(
Minecraft.getInstance(),
Minecraft.getInstance().window.guiScaledWidth,
HEIGHT,
0,
SLOT_HEIGHT
) {
val entries = mutableListOf<CobblemonConfigCategory>()
init {
this.x = 0
this.y = minecraft.window.guiScaledHeight / 2 - HEIGHT / 2
setRenderHeader(false, 0)
addEntries()
}
@Suppress("UNCHECKED_CAST")
private fun addEntries() {
parent.clonedConfig::class.memberProperties.map { property ->
if (!property.javaField!!.isAnnotationPresent(CobblemonConfigField::class.java)) {
return@map null
}
val entry: CobblemonConfigVariable? =
when (val classifier = property.returnType.classifier) {
String::class -> CobblemonConfigStringVariable(this, property as KMutableProperty1<out CobblemonConfig, String>)
Int::class -> CobblemonConfigIntVariable(this, property as KMutableProperty1<out CobblemonConfig, Int>)
Float::class -> CobblemonConfigFloatVariable(this, property as KMutableProperty1<out CobblemonConfig, Float>)
Double::class -> CobblemonConfigDoubleVariable(this, property as KMutableProperty1<out CobblemonConfig, Double>)
Boolean::class -> CobblemonConfigBooleanVariable(this, property as KMutableProperty1<out CobblemonConfig, Boolean>)
is KClass<*> -> if (classifier.java.isEnum) {
CobblemonConfigEnumVariable(this, property as KMutableProperty1<out CobblemonConfig, Enum<*>>)
} else null
else -> null
}
if (entry == null) {
if (SharedConstants.IS_RUNNING_IN_IDE) {
Cobblemon.LOGGER.warn("Widget for variable type: ${property.returnType.classifier} not registered: ${property.name}")
}
return@map null
}
return@map entry
}
.filterNotNull()
.groupBy { it.cobblemonConfigFieldAnnotation.category }
.toSortedMap(compareBy { it.ordinal })
.forEach { category, variables ->
val categoryEntry = CobblemonConfigCategory(this, category, variables)
entries.add(categoryEntry)
}
entries.forEach { entry -> entry.add() }
}
override fun renderWidget(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
super.renderWidget(guiGraphics, mouseX, mouseY, partialTick)
val hoveredVariableTooltip = this.hovered?.tooltip
if (isMouseInsideScrollList(mouseX, mouseY) && hoveredVariableTooltip != null) {
guiGraphics.renderTooltip(minecraft.font, hoveredVariableTooltip.toCharSequence(minecraft), mouseX, mouseY)
}
}
fun isMouseInsideScrollList(mouseX: Int, mouseY: Int): Boolean {
val bottomX = parent.width / 2 - SLOT_WIDTH / 2
val upperX = parent.width / 2 + SLOT_WIDTH / 2
return mouseX >= bottomX && mouseX <= upperX && mouseY >= y && mouseY <= bottom
}
override fun getRowWidth(): Int = width
override fun getScrollbarPosition(): Int = width / 2 - SLOT_WIDTH / 2 + SLOT_WIDTH + PADDING
fun filterConfigurations(text: String) {
for (entry in entries) {
entry.remove()
}
for (entry in entries) {
if (text.isBlank() || entry.filter(text)) {
entry.add(text)
}
}
scrollAmount = min(maxScroll.toDouble(), scrollAmount)
}
abstract class CobblemonConfigVariableListEntry(
val parent: CobblemonConfigVariableList,
) : Entry<CobblemonConfigVariableListEntry>() {
val children = mutableListOf<GuiEventListener>()
var tooltip: Tooltip? = null
fun getX(left: Int) = parent.width / 2 - SLOT_WIDTH / 2
fun getY(top: Int) = top
abstract fun filter(text: String): Boolean
override fun children(): MutableList<out GuiEventListener> = children
override fun narratables(): MutableList<out NarratableEntry> = children.filterIsInstance<NarratableEntry>().toMutableList()
}
class CobblemonConfigCategory(
parent: CobblemonConfigVariableList,
val category: Category,
val configVariables: List<CobblemonConfigVariable>
) : CobblemonConfigVariableListEntry(parent) {
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
drawScaledText(
context = context,
text = "cobblemon.config.ui.category.${category.lang}".asTranslated().yellow(),
x = parent.width / 2,
y = getY(top) + LABEL_Y_OFFSET,
shadow = true,
centered = true,
)
}
fun add(filter: String? = null) {
parent.addEntry(this)
configVariables.forEach { variable ->
if (filter == null || variable.filter(filter)) {
parent.addEntry(variable)
}
}
}
fun remove() {
parent.removeEntry(this)
configVariables.forEach { variable -> parent.removeEntry(variable) }
}
override fun filter(text: String): Boolean = configVariables.any { it.filter(text) }
}
}
@@ -0,0 +1,126 @@
/*
* 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.config.variable
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.PADDING
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.CommonComponents
import net.minecraft.network.chat.Component
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
abstract class CobblemonConfigButtonVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, *>,
) : CobblemonConfigVariable(parent, config) {
val button: Button =
Button.builder(labelLang) { onButtonClick() }
.size(WIDGET_WIDTH, WIDGET_HEIGHT)
.build()
init {
children.addFirst(button)
}
abstract fun onButtonClick()
override fun updateConfigValueDisplay() {
button.message = Component.literal(getConfigValueAsString())
}
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
super.render(context, index, top, left, width, height, mouseX, mouseY, hovering, partialTick)
button.x = resetButton.x - button.width - PADDING
button.y = getY(top) + WIDGET_Y_OFFSET
button.render(context, mouseX, mouseY, partialTick)
}
}
class CobblemonConfigBooleanVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Boolean>,
) : CobblemonConfigButtonVariable(parent, config) {
init {
updateConfigValueDisplay()
}
override fun getConfigValueAsString(): String = (if (isSelected()) CommonComponents.GUI_YES else CommonComponents.GUI_NO).string
override fun onButtonClick() = setConfigValue(!isSelected())
private fun isSelected() = (config.getter.call(parent.parent.clonedConfig) as Boolean?) ?: false
}
class CobblemonConfigEnumVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Enum<*>>,
) : CobblemonConfigButtonVariable(parent, config) {
private var enumIndex = 0
@Suppress("UNCHECKED_CAST")
private val enumValues: Array<Enum<*>> = config.returnType.classifier
.let { it as? KClass<Enum<*>> }
?.java?.enumConstants ?: emptyArray()
init {
val currentEnumValue = getCurrentEnumValue()
enumValues.firstOrNull { it.name == currentEnumValue?.name }?.let {
enumIndex = it.ordinal
}
updateConfigValueDisplay()
}
override fun getConfigValueAsString(): String {
return getCurrentEnumValue()?.name?.let {
lang("$labelEntry.${it.lowercase()}").string
} ?: ""
}
private fun getConfigValueTooltipString(): String {
return getCurrentEnumValue()?.name?.let {
lang("$labelEntry.${it.lowercase()}.tooltip").string
} ?: ""
}
override fun onButtonClick() {
enumIndex = (enumIndex + 1) % enumValues.size
val newEnumValue = enumValues[enumIndex]
setConfigValue(newEnumValue)
}
override fun updateConfigValueDisplay() {
super.updateConfigValueDisplay()
tooltip = Tooltip.create(Component.literal(getConfigValueTooltipString()))
}
private fun getCurrentEnumValue(): Enum<*>? = (config.getter.call(parent.parent.clonedConfig) as Enum<*>?)
}
@@ -0,0 +1,155 @@
/*
* 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.config.variable
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.PADDING
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.util.lang
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.EditBox.DEFAULT_TEXT_COLOR
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.Component
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.jvm.javaField
abstract class CobblemonConfigEditBoxVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, *>,
) : CobblemonConfigVariable(parent, config) {
val editBox = EditBox(
Minecraft.getInstance().font,
parent.x,
parent.y,
WIDGET_WIDTH,
SLOT_HEIGHT,
labelLang,
).also {
it.height = WIDGET_HEIGHT
it.setMaxLength(250)
it.value = getConfigValueAsString()
}
init {
children.addFirst(editBox)
}
override fun updateConfigValueDisplay() {
editBox.value = getConfigValueAsString()
}
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
super.render(context, index, top, left, width, height, mouseX, mouseY, hovering, partialTick)
editBox.x = resetButton.x - editBox.width - PADDING
editBox.y = getY(top) + WIDGET_Y_OFFSET
editBox.render(context, mouseX, mouseY, partialTick)
}
}
class CobblemonConfigStringVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, String>,
) : CobblemonConfigEditBoxVariable(parent, config) {
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as String?) ?: ""
init {
editBox.setResponder { value ->
setConfigValue(value, false)
}
}
}
class CobblemonConfigIntVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Int>,
) : CobblemonConfigEditBoxVariable(parent, config) {
private val intConstraintAnnotation: IntConstraint? =
config.javaField!!.annotations.firstOrNull{ it is IntConstraint } as IntConstraint?
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as Int?).toString()
init {
editBox.setFilter { value -> value.toIntOrNull() != null || value.isBlank() }
editBox.setResponder { value ->
if (value.isBlank()) {
return@setResponder
}
val intValue = value.toInt()
setConfigValue(intValue, false)
if (intConstraintAnnotation != null) {
val isBetweenIntConstraint = intValue >= intConstraintAnnotation.min && intValue <= intConstraintAnnotation.max
if (isBetweenIntConstraint) {
tooltip = originalTooltip
editBox.setTextColor(DEFAULT_TEXT_COLOR)
} else {
val errorLang = lang("config.ui.error.int_constraint", intConstraintAnnotation.min, intConstraintAnnotation.max).red()
tooltip = Tooltip.create(tooltipLang.plainCopy().add(Component.literal("\n")).add(errorLang))
ChatFormatting.RED.color?.let { editBox.setTextColor(it) }
}
}
}
}
}
class CobblemonConfigFloatVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Float>,
) : CobblemonConfigEditBoxVariable(parent, config) {
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as Float?).toString()
init {
editBox.setFilter { value -> value.toFloatOrNull() != null || value.isBlank() }
editBox.setResponder { value ->
if (value.toFloatOrNull() != null) {
setConfigValue(value.toFloat(), false)
}
}
}
}
class CobblemonConfigDoubleVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Double>,
) : CobblemonConfigEditBoxVariable(parent, config) {
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as Double?).toString()
init {
editBox.setFilter { value -> value.toDoubleOrNull() != null || value.isBlank() }
editBox.setResponder { value ->
if (value.toDoubleOrNull() != null) {
setConfigValue(value.toDouble(), false)
}
}
}
}
@@ -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.common.client.gui.config.variable
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.LABEL_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.RESET_SLOT_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList.CobblemonConfigVariableListEntry
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.CobblemonConfigField
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.MutableComponent
import java.text.Normalizer
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.jvm.javaField
abstract class CobblemonConfigVariable(
parent: CobblemonConfigVariableList,
val config: KMutableProperty1<out CobblemonConfig, *>,
) : CobblemonConfigVariableListEntry(parent) {
val cobblemonConfigFieldAnnotation: CobblemonConfigField =
config.javaField!!.annotations.firstOrNull{ it is CobblemonConfigField } as CobblemonConfigField
val labelEntry = "config.ui.${cobblemonConfigFieldAnnotation.lang}"
val tooltipEntry = "$labelEntry.tooltip"
val labelLang: MutableComponent = lang(labelEntry)
val tooltipLang: MutableComponent = lang(tooltipEntry)
val originalTooltip: Tooltip? = if (!tooltipLang.string.endsWith(tooltipEntry)) Tooltip.create(tooltipLang) else null
val resetButton: Button =
Button.builder(lang("config.ui.reset")) {
val defaultValue = config.getter.call(parent.parent.defaultConfig)
setConfigValue(defaultValue)
}
.size(RESET_SLOT_WIDTH, WIDGET_HEIGHT)
.build()
init {
tooltip = originalTooltip
children.add(resetButton)
updateResetButton()
}
abstract fun getConfigValueAsString(): String
abstract fun updateConfigValueDisplay()
fun setConfigValue(value: Any?, updateConfigValueDisplay: Boolean = true) {
config.setter.call(parent.parent.clonedConfig, value)
if (updateConfigValueDisplay) {
updateConfigValueDisplay()
}
updateResetButton()
}
private fun updateResetButton() {
resetButton.active = config.getter.call(parent.parent.clonedConfig) != config.getter.call(parent.parent.defaultConfig)
}
override fun filter(text: String): Boolean {
val normalizedLabel = Normalizer.normalize(labelLang.string, Normalizer.Form.NFD).replace("\\p{M}+".toRegex(), "")
return normalizedLabel.contains(text, ignoreCase = true)
}
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
val x = getX(left)
val y = getY(top)
drawScaledText(
context = context,
text = labelLang,
x = getX(left),
y = getY(top) + LABEL_Y_OFFSET,
shadow = true
)
resetButton.x = x + SLOT_WIDTH - RESET_SLOT_WIDTH
resetButton.y = y + WIDGET_Y_OFFSET
resetButton.render(context, mouseX, mouseY, partialTick)
}
}
@@ -15,8 +15,6 @@ import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.npc.NPCEntity
import java.util.UUID
@@ -82,7 +80,6 @@ class ReferenceRenderableFace(val entity: PosableEntity, override val isLeftSide
state.currentAspects = state.currentEntity.pokemon.aspects
drawPosablePortrait(
identifier = state.currentEntity.pokemon.species.resourceIdentifier,
repository = PokemonModelRepository,
contextScale = state.currentEntity.pokemon.form.baseScale,
matrixStack = GuiGraphics.pose(),
state = state,
@@ -96,7 +93,6 @@ class ReferenceRenderableFace(val entity: PosableEntity, override val isLeftSide
val limbSwingAmount = entity.walkAnimation.speed(partialTicks)
drawPosablePortrait(
identifier = state.npcEntity.npc.resourceIdentifier,
repository = NPCModelRepository,
matrixStack = GuiGraphics.pose(),
state = state,
reversed = !isLeftSide,
@@ -131,7 +127,6 @@ class ArtificialRenderableFace(
contextScale = species.getForm(aspects).baseScale,
state = state,
reversed = !isLeftSide,
repository = PokemonModelRepository,
partialTicks = partialTicks
)
} else if (modelType == "npc") {
@@ -140,7 +135,6 @@ class ArtificialRenderableFace(
matrixStack = GuiGraphics.pose(),
state = state,
reversed = !isLeftSide,
repository = NPCModelRepository,
partialTicks = partialTicks
)
}
@@ -11,9 +11,7 @@ package com.cobblemon.mod.common.client.gui.npc.widgets
import com.cobblemon.mod.common.api.gui.drawProfile
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Renderable
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.resources.ResourceLocation
@@ -47,7 +45,6 @@ class NPCRenderWidget(
context.pose().translate(x + (WIDTH / 2F), y + HEIGHT + (HEIGHT / 4F), -500F)
drawProfile(
repository = NPCModelRepository,
resourceIdentifier = identifier,
matrixStack = context.pose(),
partialTicks = delta,
@@ -0,0 +1,81 @@
/*
* 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.pc
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.storage.ClientBox
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestRenamePCBoxPacket
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
class BoxNameWidget(
private val pX: Int,
pY: Int,
text: Component = "BoxNameWidget".text(),
private val pcGui: PCGUI,
private val storageWidget: StorageWidget,
): EditBox(Minecraft.getInstance().font, pX, pY, 0, HEIGHT, text) {
companion object {
const val HEIGHT = 14
}
init {
setMaxLength(21)
update()
setResponder { update() }
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (cursorPosition != value.length) moveCursorToEnd(false)
drawScaledText(
context = context,
text = text(),
x = x,
y = y
)
}
override fun setFocused(focused: Boolean) {
super.setFocused(focused)
update()
if (!focused) {
RequestRenamePCBoxPacket(pcGui.pc.uuid, storageWidget.box, value).sendToServer()
pcGui.pc.boxes[storageWidget.box].name = if (value.isBlank()) null else Component.literal(value).bold()
value = ""
}
}
private fun text(): MutableComponent {
return (if (isFocused) {
"${value}_".text()
} else if (value.isEmpty()) {
getBox().name?: Component.translatable("cobblemon.ui.pc.box.title", storageWidget.box + 1)
} else {
value.text()
}).bold().font(CobblemonResources.DEFAULT_LARGE)
}
fun getBox(): ClientBox {
return pcGui.pc.boxes[storageWidget.box]
}
fun update() {
width = Minecraft.getInstance().font.width(text())
x = pX - width / 2
}
}
@@ -25,6 +25,8 @@ class BoxStorageSlot(
}
override fun shouldRender(): Boolean {
if (!super.shouldRender()) return false
val grabbedSlot = parent.grabbedSlot
return if (grabbedSlot == null) {
true
@@ -30,4 +30,6 @@ class GrabbedStorageSlot(
override fun getPokemon() = pokemon
override fun isHoveredOrFocused() = true
override fun shouldRender() = true
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.storage.pc.search.Search
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
@@ -22,6 +23,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNature
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.storage.ClientPC
import com.cobblemon.mod.common.client.storage.ClientParty
import com.cobblemon.mod.common.net.messages.server.storage.pc.SortPCBoxPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -36,6 +38,7 @@ import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import kotlin.isInitialized
class PCGUI(
val pc: ClientPC,
@@ -67,9 +70,13 @@ class PCGUI(
}
private lateinit var storageWidget: StorageWidget
private lateinit var boxNameWidget: BoxNameWidget
private lateinit var searchWidget: SearchWidget
private lateinit var wallpaperWidget: WallpapersScrollingWidget
private var modelWidget: ModelWidget? = null
internal var previewPokemon: Pokemon? = null
var search: Search = Search.DEFAULT
var ticksElapsed = 0
var selectPointerOffsetY = 0
var selectPointerOffsetIncrement = false
@@ -91,7 +98,9 @@ class PCGUI(
pX = x + 221,
pY = y + 17,
forward = true
) { storageWidget.box += 1 }
) {
storageWidget.box += 1
}
)
// Add Backwards Button
@@ -100,7 +109,20 @@ class PCGUI(
pX = x + 119,
pY = y + 17,
forward = false
) { storageWidget.box -= 1 }
) {
storageWidget.box -= 1
}
)
// Add Sort Button
addRenderableWidget(
SortButton(
pX = x + 85,
pY = y + 12,
onPress = { it as SortButton
SortPCBoxPacket(pc.uuid, storageWidget.box, it.sortMode, hasShiftDown()).sendToServer()
}
)
)
// Add Storage
@@ -113,8 +135,47 @@ class PCGUI(
)
this.storageWidget.box = openOnBox
// Add Box Name
this.boxNameWidget = BoxNameWidget(
pX = x + 172,
pY = y + 15,
pcGui = this,
storageWidget = storageWidget
)
// Add Search Widget
this.searchWidget = SearchWidget(
pX = x + 117,
pY = y + 183,
update = { search = Search.of(searchWidget.value) }
)
// Add Wallpaper Button
this.addRenderableWidget(
WallpaperButton(
pX = x + 235,
pY = y + 12,
onPress = {
configuration.showParty = wallpaperWidget.visible
wallpaperWidget.visible = !wallpaperWidget.visible
},
pcGui = this
)
)
// Add Wallpaper Widget
this.wallpaperWidget = WallpapersScrollingWidget(
pX = x + 275,
pY = y + 30,
pcGui = this,
storageWidget = storageWidget
)
this.setPreviewPokemon(null)
this.addRenderableWidget(storageWidget)
this.addRenderableWidget(boxNameWidget)
this.addRenderableWidget(searchWidget)
this.addRenderableWidget(wallpaperWidget)
super.init()
}
@@ -136,7 +197,9 @@ class PCGUI(
)
// Render Model Portrait
modelWidget?.render(context, mouseX, mouseY, delta)
if (search.passes(previewPokemon)) {
modelWidget?.render(context, mouseX, mouseY, delta)
}
// Render Base Resource
blitk(
@@ -177,7 +240,7 @@ class PCGUI(
// Render Pokemon Info
val pokemon = previewPokemon
if (pokemon != null) {
if (pokemon != null && search.passes(pokemon)) {
// Status
val status = pokemon.status?.status
if (pokemon.isFainted() || status != null) {
@@ -367,16 +430,6 @@ class PCGUI(
)
}
// Box Label
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pc.box.title", (this.storageWidget.box + 1).toString()).bold(),
x = x + 172,
y = y + 15,
centered = true
)
blitk(
matrixStack = matrices,
texture = topSpacerResource,
@@ -454,27 +507,36 @@ class PCGUI(
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (isInventoryKeyPressed(minecraft, keyCode, scanCode)) {
val boxNameSelected = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
val searchSelected = this::searchWidget.isInitialized && searchWidget.isFocused
if (isInventoryKeyPressed(minecraft, keyCode, scanCode) && !boxNameSelected && !searchSelected) {
playSound(CobblemonSounds.PC_OFF)
UnlinkPlayerFromPCPacket().sendToServer()
Minecraft.getInstance().setScreen(null)
return true
}
if ((keyCode == InputConstants.KEY_RETURN || keyCode == InputConstants.KEY_NUMPADENTER)
&& (boxNameSelected || searchSelected)) {
this.focused = null
}
when (keyCode) {
InputConstants.KEY_ESCAPE -> {
playSound(CobblemonSounds.PC_OFF)
UnlinkPlayerFromPCPacket().sendToServer()
onClose()
return true
}
InputConstants.KEY_RIGHT -> {
playSound(CobblemonSounds.PC_CLICK)
this.storageWidget.box += 1
return true
}
InputConstants.KEY_LEFT -> {
playSound(CobblemonSounds.PC_CLICK)
this.storageWidget.box -= 1
return true
}
}
return super.keyPressed(keyCode, scanCode, modifiers)
@@ -483,9 +545,7 @@ class PCGUI(
/**
* Whether this Screen should pause the Game in SinglePlayer
*/
override fun isPauseScreen(): Boolean {
return false
}
override fun isPauseScreen() = false
override fun tick() {
ticksElapsed++
@@ -521,4 +581,10 @@ class PCGUI(
modelWidget = null
}
}
fun updateBoxName() {
if (this::boxNameWidget.isInitialized) {
boxNameWidget.update()
}
}
}
@@ -14,6 +14,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
open class PCGUIConfiguration(
val exitFunction: (PCGUI) -> Unit = { it.closeNormally(unlink = true) },
val selectOverride: ((PCGUI, StorePosition, Pokemon?) -> Unit)? = null,
val showParty: Boolean = true,
var showParty: Boolean = true,
val canSelect: (Pokemon) -> Boolean = { true }
)
@@ -25,6 +25,8 @@ class PartyStorageSlot(
}
override fun shouldRender(): Boolean {
if (!super.shouldRender()) return false
val grabbedSlot = parent.grabbedSlot
return if (grabbedSlot == null) {
true
@@ -32,4 +34,8 @@ class PartyStorageSlot(
grabbedSlot.getPokemon() != getPokemon()
}
}
override fun clickable(): Boolean {
return parent.pcGui.configuration.showParty && super.clickable()
}
}
@@ -0,0 +1,66 @@
/*
* 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.pc
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.network.chat.Component
class SearchWidget(
private var pX: Int,
private var pY: Int,
text: Component = "SearchWidget".text(),
update: () -> (Unit)
): EditBox(Minecraft.getInstance().font, pX, pY, WIDTH, HEIGHT, text) {
companion object {
const val WIDTH = 110
const val HEIGHT = 14
const val HINT_COLOUR = 0x8D8D8D
private val hint = Component.translatable("cobblemon.ui.pc.search")
private val searchIcon = cobblemonResource("textures/gui/pc/pc_search_icon.png")
}
init {
setMaxLength(21)
setResponder { update() }
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (cursorPosition != value.length) moveCursorToEnd(false)
val input = if (isFocused) "${value}_".text() else value.text()
val useHint = !isFocused && value.isEmpty()
blitk(
matrixStack = context.pose(),
texture = searchIcon,
x = pX + 1,
y = pY + 3,
width = 8,
height = 8
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = if (useHint) hint.bold() else input.bold(),
x = pX + if (useHint) 55 else 14,
y = pY + 2,
colour = if (useHint) HINT_COLOUR else 0xFFFFFF,
centered = useHint
)
}
}
@@ -0,0 +1,78 @@
/*
* 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.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokemon.PokemonSortMode
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.components.Button
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import kotlin.math.abs
class SortButton(
pX: Int, pY: Int,
onPress: OnPress,
) : Button(pX, pY, WIDTH, HEIGHT, Component.literal("SortButton"), onPress, DEFAULT_NARRATION) {
var sortMode = PokemonSortMode.NAME
companion object {
const val WIDTH = 24
const val HEIGHT = 11
private val modeResources = PokemonSortMode.entries.map { cobblemonResource("textures/gui/pc/pc_sort_${it.name.lowercase()}.png") }
private val buttonResource = cobblemonResource("textures/gui/pc/pc_sort_button.png")
}
override fun renderWidget(context: net.minecraft.client.gui.GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(),
texture = buttonResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered()) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
blitk(
matrixStack = context.pose(),
texture = modeResources[sortMode.ordinal],
x = x,
y = y,
width = WIDTH,
height = HEIGHT
)
}
override fun mouseScrolled(
mouseX: Double,
mouseY: Double,
scrollX: Double,
scrollY: Double
): Boolean {
if (abs(scrollY) >= .5 && isMouseOver(mouseX, mouseY)) {
val modes = PokemonSortMode.entries
var mode = sortMode.ordinal + if (scrollY > 0) 1 else -1
if (mode < 0) mode = modes.size - 1
if (mode >= modes.size) mode = 0
sortMode = modes[mode]
return true
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1.0F))
}
}
@@ -212,7 +212,15 @@ open class StorageSlot(
}
open fun shouldRender(): Boolean {
return true
return parent.pcGui.search.passes(getPokemon())
}
open fun clickable(): Boolean {
return getPokemon() == null || parent.pcGui.search.passes(getPokemon())
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
return if (clickable()) super.mouseClicked(mouseX, mouseY, button) else false
}
fun isHovered(mouseX: Int, mouseY: Int) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + SIZE)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + SIZE))
@@ -57,7 +57,6 @@ class StorageWidget(
const val PARTY_SLOT_PADDING = 6
private val partyPanelResource = cobblemonResource("textures/gui/pc/party_panel.png")
private val screenOverlayResource = cobblemonResource("textures/gui/pc/pc_screen_overlay.png")
}
private val partySlots = arrayListOf<PartyStorageSlot>()
@@ -82,6 +81,7 @@ class StorageWidget(
// Else it's greater than max, wrap around to start
else 0
this.setupStorageSlots()
this.pcGui.updateBoxName()
}
init {
@@ -255,7 +255,7 @@ class StorageWidget(
// Screen Overlay
blitk(
matrixStack = matrices,
texture = screenOverlayResource,
texture = pc.boxes[box].wallpaper,
x = x - 17,
y = y - 17,
width = 208,
@@ -348,7 +348,7 @@ class StorageWidget(
}
if (grabbedSlot == null) {
if (clickedPokemon != null) {
if (clickedPokemon != null && pcGui.search.passes(clickedPokemon)) {
val shiftClicked = Screen.hasShiftDown()
if (shiftClicked) {
if (clickedPosition is PCPosition) {
@@ -0,0 +1,48 @@
/*
* 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.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.components.Button
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
class WallpaperButton(
pX: Int, pY: Int,
onPress: OnPress,
private val pcGui: PCGUI
) : Button(pX, pY, WIDTH, HEIGHT, Component.literal("WallpaperButton"), onPress, DEFAULT_NARRATION) {
companion object {
const val WIDTH = 24
const val HEIGHT = 11
private val buttonResource = cobblemonResource("textures/gui/pc/pc_wallpaper_button.png")
}
override fun renderWidget(context: net.minecraft.client.gui.GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(),
texture = buttonResource,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered() || !pcGui.configuration.showParty) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
}
override fun playDownSound(soundManager: SoundManager) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1.0F))
}
}
@@ -0,0 +1,153 @@
/*
* 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.pc
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.ScrollingWidget
import com.cobblemon.mod.common.client.gui.pc.WallpapersScrollingWidget.WallpaperEntry
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestChangePCBoxWallpaperPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
class WallpapersScrollingWidget(
pX: Int, val pY: Int,
val pcGui: PCGUI,
val storageWidget: StorageWidget
) : ScrollingWidget<WallpaperEntry>(
width = WIDTH,
height = HEIGHT,
left = pX,
top = 0,
slotHeight = SLOT_HEIGHT + SLOT_PADDING,
scrollBarWidth = SCROLL_BAR_WIDTH
) {
companion object {
const val WIDTH = 67
const val HEIGHT = 147
const val SLOT_WIDTH = 58
const val SLOT_HEIGHT = 53
const val SLOT_PADDING = 3
const val SCROLL_BAR_WIDTH = 3
val ENTRY_HOVERED = cobblemonResource("textures/gui/pc/wallpaper_entry_hover.png")
val BACKGROUND = cobblemonResource("textures/gui/pc/wallpaper_widget_background.png")
}
init {
this.visible = false
this.y = pY
createEntries()
}
override fun renderWidget(
context: GuiGraphics,
mouseX: Int,
mouseY: Int,
delta: Float
) {
blitk(
matrixStack = context.pose(),
texture = BACKGROUND,
x = x - 1,
y = y - 1,
width = width + 1,
height = height + 2
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.wallpapers").bold(),
x = x + 23,
y = y - 18.5,
centered = true,
shadow = true
)
super.renderWidget(context, mouseX, mouseY, delta)
}
private fun createEntries() {
clearEntries()
for (wallpaper in PCBoxWallpaperRepository.wallpapers) {
addEntry(WallpaperEntry(wallpaper))
}
}
override fun getRowLeft(): Int {
return this.left + SLOT_PADDING
}
override fun getRowWidth(): Int {
return SLOT_WIDTH
}
override fun getRowRight(): Int {
return this.rowLeft + SLOT_WIDTH
}
inner class WallpaperEntry(val wallpaper: ResourceLocation) : Slot<WallpaperEntry>() {
override fun render(
guiGraphics: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
val matrices = guiGraphics.pose()
blitk(
matrixStack = matrices,
texture = wallpaper,
x = left,
y = top,
width = width,
height = height,
)
if (hovering) {
blitk(
matrixStack = matrices,
texture = ENTRY_HOVERED,
x = left,
y = top,
width = width,
height = height
)
}
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (this@WallpapersScrollingWidget.visible && isMouseOver(mouseX, mouseY)) {
RequestChangePCBoxWallpaperPacket(pcGui.pc.uuid, storageWidget.box, wallpaper).sendToServer()
pcGui.pc.boxes[storageWidget.box].wallpaper = wallpaper
minecraft.soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1.0F))
return true
}
return false
}
override fun getNarration(): Component? {
return wallpaper.toString().text()
}
}
}
@@ -38,7 +38,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Gender
@@ -655,7 +655,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
fun recalculatePoses(renderablePokemon: RenderablePokemon) {
val state = FloatingState()
state.currentAspects = renderablePokemon.aspects
val poser = PokemonModelRepository.getPoser(renderablePokemon.species.resourceIdentifier, state)
val poser = VaryingModelRepository.getPoser(renderablePokemon.species.resourceIdentifier, state)
state.currentModel = poser
this.poseList = poser.poses
.map { it.value.poseTypes.minBy { it.ordinal } }
@@ -80,7 +80,7 @@ class SizeWidget(val pX: Int, val pY: Int) : SoundlessWidget(
matrices.pushPose()
matrices.translate(
pX.toDouble() + 50,
pY.toDouble() + (POKEMON_DESCRIPTION_HEIGHT / 2) + 18.5 - baseScale * modelScale * 1.5,
pY.toDouble() + (POKEMON_DESCRIPTION_HEIGHT / 2) + 18.5,
0.0
)
drawProfilePokemon(
@@ -161,7 +161,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
pX = x + 119, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentPokemon,
rotationVector = this.modelWidget.rotVec
rotationVector = this.modelWidget.rotationVector
)
starterRoundaboutLeft = StarterRoundabout(
@@ -169,7 +169,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
clickAction = { _, _ -> this.left() },
rotationVector = this.modelWidget.rotVec
rotationVector = this.modelWidget.rotationVector
)
starterRoundaboutRight = StarterRoundabout(
@@ -177,7 +177,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
clickAction = { _, _ -> this.right() },
rotationVector = this.modelWidget.rotVec
rotationVector = this.modelWidget.rotationVector
)
addRenderableWidget(starterRoundaboutLeft)
@@ -35,7 +35,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSwa
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.pokemon.Gender
@@ -235,7 +235,8 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
pokemon = selectedPokemon.asRenderablePokemon(),
baseScale = 2F,
rotationY = 325F,
offsetY = -10.0
offsetY = -10.0,
shouldFollowCursor = true,
)
addRenderableOnly(this.modelWidget)
}
@@ -274,9 +275,11 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
listenToMoveSet()
displayMainScreen(mainScreenIndex)
children().find { it is EvolutionSelectScreen }?.let(this::removeWidget)
if (this::modelWidget.isInitialized) {
this.modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
if (::modelWidget.isInitialized) {
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
}
if (this::nicknameEntryWidget.isInitialized) {
this.nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
}
@@ -592,12 +595,6 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
return children().any { it.mouseScrolled(mouseX, mouseY, amount, verticalAmount) }
}
/*
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
return children().any { it.mouseClicked(mouseX, mouseY, button) }
}
*/
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
if (mainScreenIndex == MOVES) mainScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
@@ -619,7 +616,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
this.focused = null
}
if (Cobblemon.config.enableDebugKeys) {
val model = PokemonModelRepository.getPoser(selectedPokemon.species.resourceIdentifier, modelWidget.state)
val model = VaryingModelRepository.getPoser(selectedPokemon.species.resourceIdentifier, modelWidget.state)
if (keyCode == InputConstants.KEY_UP) {
model.profileTranslation = model.profileTranslation.add(0.0, -0.01, 0.0)
}
@@ -648,7 +645,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
override fun onClose() {
if (Cobblemon.config.enableDebugKeys) {
val model = PokemonModelRepository.getPoser(selectedPokemon.species.resourceIdentifier, modelWidget.state)
val model = VaryingModelRepository.getPoser(selectedPokemon.species.resourceIdentifier, modelWidget.state)
Minecraft.getInstance().player?.sendSystemMessage(Component.literal("Profile Translation: ${model.profileTranslation}"))
Minecraft.getInstance().player?.sendSystemMessage(Component.literal("Profile Scale: ${model.profileScale}"))
Cobblemon.LOGGER.info("override var profileTranslation = Vec3d(${model.profileTranslation.x}, ${model.profileTranslation.y}, ${model.profileTranslation.z})")
@@ -8,7 +8,9 @@
package com.cobblemon.mod.common.client.gui.summary.widgets
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.calculateHeadYawAndPitch
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
@@ -20,29 +22,39 @@ import org.joml.Vector3f
class ModelWidget(
pX: Int, pY: Int,
pWidth: Int, pHeight: Int,
var pokemon: RenderablePokemon,
pokemon: RenderablePokemon,
val baseScale: Float = 2.7F,
var rotationY: Float = 35F,
var offsetY: Double = 0.0,
val playCryOnClick: Boolean = false,
val shouldFollowCursor: Boolean = false,
): SoundlessWidget(pX, pY, pWidth, pHeight, Component.literal("Summary - ModelWidget")) {
companion object {
var render = true
const val MAX_SECONDS_LOOKING = 6L
}
var pokemon: RenderablePokemon = pokemon
set (value) {
field = value
currentYawAndPitch = Pair(0f, 0f)
}
var state = FloatingState()
val rotVec = Vector3f(13F, rotationY, 0F)
var lookStartTime: Long? = null
var currentYawAndPitch: Pair<Float, Float> = Pair(0f, 0f)
val rotationVector = Vector3f(13F, rotationY, 0F)
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, partialTicks: Float) {
if (!render) {
return
}
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
renderPKM(context, partialTicks)
renderPKM(context, partialTicks, pMouseX, pMouseY)
}
private fun renderPKM(context: GuiGraphics, partialTicks: Float) {
private fun renderPKM(context: GuiGraphics, partialTicks: Float, mouseX: Int, mouseY: Int) {
val matrices = context.pose()
matrices.pushPose()
@@ -50,19 +62,48 @@ class ModelWidget(
x,
y,
x + width,
y + height
y + height
)
matrices.translate(x + width * 0.5, y.toDouble() + offsetY, 0.0)
matrices.scale(baseScale, baseScale, baseScale)
matrices.pushPose()
if (isHovered) {
lookStartTime = System.currentTimeMillis()
}
val timeLooking = lookStartTime?.let { (System.currentTimeMillis() - it) / 1000 } ?: MAX_SECONDS_LOOKING
val lookedTooMuch = timeLooking >= MAX_SECONDS_LOOKING
val resetToCenter = (!isHovered && lookedTooMuch) || !shouldFollowCursor || !Cobblemon.config.summaryPokemonFollowCursor
if (resetToCenter) {
lookStartTime = null
}
val rotation = Quaternionf().fromEulerXYZDegrees(rotationVector)
if (shouldFollowCursor) {
currentYawAndPitch = calculateHeadYawAndPitch(
x + width / 2f,
y + height / 2f,
rotation,
mouseX,
mouseY,
currentYawAndPitch.first,
currentYawAndPitch.second,
resetToCenter
)
}
drawProfilePokemon(
renderablePokemon = pokemon,
matrixStack = matrices,
rotation = Quaternionf().fromEulerXYZDegrees(rotVec),
rotation = rotation,
state = state,
partialTicks = partialTicks
partialTicks = partialTicks,
headYaw = currentYawAndPitch.first,
headPitch = currentYawAndPitch.second,
)
matrices.popPose()
@@ -72,7 +113,10 @@ class ModelWidget(
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (this.isHovered) playCry()
if (this.isHovered) {
playCry()
}
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinding
import com.cobblemon.mod.common.client.keybind.KeybindCategories
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
@@ -38,7 +38,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
model.portraitScale += 0.01F
}
@@ -54,7 +54,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
model.portraitScale -= 0.01F
}
@@ -71,7 +71,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
model.portraitTranslation = model.portraitTranslation.add(0.0, -0.01, 0.0)
}
@@ -87,7 +87,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
model.portraitTranslation = model.portraitTranslation.add(0.0, 0.01, 0.0)
}
@@ -104,7 +104,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
model.portraitTranslation = model.portraitTranslation.add(-0.01, 0.0, 0.0)
}
@@ -121,7 +121,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
model.portraitTranslation = model.portraitTranslation.add(0.01, 0.0, 0.0)
}
}
@@ -137,7 +137,7 @@ object DebugKeybindings {
val currentlySelectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot)
if (currentlySelectedPokemon != null) {
val state = FloatingState().also { it.currentAspects = currentlySelectedPokemon.aspects }
val model = PokemonModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(currentlySelectedPokemon.species.resourceIdentifier, state)
Minecraft.getInstance().player?.sendSystemMessage(Component.literal("Portrait Translation: ${model.portraitTranslation}"))
Minecraft.getInstance().player?.sendSystemMessage(Component.literal("Portrait Scale: ${model.portraitScale}"))
Cobblemon.LOGGER.info("override var portraitTranslation = Vec3d(${model.portraitTranslation.x}, ${model.portraitTranslation.y}, ${model.portraitTranslation.z})")
@@ -0,0 +1,38 @@
/*
* 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.battle
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.api.text.aqua
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.client.ClientMultiBattleTeamMember
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.net.messages.client.battle.TeamMemberAddNotificationPacket
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
object TeamMemberAddNotificationHandler : ClientNetworkPacketHandler<TeamMemberAddNotificationPacket> {
override fun handle(packet: TeamMemberAddNotificationPacket, client: Minecraft) {
if (CobblemonClient.teamData.multiBattleTeamMembers.any { it.uuid == packet.teamMemberUUID }) {
return //already knows about the new member
}
var newMember = ClientMultiBattleTeamMember(packet.teamMemberUUID, packet.teamMemberName)
CobblemonClient.teamData.multiBattleTeamMembers.add(newMember)
ClientPlayerIcon.update(newMember.uuid)
client.player?.sendSystemMessage(
lang(
"team.join.other",
newMember.name.copy().aqua(),
).green()
)
}
}
@@ -9,14 +9,11 @@
package com.cobblemon.mod.common.client.net.pokemon.update
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.net.messages.client.PokemonUpdatePacket
import net.minecraft.client.Minecraft
class PokemonUpdatePacketHandler<T : PokemonUpdatePacket<T>> : ClientNetworkPacketHandler<T> {
override fun handle(packet: T, client: Minecraft) {
packet.applyToPokemon()
val player = client.player ?: return
PokemonOnShoulderRenderer.clearCache(player)
}
}
@@ -0,0 +1,20 @@
/*
* 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.settings
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
import net.minecraft.client.Minecraft
object OpenCobblemonConfigEditorHandler : ClientNetworkPacketHandler<OpenCobblemonConfigScreenPacket> {
override fun handle(packet: OpenCobblemonConfigScreenPacket, client: Minecraft) {
client.setScreen(CobblemonConfigScreen(Minecraft.getInstance().screen))
}
}
@@ -0,0 +1,20 @@
/*
* 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.storage.pc
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
import net.minecraft.client.Minecraft
object ChangePCBoxWallpaperHandler : ClientNetworkPacketHandler<ChangePCBoxWallpaperPacket> {
override fun handle(packet: ChangePCBoxWallpaperPacket, client: Minecraft) {
CobblemonClient.storage.changeBoxWallpaper(packet.storeID, packet.boxNumber, packet.wallpaper)
}
}
@@ -0,0 +1,20 @@
/*
* 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.storage.pc
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
import net.minecraft.client.Minecraft
object RenamePCBoxHandler : ClientNetworkPacketHandler<RenamePCBoxPacket> {
override fun handle(packet: RenamePCBoxPacket, client: Minecraft) {
CobblemonClient.storage.renameBox(packet.storeID, packet.boxNumber, packet.name)
}
}
@@ -0,0 +1,22 @@
/*
* 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.storage.pc
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
import com.cobblemon.mod.common.net.messages.server.storage.pc.PCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
import net.minecraft.client.Minecraft
object RequestPCBoxWallpapersHandler : ClientNetworkPacketHandler<RequestPCBoxWallpapersPacket> {
override fun handle(packet: RequestPCBoxWallpapersPacket, client: Minecraft) {
PCBoxWallpaperRepository.findWallpapers(client.resourceManager)
PCBoxWallpapersPacket(PCBoxWallpaperRepository.wallpapers).sendToServer()
}
}
@@ -11,15 +11,16 @@ package com.cobblemon.mod.common.client.net.storage.pc
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.storage.ClientBox
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
object SetPCBoxPokemonHandler : ClientNetworkPacketHandler<SetPCBoxPokemonPacket> {
override fun handle(packet: SetPCBoxPokemonPacket, client: Minecraft) {
object SetPCBoxHandler : ClientNetworkPacketHandler<SetPCBoxPacket> {
override fun handle(packet: SetPCBoxPacket, client: Minecraft) {
CobblemonClient.storage.pcStores[packet.storeID]?.let { pc ->
val boxNumber = packet.boxNumber
while (pc.boxes.size <= boxNumber) { pc.boxes.add(ClientBox()) }
pc.boxes[boxNumber] = ClientBox()
pc.boxes[boxNumber] = ClientBox(if (packet.name.isBlank()) null else Component.literal(packet.name), packet.wallpaper)
packet.pokemon.forEach { (slot, pokemon) -> pc.boxes[packet.boxNumber].slots[slot] = pokemon(client.level!!.registryAccess()) }
}
}
@@ -0,0 +1,20 @@
/*
* 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.storage.pc
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
import net.minecraft.client.Minecraft
object SetPCBoxWallpapersHandler : ClientNetworkPacketHandler<SetPCBoxWallpapersPacket> {
override fun handle(packet: SetPCBoxWallpapersPacket, client: Minecraft) {
PCBoxWallpaperRepository.wallpapers = packet.wallpapers
}
}
@@ -30,12 +30,12 @@ import org.joml.Vector4f
* @author Hiroku
* @since May 14th, 2022
*/
class VaryingRenderableResolver<T : PosableModel>(
class VaryingRenderableResolver(
val name: ResourceLocation,
val variations: MutableList<ModelAssetVariation>
) {
lateinit var repository: VaryingModelRepository<T>
val posers = mutableMapOf<Pair<ResourceLocation, ResourceLocation>, T>()
lateinit var repository: VaryingModelRepository
val posers = mutableMapOf<Pair<ResourceLocation, ResourceLocation>, PosableModel>()
val models = mutableMapOf<ResourceLocation, Bone>()
fun getResolvedPoser(state: PosableState): ResourceLocation {
@@ -98,19 +98,19 @@ class VaryingRenderableResolver<T : PosableModel>(
.create()
}
fun initialize(repository: VaryingModelRepository<T>) {
fun initialize(repository: VaryingModelRepository) {
this.repository = repository
posers.clear()
getAllModels().forEach { identifier ->
try {
models[identifier] = repository.texturedModels[identifier]!!.invoke(repository.isForLivingEntityRenderer)
models[identifier] = repository.texturedModels[identifier]!!
} catch (e: Exception) {
throw IllegalStateException("Unable to load model $identifier for $name", e)
}
}
}
fun getPoser(state: PosableState): T {
fun getPoser(state: PosableState): PosableModel {
val poserName = getResolvedPoser(state)
val poserSupplier = repository.posers[poserName] ?: throw IllegalStateException("No poser found for name: $poserName for $name")
val modelName = getResolvedModel(state)
@@ -9,8 +9,9 @@
package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.block.entity.GildedChestBlockEntity
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.blockentity.BlockEntityModel
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.renderer.MultiBufferSource
@@ -40,9 +41,9 @@ class GildedChestBlockRenderer(context: BlockEntityRendererProvider.Context) : B
val poserId = entity.type.poserId
val model = BlockEntityModelRepository.getPoser(poserId, state)
val model = VaryingModelRepository.getPoser(poserId, state) as BlockEntityModel
model.context = context
val texture = BlockEntityModelRepository.getTexture(poserId, state)
val texture = VaryingModelRepository.getTexture(poserId, state)
val vertexConsumer = vertexConsumers.getBuffer(RenderType.entityCutout(texture))
model.bufferProvider = vertexConsumers
state.currentModel = model
@@ -67,7 +68,7 @@ class GildedChestBlockRenderer(context: BlockEntityRendererProvider.Context) : B
ageInTicks = state.animationSeconds * 20
)
model.render(context, matrices, vertexConsumer, light, overlay, -0x1)
model.withLayerContext(vertexConsumers, state, BlockEntityModelRepository.getLayers(poserId, state)) {
model.withLayerContext(vertexConsumers, state, VaryingModelRepository.getLayers(poserId, state)) {
model.render(context, matrices, vertexConsumer, light, OverlayTexture.NO_OVERLAY, -0x1)
}
model.setDefault()
@@ -12,23 +12,24 @@ import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilModel
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.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.parabolaFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.rerange
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.timeDilate
import com.cobblemon.mod.common.util.cobblemonResource
import kotlin.math.pow
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import kotlin.math.pow
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.core.Direction
import net.minecraft.world.level.block.HorizontalDirectionalBlock
class RestorationTankRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<RestorationTankBlockEntity> {
val context = RenderContext().also {
@@ -114,8 +115,14 @@ class RestorationTankRenderer(ctx: BlockEntityRendererProvider.Context) : BlockE
state.updatePartialTicks(tickDelta)
state.currentAspects = aspects
var fossilPoserId = fossil.identifier.withPath("${fossil.identifier.path}_fetus")
val completionPercentage = (1 - timeRemaining / FossilMultiblockStructure.TIME_TO_TAKE.toFloat()).coerceIn(0F, 1F)
val fossilFetusModel = FossilModelRepository.getPoser(fossil.identifier, state)
val fossilFetusModel = VaryingModelRepository.getPoser(fossilPoserId, state) as? FossilModel ?: let {
fossilPoserId = cobblemonResource("fossil_fetus")
VaryingModelRepository.getPoser(fossilPoserId, state) as FossilModel
}
fossilFetusModel.context = context
val embryo1Scale = EMBRYO_CURVE_1(completionPercentage)
@@ -124,15 +131,15 @@ class RestorationTankRenderer(ctx: BlockEntityRendererProvider.Context) : BlockE
val fossilScale = FOSSIL_CURVE(completionPercentage)
val identifiersAndScales = listOf(
Pair(EMBRYO_IDENTIFIERS[0], embryo1Scale),
Pair(EMBRYO_IDENTIFIERS[1], embryo2Scale),
Pair(EMBRYO_IDENTIFIERS[2], embryo3Scale),
Pair(fossil.identifier, fossilScale)
Pair(EMBRYO_IDENTIFIERS[0], embryo1Scale),
Pair(EMBRYO_IDENTIFIERS[1], embryo2Scale),
Pair(EMBRYO_IDENTIFIERS[2], embryo3Scale),
Pair(fossilPoserId, fossilScale)
)
identifiersAndScales.forEach { (identifier, scale) ->
val model = FossilModelRepository.getPoser(identifier, state)
val texture = FossilModelRepository.getTexture(identifier, state)
val model = VaryingModelRepository.getPoser(identifier, state) as FossilModel
val texture = VaryingModelRepository.getTexture(identifier, state)
if (scale > 0F) {
val vertexConsumer = vertexConsumers.getBuffer(RenderType.entityCutout(texture))
@@ -175,7 +182,7 @@ class RestorationTankRenderer(ctx: BlockEntityRendererProvider.Context) : BlockE
ageInTicks = state.animationSeconds * 20
)
model.render(context, matrices, vertexConsumer, light, overlay, -0x1)
model.withLayerContext(vertexConsumers, state, FossilModelRepository.getLayers(fossil.identifier, state)) {
model.withLayerContext(vertexConsumers, state, VaryingModelRepository.getLayers(fossilPoserId, state)) {
model.render(context, matrices, vertexConsumer, light, OverlayTexture.NO_OVERLAY, -0x1)
}
model.setDefault()
@@ -0,0 +1,196 @@
/*
* 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.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneModel
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneState
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.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.state.BlockState
class StadiumBlockRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<StadiumBlockEntity> {
val context = RenderContext().also {
it.put(RenderContext.DO_QUIRKS, false)
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.BLOCK)
}
private var pathIndex = 0
private var animationTime = 0.0
private val droneSpeed = 0.10
override fun render(
blockEntity: StadiumBlockEntity,
tickDelta: Float,
poseStack: PoseStack,
multiBufferSource: MultiBufferSource,
light: Int,
overlay: Int
) {
val blockState = blockEntity.blockState
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
val isClientSide = blockEntity.level?.isClientSide == true
if (!isClientSide) return
val stadiumBlockState = blockEntity.level?.getBlockState(blockEntity.blockPos) ?: return
if (stadiumBlockState.block != CobblemonBlocks.STADIUM) {
return
}
val droneState = blockEntity.droneState
poseStack.pushPose()
if (blockEntity.isInUse) {
if (blockEntity.pathPoints.isEmpty()) {
poseStack.popPose()
return
}
spawnDebugParticles(blockEntity) // debug particles
// Center the drone at the block's center
poseStack.translate(0.5, -3.0, 0.5)
//poseStack.mulPose(Axis.XP.rotationDegrees(180F))
//poseStack.mulPose(Axis.YP.rotationDegrees(180F))
val points = blockEntity.pathPoints
val nextIndex = (pathIndex + 1) % points.size
val currentPoint = points[pathIndex]
val nextPoint = points[nextIndex]
animationTime += tickDelta * droneSpeed
if (animationTime >= 1.0) {
animationTime = 0.0
pathIndex = nextIndex
}
val interpolatedPos = currentPoint.lerp(nextPoint, animationTime.toDouble())
println("DEBUG: Drone moving between points:")
println(" - Current Point: ${currentPoint}")
println(" - Next Point: ${nextPoint}")
println(" - Interpolated Position: $interpolatedPos")
poseStack.translate(interpolatedPos.x - blockEntity.blockPos.x,
interpolatedPos.y - blockEntity.blockPos.y,
interpolatedPos.z - blockEntity.blockPos.z)
println("DEBUG: Drone position interpolated to: $interpolatedPos")
poseStack.mulPose(Axis.YP.rotationDegrees(-yRot))
poseStack.mulPose(Axis.ZP.rotationDegrees(180F)) // flip the drone right side up
renderDrone(droneState, "sleep", tickDelta, poseStack, 1.0F, multiBufferSource, light)
} else {
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
val poseEntry = poseStack.last()
val matrix = poseEntry.pose()
poseStack.translate(0.5, 0.0, 0.5)
poseStack.mulPose(Axis.YP.rotationDegrees(yRot))
val posX = matrix.m03()
val posY = matrix.m13()
val posZ = matrix.m23()
println("DEBUG: Drone is NOT in use. Rendering default position at: x=$posX, y=$posY, z=$posZ")
poseStack.mulPose(Axis.ZP.rotationDegrees(180F))
renderDrone(droneState, "sleep", tickDelta, poseStack, 1.0F, multiBufferSource, light)
}
poseStack.popPose()
}
fun renderDrone(
state: DroneState,
pose: String,
tickDelta: Float,
poseStack: PoseStack,
scale: Float = 1F,
multiBufferSource: MultiBufferSource,
light: Int,
) {
state.updatePartialTicks(tickDelta)
val droneResource: ResourceLocation = cobblemonResource("rotom_drone")
val model = VaryingModelRepository.getPoser(droneResource, state) as DroneModel
val texture = VaryingModelRepository.getTexture(droneResource, state)
val buffer = multiBufferSource.getBuffer(RenderType.entityCutout(texture))
model.bufferProvider = multiBufferSource
state.currentModel = model
state.setPose(pose)
model.context = context
context.put(RenderContext.TEXTURE, texture)
context.put(RenderContext.RENDER_STATE, RenderContext.RenderState.BLOCK)
context.put(RenderContext.POSABLE_STATE, state)
model.applyAnimations(
entity = null,
state = state,
headYaw = 0F,
headPitch = 0F,
limbSwing = 0F,
limbSwingAmount = 0F,
ageInTicks = state.animationSeconds * 20
)
poseStack.scale(scale, scale, scale)
// TODO: Make drone lighting dynamic (simply using the light arg makes the drone always appear dark)
model.withLayerContext(multiBufferSource, state, VaryingModelRepository.getLayers(droneResource, state)) {
model.render(context, poseStack, buffer, 200, OverlayTexture.NO_OVERLAY, -0x1)
}
model.setDefault()
}
private fun spawnDebugParticles(blockEntity: StadiumBlockEntity) {
val world = blockEntity.level ?: return
if (!world.isClientSide) return // Only run on the client-side
for (point in blockEntity.pathPoints) {
world.addParticle(
net.minecraft.core.particles.ParticleTypes.FLAME, // Test particle (changeable)
point.x, point.y, point.z, // Position of the particle
0.0, 0.02, 0.0 // Slight upward motion
)
}
// Spawn a special particle at the next interpolation point
if (blockEntity.pathPoints.isNotEmpty()) {
val nextPoint = blockEntity.pathPoints[(pathIndex + 1) % blockEntity.pathPoints.size]
world.addParticle(
net.minecraft.core.particles.ParticleTypes.END_ROD, // Different effect for next point
nextPoint.x, nextPoint.y, nextPoint.z,
0.0, 0.05, 0.0
)
}
}
}
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.client.render.generic
import com.cobblemon.mod.common.client.entity.GenericBedrockClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.generic.PosableGenericEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
@@ -20,11 +19,10 @@ import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.entity.EntityRenderer
import net.minecraft.client.renderer.entity.EntityRendererProvider
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.resources.ResourceLocation
class GenericBedrockRenderer(context: EntityRendererProvider.Context) : EntityRenderer<GenericBedrockEntity>(context) {
val model = PosableGenericEntityModel()
override fun getTextureLocation(entity: GenericBedrockEntity) = GenericBedrockEntityModelRepository.getTexture(entity.category, (entity.delegate as GenericBedrockClientDelegate))
override fun getTextureLocation(entity: GenericBedrockEntity) = VaryingModelRepository.getTexture(entity.category, (entity.delegate as GenericBedrockClientDelegate))
override fun render(entity: GenericBedrockEntity, yaw: Float, partialTicks: Float, poseStack: PoseStack, buffer: MultiBufferSource, packedLight: Int) {
if (entity.isInvisible) {
return
@@ -32,7 +30,7 @@ class GenericBedrockRenderer(context: EntityRendererProvider.Context) : EntityRe
val state = entity.delegate as GenericBedrockClientDelegate
state.currentAspects = entity.aspects
val model = GenericBedrockEntityModelRepository.getPoser(entity.category, state)
val model = VaryingModelRepository.getPoser(entity.category, state)
this.model.posableModel = model
model.context = this.model.context
this.model.setupEntityTypeContext(entity)
@@ -43,7 +41,7 @@ class GenericBedrockRenderer(context: EntityRendererProvider.Context) : EntityRe
val vertexConsumer = buffer.getBuffer(RenderType.entityCutout(getTextureLocation(entity)))
state.updatePartialTicks(partialTicks)
model.setLayerContext(buffer, state, PokemonModelRepository.getLayers(entity.category, state))
model.setLayerContext(buffer, state, VaryingModelRepository.getLayers(entity.category, state))
this.model.setupAnim(entity, 0f, 0f, entity.tickCount + partialTicks, 0F, 0F)
this.model.renderToBuffer(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.gui
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager
object PCBoxWallpaperRepository {
lateinit var wallpapers: List<ResourceLocation>
var defaultWallpaper = cobblemonResource("textures/gui/pc/pc_screen_overlay.png")
fun findWallpapers(resourceManager: ResourceManager): List<ResourceLocation> {
val wallpapers = mutableListOf<ResourceLocation>(defaultWallpaper)
resourceManager.listResources("textures/gui/pc/wallpaper") { path -> path?.endsWith(".png") == true }.keys.forEach { key ->
wallpapers.add(key)
}
this.wallpapers = wallpapers
return wallpapers
}
}
@@ -9,8 +9,8 @@
package com.cobblemon.mod.common.client.render.item
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
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.entity.PoseType
import com.cobblemon.mod.common.item.PokemonItem
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
@@ -38,7 +38,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
val state = FloatingState()
state.currentAspects = aspects
matrices.pushPose()
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(species.resourceIdentifier, state)
model.context = context
context.put(RenderContext.RENDER_STATE, RenderContext.RenderState.PROFILE)
context.put(RenderContext.SPECIES, species.resourceIdentifier)
@@ -46,7 +46,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
context.put(RenderContext.POSABLE_STATE, state)
state.currentModel = model
val renderLayer = RenderType.entityCutout(PokemonModelRepository.getTexture(species.resourceIdentifier, state))
val renderLayer = RenderType.entityCutout(VaryingModelRepository.getTexture(species.resourceIdentifier, state))
val transformations = positions[mode]!!
@@ -75,7 +75,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
// x = red, y = green, z = blue, w = alpha
val tint = pokemonItem.tint(stack)
model.withLayerContext(vertexConsumers, state, PokemonModelRepository.getLayers(species.resourceIdentifier, state)) {
model.withLayerContext(vertexConsumers, state, VaryingModelRepository.getLayers(species.resourceIdentifier, state)) {
val tintRed = (tint.x * 255).toInt()
val tintGreen = (tint.y * 255).toInt()
val tintBlue = (tint.z * 255).toInt()
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.client.render.layer
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
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.entity.PoseType
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -87,27 +87,21 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
) {
val compoundTag = if (pLeftShoulder) livingEntity.shoulderEntityLeft else livingEntity.shoulderEntityRight
if (compoundTag.isPokemonEntity()) {
matrixStack.pushPose()
livingEntity.level().profiler.push("shoulder_render_".plus(if(pLeftShoulder) "left" else "right"))
val uuid = this.extractUuid(compoundTag)
val cache = playerCache.getOrPut(livingEntity.uuid) { ShoulderCache() }
var shoulderData: ShoulderData? = null
if (pLeftShoulder && cache.lastKnownLeft?.uuid != uuid) {
shoulderData = this.extractData(compoundTag, uuid)
cache.lastKnownLeft = shoulderData
}
else if (!pLeftShoulder && cache.lastKnownRight?.uuid != uuid) {
shoulderData = this.extractData(compoundTag, uuid)
cache.lastKnownRight = shoulderData
}
val shoulderData = this.extractData(compoundTag, uuid)
if (pLeftShoulder) cache.lastKnownLeft = shoulderData else cache.lastKnownRight = shoulderData
if (shoulderData == null){
// Could be null
shoulderData = (if (pLeftShoulder) cache.lastKnownLeft else cache.lastKnownRight) ?: return
// Nothing to do
return
}
matrixStack.pushPose()
var state = FloatingState()
state.currentAspects = shoulderData.aspects
val model = PokemonModelRepository.getPoser(shoulderData.species.resourceIdentifier, state)
val model = VaryingModelRepository.getPoser(shoulderData.species.resourceIdentifier, state)
model.context = context
context.put(RenderContext.SPECIES, shoulderData.species.resourceIdentifier)
context.put(RenderContext.ASPECTS, shoulderData.aspects)
@@ -129,6 +123,7 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
)
matrixStack.scale(scale, scale, scale)
matrixStack.translate(0f, 1.5f, 0f)
state = if (pLeftShoulder && shoulderData != lastRenderedLeft) {
leftState = configureState(state, model, true)
@@ -144,7 +139,7 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
state.updatePartialTicks(partialTicks)
context.put(RenderContext.POSABLE_STATE, state)
state.currentModel = model
val vertexConsumer = buffer.getBuffer(RenderType.entityCutout(PokemonModelRepository.getTexture(shoulderData.species.resourceIdentifier, state)))
val vertexConsumer = buffer.getBuffer(RenderType.entityCutout(VaryingModelRepository.getTexture(shoulderData.species.resourceIdentifier, state)))
val i = LivingEntityRenderer.getOverlayCoords(livingEntity, 0.0f)
model.applyAnimations(
@@ -157,11 +152,16 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
ageInTicks = livingEntity.tickCount.toFloat()
)
model.render(context, matrixStack, vertexConsumer, packedLight, i, -0x1)
model.withLayerContext(buffer, state, PokemonModelRepository.getLayers(shoulderData.species.resourceIdentifier, state)) {
model.withLayerContext(buffer, state, VaryingModelRepository.getLayers(shoulderData.species.resourceIdentifier, state)) {
model.render(context, matrixStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
}
model.setDefault()
matrixStack.popPose()
livingEntity.level().profiler.pop()
}
else {
val entry = playerCache.getOrPut(livingEntity.uuid) { ShoulderCache() }
if (pLeftShoulder) entry.lastKnownLeft = null else entry.lastKnownRight = null
}
}
@@ -217,16 +217,5 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
return Pair(cache.lastKnownLeft, cache.lastKnownRight)
}
/**
* Clears the cache for the respective player.
* This is required to be done whenever a Pokémon changes in any way (form, evolution, etc.)
*
* @param player The player whose cache to clear
*/
@JvmStatic
fun clearCache(player: Player) {
playerCache.remove(player.uuid)
}
}
}
@@ -20,8 +20,10 @@ import java.lang.reflect.Type
* @since October 18th, 2022
*/
class JsonModelAdapter<T : PosableModel>(private val constructor: (Bone) -> T) : InstanceCreator<T> {
var modelPart: Bone? = null
var model: T? = null
companion object {
var modelPart: Bone? = null
var model: PosableModel? = null
}
override fun createInstance(type: Type): T {
val rootBone = modelPart!!
return constructor(rootBone).also {
@@ -71,9 +71,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
@Transient
lateinit var context: RenderContext
/** Whether the renderer that will process this is going to do the weird -1.5 Y offset bullshit that the living entity renderer does. */
@Transient
open var isForLivingEntityRenderer = true
var poses = mutableMapOf<String, Pose>()
@@ -81,6 +78,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
@Transient
lateinit var locatorAccess: LocatorAccess
open var transformedParts = arrayOf<ModelPartTransformation>()
open var portraitScale = 1F
open var portraitTranslation = Vec3(0.0, 0.0, 0.0)
@@ -485,7 +484,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
/** Applies the given pose's [ModelPartTransformation]s to the model, if there is a matching pose. */
fun applyPose(state: PosableState, pose: Pose, intensity: Float) = pose.transformedParts.forEach { it.apply(state, intensity) }
fun applyPose(state: PosableState, pose: Pose, intensity: Float) = (pose.transformedParts + transformedParts).forEach { it.apply(state, intensity) }
/** Gets the first pose of this model that matches the given [PoseType]. */
fun getPose(pose: PoseType) = poses.values.firstOrNull { pose in it.poseTypes }
fun getPose(name: String) = poses[name]
@@ -697,11 +696,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
matrixStack.scale(-1F, -1F, 1F)
}
if (isForLivingEntityRenderer) {
// Standard living entity offset, only God knows why Mojang did this.
matrixStack.translate(0.0, -1.5, 0.0)
}
locatorAccess.update(matrixStack, entity, scale, state.locatorStates, isRoot = true)
}
@@ -10,9 +10,11 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.isBattling
import com.cobblemon.mod.common.util.isDusk
import com.cobblemon.mod.common.util.isStandingOnRedSand
import com.cobblemon.mod.common.util.isStandingOnSand
@@ -31,7 +33,6 @@ import java.lang.reflect.Type
* @since October 18th, 2022
*/
class PoseAdapter(
val poseConditionReader: (JsonObject) -> List<(PosableState) -> Boolean>,
val modelFinder: () -> PosableModel
) : JsonDeserializer<Pose> {
companion object {
@@ -62,7 +63,10 @@ class PoseAdapter(
addCondition("isDusk") { entity, expectedValue -> entity.isDusk() == expectedValue }
conditionsList.addAll(poseConditionReader(json))
val mustBeInBattle = json.get("isBattle")?.asBoolean
if (mustBeInBattle != null) {
conditionsList.add { mustBeInBattle == it.isBattling }
}
if (json.has("conditions")) {
val conditionSet = json.get("conditions").asJsonArray.map { it.asString.asExpressionLike() }
@@ -8,8 +8,10 @@
package com.cobblemon.mod.common.client.render.models.blockbench
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.renderer.RenderType
import com.mojang.blaze3d.vertex.VertexConsumer
@@ -46,6 +48,10 @@ abstract class PosableEntityModel<T : Entity>(
) {
val entity = context.request(RenderContext.ENTITY)
val overlay = getOverlayTexture(entity) ?: packedOverlay
if (entity is LivingEntity) {
// We're using the living entity renderer, ergo we need to apply a Y offset of 24 model units (1.5 blocks) for reasons only God can tell us.
stack.translate(0.0, 1.5, 0.0)
}
posableModel.render(context, stack, buffer, packedLight, overlay, color)
}
@@ -27,8 +27,8 @@ class TexturedModel {
@SerializedName("minecraft:geometry")
val geometry: List<ModelGeometry>? = null
fun create(isForLivingEntityRenderer: Boolean) : LayerDefinition {
return createWithUvOverride(isForLivingEntityRenderer, 0, 0, null, null)
fun create() : LayerDefinition {
return createWithUvOverride(0, 0, null, null)
}
fun resolveParentsFromRoot(boneMap: MutableMap<String, ModelBone>, bone: ModelBone): Set<ModelBone> {
@@ -147,7 +147,7 @@ class TexturedModel {
// return modelBuilder.build()
// }
fun createWithUvOverride(isForLivingEntityRenderer: Boolean, u: Int, v: Int, textureWidth: Int?, textureHeight: Int?) : LayerDefinition {
fun createWithUvOverride(u: Int, v: Int, textureWidth: Int?, textureHeight: Int?) : LayerDefinition {
val modelData = MeshDefinition()
val parts = HashMap<String, PartDefinition>()
val bones = HashMap<String, ModelBone>()
@@ -184,8 +184,7 @@ class TexturedModel {
val modelTransform : PartPose
when {
bone.parent == null -> {
// The root part always has a 24 Y offset. One of life's great mysteries.
modelTransform = PartPose.offset(0F, if (isForLivingEntityRenderer) 24F else 0F, 0F)
modelTransform = PartPose.offset(0F, 0F, 0F)
}
boneRotation != null -> {
modelTransform = PartPose.offsetAndRotation(

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