Compare commits

..
Author SHA1 Message Date
Landon Wills c921496b50 some shader stuff 2025-08-31 11:37:55 -04:00
Landon Wills a9d1000384 testing 2025-08-28 01:11:03 -04:00
Landon Wills 225721aec4 Add tropius 2025-08-26 16:58:05 -04:00
Landon Wills ae9475959d Add sound scraping 2025-08-26 16:57:52 -04:00
Landon Wills 0ecb4d32cf Add new riding mounts 2025-08-20 13:03:49 -04:00
Landon Wills 52a9e30dc1 Merge branch 'implement_pokemon' into riding-scraper 2025-08-20 12:59:43 -04:00
Landon Wills 1198bad42e Add readme 2025-08-20 10:50:44 -04:00
Landon Wills aa18d9f90e Scrape remaining files 2025-08-20 10:30:33 -04:00
Landon Wills 3833bcf7c1 Add script dependency file 2025-08-20 09:46:54 -04:00
Landon Wills d85dc372a7 Scrape from riding sheet 2025-08-20 09:44:35 -04:00
4147 changed files with 88203 additions and 585689 deletions
+1 -2
View File
@@ -12,8 +12,7 @@ out
*.ipr
*.iws
*.iml
.idea/*
!.idea/icon.png
.idea
# gradle
build
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

-4
View File
@@ -6,7 +6,6 @@
- Cat Pokémon (Meowth, Persian, etc.) intimidate Creepers and Phantoms.
- Sleep-related Pokémon (Munsharna, Komala, etc.) intimidate Phantoms.
- Some Pokémon naturally hunt each other.
- Some Pokémon herd together.
- Added a Behaviour Editor to the NPC editing screen.
- Added /behaviouredit command for opening the Behaviour Editor on Pokémon and NPCs.
- Editing variables for Pokémon is not yet supported.
@@ -15,9 +14,6 @@
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
### Fixes
- Fixed Pokémon with a moving walk target frequently spinning in place as they re-calculate their path.
### MoLang & Datapacks
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
+1 -2
View File
@@ -1,7 +1,6 @@
### Additions
- Added functionality for marks.
- Added data for all marks and ribbons from the mainline Pokémon games.
- Implemented fishing, personality, weather, and time related marks, that can rarely be granted to a wild-spawned Pokémon.
- Added data for all marks and all title ribbons from Pokémon.
- Added marks screen within the summary UI.
- Added `/givemark <player> <partySlot> <mark>` command to give a mark to a Pokémon.
- Added `/takemark <player> <partySlot> <mark>` command to remove a mark from a Pokémon.
+2 -11
View File
@@ -1,28 +1,19 @@
### Additions
- Added ability to rename PC Boxes by clicking on the name of a box.
- Added box options buttons for boxes, toggleable by clicking the right icon button in the bottom bar.
- The option buttons on the left side allow for sorting the box by name, level, Pokédex number, gender, and type. Shift clicking allows for sorting in reverse order.
- The button on the right allows for changing the box wallpaper.
- 10 additional color-based wallpapers have been added.
- 5 unlockable biome-based wallpapers have been added, which can be unlocked by visiting:
- Forest biome
- Ocean biome
- Cave Biome
- The End
- The Nether
- The option buttons on the left side allow for sorting the box by name, level, Pokédex number, gender, and type. Shift clicking allows for sorting in reverse order.
- Added filter functionality in PC UI, which supports `PokemonProperties` (e.g. `shiny=yes` shows all shiny Pokémon).
- Names can be filtered by exact or partial matches, e.g. entering "cha" will show Charmander, Charmeleon, etc.
- Added `/changewallpaper <player> <boxNumber> <wallpaper>` command to change a box wallpaper through commands.
- Added `/renamebox <player> <boxNumber> <name>` command to rename a PC box through commands.
- Added IVs and EVs stat displays in PC.
- The displays can be cycled through by scrolling when the display is hovered over.
- Added ability to cycle through boxes by scrolling when the box is hovered over.
- Added ability to change boxes by scrolling.
- PC will now open to last box viewed within a session.
### Changes
- Slots in pastures will now have darker backgrounds and italicised Pokémon names if the Pokémon is not owned by the player.
- The pasture slot will show the name of the Pokémon owner when hovered if the Pokémon is not owned.
- Increased the default PC box count from 30 to 40 to accommodate the fact that we have way more Pokémon now. If you've changed this config option then you'll need to edit it again after starting up with 1.7!
### Developer
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
+1 -10
View File
@@ -4,7 +4,6 @@
### Fixes
- Fixed the "enabled" property in spawn files not actually being respected. Where do they find these devs?
- Fixed Terralith's shrubland not counting as plains for spawning purposes the way it was intended to.
### Developer
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
@@ -12,14 +11,6 @@
- SpawningContext is now SpawnablePosition
- WorldSlice is SpawningZone
- SpawningProspector is now SpawningZoneGenerator
- Majorly refactored the hierarchy of Spawner
- The base Spawner interface provides more functions to allow single-point and area spawning given appropriate inputs.
- TickingSpawner is removed in favour of outside code handling ticking logic.
- AreaSpawner is removed.
- BasicSpawner is the first implementation of Spawner which can be used for any purpose.
- PlayerSpawners are now mixin'd into ServerPlayer and ticked from ServerPlayer#tick.
- A hierarchy diagram can be found at `./docs/spawner-hierarchy.png` in the mod repository.
- The SpawnerManager class has been removed as its functionality is all now handled elsewhere.
- Renamed things in Spawn Rules to go with the other renames:
- contextSelector is now spawnablePositionSelector
- contextSelector is now spawnablePositionSelector
- context is now spawnable_position
+6 -351
View File
@@ -2,7 +2,6 @@
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added `/npcdelete` uuid parameter support and usuable to console
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
@@ -12,11 +11,11 @@
- Pokémon's held items can now be rendered, with a visibility toggle in the Summary screen.
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added cosmetics for Spoink, Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
- Added Gilded Chest cosmetics for Chest Gimmighoul and Gholdengo
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
- Added Pokémon markings, toggleable within the summary.
- Added `/boxcount` command to change PC boxes amount
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
- Added cosmetics for Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandle line, Treecko line, Braixen, Delphox, and Dragonite.
- Added `/transformmodelpart (position|rotation|scale) <modelPart> <transform: x y z>` command that can add transformations to a pokemon's model part.
- The player executing the command must be facing the target pokemon entity. Transformations are not persistent and will revert when resources are reloaded.
- Added `legacy` and `special` sourced moves to Pokémon.
@@ -24,7 +23,7 @@
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon, evolution stone blocks, evolution stone items, Pokédex, Luminous Moss, Flame Orb, and Magmarizer.
- Added the Clear Amulet, Grip Claw, Lagging Tail, Luminous Moss, Metal Alloy, Scroll of Darkness, Scroll of Waters
- Added Recipes for Masterpiece Cup, Eject Pack.
- Added Recipes for Masterpiece Cup, Eject Pack
- Added modification to Minecraft Creative Inventory search to account for item names that contain `poké` when input contains `poke`.
- Added Campfire Pot as well as loads of new food items (Poke Puffs, Ponigiri, Sinister Tea, etc)
- Added Hearty Grains, a new crop used in the new cooking mechanic
@@ -34,14 +33,12 @@
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
- Added Galarica Nut Bushes
- Many Pokémon (mostly cats) are now feared by phantoms
- Combees are now capable of gathering nectar from flowers and delivering it to either Saccharine leaves or hives. (Hive interactions are restricted to wild Combees)
- Lightning is now affected by a Pokemon's ability/typing
- Pokémon with the ability Lightning Rod draw in lightning similar to a lightning rod block albeit with a lower priority and range, gain immunity to lightning damage, and receive a temporary damage buff.
- Pokémon with the ability Motor Drive are immune to lightning damage and receive a temporary speed buff when struck by lightning
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
- Ground type Pokémon are immune to lightning damage
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
- Added optional `filePath` parameter to MoLang functions to allow for global stores (ex. `../global/`). Path must end in a forward slash.
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
- Galarica Nut bushes now generate on beaches.
- Some Pokémon now pitch their bodies in the direction they're moving.
@@ -49,11 +46,6 @@
- Berries can now be smelted into their respective dyes.
- Added Syrupy Apples.
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
- Added bubble quirk to Kingler; like Krabby, Kingler will blows bubbles during dusk.
- Added `.Pre` and `.Post` to the following events:
- `PokemonRecallEvent`
- `TradeEvent`
- Added a new gamerule, 'healerHealsPC', when set to true a successful use of a healer will also heal all of the Pokemon in that player's PC.
### Pokémon Added
@@ -64,14 +56,6 @@
- Skiploom
- Jumpluff
- Dunsparce
- Togepi
- Togetic
- Unown
- Houndour
- Houndoom
- Ho-Oh
- Lugia
- Alola Bias Pichu (built-in resourcepack)
#### Gen 3
- Azurill
@@ -98,7 +82,6 @@
- Purugly
- Skorupi
- Drapion
- Togekiss
#### Gen 5
- Pansage
@@ -124,15 +107,11 @@
- Tynamo
- Eelektrik
- Eelektross
- Axew
- Fraxure
- Haxorus
- Mienfoo
- Mienshao
- Sewaddle
- Swadloon
- Leavanny
- Druddigon
#### Gen 6
- Skiddo
@@ -145,8 +124,6 @@
- Dedenne
- Noibat
- Noivern
- Furfrou
- You can change Furfrou's form by using shears on it while it holds a certain dye in its cosmetic slot.
#### Gen 7
- Yungoos
@@ -159,8 +136,6 @@
#### Gen 8
- Skwovet
- Greedent
- Chewtle
- Drednaw
- Toxel
- Toxtricity
- Morpeko
@@ -170,11 +145,6 @@
- Polteageist
- Galarian Corsola
- Cursola
- Mr. Rime
- Galarian Mr. Mime
- Clobbopus
- Grapploct
- Galarian Weezing
#### Gen 9
- Smoliv
@@ -193,18 +163,13 @@
- Bellibolt
- Toedscool
- Toedscruel
- Rellor
- Rabsca
- Bramblin
- Brambleghast
### Animation updates for the following Pokémon
- Garchomp
- Tropius
- Torpius
- Nosepass
- Probopass
- Sneasel
- Weavile
- Sneasler
- Braixen
- Delphox
@@ -226,80 +191,8 @@
- Porygon
- Porygon2
- Porygon-Z
- Wattrel
- Kilowattrel
- Golurk
- Spinarak
- Ariados
- Wyrdeer
- Tyrantrum
- Pidove
- Tranquill
- Unfezant
- Beldum
- Metang
- Metagross
- Plusle
- Minun
- Murkrow
- Honchkrow
- Larvesta
- Volcarona
- Dwebble
- Crustle
- Mr. Mime
- Flygon
- Pichu
- Pikachu
- Raichu
- Alolan Raichu
- Espathra
- Abra
- Kadabra
- Alakazam
- Deino
- Zweilous
- Hydreigon
- Slaking
- Klink
- Klang
- Klinklang
- Baltoy
- Claydol
- Mamoswine
- Rufflet
- Braviary
- Girafarig
- Farigiraf
- Rookidee
- Corvisquire
- Corviknight
- Venipede
- Whirlipede
- Scolipede
- Venusaur
- Parasect
- Bastiodon
- Amaura
- Varoom
- Revavroom
- Bouffalant
- Lickilicky
- Blaziken
- Rhyhorn
- Rhydon
- Rhyperior
- Tauros
- Tauros - Paldea Aqua
- Tauros - Paldea Blaze
- Tauros - Paldea Combat
- Dewgong
- Exeggutor - Kanto
### Model updates for the following Pokémon
- Cleffa
- Clefairy
- Clefable
- Gyarados
- Dragonite
- Eevee
@@ -360,54 +253,6 @@
- Klink
- Klank
- Klinklang
- Spinarak
- Ariados
- Pidove
- Tranquill
- Unfezant
- Sobble
- Drizzile
- Inteleon
- Plusle
- Minun
- Murkrow
- Zorua
- Zoroark
- Mime.Jr
- Mr. Mime
- Pichu
- Pikachu
- Raichu
- Alolan Raichu
- Dusknoir
- Deino
- Zweilous
- Hydreigon
- Dreepy
- Drakloak
- Dragapult
- Mamoswine
- Rookidee
- Corvisquire
- Corviknight
- Venipede
- Whirlipede
- Scolipede
- Farigiraf
- Staryu
- Starmie
- Gimmighoul
- Gholdengo
- Starly
- Staravia
- Staraptor
- Varoom
- Revavroom
- Nickit
- Thievul
- Litwick
- Lampent
- Chandelure
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
@@ -421,8 +266,6 @@
- Updated some item tags to better integrate behaviours between Cobblemon, Vanilla Minecraft, and other mods
- Removed Cooked Meat, Raw Meat, Protein Ingredients, and Zinc Ingredients, the first two are now using `c` namespace tags, the latter have better integrated use of tags within their recipes which removes need for custom tags.
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
- Updated the `#cobblemon:berries` tag to use subtags located in `data/cobblemon/tags/item/berries`, such as `#cobblemon:berries/hp_recovery` for sorting purposes
- Moved the tag `#cobblemon:mutated_berries` to `#cobblemon:berries/mutation_result`
- Added herbs and snowballs to the consumable in PvE and Wild battle tags
- Tweaked the Natural Materials Vanilla file to fit with the changes to tags
- Substantially optimised spawning checks mainly by front-loading biome filtering.
@@ -463,19 +306,9 @@
- Updated Pokémon state icons shown in party interfaces
- MoLang `face` function can now be run on any `PosableEntity` (Like Pokémon!) instead of just NPCs.
- MoLang `run_action_effect` now works on Pokémon.
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
- Added entity (as `q.entity`) that executed the command to the `executemolangscript` command.
- Refactored the following events to `.Pre` and `.Post` for consistency:
- `PokemonSentEvent`
- `ExperienceGainedEvent`
- `BattleStartedEvent`
- Updated `PokemonSentEvent` parameters to include the Position and Level of the Pokémon being sent out.
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
- Roseli Berry Trees now naturally generate in their preferred biomes.
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
- Shulker Boxes and Traveler's Backpacks can no longer be held by Pokémon. Thanks Monocle ;) You could re-enable this with a datapack but you'd be crazy.
- Moomoo Milk now clears Pokémon stat changes when used in battle.
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -549,19 +382,6 @@
- Fixed mod incompatibility with the `Raised` mod
- Fixed a vulnerability that could cause party and PC rollbacks under specific circumstances.
- Fixed a rare edge case where sorting your PC could be rolled back later.
- Fixed BotanyPots built-in integration
- Fixed shading of berries on berry trees.
- Fixed static text cursor for nickname input in summary.
- Fixed Cobblemon brewing recipes placing result into all slots, and not just slots with correct ingredients inside the brewing stand.
- Fixed an issue where hoppers and brewing stands were not recognizing Cobblemon brewing recipes.
- Fixed global species features... not working. Since they were created. Oops.
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
- Fixed species additions not being able to properly mark a species as implemented.
- Fixed Pokémon item models not showing a glint when enchanted.
- Fixed some specific bag items not being dropped when used in battle.
- Fixed the "use all berry bait" achievement not being progressed
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -575,6 +395,7 @@
- Reworked observable handling in `Pokemon.kt` to cut down on RAM usage and clarify the file.
- Note: This will break mods that used our observable functionality there or in MoveSet, IVs, EVs, or BenchedMoves.
- Using `Pokemon#onChange()` is now the way to mark a Pokémon as needing a save.
- Using `[Pokemon].changeObservable` is now the way to get an `Observable` for any save-worthy changes.
- Updated NPCEntity beam positioning to properly account for the baseScale property.
- Updated NPCEntity pokeball throw positioning to properly account for the baseScale property.
- Fixed `[Pokemon].copyFrom` error causing forms, IVs, and EVs to not be applied properly when using `[Pokemon].loadFromJSON` or `[Pokemon].loadFromNBT`
@@ -653,171 +474,6 @@
- Added `q.has_argument_at(<index>, [argument_value])` MoLang function to several battle-related action effect contexts.
- Added `q.hit_count` MoLang function to move action effect contexts.
- Added `is_included`, `to_lower`, `to_upper`, and `string_length` as available Molang functions.
- Fixed a crash that would occur during battles if the opponent wild Pokémon species comes from a namespace other than cobblemon
- Fixed `clientActions` inside Dialogue pages being executed twice
- Added `q.split_string(<text>, <delimiter/comma>)` Outputs an array with the divided text.
- Added `q.spawn_npc(<x>, <y>, <z>, <npc>)` Spawns an NPC at the given coordinates and returns the NPC.
- Added `q.player.seen_credits` Returns whether the player has seen the end credits.
- Added `q.player.is_in_dialogue` Returns whether the player is currently in a dialogue.
- Added `q.player.active_dialogue` Returns the active dialogue of the player, or null if none.
- Added `q.player.is_spectator` Returns whether the player is in spectator mode.
- Added `q.player.is_adventure` Returns whether the player is in adventure mode.
- Added `q.player.is_creative` Returns whether the player is in creative mode.
- Added `q.player.is_survival` Returns whether the player is in survival mode.
- Added `q.player.set_battle_theme(<sound resource>)` Sets the player's battle theme to the given theme.
- Added `q.player.battle_music(<sound_resource>, <volume>, <pitch>)` Plays the given battle music for the player.
- Added `q.player.stop_battle_music()` Stops any currently playing battle music for the player. (With a fade out).
- Added `q.player.riding_pokemon` Returns the Pokémon the player is currently riding.
- Added `q.entity.is_player` Returns whether the entity is a player.
- Added `q.entity.is_pokemon` Returns whether the entity is a Pokémon.
- Added `q.entity.is_npc` Returns whether the entity is an NPC.
- Added `q.entity.is_mob` Returns whether the entity is a vanilla mob.
- Added `q.entity.is_animal` Returns whether the entity is an animal.
- Added `q.entity.is_hostile` Returns whether the entity is a hostile mob.
- Added `q.entity.is_baby` Returns whether the entity is a baby.
- Added `q.entity.is_adult` Returns whether the entity is an adult.
- Added `q.entity.is_tamable` Returns whether the entity is tamable.
- Added `q.entity.is_tamed` Returns whether the entity is tamed.
- Added `q.entity.add_effect(<effect>, <duration>, <amplifier>, <ambient> <show_particles>)` Adds a status effect to the entity.
- Added `q.entity.remove_effect(<effect>)` Removes a status effect from the entity.
- Added `q.entity.has_effect(<effect>)` Returns whether the entity has the given status effect.
- Added `q.entity.is_looking_at(<entity>)` Returns whether the entity is looking at the given entity.
- Added `q.entity.tags` Returns an array of the entity's tags.
- Added `q.entity.add_tag(<tag>)` Adds the given tag to the entity.
- Added `q.entity.remove_tag(<tag>)` Removes the given tag from the entity.
- Added `q.entity.has_tag(<tag>)` Returns whether the entity has the given tag.
- Added `q.entity.make_intangible(<true/false>)` Makes the entity intangible.
- Added `q.pokemon.force_evolve(<index>)` Forces the Pokémon at the given index.
- Added `q.pokemon.can_evolve` Returns whether the Pokémon at the given index can evolve.
- Added `q.pokemon.is_busy` Returns whether the Pokémon is busy.
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
- Fixed `background` field and added `textColor` field for dialogues.
- Added support to reload some data registries:
- MoLang scripts
- Callbacks
- Spawn Detail Presets
- Spawn Pools
- Spawn Rules
- Cosmetic Items
- Dialogues
- Fossils
- Natural Materials
- Action Effects
- Mechanics
- Unlockable Wallpapers
- Starter Data
- Fixed `cobblemon:reel_in_pokemon` criteria not working when used together with a `baitId`
- Also changed the default from `cobblemon:empty_bait` to `any`
- The previous default is still available by using the above as baitId
### Particles
Added new/updated particles for the following moves:
- Absorb
- Acid
- Acid Spray
- Aerial Ace
- Amnesia
- Aurora Beam
- Bite
- Body Slam
- Bubble
- Bubble Beam
- Bulldoze
- Bullet Punch
- Close Combat
- Confusion
- Crunch
- Cotton Guard
- Double Team
- Dragon Claw
- Egg Bomb
- Ember
- Eruption
- Explosion
- Fire Blast
- Fire Punch
- Fire Spin
- Flame Burst
- Flame Charge
- Flamethrower
- Flame Wheel
- Frost Breath
- Frustration
- Giga Drain
- Growl
- Haze
- Hidden Power
- Horn Attack
- Hyper Fang
- Hypnosis
- Ice Beam
- Ice Punch
- Ice Shard
- Icy Wind
- Infestation
- Kinesis
- Lava Plume
- Leech Life
- Leech Seed
- Leer
- Lick
- Magical Leaf
- Mega Drain
- Minimize
- Mist
- Mud Bomb
- MudSport
- Mystical Fire
- Nasty Plot
- Poison Fang
- Poison Gas
- Poison Powder
- Population Bomb
- Pound
- Powder
- Powder Snow
- Present
- Protect
- Psybeam
- Psychic
- Pursuit
- Quick Attack
- Rage Powder
- Razor Leaf
- Return
- Rock Throw
- Sand Attack
- Scary Face
- Scratch
- Seed Bomb
- Seismic Toss
- Self Destruct
- Shadow Ball
- Shadow Claw
- Sing
- Sleep Powder
- Sludge Bomb
- Smokescreen
- Spike
- Spore
- Stomp
- String Shot
- Stun Spore
- Super Fang
- Swords Dance
- Tackle
- Tail Whip
- Thunder
- Thunder Punch
- Thunderbolt
- Thunder Shock
- Thunder Wave
- Toxic
- Water Gun
- Water Pulse
- Water Sport
- Will-O-Wisp
- Withdraw
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -1004,7 +660,6 @@ Added new/updated particles for the following moves:
- Parametric particle motion now works.
- Event-spawned particles now work.
- Particles can now have independent coordinate spaces.
- Fixed suffocation of Pokémon when a pokeball breaks near a wall
### Developer
- Updated the Pokédex data updated events to always include a `Pokemon` instance, and optionally a `DisguiseData` instance.
+1 -8
View File
@@ -31,7 +31,6 @@ repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
maven(url = "https://maven.gegy.dev")
mavenLocal()
}
@@ -45,12 +44,6 @@ dependencies {
modCompileOnly(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")
}
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
@@ -101,4 +94,4 @@ sourceSets {
}
}
}
}
}
@@ -1,22 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
/**
* Used for mixin shenanigans. ServerPlayers now store their Cobblemon spawner
* inside, so this interface helps execute that.
*
* @author Hiroku
* @since October 27th, 2025
*/
public interface PlayerSpawnerGetter {
PlayerSpawner getPlayerSpawner();
}
@@ -0,0 +1,102 @@
/*
* 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.compat;
import com.cobblemon.mod.common.CobblemonEntities;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import dev.lambdaurora.lambdynlights.LambDynLights;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import net.minecraft.world.entity.player.Player;
// Java class due to relying on entrypoint on Fabric side
public class LambDynamicLightsCompat {
// This is all called on client side
// These things are checked every tick so try to keep it as free as possible
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
// Make sure not to call anything Fabric specific :)
public static void hookCompat() {
// Pokémon entities
DynamicLightHandlers.registerDynamicLightHandler(
CobblemonEntities.POKEMON,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPokemonLightLevel,
pokemon -> false
)
);
// Shouldered Pokémon
DynamicLightHandlers.registerDynamicLightHandler(
EntityType.PLAYER,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPlayerLightLevel,
player -> false
)
);
}
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int resolvedPlayerLightLevel(Player player) {
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
final int leftShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
final int rightShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
final int itemLightLevel = customHeldItemLightLevel(player.getMainHandItem(), player.getOffhandItem());
return Math.max(itemLightLevel, Math.max(leftShoulderLightLevel, rightShoulderLightLevel));
}
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 :LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int customHeldItemLightLevel(ItemStack mainhand, ItemStack offhand) {
return (mainhand.getItem() instanceof PokedexItem || offhand.getItem() instanceof PokedexItem) ? 13 : 0;
}
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -1,28 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public abstract class CustomLuminance {
protected static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
protected static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -1,42 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
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.world.entity.EntityType;
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
PokemonLuminance.INSTANCE
);
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
PlayerLuminance.INSTANCE
);
@Override
public void onInitializeDynamicLights(DynamicLightsContext context) {
Cobblemon.LOGGER.info("Lamb Dynamic Lights compatibility enabled");
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
});
}
@SuppressWarnings({"removal", "UnstableApiUsage"})
@Override
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
}
@@ -1,72 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
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.tags.TagKey;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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"));
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.PLAYER_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Entity entity) {
if (entity instanceof Player player) {
final int shoulderLightLevel = shoulderLuminance(itemLightSourceManager, player);
final int itemLightLevel = player.isHolding(stack -> stack.is(POKEDEX)) ? 13 : 0;
return Math.max(shoulderLightLevel, itemLightLevel);
} else {
return 0;
}
}
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());
if (leftShoulder == null && rightShoulder == null) {
return 0;
}
final int leftShoulderLightLevel = extractShoulderLightLevel(leftShoulder, itemLightSourceManager, player.isUnderWater());
final int rightShoulderLightLevel = extractShoulderLightLevel(rightShoulder, itemLightSourceManager, player.isUnderWater());
return Math.max(leftShoulderLightLevel, rightShoulderLightLevel);
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, @NotNull ItemLightSourceManager itemLightSourceManager, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
}
@@ -1,45 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
public final class PokemonLuminance extends CustomLuminance implements EntityLuminance {
public static final PokemonLuminance INSTANCE = new PokemonLuminance();
private PokemonLuminance() {}
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.POKEMON_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(
@NotNull ItemLightSourceManager itemLightSourceManager,
@NotNull Entity entity
) {
if (entity instanceof PokemonEntity pokemon) {
boolean underwater = pokemon.isUnderWater();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
} else {
return 0;
}
}
}
@@ -1,100 +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.CobblemonMemories;
import com.cobblemon.mod.common.api.ai.config.task.PathToBeeHiveTaskConfig;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData;
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.phys.Vec3;
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(BeehiveBlockEntity.Occupant.class)
public abstract class BeeOccupantMixin {
@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();
Entity entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
if (entity instanceof PokemonEntity pokemonEntity) {
var state = level.getBlockState(pos);
var newPos = Vec3.ZERO;
var newYaw = 0f;
var brain = pokemonEntity.getBrain();
if (state.is(BlockTags.BEEHIVES)) {
// position in front of the entrance, face away from the block
var facing = state.getValue(HorizontalDirectionalBlock.FACING);
newPos = pos.relative(facing).getCenter();
newYaw = facing.toYRot();
brain.setMemory(CobblemonMemories.INSTANCE.getHIVE_LOCATION(), pos); // This needs to be set in the case that the hive was picked up and moved.
} else {
// 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
);
newYaw = level.random.nextFloat() * 360F;
brain.eraseMemory(CobblemonMemories.INSTANCE.getHIVE_LOCATION());
}
entity.yRotO = newYaw;
entity.setPos(newPos);
// Do honey logic
var hasNectar = brain.getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
if (hasNectar) {
// Remove nectar and reset got to hive cooldown
brain.eraseMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR());
brain.setMemoryWithExpiry(CobblemonMemories.INSTANCE.getHIVE_COOLDOWN(), true, PathToBeeHiveTaskConfig.STAY_OUT_OF_HIVE_COOLDOWN);
// Increment honey level of the hive
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;
if (i + j > 5) {
--j;
}
level.setBlockAndUpdate(pos, state.setValue(BeehiveBlock.HONEY_LEVEL, i + j));
}
}
}
cir.setReturnValue(entity);
}
}
}
@Inject(method = "of", at = @At("HEAD"), cancellable = true)
private static void cobblemon$of(Entity entity, CallbackInfoReturnable<BeehiveBlockEntity.Occupant> cir) {
if (entity instanceof PokemonEntity pokemonEntity) {
CompoundTag compoundTag = new CompoundTag();
entity.save(compoundTag);
Boolean hasNectar = pokemonEntity.getBrain().getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
cir.setReturnValue(new BeehiveBlockEntity.Occupant(CustomData.of(compoundTag), 0, hasNectar ? 2400 : 600));
cir.cancel();
}
}
}
@@ -1,102 +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 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;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(BeehiveBlockEntity.class)
public abstract class BeehiveBlockEntityMixin {
// Accessor for the private 'stored' field in BeehiveBlockEntity
@Accessor("stored")
public abstract List<BeeDataAccessor> getStored();
@Inject(method = "emptyAllLivingFromHive", at = @At("HEAD"))
private void cobblemon$emptyAllLivingFromHive(@Nullable Player player, BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, CallbackInfo ci) {
if (player != null) {
final BeehiveBlockEntity blockEntity = (BeehiveBlockEntity) (Object) this;
// if the block is sedated or the player is too far away, no aggro should be applied
if (blockEntity.isSedated() || player.position().distanceToSqr(blockEntity.getBlockPos().getCenter()) > 16.0)
return;
// from this point need to access private members of the block entity
final BeehiveBlockEntityAccessor blockEntityAccessor = (BeehiveBlockEntityAccessor) this;
List<Object> storedList = blockEntityAccessor.getStored();
List<Entity> list = Lists.newArrayList();
storedList.removeIf((beeData) -> {
BeehiveBlockEntity.Occupant occupant = ((BeeDataAccessor) beeData).invokeToOccupant();
// Checking the for the tag allows us to make sure it's one of our pokemon before trying to rehydrate it into an entity
boolean isPokemon = false;
if (occupant.entityData().contains("id")) {
isPokemon = "cobblemon:pokemon".equals(occupant.entityData().getUnsafe().getString("id"));
}
if (isPokemon) {
return BeehiveBlockEntityAccessor.releaseOccupant(
((BlockEntityAccessor) blockEntityAccessor).getLevel(),
((BlockEntityAccessor) blockEntityAccessor).getWorldPosition(),
state,
occupant,
list,
releaseStatus,
blockEntityAccessor.savedFlowerPos());
} else {
return false;
}
}
);
for (Entity entity : list) {
if (entity instanceof PokemonEntity) {
((PokemonEntity) entity).getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, player);
}
}
}
}
@Redirect(
method = "releaseOccupant",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"
)
)
private static boolean cobblemon$captureReleasedEntity(
Level level, Entity entity,
Level lvl, BlockPos pos, BlockState state,
BeehiveBlockEntity.Occupant occupant, @Nullable List<Entity> bees,
BeehiveBlockEntity.BeeReleaseStatus releaseStatus, @Nullable BlockPos flowerPos) {
if (bees != null && !bees.contains(entity)) {
// Avoid duplicates if vanilla also adds bees to the list on your version
bees.add(entity);
}
return level.addFreshEntity(entity);
}
}
@@ -7,7 +7,6 @@
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.duck.ChannelDuck;
import com.mojang.blaze3d.audio.Channel;
import org.lwjgl.openal.AL10;
@@ -1,62 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.EnderpearlItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
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.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) {
double range = player.entityInteractionRange();
Entity closestEntity = level.getEntities(player, AABB.ofSize(player.position(), range, range, range)).stream()
.filter(entity -> isLookingAt(player, entity))
.min(Comparator.comparingDouble(entity -> entity.distanceTo(player)))
.orElse(null);
if (closestEntity instanceof PokemonEntity pokemonEntity) {
if (player.isCrouching() && pokemonEntity.getOwnerUUID() == player.getUUID()) {
cir.cancel();
}
}
}
public boolean isLookingAt(Player player, Entity other) {
float maxDistance = 10F;
float stepDistance = 0.1F;
double step = stepDistance;
Vec3 startPos = player.getEyePosition();
Vec3 direction = player.getLookAngle();
while (step <= maxDistance) {
Vec3 location = startPos.add(direction.scale(step));
step += stepDistance;
if (other.getBoundingBox().contains(location)) {
return true;
}
}
return false;
}
}
@@ -1,62 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
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.monster.Creeper;
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(Fox.class)
public class EntityFoxMixin extends Mob {
private EntityFoxMixin(net.minecraft.world.entity.EntityType<? extends Creeper> entityType, Level world) {
super(entityType, world);
}
@Inject(method = "registerGoals", at = @At(value = "TAIL"), cancellable = false)
private void cobblemon$initGoals(CallbackInfo callbackInfo) {
final Fox fox = (Fox) (Object) this;
// Pokemon Entities
this.goalSelector.addGoal(
4,
new AvoidEntityGoal<>(
fox,
PokemonEntity.class,
8.0f,
1.6,
1.4,
entity -> ((PokemonEntity)entity).getBehaviour().getEntityInteract().getAvoidedByFox() && ((PokemonEntity)entity).getBeamMode() != 1
)
);
// Players with shoulder mounted Pokemon
this.goalSelector.addGoal(
3,
new AvoidEntityGoal<>(
fox,
ServerPlayer.class,
8.0f,
1.6,
1.4,
entity -> EntityBehaviour.Companion.hasFoxFearedShoulderMount((ServerPlayer)entity)
)
);
}
}
@@ -55,6 +55,50 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
}
}
@Override
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
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
)
);
}
return super.pick(hitDistance, partialTicks, hitFluids);
}
@Unique
private Vec3 cobblemon$getEyePosition(float partialTicks, PokemonEntity pokemonEntity, Vec3 locatorOffset) {
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vec3 offset = locatorOffset.add(pokemonEntity.position());
if (controller.isActive() && controller.getOrientation() != null) {
Quaternionf orientation = controller.getRenderOrientation(partialTicks);
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f)));
offset.add(rotatedEyeHeight);
}
return offset;
}
@Inject(method = "tick", at = @At("HEAD"))
private void cobblemon$updateRenderOrientation(CallbackInfo ci) {
this.cobblemon$orientationController.tick();
@@ -1,28 +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.bedrockk.molang.runtime.value.DoubleValue;
import com.cobblemon.mod.common.entity.MoLangScriptingEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.PowderSnowBlock;
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(PowderSnowBlock.class)
public abstract class PowderSnowBlockMixin {
@Inject(method = "canEntityWalkOnPowderSnow", at = @At(value = "RETURN"), cancellable = true)
private static void cobblemon$canEntityWalkOnPowderSnow(Entity entity, CallbackInfoReturnable<Boolean> ci) {
if (ci.getReturnValue() == false && entity instanceof MoLangScriptingEntity) {
ci.setReturnValue(((MoLangScriptingEntity) entity).getConfig().getMap().getOrDefault("can_stand_on_powder_snow", DoubleValue.ZERO).asDouble() == 1.0);
}
}
}
@@ -8,51 +8,20 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.PlayerSpawnerGetter;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
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.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 PlayerSpawnerGetter {
@Shadow
public abstract ServerLevel serverLevel();
@Unique
public PlayerSpawner cobblemon$spawner;
@Override
public PlayerSpawner getPlayerSpawner() {
ServerPlayer player = (ServerPlayer)(Object)this;
if (cobblemon$spawner == null) {
cobblemon$spawner = PlayerSpawnerFactory.INSTANCE.create(player);
}
return cobblemon$spawner;
}
@Inject(method = "tick", at = @At("TAIL"))
private void cobblemon$tickSpawner(CallbackInfo ci) {
if (!Cobblemon.config.getEnableSpawning()) {
return;
} else if (!serverLevel().getGameRules().getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING)) {
return;
}
getPlayerSpawner().tick();
}
public abstract class ServerPlayerMixin {
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
@@ -82,4 +51,5 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerGetter {
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
return original && !(entity instanceof PokemonEntity);
}
}
@@ -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.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.level.Level;
import net.minecraft.world.level.block.SweetBerryBushBlock;
import net.minecraft.world.level.block.state.BlockState;
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(SweetBerryBushBlock.class)
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) {
if (entity instanceof MoLangScriptingEntity) {
if (((MoLangScriptingEntity) entity).getConfig().getMap().getOrDefault("immune_to_sweet_berry_bush_block", DoubleValue.ZERO).asDouble() == 1.0) {
callbackInfo.cancel();
}
}
}
}
@@ -25,9 +25,9 @@ public class TargetingConditionsMixin {
@Inject(method = "test", at = @At("HEAD"), cancellable = true)
public void test(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> ci) {
if (targetEntity instanceof ServerPlayer player) {
if (player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE)) return;
boolean canTargetInBattle = player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE);
boolean targetInBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (targetInBattle) ci.setReturnValue(false);
if (!canTargetInBattle && targetInBattle) ci.setReturnValue(false);
}
}
}
@@ -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.world.level.block.entity.BeehiveBlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(targets = "net.minecraft.world.level.block.entity.BeehiveBlockEntity$BeeData")
public interface BeeDataAccessor {
@Invoker("toOccupant")
BeehiveBlockEntity.Occupant invokeToOccupant();
}
@@ -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.accessor;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.List;
@Mixin(BeehiveBlockEntity.class)
public interface BeehiveBlockEntityAccessor {
@Accessor("stored")
List<Object> getStored();
@Accessor("savedFlowerPos")
BlockPos savedFlowerPos();
/**
* Access the private static method:
* private static void releaseOccupant(Level level, BlockPos pos, BeeData beeData, ...)
*/
@Invoker("releaseOccupant")
static boolean releaseOccupant(Level level, BlockPos pos, BlockState state, BeehiveBlockEntity.Occupant occupant, @Nullable List<Entity> storedInHives, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, @Nullable BlockPos storedFlowerPos) {
throw new AssertionError(); // Mixin will overwrite
}
}
@@ -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();
}
@@ -15,11 +15,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
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.alchemy.PotionBrewing;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
@@ -29,8 +27,6 @@ import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
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;
import java.util.List;
import java.util.Optional;
@@ -47,9 +43,7 @@ public class BrewingStandBlockEntityMixin {
//copied from vanilla doBrew logic, but slightly tweaked to work for cobblemon, this was needed as replacing the loop was not quite possible inside doBrew
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().copy());
}
itemStack.shrink(1);
@@ -97,35 +91,4 @@ public class BrewingStandBlockEntityMixin {
return recipeHolder.map(RecipeHolder::value).orElse(null);
}
@Inject(method = "canPlaceItem", at = @At("HEAD"), cancellable = true)
private void cobblemon$canPlaceItem(int slot, ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
if (stack == null || stack.isEmpty()) return;
BrewingStandBlockEntity self = (BrewingStandBlockEntity) (Object) this;
Level level = self.getLevel();
if (level != null) {
var rm = level.getRecipeManager();
//Input slot
if (slot == 3) {
if (BrewingStandRecipe.Companion.isInput(stack, rm)) {
cir.setReturnValue(true);
return;
}
}
// Output slots
if (slot >= 0 && slot < 3) {
if (BrewingStandRecipe.Companion.isBottle(stack, rm)) {
if (self.getItem(slot).isEmpty()) {
cir.setReturnValue(true);
} else {
cir.setReturnValue(false);
}
}
}
}
}
}
@@ -31,15 +31,14 @@ public class AbstractClientPlayerMixin {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
cir.setReturnValue(usageContext.getFovMultiplier());
} else {
// Only modify fov through riding if the pokedex is not open
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
}
// Return custom fov mult for riding
cir.setReturnValue(ride.rideFovMult());
}
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
//Return custom fov mult
cir.setReturnValue(ride.rideFovMult());
}
}
}
@@ -1,46 +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.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;
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(
PoseStack poseStack,
VertexConsumer vertexConsumer,
int packedLight,
int packedOverlay,
int colour,
CallbackInfo ci
) {
Model model = (Model) (Object) this;
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
}
}
}
@@ -55,7 +55,7 @@ public abstract class CameraMixin {
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof OrientationControllable controllable)) return;
if (!(this.entity instanceof OrientationControllable controllable) || Cobblemon.config.getDisableRoll()) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() && controller.getOrientation() != null) {
if(this.returnTimer < 1) {
@@ -9,21 +9,15 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.world.entity.Entity;
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.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Predicate;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Unique
@@ -48,16 +42,4 @@ public class GameRendererMixin {
lastTime = newTime;
}
@ModifyArg(
method = "pick(Lnet/minecraft/world/entity/Entity;DDF)Lnet/minecraft/world/phys/HitResult;",
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;"
),
index = 4
)
private Predicate<Entity> filterPokemonVehicle(Predicate<Entity> original, @Local(ordinal = 0, argsOnly = true) Entity entity) {
return (entityX) -> !(entity.getVehicle() == entityX && entityX instanceof PokemonEntity) && original.test(entityX);
}
}
@@ -19,17 +19,20 @@ import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
@Mixin(HumanoidModel.class)
public class HumanoidModelMixin {
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
@Shadow @Final
public ModelPart head;
@Shadow @Final
public ModelPart hat;
@Shadow @Final
public ModelPart rightArm;
@Shadow @Final
public ModelPart leftArm;
@@ -37,8 +40,6 @@ public class HumanoidModelMixin {
public ModelPart rightLeg;
@Shadow @Final
public ModelPart leftLeg;
@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(
@@ -50,23 +51,19 @@ public class HumanoidModelMixin {
float headPitch,
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 (relevantPartsByName.isEmpty()) {
relevantPartsByName.put("arm_left", leftArm);
relevantPartsByName.put("arm_right", rightArm);
relevantPartsByName.put("leg_left", leftLeg);
relevantPartsByName.put("leg_right", rightLeg);
relevantPartsByName.put("head", head);
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
});
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
@@ -79,8 +76,7 @@ public class HumanoidModelMixin {
if (!(entity instanceof AbstractClientPlayer player)) return;
MountedPlayerRenderer.shouldApplyRootAnimation = true;
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -174,16 +174,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
return this.cobblemon$isDoubleJumping;
}
@Inject(method = "isHandsBusy", at = @At("HEAD"), cancellable = true)
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
cir.setReturnValue(seat.getHandsBusy());
}
}
@Override
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
@@ -117,8 +116,6 @@ public abstract class MouseHandlerMixin {
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
boolean returnValue = true;
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -129,12 +126,7 @@ public abstract class MouseHandlerMixin {
var sensitivity = Mth.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;
}
// Clamp player rotation if riding and the vehicle demands it
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity vehicle) {
vehicle.clampPassengerRotation(player);
return false;
}
if (!(player instanceof OrientationControllable controllable)) return true;
@@ -145,33 +137,35 @@ public abstract class MouseHandlerMixin {
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return returnValue;
return true;
}
// Send mouse input to be interpreted into rotation
// deltas by the ride controller
//Send mouse input to be interpreted into rotation
//deltas by the ride controller
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
// Perform Rotation using mouse influenced rotation deltas.
//Perform Rotation using mouse influenced rotation deltas.
controllable.getOrientationController().rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
);
// Gather and apply the current rotation deltas
//Gather and apply the current rotation deltas
var angRot = cobblemon$getAngularVelocity(movementTime);
// Apply smoothing if requested by the controller.
// This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing()) {
//Apply smoothing if requested by the controller.
//This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing())
{
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
}
// Otherwise simply apply the smoothing
else {
//Otherwise simply apply the smoothing
else
{
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
@@ -216,18 +210,6 @@ public abstract class MouseHandlerMixin {
if (vehicle == null) return Vec3.ZERO;
if (!(vehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
var invertYaw = Cobblemon.config.getInvertYaw();
var invertPitch = Cobblemon.config.getInvertPitch();
var xValue = mouseX * Cobblemon.config.getXAxisSensitivity();
var yValue = mouseY * Cobblemon.config.getYAxisSensitivity();
var swapXAndY = Cobblemon.config.getSwapXAndYAxes();
var yaw = (swapXAndY ? yValue : xValue) * (invertYaw? -1.0f : 1.0f);
var pitch = (swapXAndY ? xValue : yValue) * (invertPitch? -1.0f : 1.0f);
var sensitivity = cobblemon$getRidingSensitivity();
return pokemonEntity.ifRidingAvailableSupply(Vec3.ZERO, (behaviour, settings, state) -> {
return behaviour.rotationOnMouseXY(
@@ -235,8 +217,8 @@ public abstract class MouseHandlerMixin {
state,
pokemonEntity,
player,
pitch,
yaw,
mouseY,
mouseX,
yMouseSmoother,
xMouseSmoother,
sensitivity,
@@ -10,7 +10,8 @@ package com.cobblemon.mod.common.mixin.client.snapshots;
import com.cobblemon.mod.common.CobblemonBuildDetails;
import com.cobblemon.mod.common.client.gui.snapshots.SnapshotWarningScreen;
import com.cobblemon.mod.common.client.persisted.ClientPersistedData;
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementData;
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementReader;
import kotlin.Unit;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
@@ -41,7 +42,7 @@ public final class SnapshotWarningMixin {
return Unit.INSTANCE;
}
ClientPersistedData.INSTANCE.writeSnapshotsData(new ClientPersistedData.SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
SnapshotAcknowledgementReader.INSTANCE.write(new SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
runnable.run();
return Unit.INSTANCE;
@@ -53,7 +54,7 @@ public final class SnapshotWarningMixin {
@Unique
@SuppressWarnings("ResultOfMethodCallIgnored")
private boolean cobblemon$isAcknowledged() {
ClientPersistedData.SnapshotAcknowledgementData data = ClientPersistedData.INSTANCE.readSnapshotsData();
SnapshotAcknowledgementData data = SnapshotAcknowledgementReader.INSTANCE.read();
if (data == null) {
return false;
}
@@ -9,18 +9,12 @@
package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class)
@@ -35,27 +29,4 @@ public abstract class EntityMixin {
}
}
@WrapOperation(
method = "collide(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/Entity;onGround()Z"
)
)
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original) {
if (entity instanceof PokemonEntity && entity.hasControllingPassenger()) {
BlockPos below = entity.blockPosition().below();
Level level = entity.level();
var blockStateBelow = level.getBlockState(below);
boolean isAirOrLiquid = blockStateBelow.isAir() || !blockStateBelow.getFluidState().isEmpty();
boolean canSupportEntity = blockStateBelow.isFaceSturdy(level, below, Direction.UP);
boolean standingOnSolid = canSupportEntity && !isAirOrLiquid;
if (standingOnSolid) {
return true;
}
}
return original.call(entity);
}
}
@@ -76,7 +76,7 @@ import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.EntityCallbackHandler
import com.cobblemon.mod.common.events.CallbackHandler
import com.cobblemon.mod.common.events.FlowHandler
import com.cobblemon.mod.common.events.PokedexHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
@@ -313,7 +313,7 @@ object Cobblemon {
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
CallbackHandler.setup()
FlowHandler.setup()
EntityCallbackHandler.setup()
ifDedicatedServer {
@@ -128,7 +128,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(APRICORN_BLOCK_SET_TYPE))
@JvmField
@@ -205,7 +205,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
@JvmField
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
@JvmField
@@ -213,7 +213,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
@@ -18,78 +18,72 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.net.messages.client.data.CallbackRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import java.util.concurrent.ExecutionException
import java.util.function.Predicate
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
/**
* Holds all the callbacks that are loaded from the server's data packs. A callback is foldered under the event identifier,
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
* which can be Cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
*
* @author Hiroku
* @since February 24th, 2024
*/
object CobblemonCallbacks : DataRegistry {
override val id = cobblemonResource("callbacks")
override val observable = SimpleObservable<CobblemonCallbacks>()
object CobblemonFlows : DataRegistry {
override val id = cobblemonResource("flows")
override val observable = SimpleObservable<CobblemonFlows>()
override val type = PackType.SERVER_DATA
override fun sync(player: ServerPlayer) {
player.sendPacket(CallbackRegistrySyncPacket(clientCallbacks.entries))
player.sendPacket(FlowRegistrySyncPacket(clientFlows.entries))
}
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
val clientCallbacks = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val callbacks = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
override fun reload(manager: ResourceManager) {
clientCallbacks.clear()
callbacks.clear()
clientFlows.clear()
flows.clear()
val unsortedCallbacks = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val unsortedFlows = mutableMapOf<ResourceLocation, MutableList<Pair<String, ExpressionLike>>>()
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
val predicate: Predicate<ResourceLocation> = Predicate { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
resource.openAsReader().use { stream ->
stream.buffered().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
manager.listResources("callbacks", predicate)
.plus(manager.listResources("flows", predicate)) // old name
.forEach { (identifier, resource) ->
resource.openAsReader().use { stream ->
stream.buffered().use { reader ->
try {
val expression = reader.readText().asExpressionLike()
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
?: throw IllegalArgumentException("Invalid callback path: $identifier. Should have a folder structure that includes the name of the event to callback.")
val callbackKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
unsortedCallbacks.putIfAbsent(callbackKey, mutableListOf())
unsortedCallbacks[callbackKey]!!.add(identifier.path to expression)
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for callback: $identifier", exception)
}
val flowKey = ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)
unsortedFlows.putIfAbsent(flowKey, mutableListOf())
unsortedFlows[flowKey]!!.add(identifier.path to expression)
} catch (exception: Exception) {
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
}
}
}
unsortedCallbacks.forEach { (key, value) ->
value.sortBy { it.first }
if (key.path.startsWith("flows/client/") || key.path.startsWith("callbacks/client/")) {
clientCallbacks.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
} else {
callbacks.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
}
}
Cobblemon.LOGGER.info("Loaded ${callbacks.size} callbacks and ${clientCallbacks.size} client callbacks")
unsortedFlows.forEach { (key, value) ->
value.sortBy { it.first }
if (key.path.startsWith("flows/client/")) {
clientFlows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
} else {
flows.getOrPut(key) { mutableListOf() }.addAll(value.map { it.second })
}
}
Cobblemon.LOGGER.info("Loaded ${flows.size} flows and ${clientFlows.size} client flows")
observable.emit(this)
}
@@ -99,7 +93,7 @@ object CobblemonCallbacks : DataRegistry {
functions: Map<String, (MoParams) -> Any> = emptyMap(),
cancelable: Cancelable? = null
) {
if (eventResourceLocation !in callbacks) return
if (eventResourceLocation !in flows) return
if (cancelable == null) {
runtime.environment.query.functions.remove("cancel")
@@ -112,7 +106,7 @@ object CobblemonCallbacks : DataRegistry {
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
callbacks[eventResourceLocation]?.forEach {
flows[eventResourceLocation]?.forEach {
if (cancelable != null && cancelable.isCanceled) {
return
}
@@ -26,10 +26,10 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(PokemonItemComponent.PACKET_CODEC)
.build())
val HELD_ITEM_EFFECT: DataComponentType<HeldItemEffectComponent> = create("held_item_effect", DataComponentType.builder<HeldItemEffectComponent>()
.persistent(HeldItemEffectComponent.CODEC)
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
.build())
val HELD_ITEM_REP: DataComponentType<HeldItemCapableComponent> = DataComponentType.builder<HeldItemCapableComponent>()
.persistent(HeldItemCapableComponent.CODEC)
.networkSynchronized(HeldItemCapableComponent.PACKET_CODEC)
.build()
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
.persistent(RodBaitComponent.CODEC)
@@ -86,7 +86,6 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
@@ -193,19 +192,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val campfire_pots = mutableListOf<CampfirePotItem>()
@JvmField
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, CampfirePotColor.RED)
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
@JvmField
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, CampfirePotColor.YELLOW)
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
@JvmField
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, CampfirePotColor.GREEN)
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, "green")
@JvmField
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, CampfirePotColor.BLUE)
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
@JvmField
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, CampfirePotColor.PINK)
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
@JvmField
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, CampfirePotColor.BLACK)
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
@JvmField
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
@JvmField
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
@@ -401,7 +400,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
@JvmField
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, 0.28f, false)
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
@JvmField
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
@JvmField
@@ -696,7 +695,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
@JvmField
val MOOMOO_MILK = create("moomoo_milk", MoomooMilk(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE)))
@JvmField
val POTION = create("potion", PotionItem(PotionType.POTION))
@@ -1501,8 +1500,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
}
)
private fun campfirePotItem(block: Block, color: CampfirePotColor): CampfirePotItem {
val item = create("campfire_pot_${color.suffix}", CampfirePotItem(block, color))
private fun campfirePotItem(block: Block, type: String): CampfirePotItem {
val item = create("campfire_pot_${type}", CampfirePotItem(block))
campfire_pots.add(item)
return item
}
@@ -1649,4 +1648,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
}
}
}
@@ -39,7 +39,6 @@ object CobblemonMechanics : DataRegistry {
var potions = PotionsMechanic()
override fun sync(player: ServerPlayer) {}
override fun reload(manager: ResourceManager) {
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.GlobalPos
import net.minecraft.core.UUIDUtil
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import java.util.*
import java.util.Optional
import java.util.UUID
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
object CobblemonMemories {
val memories = mutableMapOf<String, MemoryModuleType<*>>()
@@ -43,26 +43,17 @@ object CobblemonMemories {
val TARGETED_BATTLE_POKEMON = register<UUID>("targeted_battle_pokemon")
val NEAREST_VISIBLE_ATTACKER = register<LivingEntity>("nearest_visible_attacker")
val NEARBY_GROWABLE_CROPS = register<BlockPos>("nearby_growable_crops")
val NEARBY_SWEET_BERRY_BUSH = register<BlockPos>("nearest_sweet_berry_bush")
val DISABLE_WALK_TO_BERRY_BUSH = register<Boolean>("disable_walk_to_berry_bush")
val TIME_TRYING_TO_REACH_BERRY_BUSH = register<Int>("time_trying_to_reach_berry_bush")
val IS_CONSUMING_ITEM = register<Boolean>("is_consuming_item")
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
val HIVE_LOCATION = register<BlockPos>("hive_location", BlockPos.CODEC)
val HIVE_BLACKLIST = register<List<BlockPos>>("hive_blacklist")
val HIVE_LOCATION = register<BlockPos>("hive_location")
val HIVE_COOLDOWN = register<Boolean>("hive_cooldown")
val NEARBY_FLOWER = register<BlockPos>("nearby_flower", BlockPos.CODEC)
val PATH_TO_NEARBY_FLOWER_COOLDOWN = register<Boolean>("path_to_nearby_flower_cooldown")
val HAS_NECTAR = register<Boolean>("has_nectar", PrimitiveCodec.BOOL)
val NEARBY_SACC_LEAVES = register<BlockPos>("nearby_sacc_leaves", BlockPos.CODEC)
val NEARBY_FLOWERS = register<List<BlockPos>>("nearby_flower")
val POLLINATED = register<Boolean>("pollinated", PrimitiveCodec.BOOL)
/** who am i following rn? */
val HERD_LEADER = register<String>("herd_leader", PrimitiveCodec.STRING)
/** how many are following me rn? */
val HERD_SIZE = register<Int>("herd_size") // Don't bother saving it, we'll try to keep count roughly.
val ATTACK_TARGET_DATA = register<CobblemonAttackTargetData>("attack_target_data") // This is used to store additional information about the attack target, such as when to give up pursuit.
val WANDER_CONTROL = register<CobblemonWanderControl>("wander_control")
val DISABLE_WALK_TO_WANTED_ITEM = register<Boolean>("disable_walk_to_wanted_item")
val TIME_TRYING_TO_REACH_WANTED_ITEM = register<Int>("time_trying_to_wanted_item")
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
val memoryModule = MemoryModuleType(Optional.of(codec))
@@ -9,8 +9,8 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.cooking.ToggleCookingPotLidHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
@@ -65,8 +65,8 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
@@ -157,7 +157,6 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatRangePacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
@@ -171,11 +170,11 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
@@ -209,7 +208,6 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
@@ -224,11 +222,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
@@ -312,7 +310,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MarksPotentialUpdatePacket.ID, MarksPotentialUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -391,6 +388,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler))
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -403,11 +401,10 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpawnBaitRegistrySyncPacket.ID, SpawnBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CallbackRegistrySyncPacket.ID, CallbackRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -581,7 +578,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
@@ -26,6 +26,8 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
@JvmField
val NURSE_KEY = createKey("nurse", CobblemonBlocks.HEALING_MACHINE, 1, 1)
@JvmField
val POKE_SNACK_KEY = createKey("poke_snack", CobblemonBlocks.POKE_SNACK, 0, 1)
@JvmField
val SACCHARINE_LOG_SLATHERED_KEY = createKey("saccharine_log_slathered", CobblemonBlocks.SACCHARINE_LOG_SLATHERED, 0, 1)
// TODO after 1.7
// @JvmField
@@ -1,106 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.GliderSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
object CobblemonRideSettings : DataRegistry {
override val id: ResourceLocation = cobblemonResource("ride_settings")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonRideSettings>()
val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
.create()
var bird = BirdSettings()
var glider = GliderSettings()
var helicopter = HelicopterSettings()
var hover = HoverSettings()
var jet = JetSettings()
var rocket = RocketSettings()
var horse = HorseSettings()
var minekart = MinekartSettings()
var vehicle = VehicleSettings()
var boat = BoatSettings()
var burst = BurstSettings()
var dolphin = DolphinSettings()
override fun sync(player: ServerPlayer) {
player.sendPacket(
RideSettingsSyncPacket(
bird = bird,
glider = glider,
helicopter = helicopter,
hover = hover,
jet = jet,
rocket = rocket,
horse = horse,
minekart = minekart,
vehicle = vehicle,
boat = boat,
burst = burst,
dolphin = dolphin
)
)
}
override fun reload(manager: ResourceManager) {
bird = loadStyle(manager, "bird", BirdSettings::class.java)
glider = loadStyle(manager, "glider", GliderSettings::class.java)
helicopter = loadStyle(manager, "helicopter", HelicopterSettings::class.java)
hover = loadStyle(manager, "hover", HoverSettings::class.java)
jet = loadStyle(manager, "jet", JetSettings::class.java)
rocket = loadStyle(manager, "rocket", RocketSettings::class.java)
horse = loadStyle(manager, "horse", HorseSettings::class.java)
minekart = loadStyle(manager, "minekart", MinekartSettings::class.java)
vehicle = loadStyle(manager, "vehicle", VehicleSettings::class.java)
boat = loadStyle(manager, "boat", BoatSettings::class.java)
burst = loadStyle(manager, "burst", BurstSettings::class.java)
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
}
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
manager.getResourceOrThrow(cobblemonResource("ride_settings/$name.json")).open().use {
return gson.fromJson(it.reader(), clazz)
}
}
}
@@ -37,12 +37,6 @@ object CobblemonSensors {
val NEARBY_FLOWER = register("nearby_flower", ::FlowerSensor)
val NEARBY_SWEET_BERRY_BUSH = register("nearby_sweet_berry_bush", ::SweetBerryBushSensor)
val POKEMON_NEARBY_WANTED_ITEM = register("pokemon_nearby_wanted_item", ::PokemonItemSensor)
val NEARBY_SACC_LEAVES = register("nearby_sacc_leaves", ::SacLeavesSensor)
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
val sensor = SensorType(supplier)
sensors[id] = sensor
@@ -22,19 +22,18 @@ class ReelInPokemonContext(val species : ResourceLocation, val baitId: ResourceL
class ReelInPokemonCriterionCondition(
playerCtx: Optional<ContextAwarePredicate>,
val species: String,
val baitId: String // also allows "cobblemon:empty_bait" to indicate that no bait is allowed for this criterion to match
val baitId: String
) : SimpleCriterionCondition<ReelInPokemonContext>(playerCtx) {
companion object {
val CODEC: Codec<ReelInPokemonCriterionCondition> = RecordCodecBuilder.create { it.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter { it.playerCtx },
Codec.STRING.optionalFieldOf("species", "any").forGetter { it.species },
Codec.STRING.optionalFieldOf("baitId", "any").forGetter { it.baitId }
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId) }) }
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter { it.baitId }
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId.ifEmpty { "empty_bait" }) }) }
}
override fun matches(player: ServerPlayer, context: ReelInPokemonContext): Boolean {
return (context.species == this.species.asIdentifierDefaultingNamespace() || this.species == "any")
&& (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "any")
return (context.species == this.species.asIdentifierDefaultingNamespace() || this.species == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
}
}
@@ -78,7 +78,6 @@ object Abilities : DataRegistry {
fun all() = this.abilityMap.values.toList()
fun first() = this.abilityMap.values.first()
fun get(name: String) = abilityMap[name.lowercase()]
fun getOrDummy(name: String) = get(name) ?: DUMMY
fun getOrException(name: String) = get(name) ?: throw IllegalArgumentException("Unable to find ability of name: $name")
fun count() = this.abilityMap.size
@@ -49,9 +49,10 @@ class AbilityTemplate(
@JvmStatic
val CODEC: Codec<AbilityTemplate> = CodecUtils.createByStringCodec(
Abilities::getOrDummy,
Abilities::get,
AbilityTemplate::name
) { id -> "No ability for ID $id" }
}
}
@@ -1,49 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.schedule.Activity
/**
* A [Behavior] subclass that represents tasks that check that the entity is still in one
* of the activities that this task is part of. If the entity is no longer doing any of those
* activities, the task will be interrupted and you can handle any cleanup in [onInterrupted].
*
* @author Hiroku
* @since October 17th, 2025
*/
abstract class ActivityAwareBehavior<E : LivingEntity> : Behavior<E> {
constructor(entryCondition: Map<MemoryModuleType<*>, MemoryStatus>) : super(entryCondition)
constructor(entryCondition: Map<MemoryModuleType<*>, MemoryStatus>, duration: Int) : super(entryCondition, duration)
constructor(entryCondition: Map<MemoryModuleType<*>, MemoryStatus>, minDuration: Int, maxDuration: Int) : super(entryCondition, minDuration, maxDuration)
val activities: MutableSet<Activity> = mutableSetOf()
fun isStillDoingActivity(entity: E): Boolean {
return activities.any(entity.brain::isActive)
}
override fun canStillUse(level: ServerLevel, entity: E, gameTime: Long) = isStillDoingActivity(entity)
open fun onInterrupted(level: ServerLevel, entity: E, gameTime: Long) {
// No-op
}
override fun stop(level: ServerLevel, entity: E, gameTime: Long) {
super.stop(level, entity, gameTime)
if (!isStillDoingActivity(entity)) {
onInterrupted(level, entity, gameTime)
}
}
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.ai
import com.google.common.collect.ImmutableList
import com.mojang.datafixers.util.Pair
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.schedule.Activity
@@ -22,13 +23,6 @@ class ActivityConfigurationContext(val activity: Activity) {
}
fun apply(entity: LivingEntity) {
tasks.forEach {
val activityAwareBehavior = it.second as? ActivityAwareBehavior<*>
?: (it.second as? WrapperLivingEntityTask<*>)?.task as? ActivityAwareBehavior
?: return@forEach
activityAwareBehavior.activities += activity
}
entity.brain.addActivity(activity, ImmutableList.copyOf(tasks))
}
}
@@ -9,34 +9,23 @@
package com.cobblemon.mod.common.api.ai
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.activityRegistry
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Dynamic
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
import net.minecraft.world.entity.schedule.Activity
import net.minecraft.world.entity.schedule.Schedule
class BehaviourConfigurationContext {
val runtime = MoLangRuntime().setup()
var appliedBehaviours: Set<ResourceLocation> = setOf()
var defaultActivity = Activity.IDLE
@@ -46,6 +35,7 @@ class BehaviourConfigurationContext {
val memories = mutableSetOf<MemoryModuleType<*>>()
val sensors = mutableSetOf<SensorType<*>>()
val onAddScripts = mutableListOf<ResourceLocation>()
val onAdd = mutableListOf<ExpressionLike>()
fun getOrCreateActivity(activity: Activity): ActivityConfigurationContext {
@@ -68,27 +58,32 @@ class BehaviourConfigurationContext {
this.sensors.addAll(sensors)
}
fun addOnAddScript(script: ResourceLocation) {
onAddScripts.add(script)
}
fun addOnAddScript(script: ExpressionLike) {
onAdd.add(script)
}
fun apply(entity: LivingEntity, behaviourConfigs: List<BehaviourConfig>, dynamic: Dynamic<*>) {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
runtime.withQueryValue("brain", createBrainStruct(entity, this))
if (entity is MoLangScriptingEntity) {
entity.registerVariables(behaviourConfigs.flatMap { it.getVariables(entity, this) })
entity.registerVariables(behaviourConfigs.flatMap { it.getVariables(entity) })
entity.initializeScripting()
}
// Setup the brain config
behaviourConfigs.forEach { it.preconfigure(entity, this) }
behaviourConfigs.forEach { it.configure(entity, this) }
var brain = entity.brain
// Apply the brain config
if (entity is MoLangScriptingEntity) {
brain = entity.assignNewBrainWithMemoriesAndSensors(dynamic, memories, sensors)
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
onAddScripts.forEach { CobblemonScripts.run(it, runtime) }
onAdd.forEach { runtime.resolve(it) }
}
activities.forEach { it.apply(entity) }
@@ -97,43 +92,4 @@ class BehaviourConfigurationContext {
brain.schedule = schedule
brain.setActiveActivityIfPossible(defaultActivity)
}
fun createBrainStruct(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): QueryStruct {
return QueryStruct(hashMapOf()).addStandardFunctions()
.addFunction("entity") { entity.asMostSpecificMoLangValue() }
.addFunction("create_activity") { params ->
val name = params.getString(0).asIdentifierDefaultingNamespace()
val activity = entity.level().activityRegistry.get(name) ?: return@addFunction run {
Cobblemon.LOGGER.error("Tried loading activity $name as part of an entity brain but that activity does not exist")
DoubleValue.ZERO
}
val existingActivityBuilder = behaviourConfigurationContext.activities.find { it.activity == activity }
if (existingActivityBuilder != null) {
return@addFunction createActivityStruct(existingActivityBuilder)
} else {
val activityConfigurationContext = ActivityConfigurationContext(activity)
behaviourConfigurationContext.activities.add(activityConfigurationContext)
return@addFunction createActivityStruct(activityConfigurationContext)
}
}
.addFunction("set_core_activities") { params ->
behaviourConfigurationContext.coreActivities = params.params.map { (it as ObjectValue<ActivityConfigurationContext>).obj.activity }.toSet()
return@addFunction DoubleValue.ONE
}
.addFunction("set_default_activity") { params ->
behaviourConfigurationContext.defaultActivity = params.get<ObjectValue<ActivityConfigurationContext>>(0).obj.activity
return@addFunction DoubleValue.ONE
}
}
fun createActivityStruct(activityConfigurationContext: ActivityConfigurationContext): ObjectValue<ActivityConfigurationContext> {
val struct = ObjectValue(activityConfigurationContext)
struct.addStandardFunctions()
.addFunction("add_task") { params ->
val priority = params.getInt(0)
val task = params.get(1) as ObjectValue<BehaviorControl<in LivingEntity>>
activityConfigurationContext.tasks.add(Pair(priority, task.obj))
}
return struct
}
}
@@ -33,13 +33,19 @@ class CobblemonBehaviour(
// detect if it's a ResourceLocation and failing that, Expression. The on[..]Script fields are kinda fringe though.
@SerializedName("onRemove", alternate = ["undo"])
val onRemove: ExpressionLike? = null,
@SerializedName("onRemoveScript", alternate = ["undoScript"])
val onRemoveScript: ResourceLocation? = null,
val onAdd: ExpressionLike? = null,
val onAddScript: ResourceLocation? = null,
) {
fun canBeApplied(entity: LivingEntity) = entityType?.let { entityType == entity.type.builtInRegistryHolder().unwrapKey().get().location() } != false
fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (onAdd != null) {
behaviourConfigurationContext.addOnAddScript(onAdd)
}
if (onAddScript != null) {
behaviourConfigurationContext.addOnAddScript(onAddScript)
}
configurations.forEach { it.configure(entity, behaviourConfigurationContext) }
}
@@ -47,6 +53,9 @@ class CobblemonBehaviour(
fun onRemove(entity: LivingEntity) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (onRemoveScript != null) {
CobblemonScripts.run(onRemoveScript, runtime)
}
if (onRemove != null) {
runtime.resolve(onRemove)
}
@@ -13,22 +13,6 @@ import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.mojang.datafixers.util.Either
import kotlin.jvm.optionals.getOrNull
/**
* The issue in a lot of the behaviour datapacking is that there are times when a property should be exposed to
* the user and other times that you want to fix the value in place. The remedy to this is using an [Either] where
* one side is an Expression, ergo a value that is not exposed to the user, and the other side is a fully defined
* configuration variable.
*
* When users are editing JSONs they can have a simple expression, or they can declare full variable to show to the
* user. Similarly, the classes for tasks and brain configurations can have defaults that hide from the user or
* defaults that are properly defined variables. One example where this is useful is Wander tasks which might be reused
* in panic activities where the walk speed is tuned up. When specifying the task in the panic activity, the value of
* the walk speed would be set (overriding the default which is the walk_speed MoLang variable) to a separate config
* variable that is exposed to the user. Pretty neat!
*
* @author Hiroku
* @since January 4th, 2025
*/
typealias ExpressionOrEntityVariable = Either<Expression, MoLangConfigVariable>
/**
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType
@@ -29,9 +28,9 @@ class AddMemoriesAndSensorsConfig : BehaviourConfig {
val memories: Set<MemoryModuleType<*>> = emptySet()
val sensors: Set<SensorType<*>> = mutableSetOf()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(condition).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
if (!checkCondition(entity, condition)) return
behaviourConfigurationContext.addMemories(memories)
behaviourConfigurationContext.addSensors(sensors)
}
@@ -24,13 +24,9 @@ class AddTasksToActivity : BehaviourConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val tasksByPriority = mutableMapOf<Int, List<TaskConfig>>()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = tasksByPriority.values
.flatten()
.flatMap {
it.getVariables(entity, behaviourConfigurationContext)
} + listOf(condition).asVariables()
override fun getVariables(entity: LivingEntity) = tasksByPriority.values.flatten().flatMap { it.getVariables(entity) } + listOf(condition).asVariables()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
if (!checkCondition(entity, condition)) return
val activities = if (activity != null) (activities + activity) else activities
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import net.minecraft.world.entity.LivingEntity
class AddVariablesConfig(val variables: List<MoLangConfigVariable> = emptyList()) : BehaviourConfig {
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = variables
override fun getVariables(entity: LivingEntity) = variables
// Configuration happens naturally through the override on variables
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {}
@@ -25,27 +25,18 @@ class ApplyBehaviours : BehaviourConfig {
@SerializedName("behaviours", alternate = ["behaviors"])
val behaviours = mutableListOf<ResourceLocation>()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
return if (checkCondition(behaviourConfigurationContext, condition)) {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return if (checkCondition(entity, condition)) {
behaviours.flatMap {
CobblemonBehaviours.behaviours[it]?.configurations?.flatMap { it.getVariables(entity, behaviourConfigurationContext) } ?: emptyList()
CobblemonBehaviours.behaviours[it]?.configurations?.flatMap { it.getVariables(entity) } ?: emptyList()
} + listOf(condition).asVariables()
} else {
listOf(condition).asVariables()
}
}
override fun preconfigure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
val configurations = behaviours.map { CobblemonBehaviours.behaviours[it]?.takeIf { it.canBeApplied(entity) } ?: return }
configurations.forEach {
it.configurations.forEach { it.preconfigure(entity, behaviourConfigurationContext) }
}
}
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
if (!checkCondition(entity, condition)) return
val configurations = behaviours.map { CobblemonBehaviours.behaviours[it]?.takeIf { it.canBeApplied(entity) } ?: return Cobblemon.LOGGER.warn("Behaviour $it not found") }
// Why not just add the presets to the context directly?
@@ -37,6 +37,7 @@ interface BehaviourConfig {
companion object {
val types = mutableMapOf<ResourceLocation, Class<out BehaviourConfig>>(
cobblemonResource("script") to ScriptBehaviourConfig::class.java,
cobblemonResource("custom_script") to CustomScriptBehaviourConfig::class.java,
cobblemonResource("add_tasks_to_activity") to AddTasksToActivity::class.java,
cobblemonResource("apply_behaviours") to ApplyBehaviours::class.java,
cobblemonResource("set_default_activity") to SetDefaultActivity::class.java,
@@ -47,16 +48,12 @@ interface BehaviourConfig {
)
}
/** Literally just here for shorthand. */
fun checkCondition(behaviourConfigurationContext: BehaviourConfigurationContext, expressionOrEntityVariable: ExpressionOrEntityVariable): Boolean {
return behaviourConfigurationContext.runtime.resolveBoolean(expressionOrEntityVariable.map({ it }, { "q.entity.config.${it.variableName}".asExpression() }))
}
fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable>
fun preconfigure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
// Default implementation does nothing, can be overridden if needed
fun checkCondition(entity: LivingEntity, expressionOrEntityVariable: ExpressionOrEntityVariable): Boolean {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
return runtime.resolveBoolean(expressionOrEntityVariable.map({ it }, { "q.entity.config.${it.variableName}".asExpression() }))
}
fun getVariables(entity: LivingEntity): List<MoLangConfigVariable>
fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext)
}
@@ -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.api.ai.config
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class CustomScriptBehaviourConfig : BehaviourConfig {
companion object {
val runtime = MoLangRuntime().setup()
}
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val variables: List<MoLangConfigVariable> = emptyList()
val script: ExpressionLike = "0".asExpressionLike()
val memories = emptySet<MemoryModuleType<*>>()
val sensors = emptySet<SensorType<*>>()
override fun getVariables(entity: LivingEntity) = variables
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(entity, condition)) return
behaviourConfigurationContext.addMemories(memories)
behaviourConfigurationContext.addSensors(sensors)
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
runtime.resolve(script)
}
}
@@ -117,7 +117,7 @@ Example behaviour (`uses_healing_machine.json`):
"configurations": [
{
"type": "add_tasks_to_activity",
"activity": "minecraft:idle",
"activity": "idle",
"tasksByPriority": {
"1": [
"heal_using_healing_machine",
@@ -177,26 +177,27 @@ Example:
### script
The `script` type executes a registered MoLang script which will apply some changes to the brain or entity. This can be
useful for complex variable configuration. `q.entity` will refer to the entity.
- `executeEarly`: If true, the script will be executed in the variable phase which occurs before tasks are added to the brain. Use this when your script might influence which tasks are put onto the brain.
- `script`: Either a resource location for the `molang` datapack folder or a MoLang expression that will be executed.
- `variables`: A list of variables that can be configured in the UI. Each variable has the same properties as in `add_variables`.
useful if the goal is to apply some kind of configuration to the entity that would otherwise be performed by an NPC preset
or NPC class. `q.entity` will refer to the entity. This is powerful when combined with [add_variables](#add_variables)
configs in the same behaviour preset.
Example:
```json
{
"type": "script",
"executeEarly": true,
"script": "cobblemon:configure_npc_party",
"variables": [
{
"variableName": "party_size",
"defaultValue": "3",
"displayName": "Party Size",
"description": "The number of Pokémon in the NPC's party.",
"type": "NUMBER"
}
]
"script": "cobblemon:configure_npc_party"
}
```
### custom_script
The `custom_script` type executes a custom MoLang script from this config which will apply some changes to the brain or
entity. This is useful for when you want to apply some kind of configuration to the entity that would otherwise be
by an NPC preset or NPC class. `q.entity` will refer to the entity.
Example:
```json
{
"type": "custom_script",
"script": "q.entity.set_pathfinding_malus('WATER', -1);"
}
```
@@ -9,50 +9,94 @@
package com.cobblemon.mod.common.api.ai.config
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.ai.ActivityConfigurationContext
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.util.activityRegistry
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import com.mojang.datafixers.util.Pair
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
/**
* Runs a MoLang script as part of the entity's behaviour configuration. Variables can be configured here for convenience.
*
* When [executeEarly] is true, the script will run in an earlier phase than most configs, allowing it to get ahead of
* other behaviour configurations that might depend on the changes made by the script.
*
* @author Hiroku
* @since August 11th, 2025
*/
class ScriptBehaviourConfig : BehaviourConfig {
/** If true, the script will execute as part of the getVariables phase. */
val executeEarly: Boolean = false
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val script: ExpressionLike = "0".asExpressionLike()
val script = cobblemonResource("dummy")
val variables = mutableListOf<MoLangConfigVariable>()
val memories = emptySet<MemoryModuleType<*>>()
val sensors = emptySet<SensorType<*>>()
private fun runScript(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!checkCondition(behaviourConfigurationContext, condition)) return
script.resolve(behaviourConfigurationContext.runtime)
}
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = variables
override fun preconfigure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (executeEarly) {
runScript(entity = entity, behaviourConfigurationContext = behaviourConfigurationContext)
}
}
override fun getVariables(entity: LivingEntity) = variables
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
if (!executeEarly) {
runScript(entity = entity, behaviourConfigurationContext = behaviourConfigurationContext)
}
if (!checkCondition(entity, condition)) return
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val struct = createBrainStruct(entity, behaviourConfigurationContext)
val script = CobblemonScripts.scripts[this.script]
?: run {
Cobblemon.LOGGER.error("Tried loading script $script as part of an entity brain but that script does not exist")
return
}
behaviourConfigurationContext.addMemories(memories)
behaviourConfigurationContext.addSensors(sensors)
runtime.resolve(script, mapOf("brain" to struct))
}
fun createBrainStruct(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): QueryStruct {
return QueryStruct(hashMapOf()).addStandardFunctions()
.addFunction("entity") { entity.asMostSpecificMoLangValue() }
.addFunction("create_activity") { params ->
val name = params.getString(0).asIdentifierDefaultingNamespace()
val activity = entity.level().activityRegistry.get(name) ?: return@addFunction run {
Cobblemon.LOGGER.error("Tried loading activity $name as part of an entity brain but that activity does not exist")
DoubleValue.ZERO
}
val existingActivityBuilder = behaviourConfigurationContext.activities.find { it.activity == activity }
if (existingActivityBuilder != null) {
return@addFunction createActivityStruct(existingActivityBuilder)
} else {
val activityConfigurationContext = ActivityConfigurationContext(activity)
behaviourConfigurationContext.activities.add(activityConfigurationContext)
return@addFunction createActivityStruct(activityConfigurationContext)
}
}
.addFunction("set_core_activities") { params ->
behaviourConfigurationContext.coreActivities = params.params.map { (it as ObjectValue<ActivityConfigurationContext>).obj.activity }.toSet()
return@addFunction DoubleValue.ONE
}
.addFunction("set_default_activity") { params ->
behaviourConfigurationContext.defaultActivity = params.get<ObjectValue<ActivityConfigurationContext>>(0).obj.activity
return@addFunction DoubleValue.ONE
}
}
fun createActivityStruct(activityConfigurationContext: ActivityConfigurationContext): ObjectValue<ActivityConfigurationContext> {
val struct = ObjectValue(activityConfigurationContext)
struct.addStandardFunctions()
.addFunction("add_task") { params ->
val priority = params.getInt(0)
val task = params.get(1) as ObjectValue<BehaviorControl<in LivingEntity>>
activityConfigurationContext.tasks.add(Pair(priority, task.obj))
}
return struct
}
}
@@ -15,7 +15,7 @@ import net.minecraft.world.entity.schedule.Activity
class SetCoreActivities : BehaviourConfig {
val activities = mutableListOf<Activity>()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
behaviourConfigurationContext.coreActivities = activities.toSet()
}
@@ -16,7 +16,7 @@ import net.minecraft.world.entity.schedule.Activity
class SetDefaultActivity : BehaviourConfig {
val activity: Activity = Activity.IDLE
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
behaviourConfigurationContext.defaultActivity = activity
}
@@ -8,21 +8,27 @@
package com.cobblemon.mod.common.api.ai.config
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
class SetVariablesConfig : BehaviourConfig {
var variableValues = mutableMapOf<String, ExpressionLike>()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
val runtime = MoLangRuntime().setup()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (entity is MoLangScriptingEntity) {
variableValues.forEach { (variableName, valueExpression) ->
entity.config.setDirectly(variableName, behaviourConfigurationContext.runtime.resolve(valueExpression))
entity.config.setDirectly(variableName, runtime.resolve(valueExpression))
}
}
}
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
@@ -23,18 +22,18 @@ import net.minecraft.world.entity.schedule.Activity
class ActivityChangeTaskConfig : SingleTaskConfig {
val activity: Activity = Activity.IDLE
val condition: ExpressionLike = "true".asExpressionLike()
val condition: ExpressionLike = "false".asExpressionLike()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> = emptyList()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> = emptyList()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity> {
): BehaviorControl<in LivingEntity>? {
return object : OneShot<LivingEntity>() {
override fun trigger(serverLevel: ServerLevel, livingEntity: LivingEntity, l: Long): Boolean {
mainThreadRuntime.withQueryValue("entity", livingEntity.asMostSpecificMoLangValue())
if (condition.resolveBoolean(mainThreadRuntime)) {
runtime.withQueryValue("entity", livingEntity.asMostSpecificMoLangValue())
if (condition.resolveBoolean(runtime)) {
livingEntity.brain.setActiveActivityIfPossible(activity)
return true
} else {
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WA
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
@@ -39,7 +38,7 @@ class AirWanderTaskConfig : SingleTaskConfig {
val minUpwardsMovement: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
val minDownwardsMovement: ExpressionOrEntityVariable = Either.left("3.0".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(condition, wanderChance, horizontalRange, verticalRange, speedMultiplier, minUpwardsMovement, minDownwardsMovement).asVariables()
}
@@ -47,7 +46,8 @@ class AirWanderTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.WALK_TARGET, MemoryModuleType.LOOK_TARGET, CobblemonMemories.PATH_COOLDOWN)
val wanderChanceExpression = wanderChance.asSimplifiedExpression(entity)
@@ -62,16 +62,15 @@ class AirWanderTaskConfig : SingleTaskConfig {
return@Trigger false
}
val runtime = mainThreadRuntime
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) return@Trigger false
val rotVec = entity.getViewVector(0F)
pathCooldown.setWithExpiry(true, 60L)
val target = HoverRandomPos.getPos(entity, horizontalRange.resolveInt(runtime), verticalRange.resolveInt(runtime), rotVec.x, rotVec.y, 1.5707964f, minUpwardsMovement.resolveInt(runtime), minDownwardsMovement.resolveInt(runtime))
val target = HoverRandomPos.getPos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt(), rotVec.x, rotVec.y, 1.5707964f, minUpwardsMovement.resolveInt(), minDownwardsMovement.resolveInt())
?: return@Trigger false
walkTarget.set(WalkTarget(target, speedMultiplier.resolveFloat(runtime), 3))
walkTarget.set(WalkTarget(target, speedMultiplier.resolveFloat(), 3))
lookTarget.set(BlockPosTracker(target))
return@Trigger true
}
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -19,12 +21,14 @@ class AllOfTaskConfig : TaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val tasks: List<TaskConfig> = emptyList()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = tasks.flatMap { it.getVariables(entity, behaviourConfigurationContext) }
override fun getVariables(entity: LivingEntity) = tasks.flatMap { it.getVariables(entity) }
override fun createTasks(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): List<BehaviorControl<in LivingEntity>> {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return emptyList()
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return emptyList()
return tasks.flatMap { it.createTasks(entity, behaviourConfigurationContext) }
}
}
@@ -17,7 +17,7 @@ import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class AttackAngryAtTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
@@ -16,8 +16,8 @@ import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class AttackHostileMobsTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
return emptyList()
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return emptyList<MoLangConfigVariable>()
}
override fun createTask(
@@ -10,8 +10,11 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -21,12 +24,13 @@ import net.minecraft.world.entity.ai.sensing.SensorType
class CalmDownTaskConfig : SingleTaskConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY,
MemoryModuleType.HURT_BY_ENTITY,
@@ -12,9 +12,11 @@ import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import kotlin.jvm.optionals.getOrNull
import net.minecraft.world.entity.LivingEntity
@@ -25,9 +27,7 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
/**
* Checks nearby entities and counts how many have nominated this entity as their leader. We normally update this
* as people join and leave the herd, but this is a fallback to make sure we don't get stuck with an incorrect value
* like if the entities die or whatever.
* Checks nearby entities and counts how many have nominated this entity as their leader.
*
* @author Hiroku
* @since June 17th, 2025
@@ -35,7 +35,7 @@ import net.minecraft.world.entity.ai.sensing.SensorType
class CountFollowersTaskConfig : SingleTaskConfig {
val checkTicks: ExpressionOrEntityVariable = Either.left("10".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(checkTicks).asVariables()
}
@@ -43,7 +43,8 @@ class CountFollowersTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
val checkTicksValue = checkTicks.resolveInt(behaviourConfigurationContext.runtime)
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val checkTicksValue = checkTicks.resolveInt()
behaviourConfigurationContext.addMemories(
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
CobblemonMemories.HERD_SIZE,
@@ -23,14 +23,14 @@ class DoNothingTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
val minDurationTicks: ExpressionOrEntityVariable = Either.left("40".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("80".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(minDurationTicks, maxDurationTicks).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(minDurationTicks, maxDurationTicks).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
return DoNothing(minDurationTicks.resolveInt(behaviourConfigurationContext.runtime), maxDurationTicks.resolveInt(behaviourConfigurationContext.runtime))
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
return DoNothing(minDurationTicks.resolveInt(), maxDurationTicks.resolveInt())
}
}
@@ -29,7 +29,7 @@ class EatGrassTaskConfig : SingleTaskConfig {
val cooldown = numberVariable(category = EATING_GRASS, name = COOLDOWN, default = 20 * 20).asExpressible()
val eatGrassChance = numberVariable(category = EATING_GRASS, name = EAT_GRASS_CHANCE, default = 1 / (20 * 20F)).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(
cooldown,
eatGrassChance
@@ -44,7 +44,7 @@ class EatGrassTaskConfig : SingleTaskConfig {
return null
}
val chance = eatGrassChance.resolveFloat(behaviourConfigurationContext.runtime)
val chance = eatGrassChance.resolveFloat()
if (chance == 0F) {
return null
}
@@ -54,7 +54,7 @@ class EatGrassTaskConfig : SingleTaskConfig {
return WrapperLivingEntityTask(
EatGrassTask(
chance,
cooldown.resolveInt(behaviourConfigurationContext.runtime).toLong()
cooldown.resolveInt().toLong()
),
PokemonEntity::class.java
)
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask.Companion.wrapped
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.EatHeldItemTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
class EatHeldItemTaskConfig : SingleTaskConfig {
override fun getVariables(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
behaviourConfigurationContext.addMemories(
CobblemonMemories.IS_CONSUMING_ITEM,
)
return EatHeldItemTask(entity).wrapped<PokemonEntity>()
}
}
@@ -28,7 +28,7 @@ class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.BATTLING_CATEGORY, "exit_battle_when_hurt", true).asExpressible()
var includePassiveDamage = booleanVariable(SharedEntityVariables.BATTLING_CATEGORY, "exit_battle_from_passive_damage", true).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
override fun getVariables(entity: LivingEntity) = listOf(
condition,
includePassiveDamage
).asVariables()
@@ -37,9 +37,10 @@ class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
val includePassiveDamage = includePassiveDamage.resolveBoolean(behaviourConfigurationContext.runtime)
val includePassiveDamage = includePassiveDamage.resolveBoolean()
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY,
@@ -32,7 +32,7 @@ import net.minecraft.world.level.pathfinder.PathComputationType
*/
class FindAirTaskConfig : SingleTaskConfig {
val walkSpeed = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(walkSpeed).asVariables()
}
@@ -53,7 +53,7 @@ class FindAirTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.getIsSubmerged() || entity.isInLava) {
if (!entity.getIsSubmerged()) {
return@Trigger false
}
@@ -8,16 +8,17 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.ai.ToleratedHerdLeader
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -52,25 +53,24 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
it is PokemonEntity &&
it != currentLeader &&
it.pokemon.storeCoordinates.get()?.store !is PartyStore &&
it.getHerdSize() < it.behaviour.herd.maxSize &&
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
it.getHerdSize() < it.behaviour.herd.maxSize
}.forEach { possibleLeader ->
possibleLeader as PokemonEntity
val bestMatchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
val possibleLeaderIsAlreadyALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
val currentLeaderHerdTier = currentLeaderEntity?.getHerdTier() ?: entity.getHerdTier()
if (currentLeaderHerdTier >= bestMatchingHerdLeader.tier) {
val matchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
val alreadyIsALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
val nestedHerdTier = currentLeaderEntity?.getHerdTier() ?: 0
if (nestedHerdTier >= matchingHerdLeader.tier) {
return@forEach // This leader is following a leader of a higher tier, we can't replace them
}
val distance = entity.distanceTo(possibleLeader)
if (bestMatchingHerdLeader.tier < bestTier) {
if (matchingHerdLeader.tier < bestTier) {
return@forEach // No need to check further if this leader is worse than the best found so far
} else if ((distance > bestDistance && !possibleLeaderIsAlreadyALeader) && bestMatchingHerdLeader.tier == bestTier) {
} else if ((distance > bestDistance && !alreadyIsALeader) && matchingHerdLeader.tier == bestTier) {
return@forEach // No need to check further if this leader is farther than the best found so far, unless they are already a leader - I'd prefer to join a pack
} else {
bestLeader = possibleLeader
bestDistance = distance
bestTier = bestMatchingHerdLeader.tier
bestTier = matchingHerdLeader.tier
}
}
return bestLeader ?: currentLeaderEntity
@@ -80,7 +80,7 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
// How frequently to check for whether it should herd. Probably isn't that expensive but might use this for chance.
val checkTicks: ExpressionOrEntityVariable = Either.left("60".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(checkTicks).asVariables()
}
@@ -98,7 +98,8 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
MemoryModuleType.WALK_TARGET
)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
val checkTicksValue = checkTicks.resolveInt(behaviourConfigurationContext.runtime)
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val checkTicksValue = checkTicks.resolveInt()
return BehaviorBuilder.create { instance ->
instance.group(
instance.present(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES),
@@ -118,7 +119,6 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
herdLeader.set(bestLeader.uuid.toString())
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
bestLeader.adjustHerdSize(1)
return@Trigger true
}
}
@@ -14,34 +14,23 @@ import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.FindRestingPlaceTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class FindRestingPlaceTaskConfig : SingleTaskConfig {
val condition: ExpressionLike = "true".asExpressionLike()
val horizontalSearchDistance: ExpressionOrEntityVariable = Either.left("16".asExpression())
val verticalSearchDistance: ExpressionOrEntityVariable = Either.left("5".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(horizontalSearchDistance, verticalSearchDistance).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(horizontalSearchDistance, verticalSearchDistance).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!behaviourConfigurationContext.runtime.resolveBoolean(condition)) {
return null
}
behaviourConfigurationContext.addMemories(
CobblemonMemories.PATH_COOLDOWN,
CobblemonMemories.POKEMON_DROWSY,
@@ -50,7 +39,7 @@ class FindRestingPlaceTaskConfig : SingleTaskConfig {
behaviourConfigurationContext.addSensors(CobblemonSensors.POKEMON_DROWSY)
return WrapperLivingEntityTask(
FindRestingPlaceTask.create(horizontalSearchDistance.resolveInt(behaviourConfigurationContext.runtime), verticalSearchDistance.resolveInt(behaviourConfigurationContext.runtime)),
FindRestingPlaceTask.create(horizontalSearchDistance.resolveInt(), verticalSearchDistance.resolveInt()),
PokemonEntity::class.java
)
}
@@ -9,36 +9,45 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.ai.FleeFromAttackerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class FleeAttackerTaskConfig : SingleTaskConfig {
val avoidDurationTicks: ExpressionOrEntityVariable = Either.left("600".asExpression())
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): List<MoLangConfigVariable> {
return listOf(avoidDurationTicks).asVariables()
}
override fun getVariables(entity: LivingEntity) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity> {
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY_ENTITY,
MemoryModuleType.AVOID_TARGET
)
): BehaviorControl<in LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
return FleeFromAttackerTask.create(avoidDurationTicks.asExpression())
val speedMultiplier = speedMultiplier.resolveFloat()
val desiredDistance = desiredDistance.resolveInt()
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
)
}
}
@@ -27,17 +27,18 @@ class FleeNearestHostileTaskConfig : SingleTaskConfig {
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(condition, speedMultiplier, desiredDistance).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(condition, speedMultiplier, desiredDistance).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean() || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.NEAREST_HOSTILE, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.VILLAGER_HOSTILES)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
val speedMultiplier = speedMultiplier.resolveFloat()
val desiredDistance = desiredDistance.resolveInt()
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.NEAREST_HOSTILE, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
@@ -38,7 +38,7 @@ class FlyInCirclesTaskConfig : SingleTaskConfig {
val minDurationTicks: ExpressionOrEntityVariable = Either.left("60".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("180".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(
minAngularVelocityDegrees,
maxAngularVelocityDegrees,
@@ -57,14 +57,15 @@ class FlyInCirclesTaskConfig : SingleTaskConfig {
return null
}
behaviourConfigurationContext.addMemories(MemoryModuleType.WALK_TARGET, MemoryModuleType.PATH)
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
return CircleAroundTask(
poseTypes = poseTypes,
minTurnAngleDegrees = minAngularVelocityDegrees.resolveFloat(behaviourConfigurationContext.runtime),
maxTurnAngleDegrees = maxAngularVelocityDegrees.resolveFloat(behaviourConfigurationContext.runtime),
speed = speed.resolveFloat(behaviourConfigurationContext.runtime),
verticalSpeed = verticalSpeed.resolveFloat(behaviourConfigurationContext.runtime),
minDurationTicks = minDurationTicks.resolveFloat(behaviourConfigurationContext.runtime).toInt(),
maxDurationTicks = maxDurationTicks.resolveFloat(behaviourConfigurationContext.runtime).toInt()
minTurnAngleDegrees = minAngularVelocityDegrees.resolveFloat(),
maxTurnAngleDegrees = maxAngularVelocityDegrees.resolveFloat(),
speed = speed.resolveFloat(),
verticalSpeed = verticalSpeed.resolveFloat(),
minDurationTicks = minDurationTicks.resolveFloat().toInt(),
maxDurationTicks = maxDurationTicks.resolveFloat().toInt()
).wrapped<PokemonEntity>()
}
}
@@ -19,7 +19,7 @@ import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class FollowHerdLeaderTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf()
}
@@ -27,12 +27,13 @@ class FollowWalkTargetTaskConfig : SingleTaskConfig {
val minRunTicks: ExpressionOrEntityVariable = Either.left("150".asExpression())
val maxRunTicks: ExpressionOrEntityVariable = Either.left("250".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(minRunTicks, maxRunTicks).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(minRunTicks, maxRunTicks).asVariables()
override fun createTask(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): BehaviorControl<LivingEntity>? {
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
behaviourConfigurationContext.addMemories(MemoryModuleType.WALK_TARGET, MemoryModuleType.PATH, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)
return WrapperLivingEntityTask(
FollowWalkTargetTask(minRunTicks.resolveInt(behaviourConfigurationContext.runtime), maxRunTicks.resolveInt(behaviourConfigurationContext.runtime)),
FollowWalkTargetTask(minRunTicks.resolveInt(), maxRunTicks.resolveInt()),
PathfinderMob::class.java
)
}
@@ -26,13 +26,14 @@ class GetAngryAtAttackerTaskConfig : SingleTaskConfig {
val condition = booleanVariable(ATTACKING_CATEGORY, GET_ANGRY_AT_ATTACKER, true).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(condition).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(condition).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY_ENTITY,
MemoryModuleType.ANGRY_AT,
@@ -30,7 +30,7 @@ class GoToHealingMachineTaskConfig : SingleTaskConfig {
val completionRange: ExpressionOrEntityVariable = Either.left("1".asExpression())
val walkSpeed = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(condition, walkSpeed, horizontalSearchRange, verticalSearchRange, completionRange).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(condition, walkSpeed, horizontalSearchRange, verticalSearchRange, completionRange).asVariables()
companion object {
const val SELF_HEALING = "self_healing"
@@ -41,7 +41,8 @@ class GoToHealingMachineTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(
MemoryModuleType.WALK_TARGET,
MemoryModuleType.LOOK_TARGET,
@@ -11,12 +11,9 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.OmniPathingEntity
import com.cobblemon.mod.common.util.closestPosition
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.withQueryValue
import kotlin.math.ceil
import kotlin.math.floor
import net.minecraft.core.BlockPos
@@ -36,7 +33,7 @@ import net.minecraft.world.level.pathfinder.PathComputationType
*/
class GoToLandTaskConfig : SingleTaskConfig {
val walkSpeed = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(walkSpeed).asVariables()
}
@@ -57,14 +54,13 @@ class GoToLandTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.isInWater) {
if (!entity.isInLiquid) {
return@Trigger false
}
entity as PathfinderMob
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val walkSpeedValue = walkSpeed.resolveFloat(mainThreadRuntime)
val walkSpeedValue = walkSpeed.resolveFloat()
val iterable = BlockPos.betweenClosed(
Mth.floor(entity.x - 8.0),
@@ -11,21 +11,16 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GoToSleepTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class GoToSleepTaskConfig : SingleTaskConfig {
var onlyFromStatus: ExpressionOrEntityVariable = Either.left("false".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
@@ -39,6 +34,6 @@ class GoToSleepTaskConfig : SingleTaskConfig {
CobblemonMemories.POKEMON_SLEEPING
)
behaviourConfigurationContext.addSensors(CobblemonSensors.POKEMON_DROWSY)
return WrapperLivingEntityTask(GoToSleepTask.create(onlyFromStatus.resolveBoolean(behaviourConfigurationContext.runtime)), PokemonEntity::class.java)
return WrapperLivingEntityTask(GoToSleepTask.create(), PokemonEntity::class.java)
}
}
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.HarvestSweetBerryBushTask
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class HarvestSweetBerryBushTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
behaviourConfigurationContext.addMemories(
CobblemonMemories.NEARBY_SWEET_BERRY_BUSH,
CobblemonMemories.DISABLE_WALK_TO_BERRY_BUSH,
CobblemonMemories.TIME_TRYING_TO_REACH_BERRY_BUSH,
MemoryModuleType.WALK_TARGET,
MemoryModuleType.LOOK_TARGET)
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_SWEET_BERRY_BUSH)
return HarvestSweetBerryBushTask()
}
}
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
@@ -35,12 +34,12 @@ class HateEntityTaskConfig : SingleTaskConfig {
val entityCondition: Expression = "true".asExpression()
val range: ExpressionOrEntityVariable = Either.left("24".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
val range = range.resolveFloat(behaviourConfigurationContext.runtime)
val range = range.resolveFloat()
behaviourConfigurationContext.addMemories(
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
MemoryModuleType.ANGRY_AT
@@ -53,8 +52,8 @@ class HateEntityTaskConfig : SingleTaskConfig {
).apply(instance) { entities, angryAt ->
Trigger { world, entity, _ ->
val target = instance.get(entities).findClosest {
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
return@findClosest mainThreadRuntime.resolveBoolean(entityCondition)
runtime.withQueryValue("entity", it.asMostSpecificMoLangValue())
return@findClosest runtime.resolveBoolean(entityCondition)
}.orElse(null)
if (target != null && target.distanceTo(entity) <= range) {
@@ -29,13 +29,14 @@ class HealUsingHealingMachineTaskConfig : SingleTaskConfig {
val horizontalUseRange: ExpressionOrEntityVariable = Either.left("2".asExpression())
val verticalUseRange: ExpressionOrEntityVariable = Either.left("1".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(condition, horizontalUseRange, verticalUseRange).asVariables()
override fun getVariables(entity: LivingEntity) = listOf(condition, horizontalUseRange, verticalUseRange).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.WALK_TARGET, CobblemonMemories.NPC_BATTLING,)
behaviourConfigurationContext.addSensors(CobblemonSensors.NPC_BATTLING)
return HealUsingHealingMachineTask(horizontalUseRange.asExpression(), verticalUseRange.asExpression())
@@ -8,14 +8,15 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.npc.ai.LookAtBattlingPokemonTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -30,7 +31,7 @@ class LookAtBattlingPokemonTaskConfig : SingleTaskConfig {
val minDurationTicks: ExpressionOrEntityVariable = Either.left("40".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("80".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
override fun getVariables(entity: LivingEntity) = listOf(
condition,
minDurationTicks,
maxDurationTicks
@@ -40,15 +41,16 @@ class LookAtBattlingPokemonTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!resolveBooleanVariable(LOOK_AT_BATTLING_POKEMON, behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!resolveBooleanVariable(LOOK_AT_BATTLING_POKEMON)) return null
behaviourConfigurationContext.addMemories(
MemoryModuleType.LOOK_TARGET,
CobblemonMemories.BATTLING_POKEMON
)
behaviourConfigurationContext.addSensors(CobblemonSensors.BATTLING_POKEMON)
return LookAtBattlingPokemonTask.create(
minDurationTicks = minDurationTicks.resolveInt(behaviourConfigurationContext.runtime),
maxDurationTicks = maxDurationTicks.resolveInt(behaviourConfigurationContext.runtime)
minDurationTicks = minDurationTicks.resolveInt(),
maxDurationTicks = maxDurationTicks.resolveInt()
)
}
}
@@ -27,7 +27,7 @@ class LookAtEntitiesTaskConfig : SingleTaskConfig {
val condition = booleanVariable(SharedEntityVariables.LOOKING_CATEGORY, LOOK_AT_ENTITIES, true).asExpressible()
val maxDistance = numberVariable(SharedEntityVariables.LOOKING_CATEGORY, SEE_DISTANCE, 15).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
override fun getVariables(entity: LivingEntity) = listOf(
condition,
maxDistance
).asVariables()
@@ -36,9 +36,10 @@ class LookAtEntitiesTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.LOOK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
return SetEntityLookTarget.create(maxDistance.resolveFloat(behaviourConfigurationContext.runtime))
return SetEntityLookTarget.create(maxDistance.resolveFloat())
}
}
@@ -10,28 +10,17 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.npc.ai.LookAtSpeakerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class LookAtSpeakerTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) {
return null
}
return LookAtSpeakerTask.create().also {
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
}
) = LookAtSpeakerTask.create().also {
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
}
}
@@ -28,7 +28,7 @@ class LookAtTargetTaskConfig : SingleTaskConfig {
val minDurationTicks: ExpressionOrEntityVariable = Either.left("80".asExpression())
val maxDurationTicks: ExpressionOrEntityVariable = Either.left("160".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
override fun getVariables(entity: LivingEntity) = listOf(
condition,
minDurationTicks,
maxDurationTicks
@@ -38,9 +38,10 @@ class LookAtTargetTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) return null
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
if (!condition.resolveBoolean()) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
return WrapperLivingEntityTask(LookAtTargetSink(minDurationTicks.resolveInt(behaviourConfigurationContext.runtime), maxDurationTicks.resolveInt(behaviourConfigurationContext.runtime)), Mob::class.java)
return WrapperLivingEntityTask(LookAtTargetSink(minDurationTicks.resolveInt(), maxDurationTicks.resolveInt()), Mob::class.java)
}
}
@@ -19,7 +19,7 @@ import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class LookAtTargetedBattlePokemonTaskConfig : SingleTaskConfig {
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun getVariables(entity: LivingEntity) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
@@ -20,7 +20,7 @@ class LookInDirectionTaskConfig : SingleTaskConfig {
var yaw = numberVariable(LOOKING_CATEGORY, "locked_yaw", 0F).asExpressible()
var pitch = numberVariable(LOOKING_CATEGORY, "locked_pitch", 0F).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
override fun getVariables(entity: LivingEntity) = listOf(
condition,
yaw,
pitch
@@ -8,14 +8,15 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import java.util.UUID
import net.minecraft.world.entity.LivingEntity
@@ -36,7 +37,7 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
// How frequently to check for a better herd leader
val checkTicks: ExpressionOrEntityVariable = Either.left("60".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): List<MoLangConfigVariable> {
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> {
return listOf(checkTicks).asVariables()
}
@@ -52,7 +53,8 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
val checkTicks = checkTicks.resolveInt(behaviourConfigurationContext.runtime)
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val checkTicks = checkTicks.resolveInt()
return BehaviorBuilder.create { instance ->
instance.group(
instance.present(CobblemonMemories.HERD_LEADER),
@@ -81,8 +83,6 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
if (bestLeader != null && bestLeader != leader) {
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
entity.brain.setMemory(CobblemonMemories.HERD_LEADER, bestLeader.uuid.toString())
bestLeader.adjustHerdSize(1)
leader.adjustHerdSize(-1)
}
}
return@Trigger true

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