Compare commits

..
Author SHA1 Message Date
Polymeta 584983e616 fix wrong archive being picked up pipeline
Signed-off-by: Polymeta <4726322-Polymeta@users.noreply.gitlab.com>
2026-05-20 12:10:02 +01:00
Polymeta e86559b5d4 pipeline works again 🔥 2026-05-20 11:44:42 +01:00
PolymetaandPolymeta f83bc8c58f migrate away from archloom to neoloom 2026-05-20 10:23:16 +01:00
Polymeta 278e9afb1a bump JEI and fix NF crash
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-27 21:55:01 +01:00
Polymeta bb2f8793f4 small changes
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-21 22:29:27 +01:00
Polymeta 31697b3c73 Merge branch 'main' into feature/version-bump
# Conflicts:
#	common/build.gradle.kts
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/drop/ItemDropEntry.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/battles/ShowdownInterpreter.kt
#	fabric/build.gradle.kts
#	gradle/build-logic/src/main/kotlin/cobblemon.platform-conventions.gradle.kts
#	gradle/libs.versions.toml
#	neoforge/build.gradle.kts
2026-04-21 20:35:39 +01:00
Polymeta 45943a884a commit of shame
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-21 18:21:43 +01:00
Polymeta 3f6b8b6de8 clean up villager stuff and fix cooking pot seasoning slot highlighting
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-20 23:53:12 +01:00
Hiroku c160545cf1 Merge branch 'patch-1' into 'main'
Fixed Sleep Clause

See merge request cable-mc/cobblemon!2206
2026-04-20 04:20:24 +00:00
Hiroku 8bd9d113dc Merge branch 'perf/stores' into 'main'
perf: remove unnecessary copies in PCStore and reduce indirection in PokemonStore#get

See merge request cable-mc/cobblemon!2205
2026-04-19 23:37:43 +00:00
redmageman d7718b7f3e Updated SLEEP_CLAUSE to use the correct string that is recognized by showdown. 2026-04-19 15:59:57 -05:00
Qther 49944a073b perf: remove unnecessary copies in PCStore and reduce indirection in PokemonStore#get 2026-04-19 23:57:57 +08:00
Hiroku a7ed6400b2 Merge branch 'Added-mark-ordering-via-index-number' into 'main'
Added new Sort Order field to sort marks by

See merge request cable-mc/cobblemon!2204
2026-04-19 01:26:37 +00:00
Dragonwhisper92 188b504575 Merge branch 'main' into Added-mark-ordering-via-index-number
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/summary/widgets/screens/marks/MarksWidget.kt
2026-04-19 02:19:43 +01:00
Dragonwhisper92 e59d5ee6f3 Added new Sort Order field to sort marks by 2026-04-19 01:56:23 +01:00
Hiroku ea4d170756 Merge branch 'feature/starter-screen-category-order' into 'main'
feat: Starter screen category order

See merge request cable-mc/cobblemon!2196
2026-04-19 00:23:10 +00:00
Gitoido ef8a5ce76b refactor: clean up repeat func in OpenStarterUIPacket.kt 2026-04-19 02:30:06 +03:00
Hiroku 85d2d4aca8 Merge branch 'revert-adbfa160' into 'main'
Revert "Merge branch 'Added-mark-ordering-via-index-number' into 'main'"

See merge request cable-mc/cobblemon!2203
2026-04-18 23:04:47 +00:00
Hiroku 72a9b82bd9 Revert "Merge branch 'Added-mark-ordering-via-index-number' into 'main'"
This reverts merge request !2202
2026-04-18 22:57:45 +00:00
Hiroku adbfa16076 Merge branch 'Added-mark-ordering-via-index-number' into 'main'
Changed mark order to use indexNumber then namespace, rather than just namespace

See merge request cable-mc/cobblemon!2202
2026-04-18 21:56:22 +00:00
Dragonwhisper92 8055aed5ab Changed mark order to use indexNumber then namespace, rather than just namespace 2026-04-18 21:46:15 +01:00
Paul 95c0055932 Fixed NPC battle lock caused by non-turn induced switching 2026-04-18 16:18:38 +10:00
Hiroku d553257060 Merge branch 'patch-4' into 'main'
Fix Tatami Mat missing proper footstep sounds

See merge request cable-mc/cobblemon!2174
2026-04-18 06:05:27 +00:00
Hiroku 3069ae9722 Merge branch 'Update-ItemDrops-to-allow-data-components' into 'main'
Added Data Component support for species drops

See merge request cable-mc/cobblemon!2188
2026-04-18 05:05:20 +00:00
Paul f407051c4b lil changelog [skip-ci] 2026-04-18 11:41:17 +10:00
Hiroku 9f179bb573 Merge branch 'main' into 'main'
added chatter npc behaviour, script and associated lang files

See merge request cable-mc/cobblemon!2194
2026-04-18 01:33:44 +00:00
Polymeta 696df0e0e5 cooking pot preview is functional again
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-18 00:20:17 +01:00
terrainwax d57e6d058f fix: mojang doing stupide things with their package-info.java force us to do stupide things 2026-04-17 22:41:22 +02:00
Polymeta a87929019d cooking pot works again 🔥 🔥
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-17 19:12:30 +01:00
Polymeta 6105d15a03 Merge branch 'battle-lockup-fix' into 'main'
Battle lockup fix

See merge request cable-mc/cobblemon!2199
2026-04-17 12:23:40 +00:00
Polymeta 1176af0641 Merge branch 'vera-bait-changes' into 'main'
Bait Changes

See merge request cable-mc/cobblemon!2189
2026-04-17 12:23:18 +00:00
Charzard4261andPolymeta ad958f91fe Bait Changes 2026-04-17 12:23:18 +00:00
Polymeta 84e6931b88 cooking pot screen opens again 🔥
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-17 00:28:13 +01:00
Polymeta 84c29fdf62 26.1.2 and a bunch of fixes with that
See TODOs and discord list for ongoing things

Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-16 19:06:27 +01:00
Polymeta 881ad52f36 further fixes for 1.21.11
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-16 19:06:27 +01:00
Polymeta 377dc44d7e first bump to 1.21.11
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-16 19:06:26 +01:00
Paul 02baa3f44f Changelog 2026-04-14 18:20:34 +10:00
Paul 1cc4be5d28 Merge remote-tracking branch 'origin/main' into battle-lockup-fix 2026-04-14 18:19:54 +10:00
Paul f10e617203 Fixed a battle lock that occurs when a force switch is received a tick before the associated update 2026-04-14 18:19:31 +10:00
Polymeta a77a6d21eb Merge branch 'fix/full-heal-not-working-in-battle' into 'main'
Fix: Full Heal not working (Lum berry works)

Closes #1950

See merge request cable-mc/cobblemon!2195
2026-04-13 11:00:21 +00:00
GizmoobandPolymeta 8dc3b7be42 Fix: Full Heal not working (Lum berry works) 2026-04-13 11:00:21 +00:00
Polymeta 58286d8bb5 Merge branch 'particle-slide-fix' into 'main'
Fix sliding particles incorrectly handling block collision

See merge request cable-mc/cobblemon!2180
2026-04-13 08:51:16 +00:00
Gitoido 660636034b chore: CHANGELOG.md 2026-04-09 11:02:40 +03:00
Gitoido f54ed2924d refactor: unneeded sorting 2026-04-09 10:53:00 +03:00
Gitoido a432854023 feat: datapack order field for starter category 2026-04-09 10:44:13 +03:00
Jolihan566 ae65b2c2b1 fixed 2026-04-07 20:53:48 +10:00
Jolihan566 d917e81804 fix variable name 2026-04-07 18:23:03 +10:00
Jolihan566 67a468a006 added chatter npc behaviour, script and associated lang files 2026-04-07 18:17:56 +10:00
Jackson Bowes 5e13da3e65 Merge branch 'graal-dev-runtime-fix' into 'main'
Graal dev runtime fix

See merge request cable-mc/cobblemon!2192
2026-04-04 16:12:04 -05:00
jackowes 03bc8a1b40 Add modRuntimeOnly for Graal in dev environment 2026-04-04 11:34:01 -05:00
Jackson Bowes 98eb43d13d Merge branch 'feature/flee-attempt' into 'main'
feature: add BattleFleeAttemptEvent and integrate flee attempt handling

See merge request cable-mc/cobblemon!2187
2026-04-02 16:49:57 -05:00
saul.tscandJackson Bowes ca839432aa feature: add BattleFleeAttemptEvent and integrate flee attempt handling 2026-04-02 16:49:57 -05:00
Dragonwhisper92andHazar df6f200b63 Added Data Component support for species drops 2026-04-01 20:35:25 +00:00
Hiroku 23f98f3a7d Merge branch 'gradle/JiJs' into 'main'
Move Graal and Mongo to JiJ

See merge request cable-mc/cobblemon!2181
2026-03-31 22:20:58 +00:00
Polymeta 5aeae2b350 Merge branch '1971-fix-recall-console-warning' into 'main'
Prevent calling `Entity.remove(DISCARDED)` if the entity is already being discarded

Closes #1971

See merge request cable-mc/cobblemon!2184
2026-03-25 21:24:58 +00:00
Charzard4261 b60fc26a97 Add change to CHANGELOG.md 2026-03-25 18:13:59 +00:00
Charzard4261 c1a95d0f3f Prevent calling Entity.remove(DISCARDED) if the entity is already being discarded 2026-03-25 17:21:12 +00:00
Jackson Bowes 3fd04bcdb4 Merge branch 'feature/award-xp-to-fainted-pokemon' into 'main'
Add configurable XP distribution for fainted pokemon and battle losses

See merge request cable-mc/cobblemon!2183
2026-03-23 09:28:33 -05:00
Mr4Goosey a945bad5b5 Add changelog entry for fainted pokemon XP config options 2026-03-17 22:53:08 +01:00
Mr4Goosey edde3b16bf Add configurable XP distribution for fainted pokemon and battle losses
Currently, if a pokemon faints during battle, it receives no XP for any
opponents it helped defeat - that XP simply vanishes. This is especially
punishing in trainer battles where pokemon cycling is common.

This adds faint-order tracking so that pokemon who contributed to a kill
can still receive XP, as long as the opponent fainted before they did.
It also adds support for awarding XP on battle losses or forfeits for
opponents that were defeated during the battle.

Both behaviors are behind new config options (default off):
- awardExperienceToFaintedPokemon: fainted pokemon receive XP for
  opponents they helped defeat before fainting
- awardExperienceOnBattleLoss: pokemon receive XP for opponents
  defeated even if the battle is lost or forfeited
2026-03-17 22:31:58 +01:00
Nick ffaee877a7 Make sure we don't bundle everything + handle showdown init errors better 2026-03-14 19:54:10 -07:00
Nick 186f7dbbce Move Graal and Mongo to JiJ 2026-03-14 19:19:43 -07:00
jackowes 4ae9b25d35 get rid of testing code, whoops 2026-03-14 20:30:25 -05:00
jackowes b35fe23831 - Fixed sliding particles incorrectly handling block collision 2026-03-14 16:35:50 -05:00
Jackson Bowes 16165a2e68 Merge branch 'Fixes-#1977-and-#1959-gems-and-pokeballs' into 'main'
Fixes #1977 & #1959 gems and pokeballs

Closes #1977

See merge request cable-mc/cobblemon!2177
2026-03-13 20:34:07 -05:00
Cyrus cdec0e5640 small little # change 2026-03-13 17:47:02 -07:00
Cyrus 87db8aadfe changed json for gems to be consumable and modifiers for the respectable pokeballs
edited changelog and added for json pvp battles
2026-03-10 13:25:45 -07:00
Cyrus 36921f439c changed json for gems to be consumable and modifiers for the respectable pokeballs 2026-03-10 13:12:43 -07:00
Polymeta f2d2ceb422 Merge branch 'patch-3' into 'main'
Fix incorrect spelling of "Pokémon" in some translation strings

See merge request cable-mc/cobblemon!2171
2026-03-08 21:35:45 +00:00
Micky 547ceae9ed Fix incorrect spelling of "Pokémon" in some places 2026-03-08 17:52:43 +00:00
Micky a2fe6364cd Fix Tatami Mat missing proper footstep sounds 2026-03-06 10:02:22 +00:00
Polymeta a921d55812 fix campfirepot crash 2026-03-06 09:37:07 +00:00
Polymeta 79688ebdad Merge branch 'oh-hi-mark' into 'main'
Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for pokemon

See merge request cable-mc/cobblemon!2173
2026-03-04 09:20:33 +00:00
Spicy 16d8c8bb19 Should be remove, not delete 2026-03-03 16:39:01 -06:00
Spicy e5738fecb0 Added marks, has_mark, & delete_marks as available Molang functions for pokemon 2026-03-03 16:33:44 -06:00
Jackson Bowes a46ac372a8 Merge branch 'showdown-species-name-fix' into 'main'
Fix to Showdown species names conflicting.

