Compare commits

..
Author SHA1 Message Date
apion 263bccca4b idk whats up here but I wanna swap off this branch so 2025-02-07 17:49:10 -05:00
apion 1d94adface Fabric now boots 2025-01-26 03:02:57 -05:00
apion c741ddbc2e Comments 2025-01-25 04:23:40 -05:00
apion 8cad44ffb1 NEOFORGE IS BOOTING INTO A WORLD!!! 2025-01-25 04:05:34 -05:00
apion dec0376d11 More MDG testing 2025-01-18 15:46:50 -05:00
apion 007b2a49f4 More ModDevGradle porting 2025-01-18 01:19:26 -05:00
apion 082683723d Initial moddevgradle port 2025-01-17 21:31:25 -05:00
2717 changed files with 122743 additions and 224807 deletions

No files matched your search

+41 -198
View File
@@ -1,177 +1,52 @@
# Changelog
## [1.7.0 (Month xth, 2025)](#1-7-0)
## [1.6.1 (Release TBD)](#1-6-1)
### 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.
### 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.
### 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.
### Additions
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
- Added the Model Appendages system, allowing for resolver-based bedrock model attachments to Pokémon models based on Locators.
- View the `egg` examples in `assets/cobblemon/bedrock/appendage` for an example of how to use this system.
## [1.6.1 (January 26th, 2025)](#1-6-1)
### Additions
- Added crossover paintings from Close Combat: Premonition, Altar, Slumber, and Nomad.
- Added Galarica Nuts, used for crafting Galarica Cuffs and Wreaths. Dropped from certain Pokémon. Take a wild guess which.
- Added compatibility with Repurposed Structures. (Thank you, TelepathicGrunt!)
- Added an evolution method for Karrablast to evolve into Escavalier in singleplayer.
- Pokédexes can now be placed in Chiseled Bookshelves.
- Added optional box argument to the /pc command.
- Pokédex and Dialogue screens now close when the inventory keybind is pressed.
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added the `is_standing_on_blocks(depth, blocks...)` MoLang function, which returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added debug renderer for posable entity locators.
- Added crossover paintings from Close Combat, Premonition, Altar, Slumber, and Nomad.
- Added optional box argument to the /pc command
- Added molang particle queries for getting distance to targeted entities.
- Added debug renderer for poseable entity locators.
- Added `baseScale` property to NPCs.
- Added the player to `PokerodReelEvent`.
- Pokédex and Dialogue API screens now close when the inventory keybind is pressed.
- Added Galarica Nuts, used for crafting Galarica Cuffs and Wreaths. Dropped from certain Pokémon.
- Added specific compatibility with Repurposed Structures. (Thank you, TelepathicGrunt!)
### Changes
- Pokémon will now be dynamically revealed to the Pokédex as they're seen instead of revealing entire parties at the end of battle regardless.
- Unseen wild Pokémon will update their name from '???' to their real name as soon as a battle starts to reflect the battle UI showing the actual species name.
- Pokémon under the illusion effect will reveal their disguise to the Pokédex first and then the base Pokémon once the disguise is broken.
- Added more support for a variety of Fabric/NeoForge Convention tags.
- Reformatted some tags to be more consistent.
- Edited some recipes to utilize tags instead of direct item ids, for greater mod compatibility.
- Berries will drop if broken at age 0
- Improved Fortune drops on Mint Seeds
- New Slowpoke shiny texture.
- Updated drops for many Pokémon.
- Completely resynced Pokémon move and stat data based on later games. Learnsets have changed considerably to maximise available moves.
- Cobblemon save data now saves .old files where applicable as a means to recover from file corruption due to crashes or similar abrupt stops
### Pokémon Added
#### Gen 5
- Ducklett
- Swanna
- Shelmet
- Accelgor
- Karrablast
- Escavalier
- Rufflet
- Braviary
- Foongus
- Amoonguss
#### Gen 6
- Binacle
- Barbaracle
#### Gen 7
- Dewpider
- Araquanid
- Alolan Geodude
- Alolan Graveler
- Alolan Golem
#### Gen 8
- Galarian Slowpoke
- Galarian Slowbro
- Galarian Slowking
#### Gen 9
- Paldean Tauros
### Added cries to the following Pokémon
- All Nidorans
- Shellder, Cloyster
- Pinsir
- Tyrogue, Hitmontop
- Spinda
### Animation updates for the following Pokémon
- Primeape
- Munchlax
- Snorlax
- Poliwrath
- Goldeen
- Seaking
- Dondozo
- Wobbuffet
- Charcadet
- Armarouge
- Ceruledge
- Geodude
- Graveler
- Golem
- Sandile
- Krokorok
- Krookodile
### Model updates for the following Pokémon
- Slowpoke
- Slowbro
- Slowking
- Eiscue
- Tauros
- Goldeen
- Seaking
- Charcadet
- Armarouge
- Ceruledge
- Pinsir
- Geodude
- Graveler
- Golem
### Cry updates for the following Pokémon
- Sceptile
### Changes
- Completely re-synced Pokémon move and stat data based on later games. Learnsets have changed considerably to maximise available moves.
- Pokémon will now be dynamically revealed to the Pokédex as they're seen in battle instead of revealing entire parties at the end of battle.
- Unseen wild Pokémon will update their name from '???' to their real name as soon as a battle starts to match how the battle UI shows the actual species name.
- Pokémon under the illusion effect will reveal their disguise to the Pokédex first and then the base Pokémon once the disguise is broken.
- Berries will now drop from berry trees if broken at age 0 so you aren't punished for mistaken planting.
- Increased Fortune drops on Mint Seeds.
- Updated Slowpoke's shiny texture.
- Updated drops for many Pokémon.
- Adjusted the evolution sound to match the timing of the particle effect.
- Made berry trees shear-able by dispenser blocks. I'm sure nobody will make unholy contraptions with this.
- Edited some recipes to utilize tags instead of direct item IDs, for better mod compatibility.
- Added more support for a variety of Fabric/NeoForge conventional tags.
- Reformatted some tags to be more consistent.
### Fixes
- Fixed Pokédex sometimes crashing when switching forms.
- Fixed Pokédex interface not transitioning out when closed.
- Fixed texture dimensions for the player and Pokémon interact interface.
- Fixed crash related to Tom's Simple Storage mod and the Fossil Machine.
- Fixed texture dimensions for the interact interface.
- Fixed crash related to Tom's Simple Storage mod and Fossil Machine.
- Fixed not being able to retrieve a fossil from the Fossil Machine with an empty hand.
- Fixed Pokémon being collidable (collidible? collissionable? kaleidoscopable?) while being captured by a Poké Ball.
- Fixed Pokémon being collidable while being captured by a Poké Ball.
- Fixed `full_party`, `own_zangoose_seviper`, `use_revive` and `use_candy` Advancement triggers.
- Fixed `healing_machine` Advancement by using the correct 1.21 trigger.
- Fix Display Cases not dropping items if destroyed through explosions.
- Fixed an issue where the first Pokémon in the pastured Pokémon list clipped into the interface.
- Fixed all Pokémon facing South on spawn.
- Fixed bait being consumed even when not reeling in any Pokémon.
- Parametric particle motion now works.
- Event spawned particles now work.
- Particles can now have independent coordinate spaces.
- Fixed bait being consumed when not reeling in any Pokémon.
- Fixed Miltank milk magically disappearing out of your bucket.
- Fixed Pokémon nicknames migrating from 1.5.2 not being displayed properly.
- Fixed capitalization in one of our config options. It was a very important fix. Very important. Old configs are fine.
- Fixed Poké Rods not working if Lure or Luck of the Sea enchantments get removed by other mods.
- Fixed crashes related to Pokémon when they are ready to evolve while holding an enchanted item. Very specific.
- Fixed a crash that sometimes occurred when evolving Nincada.
- Fixed Cobblemon plants not being compostable on NeoForge.
- Fixed capitalization in one of our config fields, but allowing typo'd version to still be valid.
- Fixed Poké Rods not working if lure or luck of the sea enchantments get removed by other mods.
- Fixed crashes related to Pokémon when they are ready to evolve while holding an enchanted item.
- Fixed crashes that sometimes occur when evolving Nincada.
- Fixed plants not being compostible on NeoForge.
- Fixed hide UI (F1 key) not hiding the party overlay.
- Fixed NPC MoLang command `player_lose_command` not working.
- Fixed misaligned tooltips with edit boxes in the NPC editor screen.
- Corrected misaligned tooltips with editboxes in the NPC editor screen.
- Fixed Pokémon riding two boats when attempting to deploy a platform on water.
- Fixed the summary screen showing there's experience to reach the next level when they are at the level cap.
- Made berry trees be shearable by dispenser blocks.
- Fixed the summary screen showing there's experience to reach the next level when at level cap.
- Fixed Pokémon forgetting moves when evolving on specific cases.
- Fixed Adorn compatibility, including improvements when using JEI/REI (Apricorn items now show up under the collapsed entries rather than standalone).
- Fixed error message appearing on battle log when using Solar Beam with Sunny Day.
@@ -179,68 +54,35 @@
- Fixed `/pokedex grant all` command not giving male/female/shininess completion for some Pokémon.
- Fixed `/pokedex grant only` and `/pokedex remove only` not respecting the form parameter passed.
- Fixed variant forms appearing incorrectly in the Pokédex when the normal form had not been unlocked.
- Fixed Pokémon occasionally being shot into the sky during battle. No Pokémon were harmed by this bug, probably.
- Fixed Pokémon occasionally being shot into the sky during battle.
- Fixed NPC editing GUI not updating aspects until a game restart.
- Fixed some users being unable to open their PC if a Pokémon in it had a lot of PP raises beyond normal bounds. How did you get those, anyway? Tell me or the Bellossom gets it.
- Fixed some color variants (Dubwool, Conkeldurr and Undyed wooloo) being missing in the Pokédex.
- Fixed invalid species or held items causing Players to not be able to load into their world anymore (commonly happening after removing addons/mods).
- Fix some users being unable to open their PC if a Pokémon in said PC had a lot of pp raises beyond normal bounds
- Fix some color variants (dubwool, conkeldurr and undyed wooloo) missing in the Pokédex
- Fix invalid species or held_items causing Players to not be able to load into their world anymore (commonly happening after removing addons/mods)
- Fixed Wooloo variants not being automatically registered in the owner's Pokédex when dyed.
- Fixed Vivichoke Dip and Leek & Potato Stew not returning a bowl upon consumption.
- Fixed Fossil Restoration Tank not accepting Hay Bales as organic material.
- Fixed Potion items applying double their intended healing value.
- Fixed Fast Ball capture bonuses applying to all Pokémon, making it the Best Ball instead of the Mediocre Edge-Case Ball.
- Fixed "learned new move" messages appearing for already-known moves on Pokémon evolutions.
- Fixed Pokémon Item Models breaking shadows nearby when being placed in Display Cases or Item Frames.
- Fixed berries not giving bonus yields when planted in their preferred biomes. I'm sure we've fixed that 5 times now.
- Fixed the NeoForge version not supporting "SodiumDynamicLights".
- Fixed players disconnecting from servers if they made changes to certain config options.
- Fixed players with shouldered Pokémon not being able to rejoin their 1.5.2 worlds using 1.6.
- Fixed `PokemonProperties` utilizing `ability=<some ability>` being treated as a forced ability even when it is a legal ability for the Pokémon.
- Fixed type formatting in Pokédex scanner mode when dual types require two lines.
- Fixed trading sometimes crashing the game or server.
- Fixed Wild shiny sounds not respecting the `shinyNoticeParticlesDistance` config setting.
- Fixed Pokémon being able to evolve mid-battle.
- Fixed NPC held items being able to be stolen by players. Don't be a thief!
- Fixed evolutions that require a held item consuming it as soon as meeting requirements when it should only be consumed upon evolution.
- Fixed Pokémon showing only the default form when selecting them as a target in battle.
- Fixed a possible error coming out of reeling fishing rods in specific situations.
- Fixed incorrect weights being used when Poké Fishing with Luck of the Sea.
- Parametric particle motion now works.
- Event-spawned particles now work.
- Particles can now have independent coordinate spaces.
- Fixed Vivichoke Dip and Leek and Potato Stew not returning a bowl upon consumption.
- Fixed Fossil Restoration Machine not accepting Hay Bales.
- Fixed Potion items so that they no longer apply over double their intended healing value.
### Developer
- Updated the Pokédex data updated events to always include a `Pokemon` instance, and optionally a `DisguiseData` instance.
- Updated fields in `SpawnNPCPacket` and `SpawnPokemonPacket` to be visible and mutable.
- Updated `UnvalidatedPlaySoundS2CPacket` to be public instead of internal and made its fields mutable.
- Added `hideNameTag` field and `HideNPCNameTag` nbt tag to `NPCEntity` to allow hiding the name tag of the NPC.
- Added the player to `PokerodReelEvent` so you know who is doing the reeling.
### MoLang & Datapacks
### Molang & Datapacks
- Added flows for:
- `forme_change`: Triggered when a Pokémon changes form in battle.
- `mega_evolution`: Triggered when a Pokémon mega evolves in battle. (Note: Third-party mods are required for this feature currently)
- `zpower_used`: Triggered when a Pokémon uses a Z-Power move in battle. (Note: Third-party mods are required for this feature currently)
- `terastallization`: Triggered when a Pokémon terastallizes in battle. (Note: Third-party mods are required for this feature currently)
- `mega_evolution`: Triggered when a Pokémon mega evolves in battle. (Note: Third party mods are required for this feature currently)
- `zpower_used`: Triggered when a Pokémon uses a Z-Power move in battle. (Note: Third party mods are required for this feature currently)
- `terastallization`: Triggered when a Pokémon terastallizes in battle. (Note: Third party mods are required for this feature currently)
- `battle_fainted`: Triggered when a Pokémon faints in battle.
- `battle_fled`: Triggered when a player flees from battle.
- `battle_fled`: Triggered when a Player flees from battle.
- `battle_started_pre`: Triggered when a battle starts. Cancelable!
- `battle_started_post`: Triggered when a battle starts.
- `apricorn_harvested`: Triggered when an Apricorn is harvested.
- `apricorn_harvested`: Triggered when an apricorn is harvested.
- `thrown_pokeball_hit`: Triggered when a thrown Pokéball hits a Pokémon.
- `level_up`: Triggered when a Pokémon levels up.
- `pokemon_fainted`: Triggered when a Pokémon faints.
- `pokemon_gained`: Triggered when a player gains a Pokémon.
- Added MoLang functions:
- `pokemon_gained`: Triggered when a Player gains a Pokémon.
- Added molang functions:
- For Pokémon:
- `pokemon.apply(PokemonProperties)`: Applies the given properties to the Pokémon.
- `pokemon.owner`: Returns the owner of the Pokémon or 0.0 if there is no owner or they are not online.
- For all entities:
- `entity.is_standing_on_blocks(depth, blocks...)`: Returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
- Added NPC field:
- `hideNameTag`: Hides the name tag of the NPC.
- Added `baseScale` property to NPCs.
- Added MoLang particle queries for getting distance to targeted entities.
## [1.6.0 - The Record Catch Update (December 25th, 2024)](#1-6-0)
#### "Now that there's a fishing mechanic, the mod is actually good!"
@@ -2143,4 +1985,5 @@ Thank you so much to all of our community translators that bring the mod to the
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
+27
View File
@@ -0,0 +1,27 @@
plugins {
id("java-gradle-plugin")
kotlin("jvm") version "2.1.0"
}
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
}
dependencies {
implementation("net.neoforged:moddev-gradle:2.0.75")
implementation("dev.architectury:at:1.0.1")
implementation("net.fabricmc:access-widener:2.1.0")
}
gradlePlugin {
plugins {
create("cobblemongradle") {
id = "com.cobblemon.gradle"
implementationClass = "com.cobblemon.gradle.CobblemonGradle"
version = "1.0.0"
}
}
}
+7
View File
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -0,0 +1,85 @@
package com.cobblemon.gradle
import com.cobblemon.gradle.extension.CobblemonGradleExtension
import com.cobblemon.gradle.extension.tasks.ConvertAccessWidenersTask
import com.cobblemon.gradle.extension.tasks.UnzipAccessWidenersTask
import net.neoforged.nfrtgradle.CreateMinecraftArtifacts
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.LibraryElements
import org.gradle.language.jvm.tasks.ProcessResources
class CobblemonGradle : Plugin<Project> {
override fun apply(project: Project) {
val cobblemonExtension = project.extensions.create(
CobblemonGradleExtension.EXTENSION_NAME,
CobblemonGradleExtension::class.java
)
//This exists because all of
// - com.cobblemon:mod
// - com.cobblemon:mod-resources
// - com.cobblemon:accesswidener
//technically point to the same dependency (the :common project). To pick the proper artifact from that project
//we set attributes in that project and check for them here.
val accessWideners = project.configurations.register("accessWidener") { configuration ->
configuration.isCanBeConsumed = false
configuration.isCanBeResolved = true
configuration.isCanBeDeclared = true
configuration.attributes {
it.attribute(
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements::class.java, LibraryElements.OBJECTS)
)
}
}
val includeRootResources = project.configurations.register("includeRootResources") { configuration ->
configuration.isCanBeConsumed = false
configuration.isCanBeResolved = true
configuration.isCanBeDeclared = true
configuration.attributes {
it.attribute(
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements::class.java, LibraryElements.RESOURCES)
)
}
}
val unzipAwTask = project.tasks.register("unzipAccessWideners", UnzipAccessWidenersTask::class.java) {
it.accessWidenerZips = accessWideners.get()
}
val convertAwTask = project.tasks.register(ConvertAccessWidenersTask.NAME, ConvertAccessWidenersTask::class.java) {
it.enabled = cobblemonExtension.convertAccessWideners
it.dependsOn(unzipAwTask)
it.accessWideners = project.files(unzipAwTask).plus(project.files(cobblemonExtension.additionalAccessWideners))
}
//Add root resources and access wideners to output jar
project.tasks.named("processResources", ProcessResources::class.java) { task ->
task.dependsOn(includeRootResources)
if (cobblemonExtension.convertAccessWideners) {
task.dependsOn(convertAwTask)
task.from(convertAwTask)
}
else {
task.dependsOn(unzipAwTask)
task.from(unzipAwTask)
}
task.doLast {
project.copy {
it.from(project.provider {
includeRootResources.get().resolve().map { project.zipTree(it) }
})
it.into(task.destinationDir)
}
}
}
project.tasks.withType(CreateMinecraftArtifacts::class.java) {
it.dependsOn(convertAwTask)
it.accessTransformers.from(convertAwTask)
}
}
}
@@ -0,0 +1,12 @@
package com.cobblemon.gradle.extension
import org.gradle.api.file.FileCollection
abstract class CobblemonGradleExtension {
var additionalAccessWideners: FileCollection? = null
var convertAccessWideners = true
companion object {
const val EXTENSION_NAME = "cobblemon"
}
}
@@ -0,0 +1,82 @@
package com.cobblemon.gradle.extension.tasks
import dev.architectury.at.AccessChange
import dev.architectury.at.AccessTransform
import dev.architectury.at.AccessTransformSet
import dev.architectury.at.ModifierChange
import dev.architectury.at.io.AccessTransformFormats
import net.fabricmc.accesswidener.AccessWidenerReader
import net.fabricmc.accesswidener.AccessWidenerVisitor
import org.cadixdev.bombe.type.signature.MethodSignature
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
import java.io.Writer
import java.nio.file.Files
abstract class ConvertAccessWidenersTask : DefaultTask() {
abstract var accessWideners: FileCollection
@InputFiles get
@Internal
val accessTransformerDir = project.layout.buildDirectory.dir("access-transformers")
@OutputFiles
val accessTransformers = project.files ({
accessWideners.files.map {
accessTransformerDir.get().file(it.name.replace("accesswidener", "accesstransformer.cfg"))
}
})
@TaskAction
fun convertAccessWideners() {
accessWideners.files.forEach { file ->
val awFile = Files.newBufferedReader(file.toPath())
val result = AccessTransformSet.create();
AccessWidenerReader(object : AccessWidenerVisitor {
override fun visitClass(name: String, access: AccessWidenerReader.AccessType, transitive: Boolean) {
result.getOrCreateClass(name).merge(toAt(access));
}
override fun visitMethod(owner: String, name: String, descriptor: String, access: AccessWidenerReader.AccessType, transitive: Boolean) {
result.getOrCreateClass(owner).mergeMethod(MethodSignature.of(name, descriptor), toAt(access));
}
override fun visitField(owner: String, name: String, descriptor: String, access: AccessWidenerReader.AccessType, transitive: Boolean) {
result.getOrCreateClass(owner).mergeField(name, toAt(access));
}
}).read(awFile)
val outputFile = accessTransformerDir.get().file(
file.name.replace("accesswidener", "accesstransformer.cfg")
)
var outputWriter: Writer? = null
try {
outputWriter = outputFile.asFile.writer()
AccessTransformFormats.FML.write(outputWriter, result)
}
finally {
outputWriter?.flush()
outputWriter?.close()
}
}
}
private fun toAt(access: AccessWidenerReader.AccessType): AccessTransform {
return when (access) {
AccessWidenerReader.AccessType.ACCESSIBLE -> AccessTransform.of(AccessChange.PUBLIC)
AccessWidenerReader.AccessType.EXTENDABLE, AccessWidenerReader.AccessType.MUTABLE -> AccessTransform.of(
AccessChange.PUBLIC,
ModifierChange.REMOVE
)
}
}
companion object {
const val NAME = "convertAccessWideners"
}
}
@@ -0,0 +1,33 @@
package com.cobblemon.gradle.extension.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
abstract class UnzipAccessWidenersTask : DefaultTask() {
abstract var accessWidenerZips: FileCollection
@InputFiles get
@OutputFiles
val outputFiles = project.provider {
project.files(accessWidenerZips.flatMap { zip ->
project.zipTree(zip).files.map { OUTPUT_DIR.get().file(it.name) }
})
}
@Internal
val OUTPUT_DIR = project.layout.buildDirectory.dir("cobblemon-working/access-wideners")
@TaskAction
fun run() {
project.copy {
it.from(accessWidenerZips.map(project::zipTree))
it.into(OUTPUT_DIR)
}
}
}
+3 -1
View File
@@ -1,12 +1,14 @@
plugins {
id("cobblemon.root-conventions")
//id("cobblemon.root-conventions")
id ("net.nemerosa.versioning") version "3.1.0"
}
version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
/*
if (isSnapshot) {
val fixedBranchName = versioning.info.branch.substringAfter("/")
version = "$version-${fixedBranchName}-${versioning.info.build}"
}
*/
+90 -54
View File
@@ -1,68 +1,102 @@
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import utilities.isSnapshot
import utilities.version
/*
*
* * 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/.
*
*/
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("net.nemerosa.versioning") version "3.1.0"
kotlin("jvm") version "2.1.0"
id ("net.neoforged.moddev") version "2.0.75"
id("net.kyori.blossom") version "2.1.0"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.cobblemon.gradle")
}
architectury {
common("neoforge", "fabric")
cobblemon {
additionalAccessWideners = files(sourceSets.main.get().resources).filter { it.extension == "accesswidener" }
}
val rootResources = configurations.create("rootResources") {
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.RESOURCES))
}
}
val accessWidenerExport = configurations.create("accessWidenerExport") {
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.OBJECTS))
}
}
tasks.processResources {
exclude("*.accesswidener")
}
tasks.create<Zip>("zipResources") {
dependsOn("processResources")
from(tasks.processResources.get().destinationDir)
this.archiveBaseName = "mod-resources"
}
tasks.create<Zip>("zipAccessWideners") {
from(sourceSets.main.get().resources)
include("*.accesswidener")
this.archiveBaseName = "mod-accesswideners"
}
tasks.jar {
archiveBaseName = "mod"
manifest {
attributes(mapOf("FMLModType" to "GAMELIBRARY"))
}
exclude {
it.file in tasks.processResources.get().outputs.files
}
}
tasks.assemble {
dependsOn(tasks.named("zipResources"))
dependsOn(tasks.named("zipAccessWideners"))
}
artifacts {
add("rootResources", tasks.getByName<Zip>("zipResources"))
add("accessWidenerExport", tasks.getByName("zipAccessWideners"))
}
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
}
}
repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
mavenLocal()
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://maven.blamejared.com/")
maven("https://api.modrinth.com/maven")
maven(url = "../deps")
}
dependencies {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
modApi(libs.molang)
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
compileOnly(libs.bundles.mongo)
compileOnly(libs.bundles.kotlin)
compileOnly(libs.molang)
compileOnly(libs.graal)
compileOnly(libs.jei.api)
compileOnly(libs.bundles.fabric.integrations.compileOnly) {
isTransitive = false
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
// Showdown
modCompileOnly(libs.graal)
// Data Storage
modCompileOnly(libs.bundles.mongo)
// Unit Testing
compileOnly("net.fabricmc:sponge-mixin:0.15.5+mixin.0.8.7")
compileOnly("io.github.llamalad7:mixinextras-fabric:0.4.1")
testImplementation(libs.bundles.unitTesting)
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
setEvents(listOf("failed"))
setExceptionFormat("full")
}
neoForge {
neoFormVersion = "1.21.1-20240808.144430"
interfaceInjectionData.from("src/main/resources/interfaces.json")
validateAccessTransformers = true
}
sourceSets {
@@ -72,7 +106,7 @@ sourceSets {
fun generateLicenseHeader() : String {
val builder = StringBuilder()
builder.append("/*\n")
rootProject.file("HEADER").forEachLine {
project.projectDir.parentFile.resolve("HEADER").forEachLine {
if(it.isEmpty()) {
builder.append(" *").append("\n")
} else {
@@ -85,12 +119,14 @@ sourceSets {
property("license", generateLicenseHeader())
property("modid", "cobblemon")
property("version", project.version())
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
property("version", project.version as String)
property("isSnapshot", "true")
/*
property("gitCommit", versioning.info.commit)
property("branch", versioning.info.branch)
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
*/
}
}
}
+97
View File
@@ -0,0 +1,97 @@
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import utilities.isSnapshot
import utilities.version
/*
*
* * 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/.
*
*/
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
id("net.nemerosa.versioning") version "3.1.0"
}
architectury {
common("neoforge", "fabric")
}
repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
mavenLocal()
}
dependencies {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
modApi(libs.molang)
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
isTransitive = false
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
// Showdown
modCompileOnly(libs.graal)
// Data Storage
modCompileOnly(libs.bundles.mongo)
// Unit Testing
testImplementation(libs.bundles.unitTesting)
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
setEvents(listOf("failed"))
setExceptionFormat("full")
}
}
sourceSets {
main {
blossom {
kotlinSources {
fun generateLicenseHeader() : String {
val builder = StringBuilder()
builder.append("/*\n")
rootProject.file("HEADER").forEachLine {
if(it.isEmpty()) {
builder.append(" *").append("\n")
} else {
builder.append(" * ").append(it).append("\n")
}
}
return builder.append(" */").append("\n").toString()
}
property("license", generateLicenseHeader())
property("modid", "cobblemon")
property("version", project.version())
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
property("gitCommit", versioning.info.commit)
property("branch", versioning.info.branch)
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
}
}
}
}
+95
View File
@@ -0,0 +1,95 @@
[versions]
# Kotlin
kotlin = "2.0.0"
# Plugins
licenser = "0.6.1"
shadow = "8.1.1"
blossom = "2.1.0"
idea-ext = "1.1.7"
# Loom
loom = "1.7-SNAPSHOT"
architectury-plugin = "3.4-SNAPSHOT"
# Common
molang = "1.1.18"
graal = "22.3.0"
icu4j = "71.1"
jei-api = "19.8.2.99"
# Fabric
fabric-loader = "0.16.5"
fabric-api = "0.104.0+1.21.1"
fabric-kotlin = "1.11.0+kotlin.2.0.0"
fabric-permissions-api = "0.2-SNAPSHOT"
lambDynamicLights = "3.0.1+1.21.1"
debugutils-fabric = "1.21-1.0.6-fabric"
# NeoForge
neoforge = "21.1.66"
neo-kotlin-for-forge = "5.5.0"
debugutils-forge = "1.21-1.0.6-neoforge"
# Data Storage
mongo = "4.10.2"
# Testing
junit = "5.9.0"
mockito = "5.2.0"
mockk = "1.12.1"
classgraph = "4.8.153"
[libraries]
# General Buildscript Plugins
kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
loom = { group = "dev.architectury", name = "architectury-loom", version.ref = "loom" }
architectury = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" }
shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
blossom = { group = "net.kyori", name = "blossom", version.ref = "blossom" }
ideaExt = { group = "org.jetbrains.gradle.plugin.idea-ext", name = "org.jetbrains.gradle.plugin.idea-ext.gradle.plugin", version.ref = "idea-ext"}
licenser = { group = "gradle.plugin.org.cadixdev.gradle", name = "licenser", version.ref = "licenser" }
# Kotlin
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" }
# Common
jei-api = { group = "mezz.jei", name = "jei-1.21-common-api", version.ref = "jei-api"}
molang = { group = "local.com.bedrockk", name = "molang", version.ref = "molang" }
graal = { group = "org.graalvm.js", name = "js", version.ref = "graal" }
icu4j = { group = "com.ibm.icu", name = "icu4j", version.ref = "icu4j" }
# Fabric
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
fabric-kotlin = { group = "net.fabricmc", name = "fabric-language-kotlin", version.ref = "fabric-kotlin" }
fabric-permissions = { group = "me.lucko", name = "fabric-permissions-api", version.ref = "fabric-permissions-api" }
adorn-fabric = { group = "maven.modrinth", name = "adorn", version = "6.0.0-neoforge"}
lambDynamicLights = { group = "maven.modrinth", name = "lambdynamiclights", version.ref = "lambDynamicLights"}
jei-api-fabric = { group = "mezz.jei", name = "jei-1.21-fabric-api", version.ref = "jei-api" }
jei-fabric = { group = "mezz.jei", name = "jei-1.21-fabric", version.ref = "jei-api"}
fabric-debugutils = { group = "maven.modrinth", name = "debugutils", version.ref = "debugutils-fabric"}
# NeoForge
neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
neo-kotlin-forge = { group = "thedarkcolour", name = "kotlinforforge-neoforge", version.ref = "neo-kotlin-for-forge" }
neoforge-debugutils = { group = "maven.modrinth", name = "debugutils", version.ref = "debugutils-forge"}
# Data Storage
mongo-driver-core = { group = "org.mongodb", name = "mongodb-driver-core", version.ref = "mongo" }
mongo-driver-sync = { group = "org.mongodb", name = "mongodb-driver-sync", version.ref = "mongo" }
# Testing
junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" }
mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
mock-kotlin = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
classgraph = { group = "io.github.classgraph", name = "classgraph", version.ref = "classgraph" }
[bundles]
kotlin = [ "kotlin-stdlib", "kotlin-reflect" ]
fabric = [ "fabric-kotlin", "fabric-permissions" ]
fabric-integrations-compileOnly = [ "adorn-fabric", "lambDynamicLights", "jei-api-fabric" ]
fabric-integrations-runtimeOnly = [ "jei-fabric" ]
mongo = [ "mongo-driver-core", "mongo-driver-sync" ]
unitTesting = [ "junit", "mockito-core", "mock-kotlin", "classgraph" ]
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
@@ -179,10 +178,10 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
@Override @Nullable
public PokedexEntityData resolvePokemonScan() {
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityRight())){
if(CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityRight())){
return getDataFromShoulderPokemon(this.getShoulderEntityRight());
}
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityLeft())){
if(CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityLeft())){
return getDataFromShoulderPokemon(this.getShoulderEntityLeft());
}
return null;
@@ -191,34 +190,30 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
@Nullable @Unique
private PokedexEntityData getDataFromShoulderPokemon(CompoundTag shoulderTag) {
CompoundTag pokemonTag = shoulderTag.getCompound(DataKeys.POKEMON);
if (pokemonTag.isEmpty()) return null;
if(pokemonTag.isEmpty()) return null;
Species species = PokemonSpecies.INSTANCE.getByIdentifier(ResourceLocation.parse(pokemonTag.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER)));
if (species == null) return null;
if(species == null) return null;
String formId = pokemonTag.getString(DataKeys.POKEMON_FORM_ID);
FormData form = species.getStandardForm();
List<FormData> formList = species.getForms().stream().filter(it -> it.formOnlyShowdownId().equals(formId)).toList();
if (!formList.isEmpty()) form = formList.getFirst();
if (form == null) return null;
if(!formList.isEmpty()) form = formList.getFirst();
if(form == null) return null;
String genderString = pokemonTag.getString(DataKeys.POKEMON_GENDER);
if (genderString.isEmpty()) return null;
if(genderString.isEmpty()) return null;
Gender gender = Gender.valueOf(genderString);
boolean shiny = pokemonTag.getBoolean(DataKeys.POKEMON_SHINY);
int level = pokemonTag.getInt(DataKeys.POKEMON_LEVEL);
Set<String> aspects = shoulderTag.getList(DataKeys.SHOULDER_ASPECTS, Tag.TAG_STRING).stream().map(Tag::getAsString).collect(Collectors.toSet());
Pokemon pokemon = new Pokemon();
if (level().isClientSide) {
pokemon.setSpecies(species);
pokemon.setForm(form);
pokemon.setGender(gender);
pokemon.setShiny(shiny);
pokemon.setLevel(level);
pokemon.setForcedAspects(aspects);
} else {
PlayerPartyStore party = Cobblemon.INSTANCE.getStorage().getParty(this.getUUID(), this.registryAccess());
pokemon = party.get(shoulderTag.getUUID(DataKeys.SHOULDER_UUID));
}
return new PokedexEntityData(pokemon, null);
return new PokedexEntityData(
species,
form,
gender,
aspects,
shiny,
level,
this.getUUID()
);
}
@Override
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.SeasonResolver
import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.drop.CommandDropEntry
import com.cobblemon.mod.common.api.drop.DropEntry
import com.cobblemon.mod.common.api.drop.EvolutionItemDropEntry
import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
@@ -62,7 +61,11 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.client.CobblemonPack
@@ -85,11 +88,22 @@ 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.*
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.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -97,26 +111,25 @@ 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.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
@@ -187,7 +200,6 @@ object Cobblemon {
DropEntry.register("command", CommandDropEntry::class.java)
DropEntry.register("item", ItemDropEntry::class.java, isDefault = true)
DropEntry.register("evolution", EvolutionItemDropEntry::class.java)
ExperienceGroups.registerDefaults()
CaptureCalculators.registerDefaults()
@@ -206,7 +218,7 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
sendServerSettingsPacketToPlayer(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel, this.config.maxPokemonLevel).sendToPlayer(it)
}
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
PCLinkManager.removeLink(it.player.uuid)
@@ -244,8 +256,6 @@ object Cobblemon {
// Lowest priority because this applies after luxury ball bonus as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(Priority.LOWEST) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
if (increment <= 0) //these affects are only meant to affect positive gains
return@subscribe
// Our Luxury ball spec is diff from official, but we will still assume these stack
if (event.pokemon.heldItemNoCopy().`is`(CobblemonItemTags.IS_FRIENDSHIP_BOOSTER)) {
increment += increment * 0.5F
@@ -254,17 +264,8 @@ 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() {
@@ -424,23 +425,7 @@ 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()
@@ -483,6 +468,12 @@ object Cobblemon {
} else {
this.config = CobblemonConfig()
}
config.lastSavedVersion = VERSION
this.saveConfig()
bestSpawner.loadConfig()
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadStarterConfig(): StarterConfig {
@@ -505,14 +496,12 @@ object Cobblemon {
}
}
fun saveConfig(config: CobblemonConfig) {
config.lastSavedVersion = VERSION
fun saveConfig() {
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(config, fileWriter)
CobblemonConfig.GSON.toJson(this.config, fileWriter)
fileWriter.flush()
fileWriter.close()
} catch (exception: Exception) {
@@ -534,4 +523,4 @@ object Cobblemon {
this.implementation.registerCommandArgument(cobblemonResource("npc_class"), NPCClassArgumentType::class, SingletonArgumentInfo.contextFree(NPCClassArgumentType::npcClass))
}
}
}
@@ -57,7 +57,6 @@ object CobblemonCommands {
FreezePokemonCommand.register(dispatcher)
ApplyPlayerTextureCommand.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
@@ -317,7 +317,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
@JvmField val SHELL_HELMET = heldItem("shell_helmet")
private val berries = mutableMapOf<ResourceLocation, BerryItem>()
// Plants
@@ -34,7 +34,6 @@ 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
@@ -81,7 +80,6 @@ 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
@@ -286,7 +284,6 @@ object CobblemonNetwork {
// 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()))
@@ -43,8 +43,7 @@ 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,7 +23,6 @@ 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
@@ -273,7 +272,7 @@ open class PokemonBattle(
opponent.awardExperience(opponentPokemon, experience)
}
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
pokemon.evs.add(stat, amount, BattleEvSource(this, opponentPokemon.facedOpponents.toList(), pokemon))
pokemon.evs.add(stat, amount)
}
}
@@ -55,7 +55,6 @@ class ActiveDialogue(var playerEntity: ServerPlayer, var dialogueReference: Dial
)
dialogueReference.initializationAction.invoke(this)
setPage(currentPage)
if (!completion.isDone) {
val packet = DialogueOpenedPacket(this, true)
@@ -52,6 +52,6 @@ object DialogueManager {
fun stopDialogue(playerEntity: ServerPlayer) {
val activeDialogue = playerEntity.activeDialogue ?: return
DialogueClosedPacket(activeDialogue.dialogueId).sendToPlayer(playerEntity)
activeDialogues.remove(playerEntity.uuid)
activeDialogues.remove(activeDialogue.dialogueId)
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
@@ -41,23 +40,17 @@ interface DropEntry {
* to be dropped.
*/
val percentage: Float
/**
* The drop's quantity when doing selection from the [DropTable]. See the drop table documentation for more on how
* this works.
*/
val quantity: Int
/**
* The maximum number of times the item can be selected drop the [DropTable]. In most cases this will just be 1,
* meaning that the item, once picked, cannot be picked again. A larger value will allow the same [DropEntry] from
* appearing multiple times in a drop action.
*/
val maxSelectableTimes: Int
/** Whether this is considered a possible drop at the moment or not */
fun canDrop(pokemon: Pokemon?): Boolean = true
/** The logic to use to actually drop the thing. */
fun drop(entity: LivingEntity?, world: ServerLevel, pos: Vec3, player: ServerPlayer?)
}
@@ -11,13 +11,11 @@ package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import kotlin.random.Random
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
/**
@@ -47,11 +45,9 @@ class DropTable {
* Gets a drop list from this table as a list of [DropEntry]. You can specify the range of drop quantity totals
* that will be possible, or if you leave it blank it will use [DropTable.amount].
*/
fun getDrops(amount: IntRange = this.amount, pokemon: Pokemon? = null): List<DropEntry> {
fun getDrops(amount: IntRange = this.amount): List<DropEntry> {
val chosenAmount = amount.random()
val possibleDrops = entries.filter {
it.quantity <= chosenAmount && it.canDrop(pokemon)
}.toMutableList()
val possibleDrops = entries.filter { it.quantity <= chosenAmount }.toMutableList()
if (possibleDrops.isEmpty()) {
return emptyList()
@@ -91,10 +87,11 @@ class DropTable {
world: ServerLevel,
pos: Vec3,
player: ServerPlayer?,
amount: IntRange = this.amount,
pokemon: Pokemon? = null,
amount: IntRange = this.amount
) {
val drops = getDrops(amount, pokemon).toMutableList()
val drops = getDrops(amount).toMutableList()
val heldItem = (entity as PokemonEntity).pokemon.heldItemNoCopy()
if (!heldItem.isEmpty) entity.spawnAtLocation(heldItem.item)
LOOT_DROPPED.postThen(
event = LootDroppedEvent(this, player, entity, drops),
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
@@ -1,18 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.pokemon.Pokemon
class EvolutionItemDropEntry : ItemDropEntry() {
val requirements: MutableSet<EvolutionRequirement> = mutableSetOf()
override fun canDrop(pokemon: Pokemon?): Boolean = pokemon != null && requirements.all { it.check(pokemon) }
}
@@ -9,7 +9,6 @@
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.
@@ -29,9 +28,6 @@ enum class ItemDropMethod(val methodName: String) {
TO_INVENTORY("to-inventory");
companion object {
val adapter = StringIdentifiedObjectAdapter(
{ str -> ItemDropMethod.entries.firstOrNull { it.methodName == str } },
{ value -> (value ?: ON_ENTITY).methodName },
)
val adapter = StringIdentifiedObjectAdapter { str -> values().firstOrNull { it.methodName == str } }
}
}
@@ -158,11 +158,6 @@ 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
@@ -18,17 +18,19 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
data class PokemonScannedEvent(val player: ServerPlayer, val scannedPokemonEntityData: PokedexEntityData, val scannedEntity: ScannableEntity) {
val pokedexEntityData: PokedexEntityData
get() = scannedPokemonEntityData
val isOwned: Boolean
get() = scannedPokemonEntityData.pokemon.getOwnerUUID() == player.uuid
get() = scannedPokemonEntityData.ownerUUID == player.uuid
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to scannedPokemonEntityData.pokemon.struct,
"disguise" to (scannedPokemonEntityData.disguise?.struct ?: DoubleValue.ZERO),
"pokemon" to scannedPokemonEntityData.struct,
"entity" to {
when (scannedEntity) {
is LivingEntity -> scannedEntity.asMostSpecificMoLangValue()
else -> DoubleValue.ZERO
else -> DoubleValue(0.0)
}
},
)
@@ -1,43 +0,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
/**
* Fired during EV mutation.
*
* @see [Pre]
* @see [Post]
*/
interface EvGainedEvent {
/**
* The [Stat] having EVs added to itself.
*/
val stat: Stat
/**
* 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
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.pokemon.Pokemon
/**
@@ -19,11 +18,5 @@ import com.cobblemon.mod.common.pokemon.Pokemon
*/
data class FriendshipUpdatedEvent(
val pokemon: Pokemon,
val newFriendshipInitial: Int
)
{
var newFriendship: Int = newFriendshipInitial
set(value) {
field = value.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
}
}
var newFriendship: Int
)
@@ -17,14 +17,16 @@ import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getPlayer
import com.mojang.datafixers.util.Either
import java.util.UUID
/**
* Event that fires when a Pokémon's information is gained or updated in the Pokédex.
*/
sealed interface PokedexDataChangedEvent {
val dataSource: PokedexEntityData
val dataSource: Either<PokedexEntityData, Pokemon>
val knowledge: PokedexEntryProgress
val playerUUID: UUID
val record: FormDexRecord
@@ -35,22 +37,22 @@ sealed interface PokedexDataChangedEvent {
fun getContext(): MutableMap<String, MoValue> {
return mutableMapOf(
"player" to (playerUUID.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"pokemon" to dataSource.pokemon.struct,
"disguise" to (dataSource.disguise?.struct ?: DoubleValue.ZERO),
"pokemon" to dataSource.map({ DoubleValue.ZERO }, { it.struct }),
"data" to dataSource.map({ it.struct }, { DoubleValue.ZERO }),
"knowledge" to StringValue(knowledge.name.lowercase()),
"pokedex" to pokedexManager.struct
)
}
class Pre(
override val dataSource: PokedexEntityData,
override val dataSource: Either<PokedexEntityData, Pokemon>,
override val knowledge: PokedexEntryProgress,
override val playerUUID: UUID,
override val record: FormDexRecord
) : PokedexDataChangedEvent, Cancelable()
class Post(
override val dataSource: PokedexEntityData,
override val dataSource: Either<PokedexEntityData, Pokemon>,
override val knowledge: PokedexEntryProgress,
override val playerUUID: UUID,
override val record: FormDexRecord
@@ -54,15 +54,7 @@ class BenchedMoves : Iterable<BenchedMove> {
}
}
fun add(benchedMove: BenchedMove): Boolean {
if (any { it.moveTemplate == benchedMove.moveTemplate }) {
return false
}
doThenEmit { benchedMoves.add(benchedMove) }
return true
}
fun add(benchedMove: BenchedMove) = doThenEmit { benchedMoves.add(benchedMove) }
fun addAll(benchedMoves: Iterable<BenchedMove>) = doThenEmit { this.benchedMoves.addAll(benchedMoves) }
fun clear() = doThenEmit { benchedMoves.clear() }
fun remove(benchedMove: BenchedMove) = doThenEmit { benchedMoves.remove(benchedMove) }
@@ -134,21 +134,18 @@ class MoveSet : Iterable<Move> {
return json
}
fun add(move: Move): Boolean {
fun add(move: Move) {
if (any { it.template == move.template }) {
return false
return
}
for (i in 0 until MOVE_COUNT) {
if (moves[i] == null) {
moves[i] = move
move.observable.subscribe { this.update() }
update()
return true
return
}
}
return false
}
fun update() {
@@ -57,7 +57,6 @@ class NPCClass {
var isInvulnerable = false
var isLeashable = true
var allowProjectileHits = true
var hideNameTag = false
// If you're adding stuff here, add it to NPCPreset and NPCClassAdapter too
@@ -98,7 +97,6 @@ class NPCClass {
buffer.writeBoolean(isInvulnerable)
buffer.writeBoolean(isLeashable)
buffer.writeBoolean(allowProjectileHits)
buffer.writeBoolean(hideNameTag)
}
fun decode(buffer: RegistryFriendlyByteBuf) {
@@ -150,6 +148,5 @@ class NPCClass {
isInvulnerable = buffer.readBoolean()
isLeashable = buffer.readBoolean()
allowProjectileHits = buffer.readBoolean()
hideNameTag = buffer.readBoolean()
}
}
@@ -37,7 +37,6 @@ class NPCPreset {
var isInvulnerable: Boolean? = null
var isLeashable: Boolean? = null
var allowProjectileHits: Boolean? = null
var hideNameTag: Boolean? = null
fun applyTo(npcClass: NPCClass) {
resourceIdentifier?.let { npcClass.resourceIdentifier = it }
@@ -64,6 +63,5 @@ class NPCPreset {
isInvulnerable?.let { npcClass.isInvulnerable = it }
isLeashable?.let { npcClass.isLeashable = it }
allowProjectileHits?.let { npcClass.allowProjectileHits = it }
hideNameTag?.let { npcClass.hideNameTag = it }
}
}
@@ -78,7 +78,6 @@ any number of presets and the properties of those presets will be merged into th
"skill": 5,
"autoHealParty": true,
"randomizePartyOrder": true,
"hideNameTag": false,
"party": {
"type": "simple",
"pokemon": [
@@ -412,7 +411,4 @@ Pokémon in the party.
### ai
The AI property is an array of brain configurations. This is used to configure the behaviours of the NPC. See
the documentation for brain configurations at [AI Configuration](../ai/config/README.md).
### hideNameTag
Whether the NPC's nametag should be hidden or not. Defaults to false.
the documentation for brain configurations at [AI Configuration](../ai/config/README.md).
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.permission
import com.cobblemon.mod.common.command.AbandonMultiTeam
object CobblemonPermissions {
private const val COMMAND_PREFIX = "command."
@@ -82,8 +84,6 @@ 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 {
@@ -202,8 +202,6 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
if (increment <= 0) //these affects are only meant to affect positive gains
return@subscribe
if (increment <= 1F) {
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
.forEach { increment *= it.multiplier }
@@ -42,10 +42,7 @@ class BaseStatModifier(
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior = CatchRateModifier.Behavior.MULTIPLY
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean {
val valStat = pokemon.form.baseStats[this.stat] ?: return false
return comparator(valStat)
}
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean = pokemon.form.baseStats[this.stat] != null
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
@@ -96,21 +96,15 @@ abstract class AbstractPokedexManager {
}
fun getNewInformation(pokedexEntityData: PokedexEntityData): PokedexLearnedInformation {
val speciesRecord = getSpeciesRecord(pokedexEntityData.getApparentSpecies().resourceIdentifier)
val speciesRecord = getSpeciesRecord(pokedexEntityData.species.resourceIdentifier)
if (speciesRecord == null || speciesRecord.getKnowledge() == PokedexEntryProgress.NONE) {
return PokedexLearnedInformation.SPECIES
}
val formRecord = speciesRecord.getFormRecord(pokedexEntityData.getApparentForm().name)
val formRecord = speciesRecord.getFormRecord(pokedexEntityData.form.name)
if (formRecord == null || formRecord.knowledge == PokedexEntryProgress.NONE) {
return PokedexLearnedInformation.FORM
}
// Can't update aspects on a disguise
if (pokedexEntityData.disguise != null) {
return PokedexLearnedInformation.NONE
}
if (pokedexEntityData.pokemon.aspects.any { !speciesRecord.hasAspect(it) } || pokedexEntityData.pokemon.gender !in formRecord.getGenders() || !formRecord.hasSeenShinyState(pokedexEntityData.pokemon.shiny)) {
if (pokedexEntityData.aspects.any{ !speciesRecord.hasAspect(it) } || pokedexEntityData.gender !in formRecord.getGenders() || !formRecord.hasSeenShinyState(pokedexEntityData.shiny)) {
return PokedexLearnedInformation.VARIATION
}
return PokedexLearnedInformation.NONE
@@ -89,15 +89,21 @@ class FormDexRecord {
it.knowledge = knowledge
}
fun encountered(pokemon: Pokemon) {
if (wouldBeDifferent(pokemon, PokedexEntryProgress.ENCOUNTERED)) {
addInformation(pokemon, PokedexEntryProgress.ENCOUNTERED)
}
}
fun encountered(pokedexEntityData: PokedexEntityData) {
if (wouldBeDifferent(pokedexEntityData, PokedexEntryProgress.ENCOUNTERED)) {
addInformation(pokedexEntityData, PokedexEntryProgress.ENCOUNTERED)
}
}
fun caught(pokedexEntityData: PokedexEntityData) {
if (wouldBeDifferent(pokedexEntityData, PokedexEntryProgress.CAUGHT)) {
addInformation(pokedexEntityData, PokedexEntryProgress.CAUGHT)
fun caught(pokemon: Pokemon) {
if (wouldBeDifferent(pokemon, PokedexEntryProgress.CAUGHT)) {
addInformation(pokemon, PokedexEntryProgress.CAUGHT)
}
}
@@ -119,18 +125,47 @@ class FormDexRecord {
speciesDexRecord.onFormRecordUpdated(this)
}
private fun addInformation(pokemon: Pokemon, knowledge: PokedexEntryProgress) {
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
CobblemonEvents.POKEDEX_DATA_CHANGED_PRE.postThen(
PokedexDataChangedEvent.Pre(
Either.right(pokemon),
knowledge,
pokedexManager.uuid,
this
),
ifSucceeded = {
genders.add(pokemon.gender)
seenShinyStates.add(if (pokemon.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
speciesDexRecord.addInformation(pokemon, knowledge)
speciesDexRecord.onFormRecordUpdated(this)
CobblemonEvents.POKEDEX_DATA_CHANGED_POST.post(
PokedexDataChangedEvent.Post(
Either.right(pokemon),
knowledge,
pokedexManager.uuid,
this
)
)
})
}
}
private fun addInformation(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress) {
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
CobblemonEvents.POKEDEX_DATA_CHANGED_PRE.postThen(
PokedexDataChangedEvent.Pre(
pokedexEntityData,
knowledge,
pokedexManager.uuid,
this
Either.left(pokedexEntityData),
knowledge,
pokedexManager.uuid,
this
),
ifSucceeded = {
genders.add(pokedexEntityData.pokemon.gender)
seenShinyStates.add(if (pokedexEntityData.pokemon.shiny) "shiny" else "normal")
genders.add(pokedexEntityData.gender)
seenShinyStates.add(if (pokedexEntityData.shiny) "shiny" else "normal")
if (knowledge.ordinal > this.knowledge.ordinal) {
this.knowledge = knowledge
}
@@ -138,10 +173,10 @@ class FormDexRecord {
speciesDexRecord.onFormRecordUpdated(this)
CobblemonEvents.POKEDEX_DATA_CHANGED_POST.post(
PokedexDataChangedEvent.Post(
pokedexEntityData,
knowledge,
pokedexManager.uuid,
this
Either.left(pokedexEntityData),
knowledge,
pokedexManager.uuid,
this
)
)
}
@@ -149,10 +184,19 @@ class FormDexRecord {
}
}
/** Returns whether the given [Pokemon] and [knowledge] would add new information to the Pokédex.*/
fun wouldBeDifferent(pokemon: Pokemon, knowledge: PokedexEntryProgress): Boolean {
return pokemon.gender !in genders
|| (pokemon.shiny && "shiny" !in seenShinyStates)
|| (!pokemon.shiny && "normal" !in seenShinyStates)
|| knowledge.ordinal > this.knowledge.ordinal
|| speciesDexRecord.wouldBeDifferent(pokemon)
}
fun wouldBeDifferent(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress): Boolean {
return pokedexEntityData.pokemon.gender !in genders
|| (pokedexEntityData.pokemon.shiny && "shiny" !in seenShinyStates)
|| (!pokedexEntityData.pokemon.shiny && "normal" !in seenShinyStates)
return pokedexEntityData.gender !in genders
|| (pokedexEntityData.shiny && "shiny" !in seenShinyStates)
|| (!pokedexEntityData.shiny && "normal" !in seenShinyStates)
|| knowledge.ordinal > this.knowledge.ordinal
|| speciesDexRecord.wouldBeDifferent(pokedexEntityData)
}
@@ -30,19 +30,19 @@ class PokedexManager(
fun encounter(pokemon: Pokemon) {
val speciesId = pokemon.species.resourceIdentifier
val formName = pokemon.form.name
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).encountered(PokedexEntityData(pokemon = pokemon, disguise = null))
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).encountered(pokemon)
}
fun encounter(pokedexEntityData: PokedexEntityData) {
val speciesId = pokedexEntityData.getApparentSpecies().resourceIdentifier
val formName = pokedexEntityData.getApparentForm().name
val speciesId = pokedexEntityData.species.resourceIdentifier
val formName = pokedexEntityData.form.name
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).encountered(pokedexEntityData)
}
fun catch(pokemon: Pokemon) {
val speciesId = pokemon.species.resourceIdentifier
val formName = pokemon.form.name
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).caught(PokedexEntityData(pokemon = pokemon, disguise = null))
getOrCreateSpeciesRecord(speciesId).getOrCreateFormRecord(formName).caught(pokemon)
}
override fun markDirty() {
@@ -97,7 +97,7 @@ class SpeciesDexRecord {
}
fun addInformation(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress) {
aspects.addAll(pokedexEntityData.pokemon.aspects)
aspects.addAll(pokedexEntityData.aspects)
}
fun addAspects(addedAspects: Set<String>) {
@@ -107,7 +107,7 @@ class SpeciesDexRecord {
/** Returns true if the given Pokémon contains new information. Internal because it's only to be called from [FormDexRecord.wouldBeDifferent]. */
internal fun wouldBeDifferent(pokemon: Pokemon) = pokemon.aspects.any { it !in aspects }
internal fun wouldBeDifferent(pokedexEntityData: PokedexEntityData) = pokedexEntityData.pokemon.aspects.any { it !in aspects }
internal fun wouldBeDifferent(pokedexEntityData: PokedexEntityData) = pokedexEntityData.aspects.any { it !in aspects }
fun getOrCreateFormRecord(formName: String): FormDexRecord {
return formRecords.getOrPut(formName.lowercase()) {
@@ -468,10 +468,6 @@ open class PokemonProperties {
return this.commonMatches(pokemonEntity.pokemon) && this.customProperties.none { !it.matches(pokemonEntity) }
}
fun matches(properties: PokemonProperties): Boolean {
return properties.asString() == this.asString()
}
private fun commonMatches(pokemon: Pokemon): Boolean {
level?.takeIf { it != pokemon.level }?.let { return false }
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
@@ -809,7 +805,7 @@ open class PokemonProperties {
private fun createAbility(id: String, form: FormData): Ability? {
val ability = Abilities.get(id) ?: return null
val potentialAbility = form.abilities.firstOrNull { potential -> potential.template == ability } ?: return ability.create(true)
return potentialAbility.template.create(false, potentialAbility.priority)
return potentialAbility.template.create(false)
}
}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.PokemonGainedEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
@@ -33,8 +32,9 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.level.ServerLevel
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.protocol.game.ClientboundSoundPacket
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.ItemStack
@@ -80,11 +80,6 @@ interface Evolution : EvolutionLike {
*/
val learnableMoves: MutableSet<MoveTemplate>
/**
* The items that will drop once the evolution finishes.
*/
val drops: DropTable
/**
* Checks if the given [Pokemon] passes all the conditions and is ready to evolve.
*
@@ -105,6 +100,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)
@@ -200,41 +198,19 @@ interface Evolution : EvolutionLike {
val movesToLearn = previousSpeciesLearnableMoves + this.learnableMoves
movesToLearn.forEach { move ->
val couldAddMove =
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(move.create())
} else {
pokemon.benchedMoves.add(BenchedMove(move, 0))
}
val previousSpeciesKnewMove = previousSpeciesLearnableMoves.any { move.name == it.name }
if (couldAddMove && !previousSpeciesKnewMove) {
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(move.create())
} else {
pokemon.benchedMoves.add(BenchedMove(move, 0))
}
pokemon.getOwnerPlayer()?.sendSystemMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
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(
pokemon.entity,
ownerPlayer.level() as ServerLevel,
pokemon.entity?.position() ?: ownerPlayer.position(),
ownerPlayer,
pokemon = pokemon
)
}
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
CobblemonEvents.POKEMON_GAINED.post(PokemonGainedEvent(pokemon.getOwnerUUID()!!, pokemon))
}
@@ -242,4 +218,4 @@ interface Evolution : EvolutionLike {
fun applyTo(pokemon: Pokemon) {
result.apply(pokemon)
}
}
}
@@ -21,12 +21,6 @@ object CobblemonPokemonLabels {
*/
const val LEGENDARY = "legendary"
/**
* Represents a Pokémon who is typically banned or has restricted usage in competitive sanctions.
* For example usage see this [VGC](https://www.pokemon.com/us/pokemon-news/trainers-can-once-again-use-a-restricted-pokemon-in-ranked-battles-series-10-starting-august-1-2021) page.
*/
const val RESTRICTED = "restricted"
/**
* Represents a mythical Pokémon.
* In Cobblemon terms they do not exist since we do not share the concept of timed event only Pokémon but the official ones are still tagged.
@@ -34,21 +28,14 @@ object CobblemonPokemonLabels {
const val MYTHICAL = "mythical"
/**
* Represents Pokémon that receive a boosted catch rate from the Beast Ball. (Due to Beast Boost)
* Does not include [thematically related](https://bulbapedia.bulbagarden.net/wiki/Ultra_Beast#Related_Pok%C3%A9mon) beasts.
* Represents Pokémon that originate from Ultra Space.
*/
const val ULTRA_BEAST = "ultra_beast"
/**
* Represents a Pokémon whose family could be derived from resurrecting a fossil.
* Represents the pseudo legendary Pokémon.
*/
const val FOSSIL = "fossil"
/**
* Represents the "pseudo-legendary" Pokémon. (Slow-rate three-stage lines with a base stat total of 600)
* Term reference on this [Pokémon Center](https://www.pokemoncenter.com/search/powerhouse) page.
*/
const val POWERHOUSE = "powerhouse"
const val PSEUDO_LEGENDARY = "pseudo_legendary"
/**
* Represents a baby Pokémon, this is not just a first stage Pokémon species, it is also unable to breed.
@@ -65,55 +52,52 @@ object CobblemonPokemonLabels {
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
*/
const val KANTONIAN_FORM = "kantonian_form"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
* Source for adjectival form: [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Johto#Trivia)
*/
const val JOHTONIAN_FORM = "johtonian_form"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
* Source for adjectival form:[Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Hoenn#Trivia)
*/
const val HOENNIAN_FORM = "hoennian_form"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
* Source for adjectival form:[Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Sinnoh#Trivia)
*/
const val SINNOHAN_FORM = "sinnohan_form"
const val REGIONAL_OF_KANTO = "kanto_regional"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
*/
const val UNOVAN_FORM = "unovan_form"
const val REGIONAL_OF_JOHTO = "johto_regional"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
*/
const val KALOSIAN_FORM = "kalosian_form"
const val REGIONAL_OF_HOENN = "hoenn_regional"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
*/
const val REGIONAL_OF_SINNOH = "sinnoh_regional"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
*/
const val REGIONAL_OF_UNOVA = "unova_regional"
/**
* See [REGIONAL], this has no official regionals but it consists of the "base" form that comes from the region.
*/
const val REGIONAL_OF_KALOS = "kalos_regional"
/**
* See [REGIONAL] and this [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Regional_form#Alolan_Form) page.
*/
const val ALOLAN_FORM = "alolan_form"
const val REGIONAL_OF_ALOLA = "alola_regional"
/**
* See [REGIONAL] and this [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Regional_form#Galarian_Form) page.
*/
const val GALARIAN_FORM = "galarian_form"
const val REGIONAL_OF_GALAR = "galar_regional"
/**
* See [REGIONAL] and this [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Regional_form#Hisuian_Form) page.
*/
const val HISUIAN_FORM = "hisuian_form"
const val REGIONAL_OF_HISUI = "hisui_regional"
/**
* See [REGIONAL] and this [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Regional_form#Paldean_Form) page.
*/
const val PALDEAN_FORM = "paldean_form"
const val REGIONAL_OF_PALDEA = "paldea_regional"
/**
* Represents a mega evolution.
@@ -140,7 +124,7 @@ object CobblemonPokemonLabels {
const val TOTEM = "totem"
/**
* Represents Pokémon that trigger the Booster Energy item (due to Protosynthesis/Quark Drive)
* Represents a Paradox species.
* For more information see the [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Paradox_Pok%C3%A9mon) page.
*/
const val PARADOX = "paradox"
@@ -187,24 +171,12 @@ object CobblemonPokemonLabels {
*/
const val GENERATION_7 = "gen7"
/**
* The "official" designation for Pokémon from Gen 7 (Unknown) and not the Alola region
* This may also include forms of Pokémon that had been previously introduced
*/
const val GENERATION_7B = "gen7b"
/**
* Pokémon from the national Pokédex of 810 to 905
* This may also include forms of Pokémon that had been previously introduced
*/
const val GENERATION_8 = "gen8"
/**
* The "official" designation for Pokémon from Gen 8 (Hisui) and not the Galar region
* This may also include forms of Pokémon that had been previously introduced
*/
const val GENERATION_8A = "gen8a"
/**
* Pokémon from the national Pokédex from 906 to 1008
* This may also include forms of Pokémon that had been previously introduced
@@ -1,68 +0,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
@@ -53,7 +53,7 @@ enum class Stats(override val identifier: ResourceLocation, override val display
fun getStat(statKey: String) = when(statKey.lowercase()) {
"hp" -> HP
"atk", "attack" -> ATTACK // Hyper Cutter states the full stat name "Attack"
"def", "defense", "defence" -> DEFENCE // Big Pecks states the full stat name "Defense"
"def", "defense" -> DEFENCE // Big Pecks states the full stat name "Defense"
"spa" -> SPECIAL_ATTACK
"spd" -> SPECIAL_DEFENCE
"spe", "speed" -> SPEED
@@ -11,9 +11,6 @@ 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
/**
@@ -22,13 +19,6 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 18th, 2022
*/
class StringIdentifiedObjectAdapter<T>(
val fromString: (String) -> T,
val toString: ((T) -> String)? = null,
) : JsonDeserializer<T>, JsonSerializer<T> {
class StringIdentifiedObjectAdapter<T>(val fromString: (String) -> T) : JsonDeserializer<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)
}
}
@@ -498,7 +498,7 @@ class LookAtDirection : ParticleCameraMode {
rotation.hamiltonProduct(Axis.ZP.rotationDegrees(particleAngle))
// Minecraft in 1.21 or 1.20.4 or something fucked with vertex draw order, and it flipped everything.
rotation.hamiltonProduct(Axis.XP.rotationDegrees(180F))
rotation.hamiltonProduct(Axis.YP.rotationDegrees(180F))
return rotation
}
@@ -83,9 +83,8 @@ object BestSpawner {
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
fun init() {
fun loadConfig() {
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)
@@ -95,7 +94,6 @@ 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)
@@ -115,23 +113,14 @@ object BestSpawner {
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
loadConfig()
config = BestSpawnerConfig.load()
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,7 +10,6 @@ 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
@@ -59,12 +58,6 @@ 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<*>
}
}
@@ -10,12 +10,15 @@ package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.fishing.BobberSpawnPokemonEvent
import com.cobblemon.mod.common.api.fishing.FishingBait
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
@@ -24,6 +27,7 @@ import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.tera.TeraTypes
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.pokemon.Gender
@@ -125,8 +129,8 @@ class FishingSpawnCause(
}
fun alterFriendshipAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
if (pokemonEntity.pokemon.friendship + effect.value > Cobblemon.config.maxPokemonFriendship)
pokemonEntity.pokemon.setFriendship(Cobblemon.config.maxPokemonFriendship)
if (pokemonEntity.pokemon.friendship + effect.value > 255)
pokemonEntity.pokemon.setFriendship(255)
else
pokemonEntity.pokemon.setFriendship(pokemonEntity.pokemon.friendship + effect.value.toInt())
}
@@ -53,7 +53,7 @@ class BestSpawnerConfig {
.create()
const val CONFIG_NAME = "best-spawner-config"
const val INTERNAL_PATH = "/data/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json"
const val INTERNAL_PATH = "data/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json"
const val EXTERNAL_PATH = "config/${Cobblemon.MODID}/spawning/$CONFIG_NAME.json"
fun load(): BestSpawnerConfig {
@@ -77,7 +77,7 @@ class BestSpawnerConfig {
}
private fun loadInternal(): BestSpawnerConfig {
val reader = InputStreamReader(Cobblemon::class.java.getResourceAsStream(INTERNAL_PATH)!!)
val reader = InputStreamReader(Cobblemon.javaClass.classLoader.getResourceAsStream(INTERNAL_PATH)!!)
val config = GSON.fromJson(reader, BestSpawnerConfig::class.java)
reader.close()
return config
@@ -46,16 +46,16 @@ open class JSONStoreAdapter(
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID, registryAccess: RegistryAccess): T? {
return try {
file.reader().use {
val json = gson.fromJson<JsonObject>(it)
val store = try {
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
store.loadFromJSON(json, registryAccess)
store
val br = BufferedReader(FileReader(file))
val json = gson.fromJson<JsonObject>(br)
br.close()
val store = try {
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
store.loadFromJSON(json, registryAccess)
store
} catch (e: Exception) {
null
}
@@ -42,7 +42,6 @@ abstract class OneToOneFileStoreAdapter<S>(
) : FileStoreAdapter<S>, CobblemonAdapterParent<S>() {
abstract fun save(file: File, serialized: S)
abstract fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID, registryAccess: RegistryAccess): T?
fun getFile(storeClass: Class<out PokemonStore<*>>, uuid: UUID): File {
val className = storeClass.simpleName.lowercase()
val subfolder1 = if (folderPerClass) "$className/" else ""
@@ -57,54 +56,34 @@ abstract class OneToOneFileStoreAdapter<S>(
override fun save(storeClass: Class<out PokemonStore<*>>, uuid: UUID, serialized: S) {
val file = getFile(storeClass, uuid)
val tempFile = File(file.absolutePath + ".temp")
val oldFile = File(file.absolutePath + ".old")
tempFile.createNewFile()
save(tempFile, serialized)
if (file.exists()) {
file.copyTo(oldFile, overwrite = true)
}
tempFile.copyTo(file, overwrite = true)
tempFile.delete()
}
override fun <E : StorePosition, T : PokemonStore<E>> provide(storeClass: Class<T>, uuid: UUID, registryAccess: RegistryAccess): T? {
val file = getFile(storeClass, uuid)
val oldFile = File(file.absolutePath + ".old")
val tempFile = File(file.absolutePath + ".temp")
if (tempFile.exists()) {
try {
val tempLoaded = load(tempFile, storeClass, uuid, registryAccess)
if (tempLoaded != null) {
save(file, serialize(tempLoaded, registryAccess))
return tempLoaded
}
} finally {
tempFile.delete()
}
}
return if (file.exists() && file.length() > 0L) {
return if (file.exists()) {
load(file, storeClass, uuid, registryAccess)
?: let {
file.delete()
if (oldFile.exists() && oldFile.length() > 0L) {
var result = load(oldFile, storeClass, uuid, registryAccess) ?: let {
LOGGER.error("Pokémon save file for ${storeClass.simpleName} ($uuid) was corrupted. A fresh file will be created.")
var result = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
save(storeClass, uuid, serialize(result, registryAccess))
oldFile.delete()
return result
}
LOGGER.warn("Loading old Pokémon save file for ${storeClass.simpleName} ($uuid) due to corruption of current file.")
oldFile.copyTo(file, overwrite = true)
result
}
else {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
var result = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
save(storeClass, uuid, serialize(result, registryAccess))
oldFile.delete()
result
}
LOGGER.error("Pokémon save file for ${storeClass.simpleName} ($uuid) was corrupted. A fresh file will be created.")
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
} else {
file.delete()
if (oldFile.exists() && oldFile.length() > 0L) {
var result = load(oldFile, storeClass, uuid, registryAccess) ?: let {
oldFile.delete()
return null
}
oldFile.copyTo(file, overwrite = true)
result
}
null
}
}
@@ -27,15 +27,15 @@ import net.minecraft.world.entity.player.Player
* @since February 21, 2024
*/
open class PlayerInstancedDataStoreManager {
class PlayerInstancedDataStoreManager {
val factories = mutableMapOf<PlayerInstancedDataStoreType, PlayerInstancedDataFactory<*>>()
val saveTasks = mutableMapOf<PlayerInstancedDataStoreType, ScheduledTask>()
open fun setFactory(factory: PlayerInstancedDataFactory<*>, dataType: PlayerInstancedDataStoreType) {
fun setFactory(factory: PlayerInstancedDataFactory<*>, dataType: PlayerInstancedDataStoreType) {
factories[dataType] = factory
}
open fun setup(server: MinecraftServer) {
fun setup(server: MinecraftServer) {
factories.values.forEach {
it.setup(server)
}
@@ -57,62 +57,62 @@ open class PlayerInstancedDataStoreManager {
.build()
}
open fun get(playerId: UUID, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
fun get(playerId: UUID, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
if (!factories.contains(dataType)) {
throw UnsupportedOperationException("No factory registered for $dataType")
}
return factories[dataType]!!.getForPlayer(playerId)
}
open fun get(player: Player, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
fun get(player: Player, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
return get(player.uuid, dataType)
}
open fun saveAllOfOneType(dataType: PlayerInstancedDataStoreType) {
fun saveAllOfOneType(dataType: PlayerInstancedDataStoreType) {
if (!factories.contains(dataType)) {
throw UnsupportedOperationException("No factory registered for $dataType")
}
return factories[dataType]!!.saveAll()
}
open fun saveSingle(playerData: InstancedPlayerData, dataType: PlayerInstancedDataStoreType) {
fun saveSingle(playerData: InstancedPlayerData, dataType: PlayerInstancedDataStoreType) {
if (!factories.contains(dataType)) {
throw UnsupportedOperationException("No factory registered for $dataType")
}
return factories[dataType]!!.saveSingle(playerData.uuid)
}
open fun onPlayerDisconnect(player: ServerPlayer) {
fun onPlayerDisconnect(player: ServerPlayer) {
factories.values.forEach {
it.onPlayerDisconnect(player)
}
}
open fun syncAllToPlayer(player: ServerPlayer) {
fun syncAllToPlayer(player: ServerPlayer) {
factories.values.forEach {
it.sendToPlayer(player)
}
}
open fun saveAllStores() {
fun saveAllStores() {
factories.values.forEach {
it.saveAll()
}
}
open fun getGenericData(player: ServerPlayer): GeneralPlayerData {
fun getGenericData(player: ServerPlayer): GeneralPlayerData {
return getGenericData(player.uuid)
}
open fun getGenericData(playerId: UUID): GeneralPlayerData {
fun getGenericData(playerId: UUID): GeneralPlayerData {
return get(playerId, PlayerInstancedDataStoreTypes.GENERAL) as GeneralPlayerData
}
open fun getPokedexData(player: ServerPlayer): PokedexManager {
fun getPokedexData(player: ServerPlayer): PokedexManager {
return getPokedexData(player.uuid)
}
open fun getPokedexData(playerId: UUID): PokedexManager {
fun getPokedexData(playerId: UUID): PokedexManager {
return get(playerId, PlayerInstancedDataStoreTypes.POKEDEX) as PokedexManager
}
}
@@ -8,12 +8,10 @@
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import net.minecraft.server.MinecraftServer
import net.minecraft.world.level.storage.LevelResource
import java.io.File
import java.nio.file.Path
import java.util.UUID
@@ -37,70 +35,15 @@ abstract class FileBasedPlayerDataStoreBackend<T : InstancedPlayerData>(
savePath = server.getWorldPath(LevelResource.PLAYER_DATA_DIR).parent
}
protected fun postSaveFileMoving(playerData: T) {
val tempFile = filePath(playerData.uuid, TEMPORARY_FILE_EXTENSION)
val oldFile = filePath(playerData.uuid, OLD_FILE_EXTENSION)
val file = filePath(playerData.uuid)
if (file.exists()) {
file.copyTo(oldFile, overwrite = true)
}
tempFile.copyTo(file, overwrite = true)
tempFile.delete()
}
protected fun loadWithFallback(uuid: UUID, loadFunction: (File) -> T): T {
val playerFile = filePath(uuid)
val playerFileOld = filePath(uuid, OLD_FILE_EXTENSION)
playerFile.parentFile.mkdirs()
val loadFallback: () -> T = {
playerFile.delete()
if (playerFileOld.exists() && playerFileOld.length() > 0L) {
try {
var result = loadFunction(playerFileOld)
playerFileOld.copyTo(playerFile)
Cobblemon.LOGGER.debug("Loaded .old {} for {}", subfolder, uuid)
result
} catch (e: Exception) {
Cobblemon.LOGGER.error("Failed to load .old $subfolder for $uuid due to ${e.message}. Data is lost")
Cobblemon.LOGGER.error(e)
playerFileOld.delete()
defaultData.invoke(uuid).also(::save)
}
}
else {
Cobblemon.LOGGER.error(".old File $subfolder for $uuid is corrupt or missing. Data is lost")
playerFileOld.delete()
defaultData.invoke(uuid).also(::save)
}
}
return if (playerFile.exists() && playerFile.length() > 0L) {
try {
loadFunction(playerFile)
}
catch (e: Exception) {
Cobblemon.LOGGER.warn("Failed to load $subfolder for $uuid due to ${e.message}")
Cobblemon.LOGGER.warn(e)
loadFallback()
}
} else {
loadFallback()
}.also { it.initialize() }
}
@JvmOverloads
fun getSubFile(uuid: UUID, extension: String? = null): String {
//TODO: Set file extension to be a field
fun getSubFile(uuid: UUID): String {
return if (useNestedStructure) {
"${uuid.toString().substring(0, 2)}/$uuid.$fileExt${if (extension != null) ".$extension" else ""}"
"${uuid.toString().substring(0, 2)}/$uuid.$fileExt"
} else {
"$uuid.$fileExt.${if (extension != null) ".$extension" else ""}"
"$uuid.$fileExt"
}
}
@JvmOverloads
fun filePath(uuid: UUID, extension: String? = null): File = savePath.resolve("$subfolder/${getSubFile(uuid, extension)}").toFile()
fun filePath(uuid: UUID) = savePath.resolve("$subfolder/${getSubFile(uuid)}").toFile()
companion object {
const val OLD_FILE_EXTENSION = "old"
const val TEMPORARY_FILE_EXTENSION = "tmp"
}
}
@@ -28,26 +28,29 @@ abstract class JsonBackedPlayerDataStoreBackend<T : InstancedPlayerData>(
abstract val classToken: TypeToken<T>
override fun save(playerData: T) {
val fileTmp = filePath(playerData.uuid, TEMPORARY_FILE_EXTENSION)
fileTmp.parentFile.mkdirs()
fileTmp.printWriter().use { pw ->
pw.write(gson.toJson(playerData))
}
postSaveFileMoving(playerData)
val file = filePath(playerData.uuid)
file.parentFile.mkdirs()
val pw = PrintWriter(filePath(playerData.uuid))
pw.write(gson.toJson(playerData))
pw.flush()
pw.close()
}
override fun load(uuid: UUID): T {
return loadWithFallback(uuid) {
it.reader().use { reader ->
gson.fromJson(reader, classToken).also {
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
if (newProps.isNotEmpty()) {
val defaultData = defaultData(uuid)
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
}
val playerFile = filePath(uuid)
playerFile.parentFile.mkdirs()
return if (playerFile.exists() && playerFile.length() > 0L) {
gson.fromJson(BufferedReader(FileReader(playerFile)), classToken).also {
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
if (newProps.isNotEmpty()) {
val defaultData = defaultData(uuid)
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
}
}
}
} else {
defaultData.invoke(uuid).also(::save)
}.also { it.initialize() }
}
}
@@ -24,24 +24,29 @@ abstract class NbtBackedPlayerData<T : InstancedPlayerData>(
type: PlayerInstancedDataStoreType
) : FileBasedPlayerDataStoreBackend<T>(subfolder, type, "nbt") {
abstract val codec: Codec<T>
override fun save(playerData: T) {
val fileTmp = filePath(playerData.uuid, TEMPORARY_FILE_EXTENSION)
fileTmp.parentFile.mkdirs()
val encodeResult = codec.encodeStart(NbtOps.INSTANCE, playerData)
NbtIo.write(encodeResult.result().get() as CompoundTag, fileTmp.toPath())
postSaveFileMoving(playerData)
val file = filePath(playerData.uuid)
file.parentFile.mkdirs()
val os = DataOutputStream(FileOutputStream(filePath(playerData.uuid)))
val encodeResult = NbtOps.INSTANCE.withEncoder(codec).apply(playerData)
NbtIo.write(encodeResult.result().get() as CompoundTag, os)
os.flush()
os.close()
}
override fun load(uuid: UUID): T {
return loadWithFallback(uuid) {
val input = NbtIo.read(it.toPath())
val decodeResult = codec.decode(NbtOps.INSTANCE, input)
decodeResult.getOrThrow {
val playerFile = filePath(uuid)
playerFile.parentFile.mkdirs()
return if (playerFile.exists() && playerFile.length() > 0L) {
val input = NbtIo.read(playerFile.toPath())
val decodeResult = NbtOps.INSTANCE.withDecoder(codec).apply(input)
decodeResult.getPartialOrThrow {
Cobblemon.LOGGER.error("Error decoding $subfolder for player uuid $uuid")
Cobblemon.LOGGER.error(it)
throw UnsupportedOperationException()
}.first
}
} else {
defaultData.invoke(uuid).also(::save)
}.also { it.initialize() }
}
}
@@ -23,72 +23,30 @@ object CobblemonBlockTags {
@JvmField val ALL_HANGING_SIGNS = createTag("all_hanging_signs")
@JvmField val ALL_SIGNS = createTag("all_signs")
@JvmField val ANCIENT_CITY_BLOCKS = createTag("ancient_city_blocks")
@JvmField val APRICORN_LEAVES = createTag("apricorn_leaves")
@JvmField val APRICORN_LOGS = createTag("apricorn_logs")
@JvmField val APRICORN_SAPLINGS = createTag("apricorn_saplings")
@JvmField val APRICORNS = createTag("apricorns")
@JvmField val BERRIES = createTag("berries")
@JvmField val BERRY_REPLACEABLE = createTag("berry_replaceable")
@JvmField val BERRY_SOIL = createTag("berry_soil")
@JvmField val BERRY_WILD_SOIL = createTag("berry_wild_soil")
@JvmField val BLACK_TUMBLESTONE_BRICKS = createTag("black_tumblestone_bricks")
@JvmField val BLACK_TUMBLESTONES = createTag("black_tumblestones")
@JvmField val BLUE_FLOWERS = createTag("blue_flowers")
@JvmField val BERRY_SOIL = createTag("berry_soil")
@JvmField val BERRY_REPLACEABLE = createTag("berry_replaceable")
@JvmField val CEILING_HANGING_SIGNS = createTag("ceiling_hanging_signs")
@JvmField val CROPS = createTag("crops")
@JvmField val DAWN_STONE_ORES = createTag("dawn_stone_ores")
@JvmField val DESERT_PYRAMID_BLOCKS = createTag("desert_pyramid_blocks")
@JvmField val DRIPSTONE_GROWABLE = createTag("dripstone_growable")
@JvmField val DRIPSTONE_REPLACEABLES = createTag("dripstone_replaceables")
@JvmField val DUSK_STONE_ORES = createTag("dusk_stone_ores")
@JvmField val END_CITY_BLOCKS = createTag("end_city_blocks")
@JvmField val EVOLUTION_STONE_BLOCKS = createTag("evolution_stone_blocks")
@JvmField val FIRE_STONE_ORES = createTag("fire_stone_ores")
@JvmField val FLOWERS = createTag("flowers")
@JvmField val FOSSIL_MACHINE_PARTS = createTag("fossil_machine_parts")
@JvmField val GEMSTONES = createTag("gemstones")
@JvmField val GILDED_CHESTS = createTag("gilded_chests")
@JvmField val GLAZED_TERRACOTTA_BLOCKS = createTag("glazed_terracotta_blocks")
@JvmField val ICE_STONE_ORES = createTag("ice_stone_ores")
@JvmField val JUNGLE_PYRAMID_BLOCKS = createTag("jungle_pyramid_blocks")
@JvmField val LEAF_STONE_ORES = createTag("leaf_stone_ores")
@JvmField val MACHINES = createTag("machines")
@JvmField val MANSION_BLOCKS = createTag("mansion_blocks")
@JvmField val MEDICINAL_LEEK_PLANTABLE = createTag("medicinal_leek_plantable")
@JvmField val MINTS = createTag("mints")
@JvmField val MOON_STONE_ORES = createTag("moon_stone_ores")
@JvmField val NATURAL = createTag("natural")
@JvmField val NETHER_STRUCTURE_BLOCKS = createTag("nether_structure_blocks")
@JvmField val PINK_FLOWERS = createTag("pink_flowers")
@JvmField val RED_FLOWERS = createTag("red_flowers")
@JvmField val RED_TUMBLESTONE_BRICKS = createTag("red_tumblestone_bricks")
@JvmField val RED_TUMBLESTONES = createTag("red_tumblestones")
@JvmField val REDSTONE_BLOCKS = createTag("redstone_blocks")
@JvmField val ROOTS = createTag("roots")
@JvmField val ROOTS_SPREADABLE = createTag("roots_spreadable")
@JvmField val RUINED_PORTAL_BLOCKS = createTag("ruined_portal_blocks")
@JvmField val SEES_SKY = createTag("sees_sky")
@JvmField val SHINY_STONE_ORES = createTag("shiny_stone_ores")
@JvmField val SIGNS = createTag("signs")
@JvmField val SKY_TUMBLESTONE_BRICKS = createTag("sky_tumblestone_bricks")
@JvmField val SKY_TUMBLESTONES = createTag("sky_tumblestones")
@JvmField val SMALL_FLOWERS = createTag("small_flowers")
@JvmField val SEES_SKY = createTag("sees_sky")
@JvmField val SNOW_BLOCK = createTag("snow_block")
@JvmField val ROOTS = createTag("roots")
@JvmField val STANDING_SIGNS = createTag("standing_signs")
@JvmField val SUN_STONE_ORES = createTag("sun_stone_ores")
@JvmField val THUNDER_STONE_ORES = createTag("thunder_stone_ores")
@JvmField val TRAIL_RUINS_BLOCKS = createTag("trail_ruins_blocks")
@JvmField val TRASH = createTag("trash")
@JvmField val TREES = createTag("trees")
@JvmField val TUMBLESTONE_BRICKS = createTag("tumblestone_bricks")
@JvmField val TUMBLESTONES = createTag("tumblestones")
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
@JvmField val WALL_SIGNS = createTag("wall_signs")
@JvmField val WATER_STONE_ORES = createTag("water_stone_ores")
@JvmField val WHITE_FLOWERS = createTag("white_flowers")
@JvmField val YELLOW_FLOWERS = createTag("yellow_flowers")
private fun createTag(name: String) = TagKey.create(Registries.BLOCK, cobblemonResource(name))
@@ -21,108 +21,137 @@ import net.minecraft.tags.TagKey
*/
@Suppress("unused", "HasPlatformType")
object CobblemonItemTags {
@JvmField val ABILITY_CHANGERS = create("ability_changers")
@JvmField val ANCIENT_POKE_BALLS = create("ancient_poke_balls")
@JvmField val APPLES = create("apples")
@JvmField val APRICORN_LOGS = create("apricorn_logs")
@JvmField val APRICORN_POKE_BALLS = create("apricorn_poke_balls")
@JvmField val APRICORN_SPROUTS = create("apricorn_sprouts")
@JvmField val APRICORNS = create("apricorns")
/** This tag is only used for a Torterra aspect based easter egg evolution at the moment.
* It simply includes the 'minecraft:azalea' and 'minecraft:flowering_azalea' items by default. */
@JvmField val AZALEA_TREE = create("azalea_tree")
@JvmField val BATTLE_ITEMS = create("battle_items")
@JvmField val BERRIES = create("berries")
@JvmField val BLACK_TUMBLESTONE_BRICKS = create("black_tumblestone_bricks")
@JvmField val BLACK_TUMBLESTONES = create("black_tumblestones")
@JvmField val BOATS = create("boats")
/** This tag is used for Fossil Machine natural materials */
@JvmField val COOKED_MEAT = create("cooked_meat")
@JvmField val DAWN_STONE_ORES = create("dawn_stone_ores")
@JvmField val POTTERY_SHERDS = create("decorated_pot_sherds")
@JvmField val DEEP_SEAS = create("deep_seas")
@JvmField val DUSK_STONE_ORES = create("dusk_stone_ores")
@JvmField val ETHERS = create("ethers")
@JvmField val EVOLUTION_ITEMS = create("evolution_items")
@JvmField val EVOLUTION_STONE_BLOCKS = create("evolution_stone_blocks")
@JvmField val EVOLUTION_STONES = create("evolution_stones")
@JvmField val EXPERIENCE_CANDIES = create("experience_candies")
@JvmField val FEATHERS = create("feathers")
@JvmField val FIRE_STONE_ORES = create("fire_stone_ores")
@JvmField val FOSSIL_MACHINE_PARTS = create("fossil_machine_parts")
@JvmField val FOSSILS = create("fossils")
@JvmField val GILDED_CHESTS = create("gilded_chests")
@JvmField val HANGING_SIGNS = create("hanging_signs")
@JvmField val HERBS = create("herbs")
@JvmField val ICE_STONE_ORES = create("ice_stone_ores")
@JvmField val LEAF_STONE_ORES = create("leaf_stone_ores")
@JvmField val MACHINES = create("machines")
@JvmField val MINT_LEAF = create("mint_leaf")
@JvmField val MINT_SEEDS = create("mint_seeds")
@JvmField val MINTS = create("mints")
@JvmField val MOON_STONE_ORES = create("moon_stone_ores")
@JvmField val MUTATED_BERRIES = create("mutated_berries")
@JvmField val PLANTS = create("plants")
@JvmField val POKE_BALLS = create("poke_balls")
@JvmField val POKE_RODS = create("poke_rods")
@JvmField val POKEDEX = create("pokedex")
@JvmField val POKEDEX_SCREEN = create("pokedex_screen")
@JvmField val POTIONS = create("potions")
@JvmField val PROTEIN_INGREDIENTS = create("protein_ingredients")
@JvmField
val ANCIENT_POKE_BALLS = create("ancient_poke_balls")
@JvmField
val APRICORN_LOGS = create("apricorn_logs")
@JvmField
val APRICORN_SPROUTS = create("apricorn_sprouts")
@JvmField
val APRICORNS = create("apricorns")
@JvmField
val POKEDEX = create("pokedex")
/**
* This tag is only used for a Torterra aspect based easter egg evolution at the moment.
* It simply includes the 'minecraft:azalea' and 'minecraft:flowering_azalea' items by default.
*/
@JvmField
val AZALEA_TREE = create("azalea_tree")
@JvmField
val BERRIES = create("berries")
@JvmField
val BOATS = create("boats")
@JvmField
val COBBLEMON_SEEDS = create("cobblemon_seeds")
/**
* This tag is used for Fossil Machine natural materials
*/
@JvmField
val COOKED_MEAT = create("cooked_meat")
@JvmField
val DAWN_STONE_ORES = create("dawn_stone_ores")
@JvmField
val DUSK_STONE_ORES = create("dusk_stone_ores")
@JvmField
val EVOLUTION_ITEMS = create("evolution_items")
@JvmField
val EVOLUTION_STONES = create("evolution_stones")
@JvmField
val EXPERIENCE_CANDIES = create("experience_candies")
@JvmField
val FIRE_STONE_ORES = create("fire_stone_ores")
@JvmField
val FOSSILS = create("fossils")
@JvmField
val HANGING_SIGNS = create("hanging_signs")
@JvmField
val HERBS = create("herbs")
@JvmField
val ICE_STONE_ORES = create("ice_stone_ores")
@JvmField
val LEAF_STONE_ORES = create("leaf_stone_ores")
@JvmField
val MINT_LEAF = create("mint_leaf")
@JvmField
val MINT_SEEDS = create("mint_seeds")
@JvmField
val MINTS = create("mints")
@JvmField
val MOON_STONE_ORES = create("moon_stone_ores")
@JvmField
val MUTATED_BERRIES = create("mutated_berries")
@JvmField
val PLANTS = create("plants")
@JvmField
val POKE_BALLS = create("poke_balls")
@JvmField
val PROTEIN_INGREDIENTS = create("protein_ingredients")
/** See [COOKED_MEAT] */
@JvmField val RAW_MEAT = create("raw_meat")
@JvmField val RED_TUMBLESTONE_BRICKS = create("red_tumblestone_bricks")
@JvmField val RED_TUMBLESTONES = create("red_tumblestones")
@JvmField val REMEDIES = create("remedies")
@JvmField val RESTORES = create("restores")
@JvmField val REVIVES = create("revives")
@JvmField val SEEDS = create("seeds")
@JvmField val SHINY_STONE_ORES = create("shiny_stone_ores")
@JvmField val SIGNS = create("signs")
@JvmField val SKY_TUMBLESTONE_BRICKS = create("sky_tumblestone_bricks")
@JvmField val SKY_TUMBLESTONES = create("sky_tumblestones")
@JvmField val SUN_STONE_ORES = create("sun_stone_ores")
@JvmField val SWEETS = create("sweets")
@JvmField val TEACUPS = create("teacups")
@JvmField val TEAPOTS = create("teapots")
@JvmField val THUNDER_STONE_ORES = create("thunder_stone_ores")
@JvmField val TIER_1_POKE_BALL_MATERIALS = create("tier_1_poke_ball_materials")
@JvmField val TIER_1_POKE_BALLS = create("tier_1_poke_balls")
@JvmField val TIER_2_POKE_BALL_MATERIALS = create("tier_2_poke_ball_materials")
@JvmField val TIER_2_POKE_BALLS = create("tier_2_poke_balls")
@JvmField val TIER_3_POKE_BALL_MATERIALS = create("tier_3_poke_ball_materials")
@JvmField val TIER_3_POKE_BALLS = create("tier_3_poke_balls")
@JvmField val TIER_4_POKE_BALL_MATERIALS = create("tier_4_poke_ball_materials")
@JvmField val TIER_4_POKE_BALLS = create("tier_4_poke_balls")
@JvmField val TIER_5_POKE_BALLS = create("tier_5_poke_balls")
@JvmField val TUMBLESTONE_BRICKS = create("tumblestone_bricks")
@JvmField val TUMBLESTONES = create("tumblestones")
@JvmField val TYPE_GEMS = create("type_gems")
@JvmField val VITAMINS = create("vitamins")
@JvmField val WATER_STONE_ORES = create("water_stone_ores")
@JvmField val ZINC_INGREDIENTS = create("zinc_ingredients")
@JvmField
val RAW_MEAT = create("raw_meat")
@JvmField
val SEEDS = create("seeds")
@JvmField
val SHINY_STONE_ORES = create("shiny_stone_ores")
@JvmField
val SIGNS = create("signs")
@JvmField
val SUN_STONE_ORES = create("sun_stone_ores")
@JvmField
val THUNDER_STONE_ORES = create("thunder_stone_ores")
@JvmField
val TUMBLESTONES = create("tumblestones")
@JvmField
val WATER_STONE_ORES = create("water_stone_ores")
@JvmField
val ZINC_INGREDIENTS = create("zinc_ingredients")
// Held Item Tags
@JvmField val CONSUMED_IN_NPC_BATTLE = create("held/consumed_in_npc_battle")
@JvmField val CONSUMED_IN_PVP_BATTLE = create("held/consumed_in_pvp_battle")
@JvmField val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
@JvmField val DESTINY_KNOT = create("held/destiny_knot")
@JvmField val EVERSTONE = create("held/everstone")
@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")
/** 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")
@JvmField val POWER_ANKLET = create("held/power_anklet")
@JvmField val POWER_BAND = create("held/power_band")
@JvmField val POWER_BELT = create("held/power_belt")
@JvmField val POWER_BRACER = create("held/power_bracer")
@JvmField val POWER_LENS = create("held/power_lens")
@JvmField val POWER_WEIGHT = create("held/power_weight")
@JvmField val TERRAIN_SEEDS = create("held/terrain_seeds")
@JvmField
val ANY_HELD_ITEM = create("held/is_held_item")
@JvmField
val EXPERIENCE_SHARE = create("held/experience_share")
@JvmField
val LUCKY_EGG = create("held/lucky_egg")
@JvmField
val DESTINY_KNOT = create("held/destiny_knot")
@JvmField
val EVERSTONE = create("held/everstone")
@JvmField
val POWER_ANKLET = create("held/power_anklet")
@JvmField
val POWER_BAND = create("held/power_band")
@JvmField
val POWER_BELT = create("held/power_belt")
@JvmField
val POWER_BRACER = create("held/power_bracer")
@JvmField
val POWER_LENS = create("held/power_lens")
@JvmField
val POWER_WEIGHT = create("held/power_weight")
@JvmField
val CONSUMED_IN_NPC_BATTLE = create("held/consumed_in_npc_battle")
@JvmField
val CONSUMED_IN_PVP_BATTLE = create("held/consumed_in_pvp_battle")
@JvmField
val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
@JvmField
val TERRAIN_SEEDS = create("held/terrain_seeds")
/**
* Tag that flags items as being able to "create" [CobblemonItems.LEFTOVERS].
*/
@JvmField
val LEAVES_LEFTOVERS = create("held/leaves_leftovers")
@JvmField
val POTTERY_SHERDS = create("decorated_pot_sherds")
@JvmField
val ABILITY_CHANGERS = create("ability_changers")
@JvmField
val IS_FRIENDSHIP_BOOSTER = create("is_friendship_booster")
private fun create(path: String) = TagKey.create(Registries.ITEM, cobblemonResource(path))
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.battles.dispatch
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import org.objectweb.asm.tree.analysis.Interpreter
class InstructionSet {
val instructions: MutableList<InterpreterInstruction> = mutableListOf()
@@ -68,8 +68,6 @@ class WinInstruction(val message: BattleMessage): InterpreterInstruction {
}
}
battle.dispatchGo {
battle.winners = winners
battle.losers = losers
battle.end()
CobblemonEvents.BATTLE_VICTORY.post(BattleVictoryEvent(battle, winners, losers, wasCaught))
ShowdownInterpreter.lastCauser.remove(battle.battleId)
@@ -65,8 +65,8 @@ class GraalShowdownUnbundler {
}
if (extract) {
FileUtils.copyInternalToExternal("/data/${Cobblemon.MODID}/showdown.zip", showdownZip.toPath())
FileUtils.copyInternalToExternal("/data/${Cobblemon.MODID}/showdown.json", showdownMetadataFile.toPath())
FileUtils.copyInternalToExternal("data/${Cobblemon.MODID}/showdown.zip", showdownZip.toPath())
FileUtils.copyInternalToExternal("data/${Cobblemon.MODID}/showdown.json", showdownMetadataFile.toPath())
FileUtils.unzipFile(showdownZip.toPath(), showdownDir.toPath())
showdownZip.delete()
}
@@ -75,7 +75,7 @@ class GraalShowdownUnbundler {
private fun loadShowdownMetadata() : ShowdownMetadata? {
try {
val inputStream = javaClass.getResourceAsStream("/data/${Cobblemon.MODID}/showdown.json")!!
val inputStream = javaClass.classLoader.getResourceAsStream("data/${Cobblemon.MODID}/showdown.json")!!
return gson.fromJson<ShowdownMetadata>(InputStreamReader(inputStream))
} catch (exception: Exception) {
exception.printStackTrace()
@@ -308,12 +308,9 @@ class HealingMachineBlockEntity(
} else {
// Recharging
if (blockEntity.healingCharge < blockEntity.maxCharge) {
val secondsToChargeHealingMachine = (Cobblemon.config.secondsToChargeHealingMachine).coerceAtLeast(0.0)
val totalTicks = secondsToChargeHealingMachine * 20
val chargePerTick = if (totalTicks > 0) blockEntity.maxCharge / totalTicks else 0.0
val chargePerTick = (Cobblemon.config.chargeGainedPerTick).coerceAtLeast(0F)
blockEntity.healingCharge =
(blockEntity.healingCharge + chargePerTick.toFloat()).coerceIn(0F..blockEntity.maxCharge)
(blockEntity.healingCharge + chargePerTick).coerceIn(0F..blockEntity.maxCharge)
blockEntity.updateBlockChargeLevel()
blockEntity.updateRedstoneSignal()
blockEntity.markUpdated()
@@ -21,10 +21,12 @@ import com.cobblemon.mod.common.client.render.models.blockbench.ExcludedLabels
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.*
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.repository.appendage.ModelAppendageModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.getStringOrNull
import java.util.function.Function
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.SimpleSoundInstance
@@ -51,7 +53,7 @@ object ClientMoLangFunctions {
"say" to java.util.function.Function { params -> Minecraft.getInstance().player?.sendSystemMessage(params.getString(0).text()) ?: Unit },
)
val posableModelFunctions = hashMapOf<String, Function<PosableModel, Function<MoParams, Any>>>(
val animationFunctions = hashMapOf<String, Function<PosableModel, Function<MoParams, Any>>>(
"exclude_labels" to Function { model -> Function { params ->
val labels = params.params.map { it.asString() }
ObjectValue(ExcludedLabels(labels))
@@ -242,18 +244,6 @@ object ClientMoLangFunctions {
)
}
},
"appendage_model" to Function {
model -> Function { params ->
val name = params.getString(0)
ObjectValue((ModelAppendageModelRepository to name.asResource()))
}
},
"vanilla_model" to Function {
model -> Function { params ->
val id = params.getString(0).asResource()
ObjectValue(id)
}
},
"primary_animation" to Function { model ->
Function { params ->
val animation = params.get<ObjectValue<ActiveAnimation>>(0)?.obj ?: return@Function DoubleValue.ZERO
@@ -354,7 +344,7 @@ object ClientMoLangFunctions {
}
)
fun PosableModel.animationFunctions(): HashMap<String, Function<MoParams, Any>> = posableModelFunctions.map { (k, v) -> k to v.apply(this) }.toHashMap()
fun PosableModel.animationFunctions(): HashMap<String, Function<MoParams, Any>> = animationFunctions.map { (k, v) -> k to v.apply(this) }.toHashMap()
fun MoLangRuntime.setupClient(): MoLangRuntime {
environment.query.addFunctions(clientFunctions)
@@ -47,7 +47,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscM
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.appendage.ModelAppendageModelRepository
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
@@ -101,8 +100,7 @@ object CobblemonClient {
NPCModelRepository,
FossilModelRepository,
BlockEntityModelRepository,
GenericBedrockEntityModelRepository,
ModelAppendageModelRepository
GenericBedrockEntityModelRepository
)
val overlay: PartyOverlay by lazy { PartyOverlay() }
@@ -372,24 +372,24 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
override fun tick(entity: PokemonEntity) {
incrementAge(entity)
playWildShinySounds()
if(currentEntity.ownerUUID == null && currentEntity.pokemon.shiny && secondsSinceLastShinyParticle > SHINY_PARTICLE_COOLDOWN && !currentEntity.isBattling) {
//wild shiny not looked at
playShinyEffect("cobblemon:shiny_sparkle_ambient_wild")
lastShinyParticle = System.currentTimeMillis()
}
getClientShinyPokemon()
}
fun playWildShinySounds() {
fun getClientShinyPokemon() {
val player = Minecraft.getInstance().player ?: return
val isWithinRange = player.position().distanceTo(currentEntity.position()) <= Cobblemon.config.shinyNoticeParticlesDistance
if (currentEntity.pokemon.shiny && currentEntity.ownerUUID == null) {
if (isWithinRange) {
if (secondsSinceLastShinyParticle > SHINY_PARTICLE_COOLDOWN && !currentEntity.isBattling) {
playShinyEffect("cobblemon:shiny_sparkle_ambient_wild")
lastShinyParticle = System.currentTimeMillis()
}
if (!shined && !player.isSpectator) {
playShinyEffect("cobblemon:wild_shiny_ring")
shined = true
}
} else {
if (currentEntity.pokemon.shiny && currentEntity.ownerUUID == null && !player.isSpectator ) {
if (isWithinRange && !shined) {
playShinyEffect("cobblemon:wild_shiny_ring")
shined = true
lastShinyParticle = System.currentTimeMillis()
} else if (!isWithinRange) {
shined = false
}
}
@@ -191,8 +191,6 @@ class BattleTargetSelection(
matrices.pushPose()
matrices.translate(x + TARGET_WIDTH - (25 / 2.0) - 2, y + 5.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
// Grab aspects for right variant
state.currentAspects = battlePokemon.state.currentAspects
drawProfilePokemon(
species = battlePokemon.species.resourceIdentifier,
matrixStack = matrices,
@@ -1,113 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.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)
}
}
@@ -1,190 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.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) }
}
}
@@ -1,126 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.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<*>?)
}
@@ -1,155 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.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)
}
}
}
}
@@ -1,106 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.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)
}
}
@@ -17,10 +17,6 @@ class GrabbedStorageSlot(
private val pokemon: Pokemon
) : StorageSlot(x, y, parent, {}) {
init {
isSlotSelected = true
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
renderSlot(context = context, posX = mouseX - (width / 2), posY = mouseY - (height / 2), partialTicks = delta)
}
@@ -40,8 +40,7 @@ import net.minecraft.sounds.SoundEvent
class PCGUI(
val pc: ClientPC,
val party: ClientParty,
val configuration: PCGUIConfiguration,
val openOnBox: Int = 0
val configuration: PCGUIConfiguration
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable {
companion object {
@@ -111,7 +110,6 @@ class PCGUI(
pc = pc,
party = party
)
this.storageWidget.box = openOnBox
this.setPreviewPokemon(null)
this.addRenderableWidget(storageWidget)
@@ -46,8 +46,6 @@ open class StorageSlot(
private val slotOverlayMoveIconResource = cobblemonResource("textures/gui/pasture/pc_slot_icon_move.png")
}
protected var isSlotSelected = false;
override fun playDownSound(soundManager: SoundManager) {
}
@@ -84,52 +82,50 @@ open class StorageSlot(
context.disableScissor()
if (!isSlotSelected) {
// Ensure elements are not hidden behind Pokémon render
matrices.pushPose()
matrices.translate(0.0, 0.0, 100.0)
// Level
drawScaledText(
context = context,
text = lang("ui.lv.number", pokemon.level),
x = posX + 1,
y = posY + 1,
shadow = true,
// Ensure elements are not hidden behind Pokémon render
matrices.pushPose()
matrices.translate(0.0, 0.0, 100.0)
// Level
drawScaledText(
context = context,
text = lang("ui.lv.number", pokemon.level),
x = posX + 1,
y = posY + 1,
shadow = true,
scale = PCGUI.SCALE
)
if (pokemon.gender != Gender.GENDERLESS) {
blitk(
matrixStack = matrices,
texture = if (pokemon.gender == Gender.MALE) genderIconMale else genderIconFemale,
x = (posX + 21) / PCGUI.SCALE,
y = (posY + 1) / PCGUI.SCALE,
width = 6,
height = 8,
scale = PCGUI.SCALE
)
if (pokemon.gender != Gender.GENDERLESS) {
blitk(
matrixStack = matrices,
texture = if (pokemon.gender == Gender.MALE) genderIconMale else genderIconFemale,
x = (posX + 21) / PCGUI.SCALE,
y = (posY + 1) / PCGUI.SCALE,
width = 6,
height = 8,
scale = PCGUI.SCALE
)
}
// Held Item
val heldItem = pokemon.heldItemNoCopy()
if (!heldItem.isEmpty) {
renderScaledGuiItemIcon(
itemStack = heldItem,
x = posX + 16.0,
y = posY + 16.0,
scale = 0.5,
matrixStack = matrices
)
}
matrices.popPose()
}
// Held Item
val heldItem = pokemon.heldItemNoCopy()
if (!heldItem.isEmpty) {
renderScaledGuiItemIcon(
itemStack = heldItem,
x = posX + 16.0,
y = posY + 16.0,
scale = 0.5,
matrixStack = matrices
)
}
matrices.popPose()
// Ensure overlay elements are on top
matrices.pushPose()
matrices.translate(0.0, 0.0, 500.0)
val config = parent.pcGui.configuration
if (pokemon.tetheringId != null && !isSlotSelected) {
if (pokemon.tetheringId != null) {
if (isStationary()) {
blitk(
matrixStack = matrices,
@@ -286,10 +286,7 @@ class StorageWidget(
}
pastureWidget?.render(context, mouseX, mouseY, delta)
matrices.pushPose()
matrices.translate(0.0, 0.0, 1000.0)
grabbedSlot?.render(context, mouseX, mouseY, delta)
matrices.popPose()
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
@@ -163,8 +163,8 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
},
text = lang("ui.evolve"),
resource = evolveButtonResource,
renderRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() && CobblemonClient.battle == null },
clickRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() && CobblemonClient.battle == null }
renderRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() },
clickRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() }
)
)
@@ -9,8 +9,6 @@
package com.cobblemon.mod.common.client.gui.toast
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.toasts.Toast
import net.minecraft.client.gui.components.toasts.ToastComponent
@@ -21,7 +19,6 @@ import net.minecraft.util.Mth
import java.util.*
import kotlin.math.min
@Environment(EnvType.CLIENT)
class CobblemonToast(
val id: UUID,
var icon: ItemStack,
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.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))
}
}
@@ -19,8 +19,6 @@ object ServerSettingsPacketHandler : ClientNetworkPacketHandler<ServerSettingsPa
ServerSettings.displayEntityLevelLabel = packet.displayEntityLevelLabel
ServerSettings.displayEntityNameLabel = packet.displayEntityNameLabel
ServerSettings.maxPokemonLevel = packet.maxPokemonLevel
ServerSettings.maxPokemonFriendship = packet.maxPokemonFriendship
ServerSettings.maxDynamaxLevel = packet.maxDynamaxLevel
}
}
@@ -18,6 +18,6 @@ import net.minecraft.client.Minecraft
object OpenPCHandler : ClientNetworkPacketHandler<OpenPCPacket> {
override fun handle(packet: OpenPCPacket, client: Minecraft) {
val pc = CobblemonClient.storage.pcStores[packet.storeID] ?: return
Minecraft.getInstance().setScreen(PCGUI(pc, CobblemonClient.storage.myParty, PCGUIConfiguration(), packet.box))
Minecraft.getInstance().setScreen(PCGUI(pc, CobblemonClient.storage.myParty, PCGUIConfiguration()))
}
}
@@ -116,7 +116,7 @@ class PokedexScannerRenderer {
drawScaledText(
context = graphics,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.lv.number", pokedexEntityData.pokemon.level).bold(),
text = lang("ui.lv.number", pokedexEntityData.level).bold(),
x = centerX + xOffset + xOffsetText,
y = centerY + yOffset + yOffsetText,
shadow = true,
@@ -126,7 +126,7 @@ class PokedexScannerRenderer {
if (infoDisplayedCounter == 2) {
val hasTrainer = (usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)?.ownerUUID !== null
val speciesName = pokedexEntityData.getApparentSpecies().translatedName.bold()
val speciesName = pokedexEntityData.species.translatedName.bold()
var yOffsetName = if (hasTrainer) 2 else 0
if (hasTrainer) {
drawScaledText(
@@ -149,7 +149,7 @@ class PokedexScannerRenderer {
centered = true
)
val gender = pokedexEntityData.pokemon.gender
val gender = pokedexEntityData.gender
val speciesNameWidth = Minecraft.getInstance().font.width(speciesName.font(CobblemonResources.DEFAULT_LARGE))
if (gender != Gender.GENDERLESS) {
val isMale = gender == Gender.MALE
@@ -179,15 +179,14 @@ class PokedexScannerRenderer {
}
if (infoDisplayedCounter == 3) {
val typeText = lang("type.suffix", pokedexEntityData.getApparentForm().types.map { it.displayName.copy() }.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
val typeText = lang("type.suffix", pokedexEntityData.form.types.map { it.displayName.copy() }.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
val typeWidth = Minecraft.getInstance().font.width(typeText.font(CobblemonResources.DEFAULT_LARGE))
// Split into 2 lines if text width is too long
if (typeWidth > (OUTER_INFO_FRAME_WIDTH - 8) && pokedexEntityData.getApparentForm().secondaryType !== null) {
val splitLines = typeText.string.split("/")
if (typeWidth > (OUTER_INFO_FRAME_WIDTH - 8) && pokedexEntityData.form.secondaryType !== null) {
drawScaledText(
context = graphics,
font = CobblemonResources.DEFAULT_LARGE,
text = text(splitLines[0], "/").bold(),
text = lang("type.suffix", pokedexEntityData.form.primaryType.displayName).bold(),
x = centerX + xOffset + xOffsetText,
y = centerY + yOffset + yOffsetText - 4,
shadow = true,
@@ -197,7 +196,7 @@ class PokedexScannerRenderer {
drawScaledText(
context = graphics,
font = CobblemonResources.DEFAULT_LARGE,
text = text(splitLines[1]).bold(),
text = lang("type.suffix", pokedexEntityData.form.secondaryType!!.displayName).bold(),
x = centerX + xOffset + xOffsetText,
y = centerY + yOffset + yOffsetText + 3,
shadow = true,
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.client.render
import com.cobblemon.mod.common.util.math.geometry.getOrigin
import com.cobblemon.mod.common.util.math.geometry.transformPosition
import com.mojang.blaze3d.vertex.PoseStack
import net.fabricmc.loader.impl.lib.sat4j.core.Vec
import org.joml.Matrix4f
import net.minecraft.world.phys.Vec3
@@ -12,7 +12,7 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
import com.cobblemon.mod.common.client.render.layer.CobblemonRenderTypes
import com.cobblemon.mod.common.client.render.layer.CobblemonRenderLayers
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.setPosition
import com.cobblemon.mod.common.util.math.geometry.Axis
@@ -54,7 +54,7 @@ class BerryBlockRenderer(private val context: BlockEntityRendererProvider.Contex
if (entity.renderState == null) {
entity.renderState = BerryBlockEntityRenderState()
}
val bf = vertexConsumers.getBuffer(CobblemonRenderTypes.BERRY_LAYER)
val bf = vertexConsumers.getBuffer(CobblemonRenderLayers.BERRY_LAYER)
val renderState = entity.renderState as BerryBlockEntityRenderState
// if (renderState.needsRebuild || renderState.vboLightLevel != light) {
renderToBuffer(entity, matrices, light, overlay, renderState, bf)
@@ -14,8 +14,6 @@ import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.util.cobblemonResource
import java.awt.Color
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.renderer.RenderType
@@ -33,7 +31,6 @@ import net.minecraft.world.entity.HumanoidArm
import net.minecraft.world.item.ItemDisplayContext
import org.joml.Quaternionf
@Environment(value = EnvType.CLIENT)
class PokeBobberEntityRenderer(context: EntityRendererProvider.Context?) : EntityRenderer<PokeRodFishingBobberEntity>(context) {
override fun render(fishingBobberEntity: PokeRodFishingBobberEntity, elapsedPartialTicks: Float, tickDelta: Float, matrixStack: PoseStack, vertexConsumerProvider: MultiBufferSource, light: Int) {
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.item.PokemonItem
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import com.mojang.blaze3d.platform.Lighting
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.renderer.RenderType
import com.mojang.blaze3d.vertex.VertexConsumer
import net.minecraft.client.renderer.MultiBufferSource
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.renderer.LightTexture
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.item.ItemStack
import org.joml.Quaternionf
@@ -50,10 +50,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
val transformations = positions[mode]!!
if (mode == ItemDisplayContext.GUI) {
Lighting.setupForFlatItems()
}
Lighting.setupForFlatItems()
matrices.scale(transformations.scale.x, transformations.scale.y, transformations.scale.z)
matrices.translate(transformations.translation.x, transformations.translation.y, transformations.translation.z)
state.setPoseToFirstSuitable(PoseType.PORTRAIT)
@@ -87,10 +84,7 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
model.setDefault()
matrices.popPose()
matrices.popPose()
if (mode == ItemDisplayContext.GUI) {
Lighting.setupFor3DItems()
}
Lighting.setupFor3DItems()
}
companion object {
@@ -19,7 +19,7 @@ import net.minecraft.Util
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderStateShard.*
object CobblemonRenderTypes {
object CobblemonRenderLayers {
val BERRY_LAYER = run {
val multiPhaseParameters = RenderType.CompositeState.builder()
.setLightmapState(LIGHTMAP)
@@ -1,129 +0,0 @@
package com.cobblemon.mod.common.client.render.layer
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
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.client.render.models.blockbench.repository.appendage.ModelAppendageModelRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.resources.model.ModelResourceLocation
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemDisplayContext
import org.joml.Vector3f
object ModelAppendageRenderer {
fun <T> render(
poseStack: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
livingEntity: T,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
netHeadYaw: Float,
headPitch: Float,
locator: String,
context: RenderContext,
parentModel: ResourceLocation
) where T : PokemonEntity {
val clientDelegate = livingEntity.delegate as PokemonClientDelegate
val appendages = ModelAppendageModelRepository.getAppendagesForModelAndTarget(parentModel, locator) ?: return
// avoid editing the original context
val clonedContext = context.clone()
appendages.forEach { appendage ->
val modelObj = appendage.model.resolveObject(clientDelegate.runtime).obj
if (modelObj is ResourceLocation) {
poseStack.pushPose()
clientDelegate.locatorStates[locator]?.let { bone ->
val stack = if(modelObj.toString() == "cobblemon:held_item") livingEntity.pokemon.heldItemNoCopy() else BuiltInRegistries.ITEM.get(modelObj).defaultInstance
val pos = bone.getOrigin().subtract(livingEntity.position())
val scale = Vector3f()
bone.matrix.getScale(scale)
poseStack.scale(scale.x(), scale.y(), scale.z())
poseStack.translate(pos.x, pos.y, pos.z)
poseStack.mulPose(bone.clone().matrix.setTranslation(0.0f, 0.0f, 0.0f))
poseStack.scale(
appendage.scale.x().toFloat(), appendage.scale.y().toFloat(),
appendage.scale.z().toFloat()
)
poseStack.mulPose(Axis.XP.rotationDegrees(appendage.rotation.x().toFloat()))
poseStack.mulPose(Axis.YP.rotationDegrees(appendage.rotation.y().toFloat()))
poseStack.mulPose(Axis.ZP.rotationDegrees(appendage.rotation.z().toFloat()))
poseStack.translate(appendage.offset.x(), appendage.offset.y(), appendage.offset.z())
Minecraft.getInstance().itemRenderer.renderStatic(stack, ItemDisplayContext.THIRD_PERSON_RIGHT_HAND, packedLight, OverlayTexture.NO_OVERLAY, poseStack, bufferSource, livingEntity.level(), 0)
}
poseStack.popPose()
} else if (modelObj is Pair<*, *>) {
val repo = modelObj.first as ModelAppendageModelRepository
val resLoc = modelObj.second as ResourceLocation
val model =
repo.getPoserNullable(resLoc, clientDelegate) ?: return
model.context = clonedContext
model.setLayerContext(
bufferSource,
clientDelegate,
ModelAppendageModelRepository.getLayers(resLoc, clientDelegate)
)
poseStack.pushPose()
clientDelegate.locatorStates[locator]?.let { bone ->
val pos = bone.getOrigin().subtract(livingEntity.position())
val scale = Vector3f()
bone.matrix.getScale(scale)
poseStack.scale(scale.x(), scale.y(), scale.z())
poseStack.translate(pos.x, pos.y, pos.z)
poseStack.mulPose(bone.clone().matrix.setTranslation(0.0f, 0.0f, 0.0f))
poseStack.mulPose(Axis.XP.rotationDegrees(180f))
poseStack.translate(0.0, -1.75, 0.0)
poseStack.scale(
appendage.scale.x().toFloat(), appendage.scale.y().toFloat(),
appendage.scale.z().toFloat()
)
poseStack.mulPose(Axis.XP.rotationDegrees(appendage.rotation.x().toFloat()))
poseStack.mulPose(Axis.YP.rotationDegrees(appendage.rotation.y().toFloat()))
poseStack.mulPose(Axis.ZP.rotationDegrees(appendage.rotation.z().toFloat()))
poseStack.translate(appendage.offset.x(), appendage.offset.y(), appendage.offset.z())
model.applyAnimations(
livingEntity,
clientDelegate,
limbSwing,
limbSwingAmount,
ageInTicks,
netHeadYaw,
headPitch
)
val buffer = bufferSource.getBuffer(
RenderType.entityCutout(
ModelAppendageModelRepository.getTexture(
resLoc,
clientDelegate
)
)
)
model.withLayerContext(
bufferSource,
clientDelegate,
ModelAppendageModelRepository.getLayers(resLoc, clientDelegate)
) {
model.render(clonedContext, poseStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
}
}
model.green = 1F
model.blue = 1F
model.red = 1F
model.resetLayerContext()
poseStack.popPose()
}
}
}
}
@@ -1,15 +0,0 @@
package com.cobblemon.mod.common.client.render.layer.appendage
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.google.gson.annotations.SerializedName
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
data class ModelAppendage(
@SerializedName("target_model") val targetModel: ResourceLocation?,
@SerializedName("model") val model: ExpressionLike,
@SerializedName("target_locator") val targetLocator: String,
@SerializedName("scale") val scale: Vec3 = Vec3(1.0, 1.0, 1.0),
@SerializedName("rotation") val rotation: Vec3 = Vec3(0.0, 0.0, 0.0),
@SerializedName("offset") val offset: Vec3 = Vec3(0.0, 0.0, 0.0)
)
@@ -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.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.SingleBoneLookAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
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.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ExeggutorAlolanModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("exeggutor")
override val head = getPart("head")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
val head2 = object : HeadedFrame {
override val rootPart = this@ExeggutorAlolanModel.rootPart
override val head: ModelPart = getPart("head2")
}
val head3 = object : HeadedFrame {
override val rootPart = this@ExeggutorAlolanModel.rootPart
override val head: ModelPart = getPart("head3")
}
override var portraitScale = 1.0F
override var portraitTranslation = Vec3(-1.4, 16.55, 0.0)
override var profileScale = 0.45F
override var profileTranslation = Vec3(0.0, 1.0, -30.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override fun registerPoses() {
val blink1 = quirk { bedrockStateful("exeggutor_alolan", "blink") }
val blink2 = quirk { bedrockStateful("exeggutor_alolan", "blink2") }
val blink3 = quirk { bedrockStateful("exeggutor_alolan", "blink3") }
val blink4 = quirk { bedrockStateful("exeggutor_alolan", "blink4") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("exeggutor_alolan", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink1, blink2, blink3, blink4),
condition = { !it.isBattling },
animations = arrayOf(
singleBoneLook(),
SingleBoneLookAnimation(head2, false, false, disableX = false, disableY = false),
SingleBoneLookAnimation(head3, false, false, disableX = false, disableY = false),
bedrock("exeggutor_alolan", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink1, blink2, blink3, blink4),
animations = arrayOf(
singleBoneLook(),
SingleBoneLookAnimation(head2, false, false, disableX = false, disableY = false),
SingleBoneLookAnimation(head3, false, false, disableX = false, disableY = false),
bedrock("exeggutor_alolan", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink1, blink2, blink3, blink4),
condition = { it.isBattling },
animations = arrayOf(
singleBoneLook(),
SingleBoneLookAnimation(head2, false, false, disableX = false, disableY = false),
SingleBoneLookAnimation(head3, false, false, disableX = false, disableY = false),
bedrock("exeggutor_alolan", "battle_idle")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("exeggutor_alolan", "faint") else null
}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedW
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
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.PoseType
@@ -41,8 +40,6 @@ class NidokingModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, B
lateinit var standing: Pose
lateinit var walk: Pose
override val cryAnimation = CryProvider { bedrockStateful("nidoking", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("nidoking", "blink")}
standing = registerPose(
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedW
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
@@ -41,8 +40,6 @@ class NidoqueenModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
lateinit var standing: Pose
lateinit var walk: Pose
override val cryAnimation = CryProvider { bedrockStateful("nidoqueen", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("nidoqueen", "blink")}
val spg = quirk(secondsBetweenOccurrences = 600F to 900F) { bedrockStateful("nidoqueen", "spgs_happy_place") }
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
@@ -38,8 +37,6 @@ class NidoranfModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, Q
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("nidoranf", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("nidoranf", "blink") }
standing = registerPose(
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.QuadrupedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
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.PoseType.Companion.MOVING_POSES
@@ -38,8 +37,6 @@ class NidoranmModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, Q
lateinit var standing: Pose
lateinit var walk: Pose
override val cryAnimation = CryProvider { bedrockStateful("nidoranm", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("nidoranm", "blink") }
standing = registerPose(
Loaded 100 of 2717 files, more files were not shown because too many files have changed in this diff. Show more