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
2732 changed files with 122740 additions and 226789 deletions

No files matched your search

+41 -184
View File
@@ -1,163 +1,52 @@
# Changelog
## [1.7.0 (Month xth, 2025)](#1-7-0)
### 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.
## [1.6.1 (January 26th, 2025)](#1-6-1)
## [1.6.1 (Release TBD)](#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.
@@ -165,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!"
@@ -2129,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.fabric.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" ]
@@ -1,135 +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;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import java.util.function.Function;
public interface Rollable {
Vector3f FORWARDS = new Vector3f(0, 0, 1);
Vector3f LEFT = new Vector3f(1, 0, 0);
Vector3f UP = new Vector3f(0, 1, 0);
@Nullable Matrix3f getOrientation();
Rollable updateOrientation(Function<Matrix3f, Matrix3f> update);
/**
* @return The forward vector (0, 0, 1) after the rotation matrix is applied.
*/
default Vector3f getForwardVector() {
if (getOrientation() == null) return new Vector3f(FORWARDS);
return getOrientation().transform(FORWARDS, new Vector3f());
}
/**
* @return The left vector (1, 0, 0) after the rotation matrix is applied.
*/
default Vector3f getLeftVector() {
if (getOrientation() == null) return new Vector3f(LEFT);
return getOrientation().transform(LEFT, new Vector3f());
}
/**
* @return The up vector (0, 1, 0) after the rotation matrix is applied.
*/
default Vector3f getUpVector() {
if (getOrientation() == null) return new Vector3f(UP);
return getOrientation().transform(UP, new Vector3f());
}
/**
*
* @param yaw change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateYaw(float yaw) {
return updateOrientation(prev -> prev.rotateY((float) Math.toRadians(-yaw)));
}
/**
*
* @param pitch change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotatePitch(float pitch) {
return updateOrientation(prev -> prev.rotateX((float) Math.toRadians(-pitch)));
}
/**
*
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateRoll(float roll) {
return updateOrientation(prev -> prev.rotateZ((float) Math.toRadians(-roll)));
}
/**
* Order matters when rotating an object. If you need a different order, individually call rotateYaw, rotatePitch, and rotateRoll
*
* @param yaw change in degrees
* @param pitch change in degrees
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotate(float yaw, float pitch, float roll) {
return rotateYaw(yaw).rotatePitch(pitch).rotateRoll(roll);
}
/**
* @return The yaw angle in degrees as a float.
* Value is between [-90, 90] with 0 being straight forward, 90 being straight down, and -90 being straight up
*/
default float getYaw() {
return (float) (180F - Math.toDegrees(FORWARDS.angleSigned(getForwardVector(), UP)));
}
/**
* Get the pitch angle in degrees as a float.
* Value is between [-180, 180] with 0 being South, 90 being West, 180/-180 being North, and -90 being East
*/
default float getPitch() {
return (float) Math.toDegrees(Math.asin(getForwardVector().y));
}
/**
* Get the roll angle in degrees as a float.
* Value is between [-180, 180] with 0 being no roll (normal), 90 being 90 degrees CW, 180/-180 being 180 degrees CW/CCW, and -90 being 90 degrees CCW
*/
default float getRoll() {
var upVector = getUpVector();
var forwardVector = getForwardVector();
return (float) Math.toDegrees(upVector.angleSigned(UP, forwardVector));
}
/**
* Get the angular velocity in degrees per second as a Vec3
*/
Vec3 angRollVel();
public void rotationOnMouseXY( double yMouse, double xMouse );
boolean shouldRoll();
boolean useMouseInputRoll();
void clearRotation();
}
@@ -1,96 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Shadow private Entity entity;
@Shadow @Final private Quaternionf rotation;
@Shadow @Final private Vector3f forwards;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow public abstract float getXRot();
@Shadow public abstract float getYRot();
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@Unique boolean disableRollableCameraDebug = false;
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void open_camera$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof Rollable rollable) || disableRollableCameraDebug) return;
if (!rollable.shouldRoll() && rollable.getOrientation() != null) {
if(this.returnTimer < 1) {
//Rotation is taken from entity since we no longer handle mouse ourselves
//Stops a period of time when you can't input anything.
rollable.getOrientation().set(
new Matrix3f()
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
);
if(rollAngleStart == 0){
this.returnTimer = 1;
rollable.clearRotation();
return;
}
rollable.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
this.returnTimer = 1;
rollable.clearRotation();
}
return;
}
if (rollable.getOrientation() == null) return;
applyRotation();
this.returnTimer = 0;
this.rollAngleStart = rollable.getRoll();
ci.cancel();
}
@Unique
private void applyRotation(){
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
var newRotation = rollable.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (this.minecraft.options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = rollable.getPitch();
this.yRot = rollable.getYaw();
this.forwards.set(rollable.getForwardVector());
this.up.set(rollable.getUpVector());
this.left.set(rollable.getLeftVector());
}
}
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
@Mixin(HumanoidModel.class)
public class HumanoidModelMixin {
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
@Shadow @Final
public ModelPart head;
@Shadow @Final
public ModelPart rightArm;
@Shadow @Final
public ModelPart leftArm;
@Shadow @Final
public ModelPart rightLeg;
@Shadow @Final
public ModelPart leftLeg;
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
public void cobblemon$animateRiders(
LivingEntity entity,
float limbSwing,
float limbSwingAmount,
float ageInTicks,
float netHeadYaw,
float headPitch,
CallbackInfo ci
) {
if (relevantPartsByName.isEmpty()) {
relevantPartsByName.put("arm_left", leftArm);
relevantPartsByName.put("arm_right", rightArm);
relevantPartsByName.put("leg_left", leftLeg);
relevantPartsByName.put("leg_right", rightLeg);
relevantPartsByName.put("head", head);
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity && entity instanceof AbstractClientPlayer) {
MountedPlayerRenderer.INSTANCE.animate((PokemonEntity) vehicle, (AbstractClientPlayer) entity, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -8,21 +8,16 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import org.joml.Quaternionf;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemInHandRenderer.class)
@@ -32,15 +27,5 @@ public class ItemInHandRendererMixin {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
else if (abstractClientPlayer instanceof Rollable rollable && rollable.shouldRoll()) {
}
}
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 1))
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
if (!(Minecraft.getInstance().player instanceof Rollable rollable && rollable.shouldRoll())) {
instance.mulPose(quaternion);
}
}
}
@@ -12,15 +12,9 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
@@ -34,70 +28,4 @@ public abstract class LivingEntityMixin {
}
}
/*
@Inject(method = "travelRidden", at = @At(value = "HEAD"))
private void cobblemon$altTravelRidden(Player player, Vec3 travelVector)
{
this.setSpeed(this.getRiddenSpeed(player));
this.travel(vec3);
}
/*
// Shadow the original method so we can call it.
@Shadow
protected abstract boolean isControlledByLocalInstance();
/**
* Redirect the call to isControlledByLocalInstance() within travelRidden.
* When travelRidden calls isControlledByLocalInstance(), it will get our modified result.
*/
/*
@Redirect(
method = "travelRidden(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/Vec3;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/LivingEntity;isControlledByLocalInstance()Z"
)
)
private boolean redirectIsControlledByLocalInstance(LivingEntity instance) {
// Return true if either the original method returns true or our custom condition is true.
return this.isControlledByLocalInstance() || this.b;
}
@Overwrite
private void travelRidden(Player player, Vec3 travelVector) {
Vec3 vec3 = this.getRiddenInput(player, travelVector);
this.tickRidden(player, vec3);
if (this.isControlledByLocalInstance()) {
this.setSpeed(this.getRiddenSpeed(player));
this.travel(vec3);
} else {
this.calculateEntityAnimation(false);
this.setDeltaMovement(Vec3.ZERO);
this.tryCheckInsideBlocks();
}
}
@ModifyArg(
method = "travelRidden",
at = @At(
value = "INVOKE",
target = "setDeltaMovement(Lnet/minecraft/world/phys/Vec3;)V"
),
index = 0 // the first (and only) argument
)
private Vec3 modifyDeltaMovementArgument(Vec3 original) {
// Optionally, check if original is Vec3.ZERO, to only replace that call:
if (original.equals(Vec3.ZERO)) {
// Return your custom vector instead of Vec3.ZERO
return original;
}
// Otherwise, return the original value
return original;
}
*/
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
@@ -23,21 +22,16 @@ import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MouseHandler.class)
public class MouseHandlerMixin {
@Shadow private double accumulatedScrollY;
@Shadow @Final private Minecraft minecraft;
@Shadow
private double accumulatedScrollY;
@Unique SmoothDouble xMouseSmoother = new SmoothDouble();
@Unique SmoothDouble yMouseSmoother = new SmoothDouble();
@Unique SmoothDouble pitchSmoother = new SmoothDouble();
@Unique SmoothDouble rollSmoother = new SmoothDouble();
@Unique SmoothDouble yawSmoother = new SmoothDouble();
@Shadow @Final private Minecraft minecraft;
@Shadow @Final private SmoothDouble smoothTurnY;
@@ -99,6 +93,7 @@ public class MouseHandlerMixin {
)
)
public boolean cobblemon$modifyRotation(LocalPlayer player, double cursorDeltaX, double cursorDeltaY, @Local(argsOnly = true) double d) {
// I know this technically doesn't need to be a WrapWithCondition, but it'll make things easier later for riding.
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -110,62 +105,7 @@ public class MouseHandlerMixin {
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity));
return false;
}
if (!(player instanceof Rollable rollable)) return true;
if (!rollable.shouldRoll()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return true;
}
//maybe have a rollable x mouse and rollable y mouse that take the xmousesmoother
//and y mouse smoother ( which are right now named pichsmoother and roll smoother)
// values and feed it to those rollable functions which will be called by the
//specific ride controller where rollable.rotate can then be called and used
//controller specific.
var yMouse = yMouseSmoother.getNewDeltaValue(cursorDeltaY * 0.15f, d);
var xMouse = xMouseSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
rollable.rotationOnMouseXY( yMouse, xMouse );
//get the current angular velocity requested to be applied
//by the ride controller
var angRot = rollable.angRollVel();
//Multiply against deltatime to get a consistent turn
//independent of framerate
if( angRot != null ) {
//var yaw = angRot.x * d;
//Add the yaw to the mouse here... this all needs changed. It should
//just map which 0 1 or 2 of the three it is going to modulate with
//what
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
//yaw += yawSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
//var pitch = angRot.y * d;
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
//pitch += pitchSmoother.getNewDeltaValue(cursorDeltaY * 0.15f, d);
//var roll = angRot.z * d;
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
rollable.rotate((float) yaw, (float) pitch, (float) roll);
}
return false;
}
@Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z", ordinal = 0))
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci, @Local(ordinal = 1) double e) {
if (minecraft.player == null) return;
if (!(minecraft.player instanceof Rollable rollable)) return;
if (!rollable.shouldRoll()) return;
if (minecraft.isPaused()) return;
var pitch = pitchSmoother.getNewDeltaValue(0, e);
var roll = rollSmoother.getNewDeltaValue(0, e);
rollable.rotate(0.0F, (float)pitch, (float)roll);
return true;
}
}
@@ -10,15 +10,11 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.Rollable;
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.riding.Rideable;
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.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
import com.cobblemon.mod.common.pokemon.FormData;
@@ -29,10 +25,6 @@ import com.cobblemon.mod.common.util.CompoundTagExtensionsKt;
import com.cobblemon.mod.common.util.CompoundTagUtilities;
import com.cobblemon.mod.common.util.DataKeys;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
@@ -45,18 +37,14 @@ import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -64,11 +52,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements Rollable, ScannableEntity {
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity {
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@@ -86,7 +73,6 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Shadow public abstract void displayClientMessage(Component message, boolean overlay);
@Unique private Matrix3f orientation = null;
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
@@ -192,10 +178,10 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@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;
@@ -204,157 +190,34 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@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
public LivingEntity resolveEntityScan() {
return this;
}
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (shouldRoll()) {
this.absMoveTo(x, y, z);
this.setYRot(yaw % 360.0f);
this.setXRot(pitch % 360.0f);
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
}
else {
this.absMoveTo(x, y, z);
this.absRotateTo(yaw, pitch);
}
}
@Inject(method = "tick", at = @At("HEAD"))
public void tick(CallbackInfo ci) {
if (this.shouldRoll()) {
// Proof of concept for yaw movement
if (this.getMainHandItem().is(Items.STICK)) {
this.rotateYaw(2);
}
else if (this.getMainHandItem().is(Items.BLAZE_ROD)) {
this.rotateYaw(-2);
}
}
}
@Override
public Matrix3f getOrientation() {
return orientation;
}
@Override
public Rollable updateOrientation(Function<Matrix3f, Matrix3f> update) {
//Disabled to allow non mouse inputs to modify roll
if (!shouldRoll()) return this;
if (this.orientation == null) {
this.orientation = new Matrix3f();
this.rotate(getYRot() - 180, getXRot(), 0);
}
this.orientation = update.apply(this.orientation);
setYRot(this.getYaw());
setXRot(this.getPitch());
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
return this;
}
@Override
public boolean shouldRoll() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return false;
if (player.isHolding(Items.DIAMOND)) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public void rotationOnMouseXY( double yMouse, double xMouse ) {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return;
pokemonEntity.getRiding().rotationOnMouseXY(pokemonEntity, player, yMouse, xMouse);
}
@Override
public boolean useMouseInputRoll() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return true;
if (player.isHolding(Items.DIAMOND)) return false;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useMouseInputRoll(pokemonEntity);
}
@Override
public Vec3 angRollVel() {
var ret = new Vec3(0.0, 0.0, 0.0);
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return ret;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return ret;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return ret;
return pokemonEntity.getRiding().angRollVel(pokemonEntity, player);
}
@Override
public void clearRotation() {
this.orientation = null;
}
@WrapOperation(
method = "rideTick()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;wantsToStopRiding()Z")
)
public boolean delegateDismountToController(Player instance, Operation<Boolean> original) {
if (this.getVehicle() instanceof Rideable vehicle) {
return vehicle.getRiding().dismountOnShift() && original.call(instance);
}
return original.call(instance);
}
}
}
@@ -8,14 +8,11 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -23,33 +20,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerRenderer.class)
public class PlayerRendererMixin {
private static boolean disableRollableRenderDebug = false;
@Inject(method = "render", at = @At(value = "HEAD"))
public void renderPlayer$head(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
if(player instanceof Rollable && !disableRollableRenderDebug) {
Rollable rollable = (Rollable) player;
if(rollable.shouldRoll()){
poseStack.pushPose();
Matrix4f transformationMatrix = new Matrix4f();
Vector3f center = new Vector3f(0, player.getBbHeight()/2, 0);
transformationMatrix.translate(center);
//transformationMatrix.rotate(180f.toRadians() - (rollable.getYaw()).toRadians(), rollable.getUpVector(), transformationMatrix);
transformationMatrix.rotate((float) Math.toRadians(-rollable.getPitch()), rollable.getLeftVector(), transformationMatrix);
transformationMatrix.rotate((float) Math.toRadians(-rollable.getRoll()), rollable.getForwardVector(), transformationMatrix);
transformationMatrix.translate(center.negate(new Vector3f()));
poseStack.mulPose(transformationMatrix);
}
}
}
@Inject(method = "render", at = @At(value = "TAIL"))
public void renderPlayer$tail(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
@Inject(method = "render", at = @At(value = "TAIL"))
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
ClientPlayerIcon.Companion.onRenderPlayer(player);
if(player instanceof Rollable && !disableRollableRenderDebug && ((Rollable) player).shouldRoll()){
poseStack.popPose();
}
}
}
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket;
import net.minecraft.client.player.LocalPlayer;
import org.joml.Matrix3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LocalPlayer.class)
public class LocalPlayerMixin {
@Unique Matrix3f cobblemon$lastOrientation;
@Inject(method = "sendPosition", at = @At("TAIL"))
private void cobblemon_updateRotationMatrix(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
}
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
private void cobblemon_updateRotationMatrixPassenger(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
}
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class)
public abstract class EntityMixin {
@Shadow @Nullable public abstract Entity getVehicle();
@Inject(method = "updateInWaterStateAndDoWaterCurrentPushing", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/Entity;wasTouchingWater:Z", ordinal = 1), cancellable = true)
public void cobblemon$verifyActuallyTouchingWater(CallbackInfo ci) {
if(this.getVehicle() instanceof PokemonEntity) {
ci.cancel();
}
}
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.renderer;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import org.spongepowered.asm.mixin.Mixin;
/**
* @author landonjw
*/
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin {
// @Redirect(
// method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V",
// at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;hasVehicle()Z"),
// slice = @Slice(
// from = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerpAngleDegrees(FFF)F"),
// to = @At(value = "TAIL")
// )
// )
// private boolean dodgeRenderer(LivingEntity instance) {
// return false;
// }
}
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.renderer;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* @author landonjw
*/
@Mixin(PlayerRenderer.class)
public class PlayerEntityRendererMixin {
@Inject(
method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V",
at = @At("HEAD")
)
private void doABibarelRoll$modifyRoll(AbstractClientPlayer player, PoseStack poseStack, float a, float b, float partialTicks, float i, CallbackInfo ci) {
if (player.isPassenger()) {
var vehicle = player.getVehicle();
if (vehicle instanceof PokemonEntity entity) {
MountedPlayerRenderer.INSTANCE.render(player, entity, poseStack);
}
}
}
}
@@ -1,62 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.riding;
import net.minecraft.client.player.LocalPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(LocalPlayer.class)
public class JetMixin {
@Shadow private int jumpRidingTicks;
@Shadow private float jumpRidingScale;
private float jetStrength;
// @Redirect(method = "tickMovement", at = @At(target = "Lnet/minecraft/entity/JumpingMount;getJumpCooldown()I", value = "INVOKE"))
// public int cobblemon$tryHandlingJet(JumpingMount mount) {
// if(mount instanceof PokemonEntity pokemon) {
//// if(pokemon.getDataTracker().get(PokemonEntity.MOVING)) {
//// ++this.field_3938;
//// if (this.field_3938 < 11) {
//// this.jetStrength = (float) this.field_3938 * 0.1F;
//// } else {
// this.mountJumpStrength = 0.8F + 2.0F / (float) (this.field_3938 - 9) * 0.1F;
//// }
//// } else {
//// this.field_3938 = 0;
//// this.jetStrength = 0;
//// }
//
//// return -1;
// }
//
// return 0;
// }
@Inject(
method = "aiStep",
at = @At(
target = "Lnet/minecraft/client/player/LocalPlayer;jumpRidingScale:F",
value = "FIELD",
ordinal = 4,
shift = At.Shift.AFTER
),
locals = LocalCapture.CAPTURE_FAILSOFT
)
public void cobblemon$applyCachedJumpStrength(CallbackInfo ci) {
this.jumpRidingScale = this.jetStrength;
}
}
@@ -12,14 +12,6 @@ object CobblemonBuildDetails {
const val BRANCH = "{{ branch | default("Not Specified") }}"
fun smallCommitHash(): String {
if(this.GIT_COMMIT.equals("Not Specified")) {
return "Unknown"
}
return this.GIT_COMMIT.substring(0, 7)
}
fun version(): String {
return this.VERSION + if(this.SNAPSHOT) "+${this.smallCommitHash()}" else ""
}
}
@@ -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
@@ -201,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()
@@ -220,7 +218,7 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel, this.config.maxPokemonLevel, this.config.maxPokemonFriendship, this.config.maxDynamaxLevel).sendToPlayer(it)
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)
@@ -258,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
@@ -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
@@ -51,14 +51,7 @@ import com.cobblemon.mod.common.client.net.storage.pc.OpenPCHandler
import com.cobblemon.mod.common.client.net.storage.pc.SetPCBoxPokemonHandler
import com.cobblemon.mod.common.client.net.storage.pc.SetPCPokemonHandler
import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
import com.cobblemon.mod.common.client.net.trade.TradeAcceptanceChangedHandler
import com.cobblemon.mod.common.client.net.trade.TradeCancelledHandler
import com.cobblemon.mod.common.client.net.trade.TradeCompletedHandler
import com.cobblemon.mod.common.client.net.trade.TradeOfferExpiredHandler
import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.client.net.trade.*
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
@@ -100,37 +93,14 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePart
import com.cobblemon.mod.common.net.messages.client.storage.party.MoveClientPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeCompletedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.trade.*
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.*
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
@@ -141,7 +111,6 @@ import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPoke
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
@@ -161,21 +130,9 @@ import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonT
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptancePacket
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.PerformTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
import com.cobblemon.mod.common.net.messages.server.trade.*
import com.cobblemon.mod.common.net.serverhandling.*
import com.cobblemon.mod.common.net.serverhandling.battle.*
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
@@ -187,7 +144,6 @@ import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandle
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.OrientationPacketHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
@@ -210,12 +166,7 @@ import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonTo
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.net.serverhandling.trade.*
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -492,9 +443,6 @@ object CobblemonNetwork {
// NPC packets
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
// Orientation packet(s)
list.add(PacketRegisterInfo(C2SUpdateOrientationPacket.ID, C2SUpdateOrientationPacket::decode, OrientationPacketHandler))
return list
}
@@ -502,4 +450,4 @@ object CobblemonNetwork {
//logger.info("[S->C] ${packet.type()}")
Cobblemon.implementation.networkManager.sendPacketToPlayer(player, packet)
}
}
}
@@ -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) }
}
@@ -49,7 +49,6 @@ import com.cobblemon.mod.common.api.reactive.EventObservable
import com.cobblemon.mod.common.api.reactive.Observable.Companion.filter
import com.cobblemon.mod.common.api.reactive.Observable.Companion.map
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.events.SelectDriverEvent
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.server.level.ServerPlayer
@@ -233,12 +232,4 @@ object CobblemonEvents {
val HATCH_EGG_PRE = CancelableObservable<HatchEggEvent.Pre>()
@JvmField
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
// -------------------------------------------------------------------------------------
//
// Riding
//
// -------------------------------------------------------------------------------------
@JvmField
val SELECT_DRIVER = EventObservable<SelectDriverEvent>()
}
@@ -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)
}
},
)
@@ -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).
@@ -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)
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.pokemon
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
@@ -33,7 +32,6 @@ import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
@@ -48,7 +46,6 @@ import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapt
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
@@ -103,8 +100,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.registerTypeAdapter(RideController::class.java, RideControllerAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
@@ -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
@@ -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
@@ -1,25 +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.riding
import net.minecraft.world.entity.PlayerRideableJumping
/**
* Represents an entity that supports riding.
*
* @since 1.5.0
*/
interface Rideable : PlayerRideableJumping {
/**
* Denotes the manager responsible for handling any instance of riding taking place on an entity
*/
val riding: RidingManager
}
@@ -1,149 +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.riding
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import kotlin.math.sin
data class RidingManager(val entity: PokemonEntity) {
var lastSpeed = 0F
var states: MutableMap<ResourceLocation, RidingState> = mutableMapOf()
var deltaRotation = Vec2.ZERO
val runtime: MoLangRuntime by lazy {
MoLangRuntime()
.setup()
.withQueryValue("entity", entity.struct)
.also {
it.environment.query.addFunction("passenger_count") { DoubleValue(entity.passengers.size.toDouble()) }
}
}
fun <T : RidingState> getState(id: ResourceLocation, constructor: (PokemonEntity) -> T): T {
val storedState = states[id]
if (storedState == null) {
val newState = constructor(entity)
states[id] = newState
return newState
}
return storedState as T
}
fun getController(entity: PokemonEntity): RideController? {
return entity.pokemon.riding.controller?.takeIf { it.condition.invoke(entity) }
}
/**
* Responsible for handling riding conditions and transitions amongst controllers. This will tick
* whenever the entity receives a tickControlled interaction.
*/
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val controller = getController(entity) ?: return
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
//val speedPlayer = driver.deltaMovement.horizontalDistance()
//driver.displayClientMessage(Component.literal("Speed player: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$speedPlayer b/t")), true)
val speedEntity = entity.deltaMovement.horizontalDistance()
driver.displayClientMessage(Component.literal("Speed Entity: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} b/t")), true)
}
fun speed(entity: PokemonEntity, driver: Player): Float {
val controller = getController(entity) ?: return 0.05F
this.lastSpeed = controller.speed(entity, driver)
return this.lastSpeed
}
fun controlledRotation(entity: PokemonEntity, driver: Player): Vec2 {
val controller = getController(entity) ?: return entity.rotationVector
val previousRotation = entity.rotationVector
val rotation = controller.rotation(entity, driver)
this.deltaRotation = Vec2(rotation.x - previousRotation.x, rotation.y - previousRotation.y)
return rotation
}
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
val controller = getController(entity) ?: return
return controller.clampPassengerRotation(entity, driver)
}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
val controller = getController(entity) ?: return
return controller.updatePassengerRotation(entity, driver)
}
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val controller = getController(entity) ?: return Vec3.ZERO
return controller.velocity(entity, driver, input)
}
fun canJump(entity: PokemonEntity, driver: Player): Boolean {
val controller = getController(entity) ?: return false
return controller.canJump(entity, driver)
}
fun jumpVelocity(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
val controller = getController(entity) ?: return Vec3.ZERO
return controller.jumpForce(entity, driver, jumpStrength)
}
fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
val controller = getController(entity) ?: return null
return controller.gravity(entity, regularGravity)
}
fun shouldRoll(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.shouldRoll(entity)
}
fun rotationOnMouseXY( entity: PokemonEntity, driver: Player, yMouse: Double, xMouse: Double ){
val controller = getController(entity) ?: return
controller.rotationOnMouseXY( entity, driver, yMouse, xMouse )
}
/**
* Determines whether mouse input will affect the rotation of
* the rollable object
*/
fun useMouseInputRoll(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return true
return controller.useMouseInputRoll(entity)
}
fun turnOffOnGround(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.turnOffOnGround(entity)
}
fun angRollVel(entity: PokemonEntity, driver: Player): Vec3 {
val controller = getController(entity) ?: return Vec3(0.0, 0.0, 0.0)
return controller.angRollVel(entity, driver)
}
fun dismountOnShift(): Boolean {
val controller = getController(entity) ?: return false
return controller.dismountOnShift(entity)
}
}
@@ -1,49 +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.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
class RidingProperties(
val seats: List<Seat> = listOf(),
val conditions: List<Expression> = listOf(),
val controller: RideController? = null
) {
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RidingProperties {
val seats: List<Seat> = buffer.readList { _ -> Seat.decode(buffer) }
val conditions = buffer.readList { buffer.readString().asExpression() }
val controller = buffer.readNullable { _ ->
val key = buffer.readIdentifier()
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
controller.decode(buffer)
return@readNullable controller
}
return RidingProperties(seats = seats, conditions = conditions, controller = controller)
}
}
val canRide: Boolean
get() = seats.isNotEmpty() && controller != null
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(seats) { _, seat -> seat.encode(buffer) }
buffer.writeCollection(conditions) { _, condition -> buffer.writeString(condition.getString()) }
buffer.writeNullable(controller) { _, controller -> controller.encode(buffer) }
}
}
@@ -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.riding
/**
* Honestly having a base interface is probably unnecessary I'm just considering whether there's going to be some
* kind of shared state later.
*
* @author Hiroku
* @since October 6th, 2024
*/
interface RidingState
@@ -1,91 +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.riding
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.controller.RidingAnimation
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.phys.Vec3
/**
* Seat Properties are responsible for the base information that would then be used to construct a Seat on an entity.
*/
data class Seat(
val locator: String = "seat1",
val offset: Vec3 = Vec3.ZERO,
val poseOffsets: MutableList<SeatPoseOffset> = mutableListOf(),
val poseAnimations: MutableList<SeatPoseAnimations> = mutableListOf()
) : Encodable {
fun getOffset(poseType: PoseType) : Vec3 {
return poseOffsets.firstOrNull { poseType in it.poseTypes }?.offset ?: offset
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(locator)
buffer.writeDouble(offset.x)
buffer.writeDouble(offset.y)
buffer.writeDouble(offset.z)
buffer.writeCollection(poseOffsets) { _, offset -> offset.encode(buffer) }
buffer.writeCollection(poseAnimations) { _, animations -> animations.encode(buffer) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : Seat {
return Seat(
buffer.readString(),
Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()),
buffer.readList { SeatPoseOffset.decode(buffer) },
buffer.readList { SeatPoseAnimations.decode(buffer) }
)
}
}
}
class SeatPoseOffset {
val poseTypes = mutableSetOf<PoseType>()
var offset = Vec3.ZERO
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(poseTypes) { _, poseType -> buffer.writeString(poseType.name) }
buffer.writeDouble(offset.x)
buffer.writeDouble(offset.y)
buffer.writeDouble(offset.z)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : SeatPoseOffset {
val offset = SeatPoseOffset()
offset.poseTypes.addAll(buffer.readList { buffer.readString().let { PoseType.valueOf(it) } })
offset.offset = Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble())
return offset
}
}
}
class SeatPoseAnimations {
val poseTypes = mutableSetOf<PoseType>()
var animations = mutableListOf<RidingAnimation>()
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(poseTypes) { _, poseType -> buffer.writeString(poseType.name) }
buffer.writeCollection(animations) { _, animation -> animation.encode(buffer) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : SeatPoseAnimations {
val animations = SeatPoseAnimations()
animations.poseTypes.addAll(buffer.readList { buffer.readString().let { PoseType.valueOf(it) } })
animations.animations = buffer.readList { RidingAnimation.decode(buffer) }
return animations
}
}
}
@@ -1,127 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.controller
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingState
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.writeIdentifier
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
/**
* A riding controller is the internal control mechanism for determining how a pokemon
* handles during riding. Controllers come into play once the pokemon being ridden has
* established the path it's capable of. For instance, a land based controller could have
* the pokemon experience drifting after movement, or generic movement. It's up to the
* controller to decide how the pokemon behaves.
*
* @since x.x.x
*/
interface RideController : Encodable, Decodable {
/** A reference key used to denote the individual controller */
val key: ResourceLocation
/**
* Specifies the provider of poses which helps control updating the current pose on the pokemon
* being ridden. Controllers are expected to define the pose types responsible for posing the entity
* through all expected conditions. It should define a default pose, that will be used in all cases
* where a pose does not meet the current conditions for the current mount. Typically, this will be
* an idle pose.
*/
val poseProvider: PoseProvider
/**
* Represents a condition that must be met for this controller to be active. For instance, this can be used
* to evaluate things such as current velocity or block state of the entity.
*/
val condition: (PokemonEntity) -> Boolean
/**
* Specifies the pose the ridden mount should be positioned in. This queries the pose provider using
* the entity as a means of determining the conditions the entity is currently under. This allows for
* dynamic pose updates.
*/
fun pose(entity: PokemonEntity) : PoseType {
return this.poseProvider.select(entity)
}
/**
* Calculates the current speed of the mount.
*/
fun speed(entity: PokemonEntity, driver: Player) : Float
/**
* Sets the rotation of the mount. This is typically based on the controlling driver and is manipulated as
* necessary.
*/
fun rotation(entity: PokemonEntity, driver: LivingEntity) : Vec2
/**
* Sets the change current change in rotation for that tick in three
* dimensions
*/
fun angRollVel(entity: PokemonEntity, driver: Player): Vec3 {
return Vec3(0.0, 0.0, 0.0)
}
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
driver.yRot = entity.yRot
}
/**
* Manipulates the movement input as necessary by the controller. This is primarily used to apply limits
* to the types of movements. For instance, we can apply limits to any sort of sideways movement input, so that
* it would otherwise be slower than normal forward movement.
*/
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3) : Vec3
fun canJump(entity: PokemonEntity, driver: Player) : Boolean
fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int) : Vec3
fun gravity(entity: PokemonEntity, regularGravity: Double) : Double? = null
fun shouldRoll(entity: PokemonEntity): Boolean = false
fun useMouseInputRoll(entity: PokemonEntity): Boolean = false
//If function is not overwritten by controllers then just perform the defualt
//rolling function
fun rotationOnMouseXY( entity: PokemonEntity, driver: Player, yMouse: Double, xMouse: Double ){
if(driver !is Rollable) return
val rollable = driver as Rollable
rollable.rotate(0.0f, yMouse.toFloat(), xMouse.toFloat())
}
fun turnOffOnGround(entity: PokemonEntity): Boolean = false
fun dismountOnShift(entity: PokemonEntity): Boolean = false
fun getRuntime(entity: PokemonEntity) = entity.riding.runtime
fun <T : RidingState> getState(entity: PokemonEntity, constructor: (PokemonEntity) -> T): T {
return entity.riding.getState(key, constructor)
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeIdentifier(this.key)
}
}
@@ -1,32 +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.riding.controller
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
class RidingAnimation(
val fileName: String,
val animationName: String
) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(fileName)
buffer.writeString(animationName)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : RidingAnimation {
return RidingAnimation(
buffer.readString(),
buffer.readString()
)
}
}
}
@@ -1,15 +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.riding.controller.posing
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import java.util.function.Predicate
data class PoseOption(val pose: PoseType, val condition: Predicate<PokemonEntity>)
@@ -1,31 +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.riding.controller.posing
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
class PoseProvider(private val fallback: PoseType) {
private val options = mutableListOf<PoseOption>()
fun select(entity: PokemonEntity) : PoseType {
return this.options.stream()
.filter { it.condition.test(entity) }
.map { it.pose }
.findFirst()
.orElse(this.fallback)
}
fun with(option: PoseOption) : PoseProvider {
this.options.add(option)
return this
}
}
@@ -1,33 +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.riding.events
import net.minecraft.world.entity.LivingEntity
data class SelectDriverEvent(val options: Set<LivingEntity>) {
private var result: DriverSuggestion? = null
fun result(): LivingEntity? {
return this.result?.entity
}
/**
* Suggests a particular living entity as a potential driver. Only non-negative
* values are permitted by this function. All priority values with lesser priorities
* will be outright ignored.
*/
fun suggest(target: LivingEntity, priority: Int) {
if (priority >= 0 && priority > (this.result?.priority ?: -1)) {
this.result = DriverSuggestion(target, priority)
}
}
data class DriverSuggestion(val entity: LivingEntity, val priority: Int)
}
@@ -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
}
@@ -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
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.rules.component
import com.bedrockk.molang.Expression
import com.bedrockk.molang.ast.BooleanExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -34,7 +33,7 @@ class FilterRuleComponent : SpawnRuleComponent {
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val allow: Expression = BooleanExpression(false)
val allow: Expression = BooleanExpression(true)
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return if (spawnSelector.selects(detail) && contextSelector.selects(ctx)) {
@@ -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()
@@ -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,
@@ -21,18 +21,18 @@ import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.Multimap
import java.util.UUID
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import java.util.*
import org.joml.Vector3f
fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canRide: Boolean): InteractWheelGUI {
fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean): InteractWheelGUI {
val mountShoulder = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_shoulder.png"),
tooltipText = "cobblemon.ui.interact.mount.shoulder",
onPress = {
if (canMountShoulder) {
InteractPokemonPacket(pokemonID, true, false).sendToServer()
InteractPokemonPacket(pokemonID, true).sendToServer()
closeGUI()
}
}
@@ -41,32 +41,15 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canRide
iconResource = cobblemonResource("textures/gui/interact/icon_held_item.png"),
tooltipText = "cobblemon.ui.interact.give.item",
onPress = {
InteractPokemonPacket(pokemonID, false, false).sendToServer()
InteractPokemonPacket(pokemonID, false).sendToServer()
closeGUI()
}
)
val ride = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_held_item.png"),
tooltipText = "cobblemon.ui.interact.ride",
onPress = {
if (canRide) {
InteractPokemonPacket(pokemonID, false, true).sendToServer()
closeGUI()
}
}
)
val options: Multimap<Orientation, InteractWheelOption> = ArrayListMultimap.create()
options.put(Orientation.TOP_RIGHT, giveItem)
if(canRide) {
options.put(Orientation.BOTTOM_LEFT, ride)
}
if (canMountShoulder) {
options.put(Orientation.TOP_LEFT, mountShoulder)
}
CobblemonEvents.POKEMON_INTERACTION_GUI_CREATION.post(PokemonInteractionGUICreationEvent(pokemonID, canMountShoulder, options))
return InteractWheelGUI(options, Component.translatable("cobblemon.ui.interact.pokemon"))
}
@@ -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,
@@ -15,6 +15,6 @@ import net.minecraft.client.Minecraft
object InteractPokemonUIPacketHandler: ClientNetworkPacketHandler<InteractPokemonUIPacket> {
override fun handle(packet: InteractPokemonUIPacket, client: Minecraft) {
client.setScreen(createPokemonInteractGui(packet.pokemonID, packet.canMountShoulder, packet.canRide))
client.setScreen(createPokemonInteractGui(packet.pokemonID, packet.canMountShoulder))
}
}
@@ -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,7 +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 net.fabricmc.loader.impl.lib.sat4j.core.Vec
import org.joml.Matrix4f
import net.minecraft.world.phys.Vec3
@@ -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 {
@@ -51,7 +51,7 @@ open class BedrockActiveAnimation(
headPitch: Float,
intensity: Float
): Boolean {
return animation.run(model, state, state.animationSeconds - startedSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity).also {
return animation.run(context, model, state, state.animationSeconds - startedSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity).also {
if (!it) {
afterAction(context, state)
}
@@ -14,17 +14,15 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleOptions
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.genericRuntime
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.resolveDouble
import java.util.SortedMap
import java.util.TreeMap
import net.minecraft.CrashReport
import net.minecraft.ReportedException
import net.minecraft.client.Minecraft
@@ -35,6 +33,8 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import java.util.*
data class BedrockAnimationGroup(
val formatVersion: String,
@@ -149,40 +149,7 @@ data class BedrockAnimation(
/** Useful to have, gets set after loading the animation. */
var name: String = ""
fun run(
model: PosableModel,
state: PosableState,
animationSeconds: Float,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
intensity: Float
): Boolean {
return run(
model.context,
model.relevantPartsByName,
model.rootPart,
state,
animationSeconds,
limbSwing,
limbSwingAmount,
ageInTicks,
intensity
)
}
fun run(
context: RenderContext?,
relevantPartsByName: Map<String, ModelPart>,
rootPart: Bone?,
state: PosableState,
animationSeconds: Float,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
intensity: Float
): Boolean {
fun run(context: RenderContext, model: PosableModel, state: PosableState, animationSeconds: Float, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, intensity: Float): Boolean {
var animationSeconds = animationSeconds
if (shouldLoop) {
animationSeconds %= animationLength.toFloat()
@@ -197,7 +164,7 @@ data class BedrockAnimation(
runtime.environment.setSimpleVariable("age_in_ticks", DoubleValue(ageInTicks.toDouble()))
boneTimelines.forEach { (boneName, timeline) ->
val part = relevantPartsByName[boneName] ?: if (boneName == "root_part") (rootPart as? ModelPart) else null
val part = model.relevantPartsByName[boneName] ?: if (boneName == "root_part") (model.rootPart as ModelPart) else null
if (part !== null) {
if (!timeline.position.isEmpty()) {
val position = timeline.position.resolve(animationSeconds.toDouble(), runtime).scale(intensity.toDouble())
@@ -217,7 +184,7 @@ data class BedrockAnimation(
zRot += rotation.z.toFloat().toRadians()
}
} catch (e: Exception) {
val exception = IllegalStateException("Bad animation for entity: ${(context?.request(RenderContext.ENTITY))?.effectiveName()?.string ?: "unknown entity (riding animation?)"}", e)
val exception = IllegalStateException("Bad animation for entity: ${(model.context.request(RenderContext.ENTITY))!!.effectiveName().string}", e)
val crash = CrashReport("Cobblemon encountered an unexpected crash", exception)
val section = crash.addCategory("Animation Details")
section.setDetail("Pose", state.currentPose!!)
@@ -34,7 +34,7 @@ class BedrockPoseAnimation(val animation: BedrockAnimation) : PoseAnimation() {
}
override fun setupAnim(context: RenderContext, model: PosableModel, state: PosableState, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float, intensity: Float) {
animation.run(model, state, state.animationSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity)
animation.run(context, model, state, state.animationSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity)
}
override fun applyEffects(entity: Entity?, state: PosableState, previousSeconds: Float, newSeconds: Float) {
@@ -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") }
Loaded 100 of 2732 files, more files were not shown because too many files have changed in this diff. Show more