See merge request cable-mc/cobblemon!2170
2026-03-02 15:11:29 -06:00
Tyzillion 8b6c8d27b7 This fixes a bug that was causing species with one letter difference names to conflict in Showdown. 2026-02-27 17:13:06 -05:00
4130 changed files with 31618 additions and 31815 deletions
+9 -5
View File
@@ -1,4 +1,4 @@
image: amazoncorretto:21
image: amazoncorretto:25
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
@@ -16,6 +16,7 @@ check_assets_cache:
stage: preprocessing
script:
- echo "Checking if cached hash matches current hash"
- yum install -y findutils
# Calculate the current hash
- find common/src/main/resources/assets common/src/main/resources/data -type f -exec sha256sum {} + | sort -k 2 | sha256sum > current_hash.txt
@@ -71,8 +72,8 @@ setup_packsquash:
- echo "Installing wget & unzip"
- yum install -y wget unzip
- echo "Downloading PackSquash"
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.0/PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip
- unzip PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip -d ./packsquash_bin
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.1/packsquash-x86_64-unknown-linux-musl.zip
- unzip packsquash-x86_64-unknown-linux-musl.zip -d ./packsquash_bin
- echo "Creating PackSquash options.toml"
- |
cat <<EOF > ./options.toml
@@ -148,6 +149,7 @@ build:
dependencies:
- replace_resources
script:
- yum install -y findutils
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew build
@@ -163,6 +165,7 @@ publish:
rules:
- if: '$CI_PROJECT_PATH == "cable-mc/cobblemon" && $CI_COMMIT_BRANCH == "main"'
script:
- yum install -y findutils
- export GRADLE_OPTS="-Xmx4G -Xms4G"
- chmod +x ./gradlew
- ./gradlew publish
@@ -176,9 +179,10 @@ deploy_artifacts:
dependencies:
- build
script:
- yum install -y findutils
- echo "Collecting artifacts..."
- find neoforge/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
- find fabric/build/libs/ -type f -name "*.jar" | grep -vE "dev-shadow|sources|javadoc" | xargs -I {} mv {} ./
- find neoforge/build/libs/ -type f -name "*.jar" | grep -vE "dev-slim|sources|javadoc" | xargs -I {} mv {} ./
- find fabric/build/libs/ -type f -name "*.jar" | grep -vE "dev-slim|sources|javadoc" | xargs -I {} mv {} ./
- for f in *.jar; do mv "$f" "$(echo "$f" | sed s/+/b$CI_PIPELINE_IID+/)"; done
artifacts:
paths:
-20
View File
@@ -1,20 +0,0 @@
# Changelog
## [1.8.0 (MONTH Xth, 2026)](#1-8-0)
### Additions
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
### Changes
### Fixes
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
### Developer
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
### Molang & Datapacks
### Particles
### Localization
- Updated translations for:
-16
View File
@@ -1,16 +0,0 @@
### Additions
### Changes
- Wild Pokémon now reliably have at least one offensive move, if they can have offensive moves at that level.
### Fixes
### Developer
### Molang & Datapacks
- Added `moveset_builders` datapack folder for different styles of movesets.
- Added `party_pools` datapack folder for defining groups of Pokémon for the purposes of complex NPC party definitions.
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
### Particles
+15
View File
@@ -2,6 +2,7 @@
## [1.7.4 (MONTH Xth, 2026)](#1-7-4)
### Additions
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
- Added new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
@@ -21,6 +22,7 @@
- Factor in the weight of pokemon when calculating pushing forces between hitboxes.
- Remade the Starter Selection Screen with new assets
- Refactored berry trees to be less impactful on TPS (more performant)
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts
### Fixes
- Fixes https://gitlab.com/cable-mc/cobblemon/-/issues/1943
@@ -37,15 +39,28 @@
- [Various UFO Ride Improvements](https://gitlab.com/cable-mc/cobblemon/-/merge_requests/2156)
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within it's menu
- Fixed NPCs switching pokemon very often during battles
- Fixed Campfire pot causing crashes under certain circumstances
- Fixed Type Gems not being consumed upon use
- Fixed Ancient Pokeball's to their respective modifiers
- Fixed a Server warning when recalling Pokemon
- Fixed sliding particles incorrectly handling block collision
- Fixed Full Heal not curing status conditions in battle
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
### Developer
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts
### Molang & Datapacks
- Added basic chatter NPC Behaviour
- q.item.is_enchanted()
- q.item.has_enchantment(minecraft:sharpness, 3)
- Added `has_chosen_starter` & `get_starter_uuid` as available Molang functions
- Added `marks`, `has_mark`, & `remove_marks` as available Molang functions for pokemon
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
- Added new sortOrder field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers
- Added optional `order` field to starter categories. It will allow to explicitly sort categories in starter selection screen
### Localization
- Updated translations for:
-9
View File
@@ -1,9 +0,0 @@
- Nodemaker bug, it thinks jumps are possible from water up an entire block - they aint
https://gyazo.com/759b8285e7c07046ff2e54adf0d7a7ac
- Why is magikarp trying to path through ice
https://gyazo.com/ae230758c529f885eeb0dd91e60530d2
- We should use mixins to put the path node filtering from OmniPathNodeMaker in the base classes otherwise
NPC wandering will be jank too.
+23 -13
View File
@@ -18,15 +18,10 @@ import java.time.format.DateTimeFormatter
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
id("net.kyori.blossom")
id("org.jetbrains.gradle.plugin.idea-ext")
}
architectury {
common("neoforge", "fabric")
}
repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
@@ -35,33 +30,38 @@ repositories {
mavenLocal()
}
val mappingsAttribute = Attribute.of("net.minecraft.mappings", String::class.java)
dependencies {
implementation(libs.bundles.kotlin)
modImplementation(libs.fabric.loader)
modApi(libs.molang)
compileOnly(libs.bundles.common.mixin.compileOnly)
annotationProcessor(libs.mixinExtras.common)
api(libs.molang)
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
compileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
// LambDynamicLights is handled differently because we need the Mojang-mappings version of it.
modCompileOnly(libs.lambDynamicLights) {
capabilities {
requireCapability("dev.lambdaurora.lambdynamiclights:api-mojmap")
compileOnly(libs.lambDynamicLights) {
attributes {
attribute(mappingsAttribute, "mojmap")
}
}
// 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)
compileOnly(libs.graal.core)
compileOnly(libs.graal.polyglot)
// Data Storage
modCompileOnly(libs.bundles.mongo)
compileOnly(libs.bundles.mongo)
// Unit Testing
testImplementation(libs.bundles.unitTesting)
testRuntimeOnly(libs.junit.platform)
}
tasks.withType<Test> {
@@ -72,6 +72,16 @@ tasks.withType<Test> {
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions {
freeCompilerArgs.addAll(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
sourceSets {
main {
blossom {
+2 -1
View File
@@ -1 +1,2 @@
maven.artifactId = mod
maven.artifactId = mod
fabric.loom.disableObfuscation=true
@@ -36,7 +36,7 @@ public interface Bone {
/**
* Renders a particular bone in relation to a particular entity.
*
* @param context The render context this bone is being drawn for.
* @param context The extractRenderState context this bone is being drawn for.
* @param stack The current [MatrixStack] for the renderer
* @param buffer The vertex consumer deployed by the renderer
* @param packedLight The light level to be applied to the bone
@@ -9,20 +9,21 @@
package com.cobblemon.mod.common.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonEntities;
import dev.lambdaurora.lambdynlights.api.DynamicLightsContext;
import dev.lambdaurora.lambdynlights.api.DynamicLightsInitializer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.EntityType;
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
Identifier.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
PokemonLuminance.INSTANCE
);
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
Identifier.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
PlayerLuminance.INSTANCE
);
@@ -32,11 +33,8 @@ public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
ctx.register(CobblemonEntities.POKEMON, PokemonLuminance.INSTANCE);
});
}
@SuppressWarnings({"removal", "UnstableApiUsage"})
@Override
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
}
@@ -10,10 +10,11 @@ package com.cobblemon.mod.common.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.duck.PlayerDuck;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
@@ -25,7 +26,7 @@ import org.jetbrains.annotations.Range;
public final class PlayerLuminance extends CustomLuminance implements EntityLuminance {
public static final PlayerLuminance INSTANCE = new PlayerLuminance();
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
@Override
public @NotNull Type type() {
@@ -46,8 +47,8 @@ public final class PlayerLuminance extends CustomLuminance implements EntityLumi
}
private @Range(from = 0L, to = 15L) static int shoulderLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Player player) {
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft());
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight());
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(((PlayerDuck)player).getShoulderEntityLeft());
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(((PlayerDuck)player).getShoulderEntityRight());
if (leftShoulder == null && rightShoulder == null) {
return 0;
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import net.minecraft.client.player.AbstractClientPlayer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import org.jetbrains.annotations.Nullable;
public interface AvatarRenderStateDuck {
@Nullable AbstractClientPlayer cobblemon$getMountedPlayer();
void cobblemon$setMountedPlayer(@Nullable AbstractClientPlayer player);
@Nullable PokemonEntity cobblemon$getMountedPokemon();
void cobblemon$setMountedPokemon(@Nullable PokemonEntity pokemonEntity);
}
@@ -30,4 +30,6 @@ public interface CameraDuck {
float cobblemon$getYRot();
void cobblemon$setYRot(float yRot);
void cobblemon$markDirty();
}
@@ -0,0 +1,30 @@
/*
* 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.duck;
import com.cobblemon.mod.common.entity.PoseType;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.Nullable;
public interface LivingEntityRenderStateDuck {
@Nullable LivingEntity cobblemon$getMountedEntity();
void cobblemon$setMountedEntity(@Nullable LivingEntity entity);
float cobblemon$getMountedPartialTick();
void cobblemon$setMountedPartialTick(float partialTick);
int cobblemon$getMountedSeatIndex();
void cobblemon$setMountedSeatIndex(int seatIndex);
@Nullable String cobblemon$getMountedSeatLocator();
void cobblemon$setMountedSeatLocator(@Nullable String seatLocator);
@Nullable PoseType cobblemon$getMountedPoseType();
void cobblemon$setMountedPoseType(@Nullable PoseType poseType);
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.duck;
import net.minecraft.nbt.CompoundTag;
import org.joml.Vector3f;
public interface PlayerDuck {
@@ -15,4 +16,8 @@ public interface PlayerDuck {
Vector3f getDriverInput();
void setLastSentDriverInput(Vector3f driverInput);
Vector3f getLastSentDriverInput();
CompoundTag getShoulderEntityLeft();
CompoundTag getShoulderEntityRight();
void setShoulderEntityLeft(CompoundTag entityNbt);
void setShoulderEntityRight(CompoundTag entityNbt);
}
@@ -8,8 +8,8 @@
package com.cobblemon.mod.common.duck;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipeAccess;
public interface RecipeAwareSlot {
void setRecipeManager(RecipeManager recipeManager);
void setRecipeManager(RecipeAccess recipeManager);
}
@@ -10,12 +10,12 @@ package com.cobblemon.mod.common.duck;
import org.jetbrains.annotations.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundSource;
public interface SoundEngineDuck {
void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void pauseSounds(@Nullable Identifier id, @Nullable SoundSource category);
void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void resumeSounds(@Nullable Identifier id, @Nullable SoundSource category);
}
@@ -8,13 +8,13 @@
package com.cobblemon.mod.common.duck;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundSource;
import org.jetbrains.annotations.Nullable;
public interface SoundManagerDuck {
void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void pauseSounds(@Nullable Identifier id, @Nullable SoundSource category);
void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category);
void resumeSounds(@Nullable Identifier id, @Nullable SoundSource category);
}
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.arrow.AbstractArrow;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
@@ -22,13 +22,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(targets = "net.minecraft.world.entity.animal.Bee$BeeGrowCropGoal")
@Mixin(targets = "net.minecraft.world.entity.animal.bee.Bee$BeeGrowCropGoal")
public abstract class BeeEntityMixin {
@Unique
private BlockState cobblemon$result = null;
@Inject(method = "tick()V", at = @At(
@Inject(method = "tick", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/block/BonemealableBlock;performBonemeal(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/util/RandomSource;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V",
shift = At.Shift.BY,
@@ -14,19 +14,26 @@ import com.cobblemon.mod.common.api.ai.config.task.PathToBeeHiveTaskConfig;
import com.cobblemon.mod.common.api.pokemon.PokemonProperties;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.world.feature.SaccharineTreeFeature;
import com.mojang.logging.LogUtils;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.memory.MemoryStatus;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.item.component.TypedEntityData;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BeehiveBlock;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
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.CallbackInfoReturnable;
@@ -38,25 +45,28 @@ import java.util.Objects;
@Mixin(BeehiveBlockEntity.Occupant.class)
public abstract class BeeOccupantMixin {
@Unique
private static final Logger LOGGER = LogUtils.getLogger();
@Inject(method = "createEntity", at = @At("RETURN"), cancellable = true)
private void cobblemon$createEntity(Level level, BlockPos pos, CallbackInfoReturnable<Entity> cir) {
// Needing to replicate all the post op stuff on the hive isn't great
if (cir.getReturnValue() == null) {
final BeehiveBlockEntity.Occupant occupant = (BeehiveBlockEntity.Occupant) (Object) this;
CompoundTag compoundTag = occupant.entityData().copyTag();
String id = compoundTag.getString("id");
CompoundTag compoundTag = occupant.entityData().copyTagWithoutId();
String id = compoundTag.getStringOr("id", "");
Entity entity = null;
if (id.equals(SaccharineTreeFeature.Companion.getPOKEMON_PLACEHOLD_ID())) {
// Special case for Pokemon stuffed into a beenest upon world gen.
// For technical reasons we cannot create them as full entities during world gen.
// So we look for this special id tag and complete the entity when it attempts to exit a hive.
if (compoundTag.contains("pokemonProperties")) {
String properties = compoundTag.getString("pokemonProperties");
String properties = compoundTag.getString("pokemonProperties").get();
PokemonProperties pokemon = PokemonProperties.Companion.parse(properties);
entity = pokemon.createEntity(level);
}
} else {
entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
entity = EntityType.loadEntityRecursive(compoundTag, level, EntitySpawnReason.LOAD, (entityx) -> entityx);
}
if (entity instanceof PokemonEntity pokemonEntity) {
@@ -76,11 +86,11 @@ public abstract class BeeOccupantMixin {
// Block has likely been destroyed
var center = pos.getCenter();
newPos = new Vec3(
center.x + level.random.nextFloat() * 0.6 - 0.3,
center.y + level.random.nextFloat() * 0.6 - 0.3,
center.z + level.random.nextFloat() * 0.6 - 0.3
center.x + level.getRandom().nextFloat() * 0.6 - 0.3,
center.y + level.getRandom().nextFloat() * 0.6 - 0.3,
center.z + level.getRandom().nextFloat() * 0.6 - 0.3
);
newYaw = level.random.nextFloat() * 360F;
newYaw = level.getRandom().nextFloat() * 360F;
if (brain.checkMemory(CobblemonMemories.HIVE_LOCATION, MemoryStatus.REGISTERED)) {
brain.eraseMemory(CobblemonMemories.HIVE_LOCATION);
}
@@ -100,7 +110,7 @@ public abstract class BeeOccupantMixin {
if (state.is(BlockTags.BEEHIVES, (blockStateBase) -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL))) {
int i = state.getValue(BeehiveBlock.HONEY_LEVEL);
if (i < 5) {
int j = level.random.nextInt(100) == 0 ? 2 : 1;
int j = level.getRandom().nextInt(100) == 0 ? 2 : 1;
if (i + j > 5) {
--j;
}
@@ -117,16 +127,19 @@ public abstract class BeeOccupantMixin {
@Inject(method = "of", at = @At("HEAD"), cancellable = true)
private static void cobblemon$of(Entity entity, CallbackInfoReturnable<BeehiveBlockEntity.Occupant> cir) {
if (entity instanceof PokemonEntity pokemonEntity) {
List<String> IGNORED_BEE_TAGS = Arrays.asList("Air", "ArmorDropChances", "ArmorItems", "CanPickUpLoot", "DeathTime", "FallDistance", "FallFlying", "Fire", "HandDropChances", "HandItems", "HurtByTimestamp", "HurtTime", "LeftHanded", "Motion", "NoGravity", "OnGround", "PortalCooldown", "Pos", "Rotation", "Passengers", "leash", "UUID");
CompoundTag compoundTag = new CompoundTag();
entity.save(compoundTag);
Objects.requireNonNull(compoundTag);
IGNORED_BEE_TAGS.forEach(compoundTag::remove);
boolean hasNectar = pokemonEntity.getBrain().checkMemory(CobblemonMemories.HAS_NECTAR, MemoryStatus.REGISTERED)
&& pokemonEntity.getBrain().getMemory(CobblemonMemories.HAS_NECTAR).orElse(false);
cir.setReturnValue(new BeehiveBlockEntity.Occupant(CustomData.of(compoundTag), 0, hasNectar ? 2400 : 600));
cir.cancel();
try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(pokemonEntity.problemPath(), LOGGER)) {
List<String> IGNORED_BEE_TAGS = BeehiveBlockEntity.IGNORED_BEE_TAGS;
TagValueOutput output = TagValueOutput.createWithContext(reporter, pokemonEntity.registryAccess());
pokemonEntity.save(output);
Objects.requireNonNull(output);
IGNORED_BEE_TAGS.forEach(output::discard);
CompoundTag entityTag = output.buildResult();
boolean hasNectar = pokemonEntity.getBrain().checkMemory(CobblemonMemories.HAS_NECTAR, MemoryStatus.REGISTERED)
&& pokemonEntity.getBrain().getMemory(CobblemonMemories.HAS_NECTAR).orElse(false);
cir.setReturnValue(new BeehiveBlockEntity.Occupant(TypedEntityData.of(pokemonEntity.getType(), entityTag), 0, hasNectar ? 2400 : 600));
cir.cancel();
}
}
}
}
}
@@ -12,7 +12,6 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.mixin.accessor.BeeDataAccessor;
import com.cobblemon.mod.common.mixin.accessor.BeehiveBlockEntityAccessor;
import com.cobblemon.mod.common.mixin.accessor.BlockEntityAccessor;
import com.google.common.collect.Lists;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
@@ -59,8 +58,8 @@ public abstract class BeehiveBlockEntityMixin {
}
if (isPokemon) {
return BeehiveBlockEntityAccessor.releaseOccupant(
((BlockEntityAccessor) blockEntityAccessor).getLevel(),
((BlockEntityAccessor) blockEntityAccessor).getWorldPosition(),
blockEntity.getLevel(),
blockEntity.getBlockPos(),
state,
occupant,
list,
@@ -1,35 +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.item.CobblemonBuiltinItemRenderer;
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BlockEntityWithoutLevelRenderer.class)
public class BuiltinModelItemRendererMixin {
@Inject(method = "renderByItem", at = @At("HEAD"), cancellable = true)
private void cobblemon$useDynamicItemRenderer(ItemStack stack, ItemDisplayContext mode, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, CallbackInfo ci) {
CobblemonBuiltinItemRenderer renderer = CobblemonBuiltinItemRendererRegistry.INSTANCE.rendererOf(stack.getItem());
if (renderer != null) {
renderer.render(stack, mode, matrices, vertexConsumers, light, overlay);
ci.cancel();
}
}
}
@@ -12,12 +12,14 @@ import com.cobblemon.mod.common.duck.ChannelDuck;
import com.mojang.blaze3d.audio.Channel;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.EXTEfx;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(Channel.class)
public abstract class ChannelMixin implements ChannelDuck {
@Final
@Shadow
private int source;
@@ -1,48 +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 static com.cobblemon.mod.common.datafixer.CobblemonSchemas.VERSION_KEY;
import com.cobblemon.mod.common.datafixer.CobblemonSchemas;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.mojang.serialization.MapCodec;
import java.util.Optional;
import java.util.function.Supplier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.storage.ChunkStorage;
import net.minecraft.world.level.storage.DimensionDataStorage;
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.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkStorage.class)
public class ChunkStorageMixin {
//Modifies the NBT returned by the vanilla upgradeChunkTag to also incorporate cobblemon fixup stuff
@ModifyExpressionValue(
method = "upgradeChunkTag(Lnet/minecraft/resources/ResourceKey;Ljava/util/function/Supplier;Lnet/minecraft/nbt/CompoundTag;Ljava/util/Optional;)Lnet/minecraft/nbt/CompoundTag;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/util/datafix/DataFixTypes;updateToCurrentVersion(Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;"
)
)
public CompoundTag cobblemon$doChunkFix(
CompoundTag vanillaFixed
) {
int curVersion = vanillaFixed.contains(VERSION_KEY) ? vanillaFixed.getInt(VERSION_KEY) : 0;
return DataFixTypes.CHUNK.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
}
}
@@ -46,7 +46,7 @@ public class ClientPacketListenerMixin {
passengers.stream()
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
.forEach(x -> x.startRiding(pokemon, true, true));
ci.cancel();
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.block.dispenser.DispenserBehaviorRegistry;
import net.minecraft.core.dispenser.DispenseItemBehavior;
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;
@Mixin(DispenseItemBehavior.class)
public interface DispenseItemBehaviourMixin {
@Inject(method = "bootStrap", at = @At("TAIL"))
private static void addCobblemonDispenserBehaviors(CallbackInfo ci) {
DispenserBehaviorRegistry.INSTANCE.registerDispenserBehaviors();
}
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.EnderpearlItem;
@@ -28,7 +28,7 @@ import java.util.Comparator;
@Mixin(EnderpearlItem.class)
public abstract class EnderPearlItemMixin {
@Inject(method = "use", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$use(Level level, Player player, InteractionHand usedHand, CallbackInfoReturnable<InteractionResultHolder<ItemStack>> cir) {
private void cobblemon$use(Level level, Player player, InteractionHand usedHand, CallbackInfoReturnable<InteractionResult> cir) {
double range = player.entityInteractionRange();
Entity closestEntity = level.getEntities(player, AABB.ofSize(player.position(), range, range, range)).stream()
.filter(entity -> isLookingAt(player, entity))
@@ -36,7 +36,7 @@ public abstract class EnderPearlItemMixin {
.orElse(null);
if (closestEntity instanceof PokemonEntity pokemonEntity) {
if (player.isCrouching() && pokemonEntity.getOwnerUUID() == player.getUUID()) {
if (player.isCrouching() && pokemonEntity.isOwnedBy(player)) {
cir.cancel();
}
}
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.pokemon.ai.EntityBehaviour;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.entity.animal.fox.Fox;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
@@ -9,19 +9,11 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState;
import com.cobblemon.mod.common.entity.PosableEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.resources.PlayerSkin;
import net.minecraft.client.renderer.entity.player.AvatarRenderer;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.player.PlayerModelType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -33,7 +25,7 @@ import java.util.Map;
@Mixin(EntityRenderDispatcher.class)
public class EntityRenderDispatcherMixin {
@Shadow private Map<PlayerSkin.Model, EntityRenderer<? extends Player>> playerRenderers;
@Shadow private Map<PlayerModelType, AvatarRenderer<AbstractClientPlayer>> playerRenderers;
@Inject(
method = "onResourceManagerReload",
@@ -43,55 +35,4 @@ public class EntityRenderDispatcherMixin {
CobblemonClient.INSTANCE.onAddLayer(this.playerRenderers);
}
@Inject(
method = "renderHitbox(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Lnet/minecraft/world/entity/Entity;FFFF)V",
at = @At(value = "TAIL")
)
private static void renderLocators(
PoseStack poseStack,
VertexConsumer buffer,
Entity entity,
float red,
float green,
float blue,
float alpha,
CallbackInfo ci
) {
if (entity instanceof PosableEntity posableEntity) {
if (posableEntity.getDelegate() instanceof PosableState state) {
state.getLocatorStates().forEach((locator, matrix) -> {
poseStack.pushPose();
Vec3 pos = matrix.getOrigin().subtract(entity.position());
LevelRenderer.renderLineBox(
poseStack,
buffer,
AABB.ofSize(pos, 0.25, 0.25, 0.25),
0F,
1F,
0F,
1F
);
/*if (locator.contains("middle")){
PoseStack.Pose pose = poseStack.last();
Vector3f vec = matrix.getMatrix().getRotation(new AxisAngle4f()).transform(new Vector3f(0, 0, -1));
buffer.addVertex(pose, pos.toVector3f()).setColor(0xffff00ff).setNormal(pose, vec.x, vec.y, vec.z); //Forward, pink
buffer.addVertex(pose, (float)(pos.x() + vec.x), (float)(pos.y() + vec.y), (float)(pos.z() + vec.z)).setColor(0xffff00ff).setNormal(pose, vec.x, vec.y, vec.z);
vec = matrix.getMatrix().getRotation(new AxisAngle4f()).transform(new Vector3f(0, 1, 0));
buffer.addVertex(pose, pos.toVector3f()).setColor(0xffffff00).setNormal(pose, vec.x, vec.y, vec.z); //Up, yellow
buffer.addVertex(pose, (float)(pos.x() + vec.x), (float)(pos.y() + vec.y), (float)(pos.z() + vec.z)).setColor(0xffffff00).setNormal(pose, vec.x, vec.y, vec.z);
vec = matrix.getMatrix().getRotation(new AxisAngle4f()).transform(new Vector3f(-1, 0, 0));
buffer.addVertex(pose, pos.toVector3f()).setColor(0xff00ffff).setNormal(pose, vec.x, vec.y, vec.z); //Left, cyan
buffer.addVertex(pose, (float)(pos.x() + vec.x), (float)(pos.y() + vec.y), (float)(pos.z() + vec.z)).setColor(0xff00ffff).setNormal(pose, vec.x, vec.y, vec.z);
}*/
poseStack.popPose();
});
}
}
}
}
}
@@ -14,7 +14,7 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
import net.minecraft.world.entity.monster.AbstractSkeleton;
import net.minecraft.world.entity.monster.skeleton.AbstractSkeleton;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -23,7 +23,7 @@ import org.spongepowered.asm.mixin.injection.At;
public class EntityStorageMixin {
//Modifies the NBT returned by the vanilla upgradeChunkTag to also incorporate cobblemon fixup stuff
@ModifyExpressionValue(
method = "method_31731(Lnet/minecraft/world/level/ChunkPos;Ljava/util/Optional;)Lnet/minecraft/world/level/entity/ChunkEntities;",
method = "lambda$loadEntities$0",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/chunk/storage/SimpleRegionStorage;upgradeChunkTag(Lnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;"
@@ -32,7 +32,7 @@ public class EntityStorageMixin {
public CompoundTag cobblemon$doEntityFix(
CompoundTag vanillaFixed
) {
int curVersion = vanillaFixed.contains(VERSION_KEY) ? vanillaFixed.getInt(VERSION_KEY) : 0;
int curVersion = vanillaFixed.getIntOr(VERSION_KEY, 0);
CompoundTag newTag = DataFixTypes.ENTITY_CHUNK.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
newTag.put(VERSION_KEY, IntTag.valueOf(CobblemonSchemas.DATA_VERSION));
return newTag;
@@ -15,17 +15,18 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.cobblemon.mod.common.villager.VillagerGatherableItems;
import java.util.Objects;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.npc.VillagerProfession;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.npc.villager.Villager;
import net.minecraft.world.entity.npc.villager.VillagerProfession;
import net.minecraft.world.item.ItemStack;
@Mixin(Villager.class)
public abstract class EntityVillagerMixin {
@Inject(method = "wantsToPickUp", at = @At(value = "RETURN"), cancellable = true)
private void cobblemon$canGather(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
private void cobblemon$canGather(ServerLevel serverLevel, ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
final Villager villager = (Villager) (Object) this;
if (ci.getReturnValue() == false && Objects.equals(villager.getVillagerData().getProfession(), VillagerProfession.FARMER) && villager.getInventory().canAddItem(stack)) {
if (!ci.getReturnValue() && Objects.equals(villager.getVillagerData().profession().value(), VillagerProfession.FARMER) && villager.getInventory().canAddItem(stack)) {
if(VillagerGatherableItems.INSTANCE.getVillagerGatherableItems().contains(stack.getItem()))
ci.setReturnValue(true);
}
@@ -0,0 +1,45 @@
/*
* 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.Cobblemon;
import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import kotlin.Unit;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.Consumable;
import net.minecraft.world.level.Level;
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;
@Mixin(FoodProperties.class)
public class FoodPropertiesMixin {
@Inject(method = "onConsume", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/food/FoodData;eat(Lnet/minecraft/world/food/FoodProperties;)V"))
public void dropLeftOvers(Level level, LivingEntity user, ItemStack stack, Consumable consumable, CallbackInfo ci) {
if (!level.isClientSide()) {
if (stack.is(CobblemonItemTags.LEAVES_LEFTOVERS) && level.getRandom().nextDouble() < Cobblemon.config.getAppleLeftoversChance()) {
ItemStack leftovers = new ItemStack(CobblemonItems.LEFTOVERS);
CobblemonEvents.LEFTOVERS_CREATED.postThen(
new LeftoversCreatedEvent(user, leftovers),
_ -> Unit.INSTANCE,
leftoversCreatedEvent -> {
user.handleExtraItemsCreatedOnUse(leftoversCreatedEvent.getLeftovers());
return Unit.INSTANCE;
}
);
}
}
}
}
@@ -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.mixin;
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ItemProperties.class)
public class ItemPropertiesMixin {
// Mixins into the fishing rod's register() lambda expression
// Without this, the fishing rod item model appears cast when using a PokeRod in the other hand
@Inject(method = "method_27883", at = @At(value = "RETURN", ordinal = 1), cancellable = true)
private static void cobblemon$preventRodMixup(ItemStack itemStack, ClientLevel clientLevel, LivingEntity livingEntity, int i, CallbackInfoReturnable<Float> cir) {
if (!(livingEntity instanceof Player) || ((Player) livingEntity).fishing instanceof PokeRodFishingBobberEntity) {
cir.setReturnValue(0.0f);
}
}
}
@@ -16,7 +16,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
@@ -35,8 +35,8 @@ import static net.minecraft.world.level.block.LecternBlock.HAS_BOOK;
@Mixin(LecternBlock.class)
public abstract class LecternBlockMixin {
@Inject(method = "useItemOn", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$useItemOn(ItemStack itemStack, BlockState blockState, Level world, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable<ItemInteractionResult> cir) {
if (!world.isClientSide && itemStack.getItem() instanceof PokedexItem) {
private void cobblemon$useItemOn(ItemStack itemStack, BlockState blockState, Level world, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable<InteractionResult> cir) {
if (!world.isClientSide() && itemStack.getItem() instanceof PokedexItem) {
BlockEntity blockEntity = world.getBlockEntity(blockPos);
if (!blockState.getValue(HAS_BOOK)
&& blockEntity instanceof net.minecraft.world.level.block.entity.LecternBlockEntity
@@ -53,9 +53,9 @@ public abstract class LecternBlockMixin {
if (lecternEntity instanceof LecternBlockEntity) {
((LecternBlockEntity) lecternEntity).setItemStack((pokedexItemStack));
world.playSound(null, blockPos, CobblemonSounds.POKEDEX_CLOSE, SoundSource.BLOCKS, 0.5F, 1.25F);
cir.setReturnValue(ItemInteractionResult.SUCCESS);
cir.setReturnValue(InteractionResult.SUCCESS);
} else {
cir.setReturnValue(itemStack.isEmpty() && interactionHand == InteractionHand.MAIN_HAND ? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION : ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION);
cir.setReturnValue(itemStack.isEmpty() && interactionHand == InteractionHand.MAIN_HAND ? InteractionResult.PASS : InteractionResult.TRY_WITH_EMPTY_HAND);
}
}
}
@@ -23,7 +23,7 @@ import java.util.List;
@Mixin(LightningBolt.class)
public abstract class LightningBoltMixin {
@Inject(method = "spawnFire", at = @At(value = "Head"), cancellable = true)
@Inject(method = "spawnFire", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$spawnFire(int extraIgnitions, CallbackInfo ci) {
// This prevents fire from spawning around Pokemon who otherwise should be immune to lightning and effectively absorb the hit
final LightningBolt lightningBolt = (LightningBolt) (Object) this;
@@ -41,4 +41,4 @@ public abstract class LightningBoltMixin {
ci.cancel();
}
}
}
}
@@ -15,18 +15,23 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import com.cobblemon.mod.common.api.riding.RidingStyle;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
@@ -55,8 +60,8 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
return cobblemon$orientationController;
}
@Inject(method = "onEffectRemoved", at = @At(value = "TAIL"))
private void cobblemon$onEffectRemoved(MobEffectInstance effect, CallbackInfo ci) {
@Inject(method = "removeEffect", at = @At(value = "TAIL"))
private void cobblemon$onEffectRemoved(Holder<MobEffect> effect, CallbackInfoReturnable<Boolean> cir) {
final LivingEntity entity = (LivingEntity) (Object) this;
if (entity instanceof ServerPlayer) {
ShoulderEffectRegistry.INSTANCE.onEffectEnd((ServerPlayer) entity);
@@ -76,27 +81,40 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && pokemonEntity.getDelegate() instanceof PokemonClientDelegate delegate) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
if (seatIndex >= 0 && seatIndex < pokemonEntity.getSeats().size()) {
Seat seat = pokemonEntity.getSeats().get(seatIndex);
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
if (locator != null) {
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
Vec3 eyePosition = cobblemon$getEyePosition(partialTicks, pokemonEntity, locatorOffset);
return cobblemon$pickFromEyePosition(hitDistance, partialTicks, hitFluids, eyePosition);
}
}
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
Vec3 eyePosition = cobblemon$getEyePosition(partialTicks, pokemonEntity, locatorOffset);
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
)
);
if ((Object) this instanceof RidePassenger ridePassenger) {
Vec3 rideEyePosition = ridePassenger.cobblemon$getRideEyePos();
if (!rideEyePosition.equals(Vec3.ZERO)) {
return cobblemon$pickFromEyePosition(hitDistance, partialTicks, hitFluids, rideEyePosition);
}
}
}
return super.pick(hitDistance, partialTicks, hitFluids);
}
@Unique
private @NotNull HitResult cobblemon$pickFromEyePosition(double hitDistance, float partialTicks, boolean hitFluids, Vec3 eyePosition) {
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
)
);
}
@Unique
private Vec3 cobblemon$getEyePosition(float partialTicks, PokemonEntity pokemonEntity, Vec3 locatorOffset) {
OrientationController controller = this.getOrientationController();
@@ -118,20 +136,6 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
this.cobblemon$orientationController.tick();
}
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (cobblemon$orientationController.getActive()) {
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 = "decreaseAirSupply", at = @At("HEAD"), cancellable = true)
private void cobblemon$conditionalWaterBreathing(
int currentAir,
@@ -159,4 +163,10 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
}
}
@Inject(method = "stopRiding", at = @At("HEAD"))
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
LivingEntity player = (LivingEntity)(Object)this;
if (!(player.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
controllableVehicle.getOrientationController().setActive(false);
}
}
@@ -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;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.ServerInterface;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
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.CallbackInfoReturnable;
@Mixin(ServerLevel.class)
public abstract class MinecraftDedicatedServerMixin implements ServerInterface {
@Inject(method = "shouldDiscardEntity", at = @At(value = "HEAD"), cancellable = true)
public void cobblemon$allowPokemonSpawns(Entity entity, CallbackInfoReturnable<Boolean> callback) {
if (entity instanceof PokemonEntity) {
callback.setReturnValue(false);
}
}
}
@@ -9,7 +9,8 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.api.snowstorm.ParticleMaterials;
import com.google.common.collect.ImmutableList;
import com.cobblemon.mod.common.client.particle.SnowstormParticleGroup;
import java.util.ArrayList;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
@@ -17,8 +18,10 @@ 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.CallbackInfoReturnable;
import java.util.List;
import net.minecraft.client.particle.ParticleGroup;
import net.minecraft.client.particle.ParticleEngine;
import net.minecraft.client.particle.ParticleRenderType;
@@ -31,7 +34,21 @@ final class ParticleManagerMixin {
@Inject(at = @At("RETURN"), method = "<clinit>")
private static void lodestone$addTypes(CallbackInfo ci) {
RENDER_ORDER = ImmutableList.<ParticleRenderType>builder().addAll(RENDER_ORDER)
.add(ParticleMaterials.INSTANCE.getADD(), ParticleMaterials.INSTANCE.getALPHA(), ParticleMaterials.INSTANCE.getBLEND(), ParticleMaterials.INSTANCE.getOPAQUE()).build();
ArrayList<ParticleRenderType> types = new ArrayList<>(RENDER_ORDER);
types.add(ParticleMaterials.INSTANCE.getADD());
types.add(ParticleMaterials.INSTANCE.getALPHA());
types.add(ParticleMaterials.INSTANCE.getBLEND());
types.add(ParticleMaterials.INSTANCE.getOPAQUE());
RENDER_ORDER = types;
}
}
@Inject(method = "createParticleGroup", at = @At("HEAD"), cancellable = true)
private void cobblemon$createSnowstormParticleGroup(ParticleRenderType renderType, CallbackInfoReturnable<ParticleGroup<?>> cir) {
if (renderType == ParticleMaterials.INSTANCE.getADD()
|| renderType == ParticleMaterials.INSTANCE.getALPHA()
|| renderType == ParticleMaterials.INSTANCE.getBLEND()
|| renderType == ParticleMaterials.INSTANCE.getOPAQUE()) {
cir.setReturnValue(new SnowstormParticleGroup((ParticleEngine) (Object) this, renderType));
}
}
}
@@ -13,12 +13,11 @@ import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.RidingStyle;
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverBehaviour;
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.duck.PlayerDuck;
import com.cobblemon.mod.common.duck.RidePassenger;
@@ -32,13 +31,12 @@ import com.cobblemon.mod.common.pokemon.Species;
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.util.ShoulderEntitySynchronizer;
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.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
@@ -51,10 +49,12 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.resources.Identifier;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -66,34 +66,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.*;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger {
@Unique
private CompoundTag cobblemon$shoulderEntityLeft = new CompoundTag();
@Unique
private CompoundTag cobblemon$shoulderEntityRight = new CompoundTag();
@Unique
private Vector3f cobblemon$driverInput;
@Unique
private Vector3f cobblemon$lastSentDriverInput;
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@Shadow public abstract CompoundTag getShoulderEntityRight();
@Shadow public abstract void respawnEntityOnShoulder(CompoundTag entityNbt);
@Shadow public abstract void setShoulderEntityRight(CompoundTag entityNbt);
@Shadow public abstract void setShoulderEntityLeft(CompoundTag entityNbt);
@Shadow public abstract boolean isSpectator();
@Shadow public abstract boolean addItem(ItemStack stack);
@Shadow public abstract void displayClientMessage(Component message, boolean overlay);
@Shadow @Final private static Map<Pose, EntityDimensions> POSES;
@Shadow @Final public static EntityDimensions STANDING_DIMENSIONS;
@Shadow protected int jumpTriggerTime;
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
@@ -107,71 +96,70 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
super(p_20966_, p_20967_);
}
@Inject(method = "respawnEntityOnShoulder", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/EntityType;create(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/level/Level;)Ljava/util/Optional;"), cancellable = true)
private void cobblemon$removePokemon(CompoundTag nbt, CallbackInfo ci) {
if (CompoundTagExtensionsKt.isPokemonEntity(nbt)) {
final UUID uuid = CompoundTagUtilities.getPokemonID(nbt);
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityRight())) {
final UUID uuidRight = CompoundTagUtilities.getPokemonID(this.getShoulderEntityRight());
if (uuid.equals(uuidRight)) {
this.recallPokemon(uuidRight);
this.setShoulderEntityRight(new CompoundTag());
}
}
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityLeft())) {
final UUID uuidLeft = CompoundTagUtilities.getPokemonID(this.getShoulderEntityLeft());
if (uuid.equals(uuidLeft)) {
this.recallPokemon(uuidLeft);
this.setShoulderEntityLeft(new CompoundTag());
}
}
ci.cancel();
@Inject(method = "readAdditionalSaveData", at = @At("TAIL"))
private void cobblemon$readShoulderData(ValueInput input, CallbackInfo ci) {
input.read("ShoulderEntityLeft", CompoundTag.CODEC)
.ifPresent(entityNbt -> this.cobblemon$setShoulderEntity(true, entityNbt, false));
input.read("ShoulderEntityRight", CompoundTag.CODEC)
.ifPresent(entityNbt -> this.cobblemon$setShoulderEntity(false, entityNbt, false));
}
@Inject(method = "addAdditionalSaveData", at = @At("TAIL"))
private void cobblemon$writeShoulderData(ValueOutput output, CallbackInfo ci) {
CompoundTag leftShoulder = this.getShoulderEntityLeft();
if (!leftShoulder.isEmpty()) {
output.store("ShoulderEntityLeft", CompoundTag.CODEC, leftShoulder);
}
CompoundTag rightShoulder = this.getShoulderEntityRight();
if (!rightShoulder.isEmpty()) {
output.store("ShoulderEntityRight", CompoundTag.CODEC, rightShoulder);
}
}
@Override
public EntityDimensions getDefaultDimensions(Pose pose) {
if (this.getVehicle() instanceof PokemonEntity) {
return STANDING_DIMENSIONS;
return super.getDefaultDimensions(Pose.STANDING);
}
return POSES.getOrDefault(pose, STANDING_DIMENSIONS);
return super.getDefaultDimensions(pose);
}
@Inject(
method = "removeEntitiesOnShoulder",
at = @At(
value = "JUMP",
opcode = Opcodes.IFGE,
ordinal = 0,
shift = At.Shift.AFTER
),
cancellable = true
)
private void cobblemon$preventPokemonDropping(CallbackInfo ci) {
// We want to allow both of these to forcefully remove the entities
if (this.isSpectator() || this.isDeadOrDying())
return;
if (!CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityLeft())) {
this.respawnEntityOnShoulder(this.getShoulderEntityLeft());
this.setShoulderEntityLeft(new CompoundTag());
}
if (!CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityRight())) {
this.respawnEntityOnShoulder(this.getShoulderEntityRight());
this.setShoulderEntityRight(new CompoundTag());
}
ci.cancel();
@Override
public CompoundTag getShoulderEntityLeft() {
return this.cobblemon$shoulderEntityLeft;
}
private void recallPokemon(UUID uuid) {
// We need to do this cause the Entity doesn't store a reference to its storage
final PartyStore party = Cobblemon.INSTANCE.getStorage().getParty(this.getUUID(), this.registryAccess());
for (Pokemon pokemon : party) {
if (pokemon.getUuid().equals(uuid)) {
pokemon.recall();
}
@Override
public CompoundTag getShoulderEntityRight() {
return this.cobblemon$shoulderEntityRight;
}
@Override
public void setShoulderEntityLeft(CompoundTag entityNbt) {
this.cobblemon$setShoulderEntity(true, entityNbt, true);
}
@Override
public void setShoulderEntityRight(CompoundTag entityNbt) {
this.cobblemon$setShoulderEntity(false, entityNbt, true);
}
@Unique
private void cobblemon$setShoulderEntity(boolean isLeftShoulder, CompoundTag entityNbt, boolean shouldSync) {
CompoundTag copied = entityNbt == null ? new CompoundTag() : entityNbt.copy();
if (isLeftShoulder) {
this.cobblemon$shoulderEntityLeft = copied;
} else {
this.cobblemon$shoulderEntityRight = copied;
}
if (shouldSync && !this.level().isClientSide() && (Object)this instanceof ServerPlayer player) {
ShoulderEntitySynchronizer.sync(player);
}
}
@Redirect(
method = "getDestroySpeed",
at = @At(
@@ -198,39 +186,11 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
return player.onGround();
}
@Inject(
method = "eat",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/player/Player;getFoodData()Lnet/minecraft/world/food/FoodData;",
shift = At.Shift.AFTER
)
)
public void onEatFood(Level world, ItemStack stack, FoodProperties foodComponent, CallbackInfoReturnable<ItemStack> cir) {
if (!level().isClientSide) {
if (stack.is(CobblemonItemTags.LEAVES_LEFTOVERS) && level().random.nextDouble() < Cobblemon.config.getAppleLeftoversChance()) {
ItemStack leftovers = new ItemStack(CobblemonItems.LEFTOVERS);
ServerPlayer player = Objects.requireNonNull(getServer()).getPlayerList().getPlayer(uuid);
assert player != null;
CobblemonEvents.LEFTOVERS_CREATED.postThen(
new LeftoversCreatedEvent(player, leftovers),
leftoversCreatedEvent -> null,
leftoversCreatedEvent -> {
if(!player.addItem(leftoversCreatedEvent.getLeftovers())) {
var itemPos = player.getLookAngle().scale(0.5f).add(position());
level().addFreshEntity(new ItemEntity(level(), itemPos.x(), itemPos.y(), itemPos.z(), leftoversCreatedEvent.getLeftovers()));
}
return null;
}
);
}
}
}
@Inject(method = "isInvulnerableTo", at = @At("HEAD"), cancellable = true)
public void isInvulnerableTo(DamageSource source, CallbackInfoReturnable<Boolean> ci) {
if (!level().isClientSide && (Object) this instanceof ServerPlayer player) {
boolean invulnerableInBattle = this.level().getGameRules().getBoolean(CobblemonGameRules.BATTLE_INVULNERABILITY);
public void isInvulnerableTo(net.minecraft.server.level.ServerLevel serverLevel, DamageSource source, CallbackInfoReturnable<Boolean> ci) {
if ((Object) this instanceof ServerPlayer player) {
boolean invulnerableInBattle = serverLevel.getGameRules().get(CobblemonGameRules.BATTLE_INVULNERABILITY);
boolean inBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (invulnerableInBattle && inBattle) {
ci.setReturnValue(true);
@@ -240,34 +200,40 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
@Override @Nullable
public PokedexEntityData resolvePokemonScan() {
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityRight())){
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityRight())) {
return getDataFromShoulderPokemon(this.getShoulderEntityRight());
}
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityLeft())){
if (CompoundTagUtilities.isShoulderPokemon(this.getShoulderEntityLeft())) {
return getDataFromShoulderPokemon(this.getShoulderEntityLeft());
}
return null;
}
@Nullable @Unique
@Nullable
@Unique
private PokedexEntityData getDataFromShoulderPokemon(CompoundTag shoulderTag) {
Pokemon pokemon;
if (level().isClientSide) {
CompoundTag pokemonTag = shoulderTag.getCompound(DataKeys.POKEMON);
if (level().isClientSide()) {
CompoundTag pokemonTag = shoulderTag.getCompound(DataKeys.POKEMON).orElse(new CompoundTag());
if (pokemonTag.isEmpty()) return null;
Species species = PokemonSpecies.getByIdentifier(ResourceLocation.parse(pokemonTag.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER)));
String speciesId = pokemonTag.getString(DataKeys.POKEMON_SPECIES_IDENTIFIER).orElse("");
if (speciesId.isEmpty()) return null;
Species species = PokemonSpecies.getByIdentifier(Identifier.parse(speciesId));
if (species == null) return null;
String formId = pokemonTag.getString(DataKeys.POKEMON_FORM_ID);
String formId = pokemonTag.getString(DataKeys.POKEMON_FORM_ID).orElse("");
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;
String genderString = pokemonTag.getString(DataKeys.POKEMON_GENDER);
String genderString = pokemonTag.getString(DataKeys.POKEMON_GENDER).orElse("");
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());
boolean shiny = pokemonTag.getBoolean(DataKeys.POKEMON_SHINY).orElse(false);
int level = pokemonTag.getInt(DataKeys.POKEMON_LEVEL).orElse(0);
Set<String> aspects = new HashSet<>();
for (var tag : shoulderTag.getList(DataKeys.SHOULDER_ASPECTS).orElse(new ListTag())) {
tag.asString().ifPresent(aspects::add);
}
pokemon = new Pokemon();
pokemon.setSpecies(species);
@@ -277,8 +243,8 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
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));
PartyStore party = Cobblemon.INSTANCE.getStorage().getParty(this.getUUID(), this.registryAccess());
pokemon = party.get(CompoundTagExtensionsKt.getUUID(shoulderTag, DataKeys.SHOULDER_UUID));
}
return (pokemon == null) ? null : new PokedexEntityData(pokemon, null);
}
@@ -288,21 +254,6 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
return this;
}
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (cobblemon$orientationController.getActive()) {
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);
}
}
@WrapOperation(
method = "rideTick()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;wantsToStopRiding()Z")
@@ -0,0 +1,42 @@
/*
* 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.CobblemonRecipeBookTypes;
import net.minecraft.stats.RecipeBookSettings;
import net.minecraft.world.inventory.RecipeBookType;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.function.UnaryOperator;
@Mixin(RecipeBookSettings.class)
public class RecipeBookSettingsMixin {
@Unique
private RecipeBookSettings.TypeSettings cobblemon$cookingPot = new RecipeBookSettings.TypeSettings(false, false);
@Inject(method = "getSettings", at = @At("HEAD"), cancellable = true)
public void getSettings(RecipeBookType type, CallbackInfoReturnable<RecipeBookSettings.TypeSettings> cir) {
if (type == CobblemonRecipeBookTypes.INSTANCE.getCOOKING_POT()) {
cir.setReturnValue(cobblemon$cookingPot);
}
}
@Inject(method = "updateSettings", at = @At("HEAD"), cancellable = true)
private void updateSetting(RecipeBookType recipeBookType, UnaryOperator<RecipeBookSettings.TypeSettings> operator, CallbackInfo ci) {
if (recipeBookType == CobblemonRecipeBookTypes.INSTANCE.getCOOKING_POT()) {
cobblemon$cookingPot = operator.apply(cobblemon$cookingPot);
ci.cancel();
}
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.fabric.mixin;
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonRecipeBookTypes;
import net.minecraft.world.inventory.RecipeBookType;
@@ -41,8 +41,6 @@ public class RecipeBookTypeMixin {
)
private static void cobblemon$addRecipeBookType(CallbackInfo ci) {
ArrayList<RecipeBookType> types = new ArrayList<>(List.of($VALUES));
// if you change or add any of these, neoforge uses a separate mechanism
// check enumextensions.json and `CobblemonEnumExtensions`
types.add(cobblemon$createBookType(CobblemonRecipeBookTypes.COOKING_POT_NAME, $VALUES.length));
$VALUES = types.toArray(RecipeBookType[]::new);
}
@@ -0,0 +1,94 @@
/*
* 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.CobblemonRecipePropertySets;
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipePropertySet;
import org.spongepowered.asm.mixin.Final;
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 java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Mixin(RecipeManager.class)
public abstract class RecipeManagerMixin {
@Shadow
@Final
private static Map<ResourceKey<RecipePropertySet>, RecipeManager.IngredientExtractor> RECIPE_PROPERTY_SETS;
@WrapOperation(method = "<clinit>", at = @At(value = "INVOKE", target = "Ljava/util/Map;of(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map;"))
private static Map poggers(Object k1, Object v1,
Object k2, Object v2,
Object k3, Object v3,
Object k4, Object v4,
Object k5, Object v5,
Object k6, Object v6,
Object k7, Object v7,
Operation<Map> original) {
return new HashMap(original.call(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7));
}
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void cobblemon$addPropertySets(CallbackInfo ci) {
RECIPE_PROPERTY_SETS.put(CobblemonRecipePropertySets.INSTANCE.getBREWING_BOTTLE(), recipe -> {
if (recipe instanceof BrewingStandRecipe brew) {
return Optional.of(brew.getBottle());
}
return Optional.empty();
});
RECIPE_PROPERTY_SETS.put(CobblemonRecipePropertySets.INSTANCE.getBREWING_INPUT(), recipe -> {
if (recipe instanceof BrewingStandRecipe brew) {
return Optional.of(brew.getInput());
}
return Optional.empty();
});
RECIPE_PROPERTY_SETS.put(CobblemonRecipePropertySets.INSTANCE.getCOOKING_POT_FLAVOUR_PROCESSOR(), recipe -> {
if (recipe instanceof CookingPotRecipeBase cooking && cooking.getSeasoningProcessors().stream().anyMatch(x -> x.getType().equals("flavour"))) {
return Optional.of(Ingredient.of(cooking.getResult().item().value()));
}
return Optional.empty();
});
RECIPE_PROPERTY_SETS.put(CobblemonRecipePropertySets.INSTANCE.getCOOKING_POT_FOOD_PROCESSOR(), recipe -> {
if (recipe instanceof CookingPotRecipeBase cooking && cooking.getSeasoningProcessors().stream().anyMatch(x -> x.getType().equals("food"))) {
return Optional.of(Ingredient.of(cooking.getResult().item().value()));
}
return Optional.empty();
});
RECIPE_PROPERTY_SETS.put(CobblemonRecipePropertySets.INSTANCE.getCOOKING_POT_MOB_EFFECTS_PROCESSOR(), recipe -> {
if (recipe instanceof CookingPotRecipeBase cooking && cooking.getSeasoningProcessors().stream().anyMatch(x -> x.getType().equals("mob_effects"))) {
return Optional.of(Ingredient.of(cooking.getResult().item().value()));
}
return Optional.empty();
});
RECIPE_PROPERTY_SETS.put(CobblemonRecipePropertySets.INSTANCE.getCOOKING_POT_SPAWN_BAIT_PROCESSOR(), recipe -> {
if (recipe instanceof CookingPotRecipeBase cooking && cooking.getSeasoningProcessors().stream().anyMatch(x -> x.getType().equals("spawn_bait"))) {
return Optional.of(Ingredient.of(cooking.getResult().item().value()));
}
return Optional.empty();
});
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
import com.mojang.authlib.GameProfile;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.network.protocol.Packet;
import net.minecraft.server.level.ChunkMap;
@@ -27,6 +28,8 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
@@ -43,9 +46,6 @@ import java.util.function.Consumer;
@Mixin(ServerEntity.class)
public abstract class ServerEntityMixin {
@Shadow
@Final
private Consumer<Packet<?>> broadcast;
@Shadow
@Final
private Entity entity;
@@ -30,14 +30,14 @@ public abstract class ServerLevelMixin {
final ServerLevel serverLevel = (ServerLevel) (Object) this;
if (ci.getReturnValue().isEmpty()) {
// Search for Pokemon with the ability Lightning Rod and redirect to them
AABB aABB = AABB.encapsulatingFullBlocks(blockPos, new BlockPos(blockPos.atY(serverLevel.getMaxBuildHeight()))).inflate(64.0);
AABB aABB = AABB.encapsulatingFullBlocks(blockPos, new BlockPos(blockPos.atY(serverLevel.getMaxY() + 1))).inflate(64.0);
List<LivingEntity> list = serverLevel.getEntitiesOfClass(LivingEntity.class, aABB, (livingEntity) -> livingEntity != null
&& livingEntity.isAlive()
&& livingEntity instanceof PokemonEntity
&& ((PokemonEntity) livingEntity).getBeamMode() == 0
&& ((PokemonEntity) livingEntity).getPokemon().getAbility().getName().equals("lightningrod"));
if (!list.isEmpty()) {
ci.setReturnValue(Optional.of((list.get(serverLevel.random.nextInt(list.size()))).blockPosition()));
ci.setReturnValue(Optional.of((list.get(serverLevel.getRandom().nextInt(list.size()))).blockPosition()));
}
}
}
@@ -16,29 +16,43 @@ import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
import com.cobblemon.mod.common.api.stats.CobblemonStats;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.util.CompoundTagUtilities;
import com.cobblemon.mod.common.util.ShoulderEntitySynchronizer;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
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(ServerPlayer.class)
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
@Shadow
public abstract ServerLevel serverLevel();
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@Shadow public abstract CompoundTag getShoulderEntityRight();
@Shadow public abstract void respawnEntityOnShoulder(CompoundTag entityNbt);
@Shadow public abstract void setShoulderEntityRight(CompoundTag entityNbt);
@Shadow public abstract void setShoulderEntityLeft(CompoundTag entityNbt);
@Unique
public PlayerSpawner cobblemon$spawner;
@Shadow
private long timeEntitySatOnShoulder;
@Override
public PlayerSpawner getPlayerSpawner() {
ServerPlayer player = (ServerPlayer)(Object)this;
@@ -55,21 +69,60 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
@Inject(method = "tick", at = @At("TAIL"))
private void cobblemon$tickSpawner(CallbackInfo ci) {
ServerPlayer player = (ServerPlayer)(Object)this;
if (!Cobblemon.config.getEnableSpawning()) {
return;
} else if (!serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
} else if (!player.level().getGameRules().get(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
return;
}
getPlayerSpawner().tick();
}
@Inject(method = "removeEntitiesOnShoulder", at = @At("HEAD"), cancellable = true)
private void cobblemon$preventPokemonDropping(CallbackInfo ci) {
ServerPlayer player = (ServerPlayer)(Object)this;
if (player.isSpectator() || player.isDeadOrDying()) {
return;
}
long gameTime = player.level().getGameTime();
if (this.timeEntitySatOnShoulder + 20L >= gameTime) {
return;
}
boolean changed = false;
CompoundTag leftShoulder = player.getShoulderEntityLeft();
if (!leftShoulder.isEmpty() && !CompoundTagUtilities.isShoulderPokemon(leftShoulder)) {
respawnEntityOnShoulder(leftShoulder);
setShoulderEntityLeft(new CompoundTag());
changed = true;
}
CompoundTag rightShoulder = player.getShoulderEntityRight();
if (!rightShoulder.isEmpty() && !CompoundTagUtilities.isShoulderPokemon(rightShoulder)) {
respawnEntityOnShoulder(rightShoulder);
setShoulderEntityRight(new CompoundTag());
changed = true;
}
this.timeEntitySatOnShoulder = gameTime;
if (changed) {
ShoulderEntitySynchronizer.sync(player);
}
ci.cancel();
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
ServerPlayer player = (ServerPlayer)(Object)this;
Entity vehicle = player.getVehicle();
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation(vehicle);
controllableVehicle.getOrientationController().setActive(shouldUseCustomOrientation);
var controller = controllableVehicle.getOrientationController();
controller.setActive(shouldUseCustomOrientation);
if (shouldUseCustomOrientation && controller.getOrientation() == null) {
controller.updateOrientation(matrix -> matrix);
}
}
@Unique
@@ -81,16 +134,9 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
});
}
@Inject(method = "stopRiding", at = @At("HEAD"))
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
ServerPlayer player = (ServerPlayer)(Object)this;
if (!(player.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
controllableVehicle.getOrientationController().setActive(false);
}
//TODO: Switch to sending out on load instead of preventing saving
@ModifyExpressionValue(method = "addAdditionalSaveData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z"))
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
@ModifyExpressionValue(method = "saveParentVehicle", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z"))
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(name = "rootVehicle") Entity entity) {
return original && !(entity instanceof PokemonEntity);
}
@@ -13,6 +13,7 @@ import kotlin.Unit;
import net.minecraft.core.BlockPos;
import net.minecraft.core.dispenser.ShearsDispenseItemBehavior;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -24,10 +25,10 @@ public abstract class ShearsDispenserBehaviorMixin {
// This exists, so we can execute our custom interaction only if no one redirected shears interactions to overwrite the vanilla ones.
@Inject(method = "tryShearBeehive", at = @At("HEAD"), cancellable = true)
private static void cobblemon$tryApricornHarvest(ServerLevel world, BlockPos pos, CallbackInfoReturnable<Boolean> cir) {
final BlockState state = world.getBlockState(pos);
private static void cobblemon$tryApricornHarvest(ServerLevel serverLevel, ItemStack itemStack, BlockPos blockPos, CallbackInfoReturnable<Boolean> cir) {
final BlockState state = serverLevel.getBlockState(blockPos);
if (state.getBlock() instanceof ShearableBlock shearableBlock) {
cir.setReturnValue(shearableBlock.attemptShear(world, state, pos, () -> Unit.INSTANCE));
cir.setReturnValue(shearableBlock.attemptShear(serverLevel, state, blockPos, () -> Unit.INSTANCE));
}
}
@@ -0,0 +1,45 @@
/*
* 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 static com.cobblemon.mod.common.datafixer.CobblemonSchemas.VERSION_KEY;
import com.cobblemon.mod.common.datafixer.CobblemonSchemas;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.chunk.storage.SimpleRegionStorage;
import org.spongepowered.asm.mixin.Debug;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(SimpleRegionStorage.class)
public class SimpleRegionStorageMixin {
@Final
@Shadow
private DataFixTypes dataFixType;
//Modifies the NBT returned by the vanilla upgradeChunkTag to also incorporate cobblemon fixup stuff
@ModifyExpressionValue(
method = "upgradeChunkTag(Lnet/minecraft/nbt/CompoundTag;ILnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/util/datafix/DataFixTypes;update(Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/nbt/CompoundTag;II)Lnet/minecraft/nbt/CompoundTag;"
)
)
public CompoundTag cobblemon$doChunkFix(
CompoundTag vanillaFixed
) {
int curVersion = vanillaFixed.getIntOr(VERSION_KEY, 0);
return vanillaFixed; //TODO FIGURE OUT WHAT THE HECK IS GOING ON HERE
//return this.dataFixType.update(CobblemonSchemas.getDATA_FIXER(), vanillaFixed, curVersion, CobblemonSchemas.DATA_VERSION);
}
}
@@ -21,7 +21,7 @@ import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.TickableSoundInstance;
import net.minecraft.client.sounds.ChannelAccess;
import net.minecraft.client.sounds.SoundEngine;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@@ -69,39 +69,39 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
/** Resumes the SoundInstance(s) queried by id and/or category. */
@Override
public void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
public void resumeSounds(@Nullable Identifier id, @Nullable SoundSource category) {
if (category != null) {
this.instanceBySource.get(category).forEach((sound) -> {
if (id == null || sound.getLocation().equals(id)) resume(sound);
if (id == null || sound.getIdentifier().equals(id)) resume(sound);
});
} else if (id == null) {
this.instanceToChannel.keySet().forEach(this::resume);
} else {
this.instanceToChannel.keySet().forEach(sound -> {
if (sound.getLocation().equals(id)) resume(sound);
if (sound.getIdentifier().equals(id)) resume(sound);
});
}
}
/** Pauses the SoundInstances queried by id and/or category. */
@Override
public void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
public void pauseSounds(@Nullable Identifier id, @Nullable SoundSource category) {
if (category != null) {
this.instanceBySource.get(category).forEach((sound) -> {
if (id == null || sound.getLocation().equals(id)) pause(sound);
if (id == null || sound.getIdentifier().equals(id)) pause(sound);
});
} else if (id == null) {
this.instanceToChannel.keySet().forEach(this::pause);
} else {
this.instanceToChannel.keySet().forEach(sound -> {
if (sound.getLocation().equals(id)) pause(sound);
if (sound.getIdentifier().equals(id)) pause(sound);
});
}
}
/** Allows stopping all SoundInstances that belong to a queried category. */
@Inject(method = "stop(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/sounds/SoundSource;)V", at = @At("HEAD"), cancellable = true)
public void stopSounds(@Nullable ResourceLocation id, @Nullable SoundSource category, CallbackInfo cb) {
@Inject(method = "stop(Lnet/minecraft/resources/Identifier;Lnet/minecraft/sounds/SoundSource;)V", at = @At("HEAD"), cancellable = true)
public void stopSounds(@Nullable Identifier id, @Nullable SoundSource category, CallbackInfo cb) {
if (id == null && category != null) {
this.instanceBySource.get(category).forEach(this::stop);
cb.cancel();
@@ -17,7 +17,7 @@ import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.UnderwaterAmbientSoundInstances;
import net.minecraft.client.sounds.SoundEngine;
import net.minecraft.client.sounds.SoundManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundSource;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
@@ -26,6 +26,7 @@ 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.CallbackInfoReturnable;
@Mixin(SoundManager.class)
public abstract class SoundManagerMixin implements SoundManagerDuck {
@@ -55,20 +56,20 @@ public abstract class SoundManagerMixin implements SoundManagerDuck {
/** Pauses the queried SoundInstance(s). If id is null, will pause all sounds belonging to the SoundCategory. */
@Override
public void pauseSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
public void pauseSounds(@Nullable Identifier id, @Nullable SoundSource category) {
((SoundEngineDuck)soundEngine).pauseSounds(id, category);
}
/** Resumes the queried SoundInstance(s). If id is null, will resume all sounds belonging to the SoundCategory. */
@Override
public void resumeSounds(@Nullable ResourceLocation id, @Nullable SoundSource category) {
public void resumeSounds(@Nullable Identifier id, @Nullable SoundSource category) {
((SoundEngineDuck)soundEngine).resumeSounds(id, category);
}
/** Blocks filtered sounds from being played while a BattleMusicInstance is in progress. */
@Inject(method = "play(Lnet/minecraft/client/resources/sounds/SoundInstance;)V", at = @At("HEAD"), cancellable = true)
public void playStart(SoundInstance sound, CallbackInfo cb) {
if (filterCondition(sound)) cb.cancel();
@Inject(method = "play", at = @At("HEAD"), cancellable = true)
public void playStart(SoundInstance soundInstance, CallbackInfoReturnable<SoundEngine.PlayResult> cir) {
if (filterCondition(soundInstance)) cir.cancel();
}
@Inject(method = "playDelayed(Lnet/minecraft/client/resources/sounds/SoundInstance;I)V", at = @At("HEAD"), cancellable = true)
@@ -77,14 +78,14 @@ public abstract class SoundManagerMixin implements SoundManagerDuck {
}
/** Pauses ambient loops while a BattleMusicInstance is in progress. */
@Inject(method = "play(Lnet/minecraft/client/resources/sounds/SoundInstance;)V", at = @At("TAIL"))
public void playEnd(SoundInstance sound, CallbackInfo cb) {
if (ambientLoopCondition(sound)) this.pauseSounds(sound.getLocation(), SoundSource.AMBIENT);
@Inject(method = "play", at = @At("TAIL"))
public void playEnd(SoundInstance soundInstance, CallbackInfoReturnable<SoundEngine.PlayResult> cir) {
if (ambientLoopCondition(soundInstance)) this.pauseSounds(soundInstance.getIdentifier(), SoundSource.AMBIENT);
}
@Inject(method = "playDelayed(Lnet/minecraft/client/resources/sounds/SoundInstance;I)V", at = @At("TAIL"))
public void playEnd(SoundInstance sound, int delay, CallbackInfo cb) {
if (ambientLoopCondition(sound)) this.pauseSounds(sound.getLocation(), SoundSource.AMBIENT);
if (ambientLoopCondition(sound)) this.pauseSounds(sound.getIdentifier(), SoundSource.AMBIENT);
}
/** Clears tracked sounds on the client when disconnected. */
@@ -12,10 +12,11 @@ import com.cobblemon.mod.common.world.CobblemonStructureIDs;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.Identifier;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
@@ -39,7 +40,7 @@ import java.util.function.Predicate;
public abstract class StructureMixin {
// Define a set of structures that should not spawn below Sea Level
@Unique
private static final Set<ResourceLocation> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
private static final Set<Identifier> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
CobblemonStructureIDs.STONJOURNER_HENGE,
CobblemonStructureIDs.LUNA_HENGE,
CobblemonStructureIDs.SOL_HENGE
@@ -47,13 +48,13 @@ public abstract class StructureMixin {
@Inject(method = "generate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
cancellable = true)
public void cobblemon$isValid(RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
public void cobblemon$isValid(Holder<Structure> structure, ResourceKey<Level> levelKey, RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome,
CallbackInfoReturnable<StructureStart> cir,
@Local StructureStart structureStart) {
ResourceLocation structureKey = registryAccess.registryOrThrow(Registries.STRUCTURE).getKey((Structure) (Object) this);
Identifier structureKey = structure.unwrapKey().map(ResourceKey::identifier).orElse(null);
if(!RESTRICTED_STRUCTURES.contains(structureKey)) {
return;
}
@@ -64,4 +65,4 @@ public abstract class StructureMixin {
}
}
}
}
}
@@ -29,4 +29,4 @@ public interface StructurePoolAccessor {
@Accessor("templates")
ObjectArrayList<StructurePoolElement> getElements();
}
}
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.chunk.ChunkGenerator;
@@ -38,7 +38,7 @@ public abstract class StructurePoolGeneratorMixin {
Map<String, Integer> generatedStructureGroupCounts;
private static final Map<String, Integer> structureMaxes;
private static final Map<ResourceLocation, Set<String>> structureGroups = new HashMap<>();
private static final Map<Identifier, Set<String>> structureGroups = new HashMap<>();
static {
String pokecenter = "pokecenter";
String berry = "berry_farm";
@@ -73,11 +73,11 @@ public abstract class StructurePoolGeneratorMixin {
structureMaxes = Collections.unmodifiableMap(aMap);
}
public Set<String> getGroups(ResourceLocation structureIdentifier) {
public Set<String> getGroups(Identifier structureIdentifier) {
return structureGroups.getOrDefault(structureIdentifier, Set.of());
}
public boolean hasReachedMaximum(ResourceLocation structureIdentifier) {
public boolean hasReachedMaximum(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
int count = generatedStructureGroupCounts.getOrDefault(group, 0);
@@ -89,7 +89,7 @@ public abstract class StructurePoolGeneratorMixin {
return false;
}
public void incrementStructureCount(ResourceLocation structureIdentifier) {
public void incrementStructureCount(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
generatedStructureGroupCounts.put(group, generatedStructureGroupCounts.getOrDefault(group, 0) + 1);
@@ -107,7 +107,7 @@ public abstract class StructurePoolGeneratorMixin {
while (iterator.hasNext()) {
StructurePoolElement structure = iterator.next();
ResourceLocation structurePieceLocationKey = getCobblemonOnlyLocation(structure);
Identifier structurePieceLocationKey = getCobblemonOnlyLocation(structure);
if (structurePieceLocationKey == null) {
reducedList.add(structure);
continue;
@@ -134,14 +134,14 @@ public abstract class StructurePoolGeneratorMixin {
CallbackInfo ci,
@Local(ordinal = 1) StructurePoolElement structurePoolElement
) {
ResourceLocation structureLocationKey = getCobblemonOnlyLocation(structurePoolElement);
Identifier structureLocationKey = getCobblemonOnlyLocation(structurePoolElement);
if (structureLocationKey != null) {
incrementStructureCount(structureLocationKey);
}
}
private static ResourceLocation getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
ResourceLocation location = getLocationIfAvailable(structurePoolElement);
private static Identifier getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
Identifier location = getLocationIfAvailable(structurePoolElement);
if (location == null) return null;
if (!location.getNamespace().equals("cobblemon")) return null;
@@ -149,7 +149,7 @@ public abstract class StructurePoolGeneratorMixin {
return location;
}
private static ResourceLocation getLocationIfAvailable(StructurePoolElement structurePoolElement) {
private static Identifier getLocationIfAvailable(StructurePoolElement structurePoolElement) {
if (structurePoolElement instanceof LegacySinglePoolElement legacySinglePoolElement) {
if (legacySinglePoolElement.template.left().isEmpty()) return null;
@@ -162,4 +162,4 @@ public abstract class StructurePoolGeneratorMixin {
return null;
}
}
}
}
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue;
import com.cobblemon.mod.common.entity.MoLangScriptingEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.InsideBlockEffectApplier;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.SweetBerryBushBlock;
import net.minecraft.world.level.block.state.BlockState;
@@ -25,7 +26,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class SweetBerryBushBlockMixin {
@Inject(method = "entityInside", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$entityInside(BlockState state, Level level, BlockPos pos, Entity entity, CallbackInfo callbackInfo) {
private void cobblemon$entityInside(
BlockState state,
Level level,
BlockPos pos,
Entity entity,
InsideBlockEffectApplier effectApplier,
boolean moved,
CallbackInfo callbackInfo
) {
if (entity instanceof MoLangScriptingEntity) {
if (((MoLangScriptingEntity) entity).getConfig().getMap().getOrDefault("immune_to_sweet_berry_bush_block", DoubleValue.ZERO).asDouble() == 1.0) {
callbackInfo.cancel();
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
@@ -23,11 +24,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class TargetingConditionsMixin {
@Inject(method = "test", at = @At("HEAD"), cancellable = true)
public void test(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> ci) {
public void test(ServerLevel serverLevel, LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> cir) {
if (targetEntity instanceof ServerPlayer player) {
if (player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE)) return;
if (serverLevel.getGameRules().get(CobblemonGameRules.MOB_TARGET_IN_BATTLE)) return;
boolean targetInBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (targetInBattle) ci.setReturnValue(false);
if (targetInBattle) cir.setReturnValue(false);
}
}
}
@@ -1,68 +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.Cobblemon;
import com.cobblemon.mod.common.CobblemonVillagerProfessions;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import kotlin.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.npc.VillagerProfession;
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;
@Mixin(RenderLayer.class)
public abstract class VillagerProfessionLayerMixin {
@Inject(method = "renderColoredCutoutModel", at = @At(value = "HEAD"), cancellable = true)
private static <T extends LivingEntity> void cobblemon$renderVillagerProfessionLayer(EntityModel<T> entityModel, ResourceLocation resourceLocation, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, int j, CallbackInfo ci) {
if (resourceLocation.getNamespace().equals(Cobblemon.MODID)) {
VillagerProfession profession = null;
boolean isZombie = false;
if (livingEntity instanceof Villager) profession = ((Villager) livingEntity).getVillagerData().getProfession();
if (livingEntity instanceof ZombieVillager) {
profession = ((ZombieVillager) livingEntity).getVillagerData().getProfession();
isZombie = true;
}
Pair<String, String[]> override = CobblemonVillagerProfessions.INSTANCE.getNameTagOverride(profession);
if (profession != null && override != null) {
String entityName = ChatFormatting.stripFormatting(livingEntity.getName().getString());
boolean nameMatches = false;
for (String name: override.getSecond())
if (entityName.endsWith(name)) {
nameMatches = true;
break;
}
if (nameMatches && resourceLocation.equals(getProfessionResourceLocation(resourceLocation.getNamespace(), ResourceLocation.parse(profession.name()).getPath(), isZombie))) {
ResourceLocation textureLocation = getProfessionResourceLocation(resourceLocation.getNamespace(), override.getFirst(), isZombie);
VertexConsumer vertexConsumer = multiBufferSource.getBuffer(RenderType.entityCutoutNoCull(textureLocation));
entityModel.renderToBuffer(poseStack, vertexConsumer, i, LivingEntityRenderer.getOverlayCoords(livingEntity, 0.0F), j);
ci.cancel();
}
}
}
}
private static ResourceLocation getProfessionResourceLocation(String namespace, String profession, Boolean isZombie) {
return ResourceLocation.fromNamespaceAndPath(namespace, "textures/entity/" + (isZombie ? "zombie_villager" : "villager") + "/profession/" + profession + ".png");
}
}
@@ -1,21 +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.accessor;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(BlockEntity.class)
public interface BlockEntityAccessor {
@Accessor("level") Level getLevel();
@Accessor("worldPosition") BlockPos getWorldPosition();
}
@@ -17,4 +17,4 @@ import org.spongepowered.asm.mixin.gen.Accessor;
public interface ChunkMapAccessor {
@Accessor("entityMap")
Int2ObjectMap<?> getEntityMap();
}
}
@@ -1,26 +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.accessor;
import net.minecraft.world.item.Item;
import net.minecraft.core.dispenser.DispenseItemBehavior;
import net.minecraft.world.level.block.DispenserBlock;
import java.util.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(DispenserBlock.class)
public interface DispenserBlockAccessor {
@Accessor("DISPENSER_REGISTRY")
static Map<Item, DispenseItemBehavior> getItemBehaviors() {
throw new AssertionError();
}
}
@@ -1,19 +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.accessor;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(AbstractSelectionList.class)
public interface EntryListWidgetAccessor {
@Accessor("renderHeader")
boolean getRenderHeader();
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.client.gui.components.StateSwitchingButton;
import net.minecraft.client.gui.components.CycleButton;
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin;
@@ -18,5 +18,5 @@ import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(RecipeBookComponent.class)
public interface RecipeBookComponentAccessor {
@Accessor("filterButton")
StateSwitchingButton getFilterButton();
CycleButton<Boolean> getFilterButton();
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(ServerPlayer.class)
public interface ServerPlayerAccessor {
@Invoker("setShoulderEntityLeft")
public void cobblemon$setShoulderEntityLeft(CompoundTag nbt);
@Invoker("setShoulderEntityRight")
public void cobblemon$setShoulderEntityRight(CompoundTag nbt);
@Invoker("respawnEntityOnShoulder")
public void cobblemon$respawnEntityOnShoulder(CompoundTag nbt);
}
@@ -19,7 +19,4 @@ import java.util.Set;
public interface TrackedEntityAccessor {
@Accessor("seenBy")
Set<ServerPlayerConnection> getSeenBy();
@Accessor("serverEntity")
ServerEntity getServerEntity();
}
}
@@ -20,6 +20,7 @@ import net.minecraft.core.NonNullList;
import net.minecraft.world.Containers;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.ItemStackTemplate;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
@@ -48,17 +49,17 @@ public class BrewingStandBlockEntityMixin {
ItemStack itemStack = slots.get(3);
for(int i = 0; i < 3; ++i) {
if (!slots.get(i).isEmpty()){
slots.set(i, recipe.getResult().copy());
slots.set(i, recipe.getResult().create());
}
}
itemStack.shrink(1);
if (itemStack.getItem().hasCraftingRemainingItem()) {
ItemStack itemStack2 = new ItemStack(itemStack.getItem().getCraftingRemainingItem());
ItemStackTemplate itemStack2 = itemStack.getItem().getCraftingRemainder();
if (itemStack2 != null) {
if (itemStack.isEmpty()) {
itemStack = itemStack2;
itemStack = itemStack2.create();
} else {
Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), itemStack2);
Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), itemStack2.create());
}
}
@@ -90,7 +91,10 @@ public class BrewingStandBlockEntityMixin {
}
BrewingStandInput input = new BrewingStandInput(ingredient, bottles);
RecipeManager recipeManager = level.getRecipeManager();
if (level.getServer() == null) {
return null;
}
RecipeManager recipeManager = level.getServer().getRecipeManager();
Optional<RecipeHolder<BrewingStandRecipe>> recipeHolder =
recipeManager.getRecipeFor(CobblemonRecipeTypes.INSTANCE.getBREWING_STAND(), input, level);
@@ -107,7 +111,7 @@ public class BrewingStandBlockEntityMixin {
if (level != null) {
var rm = level.getRecipeManager();
var rm = level.recipeAccess();
//Input slot
if (slot == 3) {
@@ -128,4 +132,4 @@ public class BrewingStandBlockEntityMixin {
}
}
}
}
}
@@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class BrewingStandMenuMixin {
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/Container;Lnet/minecraft/world/inventory/ContainerData;)V", at = @At("TAIL"))
private void captureLevel(int containerId, Inventory playerInventory, Container brewingStandContainer, ContainerData brewingStandData, CallbackInfo ci) {
var recipeManager = playerInventory.player.level().getRecipeManager();
var recipeManager = playerInventory.player.level().recipeAccess();
for (Slot slot : ((BrewingStandMenu) (Object) this).slots) {
if (slot instanceof RecipeAwareSlot awareSlot) {
awareSlot.setRecipeManager(recipeManager);
@@ -12,8 +12,7 @@ import com.cobblemon.mod.common.duck.RecipeAwareSlot;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level;
import net.minecraft.world.item.crafting.RecipeAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -24,10 +23,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class IngredientSlotMixin implements RecipeAwareSlot {
@Unique
private RecipeManager cobblemon$recipeManager;
private RecipeAccess cobblemon$recipeManager;
@Override
public void setRecipeManager(RecipeManager recipeManager) {
public void setRecipeManager(RecipeAccess recipeManager) {
this.cobblemon$recipeManager = recipeManager;
}
@@ -12,8 +12,7 @@ import com.cobblemon.mod.common.duck.RecipeAwareSlot;
import com.cobblemon.mod.common.item.crafting.brewingstand.BrewingStandRecipe;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level;
import net.minecraft.world.item.crafting.RecipeAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -24,10 +23,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class PotionSlotMixin implements RecipeAwareSlot {
@Unique
private RecipeManager cobblemon$recipeManager;
private RecipeAccess cobblemon$recipeManager;
@Override
public void setRecipeManager(RecipeManager recipeManager) {
public void setRecipeManager(RecipeAccess recipeManager) {
this.cobblemon$recipeManager = recipeManager;
}
@@ -21,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(AbstractClientPlayer.class)
public class AbstractClientPlayerMixin {
@Inject(
method = "getFieldOfViewModifier()F",
method = "getFieldOfViewModifier",
at = @At(
value = "HEAD"
),
@@ -35,8 +35,7 @@ public class AbstractClientPlayerMixin {
// Only modify fov through riding if the pokedex is not open
AbstractClientPlayer player = (AbstractClientPlayer) (Object) this;
// Only modify fov for the driver and not the passengers
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity && player.getVehicle().getControllingPassenger() == player) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity ride && player.getVehicle().getControllingPassenger() == player) {
// Return custom fov mult for riding
cir.setReturnValue(ride.rideFovMult());
@@ -0,0 +1,46 @@
/*
* 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.duck.AvatarRenderStateDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.state.AvatarRenderState;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(AvatarRenderState.class)
public class AvatarRenderStateMixin implements AvatarRenderStateDuck {
@Unique
private @Nullable AbstractClientPlayer cobblemon$mountedPlayer;
@Unique
private @Nullable PokemonEntity cobblemon$mountedPokemon;
@Override
public @Nullable AbstractClientPlayer cobblemon$getMountedPlayer() {
return this.cobblemon$mountedPlayer;
}
@Override
public void cobblemon$setMountedPlayer(@Nullable AbstractClientPlayer player) {
this.cobblemon$mountedPlayer = player;
}
@Override
public @Nullable PokemonEntity cobblemon$getMountedPokemon() {
return this.cobblemon$mountedPokemon;
}
@Override
public void cobblemon$setMountedPokemon(@Nullable PokemonEntity pokemonEntity) {
this.cobblemon$mountedPokemon = pokemonEntity;
}
}
@@ -0,0 +1,55 @@
/*
* 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.duck.AvatarRenderStateDuck;
import com.cobblemon.mod.common.duck.LivingEntityRenderStateDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.player.AvatarRenderer;
import net.minecraft.client.renderer.entity.state.AvatarRenderState;
import net.minecraft.world.entity.Avatar;
import net.minecraft.world.phys.Vec3;
import org.objectweb.asm.Opcodes;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(AvatarRenderer.class)
public abstract class AvatarRendererMixin {
@Inject(method = "extractRenderState", at = @At("TAIL"))
private void cobblemon$captureMountedPlayer(Avatar avatar, AvatarRenderState state, float partialTick, CallbackInfo ci) {
AvatarRenderStateDuck duck = (AvatarRenderStateDuck) state;
LivingEntityRenderStateDuck livingDuck = (LivingEntityRenderStateDuck) state;
if (avatar instanceof AbstractClientPlayer player && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
duck.cobblemon$setMountedPlayer(player);
duck.cobblemon$setMountedPokemon(pokemonEntity);
state.isCrouching = false;
} else {
duck.cobblemon$setMountedPlayer(null);
duck.cobblemon$setMountedPokemon(null);
}
}
@ModifyExpressionValue(
method = "getRenderOffset(Lnet/minecraft/client/renderer/entity/state/AvatarRenderState;)Lnet/minecraft/world/phys/Vec3;",
at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/state/HumanoidRenderState;isCrouching:Z", opcode = Opcodes.GETFIELD)
)
private boolean cobblemon$disableMountedCrouchOffset(boolean isCrouching, AvatarRenderState state) {
AvatarRenderStateDuck duck = (AvatarRenderStateDuck) state;
if (duck.cobblemon$getMountedPlayer() != null && duck.cobblemon$getMountedPokemon() != null) {
return false;
}
return isCrouching;
}
}
@@ -12,33 +12,36 @@ import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
import com.cobblemon.mod.common.duck.CameraDuck;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
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 implements CameraDuck {
@Shadow private float eyeHeight;
@Shadow private float eyeHeightOld;
@Shadow private BlockGetter level;
@Shadow private Level level;
@Shadow private Entity entity;
@Final @Shadow private Vector3f forwards;
@Final @Shadow private Vector3f up;
@Final @Shadow private Vector3f left;
@Shadow private float yRot;
@Shadow private float xRot;
@Shadow protected abstract void setPosition(Vec3 pos);
@Shadow private Vec3 position;
@Shadow @Final private BlockPos.MutableBlockPos blockPosition;
@Shadow private int matrixPropertiesDirty;
@Override public BlockGetter cobblemon$getLevel() {
return level;
@@ -60,15 +63,19 @@ public abstract class CameraMixin implements CameraDuck {
@Override public float cobblemon$getYRot() { return yRot; }
@Override public void cobblemon$setYRot(float yRot) { this.yRot = yRot; }
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
public void cobblemon$positionCamera(Camera instance, double x, double y, double z, Operation<Void> original, @Local(ordinal = 1, argsOnly = true) boolean thirdPersonReverse) {
Entity entity = instance.getEntity();
@Override public void cobblemon$markDirty() { this.matrixPropertiesDirty |= 3; }
@Inject(method = "setPosition(DDD)V", at = @At("HEAD"), cancellable = true)
private void cobblemon$positionCamera(double x, double y, double z, CallbackInfo ci) {
if (this.entity == null) return;
Entity entity = this.entity;
Entity vehicle = entity.getVehicle();
boolean thirdPersonReverse = Minecraft.getInstance().options.getCameraType().isMirrored();
MountedPokemonAnimationRenderController.INSTANCE.reset();
if(vehicle instanceof Rideable){
Vec3 position = MountedCameraRenderer.INSTANCE.getCameraPosition(
instance,
if (vehicle instanceof Rideable) {
Vec3 ridePos = MountedCameraRenderer.INSTANCE.getCameraPosition(
(Camera) (Object) this,
entity,
vehicle,
thirdPersonReverse,
@@ -76,14 +83,17 @@ public abstract class CameraMixin implements CameraDuck {
eyeHeightOld
);
if(position != null) {
setPosition(position);
if (ridePos != null) {
// Directly set the position field instead of calling setPosition(Vec3)
this.position = ridePos;
this.blockPosition.set(ridePos.x, ridePos.y, ridePos.z);
ci.cancel();
return;
}
}
// reset the rotation values if it is now not being ridden.
MountedCameraRenderer.INSTANCE.setSmoothRotation(null);
original.call(instance, x, y, z);
}
}
@@ -1,85 +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.CobblemonRecipeCategories;
import com.cobblemon.mod.common.item.crafting.CookingPotBookCategory;
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe;
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase;
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeHolder;
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.CallbackInfoReturnable;
import java.util.List;
import java.util.Map;
@Mixin(ClientRecipeBook.class)
public abstract class ClientRecipeBookMixin {
@Shadow
protected static Map<RecipeBookCategories, List<List<RecipeHolder<?>>>> categorizeAndGroupRecipes(Iterable<RecipeHolder<?>> recipes) {
return null;
}
@Shadow
private Map<RecipeBookCategories, List<RecipeCollection>> collectionsByTab;
@Shadow
private List<RecipeCollection> allCollections;
@Inject(method = "getCategory", at = @At(value = "HEAD"), cancellable = true)
private static void addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategories> cir) {
Recipe<?> recipe2 = recipe.value();
if (recipe2 instanceof CookingPotRecipeBase) {
CookingPotBookCategory category = ((CookingPotRecipeBase) recipe2).getCategory();
RecipeBookCategories var7;
switch (category) {
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
case FOODS -> var7 = CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory();
case MEDICINES -> var7 = CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory();
case COMPLEX_DISHES -> var7 = CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory();
default -> throw new MatchException(null, null);
}
cir.setReturnValue(var7);
}
}
@Inject(method = "setupCollections", at = @At("RETURN"))
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci) {
Map<RecipeBookCategories, List<List<RecipeHolder<?>>>> map = categorizeAndGroupRecipes(recipes);
Map<RecipeBookCategories, List<RecipeCollection>> map2 = Maps.newHashMap();
ImmutableList.Builder<RecipeCollection> builder = ImmutableList.builder();
map.forEach((recipeBookCategories, list) -> {
List<RecipeCollection> collections = list.stream()
.map(listx -> new RecipeCollection(registryAccess, listx))
.peek(builder::add)
.collect(ImmutableList.toImmutableList());
map2.put(recipeBookCategories, collections);
});
RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
this.collectionsByTab = ImmutableMap.copyOf(map2);
this.allCollections = builder.build();
}
}
@@ -0,0 +1,32 @@
/*
* 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.mojang.blaze3d.platform.cursor.CursorType;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
import net.minecraft.world.item.CreativeModeTab;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(CreativeModeInventoryScreen.class)
public abstract class CreativeModeInventoryScreenMixin {
@Redirect(
method = "renderTabButton",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;requestCursor(Lcom/mojang/blaze3d/platform/cursor/CursorType;)V"
)
)
private void cobblemon$skipCreativeTabCursor(GuiGraphicsExtractor guiGraphics, CursorType cursorType, GuiGraphicsExtractor context, int mouseX, int mouseY, CreativeModeTab tab) {
// 1.21.1 had no special cursor on creative tab hover; keep the arrow cursor for tab icons.
}
}
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.gui.ScrollingWidget;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.gui.components.AbstractSelectionList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(AbstractSelectionList.class)
public abstract class EntryListWidgetMixin {
@SuppressWarnings("ConstantValue")
@ModifyExpressionValue(method = "getEntryAtPosition", at = @At(value = "CONSTANT", args = {"intValue=4"}))
public int cobblemon$adjustOnlyIfNecessary(int original) {
if(ScrollingWidget.class.isInstance(this)) {
return 0;
}
return original;
}
}
@@ -49,14 +49,14 @@ public class GameRendererMixin {
}
@ModifyArg(
method = "pick(Lnet/minecraft/world/entity/Entity;DDF)Lnet/minecraft/world/phys/HitResult;",
method = "pick",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/projectile/ProjectileUtil;getEntityHitResult(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;D)Lnet/minecraft/world/phys/EntityHitResult;"
target = "Lnet/minecraft/world/phys/EntityHitResult;getEntity()Lnet/minecraft/world/entity/Entity;"
),
index = 4
)
private Predicate<Entity> filterPokemonVehicle(Predicate<Entity> original, @Local(ordinal = 0, argsOnly = true) Entity entity) {
private Predicate<Entity> filterPokemonVehicle(Predicate<Entity> original, @Local Entity entity) {
return (entityX) -> !(entity.getVehicle() == entityX && entityX instanceof PokemonEntity) && original.test(entityX);
}
@@ -8,15 +8,17 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.duck.AvatarRenderStateDuck;
import com.cobblemon.mod.common.duck.LivingEntityRenderStateDuck;
import com.cobblemon.mod.common.entity.PoseType;
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.client.renderer.entity.state.AvatarRenderState;
import net.minecraft.client.renderer.entity.state.HumanoidRenderState;
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;
@@ -41,59 +43,75 @@ public class HumanoidModelMixin {
@Shadow @Final
public ModelPart body;
@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
) {
@Inject(
method = "setupAnim(Lnet/minecraft/client/renderer/entity/state/HumanoidRenderState;)V",
at = @At("TAIL")
)
public void cobblemon$animateRiders(HumanoidRenderState renderState, CallbackInfo ci) {
MountedPlayerRenderer.shouldApplyRootAnimation = false;
if (MountedPlayerRenderer.relevantPartsByName.size() <= 1) {
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
MountedPlayerRenderer.relevantPartsByName.put("head", head);
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
if (!(renderState instanceof AvatarRenderState avatarRenderState)) {
return;
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
// If the player is on a rollable ride then use the ride local rotations to rotate the head
// This is to allow the player head to face the correct direction when "freelooking"
// TODO: find a way for this to work on built jars without borking head rotations.
// if (entity instanceof RidePassenger playerRotater &&
// vehicle instanceof OrientationControllable controller &&
// controller.getOrientationController().isActive()) {
// this.head.xRot = playerRotater.cobblemon$getRideXRot();
// headPitch = playerRotater.cobblemon$getRideXRot();
// this.head.yRot = playerRotater.cobblemon$getRideYRot();
// netHeadYaw = playerRotater.cobblemon$getRideYRot();
// }
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
headPitch = 0f;
if (this.head != null) {
this.head.yRot = 0f;
this.head.xRot = 0f;
}
}
if (!(entity instanceof AbstractClientPlayer player)) return;
MountedPlayerRenderer.shouldApplyRootAnimation = true;
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
AvatarRenderStateDuck duck = (AvatarRenderStateDuck) avatarRenderState;
LivingEntityRenderStateDuck livingDuck = (LivingEntityRenderStateDuck) avatarRenderState;
AbstractClientPlayer player = duck.cobblemon$getMountedPlayer();
PokemonEntity pokemonEntity = duck.cobblemon$getMountedPokemon();
if (player == null || pokemonEntity == null) {
return;
}
int seatIndex = livingDuck.cobblemon$getMountedSeatIndex();
String seatLocator = livingDuck.cobblemon$getMountedSeatLocator();
PoseType poseType = livingDuck.cobblemon$getMountedPoseType();
if (seatIndex < 0 || poseType == null) {
return;
}
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
MountedPlayerRenderer.relevantPartsByName.put("head", head);
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
Entity vehicle = player.getVehicle();
if (!(vehicle instanceof PokemonEntity)) {
return;
}
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
float netHeadYaw = avatarRenderState.yRot;
float headPitch = avatarRenderState.xRot;
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
headPitch = 0f;
this.head.yRot = 0f;
this.head.xRot = 0f;
}
MountedPlayerRenderer.INSTANCE.animate(
(HumanoidModel<?>)(Object)this,
pokemonEntity,
player,
avatarRenderState,
seatIndex,
seatLocator,
poseType,
netHeadYaw,
headPitch,
avatarRenderState.ageInTicks,
avatarRenderState.walkAnimationPos,
avatarRenderState.walkAnimationSpeed
);
MountedPlayerRenderer.shouldApplyRootAnimation = true;
}
}
}
@@ -0,0 +1,62 @@
/*
* 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.item.PokeBallItem;
import com.cobblemon.mod.common.item.PokedexItem;
import net.minecraft.client.renderer.entity.ItemEntityRenderer;
import net.minecraft.client.renderer.entity.state.ItemEntityRenderState;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
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(ItemEntityRenderer.class)
public class ItemEntityRendererMixin {
@Shadow @Final private ItemModelResolver itemModelResolver;
@Inject(method = "extractRenderState", at = @At("TAIL"))
private void cobblemon$overrideDroppedItemModel(ItemEntity entity, ItemEntityRenderState state, float partialTick, CallbackInfo ci) {
Identifier modelId = cobblemon$getGroundModel(entity.getItem());
if (modelId == null) {
return;
}
ItemStack renderStack = entity.getItem().copy();
renderStack.set(DataComponents.ITEM_MODEL, modelId);
this.itemModelResolver.updateForNonLiving(state.item, renderStack, ItemDisplayContext.GROUND, entity);
}
@Unique
private static Identifier cobblemon$getGroundModel(ItemStack stack) {
if (stack.getItem() instanceof PokeBallItem pokeBallItem) {
return cobblemon$asStandaloneModel(pokeBallItem.getPokeBall().getModel3d());
}
if (stack.getItem() instanceof PokedexItem pokedexItem) {
return cobblemon$asStandaloneModel(pokedexItem.getType().getItemModelPath("off"));
}
return null;
}
@Unique
private static Identifier cobblemon$asStandaloneModel(Identifier modelId) {
return modelId.withPath(path -> path.startsWith("item/") ? path.substring("item/".length()) : path);
}
}
@@ -9,16 +9,27 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.item.PokeBallItem;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
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.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.Identifier;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.client.renderer.texture.OverlayTexture;
import org.spongepowered.asm.mixin.Final;
import org.joml.Quaternionf;
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.Redirect;
@@ -26,8 +37,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemInHandRenderer.class)
public class ItemInHandRendererMixin {
@Shadow @Final private ItemModelResolver itemModelResolver;
@Inject(method = "renderArmWithItem", at = @At(value = "HEAD"), cancellable = true)
private void cobblemon$renderArmWithItem(AbstractClientPlayer abstractClientPlayer, float f, float g, InteractionHand interactionHand, float h, ItemStack itemStack, float i, PoseStack poseStack, MultiBufferSource multiBufferSource, int j, CallbackInfo ci) {
private void cobblemon$renderArmWithItem(AbstractClientPlayer abstractClientPlayer, float f, float g, InteractionHand interactionHand, float h, ItemStack itemStack, float i, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, int j, CallbackInfo ci) {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
@@ -36,10 +49,58 @@ public class ItemInHandRendererMixin {
}
}
@Inject(method = "renderItem", at = @At("HEAD"), cancellable = true)
private void cobblemon$renderItem(LivingEntity entity, ItemStack stack, ItemDisplayContext displayContext, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, int light, CallbackInfo ci) {
Identifier modelId = cobblemon$getHeldModel(stack, entity, displayContext);
if (modelId == null) {
return;
}
ItemStack renderStack = stack.copy();
renderStack.set(DataComponents.ITEM_MODEL, modelId);
ItemStackRenderState renderState = new ItemStackRenderState();
this.itemModelResolver.updateForTopItem(renderState, renderStack, displayContext, entity.level(), entity, entity.getId() + displayContext.ordinal());
renderState.submit(poseStack, submitNodeCollector, light, OverlayTexture.NO_OVERLAY, 0);
ci.cancel();
}
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V"))
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
if (!(Minecraft.getInstance().player != null && Minecraft.getInstance().player.getVehicle() instanceof OrientationControllable controllable && controllable.getOrientationController().isActive())) {
instance.mulPose(quaternion);
}
}
@Unique
private static Identifier cobblemon$getHeldModel(ItemStack stack, LivingEntity entity, ItemDisplayContext displayContext) {
if (!cobblemon$isHandContext(displayContext)) {
return null;
}
if (stack.getItem() instanceof PokeBallItem pokeBallItem) {
return cobblemon$asStandaloneModel(pokeBallItem.getPokeBall().getModel3d());
}
if (stack.getItem() instanceof PokedexItem pokedexItem) {
boolean isScanModel = entity instanceof AbstractClientPlayer && entity.getUseItem() == stack && entity.isUsingItem();
boolean canOpenScreen = (entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof PokedexItem)) || entity.getMainHandItem() == stack;
return cobblemon$asStandaloneModel(pokedexItem.getType().getItemModelPath(isScanModel ? "scanning" : (canOpenScreen ? null : "off")));
}
return null;
}
@Unique
private static boolean cobblemon$isHandContext(ItemDisplayContext displayContext) {
return displayContext == ItemDisplayContext.FIRST_PERSON_LEFT_HAND
|| displayContext == ItemDisplayContext.FIRST_PERSON_RIGHT_HAND
|| displayContext == ItemDisplayContext.THIRD_PERSON_LEFT_HAND
|| displayContext == ItemDisplayContext.THIRD_PERSON_RIGHT_HAND;
}
@Unique
private static Identifier cobblemon$asStandaloneModel(Identifier modelId) {
return modelId.withPath(path -> path.startsWith("item/") ? path.substring("item/".length()) : path);
}
}
@@ -1,103 +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.Cobblemon;
import com.cobblemon.mod.common.ModAPI;
import com.cobblemon.mod.common.item.PokeBallItem;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.item.WearableItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/*
I think it would be nice to maybe use the existing mixin we have for custom item rendering [BuiltinModelItemRendererMixin]
Obviously that would require changes. Potentially each item renderer defines what modes it overrides for?
*/
@Mixin(ItemRenderer.class)
public abstract class ItemRendererMixin {
// ignore the hint saying implmentation is never null, if something crashes then it is null and causes red herrings
@Unique private final String MODEL_PATH = (Cobblemon.implementation != null && Cobblemon.implementation.getModAPI() == ModAPI.FABRIC) ? "fabric_resource" : "standalone";
@Shadow @Final private ItemModelShaper itemModelShaper;
@Shadow public abstract void render(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource vertexConsumers, int light, int overlay, BakedModel model);
@Inject(
method = "render(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;ZLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraft/client/resources/model/BakedModel;)V",
at = @At("HEAD"),
cancellable = true
)
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED;
ResourceLocation resourceLocation = null;
if (shouldBe2d) {
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel2d();
else if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
}
if (renderMode != ItemDisplayContext.HEAD && stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel2d();
if (resourceLocation != null) {
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, "inventory"));
if (!cobblemon$isSameModel(model, replacementModel)) {
ci.cancel();
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
}
}
}
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void cobblemon$getItemModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
ResourceLocation resourceLocation = null;
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel3d();
else if (stack.getItem() instanceof PokedexItem pokedexItem) {
boolean isScanModel = entity instanceof Player && entity.getUseItem() == stack && entity.isUsingItem();
boolean canOpenScreen = entity != null && ((entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof PokedexItem)) || entity.getMainHandItem() == stack);
resourceLocation = pokedexItem.getType().getItemModelPath(isScanModel ? "scanning" : (canOpenScreen ? null : "off"));
}
else if (stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel3d();
if (resourceLocation != null) {
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, MODEL_PATH));
ClientLevel clientWorld = world instanceof ClientLevel ? (ClientLevel) world : null;
BakedModel overriddenModel = model.getOverrides().resolve(model, stack, clientWorld, entity, seed);
cir.setReturnValue(overriddenModel == null ? this.itemModelShaper.getModelManager().getMissingModel() : overriddenModel);
}
}
// this method aims to cover an issue with modernfixes and it's "dynamic resource" causing the usual .equals check to always fail, resulting in a StackOverflow
// this falls back to comparing the model contents (e.g. location of the texture, width and height)
@Unique
private static boolean cobblemon$isSameModel(BakedModel original, BakedModel replacement) {
return original.equals(replacement) ||
(original.getParticleIcon().contents().name().equals(replacement.getParticleIcon().contents().name()) &&
original.getParticleIcon().contents().width() == replacement.getParticleIcon().contents().width() &&
original.getParticleIcon().contents().height() == replacement.getParticleIcon().contents().height());
}
}
@@ -0,0 +1,85 @@
/*
* 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.duck.LivingEntityRenderStateDuck;
import com.cobblemon.mod.common.entity.PoseType;
import net.minecraft.client.renderer.entity.state.LivingEntityRenderState;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(LivingEntityRenderState.class)
public class LivingEntityRenderStateMixin implements LivingEntityRenderStateDuck {
@Unique
private @Nullable LivingEntity cobblemon$mountedEntity;
@Unique
private float cobblemon$mountedPartialTick;
@Unique
private int cobblemon$mountedSeatIndex = -1;
@Unique
private @Nullable String cobblemon$mountedSeatLocator;
@Unique
private @Nullable PoseType cobblemon$mountedPoseType;
@Override
public @Nullable LivingEntity cobblemon$getMountedEntity() {
return this.cobblemon$mountedEntity;
}
@Override
public void cobblemon$setMountedEntity(@Nullable LivingEntity entity) {
this.cobblemon$mountedEntity = entity;
}
@Override
public float cobblemon$getMountedPartialTick() {
return this.cobblemon$mountedPartialTick;
}
@Override
public void cobblemon$setMountedPartialTick(float partialTick) {
this.cobblemon$mountedPartialTick = partialTick;
}
@Override
public int cobblemon$getMountedSeatIndex() {
return this.cobblemon$mountedSeatIndex;
}
@Override
public void cobblemon$setMountedSeatIndex(int seatIndex) {
this.cobblemon$mountedSeatIndex = seatIndex;
}
@Override
public @Nullable String cobblemon$getMountedSeatLocator() {
return this.cobblemon$mountedSeatLocator;
}
@Override
public void cobblemon$setMountedSeatLocator(@Nullable String seatLocator) {
this.cobblemon$mountedSeatLocator = seatLocator;
}
@Override
public @Nullable PoseType cobblemon$getMountedPoseType() {
return this.cobblemon$mountedPoseType;
}
@Override
public void cobblemon$setMountedPoseType(@Nullable PoseType poseType) {
this.cobblemon$mountedPoseType = poseType;
}
}
@@ -8,30 +8,123 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.cobblemon.mod.common.client.render.entity.MountedLivingEntityRenderer;
import com.cobblemon.mod.common.duck.LivingEntityRenderStateDuck;
import com.cobblemon.mod.common.entity.PoseType;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.item.WearableItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.state.AvatarRenderState;
import net.minecraft.client.renderer.entity.state.LivingEntityRenderState;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Final;
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(LivingEntityRenderer.class)
public class LivingEntityRendererMixin<T extends LivingEntity, M extends EntityModel<T>> {
public class LivingEntityRendererMixin {
@Shadow @Final protected ItemModelResolver itemModelResolver;
@Inject(
method = "extractRenderState",
at = @At("TAIL")
)
private void cobblemon$captureMountedEntity(LivingEntity entity, LivingEntityRenderState state, float partialTick, CallbackInfo ci) {
LivingEntityRenderStateDuck duck = (LivingEntityRenderStateDuck) state;
if (entity.isPassenger() && entity.getVehicle() instanceof PokemonEntity) {
PokemonEntity pokemonEntity = (PokemonEntity) entity.getVehicle();
int seatIndex = pokemonEntity.getPassengers().indexOf(entity);
String seatLocator = null;
PoseType poseType = pokemonEntity.getCurrentPoseType();
if (seatIndex >= 0 && seatIndex < pokemonEntity.getSeats().size()) {
seatLocator = pokemonEntity.getSeats().get(seatIndex).getLocator();
if (seatLocator == null) {
seatLocator = "seat_" + (seatIndex + 1);
}
} else {
seatIndex = -1;
}
duck.cobblemon$setMountedEntity(entity);
duck.cobblemon$setMountedPartialTick(partialTick);
duck.cobblemon$setMountedSeatIndex(seatIndex);
duck.cobblemon$setMountedSeatLocator(seatLocator);
duck.cobblemon$setMountedPoseType(poseType);
} else {
duck.cobblemon$setMountedEntity(null);
duck.cobblemon$setMountedPartialTick(0.0F);
duck.cobblemon$setMountedSeatIndex(-1);
duck.cobblemon$setMountedSeatLocator(null);
duck.cobblemon$setMountedPoseType(null);
}
ItemStack headItem = entity.getItemBySlot(EquipmentSlot.HEAD);
if (headItem.getItem() instanceof WearableItem wearableItem) {
ItemStack renderHeadItem = headItem.copy();
renderHeadItem.set(DataComponents.ITEM_MODEL, wearableItem.getModel3d().withPath(path -> path.startsWith("item/") ? path.substring("item/".length()) : path));
this.itemModelResolver.updateForLiving(state.headItem, renderHeadItem, ItemDisplayContext.HEAD, entity);
}
}
@Inject(
method = "setupRotations",
at = @At("HEAD")
)
private void cobblemon$modifyRoll(LivingEntity entity, PoseStack poseStack, float bob, float yBodyRot, float partialTick, float scale, CallbackInfo ci) {
if (entity.isPassenger()) {
var vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
MountedLivingEntityRenderer.INSTANCE.render(entity, pokemonEntity, poseStack, bob, yBodyRot, partialTick, scale);
}
private void cobblemon$modifyRoll(LivingEntityRenderState state, PoseStack poseStack, float bodyRot, float scale, CallbackInfo ci) {
LivingEntityRenderStateDuck duck = (LivingEntityRenderStateDuck) state;
LivingEntity entity = duck.cobblemon$getMountedEntity();
if (entity == null || !(entity.getVehicle() instanceof PokemonEntity pokemonEntity)) {
return;
}
MountedLivingEntityRenderer.INSTANCE.render(
entity,
pokemonEntity,
poseStack,
state.walkAnimationPos,
bodyRot,
duck.cobblemon$getMountedPartialTick(),
scale
);
}
@Inject(
method = "submit",
at = @At("TAIL")
)
private void cobblemon$renderPlayerIcon(
LivingEntityRenderState state,
PoseStack poseStack,
SubmitNodeCollector submitNodeCollector,
CameraRenderState cameraRenderState,
CallbackInfo ci
) {
if (!(state instanceof AvatarRenderState avatarState)) {
return;
}
var level = Minecraft.getInstance().level;
if (level == null) {
return;
}
var entity = level.getEntity(avatarState.id);
if (entity instanceof Player player) {
ClientPlayerIcon.Companion.onRenderPlayer(player);
}
}
}
@@ -29,7 +29,7 @@ import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.Input;
import net.minecraft.client.player.ClientInput;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
@@ -64,7 +64,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
@Shadow
private float jumpRidingScale;
@Shadow public Input input;
@Shadow public ClientInput input;
@Shadow private int autoJumpTime;
@@ -88,6 +88,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
var driver = (LocalPlayer)(Object)this;
var vehicle = driver.getVehicle();
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
@@ -120,7 +121,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
Vector3f lastSentDriverInput = ((PlayerDuck)this).getLastSentDriverInput();
if (driverInput.equals(lastSentDriverInput)) return;
((PlayerDuck)this).setLastSentDriverInput(driverInput);
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
}
@@ -142,7 +142,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
cobblemon$lastRideYRot != rideYRot ||
cobblemon$lastRideEyePos != rideEyePos
) {
// Update 'last' values
cobblemon$lastRideXRot = rideXRot;
cobblemon$lastRideYRot = rideYRot;
@@ -188,22 +187,26 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
@Inject(method = "aiStep", at = @At("HEAD"))
private void cobblemon$aiStep(CallbackInfo ci) {
boolean jumpPressed = this.input.keyPresses.jump();
this.cobblemon$isDoubleJumping = false;
this.cobblemon$isJumping = this.input.jumping;
this.cobblemon$isJumping = jumpPressed;
}
@Inject(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSprinting()Z", shift = At.Shift.BEFORE))
private void cobblemon$updateDoubleJumping(CallbackInfo ci) {
if (!this.cobblemon$isJumping && this.input.jumping && this.autoJumpTime <= 0 && !this.isSwimming()) {
if (!this.cobblemon$isJumping && this.input.keyPresses.jump() && this.autoJumpTime <= 0 && !this.isSwimming()) {
PokemonEntity pokemonEntity = this.getVehicle() instanceof PokemonEntity vehiclePokemon ? vehiclePokemon : null;
if (this.cobblemon$survivalJumpTriggerTime != 0) {
this.cobblemon$isDoubleJumping = true;
this.cobblemon$survivalJumpTriggerTime = 0;
}
else {
} else {
this.cobblemon$survivalJumpTriggerTime = 12;
if (pokemonEntity != null) {
this.cobblemon$isDoubleJumping = true;
this.cobblemon$survivalJumpTriggerTime = 0;
}
}
}
else if (this.cobblemon$survivalJumpTriggerTime > 0) {
} else if (this.cobblemon$survivalJumpTriggerTime > 0) {
this.cobblemon$survivalJumpTriggerTime--;
}
}
@@ -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.mixin.client;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import java.util.List;
import java.util.Map;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.profiling.ProfilerFiller;
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(ModelBakery.class)
public abstract class ModelLoaderMixin {
@Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation);
@Shadow protected abstract void registerModel(ModelResourceLocation modelId, UnbakedModel unbakedModel);
@Inject(
method = "<init>",
at = @At("TAIL")
)
public void init(BlockColors blockColors,
ProfilerFiller profiler,
Map<ResourceLocation, BlockModel> jsonUnbakedModels,
Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>> blockStates,
CallbackInfo ci) {
CobblemonBakingOverrides.INSTANCE.getModels().forEach(bakingOverride -> {
var unbakedModel = this.getModel(bakingOverride.getModelLocation());
this.registerModel(bakingOverride.getModelIdentifier(), unbakedModel);
});
}
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.AgeableListModel;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.Model;
import org.spongepowered.asm.mixin.Mixin;
@@ -19,18 +18,13 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This mixin adds to the head of the renderToBuffer function and allows us to detect if it is a HumanoidModel and
* is being rendered as part of a player in the world who is riding a PokemonEntity. If so we need to obey any
* root animations that have been applied earlier.
*
* @author Hiroku
* @since May 16, 2025
*/
@Mixin(AgeableListModel.class)
public class AgeableListModelMixin {
@Inject(method = "renderToBuffer", at = @At("HEAD"))
private void cobblemon$renderToBuffer(
@Mixin(Model.class)
public abstract class ModelMixin {
@Inject(
method = "renderToBuffer(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;III)V",
at = @At("HEAD")
)
private void cobblemon$applyMountedRootAnimation(
PoseStack poseStack,
VertexConsumer vertexConsumer,
int packedLight,
@@ -38,8 +32,7 @@ public class AgeableListModelMixin {
int colour,
CallbackInfo ci
) {
Model model = (Model) (Object) this;
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
if ((Object) this instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
}
}
@@ -11,12 +11,9 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.client.keybind.keybinds.RidingFreelookBinding;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.Blaze3D;
@@ -27,7 +24,6 @@ import net.minecraft.client.player.LocalPlayer;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.phys.Vec3;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -35,14 +31,12 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts;
import static com.google.common.primitives.Floats.min;
import static net.minecraft.util.Mth.lerp;
@Mixin(MouseHandler.class)
public abstract class MouseHandlerMixin {
@Shadow private double accumulatedScrollY;
@Shadow @Final private Minecraft minecraft;
@Unique SmoothDouble xMouseSmoother = new SmoothDouble();
@@ -67,52 +61,6 @@ public abstract class MouseHandlerMixin {
@Unique private double cobblemon$timeDelta;
@Inject(
method = "onScroll",
at = @At(
value = "FIELD",
target="Lnet/minecraft/client/MouseHandler;accumulatedScrollY:D",
opcode = Opcodes.PUTFIELD,
ordinal = 2,
shift = At.Shift.BEFORE
),
cancellable = true
)
public void cobblemon$scrollParty(long window, double horizontal, double vertical, CallbackInfo ci) {
if (PartySendBinding.INSTANCE.getWasDown()) {
int i = (int) accumulatedScrollY;
if (i > 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(false);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
PartyTutorialToasts.INSTANCE.onHoldKeyScrollSwitchedSlot();
} else if (i < 0) {
accumulatedScrollY -= i;
CobblemonClient.INSTANCE.getStorage().shiftSelected(true);
ci.cancel();
PartySendBinding.INSTANCE.actioned();
PartyTutorialToasts.INSTANCE.onHoldKeyScrollSwitchedSlot();
}
}
}
@Inject(
method = "onScroll",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/player/Inventory;swapPaint(D)V"
),
cancellable = true
)
public void cobblemon$doPokedexZoom(long window, double horizontal, double vertical, CallbackInfo ci) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
usageContext.adjustZoom(vertical);
ci.cancel();
}
}
@WrapWithCondition(
method = "turnPlayer",
at = @At(
@@ -127,28 +75,10 @@ public abstract class MouseHandlerMixin {
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
boolean returnValue = true;
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
this.smoothTurnX.reset();
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6 + 0.2;
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
var lookSensitivity = spyglassSensitivity * 8.0;
var sensitivity = lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
returnValue = false;
}
var vehicle = player.getVehicle();
if (!(vehicle instanceof PokemonEntity)) return returnValue;
// Clamp player rotation if riding and the vehicle demands it
if (player.isPassenger() && vehicle instanceof PokemonEntity pokeVehicle) {
pokeVehicle.clampPassengerRotation(player);
}
// If the ride is not orientation controllable then return and perform the normal turnPlayer logic
if (!(vehicle instanceof OrientationControllable controllableVehicle) || !controllableVehicle.getOrientationController().isActive()) {
xMouseSmoother.reset();
@@ -1,54 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
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;
@Mixin(PlayerRenderer.class)
public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
public PlayerRendererMixin(EntityRendererProvider.Context context, PlayerModel<AbstractClientPlayer> model, float shadowRadius) {
super(context, model, shadowRadius);
}
@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);
}
@WrapOperation(method = "getRenderOffset(Lnet/minecraft/client/player/AbstractClientPlayer;F)Lnet/minecraft/world/phys/Vec3;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;isCrouching()Z"))
private boolean cobblemon$disableCrouchOffset(AbstractClientPlayer instance, Operation<Boolean> original) {
if (instance.isPassenger() && instance.getVehicle() instanceof PokemonEntity) {
return false;
}
return original.call(instance);
}
@Inject(method = "setModelProperties", at = @At("TAIL"))
private void cobblemon$setModelProperties(AbstractClientPlayer player, CallbackInfo ci) {
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PlayerModel<AbstractClientPlayer> playerModel = this.getModel();
playerModel.crouching = false;
}
}
}
@@ -1,63 +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.CobblemonRecipeCategories;
import net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.Minecraft;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
import net.minecraft.client.gui.screens.recipebook.RecipeBookPage;
import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton;
import org.spongepowered.asm.mixin.Final;
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(RecipeBookComponent.class)
public class RecipeBookComponentMixin {
@Shadow private ClientRecipeBook book;
@Shadow protected Minecraft minecraft;
@Shadow private int width;
@Shadow private int xOffset;
@Shadow private int height;
@Shadow @Final private RecipeBookPage recipeBookPage;
@Inject(method = "updateTabs", at = @At("HEAD"), cancellable = true)
private void customUpdateTabs(CallbackInfo ci) {
RecipeBookComponent instance = (RecipeBookComponent) (Object) this;
int i = (width - 147) / 2 - xOffset - 30;
int j = (height - 166) / 2 + 3;
int k = 27;
int l = 0;
for (RecipeBookTabButton recipeBookTabButton : instance.tabButtons) {
RecipeBookCategories recipeBookCategories = recipeBookTabButton.getCategory();
if (recipeBookCategories != RecipeBookCategories.CRAFTING_SEARCH && recipeBookCategories != RecipeBookCategories.FURNACE_SEARCH && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory()) {
if (recipeBookTabButton.updateVisibility(book)) {
recipeBookTabButton.setPosition(i, j + 27 * l++);
recipeBookTabButton.startAnimation(minecraft);
}
} else {
recipeBookTabButton.visible = true;
recipeBookTabButton.setPosition(i, j + k * l++);
}
}
ci.cancel();
}
}
@@ -1,40 +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 net.minecraft.client.ClientRecipeBook;
import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton;
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
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.CallbackInfoReturnable;
import java.util.List;
@Mixin(RecipeBookTabButton.class)
public class RecipeBookTabButtonMixin {
@Inject(method = "updateVisibility", at = @At("HEAD"), cancellable = true)
private void customUpdateVisibility(ClientRecipeBook recipeBook, CallbackInfoReturnable<Boolean> cir) {
RecipeBookTabButton instance = (RecipeBookTabButton) (Object) this;
List<RecipeCollection> list = recipeBook.getCollection(instance.getCategory());
boolean visible = false;
if (list != null) {
for (RecipeCollection recipeCollection : list) {
if (recipeCollection.hasKnownRecipes() && recipeCollection.hasFitting()) {
visible = true;
break;
}
}
}
instance.visible = visible;
cir.setReturnValue(visible);
}
}
@@ -1,53 +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 net.minecraft.client.gui.screens.recipebook.RecipeCollection;
import net.minecraft.stats.RecipeBook;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.crafting.RecipeHolder;
import org.spongepowered.asm.mixin.Final;
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 java.util.List;
import java.util.Set;
@Mixin(RecipeCollection.class)
public class RecipeCollectionMixin {
@Shadow @Final private Set<RecipeHolder<?>> fitsDimensions;
@Shadow @Final private Set<RecipeHolder<?>> craftable;
@Shadow @Final private List<RecipeHolder<?>> recipes;
@Inject(method = "canCraft", at = @At("HEAD"), cancellable = true)
private void customCanCraft(StackedContents handler, int width, int height, RecipeBook book, CallbackInfo ci) {
for (RecipeHolder<?> recipeHolder : recipes) {
boolean bl = recipeHolder.value().canCraftInDimensions(width, height) && book.contains(recipeHolder);
if (bl) {
fitsDimensions.add(recipeHolder);
} else {
fitsDimensions.remove(recipeHolder);
}
if (bl && handler.canCraft(recipeHolder.value(), null)) {
craftable.add(recipeHolder);
} else {
craftable.remove(recipeHolder);
}
}
ci.cancel();
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.TextClipping;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Style;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -35,12 +34,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
* @author Hiroku
* @since April 18th, 2025
*/
@Mixin(Font.StringRenderOutput.class)
@Mixin(targets = "net.minecraft.client.gui.Font$PreparedTextBuilder")
public class StringRenderOutputMixin {
@Inject(method = "accept", at = @At(value = "HEAD"), cancellable = true)
public void onAccept(int i, Style style, int j, CallbackInfoReturnable<Boolean> cir) {
if (!TextClipping.canDrawAnotherCharacter()) {
cir.setReturnValue(true);
cir.setReturnValue(false);
}
}
}
@@ -6,18 +6,15 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
package com.cobblemon.mod.common.mixin.client.accessor;
import net.minecraft.world.entity.vehicle.Boat;
import com.mojang.blaze3d.systems.ScissorState;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Boat.class)
public interface BoatEntityAccessor {
@Accessor("lastYd")
void setFallVelocity(double fallVelocity);
@Accessor("status")
Boat.Status getLocation();
@Mixin(GuiGraphicsExtractor.class)
public interface GuiGraphicsExtractorAccessor {
@Accessor
public GuiGraphicsExtractor.ScissorStack getScissorStack();
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client.accessor;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.renderer.entity.state.HumanoidRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(HumanoidModel.class)
public interface HumanoidModelAccessor {
@Invoker("poseRightArm")
void cobblemon$invokePoseRightArm(HumanoidRenderState renderState);
@Invoker("poseLeftArm")
void cobblemon$invokePoseLeftArm(HumanoidRenderState renderState);
@Invoker("setupAttackAnimation")
void cobblemon$invokeSetupAttackAnimation(HumanoidRenderState renderState);
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client.accessor;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(LivingEntityRenderer.class)
public interface LivingEntityRendererAccessor<S extends EntityRenderState, M extends EntityModel<? super S>> {
@Accessor("layers")
List<RenderLayer<S, M>> cobblemon$getLayers();
}
@@ -19,7 +19,7 @@ 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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.function.Function;
@@ -33,7 +33,7 @@ public final class SnapshotWarningMixin {
private final Pattern VERSION = Pattern.compile("^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)$");
@Inject(method = "addInitialScreens", at = @At("TAIL"))
public void cobblemon$addSnapshotWarningScreen(List<Function<Runnable, Screen>> output, CallbackInfo callback) {
public void cobblemon$addSnapshotWarningScreen(List<Function<Runnable, Screen>> output, CallbackInfoReturnable<Boolean> callback) {
if (CobblemonBuildDetails.SNAPSHOT && !this.cobblemon$isAcknowledged() && !SharedConstants.IS_RUNNING_IN_IDE) {
output.add((runnable) -> new SnapshotWarningScreen((acknowledgement, dsa) -> {
if (acknowledgement == SnapshotWarningScreen.Acknowledgement.NO) {
@@ -82,7 +82,7 @@ public abstract class EntityMixin {
@Unique
private void cobblemon$getCustomEyePos(CallbackInfoReturnable<Vec3> cir) {
var entity = (Entity)(Object)this;
if (entity.level().isClientSide) return;
if (entity.level().isClientSide()) return;
if (!(entity instanceof Player player)) return;
if (!(player instanceof RidePassenger ridePassenger)) return;
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
@@ -9,18 +9,24 @@
package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.TamableAnimal;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.gamerules.GameRule;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TamableAnimal.class)
public class TameableEntityMixin {
@Redirect(method = "die", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/Level;isClientSide:Z"))
public boolean cobblemon$checkIfPokemonBeforeSendingMessage(Level world) {
return world.isClientSide || (this.getClass().isAssignableFrom(PokemonEntity.class));
@Inject(method = "die", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/TamableAnimal;getOwner()Lnet/minecraft/world/entity/LivingEntity;"), cancellable = true)
public void cobblemon$checkIfPokemonBeforeSendingMessage(DamageSource damageSource, CallbackInfo ci, @Local(name = "var4") LivingEntity owner) {
if(((TamableAnimal)(Object)this).level().isClientSide() || (this.getClass().isAssignableFrom(PokemonEntity.class))) {
ci.cancel();
}
}
}
@@ -11,33 +11,33 @@ package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.properties.BlockSetType;
import net.minecraft.world.level.material.MapColor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@SuppressWarnings("unused")
@Mixin(Blocks.class)
public interface BlocksInvoker {
@Invoker("log")
static Block createLogBlock(MapColor topMapColor, MapColor sideMapColor) {
@Invoker
static BlockBehaviour.Properties callLogProperties(MapColor mapColor, MapColor mapColor2, SoundType soundType) {
throw new UnsupportedOperationException();
}
@Invoker("leaves")
static Block createLeavesBlock(SoundType soundGroup) {
@Invoker
static BlockBehaviour.Properties callLeavesProperties(SoundType soundType) {
throw new UnsupportedOperationException();
}
@Invoker("woodenButton")
static Block createWoodenButtonBlock(BlockSetType blockSetType) {
@Invoker
static BlockBehaviour.Properties callButtonProperties() {
throw new UnsupportedOperationException();
}
@Invoker("flowerPot")
static Block createFlowerPotBlock(Block flower) {
@Invoker
static BlockBehaviour.Properties callFlowerPotProperties() {
throw new UnsupportedOperationException();
}
}
}
@@ -1,21 +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.invoker;
import net.minecraft.world.level.GameRules;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(GameRules.BooleanValue.class)
public interface BooleanRuleInvoker {
@Invoker("create")
static GameRules.Type<GameRules.BooleanValue> cobblemon$create(boolean initialValue) {
throw new UnsupportedOperationException();
}
}
@@ -20,4 +20,4 @@ public interface DoorBlockInvoker {
static DoorBlock cobblemon$create(BlockSetType blockSetType, BlockBehaviour.Properties settings) {
throw new UnsupportedOperationException();
}
}
}
@@ -20,4 +20,4 @@ public interface PressurePlateBlockInvoker {
static PressurePlateBlock cobblemon$create(BlockSetType type, BlockBehaviour.Properties settings) {
throw new UnsupportedOperationException();
}
}
}

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